Merge pull request #1704 from nspcc-dev/binary_manifest

core: serialise manifest as stackitem
This commit is contained in:
Roman Khimov 2021-02-05 23:58:07 +03:00 committed by GitHub
commit e4b5b59663
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 905 additions and 17 deletions

View file

@ -23,7 +23,6 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
ne, err := nef.NewFile(script)
require.NoError(t, err)
manif := manifest.NewManifest("Test")
require.NoError(t, err)
h := state.CreateContractHash(sender, ne.Checksum, manif.Name)

View file

@ -534,7 +534,7 @@ func compareContractStates(t *testing.T, expected *state.Contract, actual stacki
act, ok := actual.Value().([]stackitem.Item)
require.True(t, ok)
expectedManifest, err := json.Marshal(expected.Manifest)
expectedManifest, err := expected.Manifest.ToStackItem()
require.NoError(t, err)
expectedNef, err := expected.NEF.Bytes()
require.NoError(t, err)
@ -544,7 +544,7 @@ func compareContractStates(t *testing.T, expected *state.Contract, actual stacki
require.Equal(t, expected.UpdateCounter, uint16(act[1].Value().(*big.Int).Int64()))
require.Equal(t, expected.Hash.BytesBE(), act[2].Value().([]byte))
require.Equal(t, expectedNef, act[3].Value().([]byte))
require.Equal(t, expectedManifest, act[4].Value().([]byte))
require.Equal(t, expectedManifest, act[4])
}
func TestMinimumDeploymentFee(t *testing.T) {

View file

@ -1,7 +1,6 @@
package state
import (
"encoding/json"
"errors"
"math"
"math/big"
@ -47,11 +46,11 @@ func (c *Contract) EncodeBinary(w *io.BinWriter) {
// ToStackItem converts state.Contract to stackitem.Item
func (c *Contract) ToStackItem() (stackitem.Item, error) {
manifest, err := json.Marshal(c.Manifest)
rawNef, err := c.NEF.Bytes()
if err != nil {
return nil, err
}
rawNef, err := c.NEF.Bytes()
m, err := c.Manifest.ToStackItem()
if err != nil {
return nil, err
}
@ -60,7 +59,7 @@ func (c *Contract) ToStackItem() (stackitem.Item, error) {
stackitem.Make(c.UpdateCounter),
stackitem.NewByteArray(c.Hash.BytesBE()),
stackitem.NewByteArray(rawNef),
stackitem.NewByteArray(manifest),
m,
}), nil
}
@ -103,11 +102,13 @@ func (c *Contract) FromStackItem(item stackitem.Item) error {
if err != nil {
return err
}
bytes, err = arr[4].TryBytes()
m := new(manifest.Manifest)
err = m.FromStackItem(arr[4])
if err != nil {
return err
}
return json.Unmarshal(bytes, &c.Manifest)
c.Manifest = *m
return nil
}
// CreateContractHash creates deployed contract hash from transaction sender

View file

@ -1,7 +1,6 @@
package state
import (
"encoding/json"
"math"
"testing"
@ -90,8 +89,7 @@ func TestContractFromStackItem(t *testing.T) {
rawNef, _ = nefFile.Bytes()
nefItem = stackitem.NewByteArray(rawNef)
manifest = manifest.DefaultManifest("stack item")
manifestB, _ = json.Marshal(manifest)
manifItem = stackitem.Make(manifestB)
manifItem, _ = manifest.ToStackItem()
badCases = []struct {
name string
@ -105,8 +103,8 @@ func TestContractFromStackItem(t *testing.T) {
{"hash is not a byte string", stackitem.Make([]stackitem.Item{id, counter, stackitem.NewArray(nil), nefItem, manifItem})},
{"hash is not a hash", stackitem.Make([]stackitem.Item{id, counter, stackitem.Make([]byte{1, 2, 3}), nefItem, manifItem})},
{"nef is not a byte string", stackitem.Make([]stackitem.Item{id, counter, chash, stackitem.NewArray(nil), manifItem})},
{"manifest is not a byte string", stackitem.Make([]stackitem.Item{id, counter, chash, nefItem, stackitem.NewArray(nil)})},
{"manifest is not correct", stackitem.Make([]stackitem.Item{id, counter, chash, nefItem, stackitem.Make(100500)})},
{"manifest is not an array", stackitem.Make([]stackitem.Item{id, counter, chash, nefItem, stackitem.NewByteArray(nil)})},
{"manifest is not correct", stackitem.Make([]stackitem.Item{id, counter, chash, nefItem, stackitem.NewArray([]stackitem.Item{stackitem.Make(100500)})})},
}
)
for _, cs := range badCases {

View file

@ -330,7 +330,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
}
return c.GetContractStateByHash(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {
@ -351,7 +351,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) {
return c.GetContractStateByAddressOrName("NWiu5oejTu925aeL9Hc1LX8SvaJhE23h15")
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {
@ -372,7 +372,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) {
return c.GetContractStateByID(0)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {

View file

@ -2,10 +2,12 @@ package manifest
import (
"encoding/json"
"errors"
"math"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
const (
@ -65,6 +67,7 @@ func NewManifest(name string) *Manifest {
Events: []Event{},
},
Groups: []Group{},
Permissions: []Permission{},
SupportedStandards: []string{},
}
m.Trusts.Restrict()
@ -138,3 +141,181 @@ func (m *Manifest) DecodeBinary(r *io.BinReader) {
r.Err = err
}
}
// ToStackItem converts Manifest to stackitem.Item.
func (m *Manifest) ToStackItem() (stackitem.Item, error) {
groups := make([]stackitem.Item, len(m.Groups))
for i := range m.Groups {
groups[i] = m.Groups[i].ToStackItem()
}
supportedStandards := make([]stackitem.Item, len(m.SupportedStandards))
for i := range m.SupportedStandards {
supportedStandards[i] = stackitem.Make(m.SupportedStandards[i])
}
abi := m.ABI.ToStackItem()
permissions := make([]stackitem.Item, len(m.Permissions))
for i := range m.Permissions {
permissions[i] = m.Permissions[i].ToStackItem()
}
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())
}
trusts = stackitem.Make(tItems)
}
extra := stackitem.Make("null")
if m.Extra != nil {
e, err := json.Marshal(m.Extra)
if err != nil {
return nil, err
}
extra = stackitem.NewByteArray(e)
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(m.Name),
stackitem.Make(groups),
stackitem.Make(supportedStandards),
abi,
stackitem.Make(permissions),
trusts,
extra,
}), nil
}
// FromStackItem converts stackitem.Item to Manifest.
func (m *Manifest) FromStackItem(item stackitem.Item) error {
var err error
if item.Type() != stackitem.StructT {
return errors.New("invalid Manifest stackitem type")
}
str := item.Value().([]stackitem.Item)
if len(str) != 7 {
return errors.New("invalid stackitem length")
}
m.Name, err = stackitem.ToString(str[0])
if err != nil {
return err
}
if str[1].Type() != stackitem.ArrayT {
return errors.New("invalid Groups stackitem type")
}
groups := str[1].Value().([]stackitem.Item)
m.Groups = make([]Group, len(groups))
for i := range groups {
group := new(Group)
err := group.FromStackItem(groups[i])
if err != nil {
return err
}
m.Groups[i] = *group
}
if str[2].Type() != stackitem.ArrayT {
return errors.New("invalid SupportedStandards stackitem type")
}
supportedStandards := str[2].Value().([]stackitem.Item)
m.SupportedStandards = make([]string, len(supportedStandards))
for i := range supportedStandards {
m.SupportedStandards[i], err = stackitem.ToString(supportedStandards[i])
if err != nil {
return err
}
}
abi := new(ABI)
if err := abi.FromStackItem(str[3]); err != nil {
return err
}
m.ABI = *abi
if str[4].Type() != stackitem.ArrayT {
return errors.New("invalid Permissions stackitem type")
}
permissions := str[4].Value().([]stackitem.Item)
m.Permissions = make([]Permission, len(permissions))
for i := range permissions {
p := new(Permission)
if err := p.FromStackItem(permissions[i]); err != nil {
return err
}
m.Permissions[i] = *p
}
if _, ok := str[5].(stackitem.Null); ok {
m.Trusts.Restrict()
} else {
if str[5].Type() != stackitem.ArrayT {
return errors.New("invalid Trusts stackitem type")
}
trusts := str[5].Value().([]stackitem.Item)
m.Trusts = WildUint160s{Value: make([]util.Uint160, len(trusts))}
for i := range trusts {
bytes, err := trusts[i].TryBytes()
if err != nil {
return err
}
m.Trusts.Value[i], err = util.Uint160DecodeBytesBE(bytes)
if err != nil {
return err
}
}
}
extra, err := str[6].TryBytes()
if err != nil {
return err
}
if string(extra) == "null" {
return nil
}
return json.Unmarshal(extra, &m.Extra)
}
// 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
}

View file

@ -2,10 +2,13 @@ package manifest
import (
"encoding/json"
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
@ -146,3 +149,184 @@ func TestIsValid(t *testing.T) {
})
})
}
func TestManifestToStackItem(t *testing.T) {
check := func(t *testing.T, expected *Manifest) {
item, err := expected.ToStackItem()
require.NoError(t, err)
actual := new(Manifest)
require.NoError(t, actual.FromStackItem(item))
require.Equal(t, expected, actual)
}
t.Run("default", func(t *testing.T) {
expected := DefaultManifest("manifest")
check(t, expected)
})
t.Run("full", func(t *testing.T) {
pk, _ := keys.NewPrivateKey()
expected := &Manifest{
Name: "manifest",
ABI: ABI{
Methods: []Method{{
Name: "method",
Offset: 15,
Parameters: []Parameter{{
Name: "param",
Type: smartcontract.StringType,
}},
ReturnType: smartcontract.BoolType,
Safe: true,
}},
Events: []Event{{
Name: "event",
Parameters: []Parameter{{
Name: "param",
Type: smartcontract.BoolType,
}},
}},
},
Groups: []Group{{
PublicKey: pk.PublicKey(),
Signature: []byte{1, 2, 3},
}},
Permissions: []Permission{*NewPermission(PermissionWildcard)},
SupportedStandards: []string{"NEP-17"},
Trusts: WildUint160s{
Value: []util.Uint160{{1, 2, 3}},
},
Extra: "some extra data",
}
check(t, expected)
})
}
func TestManifest_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid groups type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid group": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid supported standards type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid supported standard": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid ABI": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid Permissions type": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid permission": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}}),
"invalid Trusts type": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid trust": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}}),
"invalid Uint160 trust": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})}), stackitem.Null{}}),
"invalid extra type": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.Null{}}),
"invalid extra": stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewArray([]stackitem.Item{}),
stackitem.NewByteArray([]byte("not a json"))}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Manifest)
require.Error(t, p.FromStackItem(errCase))
})
}
}
func TestABI_ToStackItemFromStackItem(t *testing.T) {
a := &ABI{
Methods: []Method{{
Name: "mur",
Offset: 5,
Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}},
ReturnType: smartcontract.StringType,
Safe: true,
}},
Events: []Event{{
Name: "mur",
Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}},
}},
}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("mur")),
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("p1")),
stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))),
}),
}),
stackitem.NewBigInteger(big.NewInt(int64(smartcontract.StringType))),
stackitem.NewBigInteger(big.NewInt(int64(5))),
stackitem.NewBool(true),
}),
}),
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("mur")),
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("p1")),
stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))),
}),
}),
}),
}),
})
CheckToFromStackItem(t, a, expected)
}
func TestABI_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid methods type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid method": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}}),
"invalid events type": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}}),
"invalid event": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(ABI)
require.Error(t, p.FromStackItem(errCase))
})
}
}

