github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/walletapi/wallet_transfer.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package walletapi
    18  
    19  import "fmt"
    20  import "sort"
    21  import "math/rand"
    22  import cryptorand "crypto/rand"
    23  import "encoding/binary"
    24  import "encoding/hex"
    25  import "encoding/json"
    26  
    27  import "github.com/romana/rlog"
    28  import "github.com/vmihailenco/msgpack"
    29  
    30  import "github.com/deroproject/derosuite/config"
    31  import "github.com/deroproject/derosuite/crypto"
    32  import "github.com/deroproject/derosuite/crypto/ringct"
    33  import "github.com/deroproject/derosuite/transaction"
    34  import "github.com/deroproject/derosuite/globals"
    35  import "github.com/deroproject/derosuite/address"
    36  import "github.com/deroproject/derosuite/structures"
    37  import "github.com/deroproject/derosuite/blockchain/inputmaturity"
    38  
    39  
    40  
    41  
    42  // send amount to specific addresses
    43  func (w *Wallet) Transfer(addr []address.Address, amount []uint64, unlock_time uint64, payment_id_hex string, fees_per_kb uint64, mixin uint64) (tx *transaction.Transaction, inputs_selected []uint64, inputs_sum uint64, change_amount uint64, err error) {
    44  
    45      
    46          var  transfer_details structures.Outgoing_Transfer_Details
    47  	w.transfer_mutex.Lock()
    48  	defer w.transfer_mutex.Unlock()
    49  	if mixin == 0 {
    50  		mixin = uint64(w.account.Mixin) // use wallet mixin, if mixin not provided
    51  	}
    52  	if mixin < 5 { // enforce minimum mixin
    53  		mixin = 5
    54  	}
    55  
    56  	// if wallet is online,take the fees from the network itself
    57  	// otherwise use whatever user has provided
    58  	//if w.GetMode()  {
    59  	fees_per_kb = w.dynamic_fees_per_kb // TODO disabled as protection while lots more testing is going on
    60  	rlog.Infof("Fees per KB %d\n", fees_per_kb)
    61  	//}
    62  
    63  	if fees_per_kb == 0 {
    64  		fees_per_kb = config.FEE_PER_KB
    65  	}
    66  
    67  	var txw *TX_Wallet_Data
    68  	if len(addr) != len(amount) {
    69  		err = fmt.Errorf("Count of address and amounts mismatch")
    70  		return
    71  	}
    72  
    73  	if len(addr) < 1 {
    74  		err = fmt.Errorf("Destination address missing")
    75  		return
    76  	}
    77  
    78  	var payment_id []byte // we later on find WHETHER to include it, encrypt it depending on length
    79  
    80  	// if payment  ID is provided explicity, use it
    81  	if payment_id_hex != "" {
    82  		payment_id, err = hex.DecodeString(payment_id_hex) // payment_id in hex
    83  		if err != nil {
    84  			return
    85  		}
    86  
    87  		if len(payment_id) == 32 || len(payment_id) == 8 {
    88  
    89  		} else {
    90  			err = fmt.Errorf("Payment ID must be atleast 64 hex chars (32 bytes) or 16 hex chars 8 byte")
    91  			return
    92  		}
    93  
    94  	}
    95  
    96  	// only only single payment id
    97  	for i := range addr {
    98  		if addr[i].IsIntegratedAddress() && payment_id_hex != "" {
    99  			err = fmt.Errorf("Payment ID provided in both integrated address and separately")
   100  			return
   101  		}
   102  	}
   103  
   104  	// if integrated address payment id present , normal payment id must not be provided
   105  	for i := range addr {
   106  		if addr[i].IsIntegratedAddress() {
   107  			if len(payment_id) > 0 { // a transaction can have only single encrypted payment ID
   108  				err = fmt.Errorf("More than 1 integrated address provided")
   109  				return
   110  			}
   111  			payment_id = addr[i].PaymentID
   112  		}
   113  	}
   114  
   115  	fees := uint64(0) // start with zero fees
   116  	expected_fee := uint64(0)
   117  	total_amount_required := uint64(0)
   118  
   119  	for i := range amount {
   120  		if amount[i] == 0 { // cannot send 0  amount
   121  			err = fmt.Errorf("Sending 0 amount to destination NOT possible")
   122  			return
   123  		}
   124  		total_amount_required += amount[i]
   125  	}
   126  
   127  	// infinite tries to build a transaction
   128  	for {
   129  
   130  		// we need to make sure that account has sufficient unlocked balance ( to send amount ) + required amount of fees
   131  		unlocked, _ := w.Get_Balance()
   132  
   133  		if total_amount_required >= unlocked {
   134  			err = fmt.Errorf("Insufficent unlocked balance")
   135  			return
   136  		}
   137  
   138  		// now we need to select outputs with sufficient balance
   139  		//total_amount_required += fees
   140  		// select few outputs randomly
   141  		inputs_selected, inputs_sum = w.select_outputs_for_transfer(total_amount_required, fees+expected_fee, false)
   142  
   143  		if inputs_sum < (total_amount_required + fees) {
   144                          err = fmt.Errorf("Insufficent unlocked balance")
   145  			return
   146  		}
   147  
   148  		rlog.Infof("Selected %d (%+v) iinputs to transfer  %s DERO\n", len(inputs_selected), inputs_selected, globals.FormatMoney(inputs_sum))
   149  
   150  		/* for i := range user.Outputs_Ready {
   151  		       if i == 739 || i == 752  {
   152  
   153  		       fmt.Printf("selecting inuput %d  data %+v \n",i, user.Outputs_Ready[i])
   154  		   }
   155  
   156  		   }*/
   157  
   158  		// lets prepare the inputs for ringct, so as we can used them
   159  		var inputs []ringct.Input_info
   160  		for i := range inputs_selected {
   161  
   162  			txw, err = w.load_funds_data(inputs_selected[i], FUNDS_BUCKET)
   163  			if err != nil {
   164  				err = fmt.Errorf("Error while reading available funds index( it was just selected ) index %d err %s", inputs_selected[i], err)
   165  				return
   166  			}
   167  
   168  			rlog.Infof("current input  %d %d \n", i, inputs_selected[i])
   169  			var current_input ringct.Input_info
   170  			current_input.Amount = txw.WAmount
   171  			current_input.Key_image = crypto.Hash(txw.WKimage)
   172  			current_input.Sk = txw.WKey
   173  
   174  			//current_input.Index = i   is calculated after sorting of ring members
   175  			current_input.Index_Global = txw.TXdata.Index_Global
   176  
   177  			// add ring members here
   178  
   179  			// ring_size = 4
   180  			// TODO force random ring members
   181  
   182  			//  mandatory add ourselves as ring member, otherwise there is no point in building the tx
   183  			current_input.Ring_Members = append(current_input.Ring_Members, current_input.Index_Global)
   184  			current_input.Pubs = append(current_input.Pubs, txw.TXdata.InKey)
   185  
   186  			// add necessary amount  of random ring members
   187  			// TODO we need to make sure ring members are mature, otherwise tx will fail because o immature inputs
   188  			// This can cause certain TX to fail
   189  			for {
   190  
   191  				var buf [8]byte
   192  				cryptorand.Read(buf[:])
   193  				r, err := w.load_ring_member(binary.LittleEndian.Uint64(buf[:]) % w.account.Index_Global)
   194  				if err == nil {
   195  
   196  					// make sure ring member are not repeated
   197  					new_ring_member := true
   198  					for j := range current_input.Ring_Members { // TODO we donot need the loop
   199  						if r.Index_Global == current_input.Ring_Members[j] {
   200  							new_ring_member = false // we should not use this ring member
   201  						}
   202  						// if ring member is not mature, choose another one
   203  						if !inputmaturity.Is_Input_Mature(w.Get_Height(),
   204  							r.Height,
   205  							r.Unlock_Height,
   206  							r.Sigtype) {
   207  							new_ring_member = false // we should not use this ring member
   208  
   209  						}
   210  					}
   211  
   212  					if !new_ring_member {
   213  						continue
   214  					}
   215  					current_input.Ring_Members = append(current_input.Ring_Members, r.Index_Global)
   216  					current_input.Pubs = append(current_input.Pubs, r.InKey)
   217  				}
   218  				if uint64(len(current_input.Ring_Members)) == mixin { // atleast 5 ring members
   219  					break
   220  				}
   221  			}
   222  
   223  			// sort ring members and setup index
   224  
   225  			/*
   226  			   if i == 0 {
   227  			       current_input.Ring_Members = []uint64{2,3,739,1158}
   228  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   229  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   230  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[739].TXdata.InKey)
   231  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[1158].TXdata.InKey)
   232  			       current_input.Index = 2
   233  			   }
   234  			   if i == 1 {
   235  			       continue
   236  			       current_input.Ring_Members = []uint64{2,3,752,1158}
   237  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   238  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   239  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[752].TXdata.InKey)
   240  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[1158].TXdata.InKey)
   241  			       current_input.Index = 2
   242  
   243  			   }
   244  			*/
   245  			/*   current_input.Ring_Members = []uint64{2,3,4,inputs_selected[i]}
   246  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   247  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   248  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[4].TXdata.InKey)
   249  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[inputs_selected[i]].TXdata.InKey)
   250  			     current_input.Index = 3
   251  
   252  			*/
   253  
   254  			rlog.Infof(" current input before sorting %+v \n", current_input.Ring_Members)
   255  
   256  			current_input = sort_ring_members(current_input)
   257  			rlog.Infof(" current input after sorting  %+v \n", current_input.Ring_Members)
   258  			inputs = append(inputs, current_input)
   259  		}
   260  
   261  		//  fees = uint64(0)
   262  		// fees = uint64(66248560000)
   263  
   264  		// fill in the outputs
   265  
   266  		var outputs []ringct.Output_info
   267  
   268  	rebuild_tx_with_correct_fee:
   269  		outputs = outputs[:0]
   270  		
   271  		transfer_details.Fees = fees
   272  		transfer_details.Amount = transfer_details.Amount[:0]
   273  		transfer_details.Daddress = transfer_details.Daddress[:0]
   274  
   275  		for i := range addr {
   276  			var output ringct.Output_info
   277  			output.Amount = amount[i]
   278  			output.Public_Spend_Key = addr[i].SpendKey
   279  			output.Public_View_Key = addr[i].ViewKey
   280  			
   281  			transfer_details.Amount = append(transfer_details.Amount,amount[i]) 
   282                          transfer_details.Daddress = append(transfer_details.Daddress,addr[i].String()) 
   283  
   284  			outputs = append(outputs, output)
   285  		}
   286  
   287  		// get ready to receive change
   288  		var change ringct.Output_info
   289  		change.Amount = inputs_sum - total_amount_required - fees // we must have atleast change >= fees
   290  		change.Public_Spend_Key = w.account.Keys.Spendkey_Public  /// fill our public spend key
   291  		change.Public_View_Key = w.account.Keys.Viewkey_Public    // fill our public view key
   292  
   293  		if change.Amount > 0 { // include change only if required
   294                      
   295                      transfer_details.Amount = append(transfer_details.Amount,change.Amount) 
   296                      transfer_details.Daddress = append(transfer_details.Daddress,w.account.GetAddress().String()) 
   297  
   298                          
   299                      outputs = append(outputs, change)
   300  		}
   301  
   302  		change_amount = change.Amount
   303  		
   304  		// if encrypted payment ids are used, they are encrypted against first output
   305  		// if we shuffle outputs encrypted ids will break
   306  		if unlock_time == 0  { // shuffle output and change randomly
   307  			if  len(payment_id) == 8{ // do not shuffle if encrypted payment IDs are used
   308  
   309  			}else{
   310                      globals.Global_Random.Shuffle(len(outputs), func(i, j int) {
   311  					outputs[i], outputs[j] = outputs[j], outputs[i]
   312  			})
   313                      
   314                  }
   315              }
   316  
   317  		// outputs = append(outputs, change)
   318  		tx = w.Create_TX_v2(inputs, outputs, fees, unlock_time, payment_id, true)
   319  
   320  		tx_size := uint64(len(tx.Serialize()))
   321  		size_in_kb := tx_size / 1024
   322  
   323  		if (tx_size % 1024) != 0 { // for any part there of, use a full KB fee
   324  			size_in_kb += 1
   325  		}
   326  
   327  		minimum_fee := size_in_kb * fees_per_kb
   328  
   329  		needed_fee := w.getfees(minimum_fee) // multiply minimum fees by multiplier
   330  
   331  		rlog.Infof("minimum fee %s required fees %s provided fee %s size %d fee/kb %s\n", globals.FormatMoney(minimum_fee), globals.FormatMoney(needed_fee), globals.FormatMoney(fees), size_in_kb, globals.FormatMoney(fees_per_kb))
   332  
   333  		if fees > needed_fee { // transaction was built up successfully
   334  			fees = needed_fee // setup fees parameter exactly as much required
   335  			goto rebuild_tx_with_correct_fee
   336  		}
   337  
   338  		// keep trying until we are successfull or funds become Insufficent
   339  		if fees == needed_fee { // transaction was built up successfully
   340  			break
   341  		}
   342  
   343  		// we need to try again
   344  		fees = needed_fee               // setup estimated parameter
   345  		expected_fee = expected_fee * 2 // double the estimated fee
   346  
   347  	}
   348  	
   349  	// log enough information to wallet to display it again to users
   350  	transfer_details.PaymentID =  hex.EncodeToString(payment_id) 
   351          
   352          // get the tx secret key and store it
   353          txhash := tx.GetHash()
   354          transfer_details.TXsecretkey = w.GetTXKey(tx.GetHash())
   355          transfer_details.TXID = txhash.String()
   356          
   357          // lets marshal the structure and store it in in DB
   358          
   359          details_serialized, err := json.Marshal(transfer_details)
   360  	if err != nil {
   361                  rlog.Warnf("Err marshalling details err %s", err) 
   362  	}
   363          
   364          w.store_key_value(BLOCKCHAIN_UNIVERSE, []byte(TX_OUT_DETAILS_BUCKET), txhash[:], details_serialized[:])
   365          
   366  //        fmt.Printf("%+v\n",transfer_details)
   367  //        fmt.Printf("%+v\n",transfer_details,w.GetTXOutDetails(tx.GetHash()))
   368  
   369          
   370  
   371  	// log enough information in log file to validate sum(inputs) = sum(outputs) + fees
   372  
   373  	{
   374  
   375  		rlog.Infof("Transfering total amount %s \n", globals.FormatMoneyPrecision(inputs_sum, 12))
   376  		rlog.Infof("total amount (output) %s \n", globals.FormatMoneyPrecision(total_amount_required, 12))
   377  		rlog.Infof("change amount ( will come back ) %s \n", globals.FormatMoneyPrecision(change_amount, 12))
   378  		rlog.Infof("fees %s \n", globals.FormatMoneyPrecision(tx.RctSignature.Get_TX_Fee(), 12))
   379  		rlog.Infof("Inputs %d == outputs %d ( %d + %d + %d )", inputs_sum, (total_amount_required + change_amount + tx.RctSignature.Get_TX_Fee()), total_amount_required, change_amount, tx.RctSignature.Get_TX_Fee())
   380  		if inputs_sum != (total_amount_required + change_amount + tx.RctSignature.Get_TX_Fee()) {
   381  			rlog.Warnf("INPUTS != OUTPUTS, please check")
   382  			panic(fmt.Sprintf("Inputs %d != outputs ( %d + %d + %d )", inputs_sum, total_amount_required, change_amount, tx.RctSignature.Get_TX_Fee()))
   383  		}
   384  	}
   385  
   386  	return
   387  }
   388  
   389  // send all unlocked balance amount to specific address
   390  func (w *Wallet) Transfer_Everything(addr address.Address, payment_id_hex string, unlock_time uint64, fees_per_kb uint64, mixin uint64) (tx *transaction.Transaction, inputs_selected []uint64, inputs_sum uint64, err error) {
   391  
   392      
   393          var  transfer_details structures.Outgoing_Transfer_Details
   394  	
   395  	
   396  	w.transfer_mutex.Lock()
   397  	defer w.transfer_mutex.Unlock()
   398  
   399  	if mixin < 5 { // enforce minimum mixin
   400  		mixin = 5
   401  	}
   402  
   403  	// if wallet is online,take the fees from the network itself
   404  	// otherwise use whatever user has provided
   405  	//if w.GetMode()  {
   406  	fees_per_kb = w.dynamic_fees_per_kb // TODO disabled as protection while lots more testing is going on
   407  	rlog.Infof("Fees per KB %d\n", fees_per_kb)
   408  	//}
   409  
   410  	if fees_per_kb == 0 { // hard coded at compile time
   411  		fees_per_kb = config.FEE_PER_KB
   412  	}
   413  
   414  	var txw *TX_Wallet_Data
   415  
   416  	var payment_id []byte // we later on find WHETHER to include it, encrypt it depending on length
   417  
   418  	// if payment  ID is provided explicity, use it
   419  	if payment_id_hex != "" {
   420  		payment_id, err = hex.DecodeString(payment_id_hex) // payment_id in hex
   421  		if err != nil {
   422  			return
   423  		}
   424  
   425  		if len(payment_id) == 32 || len(payment_id) == 8 {
   426  
   427  		} else {
   428  			err = fmt.Errorf("Payment ID must be atleast 64 hex chars (32 bytes) or 16 hex chars 8 byte")
   429  			return
   430  		}
   431  
   432  	}
   433  
   434  	// only only single payment id
   435  	if addr.IsIntegratedAddress() && payment_id_hex != "" {
   436  		err = fmt.Errorf("Payment ID provided in both integrated address and separately")
   437  		return
   438  	}
   439  	// if integrated address payment id present , normal payment id must not be provided
   440  	if addr.IsIntegratedAddress() {
   441  		payment_id = addr.PaymentID
   442  	}
   443  
   444  	fees := uint64(0) // start with zero fees
   445  	expected_fee := uint64(0)
   446  
   447  	// infinite tries to build a transaction
   448  	for {
   449  
   450  		// now we need to select all outputs with sufficient balance
   451  		inputs_selected, inputs_sum = w.select_outputs_for_transfer(0, fees+expected_fee, true)
   452  
   453  		if len(inputs_selected) < 1 {
   454  			err = fmt.Errorf("Insufficent unlocked balance")
   455  			return
   456  		}
   457  
   458  		rlog.Infof("Selected %d (%+v) iinputs to transfer  %s DERO\n", len(inputs_selected), inputs_selected, globals.FormatMoney(inputs_sum))
   459  
   460  		// lets prepare the inputs for ringct, so as we can used them
   461  		var inputs []ringct.Input_info
   462  		for i := range inputs_selected {
   463  
   464  			txw, err = w.load_funds_data(inputs_selected[i], FUNDS_BUCKET)
   465  			if err != nil {
   466  				err = fmt.Errorf("Error while reading available funds index( it was just selected ) index %d err %s", inputs_selected[i], err)
   467  				return
   468  			}
   469  
   470  			rlog.Infof("current input  %d %d \n", i, inputs_selected[i])
   471  			var current_input ringct.Input_info
   472  			current_input.Amount = txw.WAmount
   473  			current_input.Key_image = crypto.Hash(txw.WKimage)
   474  			current_input.Sk = txw.WKey
   475  
   476  			//current_input.Index = i   is calculated after sorting of ring members
   477  			current_input.Index_Global = txw.TXdata.Index_Global
   478  
   479  			// add ring members here
   480  
   481  			// ring_size = 4
   482  			// TODO force random ring members
   483  
   484  			//  mandatory add ourselves as ring member, otherwise there is no point in building the tx
   485  			current_input.Ring_Members = append(current_input.Ring_Members, current_input.Index_Global)
   486  			current_input.Pubs = append(current_input.Pubs, txw.TXdata.InKey)
   487  
   488  			// add necessary amount  of random ring members
   489  			// TODO we need to make sure ring members are mature, otherwise tx will fail because o immature inputs
   490  			// This can cause certain TX to fail
   491  			for {
   492  
   493  				var buf [8]byte
   494  				cryptorand.Read(buf[:])
   495  				r, err := w.load_ring_member(binary.LittleEndian.Uint64(buf[:]) % w.account.Index_Global)
   496  				if err == nil {
   497  
   498  					// make sure ring member are not repeated
   499  					new_ring_member := true
   500  					for j := range current_input.Ring_Members { // TODO we donot need the loop
   501  						if r.Index_Global == current_input.Ring_Members[j] {
   502  							new_ring_member = false // we should not use this ring member
   503  						}
   504  						// if ring member is not mature, choose another one
   505  						if !inputmaturity.Is_Input_Mature(w.Get_Height(),
   506  							r.Height,
   507  							r.Unlock_Height,
   508  							r.Sigtype) {
   509  							new_ring_member = false // we should not use this ring member
   510  
   511  						}
   512  					}
   513  
   514  					if !new_ring_member {
   515  						continue
   516  					}
   517  					current_input.Ring_Members = append(current_input.Ring_Members, r.Index_Global)
   518  					current_input.Pubs = append(current_input.Pubs, r.InKey)
   519  				}
   520  				if uint64(len(current_input.Ring_Members)) == mixin { // atleast 5 ring members
   521  					break
   522  				}
   523  			}
   524  
   525  			// sort ring members and setup index
   526  
   527  			/*
   528  			   if i == 0 {
   529  			       current_input.Ring_Members = []uint64{2,3,739,1158}
   530  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   531  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   532  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[739].TXdata.InKey)
   533  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[1158].TXdata.InKey)
   534  			       current_input.Index = 2
   535  			   }
   536  			   if i == 1 {
   537  			       continue
   538  			       current_input.Ring_Members = []uint64{2,3,752,1158}
   539  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   540  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   541  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[752].TXdata.InKey)
   542  			       current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[1158].TXdata.InKey)
   543  			       current_input.Index = 2
   544  
   545  			   }
   546  			*/
   547  			/*   current_input.Ring_Members = []uint64{2,3,4,inputs_selected[i]}
   548  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[2].TXdata.InKey)
   549  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[3].TXdata.InKey)
   550  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[4].TXdata.InKey)
   551  			     current_input.Pubs = append(current_input.Pubs,user.Random_Outputs[inputs_selected[i]].TXdata.InKey)
   552  			     current_input.Index = 3
   553  
   554  			*/
   555  
   556  			rlog.Infof(" current input before sorting %+v \n", current_input.Ring_Members)
   557  
   558  			current_input = sort_ring_members(current_input)
   559  			rlog.Infof(" current input after sorting  %+v \n", current_input.Ring_Members)
   560  			inputs = append(inputs, current_input)
   561  		}
   562  
   563  		//  fees = uint64(0)
   564  		// fees = uint64(66248560000)
   565  
   566  		// fill in the outputs
   567  
   568  		var outputs []ringct.Output_info
   569  
   570  	rebuild_tx_with_correct_fee:
   571  		outputs = outputs[:0]
   572  
   573  		var output ringct.Output_info
   574  		output.Amount = inputs_sum - fees
   575  		output.Public_Spend_Key = addr.SpendKey
   576  		output.Public_View_Key = addr.ViewKey
   577  		
   578  		
   579  		transfer_details.Fees = fees
   580  		transfer_details.Amount = transfer_details.Amount[:0]
   581  		transfer_details.Daddress = transfer_details.Daddress[:0]
   582  
   583  		
   584  		transfer_details.Amount = append(transfer_details.Amount,output.Amount) 
   585                  transfer_details.Daddress = append(transfer_details.Daddress,addr.String()) 
   586  
   587                      
   588  
   589  		outputs = append(outputs, output)
   590  
   591  		// outputs = append(outputs, change)
   592  		tx = w.Create_TX_v2(inputs, outputs, fees, unlock_time, payment_id, true)
   593  
   594  		tx_size := uint64(len(tx.Serialize()))
   595  		size_in_kb := tx_size / 1024
   596  
   597  		if (tx_size % 1024) != 0 { // for any part there of, use a full KB fee
   598  			size_in_kb += 1
   599  		}
   600  
   601  		minimum_fee := size_in_kb * fees_per_kb
   602  
   603  		needed_fee := w.getfees(minimum_fee) // multiply minimum fees by multiplier
   604  
   605  		rlog.Infof("required fees %s provided fee %s size %d fee/kb %s\n", globals.FormatMoney(needed_fee), globals.FormatMoney(fees), size_in_kb, globals.FormatMoney(fees_per_kb))
   606  
   607  		if inputs_sum <= fees {
   608  			err = fmt.Errorf("Insufficent unlocked balance to cover fees")
   609  			return
   610  		}
   611  
   612  		if fees > needed_fee { // transaction was built up successfully
   613  			fees = needed_fee // setup fees parameter exactly as much required
   614  			goto rebuild_tx_with_correct_fee
   615  		}
   616  
   617  		// keep trying until we are successfull or funds become Insufficent
   618  		if fees == needed_fee { // transaction was built up successfully
   619  			break
   620  		}
   621  
   622  		// we need to try again
   623  		fees = needed_fee               // setup estimated parameter
   624  		expected_fee = expected_fee * 2 // double the estimated fee
   625  
   626  	}
   627  	
   628  	
   629  	
   630  	// log enough information to wallet to display it again to users
   631  	transfer_details.PaymentID =  hex.EncodeToString(payment_id) 
   632          
   633          // get the tx secret key and store it
   634          txhash := tx.GetHash()
   635          transfer_details.TXsecretkey = w.GetTXKey(tx.GetHash())
   636          transfer_details.TXID = txhash.String()
   637          
   638          // lets marshal the structure and store it in in DB
   639          
   640          details_serialized, err := json.Marshal(transfer_details)
   641  	if err != nil {
   642                  rlog.Warnf("Err marshalling details err %s", err) 
   643  	}
   644          
   645          w.store_key_value(BLOCKCHAIN_UNIVERSE, []byte(TX_OUT_DETAILS_BUCKET), txhash[:], details_serialized[:])
   646          
   647         // fmt.Printf("%+v\n",transfer_details)
   648        //  fmt.Printf("%+v\n",transfer_details,w.GetTXOutDetails(tx.GetHash()))
   649  
   650          
   651  
   652  	return
   653  }
   654  
   655  type member struct {
   656  	index uint64
   657  	key   ringct.CtKey
   658  }
   659  
   660  type members []member
   661  
   662  func (s members) Len() int {
   663  	return len(s)
   664  }
   665  func (s members) Swap(i, j int) {
   666  	s[i], s[j] = s[j], s[i]
   667  }
   668  func (s members) Less(i, j int) bool {
   669  	return s[i].index < s[j].index
   670  }
   671  
   672  // sort ring members
   673  func sort_ring_members(input ringct.Input_info) ringct.Input_info {
   674  
   675  	if len(input.Ring_Members) != len(input.Pubs) {
   676  		panic(fmt.Sprintf("Internal error !!!, ring member count %d != pubs count %d", len(input.Ring_Members), len(input.Pubs)))
   677  	}
   678  	var data_set members
   679  	for i := range input.Pubs {
   680  		data_set = append(data_set, member{input.Ring_Members[i], input.Pubs[i]})
   681  	}
   682  	sort.Sort(data_set)
   683  
   684  	for i := range input.Pubs {
   685  		input.Ring_Members[i] = data_set[i].index
   686  		input.Pubs[i] = data_set[i].key
   687  		if data_set[i].index == input.Index_Global {
   688  			input.Index = i
   689  		}
   690  	}
   691  
   692  	return input
   693  
   694  }
   695  
   696  func (w *Wallet) select_outputs_for_transfer(needed_amount uint64, fees uint64, all bool) (selected_output_index []uint64, sum uint64) {
   697  
   698  	// return []uint64{739,752}, 6000000000000
   699  	// return []uint64{4184}, user.Outputs_Ready[4184].WAmount
   700  
   701  	index_list := w.load_all_values_from_bucket(BLOCKCHAIN_UNIVERSE, []byte(FUNDS_AVAILABLE))
   702  
   703  	// shuffle the index_list
   704  	// see https://stackoverflow.com/questions/12264789/shuffle-array-in-go
   705  	for i := len(index_list) - 1; i > 0; i-- {
   706  		j := rand.Intn(i + 1)
   707  		index_list[i], index_list[j] = index_list[j], index_list[i]
   708  	}
   709  
   710  	for i := range index_list { // load index
   711  		current_index := binary.BigEndian.Uint64(index_list[i])
   712  
   713  		tx, err := w.load_funds_data(current_index, FUNDS_BUCKET)
   714  		if err != nil {
   715  			fmt.Printf("Error while reading available funds index index %d err %s", current_index, err)
   716  			continue
   717  		}
   718  
   719  		if inputmaturity.Is_Input_Mature(w.Get_Height(),
   720  			tx.TXdata.Height,
   721  			tx.TXdata.Unlock_Height,
   722  			tx.TXdata.SigType) && !w.IsKeyImageSpent(tx.WKimage) {
   723  
   724  			sum += tx.WAmount
   725  
   726  			selected_output_index = append(selected_output_index, current_index) // select this output
   727  
   728  			if !all { // user requested all inputs
   729  				if sum > (needed_amount + fees) {
   730  					return
   731  				}
   732  			}
   733  		}
   734  
   735  	}
   736  
   737  	return
   738  
   739  }
   740  
   741  // load funds data structure from DB
   742  func (w *Wallet) load_funds_data(index uint64, bucket string) (tx_wallet *TX_Wallet_Data, err error) {
   743  
   744  	value_bytes, err := w.load_key_value(BLOCKCHAIN_UNIVERSE, []byte(bucket), itob(index))
   745  	if err != nil {
   746  		err = fmt.Errorf("Error while reading available funds index index %d err %s", index, err)
   747  		return
   748  	}
   749  
   750  	tx_wallet = &TX_Wallet_Data{}
   751  	err = msgpack.Unmarshal(value_bytes, &tx_wallet)
   752  	if err != nil {
   753  		err = fmt.Errorf("Error while decoding availble funds data index %d err %s", index, err)
   754  		tx_wallet = nil
   755  		return
   756  	}
   757  	return // everything was success
   758  }
   759  
   760  // this will create ringct simple 2 transaction to transfer x amount
   761  func (w *Wallet) Create_TX_v2(inputs []ringct.Input_info, outputs []ringct.Output_info, fees uint64, unlock_time uint64, payment_id []byte, bulletproof bool) (txout *transaction.Transaction) {
   762  	var tx transaction.Transaction
   763  	tx.Version = 2
   764  	tx.Unlock_Time = unlock_time // for the first input
   765  
   766  	// setup the vins as they should be , setup key image
   767  	for i := range inputs {
   768  
   769  		/* for j := range inputs[i].Pubs {
   770  		    fmt.Printf("dest key %d %s\n",j,inputs[i].Pubs[j].Destination )
   771  		    fmt.Printf("dest key %d %s\n",j,inputs[i].Pubs[j].Mask )
   772  		}*/
   773  
   774  		//fmt.Printf("Key image %d %s\n",i, inputs[i].Key_image )
   775  		txin := transaction.Txin_to_key{Amount: 0, K_image: inputs[i].Key_image} //amount is always zero in ringct and later
   776  
   777  		if len(inputs[i].Ring_Members) != len(inputs[i].Pubs) {
   778  			panic(fmt.Sprintf("Ring members and public keys should be equal %d %d", len(inputs[i].Ring_Members), len(inputs[i].Pubs)))
   779  		}
   780  		// fill in the ring members coded as offsets
   781  		last_member := uint64(0)
   782  		for j := range inputs[i].Ring_Members {
   783  			current_offset := inputs[i].Ring_Members[j] - last_member
   784  			last_member = inputs[i].Ring_Members[j]
   785  			txin.Key_offsets = append(txin.Key_offsets, current_offset)
   786  		}
   787  
   788  		//fmt.Printf("Offsets %d = %+v\n",i, txin.Key_offsets)
   789  		tx.Vin = append(tx.Vin, txin)
   790  	}
   791  
   792  	// input setup is completed, now we need to setup outputs
   793  
   794  	// generate transaction wide unique key
   795  	tx_secret_key, tx_public_key := crypto.NewKeyPair() // create new tx key pair
   796  
   797  	/*
   798  	   // these 3 lines are temporary
   799  	    crypto.Sc_0(tx_secret_key);  // temporary for debugging puprpose, make it zero
   800  	    crypto.ScReduce32(tx_secret_key) // reduce it for crypto purpose
   801  	    tx_public_key = tx_secret_key.PublicKey()
   802  	*/
   803  
   804  	//tx_public_key is added to extra and serialized
   805  	tx.Extra_map = map[transaction.EXTRA_TAG]interface{}{}
   806  	tx.Extra_map[transaction.TX_PUBLIC_KEY] = *tx_public_key
   807  	tx.PaymentID_map = map[transaction.EXTRA_TAG]interface{}{}
   808  
   809  	if len(payment_id) == 32 {
   810  		tx.PaymentID_map[transaction.TX_EXTRA_NONCE_PAYMENT_ID] = payment_id
   811  	}
   812  
   813  	tx.Extra = tx.Serialize_Extra() // serialize the extra
   814  
   815  	for i := range outputs {
   816  
   817  		//fmt.Printf("%d amount %d\n",i,outputs[i].Amount )
   818  		derivation := crypto.KeyDerivation(&outputs[i].Public_View_Key, tx_secret_key) // keyderivation using output address view key
   819  
   820  		// payment id if encrypted are encrypted against first receipient
   821  		if i == 0 { // encrypt it now for the first output
   822  			if len(payment_id) == 8 { // it is an encrypted payment ID,
   823  				tx.PaymentID_map[transaction.TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID] = EncryptDecryptPaymentID(derivation, *tx_public_key, payment_id)
   824  			}
   825  			tx.Extra = tx.Serialize_Extra() // serialize the extra
   826  
   827  		}
   828  
   829  		// this becomes the key within Vout
   830  		index_within_tx := i
   831  		ehphermal_public_key := derivation.KeyDerivation_To_PublicKey(uint64(index_within_tx), outputs[i].Public_Spend_Key)
   832  
   833  		// added the amount and key in vout
   834  		tx.Vout = append(tx.Vout, transaction.Tx_out{Amount: 0, Target: transaction.Txout_to_key{Key: ehphermal_public_key}})
   835  
   836  		// setup key so as output amount can be encrypted, this will be passed later on to ringct package to encrypt amount
   837  		outputs[i].Scalar_Key = *(derivation.KeyDerivationToScalar(uint64(index_within_tx)))
   838  		// outputs[i].Destination = ehphermal_public_key
   839  
   840  	}
   841  
   842  	// now comes the ringct part, we always generate rinct simple, they are a bit larger (~1KB) if only single input is used
   843  	// but that is okay as soon we will migrate to bulletproof
   844  	tx.RctSignature = &ringct.RctSig{} // we always generate ringct simple
   845  
   846  	// fmt.Printf("txprefix hash %s\n",tx.GetPrefixHash() )
   847  
   848  	if bulletproof {
   849  		tx.RctSignature.Gen_RingCT_Simple_BulletProof(tx.GetPrefixHash(), inputs, outputs, fees)
   850  	} else {
   851  		tx.RctSignature.Gen_RingCT_Simple(tx.GetPrefixHash(), inputs, outputs, fees)
   852  	}
   853  
   854  	// store the tx key to db, always, since we will never no since the tx may be sent offline
   855  	txhash := tx.GetHash()
   856  	w.store_key_value(BLOCKCHAIN_UNIVERSE, []byte(SECRET_KEY_BUCKET), txhash[:], tx_secret_key[:])
   857  
   858  	return &tx
   859  }