2020-03-19 15:52:37 +00:00
|
|
|
package native
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Method is a signature for a native method.
|
|
|
|
type Method = func(ic *interop.Context, args []vm.StackItem) vm.StackItem
|
|
|
|
|
|
|
|
// MethodAndPrice is a native-contract method descriptor.
|
|
|
|
type MethodAndPrice struct {
|
|
|
|
Func Method
|
|
|
|
Price int64
|
|
|
|
RequiredFlags smartcontract.CallFlag
|
|
|
|
}
|
|
|
|
|
|
|
|
// Contract is an interface for all native contracts.
|
|
|
|
type Contract interface {
|
|
|
|
Metadata() *ContractMD
|
|
|
|
OnPersist(*interop.Context) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContractMD represents native contract instance.
|
|
|
|
type ContractMD struct {
|
|
|
|
Manifest manifest.Manifest
|
|
|
|
ServiceName string
|
|
|
|
ServiceID uint32
|
|
|
|
Script []byte
|
|
|
|
Hash util.Uint160
|
|
|
|
Methods map[string]MethodAndPrice
|
|
|
|
}
|
|
|
|
|
|
|
|
// Contracts is a set of registered native contracts.
|
|
|
|
type Contracts struct {
|
2020-03-25 10:00:11 +00:00
|
|
|
NEO *NEO
|
|
|
|
GAS *GAS
|
2020-03-19 15:52:37 +00:00
|
|
|
Contracts []Contract
|
|
|
|
}
|
|
|
|
|
2020-03-25 10:00:11 +00:00
|
|
|
// SetGAS sets GAS native contract.
|
|
|
|
func (cs *Contracts) SetGAS(g *GAS) {
|
|
|
|
cs.GAS = g
|
|
|
|
cs.Contracts = append(cs.Contracts, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetNEO sets NEO native contract.
|
|
|
|
func (cs *Contracts) SetNEO(n *NEO) {
|
|
|
|
cs.NEO = n
|
|
|
|
cs.Contracts = append(cs.Contracts, n)
|
|
|
|
}
|
|
|
|
|
2020-03-19 15:52:37 +00:00
|
|
|
// NewContractMD returns Contract with the specified list of methods.
|
|
|
|
func NewContractMD(name string) *ContractMD {
|
|
|
|
c := &ContractMD{
|
|
|
|
ServiceName: name,
|
2020-04-15 14:13:50 +00:00
|
|
|
ServiceID: emit.InteropNameToID([]byte(name)),
|
2020-03-19 15:52:37 +00:00
|
|
|
Methods: make(map[string]MethodAndPrice),
|
|
|
|
}
|
|
|
|
|
|
|
|
w := io.NewBufBinWriter()
|
|
|
|
emit.Syscall(w.BinWriter, c.ServiceName)
|
|
|
|
c.Script = w.Bytes()
|
|
|
|
c.Hash = hash.Hash160(c.Script)
|
|
|
|
c.Manifest = *manifest.DefaultManifest(c.Hash)
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// ByHash returns native contract with the specified hash.
|
|
|
|
func (cs *Contracts) ByHash(h util.Uint160) Contract {
|
|
|
|
for _, ctr := range cs.Contracts {
|
|
|
|
if ctr.Metadata().Hash.Equals(h) {
|
|
|
|
return ctr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ByID returns native contract with the specified id.
|
|
|
|
func (cs *Contracts) ByID(id uint32) Contract {
|
|
|
|
for _, ctr := range cs.Contracts {
|
|
|
|
if ctr.Metadata().ServiceID == id {
|
|
|
|
return ctr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddMethod adds new method to a native contract.
|
|
|
|
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
|
|
|
|
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
|
|
|
|
c.Methods[desc.Name] = *md
|
|
|
|
if safe {
|
|
|
|
c.Manifest.SafeMethods.Add(desc.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddEvent adds new event to a native contract.
|
|
|
|
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
|
|
|
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
|
|
|
Name: name,
|
|
|
|
Parameters: ps,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewContracts returns new empty set of native contracts.
|
|
|
|
func NewContracts() *Contracts {
|
|
|
|
return &Contracts{
|
|
|
|
Contracts: []Contract{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add adds new native contracts to the list.
|
|
|
|
func (cs *Contracts) Add(c Contract) {
|
|
|
|
cs.Contracts = append(cs.Contracts, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNativeInterop returns an interop getter for a given set of contracts.
|
|
|
|
func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.InteropFuncPrice {
|
|
|
|
return func(id uint32) *vm.InteropFuncPrice {
|
|
|
|
if c := cs.ByID(id); c != nil {
|
|
|
|
return &vm.InteropFuncPrice{
|
|
|
|
Func: getNativeInterop(ic, c),
|
|
|
|
Price: 0, // TODO price func
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// getNativeInterop returns native contract interop.
|
|
|
|
func getNativeInterop(ic *interop.Context, c Contract) func(v *vm.VM) error {
|
|
|
|
return func(v *vm.VM) error {
|
|
|
|
h := v.GetContextScriptHash(0)
|
|
|
|
if !h.Equals(c.Metadata().Hash) {
|
|
|
|
return errors.New("invalid hash")
|
|
|
|
}
|
|
|
|
name := string(v.Estack().Pop().Bytes())
|
|
|
|
args := v.Estack().Pop().Array()
|
|
|
|
m, ok := c.Metadata().Methods[name]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("method %s not found", name)
|
|
|
|
}
|
|
|
|
result := m.Func(ic, args)
|
|
|
|
v.Estack().PushVal(result)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|