Skip to content

Commit e312609

Browse files
authored
Merge pull request ethereum#1601 from bnb-chain/develop
release: add another two patches into v1.2.3
2 parents e22989f + b0ad742 commit e312609

File tree

10 files changed

+356
-21
lines changed

10 files changed

+356
-21
lines changed

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ var (
170170
utils.CheckSnapshotWithMPT,
171171
utils.EnableDoubleSignMonitorFlag,
172172
utils.VotingEnabledFlag,
173+
utils.EnableMaliciousVoteMonitorFlag,
173174
utils.BLSPasswordFileFlag,
174175
utils.BLSWalletDirFlag,
175176
utils.VoteJournalDirFlag,

cmd/utils/flags.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,11 @@ var (
901901
Usage: "Enable voting",
902902
}
903903

904+
EnableMaliciousVoteMonitorFlag = cli.BoolFlag{
905+
Name: "monitor.maliciousvote",
906+
Usage: "Enable malicious vote monitor to check whether any validator violates the voting rules of fast finality",
907+
}
908+
904909
BLSPasswordFileFlag = cli.StringFlag{
905910
Name: "blspassword",
906911
Usage: "File path for the BLS password, which contains the password to unlock BLS wallet for managing votes in fast_finality feature",
@@ -1159,12 +1164,14 @@ func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
11591164
}
11601165
}
11611166

