-
Notifications
You must be signed in to change notification settings - Fork 13
/
verify.go
104 lines (93 loc) · 3.21 KB
/
verify.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package types
import (
"errors"
"fmt"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)
// errors are private and used only for testing purposes
var (
errInvalidType = errors.New("invalid type")
errInvalidRoute = errors.New("invalid route")
)
// NewVerifier is SignatureVerifier's constructor
func NewVerifier(signModeHandler authsigning.SignModeHandler) SignatureVerifier {
return SignatureVerifier{signModeHandler: signModeHandler}
}
// SignatureVerifier takes care of verifying transactions given
// an instance of authsigning.SignModeHandler
type SignatureVerifier struct {
signModeHandler authsigning.SignModeHandler
}
// Verify takes an sdk.Tx and verifies it
func (v SignatureVerifier) Verify(tx sdk.Tx) error {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "cannot verify tx of type %T", tx)
}
msgs := sigTx.GetMsgs()
if len(msgs) == 0 {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "no message provided")
}
for i, msg := range msgs {
err := verifyMessage(msg)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "message number %d is invalid: %s", i, err)
}
}
signers, err := sigTx.GetPubKeys()
if err != nil {
return err
}
if len(signers) == 0 {
return sdkerrors.ErrInvalidPubKey
}
signatures, err := sigTx.GetSignaturesV2()
if err != nil {
return fmt.Errorf("cannot verify: %w", err)
}
if len(signatures) != len(signers) {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "signatures and signers mismatch: %d <-> %d", len(signers), len(signatures))
}
for i, signature := range signatures {
err := verifySignature(tx, signature, signers[i], v.signModeHandler)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid signature %d: %s", i, err)
}
}
return nil
}
// verifySignature verifies a single signature
func verifySignature(tx sdk.Tx, sig signing.SignatureV2, signer cryptotypes.PubKey, handler authsigning.SignModeHandler) error {
// TODO: we're imposing chainID accountNumber and sequence, is there a way to verify those params beforehand?
// TODO: so we can return a bad request error instead of an unauthorized sig one
signerData := authsigning.SignerData{
Address: sig.PubKey.String(),
ChainID: ExpectedChainID,
AccountNumber: ExpectedAccountNumber,
Sequence: ExpectedSequence,
}
err := authsigning.VerifySignature(signer, signerData, sig.Data, handler, tx)
if err != nil {
return err
}
return nil
}
// verifyMessage asserts that the message implementation fits offchain specification correctly
func verifyMessage(m sdk.Msg) error {
// ensure the sdk.msg messages are of type offchain.msg
// generally speaking we do not want to try to handle
// any other type of transaction aside from the offchain ones
// as they abide by different rules
msg, valid := m.(msg)
if !valid {
return fmt.Errorf("%w: %T", errInvalidType, m)
}
if msg.Route() != ExpectedRoute {
return fmt.Errorf("%w: %s", errInvalidRoute, msg.Route())
}
return nil
}