crypto: remove crypto.Verifiable interface

We can now verify any hash.Hashable thing.
This commit is contained in:
Roman Khimov 2021-03-25 15:22:16 +03:00
parent 04e0ea2c0f
commit df12adaa9e
14 changed files with 61 additions and 28 deletions

View file

@ -6,6 +6,7 @@ import (
"sync/atomic" "sync/atomic"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer/services" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer/services"
@ -14,7 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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/core/transaction"
"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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -49,7 +50,7 @@ func NewFakeChain() *FakeChain {
blocks: make(map[util.Uint256]*block.Block), blocks: make(map[util.Uint256]*block.Block),
hdrHashes: make(map[uint32]util.Uint256), hdrHashes: make(map[uint32]util.Uint256),
txs: make(map[util.Uint256]*transaction.Transaction), txs: make(map[util.Uint256]*transaction.Transaction),
ProtocolConfiguration: config.ProtocolConfiguration{P2PNotaryRequestPayloadPoolSize: 10}, ProtocolConfiguration: config.ProtocolConfiguration{Magic: netmode.UnitTestNet, P2PNotaryRequestPayloadPoolSize: 10},
} }
} }
@ -393,7 +394,7 @@ func (chain *FakeChain) VerifyTx(*transaction.Transaction) error {
} }
// VerifyWitness implements Blockchainer interface. // VerifyWitness implements Blockchainer interface.
func (chain *FakeChain) VerifyWitness(util.Uint160, crypto.Verifiable, *transaction.Witness, int64) error { func (chain *FakeChain) VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) error {
if chain.VerifyWitnessF != nil { if chain.VerifyWitnessF != nil {
return chain.VerifyWitnessF() return chain.VerifyWitnessF()
} }

View file

@ -7,6 +7,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/fakechain"
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
@ -186,7 +187,8 @@ func TestAppCall(t *testing.T) {
return nil, errors.New("not found") return nil, errors.New("not found")
} }
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), contractGetter, nil, nil, nil, zaptest.NewLogger(t)) fc := fakechain.NewFakeChain()
ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), contractGetter, nil, nil, nil, zaptest.NewLogger(t))
t.Run("valid script", func(t *testing.T) { t.Run("valid script", func(t *testing.T) {
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))

View file

@ -79,7 +79,7 @@ func TestConsensusPayload_Setters(t *testing.T) {
func TestConsensusPayload_Serializable(t *testing.T) { func TestConsensusPayload_Serializable(t *testing.T) {
for _, mt := range messageTypes { for _, mt := range messageTypes {
p := randomPayload(t, mt) p := randomPayload(t, mt)
actual := new(Payload) actual := &Payload{Extensible: npayload.Extensible{Network: netmode.UnitTestNet}}
data, err := testserdes.EncodeBinary(p) data, err := testserdes.EncodeBinary(p)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, testserdes.DecodeBinary(data, &actual.Extensible)) require.NoError(t, testserdes.DecodeBinary(data, &actual.Extensible))
@ -158,6 +158,7 @@ func randomPayload(t *testing.T, mt messageType) *Payload {
payload: randomMessage(t, mt), payload: randomMessage(t, mt),
}, },
Extensible: npayload.Extensible{ Extensible: npayload.Extensible{
Network: netmode.UnitTestNet,
Witness: transaction.Witness{ Witness: transaction.Witness{
InvocationScript: random.Bytes(3), InvocationScript: random.Bytes(3),
VerificationScript: []byte{byte(opcode.PUSH0)}, VerificationScript: []byte{byte(opcode.PUSH0)},

View file

@ -25,7 +25,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/stateroot" "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"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/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
@ -1742,7 +1741,7 @@ func (bc *Blockchain) InitVerificationVM(v *vm.VM, getContract func(util.Uint160
} }
// VerifyWitness checks that w is a correct witness for c signed by h. // VerifyWitness checks that w is a correct witness for c signed by h.
func (bc *Blockchain) VerifyWitness(h util.Uint160, c crypto.Verifiable, w *transaction.Witness, gas int64) error { func (bc *Blockchain) VerifyWitness(h util.Uint160, c hash.Hashable, w *transaction.Witness, gas int64) error {
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil) ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
ic.Container = c ic.Container = c
_, err := bc.verifyHashAgainstScript(h, w, ic, gas) _, err := bc.verifyHashAgainstScript(h, w, ic, gas)

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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/core/transaction"
"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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -71,7 +71,7 @@ type Blockchainer interface {
SubscribeForNotifications(ch chan<- *state.NotificationEvent) SubscribeForNotifications(ch chan<- *state.NotificationEvent)
SubscribeForTransactions(ch chan<- *transaction.Transaction) SubscribeForTransactions(ch chan<- *transaction.Transaction)
VerifyTx(*transaction.Transaction) error VerifyTx(*transaction.Transaction) error
VerifyWitness(util.Uint160, crypto.Verifiable, *transaction.Witness, int64) error VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) error
GetMemPool() *mempool.Pool GetMemPool() *mempool.Pool
UnsubscribeFromBlocks(ch chan<- *block.Block) UnsubscribeFromBlocks(ch chan<- *block.Block)
UnsubscribeFromExecutions(ch chan<- *state.AppExecResult) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
@ -34,7 +34,8 @@ const (
// Context represents context in which interops are executed. // Context represents context in which interops are executed.
type Context struct { type Context struct {
Chain blockchainer.Blockchainer Chain blockchainer.Blockchainer
Container crypto.Verifiable Container hash.Hashable
Network uint32
Natives []Contract Natives []Contract
Trigger trigger.Type Trigger trigger.Type
Block *block.Block Block *block.Block
@ -55,6 +56,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO,
nes := make([]state.NotificationEvent, 0) nes := make([]state.NotificationEvent, 0)
return &Context{ return &Context{
Chain: bc, Chain: bc,
Network: uint32(bc.GetConfig().Magic),
Natives: natives, Natives: natives,
Trigger: trigger, Trigger: trigger,
Block: block, Block: block,

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
) )
@ -14,7 +15,6 @@ import (
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using // ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
// Secp256r1 elliptic curve. // Secp256r1 elliptic curve.
func ECDSASecp256r1CheckMultisig(ic *interop.Context) error { func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
hashToCheck := ic.Container.GetSignedHash()
pkeys, err := ic.VM.Estack().PopSigElements() pkeys, err := ic.VM.Estack().PopSigElements()
if err != nil { if err != nil {
return fmt.Errorf("wrong parameters: %w", err) return fmt.Errorf("wrong parameters: %w", err)
@ -31,21 +31,20 @@ func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
if len(pkeys) < len(sigs) { if len(pkeys) < len(sigs) {
return errors.New("more signatures than there are keys") return errors.New("more signatures than there are keys")
} }
sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hashToCheck.BytesBE(), pkeys, sigs) sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hash.NetSha256(ic.Network, ic.Container).BytesBE(), pkeys, sigs)
ic.VM.Estack().PushVal(sigok) ic.VM.Estack().PushVal(sigok)
return nil return nil
} }
// ECDSASecp256r1CheckSig checks ECDSA signature using Secp256r1 elliptic curve. // ECDSASecp256r1CheckSig checks ECDSA signature using Secp256r1 elliptic curve.
func ECDSASecp256r1CheckSig(ic *interop.Context) error { func ECDSASecp256r1CheckSig(ic *interop.Context) error {
hashToCheck := ic.Container.GetSignedHash()
keyb := ic.VM.Estack().Pop().Bytes() keyb := ic.VM.Estack().Pop().Bytes()
signature := ic.VM.Estack().Pop().Bytes() signature := ic.VM.Estack().Pop().Bytes()
pkey, err := keys.NewPublicKeyFromBytes(keyb, elliptic.P256()) pkey, err := keys.NewPublicKeyFromBytes(keyb, elliptic.P256())
if err != nil { if err != nil {
return err return err
} }
res := pkey.Verify(signature, hashToCheck.BytesBE()) res := pkey.VerifyHashable(signature, ic.Network, ic.Container)
ic.VM.Estack().PushVal(res) ic.VM.Estack().PushVal(res)
return nil return nil
} }

View file

@ -67,6 +67,7 @@ func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM {
binary.LittleEndian.PutUint32(buf[1:], neoCryptoCheckMultisigID) binary.LittleEndian.PutUint32(buf[1:], neoCryptoCheckMultisigID)
ic := &interop.Context{ ic := &interop.Context{
Network: uint32(netmode.UnitTestNet),
Trigger: trigger.Verification, Trigger: trigger.Verification,
Container: container, Container: container,
} }
@ -172,7 +173,7 @@ func TestCheckSig(t *testing.T) {
verifyFunc := ECDSASecp256r1CheckSig verifyFunc := ECDSASecp256r1CheckSig
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false) d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
ic := &interop.Context{DAO: dao.NewCached(d)} ic := &interop.Context{Network: uint32(netmode.UnitTestNet), DAO: dao.NewCached(d)}
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
ic.SpawnVM() ic.SpawnVM()
for i := range args { for i := range args {

View file

@ -2,11 +2,34 @@ package hash
import ( import (
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
// Hashable represents an object which can be hashed. Usually these objects
// are io.Serializable and signable. They tend to cache the hash inside for
// effectiveness, providing this accessor method. Anything that can be
// identified with a hash can then be signed and verified.
type Hashable interface {
Hash() util.Uint256
}
func getSignedData(net uint32, hh Hashable) []byte {
var b = make([]byte, 4+32)
binary.LittleEndian.PutUint32(b, net)
h := hh.Hash()
copy(b[4:], h[:])
return b
}
// NetSha256 calculates network-specific hash of Hashable item that can then
// be signed/verified.
func NetSha256(net uint32, hh Hashable) util.Uint256 {
return Sha256(getSignedData(net, hh))
}
// Sha256 hashes the incoming byte slice // Sha256 hashes the incoming byte slice
// using the sha256 algorithm. // using the sha256 algorithm.
func Sha256(data []byte) util.Uint256 { func Sha256(data []byte) util.Uint256 {

View file

@ -11,6 +11,7 @@ import (
"math/big" "math/big"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/rfc6979" "github.com/nspcc-dev/rfc6979"
) )
@ -154,6 +155,12 @@ func (p *PrivateKey) SignHash(digest util.Uint256) []byte {
return getSignatureSlice(p.PrivateKey.Curve, r, s) return getSignatureSlice(p.PrivateKey.Curve, r, s)
} }
// SignHashable signs some Hashable item for the network specified using
// hash.NetSha256() with the private key.
func (p *PrivateKey) SignHashable(net uint32, hh hash.Hashable) []byte {
return p.SignHash(hash.NetSha256(net, hh))
}
func getSignatureSlice(curve elliptic.Curve, r, s *big.Int) []byte { func getSignatureSlice(curve elliptic.Curve, r, s *big.Int) []byte {
params := curve.Params() params := curve.Params()
curveOrderByteSize := params.P.BitLen() / 8 curveOrderByteSize := params.P.BitLen() / 8

View file

@ -343,6 +343,13 @@ func (p *PublicKey) Verify(signature []byte, hash []byte) bool {
return ecdsa.Verify(&pk, hash, rBytes, sBytes) return ecdsa.Verify(&pk, hash, rBytes, sBytes)
} }
// VerifyHashable returns true if the signature is valid and corresponds
// to the hash and public key.
func (p *PublicKey) VerifyHashable(signature []byte, net uint32, hh hash.Hashable) bool {
var digest = hash.NetSha256(net, hh)
return p.Verify(signature, digest[:])
}
// IsInfinity checks if the key is infinite (null, basically). // IsInfinity checks if the key is infinite (null, basically).
func (p *PublicKey) IsInfinity() bool { func (p *PublicKey) IsInfinity() bool {
return p.X == nil && p.Y == nil return p.X == nil && p.Y == nil

View file

@ -1,17 +1,8 @@
package crypto package crypto
import "github.com/nspcc-dev/neo-go/pkg/util"
// Verifiable represents an object which can be verified.
type Verifiable interface {
GetSignedPart() []byte
GetSignedHash() util.Uint256
}
// VerifiableDecodable represents an object which can be verified and // VerifiableDecodable represents an object which can be verified and
// those hashable part can be encoded/decoded. // those hashable part can be encoded/decoded.
type VerifiableDecodable interface { type VerifiableDecodable interface {
Verifiable
EncodeHashableFields() ([]byte, error) EncodeHashableFields() ([]byte, error)
DecodeHashableFields([]byte) error DecodeHashableFields([]byte) error
} }

View file

@ -6,7 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"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/hash"
"github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -93,7 +93,7 @@ func newTestChain() *testChain {
}, },
} }
} }
func (c *testChain) VerifyWitness(u util.Uint160, _ crypto.Verifiable, _ *transaction.Witness, _ int64) error { func (c *testChain) VerifyWitness(u util.Uint160, _ hash.Hashable, _ *transaction.Witness, _ int64) error {
if !c.verifyWitness(u) { if !c.verifyWitness(u) {
return errVerification return errVerification
} }

View file

@ -119,7 +119,7 @@ func TestParameterContext_AddSignatureMultisig(t *testing.T) {
} }
func newTestVM(w *transaction.Witness, tx *transaction.Transaction) *vm.VM { func newTestVM(w *transaction.Witness, tx *transaction.Transaction) *vm.VM {
ic := &interop.Context{Container: tx} ic := &interop.Context{Network: uint32(netmode.UnitTestNet), Container: tx}
crypto.Register(ic) crypto.Register(ic)
v := ic.SpawnVM() v := ic.SpawnVM()
v.LoadScript(w.VerificationScript) v.LoadScript(w.VerificationScript)