Merge pull request #2026 from nspcc-dev/fix-state

Fix manifest `Extra` field marshaling
This commit is contained in:
Roman Khimov 2021-06-29 15:11:25 +03:00 committed by GitHub
commit 3646270af0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 7 deletions

View file

@ -312,7 +312,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
ScriptHash: verifyH, ScriptHash: verifyH,
Name: "OnNEP11Payment", Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{ Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(nftOwnerHash.BytesBE()), stackitem.NewBuffer(nftOwnerHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewByteArray(tokenID1), stackitem.NewByteArray(tokenID1),
stackitem.NewByteArray([]byte("some_data")), stackitem.NewByteArray([]byte("some_data")),

View file

@ -1,12 +1,14 @@
package manifest package manifest
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"math" "math"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
ojson "github.com/virtuald/go-ordered-json"
) )
const ( const (
@ -140,10 +142,7 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
} }
trusts = stackitem.Make(tItems) trusts = stackitem.Make(tItems)
} }
extra := stackitem.Make("null") extra := extraToStackItem(m.Extra)
if m.Extra != nil {
extra = stackitem.NewByteArray(m.Extra)
}
return stackitem.NewStruct([]stackitem.Item{ return stackitem.NewStruct([]stackitem.Item{
stackitem.Make(m.Name), stackitem.Make(m.Name),
stackitem.Make(groups), stackitem.Make(groups),
@ -156,6 +155,29 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
}), nil }), 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. // FromStackItem converts stackitem.Item to Manifest.
func (m *Manifest) FromStackItem(item stackitem.Item) error { func (m *Manifest) FromStackItem(item stackitem.Item) error {
var err 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) 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))
}
}

View file

@ -129,7 +129,10 @@ func decodeBinaryStackItem(r *io.BinReader, allowInvalid bool) Item {
switch t { switch t {
case ByteArrayT, BufferT: case ByteArrayT, BufferT:
data := r.ReadVarBytes(MaxSize) data := r.ReadVarBytes(MaxSize)
return NewByteArray(data) if t == ByteArrayT {
return NewByteArray(data)
}
return NewBuffer(data)
case BooleanT: case BooleanT:
var b = r.ReadBool() var b = r.ReadBool()
return NewBool(b) return NewBool(b)