From 0d419d3899293919aa5bc1f9ab4b374a81640020 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 4 Mar 2020 13:18:49 +0300 Subject: [PATCH] smartcontract: implement ParameterContext --- pkg/smartcontract/context/context.go | 92 +++++++++++++++++++++++ pkg/smartcontract/context/context_test.go | 64 ++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 pkg/smartcontract/context/context.go create mode 100644 pkg/smartcontract/context/context_test.go diff --git a/pkg/smartcontract/context/context.go b/pkg/smartcontract/context/context.go new file mode 100644 index 000000000..3dd73ca5a --- /dev/null +++ b/pkg/smartcontract/context/context.go @@ -0,0 +1,92 @@ +package context + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// ParameterContext represents smartcontract parameter's context. +type ParameterContext struct { + // Type is a type of a verifiable item. + Type string + // Verifiable is an object which can be (de-)serialized. + Verifiable io.Serializable + // Items is a map from script hashes to context items. + Items map[util.Uint160]*Item +} + +type paramContext struct { + Type string `json:"type"` + Hex string `json:"hex"` + Items map[string]json.RawMessage `json:"items"` +} + +// MarshalJSON implements json.Marshaler interface. +func (c ParameterContext) MarshalJSON() ([]byte, error) { + bw := io.NewBufBinWriter() + c.Verifiable.EncodeBinary(bw.BinWriter) + if bw.Err != nil { + return nil, bw.Err + } + items := make(map[string]json.RawMessage, len(c.Items)) + for u := range c.Items { + data, err := json.Marshal(c.Items[u]) + if err != nil { + return nil, err + } + items["0x"+u.StringBE()] = data + } + pc := ¶mContext{ + Type: c.Type, + Hex: hex.EncodeToString(bw.Bytes()), + Items: items, + } + return json.Marshal(pc) +} + +// UnmarshalJSON implements json.Unmarshaler interface. +func (c *ParameterContext) UnmarshalJSON(data []byte) error { + pc := new(paramContext) + if err := json.Unmarshal(data, pc); err != nil { + return err + } + data, err := hex.DecodeString(pc.Hex) + if err != nil { + return err + } + + var verif io.Serializable + switch pc.Type { + case "Neo.Core.ContractTransaction": + verif = new(transaction.Transaction) + default: + return fmt.Errorf("unsupported type: %s", c.Type) + } + br := io.NewBinReaderFromBuf(data) + verif.DecodeBinary(br) + if br.Err != nil { + return br.Err + } + items := make(map[util.Uint160]*Item, len(pc.Items)) + for h := range pc.Items { + u, err := util.Uint160DecodeStringBE(strings.TrimPrefix(h, "0x")) + if err != nil { + return err + } + item := new(Item) + if err := json.Unmarshal(pc.Items[h], item); err != nil { + return err + } + items[u] = item + } + c.Type = pc.Type + c.Verifiable = verif + c.Items = items + return nil +} diff --git a/pkg/smartcontract/context/context_test.go b/pkg/smartcontract/context/context_test.go new file mode 100644 index 000000000..4cf7d967f --- /dev/null +++ b/pkg/smartcontract/context/context_test.go @@ -0,0 +1,64 @@ +package context + +import ( + "encoding/hex" + "encoding/json" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "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/stretchr/testify/require" +) + +func TestParameterContext_MarshalJSON(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + tx := getContractTx() + data := tx.GetSignedPart() + sign := priv.Sign(data) + + expected := &ParameterContext{ + Type: "Neo.Core.ContractTransaction", + Verifiable: tx, + Items: map[util.Uint160]*Item{ + priv.GetScriptHash(): { + Script: priv.GetScriptHash(), + Parameters: []smartcontract.Parameter{{ + Type: smartcontract.SignatureType, + Value: sign, + }}, + Signatures: map[string][]byte{ + hex.EncodeToString(priv.PublicKey().Bytes()): sign, + }, + }, + }, + } + + data, err = json.Marshal(expected) + require.NoError(t, err) + + actual := new(ParameterContext) + require.NoError(t, json.Unmarshal(data, actual)) + require.Equal(t, expected, actual) +} + +func getContractTx() *transaction.Transaction { + tx := transaction.NewContractTX() + tx.AddInput(&transaction.Input{ + PrevHash: util.Uint256{1, 2, 3, 4}, + PrevIndex: 5, + }) + tx.AddOutput(&transaction.Output{ + AssetID: util.Uint256{7, 8, 9}, + Amount: 10, + ScriptHash: util.Uint160{11, 12}, + }) + tx.Data = new(transaction.ContractTX) + tx.Attributes = make([]transaction.Attribute, 0) + tx.Scripts = make([]transaction.Witness, 0) + tx.Hash() + return tx +}