From f2e3be6fef65d703283cd1b308d98f62880034b2 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 9 Jun 2020 11:46:16 +0300 Subject: [PATCH 1/7] core: remove old AntShares interops We don't need them anymore. --- pkg/core/gas_price.go | 17 +++++++---------- pkg/core/interops.go | 35 ----------------------------------- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/pkg/core/gas_price.go b/pkg/core/gas_price.go index 56094bc0b..4d67955d2 100644 --- a/pkg/core/gas_price.go +++ b/pkg/core/gas_price.go @@ -41,22 +41,19 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 { } const ( - neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create - neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate - antSharesContractCreate = 0x2a28d29b // AntShares.Contract.Create - antSharesContractMigrate = 0xa934c8bb // AntShares.Contract.Migrate - systemStoragePut = 0x84183fe6 // System.Storage.Put - systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx - neoStoragePut = 0xf541a152 // Neo.Storage.Put - antSharesStoragePut = 0x5f300a9e // AntShares.Storage.Put + neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create + neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate + systemStoragePut = 0x84183fe6 // System.Storage.Put + systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx + neoStoragePut = 0xf541a152 // Neo.Storage.Put ) estack := v.Estack() switch id { - case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate: + case neoContractCreate, neoContractMigrate: return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64())) - case systemStoragePut, systemStoragePutEx, neoStoragePut, antSharesStoragePut: + case systemStoragePut, systemStoragePutEx, neoStoragePut: // price for storage PUT is 1 GAS per 1 KiB keySize := len(estack.Peek(1).Bytes()) valSize := len(estack.Peek(2).Bytes()) diff --git a/pkg/core/interops.go b/pkg/core/interops.go index a781bcdb5..7173fce5e 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -165,41 +165,6 @@ var neoInterops = []interop.Function{ // Aliases. {Name: "Neo.Iterator.Next", Func: enumerator.Next, Price: 1}, {Name: "Neo.Iterator.Value", Func: enumerator.Value, Price: 1}, - - // Old compatibility APIs. - {Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1}, - {Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, - {Name: "AntShares.Attribute.GetData", Func: attrGetData, Price: 1}, - {Name: "AntShares.Attribute.GetUsage", Func: attrGetUsage, Price: 1}, - {Name: "AntShares.Block.GetTransaction", Func: blockGetTransaction, Price: 1}, - {Name: "AntShares.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, - {Name: "AntShares.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, - {Name: "AntShares.Blockchain.GetAccount", Func: bcGetAccount, Price: 100}, - {Name: "AntShares.Blockchain.GetBlock", Func: bcGetBlock, Price: 200}, - {Name: "AntShares.Blockchain.GetContract", Func: bcGetContract, Price: 100}, - {Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, - {Name: "AntShares.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, - {Name: "AntShares.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, - {Name: "AntShares.Contract.Create", Func: contractCreate, Price: 0}, - {Name: "AntShares.Contract.Destroy", Func: contractDestroy, Price: 1}, - {Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1}, - {Name: "AntShares.Contract.GetStorageContext", Func: contractGetStorageContext, Price: 1}, - {Name: "AntShares.Contract.Migrate", Func: contractMigrate, Price: 0}, - {Name: "AntShares.Header.GetHash", Func: headerGetHash, Price: 1}, - {Name: "AntShares.Header.GetMerkleRoot", Func: headerGetMerkleRoot, Price: 1}, - {Name: "AntShares.Header.GetNextConsensus", Func: headerGetNextConsensus, Price: 1}, - {Name: "AntShares.Header.GetPrevHash", Func: headerGetPrevHash, Price: 1}, - {Name: "AntShares.Header.GetTimestamp", Func: headerGetTimestamp, Price: 1}, - {Name: "AntShares.Header.GetVersion", Func: headerGetVersion, Price: 1}, - {Name: "AntShares.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200}, - {Name: "AntShares.Runtime.Log", Func: runtimeLog, Price: 1}, - {Name: "AntShares.Runtime.Notify", Func: runtimeNotify, Price: 1}, - {Name: "AntShares.Storage.Delete", Func: storageDelete, Price: 100}, - {Name: "AntShares.Storage.Get", Func: storageGet, Price: 100}, - {Name: "AntShares.Storage.GetContext", Func: storageGetContext, Price: 1}, - {Name: "AntShares.Storage.Put", Func: storagePut, Price: 0}, - {Name: "AntShares.Transaction.GetAttributes", Func: txGetAttributes, Price: 1}, - {Name: "AntShares.Transaction.GetHash", Func: txGetHash, Price: 1}, } // initIDinInteropsSlice initializes IDs from names in one given From 53655c5ac23ff51252501f14e63b50ea0bfeb8ae Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 8 Jun 2020 18:36:19 +0300 Subject: [PATCH 2/7] core: implement new System.Blockchain.GetTransaction interop closes #1023 Now we put on stack stackitem.Array instead of Interop, so we don't need old transaction-related interops anymore. Removed the following interops: System.Transaction.GetHash Neo.Transaction.GetAttributes Neo.Transaction.GetHash Neo.Transaction.GetWitnesses Neo.Attribute.GetData Neo.Attribute.GetUsage Also removed the following duplicated NEO interop: Neo.Blockchain.GetTransaction --- pkg/compiler/codegen.go | 4 ++ pkg/compiler/syscall.go | 11 +---- pkg/core/interop_neo.go | 58 ----------------------- pkg/core/interop_neo_test.go | 32 ------------- pkg/core/interop_system.go | 45 ++++++++++++------ pkg/core/interop_system_test.go | 54 +++++++++++++++++++++ pkg/core/interops.go | 9 +--- pkg/core/interops_test.go | 5 -- pkg/interop/attribute/attribute.go | 65 -------------------------- pkg/interop/block/block.go | 10 ++-- pkg/interop/blockchain/blockchain.go | 36 +++++++++++--- pkg/interop/engine/engine.go | 11 ++--- pkg/interop/transaction/transaction.go | 34 -------------- 13 files changed, 129 insertions(+), 245 deletions(-) create mode 100644 pkg/core/interop_system_test.go delete mode 100644 pkg/interop/attribute/attribute.go delete mode 100644 pkg/interop/transaction/transaction.go diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 492e3cdef..247ced9d2 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1053,6 +1053,10 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) { emit.Opcode(c.prog.BinWriter, opcode.PACK) } emit.Syscall(c.prog.BinWriter, api) + switch name { + case "GetTransaction": + c.emitConvert(stackitem.StructT) + } // This NOP instruction is basically not needed, but if we do, we have a // one to one matching avm file with neo-python which is very nice for debugging. diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index ef1502cb8..b4ae7cd96 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -7,10 +7,6 @@ var syscalls = map[string]map[string]string{ "GetVotes": "Neo.Account.GetVotes", "IsStandard": "Neo.Account.IsStandard", }, - "attribute": { - "GetUsage": "Neo.Attribute.GetUsage", - "GetData": "Neo.Attribute.GetData", - }, "crypto": { "ECDsaVerify": "Neo.Crypto.ECDsaVerify", }, @@ -44,7 +40,7 @@ var syscalls = map[string]map[string]string{ "GetContract": "Neo.Blockchain.GetContract", "GetHeader": "Neo.Blockchain.GetHeader", "GetHeight": "Neo.Blockchain.GetHeight", - "GetTransaction": "Neo.Blockchain.GetTransaction", + "GetTransaction": "System.Blockchain.GetTransaction", "GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight", "GetValidators": "Neo.Blockchain.GetValidators", }, @@ -63,11 +59,6 @@ var syscalls = map[string]map[string]string{ "GetTransactions": "Neo.Block.GetTransactions", "GetTransaction": "Neo.Block.GetTransaction", }, - "transaction": { - "GetAttributes": "Neo.Transaction.GetAttributes", - "GetHash": "Neo.Transaction.GetHash", - "GetWitnesses": "Neo.Transaction.GetWitnesses", - }, "contract": { "GetScript": "Neo.Contract.GetScript", "IsPayable": "Neo.Contract.IsPayable", diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 5295d922b..d855890f8 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -57,42 +57,6 @@ func headerGetNextConsensus(ic *interop.Context, v *vm.VM) error { return nil } -// txGetAttributes returns current transaction attributes. -func txGetAttributes(ic *interop.Context, v *vm.VM) error { - txInterface := v.Estack().Pop().Value() - tx, ok := txInterface.(*transaction.Transaction) - if !ok { - return errors.New("value is not a transaction") - } - if len(tx.Attributes) > vm.MaxArraySize { - return errors.New("too many attributes") - } - attrs := make([]stackitem.Item, 0, len(tx.Attributes)) - for i := range tx.Attributes { - attrs = append(attrs, stackitem.NewInterop(&tx.Attributes[i])) - } - v.Estack().PushVal(attrs) - return nil -} - -// txGetWitnesses returns current transaction witnesses. -func txGetWitnesses(ic *interop.Context, v *vm.VM) error { - txInterface := v.Estack().Pop().Value() - tx, ok := txInterface.(*transaction.Transaction) - if !ok { - return errors.New("value is not a transaction") - } - if len(tx.Scripts) > vm.MaxArraySize { - return errors.New("too many outputs") - } - scripts := make([]stackitem.Item, 0, len(tx.Scripts)) - for i := range tx.Scripts { - scripts = append(scripts, stackitem.NewInterop(&tx.Scripts[i])) - } - v.Estack().PushVal(scripts) - return nil -} - // witnessGetVerificationScript returns current witness' script. func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error { witInterface := v.Estack().Pop().Value() @@ -107,28 +71,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error { return nil } -// attrGetData returns tx attribute data. -func attrGetData(ic *interop.Context, v *vm.VM) error { - attrInterface := v.Estack().Pop().Value() - attr, ok := attrInterface.(*transaction.Attribute) - if !ok { - return fmt.Errorf("%T is not an attribute", attr) - } - v.Estack().PushVal(attr.Data) - return nil -} - -// attrGetData returns tx attribute usage field. -func attrGetUsage(ic *interop.Context, v *vm.VM) error { - attrInterface := v.Estack().Pop().Value() - attr, ok := attrInterface.(*transaction.Attribute) - if !ok { - return fmt.Errorf("%T is not an attribute", attr) - } - v.Estack().PushVal(int(attr.Usage)) - return nil -} - // bcGetAccount returns or creates an account. func bcGetAccount(ic *interop.Context, v *vm.VM) error { accbytes := v.Estack().Pop().Bytes() diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 50cb0d9f4..ddb302ced 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -178,16 +178,6 @@ func TestHeaderGetNextConsensus(t *testing.T) { require.Equal(t, block.NextConsensus.BytesBE(), value) } -func TestTxGetAttributes(t *testing.T) { - v, tx, context, chain := createVMAndPushTX(t) - defer chain.Close() - - err := txGetAttributes(context, v) - require.NoError(t, err) - value := v.Estack().Pop().Value().([]stackitem.Item) - require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage) -} - func TestWitnessGetVerificationScript(t *testing.T) { v := vm.New() script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)} @@ -280,28 +270,6 @@ func TestECDSAVerify(t *testing.T) { }) } -func TestAttrGetData(t *testing.T) { - v, tx, context, chain := createVMAndTX(t) - defer chain.Close() - v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0])) - - err := attrGetData(context, v) - require.NoError(t, err) - data := v.Estack().Pop().Value() - require.Equal(t, tx.Attributes[0].Data, data) -} - -func TestAttrGetUsage(t *testing.T) { - v, tx, context, chain := createVMAndTX(t) - defer chain.Close() - v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0])) - - err := attrGetUsage(context, v) - require.NoError(t, err) - usage := v.Estack().Pop().Value() - require.Equal(t, big.NewInt(int64(tx.Attributes[0].Usage)), usage) -} - func TestAccountGetScriptHash(t *testing.T) { v, accState, context, chain := createVMAndAccState(t) defer chain.Close() diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 55b57fa09..7254177f4 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "math/big" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" @@ -22,6 +23,9 @@ import ( const ( // MaxStorageKeyLen is the maximum length of a key for storage items. MaxStorageKeyLen = 1024 + // MaxTraceableBlocks is the maximum number of blocks before current chain + // height we're able to give information about. + MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement ) // StorageContext contains storing script hash and read/write flag, it's used as @@ -112,13 +116,35 @@ func getTransactionAndHeight(cd *dao.Cached, v *vm.VM) (*transaction.Transaction return cd.GetTransaction(hash) } +// isTraceableBlock defines whether we're able to give information about +// the block with index specified. +func isTraceableBlock(ic *interop.Context, index uint32) bool { + height := ic.Chain.BlockHeight() + return index <= height && index+MaxTraceableBlocks > height +} + +// transactionToStackItem converts transaction.Transaction to stackitem.Item +func transactionToStackItem(t *transaction.Transaction) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(t.Hash().BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(t.Version))), + stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))), + stackitem.NewByteArray(t.Sender.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(t.SystemFee))), + stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))), + stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))), + stackitem.NewByteArray(t.Script), + }) +} + // bcGetTransaction returns transaction. func bcGetTransaction(ic *interop.Context, v *vm.VM) error { - tx, _, err := getTransactionAndHeight(ic.DAO, v) - if err != nil { - return err + tx, h, err := getTransactionAndHeight(ic.DAO, v) + if err != nil || !isTraceableBlock(ic, h) { + v.Estack().PushVal(stackitem.Null{}) + return nil } - v.Estack().PushVal(stackitem.NewInterop(tx)) + v.Estack().PushVal(transactionToStackItem(tx)) return nil } @@ -234,17 +260,6 @@ func blockGetTransaction(ic *interop.Context, v *vm.VM) error { return nil } -// txGetHash returns transaction's hash. -func txGetHash(ic *interop.Context, v *vm.VM) error { - txInterface := v.Estack().Pop().Value() - tx, ok := txInterface.(*transaction.Transaction) - if !ok { - return errors.New("value is not a transaction") - } - v.Estack().PushVal(tx.Hash().BytesBE()) - return nil -} - // engineGetScriptContainer returns transaction that contains the script being // run. func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go new file mode 100644 index 000000000..ad7a93091 --- /dev/null +++ b/pkg/core/interop_system_test.go @@ -0,0 +1,54 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +func TestBCGetTransaction(t *testing.T) { + v, tx, context, chain := createVMAndTX(t) + defer chain.Close() + + t.Run("success", func(t *testing.T) { + require.NoError(t, context.DAO.StoreAsTransaction(tx, 0)) + v.Estack().PushVal(tx.Hash().BytesBE()) + err := bcGetTransaction(context, v) + require.NoError(t, err) + + value := v.Estack().Pop().Value() + actual, ok := value.([]stackitem.Item) + require.True(t, ok) + require.Equal(t, 8, len(actual)) + require.Equal(t, tx.Hash().BytesBE(), actual[0].Value().([]byte)) + require.Equal(t, int64(tx.Version), actual[1].Value().(*big.Int).Int64()) + require.Equal(t, int64(tx.Nonce), actual[2].Value().(*big.Int).Int64()) + require.Equal(t, tx.Sender.BytesBE(), actual[3].Value().([]byte)) + require.Equal(t, int64(tx.SystemFee), actual[4].Value().(*big.Int).Int64()) + require.Equal(t, int64(tx.NetworkFee), actual[5].Value().(*big.Int).Int64()) + require.Equal(t, int64(tx.ValidUntilBlock), actual[6].Value().(*big.Int).Int64()) + require.Equal(t, tx.Script, actual[7].Value().([]byte)) + }) + + t.Run("isn't traceable", func(t *testing.T) { + require.NoError(t, context.DAO.StoreAsTransaction(tx, 1)) + v.Estack().PushVal(tx.Hash().BytesBE()) + err := bcGetTransaction(context, v) + require.NoError(t, err) + + _, ok := v.Estack().Pop().Item().(stackitem.Null) + require.True(t, ok) + }) + + t.Run("bad hash", func(t *testing.T) { + require.NoError(t, context.DAO.StoreAsTransaction(tx, 1)) + v.Estack().PushVal(tx.Hash().BytesLE()) + err := bcGetTransaction(context, v) + require.NoError(t, err) + + _, ok := v.Estack().Pop().Item().(stackitem.Null) + require.True(t, ok) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 7173fce5e..091316dfe 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -69,7 +69,7 @@ var systemInterops = []interop.Function{ {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, - {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 200}, + {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "System.Contract.Call", Func: contractCall, Price: 1}, {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1}, @@ -98,15 +98,12 @@ var systemInterops = []interop.Function{ {Name: "System.Storage.Put", Func: storagePut, Price: 0}, // These don't have static price in C# code. {Name: "System.Storage.PutEx", Func: storagePutEx, Price: 0}, {Name: "System.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1}, - {Name: "System.Transaction.GetHash", Func: txGetHash, Price: 1}, } var neoInterops = []interop.Function{ {Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100}, - {Name: "Neo.Attribute.GetData", Func: attrGetData, Price: 1}, - {Name: "Neo.Attribute.GetUsage", Func: attrGetUsage, Price: 1}, {Name: "Neo.Block.GetTransaction", Func: blockGetTransaction, Price: 1}, {Name: "Neo.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, {Name: "Neo.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, @@ -115,7 +112,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, - {Name: "Neo.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, {Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "Neo.Contract.Create", Func: contractCreate, Price: 0}, {Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1}, @@ -157,9 +153,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1}, {Name: "Neo.Storage.Put", Func: storagePut, Price: 0}, {Name: "Neo.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1}, - {Name: "Neo.Transaction.GetAttributes", Func: txGetAttributes, Price: 1}, - {Name: "Neo.Transaction.GetHash", Func: txGetHash, Price: 1}, - {Name: "Neo.Transaction.GetWitnesses", Func: txGetWitnesses, Price: 200}, {Name: "Neo.Witness.GetVerificationScript", Func: witnessGetVerificationScript, Price: 100}, // Aliases. diff --git a/pkg/core/interops_test.go b/pkg/core/interops_test.go index ee78b1aa6..20f818c8e 100644 --- a/pkg/core/interops_test.go +++ b/pkg/core/interops_test.go @@ -34,8 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) { funcs := []func(*interop.Context, *vm.VM) error{ accountGetBalance, accountGetScriptHash, - attrGetData, - attrGetUsage, blockGetTransaction, blockGetTransactionCount, blockGetTransactions, @@ -55,9 +53,6 @@ func TestUnexpectedNonInterops(t *testing.T) { storageGet, storagePut, storagePutEx, - txGetAttributes, - txGetHash, - txGetWitnesses, witnessGetVerificationScript, } for _, f := range funcs { diff --git a/pkg/interop/attribute/attribute.go b/pkg/interop/attribute/attribute.go deleted file mode 100644 index b4c0175c4..000000000 --- a/pkg/interop/attribute/attribute.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Package attribute provides getters for transaction attributes. -*/ -package attribute - -// Attribute represents transaction attribute in Neo, it's an opaque data -// structure that you can get data from only using functions from this package. -// It's similar in function to the TransactionAttribute class in the Neo .net -// framework. To use it you need to get is first using transaction.GetAttributes. -type Attribute struct{} - -// GetUsage returns the Usage field of the given attribute. It is an enumeration -// with the following possible values: -// ContractHash = 0x00 -// ECDH02 = 0x02 -// ECDH03 = 0x03 -// Script = 0x20 -// Vote = 0x30 -// CertURL = 0x80 -// DescriptionURL = 0x81 -// Description = 0x90 -// -// Hash1 = 0xa1 -// Hash2 = 0xa2 -// Hash3 = 0xa3 -// Hash4 = 0xa4 -// Hash5 = 0xa5 -// Hash6 = 0xa6 -// Hash7 = 0xa7 -// Hash8 = 0xa8 -// Hash9 = 0xa9 -// Hash10 = 0xaa -// Hash11 = 0xab -// Hash12 = 0xac -// Hash13 = 0xad -// Hash14 = 0xae -// Hash15 = 0xaf -// -// Remark = 0xf0 -// Remark1 = 0xf1 -// Remark2 = 0xf2 -// Remark3 = 0xf3 -// Remark4 = 0xf4 -// Remark5 = 0xf5 -// Remark6 = 0xf6 -// Remark7 = 0xf7 -// Remark8 = 0xf8 -// Remark9 = 0xf9 -// Remark10 = 0xfa -// Remark11 = 0xfb -// Remark12 = 0xfc -// Remark13 = 0xfd -// Remark14 = 0xfe -// Remark15 = 0xff -// This function uses `Neo.Attribute.GetUsage` syscall internally. -func GetUsage(attr Attribute) byte { - return 0x00 -} - -// GetData returns the data of the given attribute, exact interpretation of this -// data depends on attribute's Usage type. It uses `Neo.Attribute.GetData` -// syscall internally. -func GetData(attr Attribute) []byte { - return nil -} diff --git a/pkg/interop/block/block.go b/pkg/interop/block/block.go index 98cd3a50d..c3295163f 100644 --- a/pkg/interop/block/block.go +++ b/pkg/interop/block/block.go @@ -3,7 +3,7 @@ Package block provides getters for Neo Block structure. */ package block -import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" +import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" // Block represents a NEO block, it's an opaque data structure that you can get // data from only using functions from this package. It's similar in function to @@ -19,12 +19,12 @@ func GetTransactionCount(b Block) int { // GetTransactions returns a slice of transactions recorded in the given block. // It uses `Neo.Block.GetTransactions` syscall internally. -func GetTransactions(b Block) []transaction.Transaction { - return []transaction.Transaction{} +func GetTransactions(b Block) []blockchain.Transaction { + return []blockchain.Transaction{} } // GetTransaction returns transaction from the given block by its index. It // uses `Neo.Block.GetTransaction` syscall internally. -func GetTransaction(b Block, index int) transaction.Transaction { - return transaction.Transaction{} +func GetTransaction(b Block, index int) blockchain.Transaction { + return blockchain.Transaction{} } diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index bba276c7a..a18ff2ff6 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -8,9 +8,32 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/block" "github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/header" - "github.com/nspcc-dev/neo-go/pkg/interop/transaction" ) +// Transaction represents a NEO transaction. It's similar to Transaction class +// in Neo .net framework. +type Transaction struct { + // Hash represents the hash (256 bit BE value in a 32 byte slice) of the + // given transaction (which also is its ID). + Hash []byte + // Version represents the transaction version. + Version int + // Nonce is a random number to avoid hash collision. + Nonce int + // Sender represents the sender (160 bit BE value in a 20 byte slice) of the + // given Transaction. + Sender []byte + // SysFee represents fee to be burned. + SysFee int + // NetFee represents fee to be distributed to consensus nodes. + NetFee int + // ValidUntilBlock is the maximum blockchain height exceeding which + // transaction should fail verification. + ValidUntilBlock int + // Script represents code to run in NeoVM for this transaction. + Script []byte +} + // GetHeight returns current block height (index of the last accepted block). // Note that when transaction is being run as a part of new block this block is // considered as not yet accepted (persisted) and thus you'll get an index of @@ -35,12 +58,11 @@ func GetBlock(heightOrHash interface{}) block.Block { return block.Block{} } -// GetTransaction returns transaction found by the given (256 bit in BE format -// represented as a slice of 32 bytes). Refer to the `transaction` package for -// possible uses of returned structure. This function uses -// `Neo.Blockchain.GetTransaction` syscall. -func GetTransaction(hash []byte) transaction.Transaction { - return transaction.Transaction{} +// GetTransaction returns transaction found by the given hash (256 bit in BE +// format represented as a slice of 32 bytes). This function uses +// `System.Blockchain.GetTransaction` syscall. +func GetTransaction(hash []byte) Transaction { + return Transaction{} } // GetTransactionHeight returns transaction's height (index of the block that diff --git a/pkg/interop/engine/engine.go b/pkg/interop/engine/engine.go index c2ebda9dd..ace2ceff3 100644 --- a/pkg/interop/engine/engine.go +++ b/pkg/interop/engine/engine.go @@ -5,15 +5,14 @@ framework. */ package engine -import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" +import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" // GetScriptContainer returns the transaction that initially triggered current // execution context. It never changes in a single execution, no matter how deep -// this execution goes. See `transaction` package for details on how to use the -// returned value. This function uses `System.ExecutionEngine.GetScriptContainer` -// syscall. -func GetScriptContainer() transaction.Transaction { - return transaction.Transaction{} +// this execution goes. This function uses +// `System.ExecutionEngine.GetScriptContainer` syscall. +func GetScriptContainer() blockchain.Transaction { + return blockchain.Transaction{} } // GetExecutingScriptHash returns script hash (160 bit in BE form represented diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go deleted file mode 100644 index fc44f37c4..000000000 --- a/pkg/interop/transaction/transaction.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Package transaction provides functions to work with transactions. -*/ -package transaction - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop/attribute" - "github.com/nspcc-dev/neo-go/pkg/interop/witness" -) - -// Transaction represents a NEO transaction, it's an opaque data structure -// that can be used with functions from this package. It's similar to -// Transaction class in Neo .net framework. -type Transaction struct{} - -// GetHash returns the hash (256 bit BE value in a 32 byte slice) of the given -// transaction (which also is its ID). Is uses `Neo.Transaction.GetHash` syscall. -func GetHash(t Transaction) []byte { - return nil -} - -// GetAttributes returns a slice of attributes for agiven transaction. Refer to -// attribute package on how to use them. This function uses -// `Neo.Transaction.GetAttributes` syscall. -func GetAttributes(t Transaction) []attribute.Attribute { - return []attribute.Attribute{} -} - -// GetWitnesses returns a slice of witnesses of a given Transaction. Refer to -// witness package on how to use them. This function uses -// `Neo.Transaction.GetWitnesses` syscall. -func GetWitnesses(t Transaction) []witness.Witness { - return []witness.Witness{} -} From 1e63ae4b3f46a275eda640cd73ab2b630dc34f8c Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 8 Jun 2020 18:40:01 +0300 Subject: [PATCH 3/7] core: update System.Blockchain.GetTransactionHeight interop Update System.Blockchain.GetTransactionHeight and removed Neo.Blockchain.GetTransactionHeight interop as we don't need it. --- pkg/compiler/syscall.go | 2 +- pkg/core/interop_system.go | 5 +++-- pkg/core/interops.go | 1 - pkg/interop/blockchain/blockchain.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index b4ae7cd96..efd9c6daf 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -41,7 +41,7 @@ var syscalls = map[string]map[string]string{ "GetHeader": "Neo.Blockchain.GetHeader", "GetHeight": "Neo.Blockchain.GetHeight", "GetTransaction": "System.Blockchain.GetTransaction", - "GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight", + "GetTransactionHeight": "System.Blockchain.GetTransactionHeight", "GetValidators": "Neo.Blockchain.GetValidators", }, "header": { diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 7254177f4..8edb9fa4a 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -151,8 +151,9 @@ func bcGetTransaction(ic *interop.Context, v *vm.VM) error { // bcGetTransactionHeight returns transaction height. func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error { _, h, err := getTransactionAndHeight(ic.DAO, v) - if err != nil { - return err + if err != nil || !isTraceableBlock(ic, h) { + v.Estack().PushVal(-1) + return nil } v.Estack().PushVal(h) return nil diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 091316dfe..14d6b8fee 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -112,7 +112,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, - {Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "Neo.Contract.Create", Func: contractCreate, Price: 0}, {Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1}, {Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1}, diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index a18ff2ff6..fbe2bbe2d 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -67,7 +67,7 @@ func GetTransaction(hash []byte) Transaction { // GetTransactionHeight returns transaction's height (index of the block that // includes it) by the given ID (256 bit in BE format represented as a slice of -// 32 bytes). This function uses `Neo.Blockchain.GetTransactionHeight` syscall. +// 32 bytes). This function uses `System.Blockchain.GetTransactionHeight` syscall. func GetTransactionHeight(hash []byte) int { return 0 } From d692de5ea40f6b5ffd5f99eb059148e5ba4be9a0 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 9 Jun 2020 12:18:08 +0300 Subject: [PATCH 4/7] core: add System.Blockchain.GetTransactionFromBlock interop --- pkg/compiler/syscall.go | 17 ++++---- pkg/core/interop_neo_test.go | 7 +++- pkg/core/interop_system.go | 21 ++++++++++ pkg/core/interop_system_test.go | 58 ++++++++++++++++++++++++++++ pkg/core/interops.go | 1 + pkg/interop/blockchain/blockchain.go | 8 ++++ 6 files changed, 103 insertions(+), 9 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index efd9c6daf..9d719dec4 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -35,14 +35,15 @@ var syscalls = map[string]map[string]string{ "Deserialize": "Neo.Runtime.Deserialize", }, "blockchain": { - "GetAccount": "Neo.Blockchain.GetAccount", - "GetBlock": "Neo.Blockchain.GetBlock", - "GetContract": "Neo.Blockchain.GetContract", - "GetHeader": "Neo.Blockchain.GetHeader", - "GetHeight": "Neo.Blockchain.GetHeight", - "GetTransaction": "System.Blockchain.GetTransaction", - "GetTransactionHeight": "System.Blockchain.GetTransactionHeight", - "GetValidators": "Neo.Blockchain.GetValidators", + "GetAccount": "Neo.Blockchain.GetAccount", + "GetBlock": "Neo.Blockchain.GetBlock", + "GetContract": "Neo.Blockchain.GetContract", + "GetHeader": "Neo.Blockchain.GetHeader", + "GetHeight": "Neo.Blockchain.GetHeight", + "GetTransaction": "System.Blockchain.GetTransaction", + "GetTransactionFromBlock": "System.Blockchain.GetTransactionFromBlock", + "GetTransactionHeight": "System.Blockchain.GetTransactionHeight", + "GetValidators": "Neo.Blockchain.GetValidators", }, "header": { "GetIndex": "Neo.Header.GetIndex", diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index ddb302ced..3796ca8da 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -306,11 +306,16 @@ func TestContractIsPayable(t *testing.T) { // Helper functions to create VM, InteropContext, TX, Account, Contract. func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) { + v, block, context, chain := createVMAndBlock(t) + v.Estack().PushVal(stackitem.NewInterop(block)) + return v, block, context, chain +} + +func createVMAndBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) { v := vm.New() block := newDumbBlock() chain := newTestChain(t) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil) - v.Estack().PushVal(stackitem.NewInterop(block)) return v, block, context, chain } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 8edb9fa4a..18be5a935 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -148,6 +148,27 @@ func bcGetTransaction(ic *interop.Context, v *vm.VM) error { return nil } +// bcGetTransactionFromBlock returns transaction with the given index from the +// block with height or hash specified. +func bcGetTransactionFromBlock(ic *interop.Context, v *vm.VM) error { + hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop()) + if err != nil { + return err + } + block, err := ic.DAO.GetBlock(hash) + if err != nil || !isTraceableBlock(ic, block.Index) { + v.Estack().PushVal(stackitem.Null{}) + return nil + } + index := v.Estack().Pop().BigInt().Int64() + if index < 0 || index >= int64(len(block.Transactions)) { + return errors.New("wrong transaction index") + } + tx := block.Transactions[index] + v.Estack().PushVal(tx.Hash().BytesBE()) + return nil +} + // bcGetTransactionHeight returns transaction height. func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error { _, h, err := getTransactionAndHeight(ic.DAO, v) diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index ad7a93091..caed6569c 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -52,3 +52,61 @@ func TestBCGetTransaction(t *testing.T) { require.True(t, ok) }) } + +func TestBCGetTransactionFromBlock(t *testing.T) { + v, block, context, chain := createVMAndBlock(t) + defer chain.Close() + require.NoError(t, chain.AddBlock(chain.newBlock())) + require.NoError(t, context.DAO.StoreAsBlock(block)) + + t.Run("success", func(t *testing.T) { + v.Estack().PushVal(0) + v.Estack().PushVal(block.Hash().BytesBE()) + err := bcGetTransactionFromBlock(context, v) + require.NoError(t, err) + + value := v.Estack().Pop().Value() + actual, ok := value.([]byte) + require.True(t, ok) + require.Equal(t, block.Transactions[0].Hash().BytesBE(), actual) + }) + + t.Run("invalid block hash", func(t *testing.T) { + v.Estack().PushVal(0) + v.Estack().PushVal(block.Hash().BytesBE()[:10]) + err := bcGetTransactionFromBlock(context, v) + require.Error(t, err) + }) + + t.Run("isn't traceable", func(t *testing.T) { + block.Index = 2 + require.NoError(t, context.DAO.StoreAsBlock(block)) + v.Estack().PushVal(0) + v.Estack().PushVal(block.Hash().BytesBE()) + err := bcGetTransactionFromBlock(context, v) + require.NoError(t, err) + + _, ok := v.Estack().Pop().Item().(stackitem.Null) + require.True(t, ok) + }) + + t.Run("bad block hash", func(t *testing.T) { + block.Index = 1 + require.NoError(t, context.DAO.StoreAsBlock(block)) + v.Estack().PushVal(0) + v.Estack().PushVal(block.Hash().BytesLE()) + err := bcGetTransactionFromBlock(context, v) + require.NoError(t, err) + + _, ok := v.Estack().Pop().Item().(stackitem.Null) + require.True(t, ok) + }) + + t.Run("bad transaction index", func(t *testing.T) { + require.NoError(t, context.DAO.StoreAsBlock(block)) + v.Estack().PushVal(1) + v.Estack().PushVal(block.Hash().BytesBE()) + err := bcGetTransactionFromBlock(context, v) + require.Error(t, err) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 14d6b8fee..104df3834 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -70,6 +70,7 @@ var systemInterops = []interop.Function{ {Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, + {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100}, {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "System.Contract.Call", Func: contractCall, Price: 1}, {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1}, diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index fbe2bbe2d..db7b5c7db 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -65,6 +65,14 @@ func GetTransaction(hash []byte) Transaction { return Transaction{} } +// GetTransactionFromBlock returns transaction hash (256 bit in BE format +// represented as a slice of 32 bytes) from the block found by the given hash or +// index (with the same encoding as for GetHeader) by its index. This +// function uses `System.Blockchain.GetTransactionFromBlock` syscall. +func GetTransactionFromBlock(heightOrHash interface{}, index int) []byte { + return nil +} + // GetTransactionHeight returns transaction's height (index of the block that // includes it) by the given ID (256 bit in BE format represented as a slice of // 32 bytes). This function uses `System.Blockchain.GetTransactionHeight` syscall. From e2187c0a9663454e6f47fe9e2b6a32976dc2a78f Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 9 Jun 2020 12:29:09 +0300 Subject: [PATCH 5/7] core: remove Block.GetTransaction interops Removed Neo.Block.GetTransaction and System.Block.GetTransaction interops. These interops were replaced by new System.Blockchain.GetTransactionFromBlock interop. --- pkg/compiler/syscall.go | 1 - pkg/core/interop_system.go | 17 ----------------- pkg/core/interops.go | 2 -- pkg/core/interops_test.go | 1 - pkg/interop/block/block.go | 6 ------ 5 files changed, 27 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 9d719dec4..0c4135b59 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -58,7 +58,6 @@ var syscalls = map[string]map[string]string{ "block": { "GetTransactionCount": "Neo.Block.GetTransactionCount", "GetTransactions": "Neo.Block.GetTransactions", - "GetTransaction": "Neo.Block.GetTransaction", }, "contract": { "GetScript": "Neo.Contract.GetScript", diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 18be5a935..de1503a21 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -265,23 +265,6 @@ func blockGetTransactions(ic *interop.Context, v *vm.VM) error { return nil } -// blockGetTransaction returns transaction with the given number from the given -// block. -func blockGetTransaction(ic *interop.Context, v *vm.VM) error { - blockInterface := v.Estack().Pop().Value() - block, ok := blockInterface.(*block.Block) - if !ok { - return errors.New("value is not a block") - } - index := v.Estack().Pop().BigInt().Int64() - if index < 0 || index >= int64(len(block.Transactions)) { - return errors.New("wrong transaction index") - } - tx := block.Transactions[index] - v.Estack().PushVal(stackitem.NewInterop(tx)) - return nil -} - // engineGetScriptContainer returns transaction that contains the script being // run. func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 104df3834..068534d19 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -62,7 +62,6 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ - {Name: "System.Block.GetTransaction", Func: blockGetTransaction, Price: 1}, {Name: "System.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, {Name: "System.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 200}, @@ -105,7 +104,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100}, - {Name: "Neo.Block.GetTransaction", Func: blockGetTransaction, Price: 1}, {Name: "Neo.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, {Name: "Neo.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, {Name: "Neo.Blockchain.GetAccount", Func: bcGetAccount, Price: 100}, diff --git a/pkg/core/interops_test.go b/pkg/core/interops_test.go index 20f818c8e..4b1168ff9 100644 --- a/pkg/core/interops_test.go +++ b/pkg/core/interops_test.go @@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) { funcs := []func(*interop.Context, *vm.VM) error{ accountGetBalance, accountGetScriptHash, - blockGetTransaction, blockGetTransactionCount, blockGetTransactions, contractGetScript, diff --git a/pkg/interop/block/block.go b/pkg/interop/block/block.go index c3295163f..b47d25158 100644 --- a/pkg/interop/block/block.go +++ b/pkg/interop/block/block.go @@ -22,9 +22,3 @@ func GetTransactionCount(b Block) int { func GetTransactions(b Block) []blockchain.Transaction { return []blockchain.Transaction{} } - -// GetTransaction returns transaction from the given block by its index. It -// uses `Neo.Block.GetTransaction` syscall internally. -func GetTransaction(b Block, index int) blockchain.Transaction { - return blockchain.Transaction{} -} From 7a2d37cf7e8cfbfec644e74441cbe294c75a89b5 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 9 Jun 2020 13:18:32 +0300 Subject: [PATCH 6/7] core: update System.Blockchain.GetBlock interop closes #1025 Now we put on stack stackitem.Array instead of Interop, so we're able to use all available block properties without extra interop getters. Removed Neo.Blockchain.GetBlock interop as we don't need it anymore. --- pkg/compiler/codegen.go | 2 +- pkg/compiler/syscall.go | 2 +- pkg/core/interop_neo_test.go | 8 +++++++ pkg/core/interop_system.go | 20 +++++++++++++--- pkg/core/interop_system_test.go | 35 ++++++++++++++++++++++++++++ pkg/core/interops.go | 3 +-- pkg/interop/blockchain/blockchain.go | 35 ++++++++++++++++++++++++---- 7 files changed, 93 insertions(+), 12 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 247ced9d2..39b005081 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1054,7 +1054,7 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) { } emit.Syscall(c.prog.BinWriter, api) switch name { - case "GetTransaction": + case "GetTransaction", "GetBlock": c.emitConvert(stackitem.StructT) } diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 0c4135b59..0ea6e1158 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -36,7 +36,7 @@ var syscalls = map[string]map[string]string{ }, "blockchain": { "GetAccount": "Neo.Blockchain.GetAccount", - "GetBlock": "Neo.Blockchain.GetBlock", + "GetBlock": "System.Blockchain.GetBlock", "GetContract": "Neo.Blockchain.GetContract", "GetHeader": "Neo.Blockchain.GetHeader", "GetHeight": "Neo.Blockchain.GetHeight", diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 3796ca8da..626e0c5cd 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -305,6 +305,14 @@ func TestContractIsPayable(t *testing.T) { // Helper functions to create VM, InteropContext, TX, Account, Contract. +func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) { + v := vm.New() + chain := newTestChain(t) + context := chain.newInteropContext(trigger.Application, + dao.NewSimple(storage.NewMemoryStore()), nil, nil) + return v, context, chain +} + func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) { v, block, context, chain := createVMAndBlock(t) v.Estack().PushVal(stackitem.NewInterop(block)) diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index de1503a21..a571a09cf 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -53,6 +53,20 @@ func getBlockHashFromElement(bc blockchainer.Blockchainer, element *vm.Element) return hash, nil } +// blockToStackItem converts block.Block to stackitem.Item +func blockToStackItem(b *block.Block) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(b.Hash().BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(b.Version))), + stackitem.NewByteArray(b.PrevHash.BytesBE()), + stackitem.NewByteArray(b.MerkleRoot.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))), + stackitem.NewBigInteger(big.NewInt(int64(b.Index))), + stackitem.NewByteArray(b.NextConsensus.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))), + }) +} + // bcGetBlock returns current block. func bcGetBlock(ic *interop.Context, v *vm.VM) error { hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop()) @@ -60,10 +74,10 @@ func bcGetBlock(ic *interop.Context, v *vm.VM) error { return err } block, err := ic.Chain.GetBlock(hash) - if err != nil { - v.Estack().PushVal([]byte{}) + if err != nil || !isTraceableBlock(ic, block.Index) { + v.Estack().PushVal(stackitem.Null{}) } else { - v.Estack().PushVal(stackitem.NewInterop(block)) + v.Estack().PushVal(blockToStackItem(block)) } return nil } diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index caed6569c..3a06fcb4a 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -110,3 +110,38 @@ func TestBCGetTransactionFromBlock(t *testing.T) { require.Error(t, err) }) } + +func TestBCGetBlock(t *testing.T) { + v, context, chain := createVM(t) + defer chain.Close() + block := chain.newBlock() + require.NoError(t, chain.AddBlock(block)) + + t.Run("success", func(t *testing.T) { + v.Estack().PushVal(block.Hash().BytesBE()) + err := bcGetBlock(context, v) + require.NoError(t, err) + + value := v.Estack().Pop().Value() + actual, ok := value.([]stackitem.Item) + require.True(t, ok) + require.Equal(t, 8, len(actual)) + require.Equal(t, block.Hash().BytesBE(), actual[0].Value().([]byte)) + require.Equal(t, int64(block.Version), actual[1].Value().(*big.Int).Int64()) + require.Equal(t, block.PrevHash.BytesBE(), actual[2].Value().([]byte)) + require.Equal(t, block.MerkleRoot.BytesBE(), actual[3].Value().([]byte)) + require.Equal(t, int64(block.Timestamp), actual[4].Value().(*big.Int).Int64()) + require.Equal(t, int64(block.Index), actual[5].Value().(*big.Int).Int64()) + require.Equal(t, block.NextConsensus.BytesBE(), actual[6].Value().([]byte)) + require.Equal(t, int64(len(block.Transactions)), actual[7].Value().(*big.Int).Int64()) + }) + + t.Run("bad hash", func(t *testing.T) { + v.Estack().PushVal(block.Hash().BytesLE()) + err := bcGetTransaction(context, v) + require.NoError(t, err) + + _, ok := v.Estack().Pop().Item().(stackitem.Null) + require.True(t, ok) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 068534d19..97bc2a6af 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -64,7 +64,7 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin var systemInterops = []interop.Function{ {Name: "System.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, {Name: "System.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, - {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 200}, + {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250}, {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, @@ -107,7 +107,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, {Name: "Neo.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, {Name: "Neo.Blockchain.GetAccount", Func: bcGetAccount, Price: 100}, - {Name: "Neo.Blockchain.GetBlock", Func: bcGetBlock, Price: 200}, {Name: "Neo.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index db7b5c7db..5afb272a5 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -5,7 +5,6 @@ package blockchain import ( "github.com/nspcc-dev/neo-go/pkg/interop/account" - "github.com/nspcc-dev/neo-go/pkg/interop/block" "github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/header" ) @@ -34,6 +33,32 @@ type Transaction struct { Script []byte } +// Block represents a NEO block, it's a data structure that you can get +// block-related data from. It's similar to the Block class in the Neo .net +// framework. To use it you need to get it via GetBlock function call. +type Block struct { + // Hash represents the hash (256 bit BE value in a 32 byte slice) of the + // given block. + Hash []byte + // Version of the block. + Version int + // PrevHash represents the hash (256 bit BE value in a 32 byte slice) of the + // previous block. + PrevHash []byte + // MerkleRoot represents the root hash (256 bit BE value in a 32 byte slice) + // of a transaction list. + MerkleRoot []byte + // Timestamp represents millisecond-precision block timestamp. + Timestamp int + // Index represents the height of the block. + Index int + // NextConsensus representes contract address of the next miner (160 bit BE + // value in a 20 byte slice). + NextConsensus []byte + // TransactionsLength represents the length of block's transactions array. + TransactionsLength int +} + // GetHeight returns current block height (index of the last accepted block). // Note that when transaction is being run as a part of new block this block is // considered as not yet accepted (persisted) and thus you'll get an index of @@ -52,10 +77,10 @@ func GetHeader(heightOrHash interface{}) header.Header { } // GetBlock returns block found by the given hash or index (with the same -// encoding as for GetHeader). Refer to the `block` package for possible uses -// of returned structure. This function uses `Neo.Blockchain.GetBlock` syscall. -func GetBlock(heightOrHash interface{}) block.Block { - return block.Block{} +// encoding as for GetHeader). This function uses `System.Blockchain.GetBlock` +// syscall. +func GetBlock(heightOrHash interface{}) Block { + return Block{} } // GetTransaction returns transaction found by the given hash (256 bit in BE From 8b7abd36c99f874069c1c8de61b15262c4bb413d Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 9 Jun 2020 13:24:37 +0300 Subject: [PATCH 7/7] core: remove Block.GetTransactions, Block.GetTransactionsCount interops Updated System.Blockchain.GetBlock interop replaced the functionality of the following interops: System.Block.GetTransactions System.Block.GetTransactionCount Neo.Block.GetTransactions Neo.Block.GetTransactionsCount --- pkg/compiler/syscall.go | 4 ---- pkg/core/interop_system.go | 29 ----------------------------- pkg/core/interops.go | 4 ---- pkg/core/interops_test.go | 2 -- pkg/interop/block/block.go | 24 ------------------------ 5 files changed, 63 deletions(-) delete mode 100644 pkg/interop/block/block.go diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 0ea6e1158..f6bbf3ccf 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -55,10 +55,6 @@ var syscalls = map[string]map[string]string{ "GetConsensusData": "Neo.Header.GetConsensusData", "GetNextConsensus": "Neo.Header.GetNextConsensus", }, - "block": { - "GetTransactionCount": "Neo.Block.GetTransactionCount", - "GetTransactions": "Neo.Block.GetTransactions", - }, "contract": { "GetScript": "Neo.Contract.GetScript", "IsPayable": "Neo.Contract.IsPayable", diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index a571a09cf..9a30fbaf8 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -250,35 +250,6 @@ func headerGetTimestamp(ic *interop.Context, v *vm.VM) error { return nil } -// blockGetTransactionCount returns transactions count in the given block. -func blockGetTransactionCount(ic *interop.Context, v *vm.VM) error { - blockInterface := v.Estack().Pop().Value() - block, ok := blockInterface.(*block.Block) - if !ok { - return errors.New("value is not a block") - } - v.Estack().PushVal(len(block.Transactions)) - return nil -} - -// blockGetTransactions returns transactions from the given block. -func blockGetTransactions(ic *interop.Context, v *vm.VM) error { - blockInterface := v.Estack().Pop().Value() - block, ok := blockInterface.(*block.Block) - if !ok { - return errors.New("value is not a block") - } - if len(block.Transactions) > vm.MaxArraySize { - return errors.New("too many transactions") - } - txes := make([]stackitem.Item, 0, len(block.Transactions)) - for _, tx := range block.Transactions { - txes = append(txes, stackitem.NewInterop(tx)) - } - v.Estack().PushVal(txes) - return nil -} - // engineGetScriptContainer returns transaction that contains the script being // run. func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 97bc2a6af..889742b3a 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -62,8 +62,6 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ - {Name: "System.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, - {Name: "System.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250}, {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, @@ -104,8 +102,6 @@ var neoInterops = []interop.Function{ {Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100}, - {Name: "Neo.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1}, - {Name: "Neo.Block.GetTransactions", Func: blockGetTransactions, Price: 1}, {Name: "Neo.Blockchain.GetAccount", Func: bcGetAccount, Price: 100}, {Name: "Neo.Blockchain.GetContract", Func: bcGetContract, Price: 100}, {Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, diff --git a/pkg/core/interops_test.go b/pkg/core/interops_test.go index 4b1168ff9..40d4db5ae 100644 --- a/pkg/core/interops_test.go +++ b/pkg/core/interops_test.go @@ -34,8 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) { funcs := []func(*interop.Context, *vm.VM) error{ accountGetBalance, accountGetScriptHash, - blockGetTransactionCount, - blockGetTransactions, contractGetScript, contractGetStorageContext, contractIsPayable, diff --git a/pkg/interop/block/block.go b/pkg/interop/block/block.go deleted file mode 100644 index b47d25158..000000000 --- a/pkg/interop/block/block.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Package block provides getters for Neo Block structure. -*/ -package block - -import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" - -// Block represents a NEO block, it's an opaque data structure that you can get -// data from only using functions from this package. It's similar in function to -// the Block class in the Neo .net framework. To use it you need to get it via -// blockchain.GetBlock function call. -type Block struct{} - -// GetTransactionCount returns the number of recorded transactions in the given -// block. It uses `Neo.Block.GetTransactionCount` syscall internally. -func GetTransactionCount(b Block) int { - return 0 -} - -// GetTransactions returns a slice of transactions recorded in the given block. -// It uses `Neo.Block.GetTransactions` syscall internally. -func GetTransactions(b Block) []blockchain.Transaction { - return []blockchain.Transaction{} -}