func TestCreateUnspents(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // 1 out tx := coin.Transaction{} tx.PushOutput(genAddress, 11e6, 255) bh := coin.BlockHeader{ Time: tNow(), BkSeq: uint64(1), } uxout := coin.CreateUnspents(bh, tx) assert.Equal(t, len(uxout), 1) assertValidUnspents(t, bh, tx, uxout) // Multiple outs. Should work regardless of validity tx = coin.Transaction{} ux := makeUxOut(t) tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, 100, 150) tx.PushOutput(genAddress, 200, 77) bh.BkSeq++ uxout = coin.CreateUnspents(bh, tx) assert.Equal(t, len(uxout), 2) assertValidUnspents(t, bh, tx, uxout) // No outs tx = coin.Transaction{} uxout = coin.CreateUnspents(bh, tx) assertValidUnspents(t, bh, tx, uxout) }
// InjectTxn adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (utp *UnconfirmedTxnPool) InjectTxn(bc *Blockchain, t coin.Transaction) (error, bool) { if err := t.Verify(); err != nil { return err, false } if err := VerifyTransactionFee(bc, &t); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := utp.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now utp.Txns[h] = ut return nil, true } // Add txn to index unspent := bc.GetUnspent() utp.Txns[h] = utp.createUnconfirmedTxn(unspent, t) // Add predicted unspents utp.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
// Adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (self *UnconfirmedTxnPool) RecordTxn(bc *coin.Blockchain, t coin.Transaction, addrs map[coin.Address]byte, maxSize int, burnFactor uint64) (error, bool) { if err := VerifyTransaction(bc, &t, maxSize, burnFactor); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := self.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now self.Txns[h] = ut return nil, true } // Add txn to index self.Txns[h] = self.createUnconfirmedTxn(&bc.Unspent, t, addrs) // Add predicted unspents self.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
// ExecuteBlock Attempts to append block to blockchain. Returns the UxOuts created, // and an error if the block is invalid. func (bc *Blockchain) ExecuteBlock(b *coin.Block) (coin.UxArray, error) { var uxs coin.UxArray err := bc.verifyBlock(*b) if err != nil { return nil, err } txns := b.Body.Transactions for _, tx := range txns { // Remove spent outputs bc.unspent.DelMultiple(tx.In) // Create new outputs txUxs := coin.CreateUnspents(b.Head, tx) for i := range txUxs { bc.unspent.Add(txUxs[i]) } uxs = append(uxs, txUxs...) } b.Head.PrevHash = bc.head bc.addBlock(b) // update the head bc.head = b.HashHeader() bc.notify(*b) return uxs, nil }
// VerifyTransaction checks that the inputs to the transaction exist, // that the transaction does not create or destroy coins and that the // signatures on the transaction are valid func (bc Blockchain) VerifyTransaction(tx coin.Transaction) error { //CHECKLIST: DONE: check for duplicate ux inputs/double spending //CHECKLIST: DONE: check that inputs of transaction have not been spent //CHECKLIST: DONE: check there are no duplicate outputs // Q: why are coin hours based on last block time and not // current time? // A: no two computers will agree on system time. Need system clock // indepedent timing that everyone agrees on. fee values would depend on // local clock // Check transaction type and length // Check for duplicate outputs // Check for duplicate inputs // Check for invalid hash // Check for no inputs // Check for no outputs // Check for non 1e6 multiple coin outputs // Check for zero coin outputs // Check valid looking signatures if err := tx.Verify(); err != nil { return err } uxIn, err := bc.unspent.GetMultiple(tx.In) if err != nil { return err } // Checks whether ux inputs exist, // Check that signatures are allowed to spend inputs if err := tx.VerifyInput(uxIn); err != nil { return err } // Get the UxOuts we expect to have when the block is created. uxOut := coin.CreateUnspents(bc.Head().Head, tx) // Check that there are any duplicates within this set if uxOut.HasDupes() { return errors.New("Duplicate unspent outputs in transaction") } if DebugLevel1 { // Check that new unspents don't collide with existing. This should // also be checked in verifyTransactions for i := range uxOut { if bc.unspent.Has(uxOut[i].Hash()) { return errors.New("New unspent collides with existing unspent") } } } // Check that no coins are lost, and sufficient coins and hours are spent err = coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut) if err != nil { return err } return nil }
// AllIncommingOutputs returns all predicted incomming outputs. func (utp *UnconfirmedTxnPool) AllIncommingOutputs(bh coin.BlockHeader) []ReadableOutput { outs := []ReadableOutput{} for _, tx := range utp.Txns { uxOuts := coin.CreateUnspents(bh, tx.Txn) for _, ux := range uxOuts { outs = append(outs, NewReadableOutput(ux)) } } return outs }
func TestSpendsForAddresses(t *testing.T) { up := NewUnconfirmedTxnPool() unspent := coin.NewUnspentPool() addrs := make(map[cipher.Address]byte, 0) n := 4 useAddrs := make([]cipher.Address, n) for i, _ := range useAddrs { useAddrs[i] = makeAddress() } useAddrs[1] = useAddrs[0] for _, a := range useAddrs { addrs[a] = byte(1) } // Make confirmed transactions to add to unspent pool uxs := make(coin.UxArray, 0) for i := 0; i < n; i++ { txn := coin.Transaction{} txn.PushInput(randSHA256()) txn.PushOutput(useAddrs[i], 10e6, 1000) uxa := coin.CreateUnspents(coin.BlockHeader{}, txn) for _, ux := range uxa { unspent.Add(ux) } uxs = append(uxs, uxa...) } assert.Equal(t, len(uxs), 4) // Make unconfirmed txns that spend those unspents for i := 0; i < n; i++ { txn := coin.Transaction{} txn.PushInput(uxs[i].Hash()) txn.PushOutput(makeAddress(), 10e6, 1000) ut := UnconfirmedTxn{ Txn: txn, } up.Txns[ut.Hash()] = ut } // Now look them up assert.Equal(t, len(addrs), 3) assert.Equal(t, len(up.Txns), 4) auxs := up.SpendsForAddresses(&unspent, addrs) assert.Equal(t, len(auxs), 3) assert.Equal(t, len(auxs[useAddrs[0]]), 2) assert.Equal(t, len(auxs[useAddrs[2]]), 1) assert.Equal(t, len(auxs[useAddrs[3]]), 1) assert.Equal(t, auxs[useAddrs[0]], coin.UxArray{uxs[0], uxs[1]}) assert.Equal(t, auxs[useAddrs[2]], coin.UxArray{uxs[2]}) assert.Equal(t, auxs[useAddrs[3]], coin.UxArray{uxs[3]}) }
func (bc *Blockchain) updateUnspent(b coin.Block) error { if err := bc.verifyBlock(b); err != nil { return err } txns := b.Body.Transactions for _, tx := range txns { // Remove spent outputs bc.unspent.DelMultiple(tx.In) // Create new outputs txUxs := coin.CreateUnspents(b.Head, tx) for i := range txUxs { bc.unspent.Add(txUxs[i]) } } return nil }
func getUx(bc historydb.Blockchainer, seq uint64, txID cipher.SHA256, addr string) (*coin.UxOut, error) { b := bc.GetBlockInDepth(seq) if b == nil { return nil, fmt.Errorf("no block in depth:%v", seq) } tx, ok := b.GetTransaction(txID) if !ok { return nil, errors.New("found transaction failed") } uxs := coin.CreateUnspents(b.Head, tx) for _, u := range uxs { if u.Body.Address.String() == addr { return &u, nil } } return nil, nil }
// ProcessBlock will index the transaction, outputs,etc. func (hd *HistoryDB) ProcessBlock(b *coin.Block) error { // index the transactions for _, t := range b.Body.Transactions { tx := Transaction{ Tx: t, BlockSeq: b.Seq(), } if err := hd.txns.Add(&tx); err != nil { return err } // handle tx in, genesis transaction's vin is empty, so should be ignored. if b.Seq() > 0 { for _, in := range t.In { o, err := hd.outputs.Get(in) if err != nil { return err } // update output's spent block seq and txid. o.SpentBlockSeq = b.Seq() o.SpentTxID = t.Hash() if err := hd.outputs.Set(*o); err != nil { return err } } } // handle the tx out uxArray := coin.CreateUnspents(b.Head, t) for _, ux := range uxArray { uxOut := UxOut{ Out: ux, } if err := hd.outputs.Set(uxOut); err != nil { return err } if err := hd.addrUx.Add(ux.Body.Address, ux.Hash()); err != nil { return err } } } return nil }
func (fbc *fakeBlockchain) ExecuteBlock(b *coin.Block) (coin.UxArray, error) { var uxs coin.UxArray txns := b.Body.Transactions for _, tx := range txns { // Remove spent outputs fbc.unspent.DelMultiple(tx.In) // Create new outputs txUxs := coin.CreateUnspents(b.Head, tx) for i := range txUxs { fbc.unspent.Add(txUxs[i]) } uxs = append(uxs, txUxs...) } b.Head.PrevHash = fbc.Head().HashHeader() fbc.blocks = append(fbc.blocks, *b) return uxs, nil }
func TestCreateGenesisBlock(t *testing.T) { ft := FakeTree{} now := tNow() b := NewBlockchain(&ft, nil) b.CreateGenesisBlock(genAddress, _genCoins, now) // gb := createGenesisBlock(genAddress, now, _genCoins) assert.Equal(t, b.Time(), now) assert.Equal(t, b.Head().Head.BkSeq, uint64(0)) assert.Equal(t, len(b.Head().Body.Transactions), 1) assert.Equal(t, len(b.Head().Body.Transactions[0].Out), 1) assert.Equal(t, len(b.Head().Body.Transactions[0].In), 0) txn := b.Head().Body.Transactions[0] txo := txn.Out[0] assert.Equal(t, txo.Address, genAddress) assert.Equal(t, txo.Coins, _genCoins) assert.Equal(t, txo.Hours, _genCoins) assert.Equal(t, len(b.GetUnspent().Pool), 1) ux := b.GetUnspent().Array()[0] assert.Equal(t, ux.Head.BkSeq, uint64(0)) assert.Equal(t, ux.Head.Time, now) assert.Equal(t, ux.Head.Time, b.Head().Head.Time) assert.Equal(t, ux.Body.SrcTransaction, txn.InnerHash) assert.Equal(t, ux.Body.Address, genAddress) assert.Equal(t, ux.Body.Coins, _genCoins) assert.Equal(t, txo.Coins, ux.Body.Coins) assert.Equal(t, txo.Hours, ux.Body.Hours) // 1 hour per coin, at init assert.Equal(t, ux.Body.Hours, _genCoins) h := cipher.Merkle([]cipher.SHA256{b.Head().Body.Transactions[0].Hash()}) assert.Equal(t, b.Head().Head.BodyHash, h) assert.Equal(t, b.Head().Head.PrevHash, cipher.SHA256{}) // TODO -- check valid snapshot assert.NotEqual(t, b.Head().Head.UxHash, [4]byte{}) expect := coin.CreateUnspents(b.Head().Head, txn) expect.Sort() have := b.GetUnspent().Array() have.Sort() assert.Equal(t, expect, have) // Panicing assert.Panics(t, func() { b.CreateGenesisBlock(genAddress, _genCoins, _genTime) }) }
func assertValidUnspent(t *testing.T, bc *coin.Blockchain, unspent TxnUnspents, tx coin.Transaction) { expect := coin.CreateUnspents(bc.Head().Head, tx) assert.NotEqual(t, len(expect), 0) sum := 0 for _, uxs := range unspent { sum += len(uxs) } assert.Equal(t, len(expect), sum) uxs := unspent[tx.Hash()] for _, ux := range expect { found := false for _, u := range uxs { if u.Hash() == ux.Hash() { found = true break } } assert.True(t, found) } }
func assertExecuteBlock(t *testing.T, bc *Blockchain, b coin.Block, tx coin.Transaction) { seq := bc.Head().Head.BkSeq nUxs := len(bc.GetUnspent().Pool) uxs, err := bc.ExecuteBlock(&b) assert.Nil(t, err) assert.Equal(t, bc.Head().Head.BkSeq, seq+1) assert.Equal(t, len(uxs), len(tx.Out)) assert.False(t, uxs.HasDupes()) assert.Equal(t, len(bc.GetUnspent().Pool), nUxs+len(tx.Out)-len(tx.In)) for _, ux := range uxs { assert.True(t, bc.GetUnspent().Has(ux.Hash())) ux2, ok := bc.GetUnspent().Get(ux.Hash()) assert.True(t, ok) assert.Equal(t, ux, ux2) } uxs2 := coin.CreateUnspents(bc.Head().Head, tx) assert.Equal(t, len(uxs2), len(uxs)) for i, u := range uxs2 { assert.Equal(t, u.Body, uxs[i].Body) } }
func testRefresh(t *testing.T, mv *Visor, refresh func(checkPeriod, maxAge time.Duration)) { up := mv.Unconfirmed // Add a transaction that is invalid, but will not be checked yet // Add a transaction that is invalid, and will be checked and removed invalidTxUnchecked, err := makeValidTxn(mv) assert.Nil(t, err) invalidTxChecked, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, invalidTxUnchecked.Verify()) assert.Nil(t, invalidTxChecked.Verify()) // Invalidate it by spending the output that this txn references invalidator, err := makeValidTxn(mv) assert.Nil(t, err) err, known := up.RecordTxn(mv.blockchain, invalidator, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) assert.Equal(t, len(up.Txns), 1) _, err = mv.CreateAndExecuteBlock() assert.Nil(t, err) assert.Equal(t, len(up.Txns), 0) assert.NotNil(t, mv.blockchain.VerifyTransaction(invalidTxUnchecked)) assert.NotNil(t, mv.blockchain.VerifyTransaction(invalidTxChecked)) invalidUtxUnchecked := UnconfirmedTxn{ Txn: invalidTxUnchecked, Received: util.Now(), Checked: util.Now(), Announced: util.ZeroTime(), } invalidUtxChecked := invalidUtxUnchecked invalidUtxChecked.Txn = invalidTxChecked invalidUtxUnchecked.Checked = util.Now().Add(time.Hour) invalidUtxChecked.Checked = util.Now().Add(-time.Hour) up.Txns[invalidUtxUnchecked.Hash()] = invalidUtxUnchecked up.Txns[invalidUtxChecked.Hash()] = invalidUtxChecked assert.Equal(t, len(up.Txns), 2) uncheckedHash := invalidTxUnchecked.Hash() checkedHash := invalidTxChecked.Hash() up.Unspent[uncheckedHash] = coin.CreateUnspents(coin.BlockHeader{}, invalidTxUnchecked) up.Unspent[checkedHash] = coin.CreateUnspents(coin.BlockHeader{}, invalidTxChecked) // Create a transaction that is valid, and will not be checked yet validTxUnchecked, err := makeValidTxn(mv) assert.Nil(t, err) // Create a transaction that is valid, and will be checked validTxChecked, err := makeValidTxn(mv) assert.Nil(t, err) // Create a transaction that is expired validTxExpired, err := makeValidTxn(mv) assert.Nil(t, err) // Add the transaction that is valid, and will not be checked yet err, known = up.RecordTxn(mv.blockchain, validTxUnchecked, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) validUtxUnchecked := up.Txns[validTxUnchecked.Hash()] validUtxUnchecked.Checked = util.Now().Add(time.Hour) up.Txns[validUtxUnchecked.Hash()] = validUtxUnchecked assert.Equal(t, len(up.Txns), 3) // Add the transaction that is valid, and will be checked err, known = up.RecordTxn(mv.blockchain, validTxChecked, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) validUtxChecked := up.Txns[validTxChecked.Hash()] validUtxChecked.Checked = util.Now().Add(-time.Hour) up.Txns[validUtxChecked.Hash()] = validUtxChecked assert.Equal(t, len(up.Txns), 4) // Add the transaction that is expired err, known = up.RecordTxn(mv.blockchain, validTxExpired, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) validUtxExpired := up.Txns[validTxExpired.Hash()] validUtxExpired.Received = util.Now().Add(-time.Hour) up.Txns[validTxExpired.Hash()] = validUtxExpired assert.Equal(t, len(up.Txns), 5) // Pre-sanity check assert.Equal(t, len(up.Unspent), 5) for _, uxs := range up.Unspent { assert.Equal(t, len(uxs), 2) } assert.Equal(t, len(up.Txns), 5) // Refresh checkPeriod := time.Second * 2 maxAge := time.Second * 4 refresh(checkPeriod, maxAge) // All utxns that are unchecked should be exactly the same assert.Equal(t, up.Txns[validUtxUnchecked.Hash()], validUtxUnchecked) assert.Equal(t, up.Txns[invalidUtxUnchecked.Hash()], invalidUtxUnchecked) // The valid one that is checked should have its checked status updated validUtxCheckedUpdated := up.Txns[validUtxChecked.Hash()] assert.True(t, validUtxCheckedUpdated.Checked.After(validUtxChecked.Checked)) validUtxChecked.Checked = validUtxCheckedUpdated.Checked assert.Equal(t, validUtxChecked, validUtxCheckedUpdated) // The invalid checked one and the expired one should be removed _, ok := up.Txns[invalidUtxChecked.Hash()] assert.False(t, ok) _, ok = up.Txns[validUtxExpired.Hash()] assert.False(t, ok) // Also, the unspents should have 2 * nRemaining assert.Equal(t, len(up.Unspent), 3) for _, uxs := range up.Unspent { assert.Equal(t, len(uxs), 2) } assert.Equal(t, len(up.Txns), 3) }
func TestVerifyTransaction(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) gb := bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // Genesis block is not valid by normal standards assert.NotNil(t, bc.VerifyTransaction(gb.Body.Transactions[0])) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Valid txn tx, _ := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) assert.Nil(t, bc.VerifyTransaction(tx)) assert.Equal(t, bc.Len(), uint64(3)) // Failure, spending unknown output tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) tx.Sigs = nil tx.In[0] = cipher.SHA256{} tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Unspent output does not exist") assert.Equal(t, bc.Len(), uint64(3)) // Failure, duplicate input tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) tx.Sigs = nil tx.In = append(tx.In, tx.In[0]) tx.SignInputs([]cipher.SecKey{genSecret, genSecret}) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Duplicate spend") assert.Equal(t, bc.Len(), uint64(3)) // Failure, zero coin output tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) tx.Sigs = nil tx.PushOutput(genAddress, 0, 100) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Zero coin output") // Failure, hash collision with unspents tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) uxOut := coin.CreateUnspents(bc.Head().Head, tx) bc.GetUnspent().Add(uxOut[0]) assertError(t, bc.VerifyTransaction(tx), "New unspent collides with existing unspent") // Failure, not spending enough coins tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) tx.PushOutput(genAddress, 10e6, 100) tx.Sigs = nil tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Insufficient coins") // Failure, spending outputs we don't own _, s := cipher.GenerateKeyPair() tx = coin.Transaction{} for _, u := range bc.GetUnspent().Pool { if u.Body.Address != genAddress { ux = u break } } assert.NotEqual(t, ux.Body.Address, genAddress) tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, ux.Body.Coins, ux.Body.Hours) tx.SignInputs([]cipher.SecKey{s}) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Signature not valid for output being spent") // Failure, wrong signature for txn hash tx = coin.Transaction{} tx.PushInput(ux.Hash()) tx.SignInputs([]cipher.SecKey{genSecret}) tx.PushOutput(genAddress, ux.Body.Coins, ux.Body.Hours) tx.UpdateHeader() assertError(t, bc.VerifyTransaction(tx), "Signature not valid for output being spent") }
func TestVerifyTransactionSpending(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // Overspending hours tx := coin.Transaction{} uxs := bc.GetUnspent().Array() tx.PushInput(uxs[0].Hash()) tx.PushOutput(genAddress, 1e6, uxs[0].Body.Hours) tx.PushOutput(genAddress, uxs[0].Body.Coins-1e6, 1) uxIn, err := bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut := coin.CreateUnspents(bc.Head().Head, tx) assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Insufficient coin hours") // add block to blockchain. _, ux := addBlockToBlockchain(t, bc) // addBlockToBlockchain(t, bc) // Valid tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) assert.Nil(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut)) // Destroying coins tx = coin.Transaction{} tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, 1e6, 100) tx.PushOutput(genAddress, 10e6, 100) uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) err = coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut) assert.NotNil(t, err) assert.Equal(t, err.Error(), "Transactions may not create or destroy coins") assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Transactions may not create or destroy coins") // Insufficient coins tx = coin.Transaction{} tx.PushInput(ux.Hash()) p, s := cipher.GenerateKeyPair() a := cipher.AddressFromPubKey(p) coins := ux.Body.Coins assert.True(t, coins > 1e6) tx.PushOutput(a, 1e6, 100) tx.PushOutput(genAddress, coins-1e6, 100) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) uxs, err = bc.ExecuteBlock(&b) assert.Nil(t, err) tx = coin.Transaction{} tx.PushInput(uxs[0].Hash()) tx.PushOutput(a, 10e6, 1) tx.SignInputs([]cipher.SecKey{s}) tx.UpdateHeader() uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Insufficient coins") }