1162-
// setMonitor creates the monitor from the set
1163-
// command line flags, returning empty if the monitor is disabled.
1164-
func setMonitor(ctx *cli.Context, cfg *node.Config) {
1167+
// setMonitors enable monitors from the command line flags.
1168+
func setMonitors(ctx *cli.Context, cfg *node.Config) {
11651169
if ctx.GlobalBool(EnableDoubleSignMonitorFlag.Name) {
11661170
cfg.EnableDoubleSignMonitor = true
11671171
}
1172+
if ctx.GlobalBool(EnableMaliciousVoteMonitorFlag.Name) {
1173+
cfg.EnableMaliciousVoteMonitor = true
1174+
}
11681175
}
11691176

11701177
// MakeDatabaseHandles raises out the number of allowed file handles per process
@@ -1329,7 +1336,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
13291336
setNodeUserIdent(ctx, cfg)
13301337
setDataDir(ctx, cfg)
13311338
setSmartCard(ctx, cfg)
1332-
setMonitor(ctx, cfg)
1339+
setMonitors(ctx, cfg)
13331340
setBLSWalletDir(ctx, cfg)
13341341
setVoteJournalDir(ctx, cfg)
13351342

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package monitor
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/common"
5+
"github.com/ethereum/go-ethereum/core/types"
6+
"github.com/ethereum/go-ethereum/log"
7+
"github.com/ethereum/go-ethereum/metrics"
8+
lru "github.com/hashicorp/golang-lru"
9+
)
10+
11+
// follow define in core/vote
12+
const (
13+
maxSizeOfRecentEntry = 512
14+
maliciousVoteSlashScope = 256
15+
upperLimitOfVoteBlockNumber = 11
16+
)
17+
18+
var (
19+
violateRule1Counter = metrics.NewRegisteredCounter("monitor/maliciousVote/violateRule1", nil)
20+
violateRule2Counter = metrics.NewRegisteredCounter("monitor/maliciousVote/violateRule2", nil)
21+
)
22+
23+
// two purposes
24+
// 1. monitor whether there are bugs in the voting mechanism, so add metrics to observe it.
25+
// 2. do malicious vote slashing. TODO
26+
type MaliciousVoteMonitor struct {
27+
curVotes map[types.BLSPublicKey]*lru.Cache
28+
}
29+
30+
func NewMaliciousVoteMonitor() *MaliciousVoteMonitor {
31+
return &MaliciousVoteMonitor{
32+
curVotes: make(map[types.BLSPublicKey]*lru.Cache, 21), // mainnet config
33+
}
34+
}
35+
36+
func (m *MaliciousVoteMonitor) ConflictDetect(newVote *types.VoteEnvelope, pendingBlockNumber uint64) bool {
37+
// get votes for specified VoteAddress
38+
if _, ok := m.curVotes[newVote.VoteAddress]; !ok {
39+
voteDataBuffer, err := lru.New(maxSizeOfRecentEntry)
40+
if err != nil {
41+
log.Error("MaliciousVoteMonitor new lru failed", "err", err)
42+
return false
43+
}
44+
m.curVotes[newVote.VoteAddress] = voteDataBuffer
45+
}
46+
voteDataBuffer := m.curVotes[newVote.VoteAddress]
47+
sourceNumber, targetNumber := newVote.Data.SourceNumber, newVote.Data.TargetNumber
48+
49+
//Basic check
50+
// refer to https://github.com/bnb-chain/bsc-genesis-contract/blob/master/contracts/SlashIndicator.sol#LL207C4-L207C4
51+
if !(targetNumber+maliciousVoteSlashScope > pendingBlockNumber) {
52+
return false
53+
}
54+
55+
// UnderRules check
56+
blockNumber := sourceNumber + 1
57+
if !(blockNumber+maliciousVoteSlashScope > pendingBlockNumber) {
58+
blockNumber = pendingBlockNumber - maliciousVoteSlashScope + 1
59+
}
60+
for ; blockNumber <= pendingBlockNumber+upperLimitOfVoteBlockNumber; blockNumber++ {
61+
if voteDataBuffer.Contains(blockNumber) {
62+
voteData, ok := voteDataBuffer.Get(blockNumber)
63+
if !ok {
64+
log.Error("Failed to get voteData info from LRU cache.")
65+
continue
66+
}
67+
if blockNumber == targetNumber {
68+
log.Warn("violate rule1", "VoteAddress", common.Bytes2Hex(newVote.VoteAddress[:]), "voteExisted", voteData.(*types.VoteData), "newVote", newVote.Data)
69+
violateRule1Counter.Inc(1)
70+
// prepare message for slashing
71+
return true
72+
} else if (blockNumber < targetNumber && voteData.(*types.VoteData).SourceNumber > sourceNumber) ||
73+
(blockNumber > targetNumber && voteData.(*types.VoteData).SourceNumber < sourceNumber) {
74+
log.Warn("violate rule2", "VoteAddress", common.Bytes2Hex(newVote.VoteAddress[:]), "voteExisted", voteData.(*types.VoteData), "newVote", newVote.Data)
75+
violateRule2Counter.Inc(1)
76+
// prepare message for slashing
77+
return true
78+
}
79+
}
80+
}
81+
82+
// for simplicity, Just override even if the targetNumber has existed.
83+
voteDataBuffer.Add(newVote.Data.TargetNumber, newVote.Data)
84+
return false
85+
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package monitor
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/ethereum/go-ethereum/core/types"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestMaliciousVoteMonitor(t *testing.T) {
12+
//log.Root().SetHandler(log.StdoutHandler)
13+
// case 1, different voteAddress
14+
{
15+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
16+
pendingBlockNumber := uint64(1000)
17+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
18+
voteAddress := types.BLSPublicKey{}
19+
copy(voteAddress[:], voteAddrBytes[:])
20+
vote1 := &types.VoteEnvelope{
21+
VoteAddress: voteAddress,
22+
Signature: types.BLSSignature{},
23+
Data: &types.VoteData{
24+
SourceNumber: uint64(0),
25+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
26+
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
27+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
28+
},
29+
}
30+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
31+
voteAddress[0] = 4
32+
vote2 := &types.VoteEnvelope{
33+
VoteAddress: voteAddress,
34+
Signature: types.BLSSignature{},
35+
Data: &types.VoteData{
36+
SourceNumber: uint64(0),
37+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
38+
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
39+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
40+
},
41+
}
42+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
43+
}
44+
45+
// case 2, target number not in maliciousVoteSlashScope
46+
{
47+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
48+
pendingBlockNumber := uint64(1000)
49+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
50+
voteAddress := types.BLSPublicKey{}
51+
copy(voteAddress[:], voteAddrBytes[:])
52+
vote1 := &types.VoteEnvelope{
53+
VoteAddress: voteAddress,
54+
Signature: types.BLSSignature{},
55+
Data: &types.VoteData{
56+
SourceNumber: uint64(0),
57+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
58+
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
59+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
60+
},
61+
}
62+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
63+
vote2 := &types.VoteEnvelope{
64+
VoteAddress: voteAddress,
65+
Signature: types.BLSSignature{},
66+
Data: &types.VoteData{
67+
SourceNumber: uint64(0),
68+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
69+
TargetNumber: pendingBlockNumber - maliciousVoteSlashScope - 1,
70+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
71+
},
72+
}
73+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
74+
}
75+
76+
// case 3, violate rule1
77+
{
78+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
79+
pendingBlockNumber := uint64(1000)
80+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
81+
voteAddress := types.BLSPublicKey{}
82+
copy(voteAddress[:], voteAddrBytes[:])
83+
vote1 := &types.VoteEnvelope{
84+
VoteAddress: voteAddress,
85+
Signature: types.BLSSignature{},
86+
Data: &types.VoteData{
87+
SourceNumber: uint64(0),
88+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
89+
TargetNumber: pendingBlockNumber - 1,
90+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
91+
},
92+
}
93+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
94+
vote2 := &types.VoteEnvelope{
95+
VoteAddress: voteAddress,
96+
Signature: types.BLSSignature{},
97+
Data: &types.VoteData{
98+
SourceNumber: uint64(0),
99+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
100+
TargetNumber: pendingBlockNumber - 1,
101+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
102+
},
103+
}
104+
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
105+
}
106+
107+
// case 4, violate rule2, vote with smaller range first
108+
{
109+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
110+
pendingBlockNumber := uint64(1000)
111+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
112+
voteAddress := types.BLSPublicKey{}
113+
copy(voteAddress[:], voteAddrBytes[:])
114+
vote1 := &types.VoteEnvelope{
115+
VoteAddress: voteAddress,
116+
Signature: types.BLSSignature{},
117+
Data: &types.VoteData{
118+
SourceNumber: pendingBlockNumber - 4,
119+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
120+
TargetNumber: pendingBlockNumber - 1,
121+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
122+
},
123+
}
124+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
125+
vote2 := &types.VoteEnvelope{
126+
VoteAddress: voteAddress,
127+
Signature: types.BLSSignature{},
128+
Data: &types.VoteData{
129+
SourceNumber: pendingBlockNumber - 2,
130+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
131+
TargetNumber: pendingBlockNumber - 3,
132+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
133+
},
134+
}
135+
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
136+
}
137+
138+
// case 5, violate rule2, vote with larger range first
139+
{
140+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
141+
pendingBlockNumber := uint64(1000)
142+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
143+
voteAddress := types.BLSPublicKey{}
144+
copy(voteAddress[:], voteAddrBytes[:])
145+
vote1 := &types.VoteEnvelope{
146+
VoteAddress: voteAddress,
147+
Signature: types.BLSSignature{},
148+
Data: &types.VoteData{
149+
SourceNumber: pendingBlockNumber - 2,
150+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
151+
TargetNumber: pendingBlockNumber - 3,
152+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
153+
},
154+
}
155+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
156+
vote2 := &types.VoteEnvelope{
157+
VoteAddress: voteAddress,
158+
Signature: types.BLSSignature{},
159+
Data: &types.VoteData{
160+
SourceNumber: pendingBlockNumber - 4,
161+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
162+
TargetNumber: pendingBlockNumber - 1,
163+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
164+
},
165+
}
166+
assert.Equal(t, true, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
167+
}
168+
169+
// case 6, normal case
170+
{
171+
maliciousVoteMonitor := NewMaliciousVoteMonitor()
172+
pendingBlockNumber := uint64(1000)
173+
voteAddrBytes := common.Hex2BytesFixed("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", types.BLSPublicKeyLength)
174+
voteAddress := types.BLSPublicKey{}
175+
copy(voteAddress[:], voteAddrBytes[:])
176+
vote1 := &types.VoteEnvelope{
177+
VoteAddress: voteAddress,
178+
Signature: types.BLSSignature{},
179+
Data: &types.VoteData{
180+
SourceNumber: pendingBlockNumber - 4,
181+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
182+
TargetNumber: pendingBlockNumber - 3,
183+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
184+
},
185+
}
186+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote1, pendingBlockNumber))
187+
vote2 := &types.VoteEnvelope{
188+
VoteAddress: voteAddress,
189+
Signature: types.BLSSignature{},
190+
Data: &types.VoteData{
191+
SourceNumber: pendingBlockNumber - 3,
192+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
193+
TargetNumber: pendingBlockNumber - 2,
194+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
195+
},
196+
}
197+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote2, pendingBlockNumber))
198+
vote3 := &types.VoteEnvelope{
199+
VoteAddress: voteAddress,
200+
Signature: types.BLSSignature{},
201+
Data: &types.VoteData{
202+
SourceNumber: pendingBlockNumber - 2,
203+
SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
204+
TargetNumber: pendingBlockNumber - 1,
205+
TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
206+
},
207+
}
208+
assert.Equal(t, false, maliciousVoteMonitor.ConflictDetect(vote3, pendingBlockNumber))
209+
}
210+
}

