From ac527650eb173bb1dbd799d44fea7309fce42ad3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 3 Feb 2021 22:01:20 +0300 Subject: [PATCH 1/3] native: add Ledger contract, fix #1696 But don't change the way we process/store transactions and blocks. Effectively it's just an interface for smart contracts that replaces old syscalls. Transaction definition is moved temporarily to runtime package and Block definition is removed (till we solve #1691 properly). --- cli/testdata/chain50x2.acc | Bin 45879 -> 45879 bytes pkg/compiler/debug.go | 2 +- pkg/compiler/debug_test.go | 4 +- pkg/compiler/syscall.go | 7 - pkg/core/blockchain_test.go | 15 +- pkg/core/interop/interopnames/names.go | 10 -- pkg/core/interop_system.go | 138 +-------------- pkg/core/interop_system_test.go | 160 ----------------- pkg/core/interops.go | 10 -- pkg/core/native/compatibility_test.go | 11 +- pkg/core/native/contract.go | 5 + pkg/core/native/designate.go | 2 +- pkg/core/native/ledger.go | 226 +++++++++++++++++++++++++ pkg/core/native/name_service.go | 2 +- pkg/core/native/native_gas.go | 2 +- pkg/core/native/native_neo.go | 2 +- pkg/core/native/nativenames/names.go | 1 + pkg/core/native/oracle.go | 2 +- pkg/core/native/policy.go | 2 +- pkg/core/native_ledger_test.go | 177 +++++++++++++++++++ pkg/interop/blockchain/blockchain.go | 96 ----------- pkg/interop/runtime/engine.go | 29 +++- pkg/rpc/server/server_test.go | 8 +- pkg/rpc/server/testdata/testblocks.acc | Bin 7675 -> 7675 bytes 24 files changed, 466 insertions(+), 445 deletions(-) create mode 100644 pkg/core/native/ledger.go create mode 100644 pkg/core/native_ledger_test.go delete mode 100644 pkg/interop/blockchain/blockchain.go diff --git a/cli/testdata/chain50x2.acc b/cli/testdata/chain50x2.acc index 830712741f6f880cf18643a26377b1530982d758..93a3cc46f6b39753dc888d2e459c615100014096 100644 GIT binary patch literal 45879 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 literal 45879 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

