diff --git a/pkg/consensus/block.go b/pkg/consensus/block.go index ab4449b53..31b6a5e15 100644 --- a/pkg/consensus/block.go +++ b/pkg/consensus/block.go @@ -20,7 +20,7 @@ var _ block.Block = (*neoBlock)(nil) // Sign implements block.Block interface. func (n *neoBlock) Sign(key crypto.PrivateKey) error { - data := n.Base.GetHashableData() + data := n.Base.GetSignedPart() sig, err := key.Sign(data[:]) if err != nil { return err @@ -33,7 +33,7 @@ func (n *neoBlock) Sign(key crypto.PrivateKey) error { // Verify implements block.Block interface. func (n *neoBlock) Verify(key crypto.PublicKey, sign []byte) error { - data := n.Base.GetHashableData() + data := n.Base.GetSignedPart() return key.Verify(data, sign) } diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index ee9af07bd..c9f78a0eb 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -186,7 +186,7 @@ func (p *Payload) EncodeBinary(w *io.BinWriter) { // Sign signs payload using the private key. // It also sets corresponding verification and invocation scripts. func (p *Payload) Sign(key *privateKey) error { - sig, err := key.Sign(p.MarshalUnsigned()) + sig, err := key.Sign(p.GetSignedPart()) if err != nil { return err } @@ -197,6 +197,11 @@ func (p *Payload) Sign(key *privateKey) error { return nil } +// GetSignedPart implements crypto.Verifiable interface. +func (p *Payload) GetSignedPart() []byte { + return p.MarshalUnsigned() +} + // Verify verifies payload using provided Witness. func (p *Payload) Verify(scriptHash util.Uint160) bool { verification, err := core.ScriptFromWitness(scriptHash, &p.Witness) @@ -205,7 +210,7 @@ func (p *Payload) Verify(scriptHash util.Uint160) bool { } v := vm.New() - h := sha256.Sum256(p.MarshalUnsigned()) + h := sha256.Sum256(p.GetSignedPart()) v.SetCheckedHash(h[:]) v.LoadScript(verification) diff --git a/pkg/core/block/block_base.go b/pkg/core/block/block_base.go index e2dc11cbf..bfd5e4ab4 100644 --- a/pkg/core/block/block_base.go +++ b/pkg/core/block/block_base.go @@ -90,8 +90,8 @@ func (b *Base) EncodeBinary(bw *io.BinWriter) { b.Script.EncodeBinary(bw) } -// GetHashableData returns serialized hashable data of the block. -func (b *Base) GetHashableData() []byte { +// GetSignedPart returns serialized hashable data of the block. +func (b *Base) GetSignedPart() []byte { buf := io.NewBufBinWriter() // No error can occure while encoding hashable fields. b.encodeHashableFields(buf.BinWriter) @@ -106,7 +106,7 @@ func (b *Base) GetHashableData() []byte { // Since MerkleRoot already contains the hash value of all transactions, // the modification of transaction will influence the hash value of the block. func (b *Base) createHash() { - bb := b.GetHashableData() + bb := b.GetSignedPart() b.verificationHash = hash.Sha256(bb) b.hash = hash.Sha256(b.verificationHash.BytesBE()) } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index f8dad727a..3ddb1796e 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2057,5 +2057,12 @@ func (bc *Blockchain) secondsPerBlock() int { } func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context { - return interop.NewContext(trigger, bc, d, block, tx, bc.log) + ic := interop.NewContext(trigger, bc, d, block, tx, bc.log) + switch { + case tx != nil: + ic.Container = tx + case block != nil: + ic.Container = block + } + return ic } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index b1a69871f..198145cf2 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -79,7 +79,7 @@ func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, if err != nil { panic(err) } - b := b.GetHashableData() + b := b.GetSignedPart() sig := pKey.Sign(b) if len(sig) != 64 { panic("wrong signature length") diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 6f9cfafc3..d384fb3d1 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm" "go.uber.org/zap" @@ -14,6 +15,7 @@ import ( // Context represents context in which interops are executed. type Context struct { Chain blockchainer.Blockchainer + Container crypto.Verifiable Trigger trigger.Type Block *block.Block Tx *transaction.Transaction diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index ae8bf014c..d03118d21 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -48,10 +49,18 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error { return nil } -func getMessage(_ *interop.Context, item vm.StackItem) []byte { - msg, err := item.TryBytes() - if err != nil { - panic(err) +func getMessage(ic *interop.Context, item vm.StackItem) []byte { + var msg []byte + switch val := item.(type) { + case *vm.InteropItem: + msg = val.Value().(crypto.Verifiable).GetSignedPart() + case vm.NullItem: + msg = ic.Container.GetSignedPart() + default: + var err error + if msg, err = val.TryBytes(); err != nil { + return nil + } } return msg } diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 782c491b0..92603efd8 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -273,6 +273,21 @@ func TestECDSAVerify(t *testing.T) { runCase(t, false, true, sign, priv.PublicKey().Bytes(), msg) }) + t.Run("signed interop item", func(t *testing.T) { + tx := transaction.NewInvocationTX([]byte{0, 1, 2}, 1) + msg := tx.GetSignedPart() + sign := priv.Sign(msg) + runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NewInteropItem(tx)) + }) + + t.Run("signed script container", func(t *testing.T) { + tx := transaction.NewInvocationTX([]byte{0, 1, 2}, 1) + msg := tx.GetSignedPart() + sign := priv.Sign(msg) + ic.Container = tx + runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NullItem{}) + }) + t.Run("missing arguments", func(t *testing.T) { runCase(t, true, false) sign := priv.Sign(msg) diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 116a65b54..396534c8a 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -246,7 +246,7 @@ func txGetHash(ic *interop.Context, v *vm.VM) error { // engineGetScriptContainer returns transaction that contains the script being // run. func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { - v.Estack().PushVal(vm.NewInteropItem(ic.Tx)) + v.Estack().PushVal(vm.NewInteropItem(ic.Container)) return nil } diff --git a/pkg/crypto/verifiable.go b/pkg/crypto/verifiable.go new file mode 100644 index 000000000..6dc643c1d --- /dev/null +++ b/pkg/crypto/verifiable.go @@ -0,0 +1,6 @@ +package crypto + +// Verifiable represents an object which can be verified. +type Verifiable interface { + GetSignedPart() []byte +}