github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/abi.go (about)

     1  package manifest
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
     9  )
    10  
    11  const (
    12  	// MethodInit is a name for default initialization method.
    13  	MethodInit = "_initialize"
    14  
    15  	// MethodDeploy is a name for default method called during contract deployment.
    16  	MethodDeploy = "_deploy"
    17  
    18  	// MethodVerify is a name for default verification method.
    19  	MethodVerify = "verify"
    20  
    21  	// MethodOnNEP17Payment is the name of the method which is called when contract receives NEP-17 tokens.
    22  	MethodOnNEP17Payment = "onNEP17Payment"
    23  
    24  	// MethodOnNEP11Payment is the name of the method which is called when contract receives NEP-11 tokens.
    25  	MethodOnNEP11Payment = "onNEP11Payment"
    26  )
    27  
    28  // ABI represents a contract application binary interface.
    29  type ABI struct {
    30  	Methods []Method `json:"methods"`
    31  	Events  []Event  `json:"events"`
    32  }
    33  
    34  // GetMethod returns methods with the specified name.
    35  func (a *ABI) GetMethod(name string, paramCount int) *Method {
    36  	for i := range a.Methods {
    37  		if a.Methods[i].Name == name && (paramCount == -1 || len(a.Methods[i].Parameters) == paramCount) {
    38  			return &a.Methods[i]
    39  		}
    40  	}
    41  	return nil
    42  }
    43  
    44  // GetEvent returns the event with the specified name.
    45  func (a *ABI) GetEvent(name string) *Event {
    46  	for i := range a.Events {
    47  		if a.Events[i].Name == name {
    48  			return &a.Events[i]
    49  		}
    50  	}
    51  	return nil
    52  }
    53  
    54  // IsValid checks ABI consistency and correctness.
    55  func (a *ABI) IsValid() error {
    56  	if len(a.Methods) == 0 {
    57  		return errors.New("no methods")
    58  	}
    59  	for i := range a.Methods {
    60  		err := a.Methods[i].IsValid()
    61  		if err != nil {
    62  			return fmt.Errorf("method %q/%d: %w", a.Methods[i].Name, len(a.Methods[i].Parameters), err)
    63  		}
    64  	}
    65  	if len(a.Methods) > 1 {
    66  		methods := make([]struct {
    67  			name   string
    68  			params int
    69  		}, len(a.Methods))
    70  		for i := range methods {
    71  			methods[i].name = a.Methods[i].Name
    72  			methods[i].params = len(a.Methods[i].Parameters)
    73  		}
    74  		sort.Slice(methods, func(i, j int) bool {
    75  			if methods[i].name < methods[j].name {
    76  				return true
    77  			}
    78  			if methods[i].name == methods[j].name {
    79  				return methods[i].params < methods[j].params
    80  			}
    81  			return false
    82  		})
    83  		for i := range methods {
    84  			if i == 0 {
    85  				continue
    86  			}
    87  			if methods[i].name == methods[i-1].name &&
    88  				methods[i].params == methods[i-1].params {
    89  				return errors.New("duplicate method specifications")
    90  			}
    91  		}
    92  	}
    93  	for i := range a.Events {
    94  		err := a.Events[i].IsValid()
    95  		if err != nil {
    96  			return fmt.Errorf("event %q/%d: %w", a.Events[i].Name, len(a.Events[i].Parameters), err)
    97  		}
    98  	}
    99  	if len(a.Events) > 1 {
   100  		names := make([]string, len(a.Events))
   101  		for i := range a.Events {
   102  			names[i] = a.Events[i].Name
   103  		}
   104  		if stringsHaveDups(names) {
   105  			return errors.New("duplicate event names")
   106  		}
   107  	}
   108  	return nil
   109  }
   110  
   111  // ToStackItem converts ABI to stackitem.Item.
   112  func (a *ABI) ToStackItem() stackitem.Item {
   113  	methods := make([]stackitem.Item, len(a.Methods))
   114  	for i := range a.Methods {
   115  		methods[i] = a.Methods[i].ToStackItem()
   116  	}
   117  	events := make([]stackitem.Item, len(a.Events))
   118  	for i := range a.Events {
   119  		events[i] = a.Events[i].ToStackItem()
   120  	}
   121  	return stackitem.NewStruct([]stackitem.Item{
   122  		stackitem.Make(methods),
   123  		stackitem.Make(events),
   124  	})
   125  }
   126  
   127  // FromStackItem converts stackitem.Item to ABI.
   128  func (a *ABI) FromStackItem(item stackitem.Item) error {
   129  	if item.Type() != stackitem.StructT {
   130  		return errors.New("invalid ABI stackitem type")
   131  	}
   132  	str := item.Value().([]stackitem.Item)
   133  	if len(str) != 2 {
   134  		return errors.New("invalid ABI stackitem length")
   135  	}
   136  	if str[0].Type() != stackitem.ArrayT {
   137  		return errors.New("invalid Methods stackitem type")
   138  	}
   139  	methods := str[0].Value().([]stackitem.Item)
   140  	a.Methods = make([]Method, len(methods))
   141  	for i := range methods {
   142  		m := new(Method)
   143  		if err := m.FromStackItem(methods[i]); err != nil {
   144  			return err
   145  		}
   146  		a.Methods[i] = *m
   147  	}
   148  	if str[1].Type() != stackitem.ArrayT {
   149  		return errors.New("invalid Events stackitem type")
   150  	}
   151  	events := str[1].Value().([]stackitem.Item)
   152  	a.Events = make([]Event, len(events))
   153  	for i := range events {
   154  		e := new(Event)
   155  		if err := e.FromStackItem(events[i]); err != nil {
   156  			return err
   157  		}
   158  		a.Events[i] = *e
   159  	}
   160  	return nil
   161  }