-
Notifications
You must be signed in to change notification settings - Fork 2
/
double_sign.go
151 lines (139 loc) · 5.31 KB
/
double_sign.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package consensus
import (
"bytes"
"sort"
bls_core "github.com/PositionExchange/bls/ffi/go/bls"
"github.com/PositionExchange/posichain/consensus/quorum"
"github.com/PositionExchange/posichain/crypto/bls"
"github.com/PositionExchange/posichain/staking/slash"
"github.com/ethereum/go-ethereum/common"
)
// Check for double sign and if any, send it out to beacon chain for slashing.
// Returns true when it is a double-sign or there is error, otherwise, false.
func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool {
if consensus.couldThisBeADoubleSigner(recvMsg) {
addrSet := map[common.Address]struct{}{}
for _, pubKey2 := range recvMsg.SenderPubkeys {
if alreadyCastBallot := consensus.Decider.ReadBallot(
quorum.Commit, pubKey2.Bytes,
); alreadyCastBallot != nil {
for _, pubKey1 := range alreadyCastBallot.SignerPubKeys {
if bytes.Compare(pubKey2.Bytes[:], pubKey1[:]) == 0 {
for _, blk := range consensus.FBFTLog.GetBlocksByNumber(recvMsg.BlockNum) {
firstSignedHeader := blk.Header()
areHeightsEqual := firstSignedHeader.Number().Uint64() == recvMsg.BlockNum
areViewIDsEqual := firstSignedHeader.ViewID().Uint64() == recvMsg.ViewID
areHeadersEqual := firstSignedHeader.Hash() == recvMsg.BlockHash
// If signer already firstSignedHeader, and the block height is the same
// and the viewID is the same, then we need to verify the block
// hash, and if block hash is different, then that is a clear
// case of double signing
if areHeightsEqual && areViewIDsEqual && !areHeadersEqual {
var doubleSign bls_core.Sign
if err := doubleSign.Deserialize(recvMsg.Payload); err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not deserialize potential double signer")
return true
}
curHeader := consensus.Blockchain.CurrentHeader()
committee, err := consensus.Blockchain.ReadShardState(curHeader.Epoch())
if err != nil {
consensus.getLogger().Err(err).
Uint32("shard", consensus.ShardID).
Uint64("epoch", curHeader.Epoch().Uint64()).
Msg("could not read shard state")
return true
}
subComm, err := committee.FindCommitteeByID(
consensus.ShardID,
)
if err != nil {
consensus.getLogger().Err(err).
Str("msg", recvMsg.String()).
Msg("could not find subcommittee for bls key")
return true
}
addr, err := subComm.AddressForBLSKey(pubKey2.Bytes)
if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for bls key")
return true
}
if _, ok := addrSet[*addr]; ok {
// Address already slashed
break
}
leaderAddr, err := subComm.AddressForBLSKey(consensus.LeaderPubKey.Bytes)
if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for leader bls key")
return true
}
go func(reporter common.Address) {
secondKeys := make([]bls.SerializedPublicKey, len(recvMsg.SenderPubkeys))
for i, pubKey := range recvMsg.SenderPubkeys {
secondKeys[i] = pubKey.Bytes
}
evid := slash.Evidence{
ConflictingVotes: slash.ConflictingVotes{
FirstVote: slash.Vote{
SignerPubKeys: alreadyCastBallot.SignerPubKeys,
BlockHeaderHash: alreadyCastBallot.BlockHeaderHash,
Signature: alreadyCastBallot.Signature,
},
SecondVote: slash.Vote{
SignerPubKeys: secondKeys,
BlockHeaderHash: recvMsg.BlockHash,
Signature: common.Hex2Bytes(doubleSign.SerializeToHexStr()),
}},
Moment: slash.Moment{
Epoch: curHeader.Epoch(),
ShardID: consensus.ShardID,
Height: recvMsg.BlockNum,
ViewID: recvMsg.ViewID,
},
Offender: *addr,
}
sort.SliceStable(evid.ConflictingVotes.FirstVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.FirstVote.SignerPubKeys[i][:],
evid.ConflictingVotes.FirstVote.SignerPubKeys[j][:]) < 0
})
sort.SliceStable(evid.ConflictingVotes.SecondVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.SecondVote.SignerPubKeys[i][:],
evid.ConflictingVotes.SecondVote.SignerPubKeys[j][:]) < 0
})
proof := slash.Record{
Evidence: evid,
Reporter: reporter,
}
consensus.SlashChan <- proof
}(*leaderAddr)
addrSet[*addr] = struct{}{}
break
}
}
}
}
}
}
return true
}
return false
}
func (consensus *Consensus) couldThisBeADoubleSigner(
recvMsg *FBFTMessage,
) bool {
num, hash := consensus.BlockNum(), recvMsg.BlockHash
suspicious := !consensus.FBFTLog.HasMatchingAnnounce(num, hash) ||
!consensus.FBFTLog.HasMatchingPrepared(num, hash)
if suspicious {
consensus.getLogger().Debug().
Str("message", recvMsg.String()).
Uint64("block-on-consensus", num).
Msg("possible double signer")
return true
}
return false
}