func InitTransaction() coin.Transaction { var tx coin.Transaction output := cipher.MustSHA256FromHex("043836eb6f29aaeb8b9bfce847e07c159c72b25ae17d291f32125e7f1912e2a0") tx.PushInput(output) for i := 0; i < 100; i++ { addr := cipher.MustDecodeBase58Address(AddrList[i]) tx.PushOutput(addr, 1e12, 1) // 10e6*10e6 } /* seckeys := make([]cipher.SecKey, 1) seckey := "" seckeys[0] = cipher.MustSecKeyFromHex(seckey) tx.SignInputs(seckeys) */ txs := make([]cipher.Sig, 1) sig := "ed9bd7a31fe30b9e2d53b35154233dfdf48aaaceb694a07142f84cdf4f5263d21b723f631817ae1c1f735bea13f0ff2a816e24a53ccb92afae685fdfc06724de01" txs[0] = cipher.MustSigFromHex(sig) tx.Sigs = txs tx.UpdateHeader() err := tx.Verify() if err != nil { log.Panic(err) } log.Printf("signature= %s", tx.Sigs[0].Hex()) return tx }
func (fbc *fakeBlockchain) CreateGenesisBlock(genesisAddr cipher.Address, genesisCoins, timestamp uint64) coin.Block { txn := coin.Transaction{} txn.PushOutput(genesisAddr, genesisCoins, genesisCoins) body := coin.BlockBody{coin.Transactions{txn}} prevHash := cipher.SHA256{} head := coin.BlockHeader{ Time: timestamp, BodyHash: body.Hash(), PrevHash: prevHash, BkSeq: 0, Version: 0, Fee: 0, UxHash: coin.NewUnspentPool().GetUxHash(), } b := coin.Block{ Head: head, Body: body, } // b.Body.Transactions[0].UpdateHeader() fbc.blocks = append(fbc.blocks, b) ux := coin.UxOut{ Head: coin.UxHead{ Time: timestamp, BkSeq: 0, }, Body: coin.UxBody{ SrcTransaction: txn.InnerHash, //user inner hash Address: genesisAddr, Coins: genesisCoins, Hours: genesisCoins, // Allocate 1 coin hour per coin }, } fbc.unspent.Add(ux) return b }
func splitUnspent(t *testing.T, bc *Blockchain, ux coin.UxOut) coin.UxArray { tx := coin.Transaction{} hrs := ux.CoinHours(bc.Time()) if hrs < 2 { log.Panic("Not enough hours, would generate duplicate output") } assert.Equal(t, ux.Body.Address, genAddress) tx.PushInput(ux.Hash()) coinsA := ux.Body.Coins / 2 coinsB := coinsA if (ux.Body.Coins/1e6)%2 == 1 { coinsA = (ux.Body.Coins - 1e6) / 2 coinsB = coinsA + 1e6 } tx.PushOutput(genAddress, coinsA, hrs/4) tx.PushOutput(genAddress, coinsB, hrs/2) 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) assert.Equal(t, len(uxs), 2) return uxs }
func makeTransactionForChainWithHoursFee(t *testing.T, bc *Blockchain, ux coin.UxOut, sec cipher.SecKey, hours, fee uint64) (coin.Transaction, cipher.SecKey) { chrs := ux.CoinHours(bc.Time()) if chrs < hours+fee { log.Panicf("CoinHours underflow. Have %d, need at least %d", chrs, hours+fee) } assert.Equal(t, cipher.AddressFromPubKey(cipher.PubKeyFromSecKey(sec)), ux.Body.Address) knownUx, exists := bc.GetUnspent().Get(ux.Hash()) assert.True(t, exists) assert.Equal(t, knownUx, ux) tx := coin.Transaction{} tx.PushInput(ux.Hash()) p, newSec := cipher.GenerateKeyPair() addr := cipher.AddressFromPubKey(p) tx.PushOutput(addr, 1e6, hours) coinsOut := ux.Body.Coins - 1e6 if coinsOut > 0 { tx.PushOutput(genAddress, coinsOut, chrs-hours-fee) } tx.SignInputs([]cipher.SecKey{sec}) assert.Equal(t, len(tx.Sigs), 1) assert.Nil(t, cipher.ChkSig(ux.Body.Address, cipher.AddSHA256(tx.HashInner(), tx.In[0]), tx.Sigs[0])) tx.UpdateHeader() assert.Nil(t, tx.Verify()) err := bc.VerifyTransaction(tx) assert.Nil(t, err) return tx, newSec }
func TestBlockchainVerifyBlock(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) gb := bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // Genesis block not valid after the fact assert.NotNil(t, bc.verifyBlock(gb)) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Valid block tx := coin.Transaction{} tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, ux.Body.Coins, ux.CoinHours(bc.Time())) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Equal(t, len(b.Body.Transactions), 1) assert.Equal(t, len(b.Body.Transactions[0].Out), 1) assert.Nil(t, err) assert.Nil(t, bc.verifyBlock(b)) // Invalid block header b.Head.BkSeq = gb.Head.BkSeq assert.Equal(t, len(b.Body.Transactions), 1) assert.Equal(t, len(b.Body.Transactions[0].Out), 1) assertError(t, bc.verifyBlock(b), "BkSeq invalid") // Invalid transactions, makes duplicate outputs b.Head.BkSeq = bc.Head().Head.BkSeq + 1 b.Body.Transactions = append(b.Body.Transactions, b.Body.Transactions[0]) b.Head.BodyHash = b.HashBody() assertError(t, bc.verifyBlock(b), "Duplicate unspent output across transactions") }
func makeTransactionWithSecret(t *testing.T) (coin.Transaction, cipher.SecKey) { tx := coin.Transaction{} ux, s := makeUxOutWithSecret(t) tx.PushInput(ux.Hash()) tx.SignInputs([]cipher.SecKey{s}) tx.PushOutput(makeAddress(), 10e6, 100) tx.UpdateHeader() return tx, s }
// Creates a Transaction spending coins and hours from our coins func CreateSpendingTransaction(wlt wallet.Wallet, unconfirmed *UnconfirmedTxnPool, unspent *coin.UnspentPool, headTime uint64, amt wallet.Balance, fee, burnFactor uint64, dest cipher.Address) (coin.Transaction, error) { txn := coin.Transaction{} auxs := unspent.AllForAddresses(wlt.GetAddresses()) // Subtract pending spends from available puxs := unconfirmed.SpendsForAddresses(unspent, wlt.GetAddressSet()) auxs = auxs.Sub(puxs) // Determine which unspents to spend spends, err := createSpends(headTime, auxs.Flatten(), amt, fee, burnFactor) if err != nil { return txn, err } // Add these unspents as tx inputs toSign := make([]cipher.SecKey, len(spends)) spending := wallet.Balance{0, 0} for i, au := range spends { entry, exists := wlt.GetEntry(au.Body.Address) if !exists { log.Panic("On second thought, the wallet entry does not exist") } txn.PushInput(au.Hash()) toSign[i] = entry.Secret spending.Coins += au.Body.Coins spending.Hours += au.CoinHours(headTime) } // Determine how much change we get back, if any _, changeHours, err := calculateBurnAndChange(spending.Hours, amt.Hours, fee, burnFactor) if err != nil { // This should not occur, else createSpends is broken return txn, err } change := wallet.NewBalance(spending.Coins-amt.Coins, changeHours) // TODO -- send change to a new address changeAddr := spends[0].Body.Address if change.Coins == 0 { if change.Hours > 0 { msg := ("Have enough coins, but not enough to send coin hours " + "change back. Would spend %d more hours than requested.") return txn, fmt.Errorf(msg, change.Hours) } } else { txn.PushOutput(changeAddr, change.Coins, change.Hours) } // Finalize the the transaction txn.PushOutput(dest, amt.Coins, amt.Hours) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil }
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 makeMultipleOutputs(t *testing.T, bc *Blockchain) { txn := coin.Transaction{} ux := bc.GetUnspent().Array()[0] txn.PushInput(ux.Hash()) txn.PushOutput(genAddress, 1e6, 100) txn.PushOutput(genAddress, 2e6, 100) txn.PushOutput(genAddress, _genCoins-3e6, 100) txn.SignInputs([]cipher.SecKey{genSecret}) txn.UpdateHeader() assert.Nil(t, txn.Verify()) assert.Nil(t, bc.VerifyTransaction(txn)) b, err := bc.NewBlockFromTransactions(coin.Transactions{txn}, bc.Time()+_incTime) assert.Nil(t, err) assertExecuteBlock(t, bc, b, txn) }
// NewTransaction create skycoin transaction. func newTransaction(utxos []unspentOut, keys []cipher.SecKey, outs []coin.TransactionOutput) (*coin.Transaction, error) { tx := coin.Transaction{} // keys := make([]cipher.SecKey, len(utxos)) for _, u := range utxos { tx.PushInput(cipher.MustSHA256FromHex(u.Hash)) } for _, o := range outs { if (o.Coins % 1e6) != 0 { return nil, errors.New("skycoin coins must be multiple of 1e6") } tx.PushOutput(o.Address, o.Coins, o.Hours) } // tx.Verify() tx.SignInputs(keys) tx.UpdateHeader() return &tx, nil }
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) }
func addBlock(bc historydb.Blockchainer, td testData, tm uint64) (*coin.Block, *coin.Transaction, error) { tx := coin.Transaction{} // get unspent output ux, err := getUx(bc, td.Vin.BlockSeq, td.Vin.TxID, td.Vin.Addr) if err != nil { return nil, nil, err } if ux == nil { return nil, nil, errors.New("no unspent output") } tx.PushInput(ux.Hash()) for _, o := range td.Vouts { addr, err := cipher.DecodeBase58Address(o.ToAddr) if err != nil { return nil, nil, err } tx.PushOutput(addr, o.Coins, o.Hours) } sigKey := cipher.MustSecKeyFromHex(td.Vin.SigKey) tx.SignInputs([]cipher.SecKey{sigKey}) tx.UpdateHeader() if err := bc.VerifyTransaction(tx); err != nil { return nil, nil, err } preBlock := bc.GetBlock(td.PreBlockHash) b := newBlock(*preBlock, tm, *bc.GetUnspent(), coin.Transactions{tx}, _feeCalc) // uxs, err := bc.ExecuteBlock(&b) _, err = bc.ExecuteBlock(&b) if err != nil { return nil, nil, err } return &b, &tx, nil }
//DEPRECATE //deprecate dependency on wallet // Creates a Transaction spending coins and hours from our coins //MOVE SOMEWHERE ELSE //Move to wallet or move to ??? func CreateSpendingTransaction(wlt wallet.Wallet, unconfirmed *UnconfirmedTxnPool, unspent *coin.UnspentPool, headTime uint64, amt wallet.Balance, dest cipher.Address) (coin.Transaction, error) { txn := coin.Transaction{} auxs := unspent.AllForAddresses(wlt.GetAddresses()) // Subtract pending spends from available puxs := unconfirmed.SpendsForAddresses(unspent, wlt.GetAddressSet()) auxs = auxs.Sub(puxs) // Determine which unspents to spend spends, err := createSpends(headTime, auxs.Flatten(), amt) if err != nil { return txn, err } // Add these unspents as tx inputs toSign := make([]cipher.SecKey, len(spends)) spending := wallet.Balance{0, 0} for i, au := range spends { entry, exists := wlt.GetEntry(au.Body.Address) if !exists { log.Panic("On second thought, the wallet entry does not exist") } txn.PushInput(au.Hash()) toSign[i] = entry.Secret spending.Coins += au.Body.Coins spending.Hours += au.CoinHours(headTime) } //keep 1/4th of hours as change //send half to each address var changeHours uint64 = spending.Hours / 4 if amt.Coins == spending.Coins { txn.PushOutput(dest, amt.Coins, changeHours/2) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil } change := wallet.NewBalance(spending.Coins-amt.Coins, changeHours/2) // TODO -- send change to a new address changeAddr := spends[0].Body.Address //create transaction txn.PushOutput(changeAddr, change.Coins, change.Hours) txn.PushOutput(dest, amt.Coins, changeHours/2) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil }
func TestExecuteBlock(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Invalid block returns error b := coin.Block{} uxs, err := bc.ExecuteBlock(&b) assert.NotNil(t, err) assert.Nil(t, uxs) // Valid block, spends are removed from the unspent pool, new ones are // added. Blocks is updated, and new unspents are returns assert.Equal(t, bc.Len(), uint64(3)) assert.Equal(t, len(bc.GetUnspent().Pool), 2) spuxs := splitUnspent(t, bc, ux) tx := coin.Transaction{} tx.PushInput(spuxs[0].Hash()) coins := spuxs[0].Body.Coins extra := coins % 4e6 coins = (coins - extra) / 4 tx.PushOutput(genAddress, coins+extra, spuxs[0].Body.Hours/5) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/6) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/7) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/8) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() tx2 := coin.Transaction{} tx2.PushInput(spuxs[1].Hash()) tx2.PushOutput(genAddress, spuxs[1].Body.Coins, spuxs[1].Body.Hours/10) tx2.SignInputs([]cipher.SecKey{genSecret}) tx2.UpdateHeader() txns := coin.Transactions{tx, tx2} sTxns := coin.NewSortableTransactions(txns, bc.TransactionFee) unswapped := sTxns.IsSorted() txns = coin.SortTransactions(txns, bc.TransactionFee) assert.Nil(t, bc.verifyTransactions(txns)) seq := bc.Head().Head.BkSeq b, err = bc.NewBlockFromTransactions(txns, bc.Time()+_incTime) assert.Equal(t, b.Head.BkSeq, seq+1) assert.Nil(t, err) assert.Equal(t, len(b.Body.Transactions), 2) assert.Equal(t, b.Body.Transactions, txns) uxs, err = bc.ExecuteBlock(&b) assert.Nil(t, err) assert.Equal(t, len(uxs), 5) // Check that all unspents look correct and are in the unspent pool txOuts := []coin.TransactionOutput{} if unswapped { txOuts = append(txOuts, tx.Out...) txOuts = append(txOuts, tx2.Out...) } else { txOuts = append(txOuts, tx2.Out...) txOuts = append(txOuts, tx.Out...) } for i, ux := range uxs { if unswapped { if i < len(tx.Out) { assert.Equal(t, ux.Body.SrcTransaction, tx.Hash()) } else { assert.Equal(t, ux.Body.SrcTransaction, tx2.Hash()) } } else { if i < len(tx2.Out) { assert.Equal(t, ux.Body.SrcTransaction, tx2.Hash()) } else { assert.Equal(t, ux.Body.SrcTransaction, tx.Hash()) } } assert.Equal(t, ux.Body.Address, txOuts[i].Address) assert.Equal(t, ux.Body.Coins, txOuts[i].Coins) assert.Equal(t, ux.Body.Hours, txOuts[i].Hours) assert.Equal(t, ux.Head.BkSeq, b.Head.BkSeq) assert.Equal(t, ux.Head.Time, b.Head.Time) assert.True(t, bc.GetUnspent().Has(ux.Hash())) } // Check that all spends are no longer in the pool txIns := []cipher.SHA256{} txIns = append(txIns, tx.In...) txIns = append(txIns, tx2.In...) for _, ux := range txIns { assert.False(t, bc.GetUnspent().Has(ux)) } }
func TestNewBlockFromTransactions(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) gb := bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // gb.Head.Version = 0x0F // bc.Blocks[0] = gb // assert.Equal(t, bc.GetGenesisBlock().Head.Version, uint32(0x0F)) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // No transactions _, err := bc.NewBlockFromTransactions(coin.Transactions{}, bc.Time()+_incTime) assertError(t, err, "No transactions") assert.Equal(t, bc.Len(), uint64(3)) // Bad currentTime, must be greater than head time fee := uint64(100) txn, _ := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, fee) txns := coin.Transactions{txn} assert.Panics(t, func() { bc.NewBlockFromTransactions(txns, bc.Time()) }) // Valid transaction hrs := ux.CoinHours(bc.Time()) seq := bc.Head().Head.BkSeq b, err := bc.NewBlockFromTransactions(txns, bc.Time()+_incTime) assert.Nil(t, err) assert.Equal(t, len(b.Body.Transactions), 1) assert.Equal(t, b.Body.Transactions[0], txn) assert.Equal(t, b.Head.BkSeq, seq+1) assert.Equal(t, b.Head.Time, bc.Time()+_incTime) assert.Equal(t, b.Head.Version, gb.Head.Version) assert.Equal(t, b.Head.Fee, fee) assert.Equal(t, b.Head.Fee, hrs-txn.OutputHours()) assert.NotEqual(t, b.Head.Fee, uint64(0)) // Invalid transaction txn.InnerHash = cipher.SHA256{} txns = coin.Transactions{txn} _, err = bc.NewBlockFromTransactions(txns, bc.Time()+_incTime) assertError(t, err, "Invalid header hash") // Multiple transactions, sorted // First, split our genesis block into two, so we can make 2 valid txns uxs := splitUnspent(t, bc, ux) // tNow, make two valid txns txn = coin.Transaction{} txn.PushInput(uxs[0].Hash()) txn.PushOutput(genAddress, uxs[0].Body.Coins, uxs[0].Body.Hours) txn.SignInputs([]cipher.SecKey{genSecret}) txn.UpdateHeader() txn2 := coin.Transaction{} txn2.PushInput(uxs[1].Hash()) txn2.PushOutput(genAddress, uxs[1].Body.Coins, uxs[1].Body.Hours) txn2.SignInputs([]cipher.SecKey{genSecret}) txn2.UpdateHeader() // Combine them and sort txns = coin.Transactions{txn, txn2} txns = coin.SortTransactions(txns, bc.TransactionFee) b, err = bc.NewBlockFromTransactions(txns, bc.Time()+_incTime) assert.Nil(t, err) assert.Equal(t, len(b.Body.Transactions), 2) assert.Equal(t, b.Body.Transactions, txns) // Order should be preserved txns2 := coin.Transactions{txn, txn2} sTxns := coin.NewSortableTransactions(txns2, bc.TransactionFee) if sTxns.IsSorted() { txns2[0], txns2[1] = txns2[1], txns2[0] } b, err = bc.NewBlockFromTransactions(txns2, bc.Time()+_incTime) assert.Nil(t, err) assert.Equal(t, len(b.Body.Transactions), 2) assert.Equal(t, b.Body.Transactions, txns2) }
func TestProcessTransactions(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) fmt.Println("genesis time:", bc.GetGenesisBlock().Time()) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Invalid, no transactions in block // arbitrating=false txns, err := bc.processTransactions(coin.Transactions{}, false) assert.Nil(t, txns) assertError(t, err, "No transactions") // arbitrating=true txns, err = bc.processTransactions(coin.Transactions{}, true) assert.Equal(t, len(txns), 0) assert.Nil(t, err) // Invalid, txn.Verify() fails // TODO -- combine all txn.Verify() failures into one test // method, and call it from here, from ExecuteBlock(), from // Verify(), from VerifyTransaction() txns = coin.Transactions{} txn := coin.Transaction{} txn.PushInput(ux.Hash()) txn.PushOutput(genAddress, 777, 100) txn.SignInputs([]cipher.SecKey{genSecret}) txn.UpdateHeader() txns = append(txns, txn) // arbitrating=false txns2, err := bc.processTransactions(txns, false) assert.Nil(t, txns2) assertError(t, err, "Transaction outputs must be multiple of 1e6 base units") // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.NotNil(t, txns2) assert.Nil(t, err) assert.Equal(t, len(txns2), 0) // Invalid, duplicate unspent will be created by these txns txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txns = coin.Transactions{txn, txn} // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "Duplicate unspent output across transactions") assert.Nil(t, txns2) // arbitrating=true. One of the offending transactions should be removed txns2, err = bc.processTransactions(txns, true) assert.Nil(t, err) assert.Equal(t, len(txns2), 1) assert.Equal(t, txns2[0], txn) // Check that a new output will not collide with the existing pool txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txns = coin.Transactions{txn} uxb := coin.UxBody{ SrcTransaction: txn.Hash(), Coins: txn.Out[0].Coins, Hours: txn.Out[0].Hours, Address: txn.Out[0].Address, } bc.GetUnspent().Add(coin.UxOut{Body: uxb}) // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "New unspent collides with existing unspent") assert.Nil(t, txns2) // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.Equal(t, len(txns2), 0) assert.NotNil(t, txns2) assert.Nil(t, err) // Spending of duplicate inputs being spent across txns txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txn2, _ := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txn2.Out = nil txn2.PushOutput(makeAddress(), 1e6, 100) txn2.PushOutput(makeAddress(), ux.Body.Coins-1e6, 100) txn2.Sigs = nil txn2.SignInputs([]cipher.SecKey{genSecret}) txn2.UpdateHeader() txns = coin.SortTransactions(coin.Transactions{txn, txn2}, bc.TransactionFee) // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "Cannot spend output twice in the same block") assert.Nil(t, txns2) // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.Nil(t, err) assert.Equal(t, len(txns2), 1) assert.Equal(t, txns2[0], txns[0]) }
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") }