smartcontract: use permission descriptors for manifest's trusts
This commit is contained in:
parent
4fb421738b
commit
50fc9bf766
7 changed files with 200 additions and 145 deletions
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"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/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -263,8 +262,8 @@ func _deploy(data interface{}, isUpdate bool) {}
|
|||
Methods: manifest.WildStrings{},
|
||||
},
|
||||
},
|
||||
Trusts: manifest.WildUint160s{
|
||||
Value: []util.Uint160{},
|
||||
Trusts: manifest.WildPermissionDescs{
|
||||
Value: []manifest.PermissionDesc{},
|
||||
},
|
||||
Extra: json.RawMessage("null"),
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ package manifest
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// WildStrings represents string set which can be wildcard.
|
||||
|
@ -16,9 +14,9 @@ type WildStrings struct {
|
|||
Value []string
|
||||
}
|
||||
|
||||
// WildUint160s represents Uint160 set which can be wildcard.
|
||||
type WildUint160s struct {
|
||||
Value []util.Uint160
|
||||
// WildPermissionDescs represents PermissionDescriptor set which can be wildcard.
|
||||
type WildPermissionDescs struct {
|
||||
Value []PermissionDesc
|
||||
}
|
||||
|
||||
// Contains checks if v is in the container.
|
||||
|
@ -35,7 +33,7 @@ func (c *WildStrings) Contains(v string) bool {
|
|||
}
|
||||
|
||||
// Contains checks if v is in the container.
|
||||
func (c *WildUint160s) Contains(v util.Uint160) bool {
|
||||
func (c *WildPermissionDescs) Contains(v PermissionDesc) bool {
|
||||
if c.IsWildcard() {
|
||||
return true
|
||||
}
|
||||
|
@ -51,19 +49,19 @@ func (c *WildUint160s) Contains(v util.Uint160) bool {
|
|||
func (c *WildStrings) IsWildcard() bool { return c.Value == nil }
|
||||
|
||||
// IsWildcard returns true iff container is wildcard.
|
||||
func (c *WildUint160s) IsWildcard() bool { return c.Value == nil }
|
||||
func (c *WildPermissionDescs) IsWildcard() bool { return c.Value == nil }
|
||||
|
||||
// Restrict transforms container into an empty one.
|
||||
func (c *WildStrings) Restrict() { c.Value = []string{} }
|
||||
|
||||
// Restrict transforms container into an empty one.
|
||||
func (c *WildUint160s) Restrict() { c.Value = []util.Uint160{} }
|
||||
func (c *WildPermissionDescs) Restrict() { c.Value = []PermissionDesc{} }
|
||||
|
||||
// Add adds v to the container.
|
||||
func (c *WildStrings) Add(v string) { c.Value = append(c.Value, v) }
|
||||
|
||||
// Add adds v to the container.
|
||||
func (c *WildUint160s) Add(v util.Uint160) { c.Value = append(c.Value, v) }
|
||||
func (c *WildPermissionDescs) Add(v PermissionDesc) { c.Value = append(c.Value, v) }
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (c WildStrings) MarshalJSON() ([]byte, error) {
|
||||
|
@ -74,7 +72,7 @@ func (c WildStrings) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (c WildUint160s) MarshalJSON() ([]byte, error) {
|
||||
func (c WildPermissionDescs) MarshalJSON() ([]byte, error) {
|
||||
if c.IsWildcard() {
|
||||
return []byte(`"*"`), nil
|
||||
}
|
||||
|
@ -94,9 +92,9 @@ func (c *WildStrings) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (c *WildUint160s) UnmarshalJSON(data []byte) error {
|
||||
func (c *WildPermissionDescs) UnmarshalJSON(data []byte) error {
|
||||
if !bytes.Equal(data, []byte(`"*"`)) {
|
||||
us := []util.Uint160{}
|
||||
us := []PermissionDesc{}
|
||||
if err := json.Unmarshal(data, &us); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -21,15 +21,30 @@ func TestContainer_Restrict(t *testing.T) {
|
|||
require.Equal(t, 0, len(c.Value))
|
||||
})
|
||||
|
||||
t.Run("uint160", func(t *testing.T) {
|
||||
c := new(WildUint160s)
|
||||
u := random.Uint160()
|
||||
t.Run("PermissionDesc", func(t *testing.T) {
|
||||
check := func(t *testing.T, u PermissionDesc) {
|
||||
c := new(WildPermissionDescs)
|
||||
require.True(t, c.IsWildcard())
|
||||
require.True(t, c.Contains(u))
|
||||
c.Restrict()
|
||||
require.False(t, c.IsWildcard())
|
||||
require.False(t, c.Contains(u))
|
||||
require.Equal(t, 0, len(c.Value))
|
||||
}
|
||||
t.Run("Hash", func(t *testing.T) {
|
||||
check(t, PermissionDesc{
|
||||
Type: PermissionHash,
|
||||
Value: random.Uint160(),
|
||||
})
|
||||
})
|
||||
t.Run("Group", func(t *testing.T) {
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
check(t, PermissionDesc{
|
||||
Type: PermissionGroup,
|
||||
Value: pk.PublicKey(),
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -44,17 +59,24 @@ func TestContainer_Add(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("uint160", func(t *testing.T) {
|
||||
c := new(WildUint160s)
|
||||
require.Equal(t, []util.Uint160(nil), c.Value)
|
||||
|
||||
exp := []util.Uint160{random.Uint160(), random.Uint160()}
|
||||
c := new(WildPermissionDescs)
|
||||
require.Equal(t, []PermissionDesc(nil), c.Value)
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
exp := []PermissionDesc{
|
||||
{Type: PermissionHash, Value: random.Uint160()},
|
||||
{Type: PermissionGroup, Value: pk.PublicKey()},
|
||||
}
|
||||
for i := range exp {
|
||||
c.Add(exp[i])
|
||||
}
|
||||
for i := range exp {
|
||||
require.True(t, c.Contains(exp[i]))
|
||||
}
|
||||
require.False(t, c.Contains(random.Uint160()))
|
||||
pkRand, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
require.False(t, c.Contains(PermissionDesc{Type: PermissionHash, Value: random.Uint160()}))
|
||||
require.False(t, c.Contains(PermissionDesc{Type: PermissionGroup, Value: pkRand.PublicKey()}))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -85,27 +107,30 @@ func TestContainer_MarshalJSON(t *testing.T) {
|
|||
})
|
||||
})
|
||||
|
||||
t.Run("uint160", func(t *testing.T) {
|
||||
t.Run("PermissionDesc", func(t *testing.T) {
|
||||
t.Run("wildcard", func(t *testing.T) {
|
||||
expected := new(WildUint160s)
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
||||
expected := new(WildPermissionDescs)
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||
})
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
expected := new(WildUint160s)
|
||||
expected := new(WildPermissionDescs)
|
||||
expected.Restrict()
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||
})
|
||||
|
||||
t.Run("non-empty", func(t *testing.T) {
|
||||
expected := new(WildUint160s)
|
||||
expected.Add(random.Uint160())
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
||||
expected := new(WildPermissionDescs)
|
||||
expected.Add(PermissionDesc{
|
||||
Type: PermissionHash,
|
||||
Value: random.Uint160(),
|
||||
})
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
js := []byte(`["notahex"]`)
|
||||
c := new(WildUint160s)
|
||||
c := new(WildPermissionDescs)
|
||||
require.Error(t, json.Unmarshal(js, c))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
|
@ -38,7 +37,7 @@ type Manifest struct {
|
|||
// SupportedStandards is a list of standards supported by the contract.
|
||||
SupportedStandards []string `json:"supportedstandards"`
|
||||
// Trusts is a set of hashes to a which contract trusts.
|
||||
Trusts WildUint160s `json:"trusts"`
|
||||
Trusts WildPermissionDescs `json:"trusts"`
|
||||
// Extra is an implementation-defined user data.
|
||||
Extra json.RawMessage `json:"extra"`
|
||||
}
|
||||
|
@ -109,20 +108,12 @@ func (m *Manifest) IsValid(hash util.Uint160) error {
|
|||
return err
|
||||
}
|
||||
if len(m.Trusts.Value) > 1 {
|
||||
hashes := make([]util.Uint160, len(m.Trusts.Value))
|
||||
hashes := make([]PermissionDesc, len(m.Trusts.Value))
|
||||
copy(hashes, m.Trusts.Value)
|
||||
sort.Slice(hashes, func(i, j int) bool {
|
||||
return hashes[i].Less(hashes[j])
|
||||
})
|
||||
for i := range hashes {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
if hashes[i] == hashes[i-1] {
|
||||
if permissionDescsHaveDups(hashes) {
|
||||
return errors.New("duplicate trusted contracts")
|
||||
}
|
||||
}
|
||||
}
|
||||
return Permissions(m.Permissions).AreValid()
|
||||
}
|
||||
|
||||
|
@ -144,8 +135,8 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
|
|||
trusts := stackitem.Item(stackitem.Null{})
|
||||
if !m.Trusts.IsWildcard() {
|
||||
tItems := make([]stackitem.Item, len(m.Trusts.Value))
|
||||
for i := range m.Trusts.Value {
|
||||
tItems[i] = stackitem.NewByteArray(m.Trusts.Value[i].BytesBE())
|
||||
for i, v := range m.Trusts.Value {
|
||||
tItems[i] = v.ToStackItem()
|
||||
}
|
||||
trusts = stackitem.Make(tItems)
|
||||
}
|
||||
|
@ -231,16 +222,14 @@ func (m *Manifest) FromStackItem(item stackitem.Item) error {
|
|||
return errors.New("invalid Trusts stackitem type")
|
||||
}
|
||||
trusts := str[6].Value().([]stackitem.Item)
|
||||
m.Trusts = WildUint160s{Value: make([]util.Uint160, len(trusts))}
|
||||
m.Trusts = WildPermissionDescs{Value: make([]PermissionDesc, len(trusts))}
|
||||
for i := range trusts {
|
||||
bytes, err := trusts[i].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Trusts.Value[i], err = util.Uint160DecodeBytesBE(bytes)
|
||||
v := new(PermissionDesc)
|
||||
err = v.FromStackItem(trusts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Trusts.Value[i] = *v
|
||||
}
|
||||
}
|
||||
extra, err := str[7].TryBytes()
|
||||
|
|
|
@ -181,17 +181,18 @@ func TestIsValid(t *testing.T) {
|
|||
})
|
||||
m.SupportedStandards = m.SupportedStandards[:1]
|
||||
|
||||
m.Trusts.Add(util.Uint160{1, 2, 3})
|
||||
d := PermissionDesc{Type: PermissionHash, Value: util.Uint160{1, 2, 3}}
|
||||
m.Trusts.Add(d)
|
||||
t.Run("valid, with trust", func(t *testing.T) {
|
||||
require.NoError(t, m.IsValid(contractHash))
|
||||
})
|
||||
|
||||
m.Trusts.Add(util.Uint160{3, 2, 1})
|
||||
m.Trusts.Add(PermissionDesc{Type: PermissionHash, Value: util.Uint160{3, 2, 1}})
|
||||
t.Run("valid, with trusts", func(t *testing.T) {
|
||||
require.NoError(t, m.IsValid(contractHash))
|
||||
})
|
||||
|
||||
m.Trusts.Add(util.Uint160{1, 2, 3})
|
||||
m.Trusts.Add(d)
|
||||
t.Run("invalid, with trusts", func(t *testing.T) {
|
||||
require.Error(t, m.IsValid(contractHash))
|
||||
})
|
||||
|
@ -276,8 +277,17 @@ func TestManifestToStackItem(t *testing.T) {
|
|||
}},
|
||||
Permissions: []Permission{*NewPermission(PermissionWildcard)},
|
||||
SupportedStandards: []string{"NEP-17"},
|
||||
Trusts: WildUint160s{
|
||||
Value: []util.Uint160{{1, 2, 3}},
|
||||
Trusts: WildPermissionDescs{
|
||||
Value: []PermissionDesc{
|
||||
{
|
||||
Type: PermissionHash,
|
||||
Value: util.Uint160{1, 2, 3},
|
||||
},
|
||||
{
|
||||
Type: PermissionGroup,
|
||||
Value: pk.PublicKey(),
|
||||
},
|
||||
},
|
||||
},
|
||||
Extra: []byte(`even not a json allowed`),
|
||||
}
|
||||
|
|
|
@ -104,3 +104,32 @@ func stringsHaveDups(strings []string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// permissionDescsHaveDups checks given set of strings for duplicates. It modifies the slice given!
|
||||
func permissionDescsHaveDups(descs []PermissionDesc) bool {
|
||||
sort.Slice(descs, func(i, j int) bool {
|
||||
return descs[i].Less(descs[j])
|
||||
})
|
||||
for i := range descs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
j := i - 1
|
||||
if descs[i].Type != descs[j].Type {
|
||||
continue
|
||||
}
|
||||
switch descs[i].Type {
|
||||
case PermissionWildcard:
|
||||
return true
|
||||
case PermissionHash:
|
||||
if descs[i].Hash() == descs[j].Hash() {
|
||||
return true
|
||||
}
|
||||
case PermissionGroup:
|
||||
if descs[i].Group().Cmp(descs[j].Group()) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -89,6 +88,37 @@ func (d *PermissionDesc) Group() *keys.PublicKey {
|
|||
return d.Value.(*keys.PublicKey)
|
||||
}
|
||||
|
||||
// Less returns true if this value is less than given PermissionDesc value.
|
||||
func (d *PermissionDesc) Less(d1 PermissionDesc) bool {
|
||||
if d.Type < d1.Type {
|
||||
return true
|
||||
}
|
||||
if d.Type != d1.Type {
|
||||
return false
|
||||
}
|
||||
switch d.Type {
|
||||
case PermissionHash:
|
||||
return d.Hash().Less(d1.Hash())
|
||||
case PermissionGroup:
|
||||
return d.Group().Cmp(d1.Group()) < 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if both PermissionDesc values are the same.
|
||||
func (d *PermissionDesc) Equals(v PermissionDesc) bool {
|
||||
if d.Type != v.Type {
|
||||
return false
|
||||
}
|
||||
switch d.Type {
|
||||
case PermissionHash:
|
||||
return d.Hash().Equals(v.Hash())
|
||||
case PermissionGroup:
|
||||
return d.Group().Cmp(v.Group()) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValid checks if Permission is correct.
|
||||
func (p *Permission) IsValid() error {
|
||||
for i := range p.Methods.Value {
|
||||
|
@ -122,45 +152,8 @@ func (ps Permissions) AreValid() error {
|
|||
for i := range ps {
|
||||
contracts = append(contracts, ps[i].Contract)
|
||||
}
|
||||
sort.Slice(contracts, func(i, j int) bool {
|
||||
if contracts[i].Type < contracts[j].Type {
|
||||
return true
|
||||
}
|
||||
if contracts[i].Type != contracts[j].Type {
|
||||
return false
|
||||
}
|
||||
switch contracts[i].Type {
|
||||
case PermissionHash:
|
||||
return contracts[i].Hash().Less(contracts[j].Hash())
|
||||
case PermissionGroup:
|
||||
return contracts[i].Group().Cmp(contracts[j].Group()) < 0
|
||||
}
|
||||
return false
|
||||
})
|
||||
for i := range contracts {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
j := i - 1
|
||||
if contracts[i].Type != contracts[j].Type {
|
||||
continue
|
||||
}
|
||||
var bad bool
|
||||
switch contracts[i].Type {
|
||||
case PermissionWildcard:
|
||||
bad = true
|
||||
case PermissionHash:
|
||||
if contracts[i].Hash() == contracts[j].Hash() {
|
||||
bad = true
|
||||
}
|
||||
case PermissionGroup:
|
||||
if contracts[i].Group().Cmp(contracts[j].Group()) == 0 {
|
||||
bad = true
|
||||
}
|
||||
}
|
||||
if bad {
|
||||
return errors.New("duplicate contracts")
|
||||
}
|
||||
if permissionDescsHaveDups(contracts) {
|
||||
return errors.New("contracts have duplicates")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -254,20 +247,55 @@ func (d *PermissionDesc) UnmarshalJSON(data []byte) error {
|
|||
return errors.New("unknown permission")
|
||||
}
|
||||
|
||||
// ToStackItem converts PermissionDesc to stackitem.Item.
|
||||
func (d *PermissionDesc) ToStackItem() stackitem.Item {
|
||||
switch d.Type {
|
||||
case PermissionWildcard:
|
||||
return stackitem.Null{}
|
||||
case PermissionHash:
|
||||
return stackitem.NewByteArray(d.Hash().BytesBE())
|
||||
case PermissionGroup:
|
||||
return stackitem.NewByteArray(d.Group().Bytes())
|
||||
default:
|
||||
panic("unsupported PermissionDesc type")
|
||||
}
|
||||
}
|
||||
|
||||
// FromStackItem converts stackitem.Item to PermissionDesc.
|
||||
func (d *PermissionDesc) FromStackItem(item stackitem.Item) error {
|
||||
if _, ok := item.(stackitem.Null); ok {
|
||||
d.Type = PermissionWildcard
|
||||
return nil
|
||||
}
|
||||
if item.Type() != stackitem.ByteArrayT {
|
||||
return fmt.Errorf("unsupported permission descriptor type: %s", item.Type())
|
||||
}
|
||||
byteArr, err := item.TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch len(byteArr) {
|
||||
case util.Uint160Size:
|
||||
hash, _ := util.Uint160DecodeBytesBE(byteArr)
|
||||
d.Type = PermissionHash
|
||||
d.Value = hash
|
||||
case 33:
|
||||
pKey, err := keys.NewPublicKeyFromBytes(byteArr, elliptic.P256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Type = PermissionGroup
|
||||
d.Value = pKey
|
||||
default:
|
||||
return errors.New("invalid ByteArray length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToStackItem converts Permission to stackitem.Item.
|
||||
func (p *Permission) ToStackItem() stackitem.Item {
|
||||
var (
|
||||
contract stackitem.Item
|
||||
methods stackitem.Item
|
||||
)
|
||||
switch p.Contract.Type {
|
||||
case PermissionWildcard:
|
||||
contract = stackitem.Null{}
|
||||
case PermissionHash:
|
||||
contract = stackitem.NewByteArray(p.Contract.Hash().BytesBE())
|
||||
case PermissionGroup:
|
||||
contract = stackitem.NewByteArray(p.Contract.Group().Bytes())
|
||||
}
|
||||
var methods stackitem.Item
|
||||
contract := p.Contract.ToStackItem()
|
||||
if p.Methods.IsWildcard() {
|
||||
methods = stackitem.Null{}
|
||||
} else {
|
||||
|
@ -293,35 +321,12 @@ func (p *Permission) FromStackItem(item stackitem.Item) error {
|
|||
if len(str) != 2 {
|
||||
return errors.New("invalid Permission stackitem length")
|
||||
}
|
||||
if _, ok := str[0].(stackitem.Null); ok {
|
||||
p.Contract = PermissionDesc{
|
||||
Type: PermissionWildcard,
|
||||
}
|
||||
} else {
|
||||
byteArr, err := str[0].TryBytes()
|
||||
desc := new(PermissionDesc)
|
||||
err = desc.FromStackItem(str[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch len(byteArr) {
|
||||
case util.Uint160Size:
|
||||
hash, _ := util.Uint160DecodeBytesBE(byteArr)
|
||||
p.Contract = PermissionDesc{
|
||||
Type: PermissionHash,
|
||||
Value: hash,
|
||||
}
|
||||
case 33:
|
||||
pKey, err := keys.NewPublicKeyFromBytes(byteArr, elliptic.P256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Contract = PermissionDesc{
|
||||
Type: PermissionGroup,
|
||||
Value: pKey,
|
||||
}
|
||||
default:
|
||||
return errors.New("invalid Contract ByteArray length")
|
||||
}
|
||||
return fmt.Errorf("invalid Contract stackitem: %w", err)
|
||||
}
|
||||
p.Contract = *desc
|
||||
if _, ok := str[1].(stackitem.Null); ok {
|
||||
p.Methods = WildStrings{Value: nil}
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue