From: wz Date: Sat, 2 Mar 2019 03:23:31 +0000 (+0800) Subject: Dev utxo unit test (#1606) X-Git-Tag: 2.0.0-alpha~89^2~21 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fbytom.git;a=commitdiff_plain;h=21e309f453d36256c08aec2fe26b8e1bd4f94595 Dev utxo unit test (#1606) * add a unit test for attacBlocks of utxoview * modify unit test for attacBlocks of utxoview * recovery test file * delete code * Add attach block list * add init transaction and block * modify code format * add error deal --- diff --git a/test/utxo_view/utxo_view_test.go b/test/utxo_view/utxo_view_test.go new file mode 100644 index 00000000..6bd9a202 --- /dev/null +++ b/test/utxo_view/utxo_view_test.go @@ -0,0 +1,128 @@ +package utxo_view + +import ( + "os" + "testing" + + "github.com/bytom/testutil" + + "github.com/golang/protobuf/proto" + dbm "github.com/tendermint/tmlibs/db" + + "github.com/bytom/database/leveldb" + "github.com/bytom/database/storage" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" + "github.com/bytom/protocol/state" +) + +func TestAttachOrDetachBlocks(t *testing.T) { + cases := []struct { + desc string + before map[bc.Hash]*storage.UtxoEntry + want map[bc.Hash]*storage.UtxoEntry + attachBlock []*bc.Block + detachBlock []*bc.Block + attachTxStatus []*bc.TransactionStatus + detachTxStatus []*bc.TransactionStatus + }{ + { + desc: "coinbase tx", + before: make(map[bc.Hash]*storage.UtxoEntry), + want: map[bc.Hash]*storage.UtxoEntry{*newTx(mockBlocks[0].Transactions[0]).OutputHash(0): storage.NewUtxoEntry(true, mockBlocks[0].Block.Height, false)}, + attachBlock: []*bc.Block{ + types.MapBlock(&mockBlocks[0].Block), + }, + attachTxStatus: []*bc.TransactionStatus{ + &bc.TransactionStatus{VerifyStatus: []*bc.TxVerifyResult{ + &bc.TxVerifyResult{StatusFail: false}, + }}, + }, + }, + { + desc: "Chain trading 3", + before: map[bc.Hash]*storage.UtxoEntry{ + newTx(mockBlocks[1].Transactions[1]).getSpentOutputID(): storage.NewUtxoEntry(false, mockBlocks[1].Height-1, false), + }, + want: map[bc.Hash]*storage.UtxoEntry{ + *newTx(mockBlocks[1].Transactions[0]).OutputHash(0): storage.NewUtxoEntry(true, mockBlocks[1].Height, false), + *newTx(mockBlocks[1].Transactions[1]).OutputHash(0): storage.NewUtxoEntry(false, mockBlocks[1].Height, false), + *newTx(mockBlocks[1].Transactions[2]).OutputHash(0): storage.NewUtxoEntry(false, mockBlocks[1].Height, false), + *newTx(mockBlocks[1].Transactions[3]).OutputHash(0): storage.NewUtxoEntry(false, mockBlocks[1].Height, false), + *newTx(mockBlocks[1].Transactions[3]).OutputHash(1): storage.NewUtxoEntry(false, mockBlocks[1].Height, false), + }, + attachBlock: []*bc.Block{ + types.MapBlock(&mockBlocks[1].Block), + }, + attachTxStatus: []*bc.TransactionStatus{ + &bc.TransactionStatus{VerifyStatus: []*bc.TxVerifyResult{ + &bc.TxVerifyResult{StatusFail: false}, + &bc.TxVerifyResult{StatusFail: false}, + &bc.TxVerifyResult{StatusFail: false}, + &bc.TxVerifyResult{StatusFail: false}, + }}, + }, + }, + } + node := blockNode(types.MapBlock(&mockBlocks[0].Block).BlockHeader) + defer os.RemoveAll("temp") + for index, c := range cases { + testDB := dbm.NewDB("testdb", "leveldb", "temp") + store := leveldb.NewStore(testDB) + + utxoViewpoint := state.NewUtxoViewpoint() + for k, v := range c.before { + utxoViewpoint.Entries[k] = v + } + if err := store.SaveChainStatus(node, utxoViewpoint); err != nil { + t.Error(err) + } + + utxoViewpoint = state.NewUtxoViewpoint() + for index, block := range c.detachBlock { + if err := store.GetTransactionsUtxo(utxoViewpoint, block.Transactions); err != nil { + t.Error(err) + } + if err := utxoViewpoint.DetachBlock(block, c.detachTxStatus[index]); err != nil { + t.Error(err) + } + } + + for index, block := range c.attachBlock { + if err := store.GetTransactionsUtxo(utxoViewpoint, block.Transactions); err != nil { + t.Error(err) + } + if err := utxoViewpoint.ApplyBlock(block, c.attachTxStatus[index]); err != nil { + t.Error(err) + } + } + if err := store.SaveChainStatus(node, utxoViewpoint); err != nil { + t.Error(err) + } + + want := map[string]*storage.UtxoEntry{} + result := make(map[string]*storage.UtxoEntry) + + for k, v := range c.want { + want[string(calcUtxoKey(&k))] = v + } + + iter := testDB.IteratorPrefix([]byte(utxoPreFix)) + defer iter.Release() + + for iter.Next() { + utxoEntry := &storage.UtxoEntry{} + if err := proto.Unmarshal(iter.Value(), utxoEntry); err != nil { + t.Error(err) + } + key := string(iter.Key()) + result[key] = utxoEntry + } + + if !testutil.DeepEqual(want, result) { + t.Errorf("case [%d] fail. want: %v, result: %v", index, want, result) + } + testDB.Close() + os.RemoveAll("temp") + } +} diff --git a/test/utxo_view/utxo_view_test_util.go b/test/utxo_view/utxo_view_test_util.go new file mode 100644 index 00000000..f2089de1 --- /dev/null +++ b/test/utxo_view/utxo_view_test_util.go @@ -0,0 +1,172 @@ +package utxo_view + +import ( + "encoding/hex" + + "github.com/bytom/consensus" + "github.com/bytom/consensus/difficulty" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" + "github.com/bytom/protocol/state" + "github.com/bytom/testutil" +) + +const utxoPreFix = "UT:" + +func calcUtxoKey(hash *bc.Hash) []byte { + return []byte(utxoPreFix + hash.String()) +} + +type tx struct { + Tx *types.Tx +} + +func newTx(t *types.Tx) *tx { + return &tx{ + Tx: t, + } +} + +func (t *tx) getSourceID(outIndex int) *bc.Hash { + output := t.Tx.Entries[*t.Tx.OutputID(outIndex)].(*bc.Output) + return output.Source.Ref +} + +func (t *tx) getAmount(outIndex int) uint64 { + output := t.Tx.Entries[*t.Tx.OutputID(outIndex)].(*bc.Output) + return output.Source.Value.Amount +} + +func (t *tx) getSpentOutputID() bc.Hash { + return t.Tx.SpentOutputIDs[0] +} + +func (t *tx) OutputHash(outIndex int) *bc.Hash { + return t.Tx.ResultIds[outIndex] +} + +func blockNode(header *bc.BlockHeader) *state.BlockNode { + h := types.BlockHeader{ + Version: header.Version, + Height: header.Height, + PreviousBlockHash: *header.PreviousBlockId, + Timestamp: header.Timestamp, + Bits: header.Bits, + Nonce: header.Nonce, + } + return &state.BlockNode{ + Parent: nil, + Hash: h.Hash(), + WorkSum: difficulty.CalcWork(h.Bits), + Version: h.Version, + Height: h.Height, + Timestamp: h.Timestamp, + Nonce: h.Nonce, + Bits: h.Bits, + } +} + +func mustDecodeHex(str string) []byte { + data, err := hex.DecodeString(str) + if err != nil { + panic(err) + } + return data +} + +func coinBaseTx(amount uint64, arbitrary string) *types.Tx { + return types.NewTx(types.TxData{ + Inputs: []*types.TxInput{ + types.NewCoinbaseInput([]byte(arbitrary)), + }, + Outputs: []*types.TxOutput{ + types.NewTxOutput(*consensus.BTMAssetID, amount, mustDecodeHex("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + }) +} + +var mockTransaction = []*tx{} +var mockBlocks = []*block{} + +func toHash(hash string) bc.Hash { + sourceID := bc.Hash{} + sourceID.UnmarshalText([]byte(hash)) + return sourceID +} + +type block struct { + types.Block +} + +func init() { + t := &tx{ + Tx: types.NewTx(types.TxData{ + Inputs: []*types.TxInput{ + types.NewSpendInput(nil, toHash("ca9b179e549406aa583869e124e39817414d4500a8ce5476e95b6018d182b966"), *consensus.BTMAssetID, 41250000000, 0, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + Outputs: []*types.TxOutput{ + types.NewTxOutput(*consensus.BTMAssetID, 100000000, []byte("00148c704747e94387fa0b8712b053ed2132d84820ac")), + types.NewTxOutput(*consensus.BTMAssetID, 41150000000, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + }), + } + mockTransaction = append(mockTransaction, t) + + t = &tx{ + Tx: types.NewTx(types.TxData{ + Inputs: []*types.TxInput{ + types.NewSpendInput(nil, *mockTransaction[0].getSourceID(1), *consensus.BTMAssetID, 41150000000, 1, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + Outputs: []*types.TxOutput{ + types.NewTxOutput(*consensus.BTMAssetID, 100000000, []byte("00148c704747e94387fa0b8712b053ed2132d84820ac")), + types.NewTxOutput(*consensus.BTMAssetID, 41050000000, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + }), + } + mockTransaction = append(mockTransaction, t) + + t = &tx{ + Tx: types.NewTx(types.TxData{ + Inputs: []*types.TxInput{ + types.NewSpendInput(nil, *mockTransaction[1].getSourceID(1), *consensus.BTMAssetID, 41050000000, 1, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + Outputs: []*types.TxOutput{ + types.NewTxOutput(*consensus.BTMAssetID, 100000000, []byte("00148c704747e94387fa0b8712b053ed2132d84820ac")), + types.NewTxOutput(*consensus.BTMAssetID, 40950000000, []byte("00144431c4278632c6e35dd2870faa1a4b8e0a275cbc")), + }, + }), + } + mockTransaction = append(mockTransaction, t) + + mockBlocks = []*block{ + // coinbase tx + &block{Block: types.Block{ + BlockHeader: types.BlockHeader{ + Height: 100, + PreviousBlockHash: testutil.MustDecodeHash("0ab29c0bd7bff3b3b7eb98802f8d5f8833884c86c0fb21559a65cc58dda99667"), + Timestamp: 1522908275, + Nonce: 0, + }, + Transactions: []*types.Tx{ + coinBaseTx(41250000000, "arbitrary block0"), + }, + }}, + + // Chain trading 3 + &block{Block: types.Block{ + BlockHeader: types.BlockHeader{ + Height: 101, + PreviousBlockHash: testutil.MustDecodeHash("0ab29c0bd7bff3b3b7eb98802f8d5f8833884c86c0fb21559a65cc58dda99667"), + Timestamp: 1522908275, + Nonce: 0, + }, + Transactions: []*types.Tx{ + coinBaseTx(41250000000, "arbitrary block1"), + mockTransaction[0].Tx, + mockTransaction[1].Tx, + mockTransaction[2].Tx, + }, + }}, + } + +}