mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 13:41:47 +00:00
a50723ff72
It's slightly less efficient (all comparisons are always made), but for strings/ints it's negligible performance difference, while the code looks a tiny bit better. Signed-off-by: Roman Khimov <roman@nspcc.ru>
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package manifest
|
|
|
|
import (
|
|
"cmp"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
)
|
|
|
|
const (
|
|
// MethodInit is a name for default initialization method.
|
|
MethodInit = "_initialize"
|
|
|
|
// MethodDeploy is a name for default method called during contract deployment.
|
|
MethodDeploy = "_deploy"
|
|
|
|
// MethodVerify is a name for default verification method.
|
|
MethodVerify = "verify"
|
|
|
|
// MethodOnNEP17Payment is the name of the method which is called when contract receives NEP-17 tokens.
|
|
MethodOnNEP17Payment = "onNEP17Payment"
|
|
|
|
// MethodOnNEP11Payment is the name of the method which is called when contract receives NEP-11 tokens.
|
|
MethodOnNEP11Payment = "onNEP11Payment"
|
|
)
|
|
|
|
// ABI represents a contract application binary interface.
|
|
type ABI struct {
|
|
Methods []Method `json:"methods"`
|
|
Events []Event `json:"events"`
|
|
}
|
|
|
|
// GetMethod returns methods with the specified name.
|
|
func (a *ABI) GetMethod(name string, paramCount int) *Method {
|
|
for i := range a.Methods {
|
|
if a.Methods[i].Name == name && (paramCount == -1 || len(a.Methods[i].Parameters) == paramCount) {
|
|
return &a.Methods[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetEvent returns the event with the specified name.
|
|
func (a *ABI) GetEvent(name string) *Event {
|
|
for i := range a.Events {
|
|
if a.Events[i].Name == name {
|
|
return &a.Events[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsValid checks ABI consistency and correctness.
|
|
func (a *ABI) IsValid() error {
|
|
if len(a.Methods) == 0 {
|
|
return errors.New("no methods")
|
|
}
|
|
for i := range a.Methods {
|
|
err := a.Methods[i].IsValid()
|
|
if err != nil {
|
|
return fmt.Errorf("method %q/%d: %w", a.Methods[i].Name, len(a.Methods[i].Parameters), err)
|
|
}
|
|
}
|
|
if sliceHasDups(a.Methods, func(a, b Method) int {
|
|
return cmp.Or(
|
|
cmp.Compare(a.Name, b.Name),
|
|
cmp.Compare(len(a.Parameters), len(b.Parameters)),
|
|
)
|
|
}) {
|
|
return errors.New("duplicate method specifications")
|
|
}
|
|
for i := range a.Events {
|
|
err := a.Events[i].IsValid()
|
|
if err != nil {
|
|
return fmt.Errorf("event %q/%d: %w", a.Events[i].Name, len(a.Events[i].Parameters), err)
|
|
}
|
|
}
|
|
if sliceHasDups(a.Events, func(a, b Event) int {
|
|
return cmp.Compare(a.Name, b.Name)
|
|
}) {
|
|
return errors.New("duplicate event names")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ToStackItem converts ABI to stackitem.Item.
|
|
func (a *ABI) ToStackItem() stackitem.Item {
|
|
methods := make([]stackitem.Item, len(a.Methods))
|
|
for i := range a.Methods {
|
|
methods[i] = a.Methods[i].ToStackItem()
|
|
}
|
|
events := make([]stackitem.Item, len(a.Events))
|
|
for i := range a.Events {
|
|
events[i] = a.Events[i].ToStackItem()
|
|
}
|
|
return stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(methods),
|
|
stackitem.Make(events),
|
|
})
|
|
}
|
|
|
|
// FromStackItem converts stackitem.Item to ABI.
|
|
func (a *ABI) FromStackItem(item stackitem.Item) error {
|
|
if item.Type() != stackitem.StructT {
|
|
return errors.New("invalid ABI stackitem type")
|
|
}
|
|
str := item.Value().([]stackitem.Item)
|
|
if len(str) != 2 {
|
|
return errors.New("invalid ABI stackitem length")
|
|
}
|
|
if str[0].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Methods stackitem type")
|
|
}
|
|
methods := str[0].Value().([]stackitem.Item)
|
|
a.Methods = make([]Method, len(methods))
|
|
for i := range methods {
|
|
m := new(Method)
|
|
if err := m.FromStackItem(methods[i]); err != nil {
|
|
return err
|
|
}
|
|
a.Methods[i] = *m
|
|
}
|
|
if str[1].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Events stackitem type")
|
|
}
|
|
events := str[1].Value().([]stackitem.Item)
|
|
a.Events = make([]Event, len(events))
|
|
for i := range events {
|
|
e := new(Event)
|
|
if err := e.FromStackItem(events[i]); err != nil {
|
|
return err
|
|
}
|
|
a.Events[i] = *e
|
|
}
|
|
return nil
|
|
}
|