Skip to content

Commit 9182d07

Browse files
committed
miner: surface revoke error detail to builders
1 parent dbe5534 commit 9182d07

5 files changed

Lines changed: 49 additions & 41 deletions

File tree

core/types/bid_block_permission.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
type BidBlockPermissionStatus struct {
1010
Allowed bool
11-
Reason string
11+
Reason string // err detail for auto revokes (InsertChain failure), or "manual" for admin revokes
1212
BlockHash common.Hash
1313
BlockNum uint64
1414
RevokedAt time.Time

miner/bid_block.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,28 @@ func (w *worker) handleBidBlockResult(block *types.Block, task *task) {
162162

163163
w.mux.Post(core.NewSealedBlockEvent{Block: block})
164164

165-
// InsertChain: re-execute all transactions and verify stateRoot/receiptHash
165+
// InsertChain re-executes all txs and validates fields the validator could
166+
// not check at admission. Any mismatch is treated as builder dishonesty and
167+
// revokes the builder for the rest of the UTC day. Categories caught here:
168+
// - Root (post-execution state root)
169+
// - ReceiptHash (post-execution receipts trie root)
170+
// - Bloom (post-execution logs bloom)
171+
// - GasUsed (cumulative gas consumed)
172+
// - Tx precheck failures (nonce, balance, signature, intrinsic gas, ...)
173+
// - System tx value / params (e.g. deposit value vs. SystemAddress balance)
174+
// - Blob sidecar checks (KZG proofs, blob hashes)
166175
if _, err := w.chain.InsertChain(types.Blocks{block}); err != nil {
167176
log.Error("[BID BLOCK VERIFY FAILED]",
168177
"number", block.Number(),
169178
"hash", hash,
179+
"parentHash", block.ParentHash(),
180+
"txs", len(block.Transactions()),
181+
"gasUsed", block.GasUsed(),
182+
"stateRoot", block.Root(),
183+
"receiptHash", block.ReceiptHash(),
170184
"builder", task.bidBlockInfo.builder,
171-
"err", err,
172-
"revokeReason", RevokeReasonInsertChainFailed)
173-
w.permMgr.Revoke(task.bidBlockInfo.builder, RevokeReasonInsertChainFailed, hash, block.NumberU64())
185+
"err", err)
186+
w.permMgr.Revoke(task.bidBlockInfo.builder, fmt.Sprintf("InsertChain err: %v", err), hash, block.NumberU64())
174187
bidBlockRevokeGauge.Inc(1)
175188
return
176189
}

miner/bid_block_permission.go

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,16 @@ import (
1313
"github.com/ethereum/go-ethereum/core/types"
1414
)
1515

16-
// BidBlockRevokeReason classifies why a builder lost SendBidBlock permission.
17-
type BidBlockRevokeReason string
18-
19-
const (
20-
// RevokeReasonInsertChainFailed: the sealed BidBlock failed InsertChain.
21-
// These are conditions the validator cannot check before re-execution and
22-
// therefore depends on the builder to get right:
23-
// - Root (post-execution state root)
24-
// - ReceiptHash (post-execution receipts trie root)
25-
// - Bloom (post-execution logs bloom)
26-
// - GasUsed (cumulative gas consumed)
27-
// - Tx precheck failures (nonce, balance, signature, intrinsic gas, ...)
28-
// - System tx value / params (e.g. deposit value vs. SystemAddress balance)
29-
// - Blob sidecar checks (KZG proofs, blob hashes)
30-
// Any mismatch is treated as builder dishonesty and revokes permission.
31-
RevokeReasonInsertChainFailed BidBlockRevokeReason = "insertchain_failed"
32-
RevokeReasonManual BidBlockRevokeReason = "manual"
33-
)
16+
// RevokeReasonManual is the Reason value used when an operator manually revokes
17+
// a builder via SetAllowed. Automatic revokes always come from InsertChain
18+
// failures and carry the underlying error message as Reason directly — see the
19+
// auto-revoke call site in handleBidBlockResult for the conditions that trigger.
20+
const RevokeReasonManual = "manual"
3421

3522
// BidBlockRevokeRecord holds one active revoke event.
3623
type BidBlockRevokeRecord struct {
3724
RevokedAt time.Time
38-
Reason BidBlockRevokeReason
25+
Reason string // err detail for auto revokes (InsertChain failure), or RevokeReasonManual
3926
BlockHash common.Hash
4027
BlockNum uint64
4128
}
@@ -67,9 +54,11 @@ func (m *BidBlockPermissionManager) IsAllowed(builder common.Address) bool {
6754
}
6855

6956
// Revoke marks builder as denied for the remainder of the current UTC day.
57+
// reason is surfaced via GetBidBlockPermission RPC so builders can see specifics
58+
// (the InsertChain error text for auto revokes, or RevokeReasonManual for admin).
7059
func (m *BidBlockPermissionManager) Revoke(
7160
builder common.Address,
72-
reason BidBlockRevokeReason,
61+
reason string,
7362
blockHash common.Hash,
7463
blockNum uint64,
7564
) {
@@ -95,7 +84,7 @@ func (m *BidBlockPermissionManager) GetStatus(builder common.Address) types.BidB
9584
return status
9685
}
9786
status.Allowed = false
98-
status.Reason = string(rec.Reason)
87+
status.Reason = rec.Reason
9988
status.BlockHash = rec.BlockHash
10089
status.BlockNum = rec.BlockNum
10190
status.RevokedAt = rec.RevokedAt

miner/bid_block_permission_test.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import (
1616
"github.com/ethereum/go-ethereum/miner/minerconfig"
1717
)
1818

19+
// testInsertChainReason is a placeholder used by tests where the specific
20+
// InsertChain error text doesn't matter — production passes
21+
// "InsertChain err: <err.Error()>" here.
22+
const testInsertChainReason = "InsertChain err: test"
23+
1924
func getBidBlockPermissionRecord(m *BidBlockPermissionManager, builder common.Address) (BidBlockRevokeRecord, bool) {
2025
m.mu.RLock()
2126
defer m.mu.RUnlock()
@@ -49,7 +54,7 @@ func TestBidBlockPermission_RevokeBlocks(t *testing.T) {
4954
builder := common.HexToAddress("0x1")
5055
hash := common.HexToHash("0xabc")
5156

52-
m.Revoke(builder, RevokeReasonInsertChainFailed, hash, 100)
57+
m.Revoke(builder, testInsertChainReason, hash, 100)
5358
if m.IsAllowed(builder) {
5459
t.Fatal("revoked builder should not be allowed in same UTC day")
5560
}
@@ -58,8 +63,8 @@ func TestBidBlockPermission_RevokeBlocks(t *testing.T) {
5863
if !ok {
5964
t.Fatal("record expected after Revoke")
6065
}
61-
if rec.Reason != RevokeReasonInsertChainFailed {
62-
t.Fatalf("reason: got %s, want %s", rec.Reason, RevokeReasonInsertChainFailed)
66+
if rec.Reason != testInsertChainReason {
67+
t.Fatalf("reason: got %s, want %s", rec.Reason, testInsertChainReason)
6368
}
6469
if rec.BlockHash != hash {
6570
t.Fatalf("blockHash: got %s, want %s", rec.BlockHash.Hex(), hash.Hex())
@@ -74,7 +79,7 @@ func TestBidBlockPermission_BuildersIndependent(t *testing.T) {
7479
a := common.HexToAddress("0xa")
7580
b := common.HexToAddress("0xb")
7681

77-
m.Revoke(a, RevokeReasonInsertChainFailed, common.Hash{}, 1)
82+
m.Revoke(a, testInsertChainReason, common.Hash{}, 1)
7883
if m.IsAllowed(a) {
7984
t.Fatal("a should be revoked")
8085
}
@@ -87,7 +92,7 @@ func TestBidBlockPermission_RevokeOverwritesSameDay(t *testing.T) {
8792
m := NewBidBlockPermissionManager()
8893
builder := common.HexToAddress("0x1")
8994

90-
m.Revoke(builder, RevokeReasonInsertChainFailed, common.HexToHash("0x1"), 1)
95+
m.Revoke(builder, testInsertChainReason, common.HexToHash("0x1"), 1)
9196
m.Revoke(builder, RevokeReasonManual, common.HexToHash("0x2"), 2)
9297

9398
rec, ok := getBidBlockPermissionRecord(m, builder)
@@ -110,7 +115,7 @@ func TestBidBlockPermission_LazyResetCrossDay(t *testing.T) {
110115
day2 := day1.Add(24 * time.Hour)
111116

112117
setBidBlockPermissionClock(m, func() time.Time { return day1 })
113-
m.Revoke(builder, RevokeReasonInsertChainFailed, common.Hash{}, 1)
118+
m.Revoke(builder, testInsertChainReason, common.Hash{}, 1)
114119
if m.IsAllowed(builder) {
115120
t.Fatal("revoked on day1 should be blocked on day1")
116121
}
@@ -130,7 +135,7 @@ func TestBidBlockPermission_SameDayBoundary(t *testing.T) {
130135

131136
near := time.Date(2026, 5, 8, 23, 59, 59, 0, time.UTC)
132137
setBidBlockPermissionClock(m, func() time.Time { return near })
133-
m.Revoke(builder, RevokeReasonInsertChainFailed, common.Hash{}, 1)
138+
m.Revoke(builder, testInsertChainReason, common.Hash{}, 1)
134139

135140
justAfter := time.Date(2026, 5, 9, 0, 0, 1, 0, time.UTC)
136141
setBidBlockPermissionClock(m, func() time.Time { return justAfter })
@@ -193,7 +198,7 @@ func TestBidBlockPermission_ConcurrentAccess(t *testing.T) {
193198
wg.Add(3)
194199
b := builders[i%len(builders)]
195200
go func() { defer wg.Done(); m.IsAllowed(b) }()
196-
go func() { defer wg.Done(); m.Revoke(b, RevokeReasonInsertChainFailed, common.Hash{}, 1) }()
201+
go func() { defer wg.Done(); m.Revoke(b, testInsertChainReason, common.Hash{}, 1) }()
197202
go func() { defer wg.Done(); getBidBlockPermissionRecord(m, b) }()
198203
}
199204
wg.Wait()
@@ -212,7 +217,7 @@ func TestBidBlockPermission_ActiveRevokeCount(t *testing.T) {
212217

213218
a := common.HexToAddress("0xa")
214219
b := common.HexToAddress("0xb")
215-
m.Revoke(a, RevokeReasonInsertChainFailed, common.Hash{}, 1)
220+
m.Revoke(a, testInsertChainReason, common.Hash{}, 1)
216221
m.Revoke(b, RevokeReasonManual, common.Hash{}, 2)
217222

218223
if got := m.ActiveRevokeCount(); got != 2 {
@@ -241,12 +246,12 @@ func TestBidBlockPermission_GetStatus(t *testing.T) {
241246
}
242247

243248
hash := common.HexToHash("0xabc")
244-
m.Revoke(builder, RevokeReasonInsertChainFailed, hash, 100)
249+
m.Revoke(builder, testInsertChainReason, hash, 100)
245250
status = m.GetStatus(builder)
246251
if status.Allowed {
247252
t.Fatal("revoked builder should not be allowed")
248253
}
249-
if status.Reason != string(RevokeReasonInsertChainFailed) {
254+
if status.Reason != testInsertChainReason {
250255
t.Fatalf("reason: got %s", status.Reason)
251256
}
252257
if status.BlockHash != hash || status.BlockNum != 100 || !status.RevokedAt.Equal(now) || !status.ResetAt.Equal(resetAt) {
@@ -266,7 +271,7 @@ func TestBidBlockAdmission_RevokedDoesNotConsumeQuota(t *testing.T) {
266271
const blockNum uint64 = 100
267272

268273
b.builders[builder] = nil
269-
permMgr.Revoke(builder, RevokeReasonInsertChainFailed, common.Hash{}, blockNum-1)
274+
permMgr.Revoke(builder, testInsertChainReason, common.Hash{}, blockNum-1)
270275

271276
if !b.ExistBuilder(builder) {
272277
t.Fatal("registered builder must pass ExistBuilder")
@@ -339,7 +344,7 @@ func TestMinerBidBlockPermission_UsesWorkerManager(t *testing.T) {
339344
miner := &Miner{worker: &worker{permMgr: m}}
340345
builder := common.HexToAddress("0x1")
341346

342-
m.Revoke(builder, RevokeReasonInsertChainFailed, common.Hash{}, 1)
347+
m.Revoke(builder, testInsertChainReason, common.Hash{}, 1)
343348
if miner.GetBidBlockPermission(builder).Allowed {
344349
t.Fatal("miner should report worker revoke")
345350
}
@@ -366,7 +371,7 @@ func TestBidBlockPermission_SetAllowed_Clear(t *testing.T) {
366371
m := NewBidBlockPermissionManager()
367372
builder := common.HexToAddress("0x1")
368373

369-
m.Revoke(builder, RevokeReasonInsertChainFailed, common.HexToHash("0xabc"), 100)
374+
m.Revoke(builder, testInsertChainReason, common.HexToHash("0xabc"), 100)
370375
m.SetAllowed(builder, true)
371376
if !m.IsAllowed(builder) {
372377
t.Fatal("manual SetAllowed(true) should override revoke")

miner/bid_simulator.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,8 @@ func (b *bidSimulator) AddBidBlock(parentHash common.Hash, block *types.DecodedB
654654
defer b.bestBidBlockMu.Unlock()
655655

656656
if existing := b.bestBidBlock[parentHash]; existing != nil && block.GasFee.Cmp(existing.GasFee) < 0 {
657-
return fmt.Errorf("BidBlock gasFee below current best: got %v, best %v", block.GasFee, existing.GasFee)
657+
return fmt.Errorf("BidBlock gasFee below current best: got %s, best %s",
658+
weiToEtherStringF6(block.GasFee), weiToEtherStringF6(existing.GasFee))
658659
}
659660
b.bestBidBlock[parentHash] = block
660661
return nil

0 commit comments

Comments
 (0)