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
}
// 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()

View file

@ -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
}

View file

@ -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 := &paramContext{
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 {