core: add System.Blockchain.GetTransactionFromBlock interop

This commit is contained in:
Anna Shaleva 2020-06-09 12:18:08 +03:00
parent 1e63ae4b3f
commit d692de5ea4
6 changed files with 103 additions and 9 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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