package state import ( "testing" "github.com/vapor/consensus" "github.com/vapor/database/storage" "github.com/vapor/protocol/bc" "github.com/vapor/testutil" ) var defaultEntry = map[bc.Hash]bc.Entry{ bc.Hash{V0: 0}: &bc.Output{ Source: &bc.ValueSource{ Value: &bc.AssetAmount{ AssetId: &bc.AssetID{V0: 0}, }, }, }, } var gasOnlyTxEntry = map[bc.Hash]bc.Entry{ bc.Hash{V1: 0}: &bc.Output{ Source: &bc.ValueSource{ Value: &bc.AssetAmount{ AssetId: consensus.BTMAssetID, }, }, }, bc.Hash{V1: 1}: &bc.Output{ Source: &bc.ValueSource{ Value: &bc.AssetAmount{ AssetId: &bc.AssetID{V0: 999}, }, }, }, } func TestApplyBlock(t *testing.T) { cases := []struct { block *bc.Block inputView *UtxoViewpoint fetchView *UtxoViewpoint gasOnlyTx bool err bool }{ { // can't find prevout in tx entries block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 1}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, false), }, }, fetchView: NewUtxoViewpoint(), err: true, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: NewUtxoViewpoint(), err: true, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, true), }, }, err: true, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, false), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, true), }, }, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ Height: 101, TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, false), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, true), }, }, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ Height: 0, TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, false), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, true), }, }, err: true, }, { // output will be store block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{ &bc.Hash{V0: 0}, }, }, SpentOutputIDs: []bc.Hash{}, Entries: defaultEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, false), }, }, err: false, }, { // apply gas only tx, non-btm asset spent input will not be spent block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V1: 0}, bc.Hash{V1: 1}, }, Entries: gasOnlyTxEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(false, 0, false), bc.Hash{V1: 1}: storage.NewUtxoEntry(false, 0, false), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(false, 0, true), bc.Hash{V1: 1}: storage.NewUtxoEntry(false, 0, false), }, }, gasOnlyTx: true, err: false, }, { // apply gas only tx, non-btm asset spent output will not be store block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{ &bc.Hash{V1: 0}, &bc.Hash{V1: 1}, }, }, SpentOutputIDs: []bc.Hash{}, Entries: gasOnlyTxEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(true, 0, false), }, }, gasOnlyTx: true, err: false, }, } for i, c := range cases { c.block.TransactionStatus.SetStatus(0, c.gasOnlyTx) if err := c.inputView.ApplyBlock(c.block, c.block.TransactionStatus); c.err != (err != nil) { t.Errorf("case #%d want err = %v, get err = %v", i, c.err, err) } if c.err { continue } if !testutil.DeepEqual(c.inputView, c.fetchView) { t.Errorf("test case %d, want %v, get %v", i, c.fetchView, c.inputView) } } } func TestDetachBlock(t *testing.T) { cases := []struct { block *bc.Block inputView *UtxoViewpoint fetchView *UtxoViewpoint gasOnlyTx bool err bool }{ { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, false), }, }, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{ &bc.Hash{V0: 0}, }, }, SpentOutputIDs: []bc.Hash{}, Entries: defaultEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, true), }, }, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, false), }, }, err: true, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V0: 0}, }, Entries: defaultEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, true), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 0}: storage.NewUtxoEntry(false, 0, false), }, }, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{}, }, SpentOutputIDs: []bc.Hash{ bc.Hash{V1: 0}, bc.Hash{V1: 1}, }, Entries: gasOnlyTxEntry, }, }, }, inputView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(false, 0, true), bc.Hash{V1: 1}: storage.NewUtxoEntry(false, 0, true), }, }, fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(false, 0, false), bc.Hash{V1: 1}: storage.NewUtxoEntry(false, 0, true), }, }, gasOnlyTx: true, err: false, }, { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ &bc.Tx{ TxHeader: &bc.TxHeader{ ResultIds: []*bc.Hash{ &bc.Hash{V1: 0}, &bc.Hash{V1: 1}, }, }, SpentOutputIDs: []bc.Hash{}, Entries: gasOnlyTxEntry, }, }, }, inputView: NewUtxoViewpoint(), fetchView: &UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V1: 0}: storage.NewUtxoEntry(false, 0, true), }, }, gasOnlyTx: true, err: false, }, } for i, c := range cases { c.block.TransactionStatus.SetStatus(0, c.gasOnlyTx) if err := c.inputView.DetachBlock(c.block, c.block.TransactionStatus); c.err != (err != nil) { t.Errorf("case %d want err = %v, get err = %v", i, c.err, err) } if c.err { continue } if !testutil.DeepEqual(c.inputView, c.fetchView) { t.Errorf("test case %d, want %v, get %v", i, c.fetchView, c.inputView) } } }