Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: Alex | Interchain Labs <alex@skip.money>
133 lines
3.2 KiB
Go
133 lines
3.2 KiB
Go
package offchain
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
clientcontext "cosmossdk.io/client/v2/context"
|
|
clitx "cosmossdk.io/client/v2/tx"
|
|
"cosmossdk.io/core/address"
|
|
txsigning "cosmossdk.io/x/tx/signing"
|
|
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
|
)
|
|
|
|
// Verify verifies a digest after unmarshalling it.
|
|
func Verify(ctx clientcontext.Context, digest []byte, fileFormat string) error {
|
|
txConfig, err := clitx.NewTxConfig(clitx.ConfigOptions{
|
|
AddressCodec: ctx.AddressCodec,
|
|
Cdc: ctx.Cdc,
|
|
ValidatorAddressCodec: ctx.ValidatorAddressCodec,
|
|
EnabledSignModes: enabledSignModes,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dTx, err := unmarshal(fileFormat, digest, txConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return verify(ctx.AddressCodec, txConfig, dTx)
|
|
}
|
|
|
|
// verify verifies given Tx.
|
|
func verify(addressCodec address.Codec, txConfig clitx.TxConfig, dTx clitx.Tx) error {
|
|
signModeHandler := txConfig.SignModeHandler()
|
|
|
|
signers, err := dTx.GetSigners()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sigs, err := dTx.GetSignatures()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(sigs) != len(signers) {
|
|
return errors.New("mismatch between the number of signatures and signers")
|
|
}
|
|
|
|
for i, sig := range sigs {
|
|
pubKey := sig.PubKey
|
|
if !bytes.Equal(pubKey.Address(), signers[i]) {
|
|
return errors.New("signature does not match its respective signer")
|
|
}
|
|
|
|
addr, err := addressCodec.BytesToString(pubKey.Address())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
anyPk, err := codectypes.NewAnyWithValue(pubKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
txSignerData := txsigning.SignerData{
|
|
ChainID: ExpectedChainID,
|
|
AccountNumber: ExpectedAccountNumber,
|
|
Sequence: ExpectedSequence,
|
|
Address: addr,
|
|
PubKey: &anypb.Any{
|
|
TypeUrl: anyPk.TypeUrl,
|
|
Value: anyPk.Value,
|
|
},
|
|
}
|
|
|
|
txData, err := dTx.GetSigningTxData()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = verifySignature(context.Background(), pubKey, txSignerData, sig.Data, signModeHandler, txData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// unmarshal unmarshals a digest to a Tx using protobuf protojson.
|
|
func unmarshal(format string, bz []byte, config clitx.TxConfig) (clitx.Tx, error) {
|
|
switch format {
|
|
case "json":
|
|
return config.TxJSONDecoder()(bz)
|
|
case "text":
|
|
return config.TxTextDecoder()(bz)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported format: %s", format)
|
|
}
|
|
}
|
|
|
|
// verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes.
|
|
func verifySignature(
|
|
ctx context.Context,
|
|
pubKey cryptotypes.PubKey,
|
|
signerData txsigning.SignerData,
|
|
signatureData clitx.SignatureData,
|
|
handler *txsigning.HandlerMap,
|
|
txData txsigning.TxData,
|
|
) error {
|
|
switch data := signatureData.(type) {
|
|
case *clitx.SingleSignatureData:
|
|
signBytes, err := handler.GetSignBytes(ctx, data.SignMode, signerData, txData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !pubKey.VerifySignature(signBytes, data.Signature) {
|
|
return errors.New("unable to verify single signer signature")
|
|
}
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unexpected SignatureData %T", signatureData)
|
|
}
|
|
}
|