Merge pull request #1034 from nspcc-dev/neo3/interop/gettransaction
*: implement System.Blockchain.GetTransaction and System.Blockchain.GetBlock interops
This commit is contained in:
commit
851e72fd97
14 changed files with 326 additions and 395 deletions
|
@ -1053,6 +1053,10 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) {
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.PACK)
|
emit.Opcode(c.prog.BinWriter, opcode.PACK)
|
||||||
}
|
}
|
||||||
emit.Syscall(c.prog.BinWriter, api)
|
emit.Syscall(c.prog.BinWriter, api)
|
||||||
|
switch name {
|
||||||
|
case "GetTransaction", "GetBlock":
|
||||||
|
c.emitConvert(stackitem.StructT)
|
||||||
|
}
|
||||||
|
|
||||||
// This NOP instruction is basically not needed, but if we do, we have a
|
// 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.
|
// one to one matching avm file with neo-python which is very nice for debugging.
|
||||||
|
|
|
@ -7,10 +7,6 @@ var syscalls = map[string]map[string]string{
|
||||||
"GetVotes": "Neo.Account.GetVotes",
|
"GetVotes": "Neo.Account.GetVotes",
|
||||||
"IsStandard": "Neo.Account.IsStandard",
|
"IsStandard": "Neo.Account.IsStandard",
|
||||||
},
|
},
|
||||||
"attribute": {
|
|
||||||
"GetUsage": "Neo.Attribute.GetUsage",
|
|
||||||
"GetData": "Neo.Attribute.GetData",
|
|
||||||
},
|
|
||||||
"crypto": {
|
"crypto": {
|
||||||
"ECDsaVerify": "Neo.Crypto.ECDsaVerify",
|
"ECDsaVerify": "Neo.Crypto.ECDsaVerify",
|
||||||
},
|
},
|
||||||
|
@ -39,14 +35,15 @@ var syscalls = map[string]map[string]string{
|
||||||
"Deserialize": "Neo.Runtime.Deserialize",
|
"Deserialize": "Neo.Runtime.Deserialize",
|
||||||
},
|
},
|
||||||
"blockchain": {
|
"blockchain": {
|
||||||
"GetAccount": "Neo.Blockchain.GetAccount",
|
"GetAccount": "Neo.Blockchain.GetAccount",
|
||||||
"GetBlock": "Neo.Blockchain.GetBlock",
|
"GetBlock": "System.Blockchain.GetBlock",
|
||||||
"GetContract": "Neo.Blockchain.GetContract",
|
"GetContract": "Neo.Blockchain.GetContract",
|
||||||
"GetHeader": "Neo.Blockchain.GetHeader",
|
"GetHeader": "Neo.Blockchain.GetHeader",
|
||||||
"GetHeight": "Neo.Blockchain.GetHeight",
|
"GetHeight": "Neo.Blockchain.GetHeight",
|
||||||
"GetTransaction": "Neo.Blockchain.GetTransaction",
|
"GetTransaction": "System.Blockchain.GetTransaction",
|
||||||
"GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight",
|
"GetTransactionFromBlock": "System.Blockchain.GetTransactionFromBlock",
|
||||||
"GetValidators": "Neo.Blockchain.GetValidators",
|
"GetTransactionHeight": "System.Blockchain.GetTransactionHeight",
|
||||||
|
"GetValidators": "Neo.Blockchain.GetValidators",
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"GetIndex": "Neo.Header.GetIndex",
|
"GetIndex": "Neo.Header.GetIndex",
|
||||||
|
@ -58,16 +55,6 @@ var syscalls = map[string]map[string]string{
|
||||||
"GetConsensusData": "Neo.Header.GetConsensusData",
|
"GetConsensusData": "Neo.Header.GetConsensusData",
|
||||||
"GetNextConsensus": "Neo.Header.GetNextConsensus",
|
"GetNextConsensus": "Neo.Header.GetNextConsensus",
|
||||||
},
|
},
|
||||||
"block": {
|
|
||||||
"GetTransactionCount": "Neo.Block.GetTransactionCount",
|
|
||||||
"GetTransactions": "Neo.Block.GetTransactions",
|
|
||||||
"GetTransaction": "Neo.Block.GetTransaction",
|
|
||||||
},
|
|
||||||
"transaction": {
|
|
||||||
"GetAttributes": "Neo.Transaction.GetAttributes",
|
|
||||||
"GetHash": "Neo.Transaction.GetHash",
|
|
||||||
"GetWitnesses": "Neo.Transaction.GetWitnesses",
|
|
||||||
},
|
|
||||||
"contract": {
|
"contract": {
|
||||||
"GetScript": "Neo.Contract.GetScript",
|
"GetScript": "Neo.Contract.GetScript",
|
||||||
"IsPayable": "Neo.Contract.IsPayable",
|
"IsPayable": "Neo.Contract.IsPayable",
|
||||||
|
|
|
@ -41,22 +41,19 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create
|
neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create
|
||||||
neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate
|
neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate
|
||||||
antSharesContractCreate = 0x2a28d29b // AntShares.Contract.Create
|
systemStoragePut = 0x84183fe6 // System.Storage.Put
|
||||||
antSharesContractMigrate = 0xa934c8bb // AntShares.Contract.Migrate
|
systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx
|
||||||
systemStoragePut = 0x84183fe6 // System.Storage.Put
|
neoStoragePut = 0xf541a152 // Neo.Storage.Put
|
||||||
systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx
|
|
||||||
neoStoragePut = 0xf541a152 // Neo.Storage.Put
|
|
||||||
antSharesStoragePut = 0x5f300a9e // AntShares.Storage.Put
|
|
||||||
)
|
)
|
||||||
|
|
||||||
estack := v.Estack()
|
estack := v.Estack()
|
||||||
|
|
||||||
switch id {
|
switch id {
|
||||||
case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate:
|
case neoContractCreate, neoContractMigrate:
|
||||||
return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64()))
|
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
|
// price for storage PUT is 1 GAS per 1 KiB
|
||||||
keySize := len(estack.Peek(1).Bytes())
|
keySize := len(estack.Peek(1).Bytes())
|
||||||
valSize := len(estack.Peek(2).Bytes())
|
valSize := len(estack.Peek(2).Bytes())
|
||||||
|
|
|
@ -57,42 +57,6 @@ func headerGetNextConsensus(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
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.
|
// witnessGetVerificationScript returns current witness' script.
|
||||||
func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
||||||
witInterface := v.Estack().Pop().Value()
|
witInterface := v.Estack().Pop().Value()
|
||||||
|
@ -107,28 +71,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
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.
|
// bcGetAccount returns or creates an account.
|
||||||
func bcGetAccount(ic *interop.Context, v *vm.VM) error {
|
func bcGetAccount(ic *interop.Context, v *vm.VM) error {
|
||||||
accbytes := v.Estack().Pop().Bytes()
|
accbytes := v.Estack().Pop().Bytes()
|
||||||
|
|
|
@ -178,16 +178,6 @@ func TestHeaderGetNextConsensus(t *testing.T) {
|
||||||
require.Equal(t, block.NextConsensus.BytesBE(), value)
|
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) {
|
func TestWitnessGetVerificationScript(t *testing.T) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
|
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) {
|
func TestAccountGetScriptHash(t *testing.T) {
|
||||||
v, accState, context, chain := createVMAndAccState(t)
|
v, accState, context, chain := createVMAndAccState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
@ -337,12 +305,25 @@ func TestContractIsPayable(t *testing.T) {
|
||||||
|
|
||||||
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
// 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) {
|
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()
|
v := vm.New()
|
||||||
block := newDumbBlock()
|
block := newDumbBlock()
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
||||||
v.Estack().PushVal(stackitem.NewInterop(block))
|
|
||||||
return v, block, context, chain
|
return v, block, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"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/blockchainer"
|
||||||
|
@ -22,6 +23,9 @@ import (
|
||||||
const (
|
const (
|
||||||
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
||||||
MaxStorageKeyLen = 1024
|
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
|
// StorageContext contains storing script hash and read/write flag, it's used as
|
||||||
|
@ -49,6 +53,20 @@ func getBlockHashFromElement(bc blockchainer.Blockchainer, element *vm.Element)
|
||||||
return hash, nil
|
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.
|
// bcGetBlock returns current block.
|
||||||
func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
||||||
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
||||||
|
@ -56,10 +74,10 @@ func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
block, err := ic.Chain.GetBlock(hash)
|
block, err := ic.Chain.GetBlock(hash)
|
||||||
if err != nil {
|
if err != nil || !isTraceableBlock(ic, block.Index) {
|
||||||
v.Estack().PushVal([]byte{})
|
v.Estack().PushVal(stackitem.Null{})
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(stackitem.NewInterop(block))
|
v.Estack().PushVal(blockToStackItem(block))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -112,21 +130,65 @@ func getTransactionAndHeight(cd *dao.Cached, v *vm.VM) (*transaction.Transaction
|
||||||
return cd.GetTransaction(hash)
|
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.
|
// bcGetTransaction returns transaction.
|
||||||
func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
|
func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
|
||||||
tx, _, err := getTransactionAndHeight(ic.DAO, v)
|
tx, h, err := getTransactionAndHeight(ic.DAO, v)
|
||||||
|
if err != nil || !isTraceableBlock(ic, h) {
|
||||||
|
v.Estack().PushVal(stackitem.Null{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
v.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, v *vm.VM) error {
|
||||||
|
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(stackitem.NewInterop(tx))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetTransactionHeight returns transaction height.
|
// bcGetTransactionHeight returns transaction height.
|
||||||
func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error {
|
func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error {
|
||||||
_, h, err := getTransactionAndHeight(ic.DAO, v)
|
_, h, err := getTransactionAndHeight(ic.DAO, v)
|
||||||
if err != nil {
|
if err != nil || !isTraceableBlock(ic, h) {
|
||||||
return err
|
v.Estack().PushVal(-1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(h)
|
v.Estack().PushVal(h)
|
||||||
return nil
|
return nil
|
||||||
|
@ -188,63 +250,6 @@ func headerGetTimestamp(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// engineGetScriptContainer returns transaction that contains the script being
|
||||||
// run.
|
// run.
|
||||||
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
||||||
|
|
147
pkg/core/interop_system_test.go
Normal file
147
pkg/core/interop_system_test.go
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
|
@ -62,14 +62,12 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin
|
||||||
|
|
||||||
// All lists are sorted, keep 'em this way, please.
|
// All lists are sorted, keep 'em this way, please.
|
||||||
var systemInterops = []interop.Function{
|
var systemInterops = []interop.Function{
|
||||||
{Name: "System.Block.GetTransaction", Func: blockGetTransaction, Price: 1},
|
{Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250},
|
||||||
{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.GetContract", Func: bcGetContract, Price: 100},
|
{Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100},
|
||||||
{Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
{Name: "System.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
||||||
{Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
{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.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100},
|
||||||
{Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
|
{Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
|
||||||
{Name: "System.Contract.Call", Func: contractCall, Price: 1},
|
{Name: "System.Contract.Call", Func: contractCall, Price: 1},
|
||||||
{Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1},
|
{Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1},
|
||||||
|
@ -98,25 +96,16 @@ var systemInterops = []interop.Function{
|
||||||
{Name: "System.Storage.Put", Func: storagePut, Price: 0}, // These don't have static price in C# code.
|
{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.Storage.PutEx", Func: storagePutEx, Price: 0},
|
||||||
{Name: "System.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1},
|
{Name: "System.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1},
|
||||||
{Name: "System.Transaction.GetHash", Func: txGetHash, Price: 1},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var neoInterops = []interop.Function{
|
var neoInterops = []interop.Function{
|
||||||
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
||||||
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
||||||
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
|
{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},
|
|
||||||
{Name: "Neo.Blockchain.GetAccount", Func: bcGetAccount, Price: 100},
|
{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.GetContract", Func: bcGetContract, Price: 100},
|
||||||
{Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
{Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
||||||
{Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
{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.Create", Func: contractCreate, Price: 0},
|
||||||
{Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1},
|
{Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1},
|
||||||
{Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1},
|
{Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1},
|
||||||
|
@ -157,49 +146,11 @@ var neoInterops = []interop.Function{
|
||||||
{Name: "Neo.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1},
|
{Name: "Neo.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1},
|
||||||
{Name: "Neo.Storage.Put", Func: storagePut, Price: 0},
|
{Name: "Neo.Storage.Put", Func: storagePut, Price: 0},
|
||||||
{Name: "Neo.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1},
|
{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},
|
{Name: "Neo.Witness.GetVerificationScript", Func: witnessGetVerificationScript, Price: 100},
|
||||||
|
|
||||||
// Aliases.
|
// Aliases.
|
||||||
{Name: "Neo.Iterator.Next", Func: enumerator.Next, Price: 1},
|
{Name: "Neo.Iterator.Next", Func: enumerator.Next, Price: 1},
|
||||||
{Name: "Neo.Iterator.Value", Func: enumerator.Value, 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
|
// initIDinInteropsSlice initializes IDs from names in one given
|
||||||
|
|
|
@ -34,11 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
||||||
funcs := []func(*interop.Context, *vm.VM) error{
|
funcs := []func(*interop.Context, *vm.VM) error{
|
||||||
accountGetBalance,
|
accountGetBalance,
|
||||||
accountGetScriptHash,
|
accountGetScriptHash,
|
||||||
attrGetData,
|
|
||||||
attrGetUsage,
|
|
||||||
blockGetTransaction,
|
|
||||||
blockGetTransactionCount,
|
|
||||||
blockGetTransactions,
|
|
||||||
contractGetScript,
|
contractGetScript,
|
||||||
contractGetStorageContext,
|
contractGetStorageContext,
|
||||||
contractIsPayable,
|
contractIsPayable,
|
||||||
|
@ -55,9 +50,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
||||||
storageGet,
|
storageGet,
|
||||||
storagePut,
|
storagePut,
|
||||||
storagePutEx,
|
storagePutEx,
|
||||||
txGetAttributes,
|
|
||||||
txGetHash,
|
|
||||||
txGetWitnesses,
|
|
||||||
witnessGetVerificationScript,
|
witnessGetVerificationScript,
|
||||||
}
|
}
|
||||||
for _, f := range funcs {
|
for _, f := range funcs {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
Package block provides getters for Neo Block structure.
|
|
||||||
*/
|
|
||||||
package block
|
|
||||||
|
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/transaction"
|
|
||||||
|
|
||||||
// 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) []transaction.Transaction {
|
|
||||||
return []transaction.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{}
|
|
||||||
}
|
|
|
@ -5,12 +5,60 @@ package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/account"
|
"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/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/header"
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
// 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
|
// 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
|
// considered as not yet accepted (persisted) and thus you'll get an index of
|
||||||
|
@ -29,23 +77,30 @@ func GetHeader(heightOrHash interface{}) header.Header {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlock returns block found by the given hash or index (with the same
|
// 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
|
// encoding as for GetHeader). This function uses `System.Blockchain.GetBlock`
|
||||||
// of returned structure. This function uses `Neo.Blockchain.GetBlock` syscall.
|
// syscall.
|
||||||
func GetBlock(heightOrHash interface{}) block.Block {
|
func GetBlock(heightOrHash interface{}) Block {
|
||||||
return block.Block{}
|
return Block{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction returns transaction found by the given (256 bit in BE format
|
// GetTransaction returns transaction found by the given hash (256 bit in BE
|
||||||
// represented as a slice of 32 bytes). Refer to the `transaction` package for
|
// format represented as a slice of 32 bytes). This function uses
|
||||||
// possible uses of returned structure. This function uses
|
// `System.Blockchain.GetTransaction` syscall.
|
||||||
// `Neo.Blockchain.GetTransaction` syscall.
|
func GetTransaction(hash []byte) Transaction {
|
||||||
func GetTransaction(hash []byte) transaction.Transaction {
|
return Transaction{}
|
||||||
return transaction.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
|
// 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
|
// 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 {
|
func GetTransactionHeight(hash []byte) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,14 @@ framework.
|
||||||
*/
|
*/
|
||||||
package engine
|
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
|
// GetScriptContainer returns the transaction that initially triggered current
|
||||||
// execution context. It never changes in a single execution, no matter how deep
|
// 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
|
// this execution goes. This function uses
|
||||||
// returned value. This function uses `System.ExecutionEngine.GetScriptContainer`
|
// `System.ExecutionEngine.GetScriptContainer` syscall.
|
||||||
// syscall.
|
func GetScriptContainer() blockchain.Transaction {
|
||||||
func GetScriptContainer() transaction.Transaction {
|
return blockchain.Transaction{}
|
||||||
return transaction.Transaction{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExecutingScriptHash returns script hash (160 bit in BE form represented
|
// GetExecutingScriptHash returns script hash (160 bit in BE form represented
|
||||||
|
|
|
@ -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{}
|
|
||||||
}
|
|
Loading…
Reference in a new issue