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 }