View file

@ -1,13 +1,16 @@
package manifest
import (
"crypto/elliptic"
"encoding/hex"
"encoding/json"
"errors"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Parameter represents smartcontract's parameter's definition.
@ -84,3 +87,172 @@ func (g *Group) UnmarshalJSON(data []byte) error {
g.Signature = aux.Signature
return nil
}
// ToStackItem converts Group to stackitem.Item.
func (g *Group) ToStackItem() stackitem.Item {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(g.PublicKey.Bytes()),
stackitem.NewByteArray(g.Signature),
})
}
// FromStackItem converts stackitem.Item to Group.
func (g *Group) FromStackItem(item stackitem.Item) error {
if item.Type() != stackitem.StructT {
return errors.New("invalid Group stackitem type")
}
group := item.Value().([]stackitem.Item)
if len(group) != 2 {
return errors.New("invalid Group stackitem length")
}
pKey, err := group[0].TryBytes()
if err != nil {
return err
}
g.PublicKey, err = keys.NewPublicKeyFromBytes(pKey, elliptic.P256())
if err != nil {
return err
}
sig, err := group[1].TryBytes()
if err != nil {
return err
}
g.Signature = sig
return nil
}
// ToStackItem converts Method to stackitem.Item.
func (m *Method) ToStackItem() stackitem.Item {
params := make([]stackitem.Item, len(m.Parameters))
for i := range m.Parameters {
params[i] = m.Parameters[i].ToStackItem()
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(m.Name),
stackitem.Make(params),
stackitem.Make(int(m.ReturnType)),
stackitem.Make(m.Offset),
stackitem.Make(m.Safe),
})
}
// FromStackItem converts stackitem.Item to Method.
func (m *Method) FromStackItem(item stackitem.Item) error {
var err error
if item.Type() != stackitem.StructT {
return errors.New("invalid Method stackitem type")
}
method := item.Value().([]stackitem.Item)
if len(method) != 5 {
return errors.New("invalid Method stackitem length")
}
m.Name, err = stackitem.ToString(method[0])
if err != nil {
return err
}
if method[1].Type() != stackitem.ArrayT {
return errors.New("invalid Params stackitem type")
}
params := method[1].Value().([]stackitem.Item)
m.Parameters = make([]Parameter, len(params))
for i := range params {
p := new(Parameter)
if err := p.FromStackItem(params[i]); err != nil {
return err
}
m.Parameters[i] = *p
}
rTyp, err := method[2].TryInteger()
if err != nil {
return err
}
m.ReturnType, err = smartcontract.ConvertToParamType(int(rTyp.Int64()))
if err != nil {
return err
}
offset, err := method[3].TryInteger()
if err != nil {
return err
}
m.Offset = int(offset.Int64())
safe, err := method[4].TryBool()
if err != nil {
return err
}
m.Safe = safe
return nil
}
// ToStackItem converts Parameter to stackitem.Item.
func (p *Parameter) ToStackItem() stackitem.Item {
return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(p.Name),
stackitem.Make(int(p.Type)),
})
}
// FromStackItem converts stackitem.Item to Parameter.
func (p *Parameter) FromStackItem(item stackitem.Item) error {
var err error
if item.Type() != stackitem.StructT {
return errors.New("invalid Parameter stackitem type")
}
param := item.Value().([]stackitem.Item)
if len(param) != 2 {
return errors.New("invalid Parameter stackitem length")
}
p.Name, err = stackitem.ToString(param[0])
if err != nil {
return err
}
typ, err := param[1].TryInteger()
if err != nil {
return err
}
p.Type, err = smartcontract.ConvertToParamType(int(typ.Int64()))
if err != nil {
return err
}
return nil
}
// ToStackItem converts Event to stackitem.Item.
func (e *Event) ToStackItem() stackitem.Item {
params := make([]stackitem.Item, len(e.Parameters))
for i := range e.Parameters {
params[i] = e.Parameters[i].ToStackItem()
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(e.Name),
stackitem.Make(params),
})
}
// FromStackItem converts stackitem.Item to Event.
func (e *Event) FromStackItem(item stackitem.Item) error {
var err error
if item.Type() != stackitem.StructT {
return errors.New("invalid Event stackitem type")
}
event := item.Value().([]stackitem.Item)
if len(event) != 2 {
return errors.New("invalid Event stackitem length")
}
e.Name, err = stackitem.ToString(event[0])
if err != nil {
return err
}
if event[1].Type() != stackitem.ArrayT {
return errors.New("invalid Params stackitem type")
}
params := event[1].Value().([]stackitem.Item)
e.Parameters = make([]Parameter, len(params))
for i := range params {
p := new(Parameter)
if err := p.FromStackItem(params[i]); err != nil {
return err
}
e.Parameters[i] = *p
}
return nil
}

View file

@ -0,0 +1,144 @@
package manifest
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestMethod_ToStackItemFromStackItem(t *testing.T) {
m := &Method{
Name: "mur",
Offset: 5,
Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}},
ReturnType: smartcontract.StringType,
Safe: true,
}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(m.Name)),
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(m.Parameters[0].Name)),
stackitem.NewBigInteger(big.NewInt(int64(m.Parameters[0].Type))),
}),
}),
stackitem.NewBigInteger(big.NewInt(int64(m.ReturnType))),
stackitem.NewBigInteger(big.NewInt(int64(m.Offset))),
stackitem.NewBool(m.Safe),
})
CheckToFromStackItem(t, m, expected)
}
func TestMethod_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid parameters type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid parameter": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{stackitem.NewStruct([]stackitem.Item{})}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid return type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
"invalid offset": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid safe": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(5)), stackitem.NewInterop(nil)}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Method)
require.Error(t, p.FromStackItem(errCase))
})
}
}
func TestParameter_ToStackItemFromStackItem(t *testing.T) {
p := &Parameter{
Name: "param",
Type: smartcontract.StringType,
}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(p.Name)),
stackitem.NewBigInteger(big.NewInt(int64(p.Type))),
})
CheckToFromStackItem(t, p, expected)
}
func TestParameter_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid type type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}}),
"invalid type value": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewBigInteger(big.NewInt(-100500))}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Parameter)
require.Error(t, p.FromStackItem(errCase))
})
}
}
func TestEvent_ToStackItemFromStackItem(t *testing.T) {
m := &Event{
Name: "mur",
Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}},
}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(m.Name)),
stackitem.NewArray([]stackitem.Item{
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(m.Parameters[0].Name)),
stackitem.NewBigInteger(big.NewInt(int64(m.Parameters[0].Type))),
}),
}),
})
CheckToFromStackItem(t, m, expected)
}
func TestEvent_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid parameters type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}}),
"invalid parameter": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{stackitem.NewStruct([]stackitem.Item{})})}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Event)
require.Error(t, p.FromStackItem(errCase))
})
}
}
func TestGroup_ToStackItemFromStackItem(t *testing.T) {
pk, _ := keys.NewPrivateKey()
g := &Group{
PublicKey: pk.PublicKey(),
Signature: []byte{1, 2, 3},
}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(pk.PublicKey().Bytes()),
stackitem.NewByteArray([]byte{1, 2, 3}),
})
CheckToFromStackItem(t, g, expected)
}
func TestGroup_FromStackItemErrors(t *testing.T) {
pk, _ := keys.NewPrivateKey()
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid pub type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}),
"invalid pub bytes": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{1}), stackitem.Null{}}),
"invalid sig type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray(pk.Bytes()), stackitem.NewInterop(nil)}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Group)
require.Error(t, p.FromStackItem(errCase))
})
}
}

