Skip to content

Commit

Permalink
Merge pull request #1034 from nspcc-dev/neo3/interop/gettransaction
Browse files Browse the repository at this point in the history
*: implement System.Blockchain.GetTransaction and System.Blockchain.GetBlock interops
  • Loading branch information
roman-khimov authored Jun 10, 2020
2 parents 7ddcc35 + 8b7abd3 commit 851e72f
Show file tree
Hide file tree
Showing 14 changed files with 326 additions and 395 deletions.
4 changes: 4 additions & 0 deletions pkg/compiler/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,10 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) {
emit.Opcode(c.prog.BinWriter, opcode.PACK)
}
emit.Syscall(c.prog.BinWriter, api)
switch name {
case "GetTransaction", "GetBlock":
c.emitConvert(stackitem.StructT)
}

// This NOP instruction is basically not needed, but if we do, we have a
// one to one matching avm file with neo-python which is very nice for debugging.
Expand Down
31 changes: 9 additions & 22 deletions pkg/compiler/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ var syscalls = map[string]map[string]string{
"GetVotes": "Neo.Account.GetVotes",
"IsStandard": "Neo.Account.IsStandard",
},
"attribute": {
"GetUsage": "Neo.Attribute.GetUsage",
"GetData": "Neo.Attribute.GetData",
},
"crypto": {
"ECDsaVerify": "Neo.Crypto.ECDsaVerify",
},
Expand Down Expand Up @@ -39,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": "Neo.Blockchain.GetTransaction",
"GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight",
"GetValidators": "Neo.Blockchain.GetValidators",
"GetAccount": "Neo.Blockchain.GetAccount",
"GetBlock": "System.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",
Expand All @@ -58,16 +55,6 @@ var syscalls = map[string]map[string]string{
"GetConsensusData": "Neo.Header.GetConsensusData",
"GetNextConsensus": "Neo.Header.GetNextConsensus",
},
"block": {
"GetTransactionCount": "Neo.Block.GetTransactionCount",
"GetTransactions": "Neo.Block.GetTransactions",
"GetTransaction": "Neo.Block.GetTransaction",
},
"transaction": {
"GetAttributes": "Neo.Transaction.GetAttributes",
"GetHash": "Neo.Transaction.GetHash",
"GetWitnesses": "Neo.Transaction.GetWitnesses",
},
"contract": {
"GetScript": "Neo.Contract.GetScript",
"IsPayable": "Neo.Contract.IsPayable",
Expand Down
17 changes: 7 additions & 10 deletions pkg/core/gas_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,19 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 {
}

const (
neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create
neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate
antSharesContractCreate = 0x2a28d29b // AntShares.Contract.Create
antSharesContractMigrate = 0xa934c8bb // AntShares.Contract.Migrate
systemStoragePut = 0x84183fe6 // System.Storage.Put
systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx
neoStoragePut = 0xf541a152 // Neo.Storage.Put
antSharesStoragePut = 0x5f300a9e // AntShares.Storage.Put
neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create
neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate
systemStoragePut = 0x84183fe6 // System.Storage.Put
systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx
neoStoragePut = 0xf541a152 // Neo.Storage.Put
)

estack := v.Estack()

switch id {
case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate:
case neoContractCreate, neoContractMigrate:
return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64()))
case systemStoragePut, systemStoragePutEx, neoStoragePut, antSharesStoragePut:
case systemStoragePut, systemStoragePutEx, neoStoragePut:
// price for storage PUT is 1 GAS per 1 KiB
keySize := len(estack.Peek(1).Bytes())
valSize := len(estack.Peek(2).Bytes())
Expand Down
58 changes: 0 additions & 58 deletions pkg/core/interop_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,42 +57,6 @@ func headerGetNextConsensus(ic *interop.Context, v *vm.VM) error {
return nil
}

// txGetAttributes returns current transaction attributes.
func txGetAttributes(ic *interop.Context, v *vm.VM) error {
txInterface := v.Estack().Pop().Value()
tx, ok := txInterface.(*transaction.Transaction)
if !ok {
return errors.New("value is not a transaction")
}
if len(tx.Attributes) > vm.MaxArraySize {
return errors.New("too many attributes")
}
attrs := make([]stackitem.Item, 0, len(tx.Attributes))
for i := range tx.Attributes {
attrs = append(attrs, stackitem.NewInterop(&tx.Attributes[i]))
}
v.Estack().PushVal(attrs)
return nil
}

// txGetWitnesses returns current transaction witnesses.
func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
txInterface := v.Estack().Pop().Value()
tx, ok := txInterface.(*transaction.Transaction)
if !ok {
return errors.New("value is not a transaction")
}
if len(tx.Scripts) > vm.MaxArraySize {
return errors.New("too many outputs")
}
scripts := make([]stackitem.Item, 0, len(tx.Scripts))
for i := range tx.Scripts {
scripts = append(scripts, stackitem.NewInterop(&tx.Scripts[i]))
}
v.Estack().PushVal(scripts)
return nil
}

// witnessGetVerificationScript returns current witness' script.
func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
witInterface := v.Estack().Pop().Value()
Expand All @@ -107,28 +71,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
return nil
}

// attrGetData returns tx attribute data.
func attrGetData(ic *interop.Context, v *vm.VM) error {
attrInterface := v.Estack().Pop().Value()
attr, ok := attrInterface.(*transaction.Attribute)
if !ok {
return fmt.Errorf("%T is not an attribute", attr)
}
v.Estack().PushVal(attr.Data)
return nil
}

// attrGetData returns tx attribute usage field.
func attrGetUsage(ic *interop.Context, v *vm.VM) error {
attrInterface := v.Estack().Pop().Value()
attr, ok := attrInterface.(*transaction.Attribute)
if !ok {
return fmt.Errorf("%T is not an attribute", attr)
}
v.Estack().PushVal(int(attr.Usage))
return nil
}

// bcGetAccount returns or creates an account.
func bcGetAccount(ic *interop.Context, v *vm.VM) error {
accbytes := v.Estack().Pop().Bytes()
Expand Down
47 changes: 14 additions & 33 deletions pkg/core/interop_neo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,6 @@ func TestHeaderGetNextConsensus(t *testing.T) {
require.Equal(t, block.NextConsensus.BytesBE(), value)
}

func TestTxGetAttributes(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()

err := txGetAttributes(context, v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]stackitem.Item)
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
}

func TestWitnessGetVerificationScript(t *testing.T) {
v := vm.New()
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
Expand Down Expand Up @@ -280,28 +270,6 @@ func TestECDSAVerify(t *testing.T) {
})
}

func TestAttrGetData(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))

err := attrGetData(context, v)
require.NoError(t, err)
data := v.Estack().Pop().Value()
require.Equal(t, tx.Attributes[0].Data, data)
}

func TestAttrGetUsage(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))

err := attrGetUsage(context, v)
require.NoError(t, err)
usage := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(tx.Attributes[0].Usage)), usage)
}

func TestAccountGetScriptHash(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
Expand Down Expand Up @@ -337,12 +305,25 @@ 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))
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
}

Expand Down
Loading

0 comments on commit 851e72f

Please sign in to comment.