fi}j)HGBZSmR{A?I5}92RjcsC_Dx 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 L!8lw1=*s>F&T7bW diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index e91acd718..fe8588ddf 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -239,7 +239,7 @@ func scAndVMInteropTypeFromExpr(named *types.Named) (smartcontract.ParamType, st name := named.Obj().Name() pkg := named.Obj().Pkg().Name() switch pkg { - case "blockchain", "contract": + case "runtime", "contract": return smartcontract.ArrayType, stackitem.ArrayT // Block, Transaction, Contract case "interop": if name != "Interface" { diff --git a/pkg/compiler/debug_test.go b/pkg/compiler/debug_test.go index ab2ba254a..04f545384 100644 --- a/pkg/compiler/debug_test.go +++ b/pkg/compiler/debug_test.go @@ -16,7 +16,7 @@ func TestCodeGen_DebugInfo(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/interop" import "github.com/nspcc-dev/neo-go/pkg/interop/storage" - import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" func Main(op string) bool { var s string _ = s @@ -47,7 +47,7 @@ func unexportedMethod() int { return 1 } func MethodParams(addr interop.Hash160, h interop.Hash256, sig interop.Signature, pub interop.PublicKey, inter interop.Interface, - ctx storage.Context, tx blockchain.Transaction) bool { + ctx storage.Context, tx runtime.Transaction) bool { return true } type MyStruct struct {} diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index ead24c899..a82002da0 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -14,13 +14,6 @@ var syscalls = map[string]map[string]string{ "Itoa": interopnames.SystemBinaryItoa, "Serialize": interopnames.SystemBinarySerialize, }, - "blockchain": { - "GetBlock": interopnames.SystemBlockchainGetBlock, - "GetHeight": interopnames.SystemBlockchainGetHeight, - "GetTransaction": interopnames.SystemBlockchainGetTransaction, - "GetTransactionFromBlock": interopnames.SystemBlockchainGetTransactionFromBlock, - "GetTransactionHeight": interopnames.SystemBlockchainGetTransactionHeight, - }, "contract": { "Call": interopnames.SystemContractCall, "CreateStandardAccount": interopnames.SystemContractCreateStandardAccount, diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 624eb5d05..7d72223c1 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -2,10 +2,8 @@ package core import ( "errors" - "fmt" "math/big" "math/rand" - "strings" "testing" "time" @@ -1165,12 +1163,16 @@ func TestIsTxStillRelevant(t *testing.T) { require.NoError(t, bc.AddBlock(bc.newBlock())) require.True(t, bc.IsTxStillRelevant(tx3, nil, false)) }) + /* // neo-project/neo#2289 t.Run("contract witness check fails", func(t *testing.T) { src := fmt.Sprintf(`package verify - import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" + import ( + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/util" + ) func Verify() bool { - currentHeight := blockchain.GetHeight() - return currentHeight < %d + currentHeight := contract.Call(util.FromAddress("NV5WuMGkwhQexQ4afTwuRojMeWwfWrEAdv"), "currentIndex", contract.ReadStates) + return currentHeight.(int) < %d }`, bc.BlockHeight()+2) // deploy + next block txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src)) require.NoError(t, err) @@ -1184,7 +1186,7 @@ func TestIsTxStillRelevant(t *testing.T) { Account: h, Scopes: transaction.None, }) - tx.NetworkFee += 1_000_000 + tx.NetworkFee += 10_000_000 require.NoError(t, testchain.SignTx(bc, tx)) tx.Scripts = append(tx.Scripts, transaction.Witness{}) @@ -1192,6 +1194,7 @@ func TestIsTxStillRelevant(t *testing.T) { require.NoError(t, bc.AddBlock(bc.newBlock())) require.False(t, bc.IsTxStillRelevant(tx, mp, false)) }) + */ } func TestMemPoolRemoval(t *testing.T) { diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index 8a76df3dd..b4975e090 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -10,11 +10,6 @@ const ( SystemBinaryDeserialize = "System.Binary.Deserialize" SystemBinaryItoa = "System.Binary.Itoa" SystemBinarySerialize = "System.Binary.Serialize" - SystemBlockchainGetBlock = "System.Blockchain.GetBlock" - SystemBlockchainGetHeight = "System.Blockchain.GetHeight" - SystemBlockchainGetTransaction = "System.Blockchain.GetTransaction" - SystemBlockchainGetTransactionFromBlock = "System.Blockchain.GetTransactionFromBlock" - SystemBlockchainGetTransactionHeight = "System.Blockchain.GetTransactionHeight" SystemCallbackCreate = "System.Callback.Create" SystemCallbackCreateFromMethod = "System.Callback.CreateFromMethod" SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" @@ -69,11 +64,6 @@ var names = []string{ SystemBinaryDeserialize, SystemBinaryItoa, SystemBinarySerialize, - SystemBlockchainGetBlock, - SystemBlockchainGetHeight, - SystemBlockchainGetTransaction, - SystemBlockchainGetTransactionFromBlock, - SystemBlockchainGetTransactionHeight, SystemCallbackCreate, SystemCallbackCreateFromMethod, SystemCallbackCreateFromSyscall, diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 7fc48ea5f..d6ffc3836 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -4,13 +4,10 @@ import ( "crypto/elliptic" "errors" "fmt" - "math" - "math/big" "github.com/nspcc-dev/neo-go/pkg/core/block" - "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" - "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -45,144 +42,15 @@ const ( Constant StorageFlag = 0x01 ) -// getBlockHashFromElement converts given vm.Element to block hash using given -// Blockchainer if needed. Interop functions accept both block numbers and -// block hashes as parameters, thus this function is needed. -func getBlockHashFromElement(bc blockchainer.Blockchainer, element *vm.Element) (util.Uint256, error) { - var hash util.Uint256 - hashbytes := element.Bytes() - if len(hashbytes) <= 5 { - hashint := element.BigInt().Int64() - if hashint < 0 || hashint > math.MaxUint32 { - return hash, errors.New("bad block index") - } - hash = bc.GetHeaderHash(int(hashint)) - } else { - return util.Uint256DecodeBytesBE(hashbytes) - } - 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) error { - hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop()) - if err != nil { - return err - } - block, err := ic.Chain.GetBlock(hash) - if err != nil || !isTraceableBlock(ic, block.Index) { - ic.VM.Estack().PushVal(stackitem.Null{}) - } else { - ic.VM.Estack().PushVal(blockToStackItem(block)) - } - return nil -} - -// bcGetHeight returns blockchain height. -func bcGetHeight(ic *interop.Context) error { - ic.VM.Estack().PushVal(ic.Chain.BlockHeight()) - return nil -} - -// getTransactionAndHeight gets parameter from the vm evaluation stack and -// returns transaction and its height if it's present in the blockchain. -func getTransactionAndHeight(cd *dao.Cached, v *vm.VM) (*transaction.Transaction, uint32, error) { - hashbytes := v.Estack().Pop().Bytes() - hash, err := util.Uint256DecodeBytesBE(hashbytes) - if err != nil { - return nil, 0, err - } - 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() - MaxTraceableBlocks := ic.Chain.GetConfig().MaxTraceableBlocks - 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) error { - tx, h, err := getTransactionAndHeight(ic.DAO, ic.VM) - if err != nil || !isTraceableBlock(ic, h) { - ic.VM.Estack().PushVal(stackitem.Null{}) - return nil - } - ic.VM.Estack().PushVal(transactionToStackItem(tx)) - return nil -} - -// bcGetTransactionFromBlock returns transaction with the given index from the -// block with height or hash specified. -func bcGetTransactionFromBlock(ic *interop.Context) error { - hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop()) - if err != nil { - return err - } - index := ic.VM.Estack().Pop().BigInt().Int64() - block, err := ic.DAO.GetBlock(hash) - if err != nil || !isTraceableBlock(ic, block.Index) { - ic.VM.Estack().PushVal(stackitem.Null{}) - return nil - } - if index < 0 || index >= int64(len(block.Transactions)) { - return errors.New("wrong transaction index") - } - tx := block.Transactions[index] - ic.VM.Estack().PushVal(tx.Hash().BytesBE()) - return nil -} - -// bcGetTransactionHeight returns transaction height. -func bcGetTransactionHeight(ic *interop.Context) error { - _, h, err := getTransactionAndHeight(ic.DAO, ic.VM) - if err != nil || !isTraceableBlock(ic, h) { - ic.VM.Estack().PushVal(-1) - return nil - } - ic.VM.Estack().PushVal(h) - return nil -} - // engineGetScriptContainer returns transaction or block that contains the script // being run. func engineGetScriptContainer(ic *interop.Context) error { var item stackitem.Item switch t := ic.Container.(type) { case *transaction.Transaction: - item = transactionToStackItem(t) + item = native.TransactionToStackItem(t) case *block.Block: - item = blockToStackItem(t) + item = native.BlockToStackItem(t) default: return errors.New("unknown script container") } diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index 9b574242e..2ada38325 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -29,166 +29,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestBCGetTransactionHeight(t *testing.T) { - v, tx, ic, chain := createVMAndTX(t) - defer chain.Close() - - for i := 0; i < 13; i++ { - require.NoError(t, chain.AddBlock(chain.newBlock())) - } - require.NoError(t, ic.DAO.StoreAsTransaction(tx, 13, nil)) - t.Run("good", func(t *testing.T) { - v.Estack().PushVal(tx.Hash().BytesBE()) - require.NoError(t, bcGetTransactionHeight(ic)) - require.Equal(t, big.NewInt(13), v.Estack().Pop().BigInt()) - }) - t.Run("bad", func(t *testing.T) { - h := tx.Hash() - h[0] ^= 0xFF - v.Estack().PushVal(h.BytesBE()) - require.NoError(t, bcGetTransactionHeight(ic)) - require.Equal(t, big.NewInt(-1), v.Estack().Pop().BigInt()) - }) -} - -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, nil)) - v.Estack().PushVal(tx.Hash().BytesBE()) - err := bcGetTransaction(context) - 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, nil)) - v.Estack().PushVal(tx.Hash().BytesBE()) - err := bcGetTransaction(context) - 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, nil)) - v.Estack().PushVal(tx.Hash().BytesLE()) - err := bcGetTransaction(context) - require.NoError(t, err) - - _, ok := v.Estack().Pop().Item().(stackitem.Null) - 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, nil)) - - t.Run("success", func(t *testing.T) { - v.Estack().PushVal(0) - v.Estack().PushVal(block.Hash().BytesBE()) - err := bcGetTransactionFromBlock(context) - 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) - require.Error(t, err) - }) - - t.Run("isn't traceable", func(t *testing.T) { - block.Index = 2 - require.NoError(t, context.DAO.StoreAsBlock(block, nil)) - v.Estack().PushVal(0) - v.Estack().PushVal(block.Hash().BytesBE()) - err := bcGetTransactionFromBlock(context) - 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, nil)) - v.Estack().PushVal(0) - v.Estack().PushVal(block.Hash().BytesLE()) - err := bcGetTransactionFromBlock(context) - 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, nil)) - v.Estack().PushVal(1) - v.Estack().PushVal(block.Hash().BytesBE()) - err := bcGetTransactionFromBlock(context) - 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) - 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) - require.NoError(t, err) - - _, ok := v.Estack().Pop().Item().(stackitem.Null) - require.True(t, ok) - }) -} - func TestContractIsStandard(t *testing.T) { v, ic, chain := createVM(t) defer chain.Close() diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 08dccd64d..7e90fbc36 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -39,16 +39,6 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 1 << 14, ParamCount: 1}, {Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 1 << 12, ParamCount: 2}, {Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 1 << 12, ParamCount: 1}, - {Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 1 << 16, - RequiredFlags: callflag.ReadStates, ParamCount: 1}, - {Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 1 << 4, - RequiredFlags: callflag.ReadStates}, - {Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1 << 15, - RequiredFlags: callflag.ReadStates, ParamCount: 1}, - {Name: interopnames.SystemBlockchainGetTransactionFromBlock, Func: bcGetTransactionFromBlock, Price: 1 << 15, - RequiredFlags: callflag.ReadStates, ParamCount: 2}, - {Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15, - RequiredFlags: callflag.ReadStates, ParamCount: 1}, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, RequiredFlags: callflag.AllowCall, ParamCount: 4}, {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, diff --git a/pkg/core/native/compatibility_test.go b/pkg/core/native/compatibility_test.go index de96eb982..f581264df 100644 --- a/pkg/core/native/compatibility_test.go +++ b/pkg/core/native/compatibility_test.go @@ -9,11 +9,12 @@ import ( // Compatibility test. hashes are taken directly from C# node. func TestNativeHashes(t *testing.T) { require.Equal(t, "a501d7d7d10983673b61b7a2d3a813b36f9f0e43", newManagement().Hash.StringLE()) - require.Equal(t, "f617baca689d1abddedda7c3b80675c4ac21e932", newNEO().Hash.StringLE()) - require.Equal(t, "75844530eb44f4715a42950bb59b4d7ace0b2f3d", newGAS().Hash.StringLE()) - require.Equal(t, "e21a28cfc1e662e152f668c86198141cc17b6c37", newPolicy().Hash.StringLE()) - require.Equal(t, "69b1909aaa14143b0624ba0d61d5cd3b8b67529d", newDesignate(false).Hash.StringLE()) - require.Equal(t, "b82bbf650f963dbf71577d10ea4077e711a13e7b", newOracle().Hash.StringLE()) + require.Equal(t, "971d69c6dd10ce88e7dfffec1dc603c6125a8764", newLedger().Hash.StringLE()) + require.Equal(t, "f61eebf573ea36593fd43aa150c055ad7906ab83", newNEO().Hash.StringLE()) + require.Equal(t, "70e2301955bf1e74cbb31d18c2f96972abadb328", newGAS().Hash.StringLE()) + require.Equal(t, "79bcd398505eb779df6e67e4be6c14cded08e2f2", newPolicy().Hash.StringLE()) + require.Equal(t, "597b1471bbce497b7809e2c8f10db67050008b02", newDesignate(false).Hash.StringLE()) + require.Equal(t, "8dc0e742cbdfdeda51ff8a8b78d46829144c80ee", newOracle().Hash.StringLE()) // Not yet a part of NEO. //require.Equal(t, "", newNotary().Hash.StringLE()()) } diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index 567042446..7a0479144 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -16,6 +16,7 @@ const reservedContractID = -100 // Contracts is a set of registered native contracts. type Contracts struct { Management *Management + Ledger *Ledger NEO *NEO GAS *GAS Policy *Policy @@ -60,6 +61,10 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { cs.Management = mgmt cs.Contracts = append(cs.Contracts, mgmt) + ledger := newLedger() + cs.Ledger = ledger + cs.Contracts = append(cs.Contracts, ledger) + gas := newGAS() neo := newNEO() neo.GAS = gas diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 768d5b177..07680ce7a 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -48,7 +48,7 @@ type roleData struct { } const ( - designateContractID = -5 + designateContractID = -6 // maxNodeCount is the maximum number of nodes to set the role for. maxNodeCount = 32 diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go new file mode 100644 index 000000000..b521238ce --- /dev/null +++ b/pkg/core/native/ledger.go @@ -0,0 +1,226 @@ +package native + +import ( + "fmt" + "math" + "math/big" + + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" + "github.com/nspcc-dev/neo-go/pkg/core/dao" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Ledger provides an interface to blocks/transactions storage for smart +// contracts. It's not a part of the proper chain's state, so it's just a +// proxy between regular Blockchain/DAO interface and smart contracts. +type Ledger struct { + interop.ContractMD +} + +const ( + ledgerContractID = -2 + + prefixBlockHash = 9 + prefixCurrentBlock = 12 + prefixBlock = 5 + prefixTransaction = 11 +) + +// newLedger creates new Ledger native contract. +func newLedger() *Ledger { + var l = &Ledger{ + ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID), + } + desc := newDescriptor("currentHash", smartcontract.Hash256Type) + md := newMethodAndPrice(l.currentHash, 1000000, callflag.ReadStates) + l.AddMethod(md, desc) + + desc = newDescriptor("currentIndex", smartcontract.IntegerType) + md = newMethodAndPrice(l.currentIndex, 1000000, callflag.ReadStates) + l.AddMethod(md, desc) + + desc = newDescriptor("getBlock", smartcontract.ArrayType, + manifest.NewParameter("indexOrHash", smartcontract.ByteArrayType)) + md = newMethodAndPrice(l.getBlock, 1000000, callflag.ReadStates) + l.AddMethod(md, desc) + + desc = newDescriptor("getTransaction", smartcontract.ArrayType, + manifest.NewParameter("hash", smartcontract.ByteArrayType)) + md = newMethodAndPrice(l.getTransaction, 1000000, callflag.ReadStates) + l.AddMethod(md, desc) + + desc = newDescriptor("getTransactionHeight", smartcontract.IntegerType, + manifest.NewParameter("hash", smartcontract.ByteArrayType)) + md = newMethodAndPrice(l.getTransactionHeight, 1000000, callflag.ReadStates) + l.AddMethod(md, desc) + + desc = newDescriptor("getTransactionFromBlock", smartcontract.ArrayType, + manifest.NewParameter("blockIndexOrHash", smartcontract.ByteArrayType), + manifest.NewParameter("txIndex", smartcontract.IntegerType)) + md = newMethodAndPrice(l.getTransactionFromBlock, 2000000, callflag.ReadStates) + l.AddMethod(md, desc) + + return l +} + +// Metadata implements Contract interface. +func (l *Ledger) Metadata() *interop.ContractMD { + return &l.ContractMD +} + +// Initialize implements Contract interface. +func (l *Ledger) Initialize(ic *interop.Context) error { + return nil +} + +// OnPersist implements Contract interface. +func (l *Ledger) OnPersist(ic *interop.Context) error { + // Actual block/tx processing is done in Blockchain.storeBlock(). + // Even though C# node add them to storage here, they're not + // accessible to smart contracts (see isTraceableBlock()), thus + // the end effect is the same. + return nil +} + +// PostPersist implements Contract interface. +func (l *Ledger) PostPersist(ic *interop.Context) error { + return nil // Actual block/tx processing is done in Blockchain.storeBlock(). +} + +// currentHash implements currentHash SC method. +func (l *Ledger) currentHash(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.Make(ic.Chain.CurrentBlockHash().BytesBE()) +} + +// currentIndex implements currentIndex SC method. +func (l *Ledger) currentIndex(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.Make(ic.Chain.BlockHeight()) +} + +// getBlock implements getBlock SC method. +func (l *Ledger) getBlock(ic *interop.Context, params []stackitem.Item) stackitem.Item { + hash := getBlockHashFromItem(ic.Chain, params[0]) + block, err := ic.Chain.GetBlock(hash) + if err != nil || !isTraceableBlock(ic.Chain, block.Index) { + return stackitem.Null{} + } + return BlockToStackItem(block) +} + +// getTransaction returns transaction to the SC. +func (l *Ledger) getTransaction(ic *interop.Context, params []stackitem.Item) stackitem.Item { + tx, h, err := getTransactionAndHeight(ic.DAO, params[0]) + if err != nil || !isTraceableBlock(ic.Chain, h) { + return stackitem.Null{} + } + return TransactionToStackItem(tx) +} + +// getTransactionHeight returns transaction height to the SC. +func (l *Ledger) getTransactionHeight(ic *interop.Context, params []stackitem.Item) stackitem.Item { + _, h, err := getTransactionAndHeight(ic.DAO, params[0]) + if err != nil || !isTraceableBlock(ic.Chain, h) { + return stackitem.Make(-1) + } + return stackitem.Make(h) +} + +// getTransactionFromBlock returns transaction with the given index from the +// block with height or hash specified. +func (l *Ledger) getTransactionFromBlock(ic *interop.Context, params []stackitem.Item) stackitem.Item { + hash := getBlockHashFromItem(ic.Chain, params[0]) + index := toUint32(params[1]) + block, err := ic.Chain.GetBlock(hash) + if err != nil || !isTraceableBlock(ic.Chain, block.Index) { + return stackitem.Null{} + } + if index >= uint32(len(block.Transactions)) { + panic("wrong transaction index") + } + return TransactionToStackItem(block.Transactions[index]) +} + +// isTraceableBlock defines whether we're able to give information about +// the block with index specified. +func isTraceableBlock(bc blockchainer.Blockchainer, index uint32) bool { + height := bc.BlockHeight() + MaxTraceableBlocks := bc.GetConfig().MaxTraceableBlocks + return index <= height && index+MaxTraceableBlocks > height +} + +// getBlockHashFromItem converts given stackitem.Item to block hash using given +// Blockchainer if needed. Interop functions accept both block numbers and +// block hashes as parameters, thus this function is needed. It's supposed to +// be called within VM context, so it panics if anything goes wrong. +func getBlockHashFromItem(bc blockchainer.Blockchainer, item stackitem.Item) util.Uint256 { + bigindex, err := item.TryInteger() + if err == nil && bigindex.IsInt64() { + index := bigindex.Int64() + if index < 0 || index > math.MaxUint32 { + panic("bad block index") + } + if uint32(index) > bc.BlockHeight() { + panic(fmt.Errorf("no block with index %d", index)) + } + return bc.GetHeaderHash(int(index)) + } + bytes, err := item.TryBytes() + if err != nil { + panic(err) + } + hash, err := util.Uint256DecodeBytesBE(bytes) + if err != nil { + panic(err) + } + return hash +} + +// getTransactionAndHeight returns transaction and its height if it's present +// on the chain. It panics if anything goes wrong. +func getTransactionAndHeight(cd *dao.Cached, item stackitem.Item) (*transaction.Transaction, uint32, error) { + hashbytes, err := item.TryBytes() + if err != nil { + panic(err) + } + hash, err := util.Uint256DecodeBytesBE(hashbytes) + if err != nil { + panic(err) + } + return cd.GetTransaction(hash) +} + +// 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)))), + }) +} + +// 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), + }) +} diff --git a/pkg/core/native/name_service.go b/pkg/core/native/name_service.go index 0690cb414..cfc3480ca 100644 --- a/pkg/core/native/name_service.go +++ b/pkg/core/native/name_service.go @@ -54,7 +54,7 @@ const ( ) const ( - nameServiceID = -7 + nameServiceID = -8 prefixRoots = 10 prefixDomainPrice = 22 diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 414cb3cdd..00111cea1 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -18,7 +18,7 @@ type GAS struct { NEO *NEO } -const gasContractID = -3 +const gasContractID = -4 // GASFactor is a divisor for finding GAS integral value. const GASFactor = NEOTotalSupply diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index d39100f83..614300ab2 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -51,7 +51,7 @@ type NEO struct { } const ( - neoContractID = -2 + neoContractID = -3 // NEOTotalSupply is the total amount of NEO in the system. NEOTotalSupply = 100000000 // prefixCandidate is a prefix used to store validator's data. diff --git a/pkg/core/native/nativenames/names.go b/pkg/core/native/nativenames/names.go index eb007cbbc..0f37e5ba8 100644 --- a/pkg/core/native/nativenames/names.go +++ b/pkg/core/native/nativenames/names.go @@ -3,6 +3,7 @@ package nativenames // Names of all native contracts. const ( Management = "ContractManagement" + Ledger = "LedgerContract" Neo = "NeoToken" Gas = "GasToken" Policy = "PolicyContract" diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index ed775d7d4..ce48494f2 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -46,7 +46,7 @@ type Oracle struct { } const ( - oracleContractID = -6 + oracleContractID = -7 maxURLLength = 256 maxFilterLength = 128 maxCallbackLength = 32 diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 4afdef5ce..a9f0de9c7 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -21,7 +21,7 @@ import ( ) const ( - policyContractID = -4 + policyContractID = -5 defaultMaxBlockSize = 1024 * 256 defaultMaxTransactionsPerBlock = 512 diff --git a/pkg/core/native_ledger_test.go b/pkg/core/native_ledger_test.go new file mode 100644 index 000000000..cb25ccf14 --- /dev/null +++ b/pkg/core/native_ledger_test.go @@ -0,0 +1,177 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +func TestLedgerGetTransactionHeight(t *testing.T) { + _, tx, _, chain := createVMAndTX(t) + defer chain.Close() + + ledger := chain.contracts.ByName(nativenames.Ledger).Metadata().Hash + + for i := 0; i < 13; i++ { + require.NoError(t, chain.AddBlock(chain.newBlock())) + } + require.NoError(t, chain.dao.StoreAsTransaction(tx, 13, nil)) + t.Run("good", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionHeight", tx.Hash().BytesBE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Make(13)) + }) + t.Run("bad", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionHeight", tx.Hash().BytesLE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Make(-1)) + }) + t.Run("not a hash", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionHeight", []byte{1}) + require.NoError(t, err) + checkFAULTState(t, res) + }) +} + +func TestLedgerGetTransaction(t *testing.T) { + _, tx, _, chain := createVMAndTX(t) + defer chain.Close() + ledger := chain.contracts.ByName(nativenames.Ledger).Metadata().Hash + + t.Run("success", func(t *testing.T) { + require.NoError(t, chain.dao.StoreAsTransaction(tx, 0, nil)) + + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransaction", tx.Hash().BytesBE()) + require.NoError(t, err) + require.Equal(t, vm.HaltState, res.VMState, res.FaultException) + require.Equal(t, 1, len(res.Stack)) + value := res.Stack[0].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, chain.dao.StoreAsTransaction(tx, 2, nil)) // block 1 is added above + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransaction", tx.Hash().BytesBE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) + t.Run("bad hash", func(t *testing.T) { + require.NoError(t, chain.dao.StoreAsTransaction(tx, 0, nil)) + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransaction", tx.Hash().BytesLE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) +} + +func TestLedgerGetTransactionFromBlock(t *testing.T) { + chain := newTestChain(t) + defer chain.Close() + ledger := chain.contracts.ByName(nativenames.Ledger).Metadata().Hash + + res, err := invokeContractMethod(chain, 100000000, ledger, "currentIndex") // adds a block + require.NoError(t, err) + checkResult(t, res, stackitem.Make(0)) + bhash := chain.GetHeaderHash(1) + b, err := chain.GetBlock(bhash) + require.NoError(t, err) + + t.Run("success", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesBE(), int64(0)) + require.NoError(t, err) + require.Equal(t, vm.HaltState, res.VMState, res.FaultException) + require.Equal(t, 1, len(res.Stack)) + value := res.Stack[0].Value() + + actual, ok := value.([]stackitem.Item) + require.True(t, ok) + require.Equal(t, b.Transactions[0].Hash().BytesBE(), actual[0].Value().([]byte)) + }) + t.Run("bad transaction index", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesBE(), int64(1)) + require.NoError(t, err) + checkFAULTState(t, res) + }) + t.Run("invalid block hash (>int64)", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesBE()[:10], int64(0)) + require.NoError(t, err) + checkFAULTState(t, res) + }) + t.Run("invalid block hash (int64)", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesBE()[:6], int64(0)) + require.NoError(t, err) + checkFAULTState(t, res) + }) + t.Run("bad block hash", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesLE(), int64(0)) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) + t.Run("isn't traceable", func(t *testing.T) { + b.Index = chain.BlockHeight() + 1 + require.NoError(t, chain.dao.StoreAsBlock(b, nil)) + res, err := invokeContractMethod(chain, 100000000, ledger, "getTransactionFromBlock", bhash.BytesBE(), int64(0)) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) +} + +func TestLedgerGetBlock(t *testing.T) { + chain := newTestChain(t) + defer chain.Close() + ledger := chain.contracts.ByName(nativenames.Ledger).Metadata().Hash + + bhash := chain.GetHeaderHash(0) + res, err := invokeContractMethod(chain, 100000000, ledger, "currentHash") // adds a block + require.NoError(t, err) + checkResult(t, res, stackitem.Make(bhash.BytesBE())) + bhash = chain.GetHeaderHash(1) + b, err := chain.GetBlock(bhash) + require.NoError(t, err) + + t.Run("success", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getBlock", bhash.BytesBE()) + require.NoError(t, err) + require.Equal(t, vm.HaltState, res.VMState, res.FaultException) + require.Equal(t, 1, len(res.Stack)) + value := res.Stack[0].Value() + + actual, ok := value.([]stackitem.Item) + require.True(t, ok) + require.Equal(t, 8, len(actual)) + require.Equal(t, b.Hash().BytesBE(), actual[0].Value().([]byte)) + require.Equal(t, int64(b.Version), actual[1].Value().(*big.Int).Int64()) + require.Equal(t, b.PrevHash.BytesBE(), actual[2].Value().([]byte)) + require.Equal(t, b.MerkleRoot.BytesBE(), actual[3].Value().([]byte)) + require.Equal(t, int64(b.Timestamp), actual[4].Value().(*big.Int).Int64()) + require.Equal(t, int64(b.Index), actual[5].Value().(*big.Int).Int64()) + require.Equal(t, b.NextConsensus.BytesBE(), actual[6].Value().([]byte)) + require.Equal(t, int64(len(b.Transactions)), actual[7].Value().(*big.Int).Int64()) + }) + t.Run("bad hash", func(t *testing.T) { + res, err := invokeContractMethod(chain, 100000000, ledger, "getBlock", bhash.BytesLE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) + t.Run("isn't traceable", func(t *testing.T) { + b.Index = chain.BlockHeight() + 1 + require.NoError(t, chain.dao.StoreAsBlock(b, nil)) + res, err := invokeContractMethod(chain, 100000000, ledger, "getBlock", bhash.BytesBE()) + require.NoError(t, err) + checkResult(t, res, stackitem.Null{}) + }) +} diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go deleted file mode 100644 index fffd667fa..000000000 --- a/pkg/interop/blockchain/blockchain.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Package blockchain provides functions to access various blockchain data. -*/ -package blockchain - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop" -) - -// 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 interop.Hash256 - // 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 interop.Hash160 - // 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 -} - -// 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 interop.Hash256 - // Version of the block. - Version int - // PrevHash represents the hash (256 bit BE value in a 32 byte slice) of the - // previous block. - PrevHash interop.Hash256 - // MerkleRoot represents the root hash (256 bit BE value in a 32 byte slice) - // of a transaction list. - MerkleRoot interop.Hash256 - // Timestamp represents millisecond-precision block timestamp. - Timestamp int - // Index represents the height of the block. - Index int - // NextConsensus represents contract address of the next miner (160 bit BE - // value in a 20 byte slice). - NextConsensus interop.Hash160 - // 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 -// the previous (already accepted) block. This function uses -// `System.Blockchain.GetHeight` syscall. -func GetHeight() int { - return 0 -} - -// GetBlock returns block found by the given hash or index (with the same -// 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 -// format represented as a slice of 32 bytes). This function uses -// `System.Blockchain.GetTransaction` syscall. -func GetTransaction(hash interop.Hash256) *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) interop.Hash256 { - 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. -func GetTransactionHeight(hash interop.Hash256) int { - return 0 -} diff --git a/pkg/interop/runtime/engine.go b/pkg/interop/runtime/engine.go index 64e0b7b66..a8180b457 100644 --- a/pkg/interop/runtime/engine.go +++ b/pkg/interop/runtime/engine.go @@ -2,15 +2,38 @@ package runtime import ( "github.com/nspcc-dev/neo-go/pkg/interop" - "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" ) +// 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 interop.Hash256 + // 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 interop.Hash160 + // 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 +} + // GetScriptContainer returns the transaction that initially triggered current // execution context. It never changes in a single execution, no matter how deep // this execution goes. This function uses // `System.Runtime.GetScriptContainer` syscall. -func GetScriptContainer() *blockchain.Transaction { - return &blockchain.Transaction{} +func GetScriptContainer() *Transaction { + return &Transaction{} } // GetExecutingScriptHash returns script hash (160 bit in BE form represented diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index a2da8e512..b99011ef4 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -28,9 +28,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" - rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster" "github.com/nspcc-dev/neo-go/pkg/rpc/response" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" + rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -59,7 +59,7 @@ type rpcTestCase struct { } const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a" -const deploymentTxHash = "e6ffce4533231c4efdea9a65c7abc0e7073d96a4ebc66f402db3a84b6f8939ef" +const deploymentTxHash = "9a9d6b0876d1e6cfd68efadd0facaaba7e07efbe7b24282d094a0893645581f3" const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70" const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943" @@ -182,7 +182,7 @@ var rpcTestCases = map[string][]rpcTestCase{ check: func(t *testing.T, e *executor, cs interface{}) { res, ok := cs.(*state.Contract) require.True(t, ok) - assert.Equal(t, int32(-4), res.ID) + assert.Equal(t, int32(-5), res.ID) }, }, { @@ -192,7 +192,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "negative, bad ID", - params: `[-8]`, + params: `[-100]`, fail: true, }, { diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index ac9b113acf0b840db78298304731f972261b4444..d8320664d243bb798d2ba3d249fbc6e524d007b9 100644 GIT binary patch delta 2530 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 Date: Wed, 3 Feb 2021 22:37:24 +0300 Subject: [PATCH 2/3] core: allow to read states for non-contract verifications C# does it this way now: callFlags = !witness.VerificationScript.IsStandardContract() ? CallFlags.ReadStates : CallFlags.None So non-standard scripts _always_ have access to state and standards ones just don't care (their code is known and it doesn't touch state). --- pkg/core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 20481bd02..3173bb3ed 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1744,7 +1744,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, if bc.contracts.ByHash(hash) != nil { return ErrNativeContractWitness } - v.LoadScriptWithFlags(witness.VerificationScript, callflag.NoneFlag) + v.LoadScriptWithFlags(witness.VerificationScript, callflag.ReadStates) } else { cs, err := ic.GetContract(hash) if err != nil { From edcee68f9147235c52ce8cc18e2c90489a770e12 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 3 Feb 2021 23:09:52 +0300 Subject: [PATCH 3/3] docs: add a note about getstorage Initially I wanted to emulate it, but probably there is no much gain in doing so. It can be done if need be. --- docs/rpc.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/rpc.md b/docs/rpc.md index 4f0462271..68c68fdf9 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -84,6 +84,11 @@ node where it only works for addresses from opened wallet. It's possible to get non-native contract state by its ID, unlike with C# node where it only works for native contracts. +##### `getstorage` + +This method doesn't work for the Ledger contract, you can get data via regular +`getblock` and `getrawtransaction` calls. + ### Unsupported methods Methods listed down below are not going to be supported for various reasons