View file

@ -1,6 +1,7 @@
package manifest
import (
"crypto/elliptic"
"encoding/hex"
"encoding/json"
"errors"
@ -8,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// PermissionType represents permission type.
@ -171,3 +173,91 @@ func (d *PermissionDesc) UnmarshalJSON(data []byte) error {
}
return errors.New("unknown permission")
}
// 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())
}
if p.Methods.IsWildcard() {
methods = stackitem.Null{}
} else {
m := make([]stackitem.Item, len(p.Methods.Value))
for i := range p.Methods.Value {
m[i] = stackitem.Make(p.Methods.Value[i])
}
methods = stackitem.Make(m)
}
return stackitem.NewStruct([]stackitem.Item{
contract,
methods,
})
}
// FromStackItem converts stackitem.Item to Permission.
func (p *Permission) FromStackItem(item stackitem.Item) error {
var err error
if item.Type() != stackitem.StructT {
return errors.New("invalid Permission stackitem type")
}
str := item.Value().([]stackitem.Item)
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()
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")
}
}
if _, ok := str[1].(stackitem.Null); ok {
p.Methods = WildStrings{Value: nil}
} else {
if str[1].Type() != stackitem.ArrayT {
return errors.New("invalid Methods stackitem type")
}
methods := str[1].Value().([]stackitem.Item)
p.Methods = WildStrings{
Value: make([]string, len(methods)),
}
for i := range methods {
p.Methods.Value[i], err = stackitem.ToString(methods[i])
if err != nil {
return err
}
}
}
return nil
}

