rpcbinding: properly support maps

I'm not sure that map with a `*big.Int` key is a good one, but it can work
this way.
This commit is contained in:
Roman Khimov 2022-12-01 22:16:37 +03:00
parent 8e0be6e7a5
commit ce67e6795e
2 changed files with 98 additions and 15 deletions

View file

@ -109,7 +109,7 @@ type ManagementGroup struct {
type ManagementManifest struct { type ManagementManifest struct {
Name string Name string
Groups []*ManagementGroup Groups []*ManagementGroup
Features *stackitem.Map Features map[string]string
SupportedStandards []string SupportedStandards []string
ABI *ManagementABI ABI *ManagementABI
Permissions []*ManagementPermission Permissions []*ManagementPermission
@ -151,7 +151,7 @@ type StructsInternal struct {
Sign []byte Sign []byte
ArrOfBytes [][]byte ArrOfBytes [][]byte
ArrOfH160 []util.Uint160 ArrOfH160 []util.Uint160
Map *stackitem.Map Map map[*big.Int]keys.PublicKeys
Struct *StructsInternal Struct *StructsInternal
} }
// Invoker is used by ContractReader to call various safe methods. // Invoker is used by ContractReader to call various safe methods.
@ -959,11 +959,42 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife
} }
index++ index++
res.Features, err = func (item stackitem.Item) (*stackitem.Map, error) { res.Features, err = func (item stackitem.Item) (map[string]string, error) {
if t := item.Type(); t != stackitem.MapT { m, ok := item.Value().([]stackitem.MapElement)
return nil, fmt.Errorf("%s is not a map", t.String()) if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
} }
return item.(*stackitem.Map), nil res := make(map[string]string)
for i := range m {
k, err := func (item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
} (m[i].Key)
if err != nil {
return nil, err
}
v, err := func (item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
} (m[i].Value)
if err != nil {
return nil, err
}
res[k] = v
}
return res, nil
} (arr[index]) } (arr[index])
if err != nil { if err != nil {
return nil, err return nil, err
@ -1402,11 +1433,47 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er
} }
index++ index++
res.Map, err = func (item stackitem.Item) (*stackitem.Map, error) { res.Map, err = func (item stackitem.Item) (map[*big.Int]keys.PublicKeys, error) {
if t := item.Type(); t != stackitem.MapT { m, ok := item.Value().([]stackitem.MapElement)
return nil, fmt.Errorf("%s is not a map", t.String()) if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
} }
return item.(*stackitem.Map), nil res := make(map[*big.Int]keys.PublicKeys)
for i := range m {
k, err := m[i].Key.TryInteger()
if err != nil {
return nil, err
}
v, err := func (item stackitem.Item) (keys.PublicKeys, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make(keys.PublicKeys, len(arr))
for i := range res {
res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
} (arr[i])
if err != nil {
return nil, err
}
}
return res, nil
} (m[i].Value)
if err != nil {
return nil, err
}
res[k] = v
}
return res, nil
} (arr[index]) } (arr[index])
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -378,7 +378,9 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended
return "[]interface{}", "" return "[]interface{}", ""
case smartcontract.MapType: case smartcontract.MapType:
return "*stackitem.Map", "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" kt, _ := extendedTypeToGo(binding.ExtendedType{Base: et.Key}, named)
vt, _ := extendedTypeToGo(*et.Value, named)
return "map[" + kt + "]" + vt, "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
case smartcontract.InteropInterfaceType: case smartcontract.InteropInterfaceType:
return "interface{}", "" return "interface{}", ""
case smartcontract.VoidType: case smartcontract.VoidType:
@ -472,11 +474,25 @@ func etTypeConverter(et binding.ExtendedType, v string) string {
}, v) }, v)
case smartcontract.MapType: case smartcontract.MapType:
return `func (item stackitem.Item) (*stackitem.Map, error) { at, _ := extendedTypeToGo(et, nil)
if t := item.Type(); t != stackitem.MapT { return `func (item stackitem.Item) (` + at + `, error) {
return nil, fmt.Errorf("%s is not a map", t.String()) m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
} }
return item.(*stackitem.Map), nil res := make(` + at + `)
for i := range m {
k, err := ` + strings.ReplaceAll(etTypeConverter(binding.ExtendedType{Base: et.Key}, "m[i].Key"), "\n", "\n\t\t") + `
if err != nil {
return nil, err
}
v, err := ` + strings.ReplaceAll(etTypeConverter(*et.Value, "m[i].Value"), "\n", "\n\t\t") + `
if err != nil {
return nil, err
}
res[k] = v
}
return res, nil
} (` + v + `)` } (` + v + `)`
case smartcontract.InteropInterfaceType: case smartcontract.InteropInterfaceType:
return "item.Value(), nil" return "item.Value(), nil"