contracts: decipher oracle test contract

Make it a proper Go contract, opcodes are fun until you want to change
something in them. Part of #2412.
This commit is contained in:
Roman Khimov 2022-07-22 11:44:11 +03:00
parent 593fa4cac8
commit da2db74bc9
8 changed files with 45 additions and 77 deletions

View file

@ -21,8 +21,10 @@ var (
helper2ContractNEFPath = filepath.Join("management_helper", "management_helper2.nef")
helper2ContractManifestPath = filepath.Join("management_helper", "management_helper2.manifest.json")
oracleContractNEFPath = filepath.Join("oracle_contract", "oracle.nef")
oracleContractManifestPath = filepath.Join("oracle_contract", "oracle.manifest.json")
oracleContractModPath = "oracle_contract"
oracleContractYAMLPath = filepath.Join(oracleContractModPath, "oracle.yml")
oracleContractNEFPath = filepath.Join(oracleContractModPath, "oracle.nef")
oracleContractManifestPath = filepath.Join(oracleContractModPath, "oracle.manifest.json")
)
// GetTestContractState reads 2 pre-compiled contracts generated by

View file

@ -41,81 +41,10 @@ func TestGenerateHelperContracts(t *testing.T) {
// Oracle and StdLib native hashes and saves the generated NEF and manifest to `oracle_contract` folder.
// Set `saveState` flag to true and run the test to rewrite NEF and manifest files.
func generateOracleContract(t *testing.T, saveState bool) {
bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
c.P2PSigExtensions = true
})
e := neotest.NewExecutor(t, bc, validator, committee)
oracleHash := e.NativeHash(t, nativenames.Oracle)
stdHash := e.NativeHash(t, nativenames.StdLib)
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(callflag.All))
emit.String(w.BinWriter, "request")
emit.Bytes(w.BinWriter, oracleHash.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET)
// `handle` method aborts if len(userData) == 2 and does NOT perform witness checks
// for the sake of contract code simplicity (the contract is used in multiple testchains).
offset := w.Len()
emit.Opcodes(w.BinWriter, opcode.OVER)
emit.Opcodes(w.BinWriter, opcode.SIZE)
emit.Int(w.BinWriter, 2)
emit.Instruction(w.BinWriter, opcode.JMPNE, []byte{3})
emit.Opcodes(w.BinWriter, opcode.ABORT)
emit.Int(w.BinWriter, 4) // url, userData, code, result
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`)
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`)
emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes
emit.String(w.BinWriter, "lastOracleResponse")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcodes(w.BinWriter, opcode.RET)
m := manifest.NewManifest("TestOracle")
m.ABI.Methods = []manifest.Method{
{
Name: "requestURL",
Offset: 0,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("filter", smartcontract.StringType),
manifest.NewParameter("callback", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("gasForResponse", smartcontract.IntegerType),
},
ReturnType: smartcontract.VoidType,
},
{
Name: "handle",
Offset: offset,
Parameters: []manifest.Parameter{
manifest.NewParameter("url", smartcontract.StringType),
manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("code", smartcontract.IntegerType),
manifest.NewParameter("result", smartcontract.ByteArrayType),
},
ReturnType: smartcontract.VoidType,
},
}
perm := manifest.NewPermission(manifest.PermissionHash, oracleHash)
perm.Methods.Add("request")
m.Permissions = append(m.Permissions, *perm)
// Generate NEF file.
script := w.Bytes()
ne, err := nef.NewFile(script)
require.NoError(t, err)
ctr := neotest.CompileFile(t, util.Uint160{}, oracleContractModPath, oracleContractYAMLPath)
// Write NEF file.
bytes, err := ne.Bytes()
bytes, err := ctr.NEF.Bytes()
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractNEFPath, bytes, os.ModePerm)
@ -123,7 +52,7 @@ func generateOracleContract(t *testing.T, saveState bool) {
}
// Write manifest file.
mData, err := json.Marshal(m)
mData, err := json.Marshal(ctr.Manifest)
require.NoError(t, err)
if saveState {
err = os.WriteFile(oracleContractManifestPath, mData, os.ModePerm)

View file

@ -0,0 +1,5 @@
module github.com/nspcc-dev/neo-go/examples/oracle
go 1.17
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b

View file

@ -0,0 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b h1:J7QZNmnO84esVuPbBo88fwAG4XVnDjlSTiO1ewLNCkQ=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=

View file

@ -0,0 +1,24 @@
package oraclecontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop/native/oracle"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
// RequestURL accepts a complete set of parameters to make an oracle request and
// performs it.
func RequestURL(url string, filter []byte, callback string, userData interface{}, gasForResponse int) {
oracle.Request(url, filter, callback, userData, gasForResponse)
}
// Handle is a response handler that writes response data to the storage.
func Handle(url string, data interface{}, code int, res []byte) {
// ABORT if len(data) == 2, some tests use this feature.
if data != nil && len(data.(string)) == 2 {
util.Abort()
}
params := []interface{}{url, data, code, res}
storage.Put(storage.GetContext(), "lastOracleResponse", std.Serialize(params))
}

View file

@ -1 +1 @@
{"name":"TestOracle","abi":{"methods":[{"name":"requestURL","offset":0,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"handle","offset":41,"parameters":[{"name":"url","type":"String"},{"name":"userData","type":"Any"},{"name":"code","type":"Integer"},{"name":"result","type":"ByteArray"}],"returntype":"Void","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","methods":["request"]}],"supportedstandards":[],"trusts":[],"extra":null}
{"name":"Oracle test","abi":{"methods":[{"name":"handle","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"data","type":"Any"},{"name":"code","type":"Integer"},{"name":"res","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"requestURL","offset":0,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"ByteArray"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["request"]}],"supportedstandards":[],"trusts":[],"extra":null}

View file

@ -0,0 +1,6 @@
name: "Oracle test"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: []
events:
permissions:
- methods: ["request"]