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.
This commit is contained in:
Roman Khimov 2020-06-18 11:39:47 +03:00
parent dde0763840
commit a7cce3f894
3 changed files with 43 additions and 15 deletions

View file

@ -112,8 +112,9 @@ func (t *Transaction) VerificationHash() util.Uint256 {
return t.verificationHash return t.verificationHash
} }
// DecodeBinary implements Serializable interface. // decodeHashableFields decodes the fields that are used for signing the
func (t *Transaction) DecodeBinary(br *io.BinReader) { // transaction, which are all fields except the scripts.
func (t *Transaction) decodeHashableFields(br *io.BinReader) {
t.Version = uint8(br.ReadB()) t.Version = uint8(br.ReadB())
if t.Version > 0 { if t.Version > 0 {
br.Err = errors.New("only version 0 is supported") 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") br.Err = errors.New("no script")
return return
} }
}
// DecodeBinary implements Serializable interface.
func (t *Transaction) DecodeBinary(br *io.BinReader) {
t.decodeHashableFields(br)
if br.Err != nil {
return
}
br.ReadArray(&t.Scripts) br.ReadArray(&t.Scripts)
// Create the hash of the transaction at decode, so we dont need // Create the hash of the transaction at decode, so we dont need
@ -218,6 +226,23 @@ func (t *Transaction) GetSignedPart() []byte {
return buf.Bytes() 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 // Bytes converts the transaction to []byte
func (t *Transaction) Bytes() []byte { func (t *Transaction) Bytes() []byte {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()

View file

@ -4,3 +4,10 @@ package crypto
type Verifiable interface { type Verifiable interface {
GetSignedPart() []byte GetSignedPart() []byte
} }
// VerifiableDecodable represents an object which can be both verified and
// decoded from given data.
type VerifiableDecodable interface {
Verifiable
DecodeSignedPart([]byte) error
}

View file

@ -10,6 +10,7 @@ import (
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
@ -24,7 +25,7 @@ type ParameterContext struct {
// Type is a type of a verifiable item. // Type is a type of a verifiable item.
Type string Type string
// Verifiable is an object which can be (de-)serialized. // 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 is a map from script hashes to context items.
Items map[util.Uint160]*Item Items map[util.Uint160]*Item
} }
@ -41,7 +42,7 @@ type sigWithIndex struct {
} }
// NewParameterContext returns ParameterContext with the specified type and item to sign. // 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{ return &ParameterContext{
Type: typ, Type: typ,
Verifiable: verif, Verifiable: verif,
@ -144,11 +145,7 @@ func (c *ParameterContext) getItemForContract(ctr *wallet.Contract) *Item {
// MarshalJSON implements json.Marshaler interface. // MarshalJSON implements json.Marshaler interface.
func (c ParameterContext) MarshalJSON() ([]byte, error) { func (c ParameterContext) MarshalJSON() ([]byte, error) {
bw := io.NewBufBinWriter() verif := c.Verifiable.GetSignedPart()
c.Verifiable.EncodeBinary(bw.BinWriter)
if bw.Err != nil {
return nil, bw.Err
}
items := make(map[string]json.RawMessage, len(c.Items)) items := make(map[string]json.RawMessage, len(c.Items))
for u := range c.Items { for u := range c.Items {
data, err := json.Marshal(c.Items[u]) data, err := json.Marshal(c.Items[u])
@ -159,7 +156,7 @@ func (c ParameterContext) MarshalJSON() ([]byte, error) {
} }
pc := &paramContext{ pc := &paramContext{
Type: c.Type, Type: c.Type,
Hex: hex.EncodeToString(bw.Bytes()), Hex: hex.EncodeToString(verif),
Items: items, Items: items,
} }
return json.Marshal(pc) return json.Marshal(pc)
@ -176,17 +173,16 @@ func (c *ParameterContext) UnmarshalJSON(data []byte) error {
return err return err
} }
var verif io.Serializable var verif crypto.VerifiableDecodable
switch pc.Type { switch pc.Type {
case "Neo.Core.ContractTransaction": case "Neo.Core.ContractTransaction":
verif = new(transaction.Transaction) verif = new(transaction.Transaction)
default: default:
return fmt.Errorf("unsupported type: %s", c.Type) return fmt.Errorf("unsupported type: %s", c.Type)
} }
br := io.NewBinReaderFromBuf(data) err = verif.DecodeSignedPart(data)
verif.DecodeBinary(br) if err != nil {
if br.Err != nil { return err
return br.Err
} }
items := make(map[util.Uint160]*Item, len(pc.Items)) items := make(map[util.Uint160]*Item, len(pc.Items))
for h := range pc.Items { for h := range pc.Items {