smartcontract: marshal manifest Extra field as object, fix #2021

This is needed to strip whitespace which can be present.

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2021-06-28 19:09:19 +03:00
parent 8d67a03aec
commit 9c06ba6b07
2 changed files with 49 additions and 5 deletions

View file

@ -1,12 +1,14 @@
package manifest
import (
"bytes"
"encoding/json"
"errors"
"math"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
ojson "github.com/virtuald/go-ordered-json"
)
const (
@ -140,10 +142,7 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
}
trusts = stackitem.Make(tItems)
}
extra := stackitem.Make("null")
if m.Extra != nil {
extra = stackitem.NewByteArray(m.Extra)
}
extra := extraToStackItem(m.Extra)
return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(m.Name),
stackitem.Make(groups),
@ -156,6 +155,29 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
}), nil
}
// extraToStackItem removes indentation from `Extra` field in JSON and
// converts it to a byte-array stack item.
func extraToStackItem(rawExtra []byte) stackitem.Item {
extra := stackitem.Make("null")
if rawExtra == nil || string(rawExtra) == "null" {
return extra
}
d := ojson.NewDecoder(bytes.NewReader(rawExtra))
// The result is put directly in the database and affects state-root calculation,
// thus use ordered map to stay compatible with C# implementation.
d.UseOrderedObject()
// Prevent accidental precision loss.
d.UseNumber()
var obj interface{}
// The error can't really occur because `json.RawMessage` is already a valid json.
_ = d.Decode(&obj)
res, _ := ojson.Marshal(obj)
return stackitem.NewByteArray(res)
}
// FromStackItem converts stackitem.Item to Manifest.
func (m *Manifest) FromStackItem(item stackitem.Item) error {
var err error

View file

@ -289,7 +289,7 @@ func TestManifestToStackItem(t *testing.T) {
},
},
},
Extra: []byte(`even not a json allowed`),
Extra: []byte(`"even string allowed"`),
}
check(t, expected)
})
@ -395,3 +395,25 @@ func TestABI_FromStackItemErrors(t *testing.T) {
})
}
}
func TestExtraToStackItem(t *testing.T) {
testCases := []struct {
raw, expected string
}{
{"null", "null"},
{"1", "1"},
{"1.23456789101112131415", "1.23456789101112131415"},
{`"string with space"`, `"string with space"`},
{`{ "a":1, "sss" : {"m" : 1, "a" : 2} , "x" : 2 ,"c" :3,"z":4, "s":"5"}`,
`{"a":1,"sss":{"m":1,"a":2},"x":2,"c":3,"z":4,"s":"5"}`},
{` [ 1, "array", { "d": "z", "a":"x", "c" : "y", "b":3}]`,
`[1,"array",{"d":"z","a":"x","c":"y","b":3}]`},
}
for _, tc := range testCases {
res := extraToStackItem([]byte(tc.raw))
actual, ok := res.Value().([]byte)
require.True(t, ok)
require.Equal(t, tc.expected, string(actual))
}
}