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.
This commit is contained in:
Anna Shaleva 2020-06-09 13:18:32 +03:00
parent e2187c0a96
commit 7a2d37cf7e
7 changed files with 93 additions and 12 deletions

View file

@ -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)
}

View file

@ -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",

View file

@ -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))

View file

@ -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
}

View file

@ -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)
})
}

View file

@ -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},

View file

@ -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