From 9f7018503a12d4c6c7ae1e6e6a2835569f3bf502 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 26 Feb 2020 10:58:20 +0300 Subject: [PATCH] core/transaction: use new transaction.InOut for References We don't need a map here, use simpler structures. --- pkg/core/blockchain.go | 49 ++++++++++++++++++----------------- pkg/core/blockchainer.go | 2 +- pkg/core/interop_neo.go | 26 ++++++++----------- pkg/core/transaction/inout.go | 8 ++++++ pkg/network/helper_test.go | 2 +- 5 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 pkg/core/transaction/inout.go diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 38f8103c6..3d0a6a5c6 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1009,28 +1009,28 @@ func (bc *Blockchain) GetConfig() config.ProtocolConfiguration { return bc.config } -// References returns a map with input coin reference (prevhash and index) as key -// and transaction output as value from a transaction t. +// References maps transaction's inputs into a slice of InOuts, effectively +// joining each Input with the corresponding Output. // @TODO: unfortunately we couldn't attach this method to the Transaction struct in the // transaction package because of a import cycle problem. Perhaps we should think to re-design // the code base to avoid this situation. -func (bc *Blockchain) References(t *transaction.Transaction) map[transaction.Input]*transaction.Output { - references := make(map[transaction.Input]*transaction.Output) +func (bc *Blockchain) References(t *transaction.Transaction) ([]transaction.InOut, error) { + references := make([]transaction.InOut, 0, len(t.Inputs)) for _, inputs := range transaction.GroupInputsByPrevHash(t.Inputs) { prevHash := inputs[0].PrevHash tx, _, err := bc.dao.GetTransaction(prevHash) if err != nil { - return nil + return nil, errors.New("bad input reference") } for _, in := range inputs { if int(in.PrevIndex) > len(tx.Outputs)-1 { - return nil + return nil, errors.New("bad input reference") } - references[*in] = &tx.Outputs[in.PrevIndex] + references = append(references, transaction.InOut{In: *in, Out: tx.Outputs[in.PrevIndex]}) } } - return references + return references, nil } // FeePerByte returns network fee divided by the size of the transaction. @@ -1041,9 +1041,13 @@ func (bc *Blockchain) FeePerByte(t *transaction.Transaction) util.Fixed8 { // NetworkFee returns network fee. func (bc *Blockchain) NetworkFee(t *transaction.Transaction) util.Fixed8 { inputAmount := util.Fixed8FromInt64(0) - for _, txOutput := range bc.References(t) { - if txOutput.AssetID == UtilityTokenID() { - inputAmount.Add(txOutput.Amount) + refs, err := bc.References(t) + if err != nil { + return inputAmount + } + for i := range refs { + if refs[i].Out.AssetID == UtilityTokenID() { + inputAmount.Add(refs[i].Out.Amount) } } @@ -1310,14 +1314,14 @@ func (bc *Blockchain) GetTransactionResults(t *transaction.Transaction) []*trans var results []*transaction.Result tempGroupResult := make(map[util.Uint256]util.Fixed8) - references := bc.References(t) - if references == nil { + references, err := bc.References(t) + if err != nil { return nil } - for _, output := range references { + for _, inout := range references { tempResults = append(tempResults, &transaction.Result{ - AssetID: output.AssetID, - Amount: output.Amount, + AssetID: inout.Out.AssetID, + Amount: inout.Out.Amount, }) } for _, output := range t.Outputs { @@ -1534,16 +1538,13 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([ if t.Type == transaction.ClaimType { return bc.GetScriptHashesForVerifyingClaim(t) } - references := bc.References(t) - if references == nil { - return nil, errors.New("invalid inputs") + references, err := bc.References(t) + if err != nil { + return nil, err } hashes := make(map[util.Uint160]bool) - for _, i := range t.Inputs { - h := references[i].ScriptHash - if _, ok := hashes[h]; !ok { - hashes[h] = true - } + for i := range references { + hashes[references[i].Out.ScriptHash] = true } for _, a := range t.Attributes { if a.Usage == transaction.Script { diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index dae78c48b..822c80fec 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -39,7 +39,7 @@ type Blockchainer interface { GetTestVM() (*vm.VM, storage.Store) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetUnspentCoinState(util.Uint256) *UnspentCoinState - References(t *transaction.Transaction) map[transaction.Input]*transaction.Output + References(t *transaction.Transaction) ([]transaction.InOut, error) mempool.Feer // fee interface PoolTx(*transaction.Transaction) error VerifyTx(*transaction.Transaction, *block.Block) error diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index b3018e51c..1eb8e9b3c 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -34,12 +34,6 @@ const ( DefaultAssetLifetime = 1 + BlocksPerYear ) -// txInOut is used to pushed one key-value pair from References() onto the stack. -type txInOut struct { - in transaction.Input - out transaction.Output -} - // headerGetVersion returns version from the header. func (ic *interopContext) headerGetVersion(v *vm.VM) error { header, err := popHeaderFromVM(v) @@ -141,14 +135,16 @@ func (ic *interopContext) txGetReferences(v *vm.VM) error { if !ok { return fmt.Errorf("type mismatch: %T is not a Transaction", txInterface) } - refs := ic.bc.References(tx) + refs, err := ic.bc.References(tx) + if err != nil { + return err + } if len(refs) > vm.MaxArraySize { return errors.New("too many references") } stackrefs := make([]vm.StackItem, 0, len(refs)) - for _, k := range tx.Inputs { - tio := txInOut{k, *refs[k]} + for _, tio := range refs { stackrefs = append(stackrefs, vm.NewInteropItem(tio)) } v.Estack().PushVal(stackrefs) @@ -243,11 +239,11 @@ func popInputFromVM(v *vm.VM) (*transaction.Input, error) { inInterface := v.Estack().Pop().Value() input, ok := inInterface.(*transaction.Input) if !ok { - txio, ok := inInterface.(txInOut) + txio, ok := inInterface.(transaction.InOut) if !ok { - return nil, fmt.Errorf("type mismatch: %T is not an Input or txInOut", inInterface) + return nil, fmt.Errorf("type mismatch: %T is not an Input or InOut", inInterface) } - input = &txio.in + input = &txio.In } return input, nil } @@ -277,11 +273,11 @@ func popOutputFromVM(v *vm.VM) (*transaction.Output, error) { outInterface := v.Estack().Pop().Value() output, ok := outInterface.(*transaction.Output) if !ok { - txio, ok := outInterface.(txInOut) + txio, ok := outInterface.(transaction.InOut) if !ok { - return nil, fmt.Errorf("type mismatch: %T is not an Output or txInOut", outInterface) + return nil, fmt.Errorf("type mismatch: %T is not an Output or InOut", outInterface) } - output = &txio.out + output = &txio.Out } return output, nil } diff --git a/pkg/core/transaction/inout.go b/pkg/core/transaction/inout.go new file mode 100644 index 000000000..75bc026ac --- /dev/null +++ b/pkg/core/transaction/inout.go @@ -0,0 +1,8 @@ +package transaction + +// InOut represents an Input bound to its corresponding Output which is a useful +// combination for many purposes. +type InOut struct { + In Input + Out Output +} diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 2759c37df..d21eb2ae6 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -33,7 +33,7 @@ func (chain testChain) GetConfig() config.ProtocolConfiguration { panic("TODO") } -func (chain testChain) References(t *transaction.Transaction) map[transaction.Input]*transaction.Output { +func (chain testChain) References(t *transaction.Transaction) ([]transaction.InOut, error) { panic("TODO") }