/
evidence.go
136 lines (117 loc) · 4.4 KB
/
evidence.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package keeper
import (
"bytes"
"context"
"encoding/hex"
"fmt"
sdkerrors "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/x/gravity/types"
)
func (k Keeper) CheckBadSignatureEvidence(
ctx context.Context,
msg *types.MsgSubmitBadSignatureEvidence,
chainReferenceID string,
) error {
var subject types.EthereumSigned
err := k.cdc.UnpackAny(msg.Subject, &subject)
if err != nil {
return sdkerrors.Wrap(types.ErrInvalid, fmt.Sprintf("invalid Any encoded evidence %s", err))
}
switch subject := subject.(type) {
case *types.OutgoingTxBatch:
subject.ChainReferenceId = chainReferenceID
return k.checkBadSignatureEvidenceInternal(ctx, subject, msg.Signature)
default:
return sdkerrors.Wrap(types.ErrInvalid, fmt.Sprintf("bad signature must be over a batch. got %s", subject))
}
}
func (k Keeper) checkBadSignatureEvidenceInternal(ctx context.Context, subject types.EthereumSigned, signature string) error {
// Get checkpoint of the supposed bad signature (fake batch submitted to eth)
ci, err := k.evmKeeper.GetChainInfo(ctx, subject.GetChainReferenceID())
if err != nil {
return sdkerrors.Wrap(err, "unable to create batch")
}
turnstoneID := string(ci.SmartContractUniqueID)
checkpoint, err := subject.GetCheckpoint(turnstoneID)
if err != nil {
return err
}
// Try to find the checkpoint in the archives. If it exists, we don't slash because
// this is not a bad signature
if k.GetPastEthSignatureCheckpoint(ctx, checkpoint) {
return sdkerrors.Wrap(types.ErrInvalid, "Checkpoint exists, cannot slash")
}
// Decode Eth signature to bytes
// strip 0x prefix if needed
if signature[:2] == "0x" {
signature = signature[2:]
}
sigBytes, err := hex.DecodeString(signature)
if err != nil {
return sdkerrors.Wrap(types.ErrInvalid, fmt.Sprintf("signature decoding %s", signature))
}
// Get eth address of the offending validator using the checkpoint and the signature
ethAddress, err := types.EthAddressFromSignature(checkpoint, sigBytes)
if err != nil {
return sdkerrors.Wrap(types.ErrInvalid, fmt.Sprintf("signature to eth address failed with checkpoint %s and signature %s", hex.EncodeToString(checkpoint), signature))
}
// Find the offending validator by eth address
val, found, err := k.GetValidatorByEthAddress(ctx, *ethAddress, subject.GetChainReferenceID())
if err != nil {
return err
}
if !found {
return sdkerrors.Wrap(types.ErrInvalid, fmt.Sprintf("Did not find validator for eth address %s from signature %s with checkpoint %s and TurnstoneID %s", ethAddress.GetAddress().Hex(), signature, hex.EncodeToString(checkpoint), turnstoneID))
}
// Slash the offending validator
cons, err := val.GetConsAddr()
if err != nil {
return sdkerrors.Wrap(err, "Could not get consensus key address for validator")
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
params := k.GetParams(ctx)
if !val.IsJailed() {
err := k.StakingKeeper.Jail(ctx, cons)
if err != nil {
return fmt.Errorf("checkBadSignatureEvidenceInternal jail: %w", err)
}
_, err = k.StakingKeeper.Slash(ctx, cons, sdkCtx.BlockHeight(), val.ConsensusPower(sdk.DefaultPowerReduction), params.SlashFractionBadEthSignature)
if err != nil {
return err
}
}
return nil
}
// SetPastEthSignatureCheckpoint puts the checkpoint of a batch into a set
// in order to prove later that it existed at one point.
func (k Keeper) SetPastEthSignatureCheckpoint(ctx context.Context, checkpoint []byte) {
store := k.GetStore(ctx)
store.Set(types.GetPastEthSignatureCheckpointKey(checkpoint), []byte{0x1})
}
// GetPastEthSignatureCheckpoint tells you whether a given checkpoint has ever existed
func (k Keeper) GetPastEthSignatureCheckpoint(ctx context.Context, checkpoint []byte) (found bool) {
store := k.GetStore(ctx)
if bytes.Equal(store.Get(types.GetPastEthSignatureCheckpointKey(checkpoint)), []byte{0x1}) {
return true
} else {
return false
}
}
func (k Keeper) IteratePastEthSignatureCheckpoints(ctx context.Context, cb func(key []byte, value []byte) (stop bool)) error {
prefixStore := prefix.NewStore(k.GetStore(ctx), types.PastEthSignatureCheckpointKey)
iter := prefixStore.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := iter.Key()
val := iter.Value()
if !bytes.Equal(val, []byte{0x1}) {
return fmt.Errorf("Invalid stored past eth signature checkpoint key=%v: value %v", key, val)
}
if cb(key, val) {
break
}
}
return nil
}