From 2879f89337baa883a47a369fd2ee60b9ee469181 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 13 Apr 2020 13:33:48 +0300 Subject: [PATCH 1/5] crypto: declare Verifiable and ScriptContainer interfaces --- pkg/crypto/verifiable.go | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 pkg/crypto/verifiable.go 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 +} From 8f08065a8e6756f39066e7fb4492c646ae778f2b Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 13 Apr 2020 13:43:36 +0300 Subject: [PATCH 2/5] interop/crypto: allow ECDsaVerify to accept interop items When invokes with interop item on stack, it should check for the signature of Verifiable item it contains. --- pkg/core/interop/crypto/ecdsa.go | 13 ++++++++++--- pkg/core/interop_neo_test.go | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index ae8bf014c..74d4f3988 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" @@ -49,9 +50,15 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error { } func getMessage(_ *interop.Context, item vm.StackItem) []byte { - msg, err := item.TryBytes() - if err != nil { - panic(err) + var msg []byte + switch val := item.(type) { + case *vm.InteropItem: + msg = val.Value().(crypto.Verifiable).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..e13a3faaf 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -273,6 +273,13 @@ 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("missing arguments", func(t *testing.T) { runCase(t, true, false) sign := priv.Sign(msg) From a92872931c454977774f1162a822a6bf45673a2c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 13 Apr 2020 16:18:28 +0300 Subject: [PATCH 3/5] interop/crypto: allow ECDsaVerify to verify ScriptContainer When verifying transaction or block, verification script can be a simple PUSHNULL + SYSCALL, which means that script-encontaining entity should be verified. --- pkg/core/interop/context.go | 2 ++ pkg/core/interop/crypto/ecdsa.go | 4 +++- pkg/core/interop_neo_test.go | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) 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 74d4f3988..d03118d21 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -49,11 +49,13 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error { return nil } -func getMessage(_ *interop.Context, item vm.StackItem) []byte { +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 { diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index e13a3faaf..92603efd8 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -280,6 +280,14 @@ func TestECDSAVerify(t *testing.T) { 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) From 82b230f19f623753d2638c409a480b03e5c3c36a Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 13 Apr 2020 16:31:04 +0300 Subject: [PATCH 4/5] core: rename *block.Base.GetHashableData to GetSignedPart() This allow to use `Block` as a Verifiable item. When tx is provided, it is set as an interop's script container. Otherwise, block is set. --- pkg/consensus/block.go | 4 ++-- pkg/core/block/block_base.go | 6 +++--- pkg/core/blockchain.go | 9 ++++++++- pkg/core/helper_test.go | 2 +- pkg/core/interop_system.go | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) 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/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_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 } From a224917229714f091e8c85a7db1e15116b292869 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 14 Apr 2020 17:45:02 +0300 Subject: [PATCH 5/5] consensus: implement Verifiable interface for Payload --- pkg/consensus/payload.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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)