View file

@ -3,11 +3,13 @@ package manifest
import (
"encoding/json"
"fmt"
"reflect"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
@ -92,3 +94,67 @@ func testMarshalUnmarshal(t *testing.T, expected, actual interface{}) {
require.NoError(t, json.Unmarshal(data, actual))
require.Equal(t, expected, actual)
}
func TestPermission_ToStackItemFromStackItem(t *testing.T) {
t.Run("wildcard", func(t *testing.T) {
p := NewPermission(PermissionWildcard)
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.Null{},
stackitem.Null{},
})
CheckToFromStackItem(t, p, expected)
})
t.Run("hash", func(t *testing.T) {
p := NewPermission(PermissionHash, util.Uint160{1, 2, 3})
p.Methods = WildStrings{Value: []string{"a"}}
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(util.Uint160{1, 2, 3}.BytesBE()),
stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray([]byte("a")),
}),
})
CheckToFromStackItem(t, p, expected)
})
t.Run("group", func(t *testing.T) {
pk, _ := keys.NewPrivateKey()
p := NewPermission(PermissionGroup, pk.PublicKey())
expected := stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(pk.PublicKey().Bytes()),
stackitem.Null{},
})
CheckToFromStackItem(t, p, expected)
})
}
type Interoperable interface {
ToStackItem() stackitem.Item
FromStackItem(stackitem.Item) error
}
func CheckToFromStackItem(t *testing.T, source Interoperable, expected stackitem.Item) {
actual := source.ToStackItem()
require.Equal(t, expected, actual)
actualSource := reflect.New(reflect.TypeOf(source).Elem()).Interface().(Interoperable)
require.NoError(t, actualSource.FromStackItem(actual))
require.Equal(t, source, actualSource)
}
func TestPermission_FromStackItemErrors(t *testing.T) {
errCases := map[string]stackitem.Item{
"not a struct": stackitem.NewArray([]stackitem.Item{}),
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
"invalid contract type": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewBool(false)}),
"invalid contract length": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3}), stackitem.NewBool(false)}),
"invalid contract pubkey": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray(make([]byte, 33)), stackitem.NewBool(false)}),
"invalid methods type": stackitem.NewStruct([]stackitem.Item{stackitem.Null{}, stackitem.NewBool(false)}),
"invalid method name": stackitem.NewStruct([]stackitem.Item{stackitem.Null{}, stackitem.NewArray([]stackitem.Item{stackitem.NewArray([]stackitem.Item{})})}),
}
for name, errCase := range errCases {
t.Run(name, func(t *testing.T) {
p := new(Permission)
require.Error(t, p.FromStackItem(errCase))
})
}
}