core/systemcontracts/upgrade.go

Lines changed: 9 additions & 9 deletions
Large diffs are not rendered by default.

core/vote/vote_manager.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func (voteManager *VoteManager) UnderRules(header *types.Header) (bool, uint64,
194194
continue
195195
}
196196
if voteData.(*types.VoteData).SourceNumber > sourceNumber {
197-
log.Debug(fmt.Sprintf("error: cur vote %d-->%d is within the span of other votes %d-->%d",
197+
log.Debug(fmt.Sprintf("error: cur vote %d-->%d is across the span of other votes %d-->%d",
198198
sourceNumber, targetNumber, voteData.(*types.VoteData).SourceNumber, voteData.(*types.VoteData).TargetNumber))
199199
return false, 0, common.Hash{}
200200
}
@@ -208,14 +208,15 @@ func (voteManager *VoteManager) UnderRules(header *types.Header) (bool, uint64,
208208
continue
209209
}
210210
if voteData.(*types.VoteData).SourceNumber < sourceNumber {
211-
log.Debug("error: other votes are within span of cur vote")
211+
log.Debug(fmt.Sprintf("error: cur vote %d-->%d is within the span of other votes %d-->%d",
212+
sourceNumber, targetNumber, voteData.(*types.VoteData).SourceNumber, voteData.(*types.VoteData).TargetNumber))
212213
return false, 0, common.Hash{}
213214
}
214215
}
215216
}
216217

217218
// Rule 3: Validators always vote for their canonical chain’s latest block.
218-
// Since the header subscribed to is the canonical chain, so this rule is satisified by default.
219+
// Since the header subscribed to is the canonical chain, so this rule is satisfied by default.
219220
log.Debug("All three rules check passed")
220221
return true, sourceNumber, sourceHash
221222
}

eth/backend.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/ethereum/go-ethereum/consensus/parlia"
3636
"github.com/ethereum/go-ethereum/core"
3737
"github.com/ethereum/go-ethereum/core/bloombits"
38+
"github.com/ethereum/go-ethereum/core/monitor"
3839
"github.com/ethereum/go-ethereum/core/rawdb"
3940
"github.com/ethereum/go-ethereum/core/state/pruner"
4041
"github.com/ethereum/go-ethereum/core/types"
@@ -300,6 +301,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
300301
}
301302
if eth.votePool != nil {
302303
eth.handler.votepool = eth.votePool
304+
if stack.Config().EnableMaliciousVoteMonitor {
305+
eth.handler.maliciousVoteMonitor = monitor.NewMaliciousVoteMonitor()
306+
log.Info("Create MaliciousVoteMonitor successfully")
307+
}
303308
}
304309

305310
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)

0 commit comments

Comments
 (0)