github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/transaction/transaction.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 transaction 18 19 import "fmt" 20 import "bytes" 21 import "encoding/binary" 22 23 import "github.com/romana/rlog" 24 25 import "github.com/deroproject/derosuite/crypto" 26 import "github.com/deroproject/derosuite/crypto/ringct" 27 28 const TXIN_GEN = byte(0xff) 29 const TXIN_TO_SCRIPT = byte(0) 30 const TXIN_TO_SCRIPTHASH = byte(1) 31 const TXIN_TO_KEY = byte(2) 32 33 const TXOUT_TO_SCRIPT = byte(0) 34 const TXOUT_TO_SCRIPTHASH = byte(1) 35 const TXOUT_TO_KEY = byte(2) 36 37 var TX_IN_NAME = map[byte]string{ 38 TXIN_GEN: "Coinbase", 39 TXIN_TO_SCRIPT: "To Script", 40 TXIN_TO_SCRIPTHASH: "To Script hash", 41 TXIN_TO_KEY: "To key", 42 } 43 44 const TRANSACTION = byte(0xcc) 45 const BLOCK = byte(0xbb) 46 47 /* 48 VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0); 49 VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1); 50 VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2); 51 VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0); 52 VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1); 53 VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); 54 VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); 55 VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); 56 */ 57 /* outputs */ 58 59 type Txout_to_script struct { 60 // std::vector<crypto::public_key> keys; 61 // std::vector<uint8_t> script; 62 63 Keys [][32]byte 64 Script []byte 65 66 /* BEGIN_SERIALIZE_OBJECT() 67 FIELD(keys) 68 FIELD(script) 69 END_SERIALIZE() 70 71 */ 72 } 73 74 type Txout_to_scripthash struct { 75 //crypto::hash hash; 76 Hash [32]byte 77 } 78 79 type Txout_to_key struct { 80 Key crypto.Key 81 // Mask [32]byte `json:"-"` 82 /*txout_to_key() { } 83 txout_to_key(const crypto::public_key &_key) : key(_key) { } 84 crypto::public_key key;*/ 85 86 } 87 88 // there can be only 4 types if inputs 89 90 // used by miner 91 type Txin_gen struct { 92 Height uint64 // stored as varint 93 } 94 95 type Txin_to_script struct { 96 Prev [32]byte 97 Prevout uint64 98 Sigset []byte 99 100 /* BEGIN_SERIALIZE_OBJECT() 101 FIELD(prev) 102 VARINT_FIELD(prevout) 103 FIELD(sigset) 104 END_SERIALIZE() 105 */ 106 } 107 108 type Txin_to_scripthash struct { 109 Prev [32]byte 110 Prevout uint64 111 Script Txout_to_script 112 Sigset []byte 113 114 /* BEGIN_SERIALIZE_OBJECT() 115 FIELD(prev) 116 VARINT_FIELD(prevout) 117 FIELD(script) 118 FIELD(sigset) 119 END_SERIALIZE() 120 */ 121 } 122 123 type Txin_to_key struct { 124 Amount uint64 125 Key_offsets []uint64 // this is encoded as a varint for length and then all offsets are stored as varint 126 //crypto::key_image k_image; // double spending protection 127 K_image crypto.Hash `json:"k_image"` // key image 128 129 /* BEGIN_SERIALIZE_OBJECT() 130 VARINT_FIELD(amount) 131 FIELD(key_offsets) 132 FIELD(k_image) 133 END_SERIALIZE() 134 */ 135 } 136 137 type Txin_v interface{} // it can only be txin_gen, txin_to_script, txin_to_scripthash, txin_to_key 138 139 type Tx_out struct { 140 Amount uint64 141 Target interface{} // txout_target_v ;, it can only be txout_to_script, txout_to_scripthash, txout_to_key 142 143 /* BEGIN_SERIALIZE_OBJECT() 144 VARINT_FIELD(amount) 145 FIELD(target) 146 END_SERIALIZE() 147 */ 148 149 } 150 151 // the core transaction 152 type Transaction_Prefix struct { 153 Version uint64 `json:"version"` 154 Unlock_Time uint64 `json:"unlock_time"` // used to lock first output 155 Vin []Txin_v 156 Vout []Tx_out 157 Extra []byte 158 Extra_map map[EXTRA_TAG]interface{} `json:"-"` // all information parsed from extra is placed here 159 PaymentID_map map[EXTRA_TAG]interface{} `json:"-"` // payments id parsed or set are placed her 160 ExtraType byte `json:"-"` // NOT used, candidate for deletion 161 } 162 163 type Transaction struct { 164 Transaction_Prefix 165 // same as Transaction_Prefix 166 // Signature not sure of what form 167 Signature []Signature_v1 `json:"-"` // old format, the array size is always equal to vin length, 168 //Signature_RCT RCT_Signature // version 2 169 170 RctSignature *ringct.RctSig 171 Expanded bool `json:"-"` 172 } 173 174 func (tx *Transaction) GetHash() (result crypto.Hash) { 175 switch tx.Version { 176 177 /*case 1: 178 result = crypto.Hash(crypto.Keccak256(tx.SerializeHeader())) 179 */ 180 181 case 2: 182 // version 2 requires first computing 3 separate hashes 183 // prefix, rctBase and rctPrunable 184 // and then hashing the hashes together to get the final hash 185 prefixHash := tx.GetPrefixHash() 186 rctBaseHash := tx.RctSignature.BaseHash() 187 rctPrunableHash := tx.RctSignature.PrunableHash() 188 result = crypto.Hash(crypto.Keccak256(prefixHash[:], rctBaseHash[:], rctPrunableHash[:])) 189 default: 190 panic("Transaction version unknown") 191 192 } 193 194 return 195 } 196 197 func (tx *Transaction) GetPrefixHash() (result crypto.Hash) { 198 result = crypto.Keccak256(tx.SerializeHeader()) 199 return result 200 } 201 202 // returns whether the tx is coinbase 203 func (tx *Transaction) IsCoinbase() (result bool) { 204 switch tx.Vin[0].(type) { 205 case Txin_gen: 206 return true 207 default: 208 return false 209 } 210 } 211 212 func (tx *Transaction) DeserializeHeader(buf []byte) (err error) { 213 214 Key_offset_count := uint64(0) // used to calculate expected signatures in v1 215 216 Mixin := -1 217 218 tx.Clear() // clear existing 219 220 //Mixin_count := 0 // for signature purpose 221 222 done := 0 223 tx.Version, done = binary.Uvarint(buf) 224 if done <= 0 { 225 return fmt.Errorf("Invalid Version in Transaction\n") 226 } 227 228 if tx.Version != 2 { 229 return fmt.Errorf("Transaction version not equal to 2 \n") 230 } 231 rlog.Tracef(10, "transaction version %d\n", tx.Version) 232 233 buf = buf[done:] 234 tx.Unlock_Time, done = binary.Uvarint(buf) 235 if done <= 0 { 236 return fmt.Errorf("Invalid Unlock_Time in Transaction\n") 237 } 238 buf = buf[done:] 239 240 // parse vin length 241 vin_length, done := binary.Uvarint(buf) 242 if done <= 0 { 243 return fmt.Errorf("Invalid Vin length in Transaction\n") 244 } 245 buf = buf[done:] 246 247 if vin_length == 0 { 248 return fmt.Errorf("Vin input cannot be zero in Transaction\n") 249 250 } 251 252 rlog.Tracef(10, "vin length %d\n", vin_length) 253 254 for i := uint64(0); i < vin_length; i++ { 255 256 vin_type := buf[0] 257 258 buf = buf[1:] // consume 1 more byte 259 260 rlog.Tracef(10, "Processing i %d vin_type %s hex %x\n", i, TX_IN_NAME[vin_type], buf[:40]) 261 262 switch vin_type { 263 case TXIN_GEN: 264 rlog.Tracef(10, "Coinbase transaction\n") 265 266 var current_vin Txin_gen 267 current_vin.Height, done = binary.Uvarint(buf) 268 if done <= 0 { 269 return fmt.Errorf("Invalid Height for Txin_gen vin in Transaction\n") 270 } 271 buf = buf[done:] 272 tx.Vin = append(tx.Vin, current_vin) 273 274 /*case TXIN_TO_SCRIPT: 275 panic("TXIN_TO_SCRIPT not implemented") 276 case TXIN_TO_SCRIPTHASH: 277 panic("TXIN_TO_SCRIPTHASH not implemented") 278 */ 279 case TXIN_TO_KEY: 280 var current_vin Txin_to_key 281 282 // parse Amount 283 current_vin.Amount, done = binary.Uvarint(buf) 284 if done <= 0 { 285 return fmt.Errorf("Invalid Amount for Txin_to_key vin in Transaction\n") 286 } 287 buf = buf[done:] 288 289 if current_vin.Amount != 0 { 290 return fmt.Errorf("V2 Transactions have all input amount as zero\n") 291 } 292 293 //fmt.Printf("Remaining data %x\n", buf[:20]); 294 295 mixin_count, done := binary.Uvarint(buf) 296 if done <= 0 { 297 return fmt.Errorf("Invalid offset_count for Txin_to_key vin in Transaction\n") 298 } 299 buf = buf[done:] 300 301 // safety check mixin cannot be larger than say x 302 // do we need a safety check 303 if mixin_count > 4000 { 304 return fmt.Errorf("Mixin cannot be larger than 400\n") 305 } 306 307 if Mixin < 0 { 308 Mixin = int(mixin_count) 309 } 310 311 if Mixin != int(mixin_count) { // all vins must have same mixin 312 return fmt.Errorf("Different mixin in Transaction\n") 313 314 } 315 316 //Mixin_input_count += Mixin 317 318 for j := uint64(0); j < mixin_count; j++ { 319 offset, done := binary.Uvarint(buf) 320 if done <= 0 { 321 return fmt.Errorf("Invalid key offset for Txin_to_key vin in Transaction\n") 322 } 323 buf = buf[done:] 324 current_vin.Key_offsets = append(current_vin.Key_offsets, offset) 325 } 326 327 Key_offset_count += mixin_count 328 329 copy(current_vin.K_image[:], buf[:32]) // copy key image 330 331 buf = buf[32:] // consume key image bytes 332 333 tx.Vin = append(tx.Vin, current_vin) 334 335 // panic("TXIN_TO_KEY not implemented") 336 337 default: 338 return fmt.Errorf("Invalid VIN type in Transaction") 339 340 } 341 342 } 343 344 //fmt.Printf("TX before vout %+v\n", tx) 345 346 //fmt.Printf("buf before vout length %x\n", buf) 347 vout_length, done := binary.Uvarint(buf) 348 if done <= 0 { 349 return fmt.Errorf("Invalid Vout length in Transaction\n") 350 } 351 buf = buf[done:] 352 353 if vout_length == 0 { 354 return fmt.Errorf("Vout cannot be zero in Transaction\n") 355 } 356 357 for i := uint64(0); i < vout_length; i++ { 358 359 // amount is decoded earlier 360 361 amount, done := binary.Uvarint(buf) 362 if done <= 0 { 363 return fmt.Errorf("Invalid Amount in Transaction\n") 364 } 365 buf = buf[done:] 366 367 /*if amount != 0 { 368 return fmt.Errorf("V2 Transactions have all output amount as zero\n") 369 }*/ 370 371 // decode vout type 372 373 vout_type := buf[0] 374 buf = buf[1:] // consume 1 more byte 375 376 rlog.Tracef(10, "Vout Amount length %d vout type %d \n", amount, vout_type) 377 378 /*if tx.Version == 2 && amount != 0 { // version 2 must have amount 0 379 return fmt.Errorf("Amount must be zero in Transaction\n") 380 }*/ 381 382 switch vout_type { 383 /*case TXOUT_TO_SCRIPT: 384 //fmt.Printf("out to script\n") 385 panic("TXOUT_TO_SCRIPT not implemented") 386 case TXOUT_TO_SCRIPTHASH: 387 //fmt.Printf("out to scripthash\n") 388 var current_vout Txout_to_scripthash 389 copy(current_vout.Hash[:], buf[0:32]) 390 tx.Vout = append(tx.Vout, Tx_out{Amount: amount, Target: current_vout}) 391 392 buf = buf[32:] 393 394 //panic("TXOUT_TO_SCRIPTHASH not implemented") 395 */ 396 case TXOUT_TO_KEY: 397 //fmt.Printf("out to key\n") 398 399 var current_vout Txout_to_key 400 401 copy(current_vout.Key[:], buf[0:32]) 402 buf = buf[32:] 403 404 //Mixin_input_count++ 405 406 tx.Vout = append(tx.Vout, Tx_out{Amount: amount, Target: current_vout}) 407 408 default: 409 return fmt.Errorf("Invalid VOUT type in Transaction\n") 410 411 } 412 413 } 414 415 // fmt.Printf("Extra %x\n", buf) 416 // decode extra 417 extra_length, done := binary.Uvarint(buf) 418 if done <= 0 { 419 return fmt.Errorf("Invalid Extra length in Transaction\n") 420 } 421 buf = buf[done:] 422 423 // BUG extra needs to be processed in a loop till we load all extra fields 424 425 //tx.ExtraType = buf[0] 426 // buf = buf[1:] // consume 1 more byte 427 428 // extra_length-- 429 430 rlog.Tracef(8, "extra len %d have %d \n", extra_length, len(buf)) 431 tx.Extra = buf[:extra_length] 432 433 // whatever is leftover is signature 434 buf = buf[extra_length:] // consume more bytes 435 436 switch tx.Version { 437 /*case 1: // old style signatures, load value 438 for i := uint64(0); i < Key_offset_count; i++ { 439 var s Signature_v1 440 copy(s.R[:], buf[:32]) 441 copy(s.C[:], buf[32:64]) 442 tx.Signature = append(tx.Signature, s) 443 buf = buf[SIGNATURE_V1_LENGTH:] 444 } 445 */ 446 447 case 2: 448 bufreader := bytes.NewReader(buf) 449 450 Mixin -= 1 // one is ours, rest are mixin 451 452 tx.RctSignature, err = ringct.ParseRingCtSignature(bufreader, len(tx.Vin), len(tx.Vout), Mixin) 453 if err != nil { 454 return err 455 } 456 457 // we can expand and set some bulletproofs for later on verification, 458 /* 459 if tx.RctSignature.Get_Sig_Type() == ringct.RCTTypeSimpleBulletproof || tx.RctSignature.Get_Sig_Type() == ringct.RCTTypeFullBulletproof { 460 461 if len(tx.RctSignature.ECdhInfo) != len(tx.Vout) ||len(tx.Vout) != len(tx.RctSignature.BulletSigs) { 462 return fmt.Errorf("Invalid Bulletproof signature in Transaction\n") 463 } 464 for i := range tx.RctSignature.ECdhInfo { 465 tx.RctSignature.BulletSigs[i].V = []crypto.Key{ crypto.Key(tx.RctSignature.OutPk[i].Mask) } 466 467 fmt.Printf("verifying BP now \t ") 468 469 if tx.RctSignature.BulletSigs[i].BULLETPROOF_Verify() { 470 fmt.Printf("Bulletproof verification done successfully") 471 }else{ 472 fmt.Printf("Bulletproof verification failed %+v \n", tx.RctSignature.BulletSigs[i]) 473 } 474 } 475 476 477 }*/ 478 479 default: 480 return fmt.Errorf("Version 1 is NOT supported\n") 481 } 482 483 /* we must deserialize signature some where else 484 485 486 487 488 //fmt.Printf("extra bytes %x\n",buf) 489 490 //fmt.Printf("signature len %d should be %d\n",len(buf),len(tx.Vin)*SIGNATURE_V1_LENGTH) 491 fmt.Printf("signature len %d should be %d\n",len(buf),Key_offset_count*SIGNATURE_V1_LENGTH) 492 493 494 495 switch tx.Version { 496 case 1 : // old style signatures, load value 497 for i := uint64(0); i < Key_offset_count;i++{ 498 var s Signature_v1 499 copy(s.R[:],buf[:32]) 500 copy(s.C[:],buf[32:64]) 501 tx.Signature = append(tx.Signature, s) 502 buf = buf[SIGNATURE_V1_LENGTH:] 503 } 504 case 2: 505 tx.Signature_RCT.Type, done = binary.Uvarint(buf) 506 if done <= 0 { 507 return fmt.Errorf("Invalid RCT signature in Transaction\n") 508 } 509 buf = buf[done:] 510 511 switch tx.Signature_RCT.Type { 512 513 case 0 : // no signature break 514 515 case 1 : 516 517 518 519 tx.Signature_RCT.TxnFee, done = binary.Uvarint(buf) 520 if done <= 0 { 521 return fmt.Errorf("Invalid txn fee in Transaction\n") 522 } 523 buf = buf[done:] 524 525 fmt.Printf("RCT signature type %d Fee %d\n",tx.Signature_RCT.Type, tx.Signature_RCT.TxnFee) 526 527 528 // how many masked inputs depends on number of masked outouts 529 for i := (0); i < len(tx.Vout);i++{ 530 // read masked input 531 var info ECDHinfo 532 copy(info.Mask[:], buf[0:32]) 533 copy(info.Amount[:], buf[32:64]) 534 tx.Signature_RCT.Amounts = append(tx.Signature_RCT.Amounts, info) 535 buf = buf[64:] 536 } 537 538 // now parse the public keys 539 for i := (0); i < len(tx.Vout);i++{ 540 // read masked input 541 var tmp [32]byte 542 copy(tmp[:], buf[0:32]) 543 544 tx.Signature_RCT.OutPK = append(tx.Signature_RCT.OutPK, tmp) 545 buf = buf[32:] 546 } 547 548 case 2 : // panic("ringct type 2 currently not handled") 549 550 551 default: 552 panic("unknown signature style") 553 554 555 } 556 557 default: 558 panic("unknown transaction version \n") 559 560 561 562 } 563 564 565 */ 566 567 rlog.Tracef(8, "TX deserialized %+v\n", tx) 568 569 /* 570 data.Local_time = binary.LittleEndian.Uint64( buf[24:], ) 571 572 data.Local_Port = binary.LittleEndian.Uint32( buf[41:]) 573 574 _ = data.Network_UUID.UnmarshalBinary(buf[58:58+16]) 575 576 577 data.Peer_ID = binary.LittleEndian.Uint64( buf[83:] ) 578 */ 579 return nil //fmt.Errorf("Done Transaction\n") 580 581 } 582 583 // calculated prefi has signature 584 func (tx *Transaction) Clear() { 585 // clean the transaction everything 586 tx.Version = 0 587 tx.Unlock_Time = 0 588 tx.Vin = tx.Vin[:0] 589 tx.Vout = tx.Vout[:0] 590 tx.Extra = tx.Extra[:0] 591 592 } 593 594 func (tx *Transaction) SerializeHeader() []byte { 595 596 var serialised_header bytes.Buffer 597 598 buf := make([]byte, binary.MaxVarintLen64) 599 600 n := binary.PutUvarint(buf, tx.Version) 601 serialised_header.Write(buf[:n]) 602 603 n = binary.PutUvarint(buf, tx.Unlock_Time) 604 serialised_header.Write(buf[:n]) 605 606 if len(tx.Vin) < 1 { 607 panic("No vins") 608 } 609 610 n = binary.PutUvarint(buf, uint64(len(tx.Vin))) 611 serialised_header.Write(buf[:n]) 612 613 for _, current_vin := range tx.Vin { 614 switch current_vin.(type) { 615 case Txin_gen: 616 serialised_header.WriteByte(TXIN_GEN) 617 n = binary.PutUvarint(buf, current_vin.(Txin_gen).Height) 618 serialised_header.Write(buf[:n]) 619 620 case Txin_to_key: 621 serialised_header.WriteByte(TXIN_TO_KEY) 622 n = binary.PutUvarint(buf, current_vin.(Txin_to_key).Amount) 623 serialised_header.Write(buf[:n]) 624 625 // number of Ring member 626 n = binary.PutUvarint(buf, uint64(len(current_vin.(Txin_to_key).Key_offsets))) 627 serialised_header.Write(buf[:n]) 628 629 // write ring members 630 for _, offset := range current_vin.(Txin_to_key).Key_offsets { 631 n = binary.PutUvarint(buf, offset) 632 serialised_header.Write(buf[:n]) 633 634 } 635 636 // dump key image, interface needs a concrete type feor accessing array 637 cvin := current_vin.(Txin_to_key) 638 serialised_header.Write(cvin.K_image[:]) 639 640 } 641 } 642 643 // time to serialize vouts 644 645 if len(tx.Vout) < 1 { 646 panic("No vout") 647 } 648 649 n = binary.PutUvarint(buf, uint64(len(tx.Vout))) 650 serialised_header.Write(buf[:n]) 651 652 for _, current_vout := range tx.Vout { 653 654 // dump amount 655 n := binary.PutUvarint(buf, current_vout.Amount) 656 serialised_header.Write(buf[:n]) 657 658 switch current_vout.Target.(type) { 659 case Txout_to_key: 660 661 serialised_header.WriteByte(TXOUT_TO_KEY) 662 663 target := current_vout.Target.(Txout_to_key) 664 serialised_header.Write(target.Key[:]) 665 666 //serialised_header.Write(current_vout.Target.(Txout_to_key).Key[:]) 667 668 default: 669 panic("This type of Txout not suppported") 670 671 } 672 } 673 674 // dump any extras 675 n = binary.PutUvarint(buf, uint64(len(tx.Extra))) 676 serialised_header.Write(buf[:n]) 677 678 //rlog.Tracef("Extra length %d while serializing\n ", len(tx.Extra)) 679 680 serialised_header.Write(tx.Extra[:]) 681 682 return serialised_header.Bytes() 683 684 } 685 686 // serialize entire transaction include signature 687 func (tx *Transaction) Serialize() []byte { 688 689 header_bytes := tx.SerializeHeader() 690 base_bytes := tx.RctSignature.SerializeBase() 691 prunable := tx.RctSignature.SerializePrunable() 692 693 buf := append(header_bytes, base_bytes...) 694 buf = append(buf, prunable...) 695 696 return buf 697 698 } 699 700 /* 701 702 func (tx *Transaction) IsCoinbase() (result bool){ 703 704 // check whether the type is Txin.get 705 706 if len(tx.Vin) != 0 { // coinbase transactions have no vin 707 return 708 } 709 710 if tx.Vout[0].(Target) != 0 { // coinbase transactions have no vin 711 return 712 } 713 714 715 }*/