View file

@ -35,6 +35,24 @@ const (
VoidType ParamType = 0xff
)
// validParamTypes contains a map of known ParamTypes
var validParamTypes = map[ParamType]bool{
UnknownType: true,
AnyType: true,
BoolType: true,
IntegerType: true,
ByteArrayType: true,
StringType: true,
Hash160Type: true,
Hash256Type: true,
PublicKeyType: true,
SignatureType: true,
ArrayType: true,
MapType: true,
InteropInterfaceType: true,
VoidType: true,
}
// String implements the stringer interface.
func (pt ParamType) String() string {
switch pt {
@ -268,3 +286,11 @@ func inferParamType(val string) ParamType {
// Anything can be a string.
return StringType
}
// ConvertToParamType converts provided value to parameter type if it's a valid type.
func ConvertToParamType(val int) (ParamType, error) {
if validParamTypes[ParamType(val)] {
return ParamType(val), nil
}
return UnknownType, errors.New("unknown parameter type")
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseParamType(t *testing.T) {
@ -315,3 +316,29 @@ func mustHex(s string) []byte {
}
return b
}
func TestConvertToParamType(t *testing.T) {
for _, expected := range []ParamType{
UnknownType,
AnyType,
BoolType,
IntegerType,
ByteArrayType,
StringType,
Hash160Type,
Hash256Type,
PublicKeyType,
SignatureType,
ArrayType,
MapType,
InteropInterfaceType,
VoidType,
} {
actual, err := ConvertToParamType(int(expected))
require.NoError(t, err)
require.Equal(t, expected, actual)
}
_, err := ConvertToParamType(0x01)
require.NotNil(t, err)
}