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 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)) } }