// NewTx returns a new instance of a transaction given an underlying // wire.MsgTx. See Tx. func NewTx(msgTx *wire.MsgTx) *Tx { return &Tx{ hash: msgTx.TxSha(), msgTx: msgTx, txTree: TxTreeUnknown, txIndex: TxIndexUnknown, } }
// NewTxFromReader returns a new instance of a transaction given a // Reader to deserialize the transaction. See Tx. func NewTxFromReader(r io.Reader) (*Tx, error) { // Deserialize the bytes into a MsgTx. var msgTx wire.MsgTx err := msgTx.Deserialize(r) if err != nil { return nil, err } t := Tx{ hash: msgTx.TxSha(), msgTx: &msgTx, txTree: TxTreeUnknown, txIndex: TxIndexUnknown, } return &t, nil }
// NewTxDeepTxIns is used to deep copy a transaction, maintaining the old // pointers to the TxOuts while replacing the old pointers to the TxIns with // deep copies. This is to prevent races when the fraud proofs for the // transactions are set by the miner. func NewTxDeepTxIns(msgTx *wire.MsgTx) *Tx { if msgTx == nil { return nil } newMsgTx := new(wire.MsgTx) // Copy the fixed fields. newMsgTx.Version = msgTx.Version newMsgTx.LockTime = msgTx.LockTime newMsgTx.Expiry = msgTx.Expiry // Copy the TxIns deeply. for _, txIn := range msgTx.TxIn { sigScrLen := len(txIn.SignatureScript) sigScrCopy := make([]byte, sigScrLen, sigScrLen) txInCopy := new(wire.TxIn) txInCopy.PreviousOutPoint.Hash = txIn.PreviousOutPoint.Hash txInCopy.PreviousOutPoint.Index = txIn.PreviousOutPoint.Index txInCopy.PreviousOutPoint.Tree = txIn.PreviousOutPoint.Tree txInCopy.Sequence = txIn.Sequence txInCopy.ValueIn = txIn.ValueIn txInCopy.BlockHeight = txIn.BlockHeight txInCopy.BlockIndex = txIn.BlockIndex txInCopy.SignatureScript = sigScrCopy newMsgTx.AddTxIn(txIn) } // Shallow copy the TxOuts. for _, txOut := range msgTx.TxOut { newMsgTx.AddTxOut(txOut) } return &Tx{ hash: msgTx.TxSha(), msgTx: msgTx, txTree: TxTreeUnknown, txIndex: TxIndexUnknown, } }
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */ 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */ 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */ 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */ 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */ 0x1d, 0x5f, 0xac, /* |._.| */ }, }, }, LockTime: 0, Expiry: 0, } // testNetGenesisMerkleRoot is the hash of the first transaction in the genesis block // for the test network. var testNetGenesisMerkleRoot = genesisCoinbaseTxLegacy.TxSha() // testNetGenesisBlock defines the genesis block of the block chain which // serves as the public transaction ledger for the test network (version 3). var testNetGenesisBlock = wire.MsgBlock{ Header: wire.BlockHeader{ Version: 1, PrevBlock: chainhash.Hash{}, MerkleRoot: testNetGenesisMerkleRoot, Timestamp: time.Unix(1453908222, 0), // 2016-01-27 TestNet9 Bits: 0x1e00ffff, SBits: 20000000, Nonce: 0x18aea41a, }, Transactions: []*wire.MsgTx{&genesisCoinbaseTxLegacy}, }
func TestStakeInvalidationTxInsert(t *testing.T) { db, s, teardown, err := setup() defer teardown() if err != nil { t.Fatal(err) } g := makeBlockGenerator() block1Header := g.generate(dcrutil.BlockValid) block2Header := g.generate(dcrutil.BlockValid) block3Header := g.generate(0) block1Tx := wire.MsgTx{ TxOut: []*wire.TxOut{{Value: 2e8}}, } block2Tx := wire.MsgTx{ TxIn: []*wire.TxIn{ {PreviousOutPoint: wire.OutPoint{Hash: block1Tx.TxSha(), Index: 0, Tree: 0}}, }, TxOut: []*wire.TxOut{{Value: 1e8}}, } block1TxRec, err := NewTxRecordFromMsgTx(&block1Tx, time.Time{}) if err != nil { t.Fatal(err) } block2TxRec, err := NewTxRecordFromMsgTx(&block2Tx, time.Time{}) if err != nil { t.Fatal(err) } err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(wtxmgrNamespaceKey) addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey) headerData := makeHeaderDataSlice(block1Header, block2Header, block3Header) err = s.InsertMainChainHeaders(ns, addrmgrNs, headerData) if err != nil { return err } err = s.InsertMinedTx(ns, addrmgrNs, block1TxRec, &headerData[0].BlockHash) if err != nil { return err } err = s.AddCredit(ns, block1TxRec, makeBlockMeta(block1Header), 0, false, 0) if err != nil { return err } err = s.InsertMinedTx(ns, addrmgrNs, block2TxRec, &headerData[1].BlockHash) if err != nil { return err } err = s.AddCredit(ns, block2TxRec, makeBlockMeta(block2Header), 0, false, 0) if err != nil { return err } // The transaction in block 2 was inserted invalidated. There should // only be one unspent output, from block 1. bal, err := s.Balance(ns, addrmgrNs, 1, BFBalanceFullScan, true, 0) if err != nil { return err } if bal != dcrutil.Amount(block1Tx.TxOut[0].Value) { t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block1Tx.TxOut[0].Value), bal) } bal, err = s.Balance(ns, addrmgrNs, 1, BFBalanceSpendable, true, 0) if err != nil { return err } if bal != dcrutil.Amount(block1Tx.TxOut[0].Value) { t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block1Tx.TxOut[0].Value), bal) } credits, err := s.UnspentOutputs(ns) if err != nil { return err } if len(credits) != 1 { t.Errorf("Expected only 1 credit, got %v", len(credits)) return nil } if credits[0].Hash != block1Tx.TxSha() { t.Errorf("Credit hash does not match tx from block 1") return nil } if credits[0].Amount != dcrutil.Amount(block1Tx.TxOut[0].Value) { t.Errorf("Credit value does not match tx output 0 from block 1") return nil } return nil }) if err != nil { t.Error(err) } }
func TestStakeInvalidationOfTip(t *testing.T) { db, s, teardown, err := setup() defer teardown() if err != nil { t.Fatal(err) } g := makeBlockGenerator() block1Header := g.generate(dcrutil.BlockValid) block2Header := g.generate(dcrutil.BlockValid) block3Header := g.generate(0) block1Tx := wire.MsgTx{ TxOut: []*wire.TxOut{{Value: 2e8}}, } block2Tx := wire.MsgTx{ TxIn: []*wire.TxIn{ {PreviousOutPoint: wire.OutPoint{Hash: block1Tx.TxSha(), Index: 0, Tree: 0}}, }, TxOut: []*wire.TxOut{{Value: 1e8}}, } block1TxRec, err := NewTxRecordFromMsgTx(&block1Tx, time.Time{}) if err != nil { t.Fatal(err) } block2TxRec, err := NewTxRecordFromMsgTx(&block2Tx, time.Time{}) if err != nil { t.Fatal(err) } const balanceFlag = BFBalanceSpendable err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(wtxmgrNamespaceKey) addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey) err := s.InsertMemPoolTx(ns, block1TxRec) if err != nil { return err } err = s.AddCredit(ns, block1TxRec, nil, 0, false, 0) if err != nil { return err } err = s.InsertMemPoolTx(ns, block2TxRec) if err != nil { return err } err = s.AddCredit(ns, block2TxRec, nil, 0, false, 0) if err != nil { return err } bal, err := s.Balance(ns, addrmgrNs, 0, balanceFlag, false, 0) if err != nil { return err } if bal != 1e8 { t.Errorf("Wrong balance before mining either transaction: %v", bal) } headerData := makeHeaderDataSlice(block1Header, block2Header) err = s.InsertMainChainHeaders(ns, addrmgrNs, headerData) if err != nil { return err } err = s.InsertMinedTx(ns, addrmgrNs, block1TxRec, &headerData[0].BlockHash) if err != nil { return err } err = s.InsertMinedTx(ns, addrmgrNs, block2TxRec, &headerData[1].BlockHash) if err != nil { return err } // At this point there should only be one credit for the tx in block 2. bal, err = s.Balance(ns, addrmgrNs, 1, balanceFlag, false, 0) if err != nil { return err } if bal != dcrutil.Amount(block2Tx.TxOut[0].Value) { t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block2Tx.TxOut[0].Value), bal) } credits, err := s.UnspentOutputs(ns) if err != nil { return err } if len(credits) != 1 { t.Errorf("Expected only 1 credit, got %v", len(credits)) return nil } if credits[0].Hash != block2Tx.TxSha() { t.Errorf("Credit hash does match tx from block 2") return nil } if credits[0].Amount != dcrutil.Amount(block2Tx.TxOut[0].Value) { t.Errorf("Credit value does not match tx output 0 from block 2") return nil } // Add the next block header which invalidates the regular tx tree of // block 2. t.Log("Invalidating block 2") headerData = makeHeaderDataSlice(block3Header) err = s.InsertMainChainHeaders(ns, addrmgrNs, headerData) if err != nil { return err } /* d := makeHeaderData(block3Header) err = s.ExtendMainChain(ns, &d) if err != nil { return err } */ // Now the transaction in block 2 is invalidated. There should only be // one unspent output, from block 1. bal, err = s.Balance(ns, addrmgrNs, 1, balanceFlag, false, 0) if err != nil { return err } if bal != dcrutil.Amount(block1Tx.TxOut[0].Value) { t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block1Tx.TxOut[0].Value), bal) } credits, err = s.UnspentOutputs(ns) if err != nil { return err } if len(credits) != 1 { t.Errorf("Expected only 1 credit, got %v", len(credits)) return nil } if credits[0].Hash != block1Tx.TxSha() { t.Errorf("Credit hash does not match tx from block 1") return nil } if credits[0].Amount != dcrutil.Amount(block1Tx.TxOut[0].Value) { t.Errorf("Credit value does not match tx output 0 from block 1") return nil } return nil }) if err != nil { t.Error(err) } }
// DebugMsgTxString dumps a verbose message containing information about the // contents of a transaction. func DebugMsgTxString(msgTx *wire.MsgTx) string { tx := dcrutil.NewTx(msgTx) isSStx, _ := stake.IsSStx(tx) isSSGen, _ := stake.IsSSGen(tx) var sstxType []bool var sstxPkhs [][]byte var sstxAmts []int64 var sstxRules [][]bool var sstxLimits [][]uint16 if isSStx { sstxType, sstxPkhs, sstxAmts, _, sstxRules, sstxLimits = stake.GetSStxStakeOutputInfo(tx) } var buffer bytes.Buffer hash := msgTx.TxSha() str := fmt.Sprintf("Transaction hash: %v, Version %v, Locktime: %v, "+ "Expiry %v\n\n", hash, msgTx.Version, msgTx.LockTime, msgTx.Expiry) buffer.WriteString(str) str = fmt.Sprintf("==INPUTS==\nNumber of inputs: %v\n\n", len(msgTx.TxIn)) buffer.WriteString(str) for i, input := range msgTx.TxIn { str = fmt.Sprintf("Input number: %v\n", i) buffer.WriteString(str) str = fmt.Sprintf("Previous outpoint hash: %v, ", input.PreviousOutPoint.Hash) buffer.WriteString(str) str = fmt.Sprintf("Previous outpoint index: %v, ", input.PreviousOutPoint.Index) buffer.WriteString(str) str = fmt.Sprintf("Previous outpoint tree: %v \n", input.PreviousOutPoint.Tree) buffer.WriteString(str) str = fmt.Sprintf("Sequence: %v \n", input.Sequence) buffer.WriteString(str) str = fmt.Sprintf("ValueIn: %v \n", input.ValueIn) buffer.WriteString(str) str = fmt.Sprintf("BlockHeight: %v \n", input.BlockHeight) buffer.WriteString(str) str = fmt.Sprintf("BlockIndex: %v \n", input.BlockIndex) buffer.WriteString(str) str = fmt.Sprintf("Raw signature script: %x \n", input.SignatureScript) buffer.WriteString(str) sigScr, _ := txscript.DisasmString(input.SignatureScript) str = fmt.Sprintf("Disasmed signature script: %v \n\n", sigScr) buffer.WriteString(str) } str = fmt.Sprintf("==OUTPUTS==\nNumber of outputs: %v\n\n", len(msgTx.TxOut)) buffer.WriteString(str) for i, output := range msgTx.TxOut { str = fmt.Sprintf("Output number: %v\n", i) buffer.WriteString(str) coins := float64(output.Value) / 1e8 str = fmt.Sprintf("Output amount: %v atoms or %v coins\n", output.Value, coins) buffer.WriteString(str) // SStx OP_RETURNs, dump pkhs and amts committed if isSStx && i != 0 && i%2 == 1 { coins := float64(sstxAmts[i/2]) / 1e8 str = fmt.Sprintf("SStx commit amount: %v atoms or %v coins\n", sstxAmts[i/2], coins) buffer.WriteString(str) str = fmt.Sprintf("SStx commit address: %x\n", sstxPkhs[i/2]) buffer.WriteString(str) str = fmt.Sprintf("SStx address type is P2SH: %v\n", sstxType[i/2]) buffer.WriteString(str) str = fmt.Sprintf("SStx all address types is P2SH: %v\n", sstxType) buffer.WriteString(str) str = fmt.Sprintf("Voting is fee limited: %v\n", sstxLimits[i/2][0]) buffer.WriteString(str) if sstxRules[i/2][0] { str = fmt.Sprintf("Voting limit imposed: %v\n", sstxLimits[i/2][0]) buffer.WriteString(str) } str = fmt.Sprintf("Revoking is fee limited: %v\n", sstxRules[i/2][1]) buffer.WriteString(str) if sstxRules[i/2][1] { str = fmt.Sprintf("Voting limit imposed: %v\n", sstxLimits[i/2][1]) buffer.WriteString(str) } } // SSGen block/block height OP_RETURN. if isSSGen && i == 0 { blkHash, blkHeight, _ := stake.GetSSGenBlockVotedOn(tx) str = fmt.Sprintf("SSGen block hash voted on: %v, height: %v\n", blkHash, blkHeight) buffer.WriteString(str) } if isSSGen && i == 1 { vb := stake.GetSSGenVoteBits(tx) str = fmt.Sprintf("SSGen vote bits: %v\n", vb) buffer.WriteString(str) } str = fmt.Sprintf("Raw script: %x \n", output.PkScript) buffer.WriteString(str) scr, _ := txscript.DisasmString(output.PkScript) str = fmt.Sprintf("Disasmed script: %v \n\n", scr) buffer.WriteString(str) } return buffer.String() }
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */ 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */ 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */ 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */ 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */ 0x1d, 0x5f, 0xac, /* |._.| */ }, }, }, LockTime: 0, Expiry: 0, } // genesisMerkleRoot is the hash of the first transaction in the genesis block // for the main network. var genesisMerkleRoot = genesisCoinbaseTxLegacy.TxSha() var regTestGenesisCoinbaseTx = wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: chainhash.Hash{}, Index: 0xffffffff, }, SignatureScript: []byte{ 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */ 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */ 0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */ 0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */ 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */
// loadTxStore returns a transaction store loaded from a file. func loadTxStore(filename string) (blockchain.TxStore, error) { // The txstore file format is: // <num tx data entries> <tx length> <serialized tx> <blk height> // <num spent bits> <spent bits> // // All num and length fields are little-endian uint32s. The spent bits // field is padded to a byte boundary. filename = filepath.Join("testdata/", filename) fi, err := os.Open(filename) if err != nil { return nil, err } // Choose read based on whether the file is compressed or not. var r io.Reader if strings.HasSuffix(filename, ".bz2") { r = bzip2.NewReader(fi) } else { r = fi } defer fi.Close() // Num of transaction store objects. var numItems uint32 if err := binary.Read(r, binary.LittleEndian, &numItems); err != nil { return nil, err } txStore := make(blockchain.TxStore) var uintBuf uint32 for height := uint32(0); height < numItems; height++ { txD := blockchain.TxData{} // Serialized transaction length. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } serializedTxLen := uintBuf if serializedTxLen > wire.MaxBlockPayload { return nil, fmt.Errorf("Read serialized transaction "+ "length of %d is larger max allowed %d", serializedTxLen, wire.MaxBlockPayload) } // Transaction. var msgTx wire.MsgTx err = msgTx.Deserialize(r) if err != nil { return nil, err } txD.Tx = dcrutil.NewTx(&msgTx) // Transaction hash. txHash := msgTx.TxSha() txD.Hash = &txHash // Block height the transaction came from. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } txD.BlockHeight = int64(uintBuf) // Num spent bits. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } numSpentBits := uintBuf numSpentBytes := numSpentBits / 8 if numSpentBits%8 != 0 { numSpentBytes++ } // Packed spent bytes. spentBytes := make([]byte, numSpentBytes) _, err = io.ReadFull(r, spentBytes) if err != nil { return nil, err } // Populate spent data based on spent bits. txD.Spent = make([]bool, numSpentBits) for byteNum, spentByte := range spentBytes { for bit := 0; bit < 8; bit++ { if uint32((byteNum*8)+bit) < numSpentBits { if spentByte&(1<<uint(bit)) != 0 { txD.Spent[(byteNum*8)+bit] = true } } } } txStore[*txD.Hash] = &txD } return txStore, nil }
// TestCalcSignatureHash does some rudimentary testing of msg hash calculation. func TestCalcSignatureHash(t *testing.T) { tx := new(wire.MsgTx) for i := 0; i < 3; i++ { txIn := new(wire.TxIn) txIn.Sequence = 0xFFFFFFFF txIn.PreviousOutPoint.Hash = chainhash.HashFuncH([]byte{byte(i)}) txIn.PreviousOutPoint.Index = uint32(i) txIn.PreviousOutPoint.Tree = int8(0) tx.AddTxIn(txIn) } for i := 0; i < 2; i++ { txOut := new(wire.TxOut) txOut.PkScript = []byte{0x01, 0x01, 0x02, 0x03} txOut.Value = 0x0000FF00FF00FF00 tx.AddTxOut(txOut) } want, _ := hex.DecodeString("d09285b6f60c71329323bc2e76c48" + "a462cde4e1032aa8f59c55823f1722c7f4a") pops, _ := txscript.TstParseScript([]byte{0x01, 0x01, 0x02, 0x03}) // Test prefix caching. msg1, err := txscript.CalcSignatureHash(pops, txscript.SigHashAll, tx, 0, nil) if err != nil { t.Fatalf("unexpected error %v", err.Error()) } prefixHash := tx.TxSha() msg2, err := txscript.CalcSignatureHash(pops, txscript.SigHashAll, tx, 0, &prefixHash) if err != nil { t.Fatalf("unexpected error %v", err.Error()) } if !bytes.Equal(msg1, want) { t.Errorf("for sighash all sig noncached wrong msg %x given, want %x", msg1, want) } if !bytes.Equal(msg2, want) { t.Errorf("for sighash all sig cached wrong msg %x given, want %x", msg1, want) } if !bytes.Equal(msg1, msg2) { t.Errorf("for sighash all sig non-equivalent msgs %x and %x were "+ "returned when using a cached prefix", msg1, msg2) } // Move the index and make sure that we get a whole new hash, despite // using the same TxOuts. msg3, err := txscript.CalcSignatureHash(pops, txscript.SigHashAll, tx, 1, &prefixHash) if err != nil { t.Fatalf("unexpected error %v", err.Error()) } if bytes.Equal(msg1, msg3) { t.Errorf("for sighash all sig equivalent msgs %x and %x were "+ "returned when using a cached prefix but different indices", msg1, msg3) } }