From a7cce3f894c75068e46ee4a638ece7d379404fe8 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 18 Jun 2020 11:39:47 +0300 Subject: [PATCH] smartcontract: use new VerifiableDecodable for ParameterContext And implement it for Transaction, the only user of ParameterContext for now. Which make correct signing/verifying possible for cases when serialization for general transmission and signing differ. --- pkg/core/transaction/transaction.go | 29 ++++++++++++++++++++++++++-- pkg/crypto/verifiable.go | 7 +++++++ pkg/smartcontract/context/context.go | 22 +++++++++------------ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index c48eb5e8e..79d25a4b5 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -112,8 +112,9 @@ func (t *Transaction) VerificationHash() util.Uint256 { return t.verificationHash } -// DecodeBinary implements Serializable interface. -func (t *Transaction) DecodeBinary(br *io.BinReader) { +// decodeHashableFields decodes the fields that are used for signing the +// transaction, which are all fields except the scripts. +func (t *Transaction) decodeHashableFields(br *io.BinReader) { t.Version = uint8(br.ReadB()) if t.Version > 0 { br.Err = errors.New("only version 0 is supported") @@ -154,7 +155,14 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) { br.Err = errors.New("no script") return } +} +// DecodeBinary implements Serializable interface. +func (t *Transaction) DecodeBinary(br *io.BinReader) { + t.decodeHashableFields(br) + if br.Err != nil { + return + } br.ReadArray(&t.Scripts) // Create the hash of the transaction at decode, so we dont need @@ -218,6 +226,23 @@ func (t *Transaction) GetSignedPart() []byte { return buf.Bytes() } +// DecodeSignedPart decodes a part of transaction from GetSignedPart data. +func (t *Transaction) DecodeSignedPart(buf []byte) error { + r := io.NewBinReaderFromBuf(buf) + t.decodeHashableFields(r) + if r.Err != nil { + return r.Err + } + // Ensure all the data was read. + _ = r.ReadB() + if r.Err == nil { + return errors.New("additional data after the signed part") + } + t.Scripts = make([]Witness, 0) + _ = t.createHash() + return nil +} + // Bytes converts the transaction to []byte func (t *Transaction) Bytes() []byte { buf := io.NewBufBinWriter() diff --git a/pkg/crypto/verifiable.go b/pkg/crypto/verifiable.go index 6dc643c1d..341358f62 100644 --- a/pkg/crypto/verifiable.go +++ b/pkg/crypto/verifiable.go @@ -4,3 +4,10 @@ package crypto type Verifiable interface { GetSignedPart() []byte } + +// VerifiableDecodable represents an object which can be both verified and +// decoded from given data. +type VerifiableDecodable interface { + Verifiable + DecodeSignedPart([]byte) error +} diff --git a/pkg/smartcontract/context/context.go b/pkg/smartcontract/context/context.go index c92b1fcd3..16fc0b424 100644 --- a/pkg/smartcontract/context/context.go +++ b/pkg/smartcontract/context/context.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -24,7 +25,7 @@ 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 + Verifiable crypto.VerifiableDecodable // Items is a map from script hashes to context items. Items map[util.Uint160]*Item } @@ -41,7 +42,7 @@ type sigWithIndex struct { } // NewParameterContext returns ParameterContext with the specified type and item to sign. -func NewParameterContext(typ string, verif io.Serializable) *ParameterContext { +func NewParameterContext(typ string, verif crypto.VerifiableDecodable) *ParameterContext { return &ParameterContext{ Type: typ, Verifiable: verif, @@ -144,11 +145,7 @@ func (c *ParameterContext) getItemForContract(ctr *wallet.Contract) *Item { // 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 - } + verif := c.Verifiable.GetSignedPart() items := make(map[string]json.RawMessage, len(c.Items)) for u := range c.Items { data, err := json.Marshal(c.Items[u]) @@ -159,7 +156,7 @@ func (c ParameterContext) MarshalJSON() ([]byte, error) { } pc := ¶mContext{ Type: c.Type, - Hex: hex.EncodeToString(bw.Bytes()), + Hex: hex.EncodeToString(verif), Items: items, } return json.Marshal(pc) @@ -176,17 +173,16 @@ func (c *ParameterContext) UnmarshalJSON(data []byte) error { return err } - var verif io.Serializable + var verif crypto.VerifiableDecodable 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 + err = verif.DecodeSignedPart(data) + if err != nil { + return err } items := make(map[util.Uint160]*Item, len(pc.Items)) for h := range pc.Items {