Merge pull request #1702 from nspcc-dev/native/interop

Represent native contracts as interop packages
This commit is contained in:
Roman Khimov 2021-02-08 17:10:09 +03:00 committed by GitHub
commit 71494e6ae4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1016 additions and 51 deletions

View file

@ -275,6 +275,26 @@ func isSyscall(fun *funcScope) bool {
return ok return ok
} }
const interopPrefix = "github.com/nspcc-dev/neo-go/pkg/interop"
func isInteropPath(s string) bool { func isInteropPath(s string) bool {
return strings.HasPrefix(s, "github.com/nspcc-dev/neo-go/pkg/interop") return strings.HasPrefix(s, interopPrefix)
}
func isNativeHelpersPath(s string) bool {
return strings.HasPrefix(s, interopPrefix+"/native")
}
// canConvert returns true if type doesn't need to be converted on type assertion.
func canConvert(s string) bool {
if len(s) != 0 && s[0] == '*' {
s = s[1:]
}
if isInteropPath(s) {
s = s[len(interopPrefix):]
return s != "/iterator.Iterator" && s != "/storage.Context" &&
s != "/native/ledger.Block" && s != "/native/ledger.Transaction" &&
s != "/native/management.Contract"
}
return true
} }

View file

@ -821,13 +821,20 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
} }
f, ok = c.funcs[name] f, ok = c.funcs[name]
// @FIXME this could cause runtime errors. if ok {
f.selector = fun.X.(*ast.Ident) f.selector = fun.X.(*ast.Ident)
if !ok { isBuiltin = isCustomBuiltin(f)
} else {
typ := c.typeOf(fun)
if _, ok := typ.(*types.Signature); ok {
c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name) c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name)
return nil return nil
} }
isBuiltin = isCustomBuiltin(f)
ast.Walk(c, n.Args[0])
c.emitExplicitConvert(c.typeOf(n.Args[0]), typ)
return nil
}
case *ast.ArrayType: case *ast.ArrayType:
// For now we will assume that there are only byte slice conversions. // For now we will assume that there are only byte slice conversions.
// E.g. []byte("foobar") or []byte(scriptHash). // E.g. []byte("foobar") or []byte(scriptHash).
@ -1190,8 +1197,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// not the assertion type. // not the assertion type.
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
ast.Walk(c, n.X) ast.Walk(c, n.X)
typ := toNeoType(c.typeOf(n.Type)) goTyp := c.typeOf(n.Type)
if canConvert(goTyp.String()) {
typ := toNeoType(goTyp)
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)}) emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
}
return nil return nil
} }
return c return c
@ -1242,6 +1252,24 @@ func (c *codegen) processDefers() {
} }
} }
// emitExplicitConvert handles `someType(someValue)` conversions between string/[]byte.
// Rules for conversion:
// 1. interop.* types are converted to ByteArray if not already.
// 2. Otherwise convert between ByteArray/Buffer.
// 3. Rules for types which are not string/[]byte should already
// be enforced by go parser.
func (c *codegen) emitExplicitConvert(from, to types.Type) {
if isInteropPath(to.String()) {
if isByteSlice(from) && !isString(from) {
c.emitConvert(stackitem.ByteArrayT)
}
} else if isByteSlice(to) && !isByteSlice(from) {
c.emitConvert(stackitem.BufferT)
} else if isString(to) && !isString(from) {
c.emitConvert(stackitem.ByteArrayT)
}
}
func (c *codegen) rangeLoadKey() { func (c *codegen) rangeLoadKey() {
emit.Int(c.prog.BinWriter, 2) emit.Int(c.prog.BinWriter, 2)
emit.Opcodes(c.prog.BinWriter, emit.Opcodes(c.prog.BinWriter,
@ -1890,7 +1918,8 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
// Don't convert the function if it's not used. This will save a lot // Don't convert the function if it's not used. This will save a lot
// of bytecode space. // of bytecode space.
name := c.getFuncNameFromDecl(pkg.Path(), n) name := c.getFuncNameFromDecl(pkg.Path(), n)
if !isInitFunc(n) && !isDeployFunc(n) && funUsage.funcUsed(name) && !isInteropPath(pkg.Path()) { if !isInitFunc(n) && !isDeployFunc(n) && funUsage.funcUsed(name) &&
(!isInteropPath(pkg.Path()) || isNativeHelpersPath(pkg.Path())) {
c.convertFuncDecl(f, n, pkg) c.convertFuncDecl(f, n, pkg)
} }
} }

View file

@ -87,6 +87,37 @@ func TestTypeConversion(t *testing.T) {
eval(t, src, big.NewInt(42)) eval(t, src, big.NewInt(42))
} }
func TestSelectorTypeConversion(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/types"
import "github.com/nspcc-dev/neo-go/pkg/interop/util"
import "github.com/nspcc-dev/neo-go/pkg/interop"
func Main() int {
var a int
if util.Equals(types.Buffer(nil), nil) {
a += 1
}
// Buffer != ByteArray
if util.Equals(types.Buffer("\x12"), "\x12") {
a += 10
}
tmp := []byte{0x23}
if util.Equals(types.ByteString(tmp), "\x23") {
a += 100
}
addr := "aaaaaaaaaaaaaaaaaaaa"
buf := []byte(addr)
if util.Equals(interop.Hash160(addr), interop.Hash160(buf)) {
a += 1000
}
return a
}`
eval(t, src, big.NewInt(1101))
}
func TestTypeConversionString(t *testing.T) { func TestTypeConversionString(t *testing.T) {
src := `package foo src := `package foo
type mystr string type mystr string

View file

@ -239,7 +239,7 @@ func scAndVMInteropTypeFromExpr(named *types.Named) (smartcontract.ParamType, st
name := named.Obj().Name() name := named.Obj().Name()
pkg := named.Obj().Pkg().Name() pkg := named.Obj().Pkg().Name()
switch pkg { switch pkg {
case "runtime", "contract": case "ledger", "contract":
return smartcontract.ArrayType, stackitem.ArrayT // Block, Transaction, Contract return smartcontract.ArrayType, stackitem.ArrayT // Block, Transaction, Contract
case "interop": case "interop":
if name != "Interface" { if name != "Interface" {

View file

@ -16,7 +16,7 @@ func TestCodeGen_DebugInfo(t *testing.T) {
src := `package foo src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop" import "github.com/nspcc-dev/neo-go/pkg/interop"
import "github.com/nspcc-dev/neo-go/pkg/interop/storage" import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" import "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
func Main(op string) bool { func Main(op string) bool {
var s string var s string
_ = s _ = s
@ -47,7 +47,7 @@ func unexportedMethod() int { return 1 }
func MethodParams(addr interop.Hash160, h interop.Hash256, func MethodParams(addr interop.Hash160, h interop.Hash256,
sig interop.Signature, pub interop.PublicKey, sig interop.Signature, pub interop.PublicKey,
inter interop.Interface, inter interop.Interface,
ctx storage.Context, tx runtime.Transaction) bool { ctx storage.Context, tx ledger.Transaction) bool {
return true return true
} }
type MyStruct struct {} type MyStruct struct {}

271
pkg/compiler/native_test.go Normal file
View file

@ -0,0 +1,271 @@
package compiler_test
import (
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/nameservice"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/native/notary"
"github.com/nspcc-dev/neo-go/pkg/interop/native/oracle"
"github.com/nspcc-dev/neo-go/pkg/interop/native/policy"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestContractHashes(t *testing.T) {
cs := native.NewContracts(true)
require.Equal(t, []byte(neo.Hash), cs.NEO.Hash.BytesBE())
require.Equal(t, []byte(gas.Hash), cs.GAS.Hash.BytesBE())
require.Equal(t, []byte(oracle.Hash), cs.Oracle.Hash.BytesBE())
require.Equal(t, []byte(roles.Hash), cs.Designate.Hash.BytesBE())
require.Equal(t, []byte(policy.Hash), cs.Policy.Hash.BytesBE())
require.Equal(t, []byte(nameservice.Hash), cs.NameService.Hash.BytesBE())
require.Equal(t, []byte(ledger.Hash), cs.Ledger.Hash.BytesBE())
require.Equal(t, []byte(management.Hash), cs.Management.Hash.BytesBE())
require.Equal(t, []byte(notary.Hash), cs.Notary.Hash.BytesBE())
}
// testPrintHash is a helper for updating contract hashes.
func testPrintHash(u util.Uint160) {
fmt.Print(`"`)
for _, b := range u.BytesBE() {
fmt.Printf("\\x%02x", b)
}
fmt.Println(`"`)
}
func TestContractParameterTypes(t *testing.T) {
require.EqualValues(t, management.AnyType, smartcontract.AnyType)
require.EqualValues(t, management.BoolType, smartcontract.BoolType)
require.EqualValues(t, management.IntegerType, smartcontract.IntegerType)
require.EqualValues(t, management.ByteArrayType, smartcontract.ByteArrayType)
require.EqualValues(t, management.StringType, smartcontract.StringType)
require.EqualValues(t, management.Hash160Type, smartcontract.Hash160Type)
require.EqualValues(t, management.Hash256Type, smartcontract.Hash256Type)
require.EqualValues(t, management.PublicKeyType, smartcontract.PublicKeyType)
require.EqualValues(t, management.SignatureType, smartcontract.SignatureType)
require.EqualValues(t, management.ArrayType, smartcontract.ArrayType)
require.EqualValues(t, management.MapType, smartcontract.MapType)
require.EqualValues(t, management.InteropInterfaceType, smartcontract.InteropInterfaceType)
require.EqualValues(t, management.VoidType, smartcontract.VoidType)
}
func TestRoleManagementRole(t *testing.T) {
require.EqualValues(t, native.RoleOracle, roles.Oracle)
require.EqualValues(t, native.RoleStateValidator, roles.StateValidator)
require.EqualValues(t, native.RoleP2PNotary, roles.P2PNotary)
}
func TestNameServiceRecordType(t *testing.T) {
require.EqualValues(t, native.RecordTypeA, nameservice.TypeA)
require.EqualValues(t, native.RecordTypeCNAME, nameservice.TypeCNAME)
require.EqualValues(t, native.RecordTypeTXT, nameservice.TypeTXT)
require.EqualValues(t, native.RecordTypeAAAA, nameservice.TypeAAAA)
}
type nativeTestCase struct {
method string
params []string
}
// Here we test that corresponding method does exist, is invoked and correct value is returned.
func TestNativeHelpersCompile(t *testing.T) {
cs := native.NewContracts(true)
u160 := `interop.Hash160("aaaaaaaaaaaaaaaaaaaa")`
u256 := `interop.Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
pub := `interop.PublicKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
nep17TestCases := []nativeTestCase{
{"balanceOf", []string{u160}},
{"decimals", nil},
{"symbol", nil},
{"totalSupply", nil},
{"transfer", []string{u160, u160, "123", "nil"}},
}
runNativeTestCases(t, cs.NEO.ContractMD, "neo", append([]nativeTestCase{
{"getCandidates", nil},
{"getCommittee", nil},
{"getGasPerBlock", nil},
{"getNextBlockValidators", nil},
{"registerCandidate", []string{pub}},
{"setGasPerBlock", []string{"1"}},
{"vote", []string{u160, pub}},
{"unclaimedGas", []string{u160, "123"}},
{"unregisterCandidate", []string{pub}},
}, nep17TestCases...))
runNativeTestCases(t, cs.GAS.ContractMD, "gas", nep17TestCases)
runNativeTestCases(t, cs.Oracle.ContractMD, "oracle", []nativeTestCase{
{"request", []string{`"url"`, "nil", `"callback"`, "nil", "123"}},
})
runNativeTestCases(t, cs.Designate.ContractMD, "roles", []nativeTestCase{
{"designateAsRole", []string{"1", "[]interop.PublicKey{}"}},
{"getDesignatedByRole", []string{"1", "1000"}},
})
runNativeTestCases(t, cs.Policy.ContractMD, "policy", []nativeTestCase{
{"blockAccount", []string{u160}},
{"getExecFeeFactor", nil},
{"getFeePerByte", nil},
{"getMaxBlockSize", nil},
{"getMaxBlockSystemFee", nil},
{"getMaxTransactionsPerBlock", nil},
{"getStoragePrice", nil},
{"isBlocked", []string{u160}},
{"setExecFeeFactor", []string{"42"}},
{"setFeePerByte", []string{"42"}},
{"setMaxBlockSize", []string{"42"}},
{"setMaxBlockSystemFee", []string{"42"}},
{"setMaxTransactionsPerBlock", []string{"42"}},
{"setStoragePrice", []string{"42"}},
{"unblockAccount", []string{u160}},
})
runNativeTestCases(t, cs.NameService.ContractMD, "nameservice", []nativeTestCase{
// nonfungible
{"symbol", nil},
{"decimals", nil},
{"totalSupply", nil},
{"ownerOf", []string{`"neo.com"`}},
{"balanceOf", []string{u160}},
{"properties", []string{`"neo.com"`}},
{"tokens", nil},
{"tokensOf", []string{u160}},
{"transfer", []string{u160, `"neo.com"`}},
// name service
{"addRoot", []string{`"com"`}},
{"deleteRecord", []string{`"neo.com"`, "nameservice.TypeA"}},
{"isAvailable", []string{`"neo.com"`}},
{"getPrice", nil},
{"getRecord", []string{`"neo.com"`, "nameservice.TypeA"}},
{"register", []string{`"neo.com"`, u160}},
{"renew", []string{`"neo.com"`}},
{"resolve", []string{`"neo.com"`, "nameservice.TypeA"}},
{"setPrice", []string{"42"}},
{"setAdmin", []string{`"neo.com"`, u160}},
{"setRecord", []string{`"neo.com"`, "nameservice.TypeA", `"1.1.1.1"`}},
})
runNativeTestCases(t, cs.Ledger.ContractMD, "ledger", []nativeTestCase{
{"currentHash", nil},
{"currentIndex", nil},
{"getBlock", []string{"1"}},
{"getTransaction", []string{u256}},
{"getTransactionFromBlock", []string{u256, "1"}},
{"getTransactionHeight", []string{u256}},
})
runNativeTestCases(t, cs.Notary.ContractMD, "notary", []nativeTestCase{
{"lockDepositUntil", []string{u160, "123"}},
{"withdraw", []string{u160, u160}},
{"balanceOf", []string{u160}},
{"expirationOf", []string{u160}},
{"getMaxNotValidBeforeDelta", nil},
{"setMaxNotValidBeforeDelta", []string{"42"}},
})
runNativeTestCases(t, cs.Management.ContractMD, "management", []nativeTestCase{
{"deploy", []string{"nil", "nil"}},
{"deployWithData", []string{"nil", "nil", "123"}},
{"destroy", nil},
{"getContract", []string{u160}},
{"getMinimumDeploymentFee", nil},
{"setMinimumDeploymentFee", []string{"42"}},
{"update", []string{"nil", "nil"}},
{"updateWithData", []string{"nil", "nil", "123"}},
})
}
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) {
t.Run(ctr.Name, func(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.method, func(t *testing.T) {
runNativeTestCase(t, ctr, name, tc.method, tc.params...)
})
}
})
}
func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string, params ...string) {
md, ok := ctr.GetMethod(strings.TrimSuffix(method, "WithData"), len(params))
require.True(t, ok)
isVoid := md.MD.ReturnType == smartcontract.VoidType
srcTmpl := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/native/%s"
import "github.com/nspcc-dev/neo-go/pkg/interop"
var _ interop.Hash160
`
if isVoid {
srcTmpl += `func Main() { %s.%s(%s) }`
} else {
srcTmpl += `func Main() interface{} { return %s.%s(%s) }`
}
methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only
methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS")
src := fmt.Sprintf(srcTmpl, name, name, methodUpper, strings.Join(params, ","))
v, s := vmAndCompileInterop(t, src)
id := interopnames.ToID([]byte(interopnames.SystemContractCall))
result := getTestStackItem(md.MD.ReturnType)
s.interops[id] = testContractCall(t, ctr.Hash, md, result)
require.NoError(t, v.Run())
if isVoid {
require.Equal(t, 0, v.Estack().Len())
return
}
require.Equal(t, 1, v.Estack().Len(), "stack contains unexpected items")
require.Equal(t, result.Value(), v.Estack().Pop().Item().Value())
}
func getTestStackItem(typ smartcontract.ParamType) stackitem.Item {
switch typ {
case smartcontract.AnyType, smartcontract.VoidType:
return stackitem.Null{}
case smartcontract.BoolType:
return stackitem.NewBool(true)
case smartcontract.IntegerType:
return stackitem.NewBigInteger(big.NewInt(42))
case smartcontract.ByteArrayType, smartcontract.StringType, smartcontract.Hash160Type,
smartcontract.Hash256Type, smartcontract.PublicKeyType, smartcontract.SignatureType:
return stackitem.NewByteArray([]byte("result"))
case smartcontract.ArrayType:
return stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true), stackitem.Null{}})
case smartcontract.MapType:
return stackitem.NewMapWithValue([]stackitem.MapElement{{
Key: stackitem.NewByteArray([]byte{1, 2, 3}),
Value: stackitem.NewByteArray([]byte{5, 6, 7}),
}})
case smartcontract.InteropInterfaceType:
return stackitem.NewInterop(42)
default:
panic("unexpected type")
}
}
func testContractCall(t *testing.T, hash util.Uint160, md interop.MethodAndPrice, result stackitem.Item) func(*vm.VM) error {
return func(v *vm.VM) error {
h := v.Estack().Pop().Bytes()
require.Equal(t, hash.BytesBE(), h)
method := v.Estack().Pop().String()
require.Equal(t, md.MD.Name, method)
fs := callflag.CallFlag(int32(v.Estack().Pop().BigInt().Int64()))
require.Equal(t, md.RequiredFlags, fs)
args := v.Estack().Pop().Array()
require.Equal(t, len(md.MD.Parameters), len(args))
v.Estack().PushVal(result)
return nil
}
}

7
pkg/compiler/testdata/types/types.go vendored Normal file
View file

@ -0,0 +1,7 @@
package types
// Buffer represents Buffer VM type.
type Buffer []byte
// ByteString represents ByteString VM type.
type ByteString string

View file

@ -7,19 +7,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// Compatibility test. hashes are taken directly from C# node.
func TestNativeHashes(t *testing.T) {
require.Equal(t, "a501d7d7d10983673b61b7a2d3a813b36f9f0e43", newManagement().Hash.StringLE())
require.Equal(t, "971d69c6dd10ce88e7dfffec1dc603c6125a8764", newLedger().Hash.StringLE())
require.Equal(t, "f61eebf573ea36593fd43aa150c055ad7906ab83", newNEO().Hash.StringLE())
require.Equal(t, "70e2301955bf1e74cbb31d18c2f96972abadb328", newGAS().Hash.StringLE())
require.Equal(t, "79bcd398505eb779df6e67e4be6c14cded08e2f2", newPolicy().Hash.StringLE())
require.Equal(t, "597b1471bbce497b7809e2c8f10db67050008b02", newDesignate(false).Hash.StringLE())
require.Equal(t, "8dc0e742cbdfdeda51ff8a8b78d46829144c80ee", newOracle().Hash.StringLE())
// Not yet a part of NEO.
//require.Equal(t, "", newNotary().Hash.StringLE()())
}
// "C" and "O" can easily be typed by accident. // "C" and "O" can easily be typed by accident.
func TestNamesASCII(t *testing.T) { func TestNamesASCII(t *testing.T) {
cs := NewContracts(true) cs := NewContracts(true)

View file

@ -0,0 +1,35 @@
package gas
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents GAS contract hash.
const Hash = "\x28\xb3\xad\xab\x72\x69\xf9\xc2\x18\x1d\xb3\xcb\x74\x1e\xbf\x55\x19\x30\xe2\x70"
// Symbol represents `symbol` method of GAS native contract.
func Symbol() string {
return contract.Call(interop.Hash160(Hash), "symbol", contract.NoneFlag).(string)
}
// Decimals represents `decimals` method of GAS native contract.
func Decimals() int {
return contract.Call(interop.Hash160(Hash), "decimals", contract.NoneFlag).(int)
}
// TotalSupply represents `totalSupply` method of GAS native contract.
func TotalSupply() int {
return contract.Call(interop.Hash160(Hash), "totalSupply", contract.ReadStates).(int)
}
// BalanceOf represents `balanceOf` method of GAS native contract.
func BalanceOf(addr interop.Hash160) int {
return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int)
}
// Transfer represents `transfer` method of GAS native contract.
func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
return contract.Call(interop.Hash160(Hash), "transfer",
contract.WriteStates|contract.AllowCall|contract.AllowNotify, from, to, amount, data).(bool)
}

View file

@ -0,0 +1,29 @@
package ledger
import "github.com/nspcc-dev/neo-go/pkg/interop"
// 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 interop.Hash256
// Version of the block.
Version int
// PrevHash represents the hash (256 bit BE value in a 32 byte slice) of the
// previous block.
PrevHash interop.Hash256
// MerkleRoot represents the root hash (256 bit BE value in a 32 byte slice)
// of a transaction list.
MerkleRoot interop.Hash256
// Timestamp represents millisecond-precision block timestamp.
Timestamp int
// Index represents the height of the block.
Index int
// NextConsensus represents contract address of the next miner (160 bit BE
// value in a 20 byte slice).
NextConsensus interop.Hash160
// TransactionsLength represents the length of block's transactions array.
TransactionsLength int
}

View file

@ -0,0 +1,40 @@
package ledger
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents Ledger contract hash.
const Hash = "\x64\x87\x5a\x12\xc6\x03\xc6\x1d\xec\xff\xdf\xe7\x88\xce\x10\xdd\xc6\x69\x1d\x97"
// CurrentHash represents `currentHash` method of Ledger native contract.
func CurrentHash() interop.Hash256 {
return contract.Call(interop.Hash160(Hash), "currentHash", contract.ReadStates).(interop.Hash256)
}
// CurrentIndex represents `currentIndex` method of Ledger native contract.
func CurrentIndex() int {
return contract.Call(interop.Hash160(Hash), "currentIndex", contract.ReadStates).(int)
}
// GetBlock represents `getBlock` method of Ledger native contract.
func GetBlock(indexOrHash interface{}) *Block {
return contract.Call(interop.Hash160(Hash), "getBlock", contract.ReadStates, indexOrHash).(*Block)
}
// GetTransaction represents `getTransaction` method of Ledger native contract.
func GetTransaction(hash interop.Hash256) *Transaction {
return contract.Call(interop.Hash160(Hash), "getTransaction", contract.ReadStates, hash).(*Transaction)
}
// GetTransactionHeight represents `getTransactionHeight` method of Ledger native contract.
func GetTransactionHeight(hash interop.Hash256) int {
return contract.Call(interop.Hash160(Hash), "getTransactionHeight", contract.ReadStates, hash).(int)
}
// GetTransactionFromBlock represents `getTransactionFromBlock` method of Ledger native contract.
func GetTransactionFromBlock(indexOrHash interface{}, txIndex int) *Transaction {
return contract.Call(interop.Hash160(Hash), "getTransactionFromBlock", contract.ReadStates,
indexOrHash, txIndex).(*Transaction)
}

View file

@ -0,0 +1,27 @@
package ledger
import "github.com/nspcc-dev/neo-go/pkg/interop"
// 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 interop.Hash256
// 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 interop.Hash160
// 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
}

View file

@ -0,0 +1,82 @@
package management
import "github.com/nspcc-dev/neo-go/pkg/interop"
// Contract represents deployed contract.
type Contract struct {
ID int
UpdateCounter int
Hash interop.Hash160
NEF []byte
Manifest Manifest
}
// ParameterType represents smartcontract parameter type.
type ParameterType byte
// Various parameter types.
const (
AnyType ParameterType = 0x00
BoolType ParameterType = 0x10
IntegerType ParameterType = 0x11
ByteArrayType ParameterType = 0x12
StringType ParameterType = 0x13
Hash160Type ParameterType = 0x14
Hash256Type ParameterType = 0x15
PublicKeyType ParameterType = 0x16
SignatureType ParameterType = 0x17
ArrayType ParameterType = 0x20
MapType ParameterType = 0x22
InteropInterfaceType ParameterType = 0x30
VoidType ParameterType = 0xff
)
// Manifest represents contract's manifest.
type Manifest struct {
Name string
Groups []Group
SupportedStandards []string
ABI ABI
Permissions []Permission
Trusts []interop.Hash160
Extra interface{}
}
// ABI represents contract's ABI.
type ABI struct {
Methods []Method
Events []Event
}
// Method represents contract method.
type Method struct {
Name string
Params []Parameter
ReturnType ParameterType
Offset int
Safe bool
}
// Event represents contract event.
type Event struct {
Name string
Params []Parameter
}
// Parameter represents method parameter.
type Parameter struct {
Name string
Type ParameterType
}
// Permission represents contract permission.
type Permission struct {
Contract interop.Hash160
Methods []string
}
// Group represents manifest group.
type Group struct {
PublicKey interop.PublicKey
Signature interop.Signature
}

View file

@ -0,0 +1,53 @@
package management
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents Management contract hash.
const Hash = "\x43\x0e\x9f\x6f\xb3\x13\xa8\xd3\xa2\xb7\x61\x3b\x67\x83\x09\xd1\xd7\xd7\x01\xa5"
// Deploy represents `deploy` method of Management native contract.
func Deploy(script, manifest []byte) *Contract {
return contract.Call(interop.Hash160(Hash), "deploy",
contract.WriteStates|contract.AllowNotify, script, manifest).(*Contract)
}
// DeployWithData represents `deploy` method of Management native contract.
func DeployWithData(script, manifest []byte, data interface{}) *Contract {
return contract.Call(interop.Hash160(Hash), "deploy",
contract.WriteStates|contract.AllowNotify, script, manifest, data).(*Contract)
}
// Destroy represents `destroy` method of Management native contract.
func Destroy() {
contract.Call(interop.Hash160(Hash), "destroy", contract.WriteStates|contract.AllowNotify)
}
// GetContract represents `getContract` method of Management native contract.
func GetContract(addr interop.Hash160) *Contract {
return contract.Call(interop.Hash160(Hash), "getContract", contract.ReadStates, addr).(*Contract)
}
// GetMinimumDeploymentFee represents `getMinimumDeploymentFee` method of Management native contract.
func GetMinimumDeploymentFee() int {
return contract.Call(interop.Hash160(Hash), "getMinimumDeploymentFee", contract.ReadStates).(int)
}
// SetMinimumDeploymentFee represents `setMinimumDeploymentFee` method of Management native contract.
func SetMinimumDeploymentFee(value int) {
contract.Call(interop.Hash160(Hash), "setMinimumDeploymentFee", contract.WriteStates, value)
}
// Update represents `update` method of Management native contract.
func Update(script, manifest []byte) {
contract.Call(interop.Hash160(Hash), "update",
contract.WriteStates|contract.AllowNotify, script, manifest)
}
// UpdateWithData represents `update` method of Management native contract.
func UpdateWithData(script, manifest []byte, data interface{}) {
contract.Call(interop.Hash160(Hash), "update",
contract.WriteStates|contract.AllowNotify, script, manifest, data)
}

View file

@ -0,0 +1,125 @@
package nameservice
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
)
// RecordType represents NameService record type.
type RecordType byte
// Various record type.
const (
TypeA RecordType = 1
TypeCNAME RecordType = 5
TypeTXT RecordType = 16
TypeAAAA RecordType = 28
)
// Hash represents NameService contract hash.
const Hash = "\x8c\x02\xb8\x43\x98\x6b\x3c\x44\x4f\xf8\x6a\xd5\xa9\x43\xfe\x8d\xb6\x24\xb5\xa2"
// Symbol represents `symbol` method of NameService native contract.
func Symbol() string {
return contract.Call(interop.Hash160(Hash), "symbol", contract.NoneFlag).(string)
}
// Decimals represents `decimals` method of NameService native contract.
func Decimals() int {
return contract.Call(interop.Hash160(Hash), "decimals", contract.NoneFlag).(int)
}
// TotalSupply represents `totalSupply` method of NameService native contract.
func TotalSupply() int {
return contract.Call(interop.Hash160(Hash), "totalSupply", contract.ReadStates).(int)
}
// OwnerOf represents `ownerOf` method of NameService native contract.
func OwnerOf(tokenID string) interop.Hash160 {
return contract.Call(interop.Hash160(Hash), "ownerOf", contract.ReadStates, tokenID).(interop.Hash160)
}
// BalanceOf represents `balanceOf` method of NameService native contract.
func BalanceOf(owner interop.Hash160) int {
return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, owner).(int)
}
// Properties represents `properties` method of NameService native contract.
func Properties(tokenID string) map[string]interface{} {
return contract.Call(interop.Hash160(Hash), "properties", contract.ReadStates, tokenID).(map[string]interface{})
}
// Tokens represents `tokens` method of NameService native contract.
func Tokens() iterator.Iterator {
return contract.Call(interop.Hash160(Hash), "tokens",
contract.ReadStates).(iterator.Iterator)
}
// TokensOf represents `tokensOf` method of NameService native contract.
func TokensOf(addr interop.Hash160) iterator.Iterator {
return contract.Call(interop.Hash160(Hash), "tokensOf",
contract.ReadStates, addr).(iterator.Iterator)
}
// Transfer represents `transfer` method of NameService native contract.
func Transfer(to interop.Hash160, tokenID string) bool {
return contract.Call(interop.Hash160(Hash), "transfer",
contract.WriteStates|contract.AllowNotify, to, tokenID).(bool)
}
// AddRoot represents `addRoot` method of NameService native contract.
func AddRoot(root string) {
contract.Call(interop.Hash160(Hash), "addRoot", contract.WriteStates, root)
}
// SetPrice represents `setPrice` method of NameService native contract.
func SetPrice(price int) {
contract.Call(interop.Hash160(Hash), "setPrice", contract.WriteStates, price)
}
// GetPrice represents `getPrice` method of NameService native contract.
func GetPrice() int {
return contract.Call(interop.Hash160(Hash), "getPrice", contract.ReadStates).(int)
}
// IsAvailable represents `isAvailable` method of NameService native contract.
func IsAvailable(name string) bool {
return contract.Call(interop.Hash160(Hash), "isAvailable", contract.ReadStates, name).(bool)
}
// Register represents `register` method of NameService native contract.
func Register(name string, owner interop.Hash160) bool {
return contract.Call(interop.Hash160(Hash), "register", contract.WriteStates, name, owner).(bool)
}
// Renew represents `renew` method of NameService native contract.
func Renew(name string) int {
return contract.Call(interop.Hash160(Hash), "renew", contract.WriteStates, name).(int)
}
// SetAdmin represents `setAdmin` method of NameService native contract.
func SetAdmin(name string, admin interop.Hash160) {
contract.Call(interop.Hash160(Hash), "setAdmin", contract.WriteStates, name, admin)
}
// SetRecord represents `setRecord` method of NameService native contract.
func SetRecord(name string, recType RecordType, data string) {
contract.Call(interop.Hash160(Hash), "setRecord", contract.WriteStates, name, recType, data)
}
// GetRecord represents `getRecord` method of NameService native contract.
// It returns `nil` if record is missing.
func GetRecord(name string, recType RecordType) []byte {
return contract.Call(interop.Hash160(Hash), "getRecord", contract.ReadStates, name, recType).([]byte)
}
// DeleteRecord represents `deleteRecord` method of NameService native contract.
func DeleteRecord(name string, recType RecordType) {
contract.Call(interop.Hash160(Hash), "deleteRecord", contract.WriteStates, name, recType)
}
// Resolve represents `resolve` method of NameService native contract.
func Resolve(name string, recType RecordType) []byte {
return contract.Call(interop.Hash160(Hash), "resolve", contract.ReadStates, name, recType).([]byte)
}

View file

@ -0,0 +1,80 @@
package neo
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents NEO contract hash.
const Hash = "\x83\xab\x06\x79\xad\x55\xc0\x50\xa1\x3a\xd4\x3f\x59\x36\xea\x73\xf5\xeb\x1e\xf6"
// Symbol represents `symbol` method of NEO native contract.
func Symbol() string {
return contract.Call(interop.Hash160(Hash), "symbol", contract.NoneFlag).(string)
}
// Decimals represents `decimals` method of NEO native contract.
func Decimals() int {
return contract.Call(interop.Hash160(Hash), "decimals", contract.NoneFlag).(int)
}
// TotalSupply represents `totalSupply` method of NEO native contract.
func TotalSupply() int {
return contract.Call(interop.Hash160(Hash), "totalSupply", contract.ReadStates).(int)
}
// BalanceOf represents `balanceOf` method of NEO native contract.
func BalanceOf(addr interop.Hash160) int {
return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int)
}
// Transfer represents `transfer` method of NEO native contract.
func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
return contract.Call(interop.Hash160(Hash), "transfer",
contract.WriteStates|contract.AllowCall|contract.AllowNotify, from, to, amount, data).(bool)
}
// GetCommittee represents `getCommittee` method of NEO native contract.
func GetCommittee() []interop.PublicKey {
return contract.Call(interop.Hash160(Hash), "getCommittee", contract.ReadStates).([]interop.PublicKey)
}
// GetCandidates represents `getCandidates` method of NEO native contract.
func GetCandidates() []interop.PublicKey {
return contract.Call(interop.Hash160(Hash), "getCandidates", contract.ReadStates).([]interop.PublicKey)
}
// GetNextBlockValidators represents `getNextBlockValidators` method of NEO native contract.
func GetNextBlockValidators() []interop.PublicKey {
return contract.Call(interop.Hash160(Hash), "getNextBlockValidators", contract.ReadStates).([]interop.PublicKey)
}
// GetGASPerBlock represents `getGasPerBlock` method of NEO native contract.
func GetGASPerBlock() int {
return contract.Call(interop.Hash160(Hash), "getGasPerBlock", contract.ReadStates).(int)
}
// SetGASPerBlock represents `setGasPerBlock` method of NEO native contract.
func SetGASPerBlock(amount int) {
contract.Call(interop.Hash160(Hash), "setGasPerBlock", contract.WriteStates, amount)
}
// RegisterCandidate represents `registerCandidate` method of NEO native contract.
func RegisterCandidate(pub interop.PublicKey) bool {
return contract.Call(interop.Hash160(Hash), "registerCandidate", contract.WriteStates, pub).(bool)
}
// UnregisterCandidate represents `unregisterCandidate` method of NEO native contract.
func UnregisterCandidate(pub interop.PublicKey) bool {
return contract.Call(interop.Hash160(Hash), "unregisterCandidate", contract.WriteStates, pub).(bool)
}
// Vote represents `vote` method of NEO native contract.
func Vote(addr interop.Hash160, pub interop.PublicKey) bool {
return contract.Call(interop.Hash160(Hash), "vote", contract.WriteStates, addr, pub).(bool)
}
// UnclaimedGAS represents `unclaimedGas` method of NEO native contract.
func UnclaimedGAS(addr interop.Hash160, end int) int {
return contract.Call(interop.Hash160(Hash), "unclaimedGas", contract.ReadStates, addr, end).(int)
}

View file

@ -0,0 +1,41 @@
package notary
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents Notary contract hash.
const Hash = "\x0c\xcf\x26\x94\x3f\xb5\xc9\xb6\x05\xe2\x06\xd2\xa2\x75\xbe\x3e\xa6\xa4\x75\xf4"
// LockDepositUntil represents `lockDepositUntil` method of Notary native contract.
func LockDepositUntil(addr interop.Hash160, till int) bool {
return contract.Call(interop.Hash160(Hash), "lockDepositUntil", contract.WriteStates,
addr, till).(bool)
}
// Withdraw represents `withdraw` method of Notary native contract.
func Withdraw(from, to interop.Hash160) bool {
return contract.Call(interop.Hash160(Hash), "withdraw", contract.WriteStates,
from, to).(bool)
}
// BalanceOf represents `balanceOf` method of Notary native contract.
func BalanceOf(addr interop.Hash160) int {
return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int)
}
// ExpirationOf represents `expirationOf` method of Notary native contract.
func ExpirationOf(addr interop.Hash160) int {
return contract.Call(interop.Hash160(Hash), "expirationOf", contract.ReadStates, addr).(int)
}
// GetMaxNotValidBeforeDelta represents `getMaxNotValidBeforeDelta` method of Notary native contract.
func GetMaxNotValidBeforeDelta() int {
return contract.Call(interop.Hash160(Hash), "getMaxNotValidBeforeDelta", contract.ReadStates).(int)
}
// SetMaxNotValidBeforeDelta represents `setMaxNotValidBeforeDelta` method of Notary native contract.
func SetMaxNotValidBeforeDelta(value int) {
contract.Call(interop.Hash160(Hash), "setMaxNotValidBeforeDelta", contract.WriteStates, value)
}

View file

@ -0,0 +1,16 @@
package oracle
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents Oracle contract hash.
const Hash = "\xee\x80\x4c\x14\x29\x68\xd4\x78\x8b\x8a\xff\x51\xda\xde\xdf\xcb\x42\xe7\xc0\x8d"
// Request represents `request` method of Oracle native contract.
func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) {
contract.Call(interop.Hash160(Hash), "request",
contract.WriteStates|contract.AllowNotify,
url, filter, cb, userData, gasForResponse)
}

View file

@ -0,0 +1,84 @@
package policy
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents Policy contract hash.
const Hash = "\xf2\xe2\x08\xed\xcd\x14\x6c\xbe\xe4\x67\x6e\xdf\x79\xb7\x5e\x50\x98\xd3\xbc\x79"
// GetMaxTransactionsPerBlock represents `getMaxTransactionsPerBlock` method of Policy native contract.
func GetMaxTransactionsPerBlock() int {
return contract.Call(interop.Hash160(Hash), "getMaxTransactionsPerBlock", contract.ReadStates).(int)
}
// SetMaxTransactionsPerBlock represents `setMaxTransactionsPerBlock` method of Policy native contract.
func SetMaxTransactionsPerBlock(value int) {
contract.Call(interop.Hash160(Hash), "setMaxTransactionsPerBlock", contract.WriteStates, value)
}
// GetMaxBlockSize represents `getMaxBlockSize` method of Policy native contract.
func GetMaxBlockSize() int {
return contract.Call(interop.Hash160(Hash), "getMaxBlockSize", contract.ReadStates).(int)
}
// SetMaxBlockSize represents `setMaxBlockSize` method of Policy native contract.
func SetMaxBlockSize(value int) {
contract.Call(interop.Hash160(Hash), "setMaxBlockSize", contract.WriteStates, value)
}
// GetFeePerByte represents `getFeePerByte` method of Policy native contract.
func GetFeePerByte() int {
return contract.Call(interop.Hash160(Hash), "getFeePerByte", contract.ReadStates).(int)
}
// SetFeePerByte represents `setFeePerByte` method of Policy native contract.
func SetFeePerByte(value int) {
contract.Call(interop.Hash160(Hash), "setFeePerByte", contract.WriteStates, value)
}
// GetMaxBlockSystemFee represents `getMaxBlockSystemFee` method of Policy native contract.
func GetMaxBlockSystemFee() int {
return contract.Call(interop.Hash160(Hash), "getMaxBlockSystemFee", contract.ReadStates).(int)
}
// SetMaxBlockSystemFee represents `setMaxBlockSystemFee` method of Policy native contract.
func SetMaxBlockSystemFee(value int) {
contract.Call(interop.Hash160(Hash), "setMaxBlockSystemFee", contract.WriteStates, value)
}
// GetExecFeeFactor represents `getExecFeeFactor` method of Policy native contract.
func GetExecFeeFactor() int {
return contract.Call(interop.Hash160(Hash), "getExecFeeFactor", contract.ReadStates).(int)
}
// SetExecFeeFactor represents `setExecFeeFactor` method of Policy native contract.
func SetExecFeeFactor(value int) {
contract.Call(interop.Hash160(Hash), "setExecFeeFactor", contract.WriteStates, value)
}
// GetStoragePrice represents `getStoragePrice` method of Policy native contract.
func GetStoragePrice() int {
return contract.Call(interop.Hash160(Hash), "getStoragePrice", contract.ReadStates).(int)
}
// SetStoragePrice represents `setStoragePrice` method of Policy native contract.
func SetStoragePrice(value int) {
contract.Call(interop.Hash160(Hash), "setStoragePrice", contract.WriteStates, value)
}
// IsBlocked represents `isBlocked` method of Policy native contract.
func IsBlocked(addr interop.Hash160) bool {
return contract.Call(interop.Hash160(Hash), "isBlocked", contract.ReadStates, addr).(bool)
}
// BlockAccount represents `blockAccount` method of Policy native contract.
func BlockAccount(addr interop.Hash160) bool {
return contract.Call(interop.Hash160(Hash), "blockAccount", contract.WriteStates, addr).(bool)
}
// UnblockAccount represents `unblockAccount` method of Policy native contract.
func UnblockAccount(addr interop.Hash160) bool {
return contract.Call(interop.Hash160(Hash), "unblockAccount", contract.WriteStates, addr).(bool)
}

View file

@ -0,0 +1,31 @@
package roles
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
)
// Hash represents RoleManagement contract hash.
const Hash = "\x02\x8b\x00\x50\x70\xb6\x0d\xf1\xc8\xe2\x09\x78\x7b\x49\xce\xbb\x71\x14\x7b\x59"
// Role represents node role.
type Role byte
// Various node roles.
const (
StateValidator Role = 4
Oracle Role = 8
P2PNotary Role = 128
)
// GetDesignatedByRole represents `getDesignatedByRole` method of RoleManagement native contract.
func GetDesignatedByRole(r Role, height uint32) []interop.PublicKey {
return contract.Call(interop.Hash160(Hash), "getDesignatedByRole",
contract.ReadStates, r, height).([]interop.PublicKey)
}
// DesignateAsRole represents `designateAsRole` method of RoleManagement native contract.
func DesignateAsRole(r Role, pubs []interop.PublicKey) {
contract.Call(interop.Hash160(Hash), "designateAsRole",
contract.WriteStates, r, pubs)
}

View file

@ -2,38 +2,15 @@ package runtime
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
) )
// 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 interop.Hash256
// 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 interop.Hash160
// 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
}
// 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. This function uses // this execution goes. This function uses
// `System.Runtime.GetScriptContainer` syscall. // `System.Runtime.GetScriptContainer` syscall.
func GetScriptContainer() *Transaction { func GetScriptContainer() *ledger.Transaction {
return &Transaction{} return &ledger.Transaction{}
} }
// GetExecutingScriptHash returns script hash (160 bit in BE form represented // GetExecutingScriptHash returns script hash (160 bit in BE form represented