mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-07 07:28:40 +00:00
internal/ethapi: align capabilities response with spec
This commit is contained in:
parent
f8fb64a285
commit
e4ac40b5b5
2 changed files with 149 additions and 119 deletions
|
|
@ -76,41 +76,36 @@ type Capabilities struct {
|
||||||
|
|
||||||
// CapabilityHead is the current canonical head as reported by the node.
|
// CapabilityHead is the current canonical head as reported by the node.
|
||||||
type CapabilityHead struct {
|
type CapabilityHead struct {
|
||||||
BlockNumber hexutil.Uint64 `json:"blockNumber"`
|
Number hexutil.Uint64 `json:"number"`
|
||||||
BlockHash common.Hash `json:"blockHash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CapabilityResource describes the availability of a single data resource.
|
// CapabilityResource describes the availability of a single data resource.
|
||||||
type CapabilityResource struct {
|
type CapabilityResource struct {
|
||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
OldestBlock hexutil.Uint64 `json:"oldestBlock"`
|
OldestBlock *hexutil.Uint64 `json:"oldestBlock,omitempty"`
|
||||||
DeleteStrategy DeleteStrategy `json:"deleteStrategy"`
|
DeleteStrategy *DeleteStrategy `json:"deleteStrategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteStrategy describes how data of a resource is removed over time.
|
// DeleteStrategy describes how data of a resource is removed over time.
|
||||||
//
|
//
|
||||||
// Two strategies are defined by the spec:
|
// The spec currently defines one strategy: "window", meaning data is retained
|
||||||
//
|
// for a sliding window of the most recent RetentionBlocks blocks. Resources
|
||||||
// - "none": data is never deleted; the resource is permanently
|
// without sliding deletion omit deleteStrategy.
|
||||||
// retained from oldestBlock onwards.
|
|
||||||
// - "window": data is retained for a sliding window of the most recent
|
|
||||||
// RetentionBlocks blocks.
|
|
||||||
//
|
|
||||||
// RetentionBlocks is omitted from the JSON output for the "none" strategy.
|
|
||||||
type DeleteStrategy struct {
|
type DeleteStrategy struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
RetentionBlocks *uint64 `json:"retentionBlocks,omitempty"`
|
RetentionBlocks *uint64 `json:"retentionBlocks,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// strategyNone returns a DeleteStrategy with type "none".
|
|
||||||
func strategyNone() DeleteStrategy {
|
|
||||||
return DeleteStrategy{Type: "none"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strategyWindow returns a DeleteStrategy with type "window" and the given
|
// strategyWindow returns a DeleteStrategy with type "window" and the given
|
||||||
// retention block count.
|
// retention block count.
|
||||||
func strategyWindow(retention uint64) DeleteStrategy {
|
func strategyWindow(retention uint64) *DeleteStrategy {
|
||||||
return DeleteStrategy{Type: "window", RetentionBlocks: &retention}
|
return &DeleteStrategy{Type: "window", RetentionBlocks: &retention}
|
||||||
|
}
|
||||||
|
|
||||||
|
func capabilityOldestBlock(number uint64) *hexutil.Uint64 {
|
||||||
|
oldest := hexutil.Uint64(number)
|
||||||
|
return &oldest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capabilities implements the eth_capabilities RPC method as defined in
|
// Capabilities implements the eth_capabilities RPC method as defined in
|
||||||
|
|
@ -149,28 +144,28 @@ func buildCapabilities(headNum uint64, headHash common.Hash, cutoff uint64, ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// resource builds a CapabilityResource for a window-style resource.
|
// resource builds a CapabilityResource for a window-style resource.
|
||||||
// A window of zero is reported as deleteStrategy "none".
|
// Disabled resources intentionally omit oldestBlock and deleteStrategy,
|
||||||
|
// because those fields would otherwise look like usable history ranges.
|
||||||
resource := func(disabled bool, window uint64, floor uint64) CapabilityResource {
|
resource := func(disabled bool, window uint64, floor uint64) CapabilityResource {
|
||||||
ds := strategyNone()
|
if disabled {
|
||||||
|
return CapabilityResource{Disabled: true}
|
||||||
|
}
|
||||||
|
res := CapabilityResource{
|
||||||
|
OldestBlock: capabilityOldestBlock(windowOldest(window, floor)),
|
||||||
|
}
|
||||||
if window != 0 {
|
if window != 0 {
|
||||||
ds = strategyWindow(window)
|
res.DeleteStrategy = strategyWindow(window)
|
||||||
}
|
|
||||||
return CapabilityResource{
|
|
||||||
Disabled: disabled,
|
|
||||||
OldestBlock: hexutil.Uint64(windowOldest(window, floor)),
|
|
||||||
DeleteStrategy: ds,
|
|
||||||
}
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bodies and receipts share the same retention model in
|
// Bodies and receipts share the same retention model in
|
||||||
// geth: they are either kept in full ("all") or pruned to a fixed
|
// geth: they are either kept in full ("all") or pruned to a fixed
|
||||||
// boundary ("postmerge"). In neither case is there a sliding
|
// boundary ("postmerge"). In neither case is there a sliding deletion
|
||||||
// deletion window, so the strategy is always "none" and the oldest
|
// window, so deleteStrategy is omitted and oldestBlock equals the history
|
||||||
// block equals the history pruning cutoff.
|
// pruning cutoff.
|
||||||
blocks := CapabilityResource{
|
blocks := CapabilityResource{
|
||||||
Disabled: false,
|
OldestBlock: capabilityOldestBlock(cutoff),
|
||||||
OldestBlock: hexutil.Uint64(cutoff),
|
|
||||||
DeleteStrategy: strategyNone(),
|
|
||||||
}
|
}
|
||||||
receipts := blocks
|
receipts := blocks
|
||||||
|
|
||||||
|
|
@ -210,8 +205,8 @@ func buildCapabilities(headNum uint64, headHash common.Hash, cutoff uint64, ret
|
||||||
|
|
||||||
return &Capabilities{
|
return &Capabilities{
|
||||||
Head: CapabilityHead{
|
Head: CapabilityHead{
|
||||||
BlockNumber: hexutil.Uint64(headNum),
|
Number: hexutil.Uint64(headNum),
|
||||||
BlockHash: headHash,
|
Hash: headHash,
|
||||||
},
|
},
|
||||||
State: state,
|
State: state,
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,6 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
)
|
)
|
||||||
headHash := common.HexToHash("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
|
headHash := common.HexToHash("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
|
||||||
|
|
||||||
// retentionWindow is a small helper for asserting on
|
|
||||||
// CapabilityResource fields.
|
|
||||||
retentionWindow := func(n uint64) *uint64 { return &n }
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
headNum uint64
|
headNum uint64
|
||||||
|
|
@ -53,12 +49,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
StateScheme: rawdb.PathScheme,
|
StateScheme: rawdb.PathScheme,
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"blocks": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"blocks": {OldestBlock: hexUintPtr(0)},
|
||||||
"receipts": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"receipts": {OldestBlock: hexUintPtr(0)},
|
||||||
"tx": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"tx": {OldestBlock: hexUintPtr(0)},
|
||||||
"logs": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"logs": {OldestBlock: hexUintPtr(0)},
|
||||||
"state": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"state": {OldestBlock: hexUintPtr(0)},
|
||||||
"stateproofs": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"stateproofs": {OldestBlock: hexUintPtr(0)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -71,8 +67,8 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
// blocks/receipts honor the absolute cutoff with no
|
// blocks/receipts honor the absolute cutoff with no
|
||||||
// sliding window.
|
// sliding window.
|
||||||
"blocks": {OldestBlock: hexUint(postmerge), DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"blocks": {OldestBlock: hexUintPtr(postmerge)},
|
||||||
"receipts": {OldestBlock: hexUint(postmerge), DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"receipts": {OldestBlock: hexUintPtr(postmerge)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -86,12 +82,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"tx": {
|
"tx": {
|
||||||
OldestBlock: hexUint(5_000_000 - 2_350_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 2_350_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(2_350_000)},
|
DeleteStrategy: windowStrategy(2_350_000),
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
OldestBlock: hexUint(5_000_000 - 2_350_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 2_350_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(2_350_000)},
|
DeleteStrategy: windowStrategy(2_350_000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -105,8 +101,8 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"tx": {
|
"tx": {
|
||||||
OldestBlock: 0,
|
OldestBlock: hexUintPtr(0),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(2_350_000)},
|
DeleteStrategy: windowStrategy(2_350_000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -120,8 +116,8 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"tx": {
|
"tx": {
|
||||||
OldestBlock: hexUint(4_000_000),
|
OldestBlock: hexUintPtr(4_000_000),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(2_350_000)},
|
DeleteStrategy: windowStrategy(2_350_000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -137,12 +133,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {
|
"state": {
|
||||||
OldestBlock: hexUint(5_000_000 - 90_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 90_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(90_000)},
|
DeleteStrategy: windowStrategy(90_000),
|
||||||
},
|
},
|
||||||
"stateproofs": {
|
"stateproofs": {
|
||||||
OldestBlock: hexUint(5_000_000 - 100_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 100_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(100_000)},
|
DeleteStrategy: windowStrategy(100_000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -157,9 +153,7 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"logs": {
|
"logs": {
|
||||||
Disabled: true,
|
Disabled: true,
|
||||||
OldestBlock: hexUint(5_000_000 - 2_350_000 + 1),
|
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(2_350_000)},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -175,12 +169,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {
|
"state": {
|
||||||
OldestBlock: hexUint(5_000_000 - 90_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 90_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(90_000)},
|
DeleteStrategy: windowStrategy(90_000),
|
||||||
},
|
},
|
||||||
"stateproofs": {
|
"stateproofs": {
|
||||||
OldestBlock: hexUint(5_000_000 - 50_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 50_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(50_000)},
|
DeleteStrategy: windowStrategy(50_000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -196,12 +190,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {
|
"state": {
|
||||||
OldestBlock: hexUint(5_000_000 - 90_000 + 1),
|
OldestBlock: hexUintPtr(5_000_000 - 90_000 + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(90_000)},
|
DeleteStrategy: windowStrategy(90_000),
|
||||||
},
|
},
|
||||||
"stateproofs": {
|
"stateproofs": {
|
||||||
OldestBlock: hexUint(5_000_000 - corestate.TriesInMemory + 1),
|
OldestBlock: hexUintPtr(5_000_000 - corestate.TriesInMemory + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(corestate.TriesInMemory)},
|
DeleteStrategy: windowStrategy(corestate.TriesInMemory),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -215,8 +209,8 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
StateHistory: 90_000,
|
StateHistory: 90_000,
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"state": {OldestBlock: hexUintPtr(0)},
|
||||||
"stateproofs": {OldestBlock: 0, DeleteStrategy: DeleteStrategy{Type: "none"}},
|
"stateproofs": {OldestBlock: hexUintPtr(0)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -229,12 +223,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {
|
"state": {
|
||||||
OldestBlock: hexUint(5_000_000 - corestate.TriesInMemory + 1),
|
OldestBlock: hexUintPtr(5_000_000 - corestate.TriesInMemory + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(corestate.TriesInMemory)},
|
DeleteStrategy: windowStrategy(corestate.TriesInMemory),
|
||||||
},
|
},
|
||||||
"stateproofs": {
|
"stateproofs": {
|
||||||
OldestBlock: hexUint(5_000_000 - corestate.TriesInMemory + 1),
|
OldestBlock: hexUintPtr(5_000_000 - corestate.TriesInMemory + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(corestate.TriesInMemory)},
|
DeleteStrategy: windowStrategy(corestate.TriesInMemory),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -248,12 +242,12 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: map[string]CapabilityResource{
|
expected: map[string]CapabilityResource{
|
||||||
"state": {
|
"state": {
|
||||||
OldestBlock: hexUint(5_000_000 - corestate.TriesInMemory + 1),
|
OldestBlock: hexUintPtr(5_000_000 - corestate.TriesInMemory + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(corestate.TriesInMemory)},
|
DeleteStrategy: windowStrategy(corestate.TriesInMemory),
|
||||||
},
|
},
|
||||||
"stateproofs": {
|
"stateproofs": {
|
||||||
OldestBlock: hexUint(5_000_000 - corestate.TriesInMemory + 1),
|
OldestBlock: hexUintPtr(5_000_000 - corestate.TriesInMemory + 1),
|
||||||
DeleteStrategy: DeleteStrategy{Type: "window", RetentionBlocks: retentionWindow(corestate.TriesInMemory)},
|
DeleteStrategy: windowStrategy(corestate.TriesInMemory),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -264,11 +258,11 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
caps := buildCapabilities(tt.headNum, headHash, tt.cutoff, tt.ret)
|
caps := buildCapabilities(tt.headNum, headHash, tt.cutoff, tt.ret)
|
||||||
|
|
||||||
// Head is always present.
|
// Head is always present.
|
||||||
if uint64(caps.Head.BlockNumber) != tt.headNum {
|
if uint64(caps.Head.Number) != tt.headNum {
|
||||||
t.Errorf("head.blockNumber = %d, want %d", uint64(caps.Head.BlockNumber), tt.headNum)
|
t.Errorf("head.number = %d, want %d", uint64(caps.Head.Number), tt.headNum)
|
||||||
}
|
}
|
||||||
if caps.Head.BlockHash != headHash {
|
if caps.Head.Hash != headHash {
|
||||||
t.Errorf("head.blockHash = %x, want %x", caps.Head.BlockHash, headHash)
|
t.Errorf("head.hash = %x, want %x", caps.Head.Hash, headHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := map[string]CapabilityResource{
|
actual := map[string]CapabilityResource{
|
||||||
|
|
@ -284,23 +278,38 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
if got.Disabled != want.Disabled {
|
if got.Disabled != want.Disabled {
|
||||||
t.Errorf("%s.disabled = %v, want %v", name, got.Disabled, want.Disabled)
|
t.Errorf("%s.disabled = %v, want %v", name, got.Disabled, want.Disabled)
|
||||||
}
|
}
|
||||||
if got.OldestBlock != want.OldestBlock {
|
switch {
|
||||||
t.Errorf("%s.oldestBlock = %d, want %d", name, uint64(got.OldestBlock), uint64(want.OldestBlock))
|
case want.OldestBlock == nil && got.OldestBlock != nil:
|
||||||
}
|
t.Errorf("%s.oldestBlock = %d, want absent", name, uint64(*got.OldestBlock))
|
||||||
if got.DeleteStrategy.Type != want.DeleteStrategy.Type {
|
case want.OldestBlock != nil && got.OldestBlock == nil:
|
||||||
t.Errorf("%s.deleteStrategy.type = %q, want %q", name, got.DeleteStrategy.Type, want.DeleteStrategy.Type)
|
t.Errorf("%s.oldestBlock absent, want %d", name, uint64(*want.OldestBlock))
|
||||||
|
case want.OldestBlock != nil && got.OldestBlock != nil:
|
||||||
|
if *got.OldestBlock != *want.OldestBlock {
|
||||||
|
t.Errorf("%s.oldestBlock = %d, want %d",
|
||||||
|
name, uint64(*got.OldestBlock), uint64(*want.OldestBlock))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case want.DeleteStrategy.RetentionBlocks == nil && got.DeleteStrategy.RetentionBlocks != nil:
|
case want.DeleteStrategy == nil && got.DeleteStrategy != nil:
|
||||||
t.Errorf("%s.deleteStrategy.retentionBlocks = %d, want absent",
|
t.Errorf("%s.deleteStrategy = %#v, want absent", name, got.DeleteStrategy)
|
||||||
name, *got.DeleteStrategy.RetentionBlocks)
|
case want.DeleteStrategy != nil && got.DeleteStrategy == nil:
|
||||||
case want.DeleteStrategy.RetentionBlocks != nil && got.DeleteStrategy.RetentionBlocks == nil:
|
t.Errorf("%s.deleteStrategy absent, want %#v", name, want.DeleteStrategy)
|
||||||
t.Errorf("%s.deleteStrategy.retentionBlocks absent, want %d",
|
case want.DeleteStrategy != nil && got.DeleteStrategy != nil:
|
||||||
name, *want.DeleteStrategy.RetentionBlocks)
|
if got.DeleteStrategy.Type != want.DeleteStrategy.Type {
|
||||||
case want.DeleteStrategy.RetentionBlocks != nil && got.DeleteStrategy.RetentionBlocks != nil:
|
t.Errorf("%s.deleteStrategy.type = %q, want %q", name, got.DeleteStrategy.Type, want.DeleteStrategy.Type)
|
||||||
if *got.DeleteStrategy.RetentionBlocks != *want.DeleteStrategy.RetentionBlocks {
|
}
|
||||||
t.Errorf("%s.deleteStrategy.retentionBlocks = %d, want %d",
|
switch {
|
||||||
name, *got.DeleteStrategy.RetentionBlocks, *want.DeleteStrategy.RetentionBlocks)
|
case want.DeleteStrategy.RetentionBlocks == nil && got.DeleteStrategy.RetentionBlocks != nil:
|
||||||
|
t.Errorf("%s.deleteStrategy.retentionBlocks = %d, want absent",
|
||||||
|
name, *got.DeleteStrategy.RetentionBlocks)
|
||||||
|
case want.DeleteStrategy.RetentionBlocks != nil && got.DeleteStrategy.RetentionBlocks == nil:
|
||||||
|
t.Errorf("%s.deleteStrategy.retentionBlocks absent, want %d",
|
||||||
|
name, *want.DeleteStrategy.RetentionBlocks)
|
||||||
|
case want.DeleteStrategy.RetentionBlocks != nil && got.DeleteStrategy.RetentionBlocks != nil:
|
||||||
|
if *got.DeleteStrategy.RetentionBlocks != *want.DeleteStrategy.RetentionBlocks {
|
||||||
|
t.Errorf("%s.deleteStrategy.retentionBlocks = %d, want %d",
|
||||||
|
name, *got.DeleteStrategy.RetentionBlocks, *want.DeleteStrategy.RetentionBlocks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,18 +319,20 @@ func TestBuildCapabilities(t *testing.T) {
|
||||||
|
|
||||||
// TestCapabilitiesJSONShape verifies that the marshalled JSON conforms to
|
// TestCapabilitiesJSONShape verifies that the marshalled JSON conforms to
|
||||||
// the schema defined in https://github.com/ethereum/execution-apis/pull/755:
|
// the schema defined in https://github.com/ethereum/execution-apis/pull/755:
|
||||||
// "none" strategies must omit retentionBlocks, oldestBlock must be a hex
|
// head fields are named number/hash, resources without a sliding window omit
|
||||||
// quantity, retentionBlocks must be a decimal integer.
|
// deleteStrategy, disabled resources omit range fields, and retentionBlocks is
|
||||||
|
// a decimal integer.
|
||||||
func TestCapabilitiesJSONShape(t *testing.T) {
|
func TestCapabilitiesJSONShape(t *testing.T) {
|
||||||
caps := buildCapabilities(
|
caps := buildCapabilities(
|
||||||
5_000_000,
|
5_000_000,
|
||||||
common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"),
|
common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"),
|
||||||
0,
|
0,
|
||||||
HistoryRetention{
|
HistoryRetention{
|
||||||
StateScheme: rawdb.PathScheme,
|
StateScheme: rawdb.PathScheme,
|
||||||
TxIndexHistory: 2_350_000,
|
TxIndexHistory: 2_350_000,
|
||||||
LogIndexHistory: 2_350_000,
|
LogIndexHistory: 2_350_000,
|
||||||
StateHistory: 90_000,
|
LogIndexDisabled: true,
|
||||||
|
StateHistory: 90_000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -344,23 +355,40 @@ func TestCapabilitiesJSONShape(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// head.blockNumber must be a hex string ("0x..."), blockHash must be a 0x hash.
|
// head.number must be a hex string ("0x..."), hash must be a 0x hash.
|
||||||
head := generic["head"].(map[string]any)
|
head := generic["head"].(map[string]any)
|
||||||
if bn, ok := head["blockNumber"].(string); !ok || len(bn) < 3 || bn[:2] != "0x" {
|
if number, ok := head["number"].(string); !ok || len(number) < 3 || number[:2] != "0x" {
|
||||||
t.Errorf("head.blockNumber not hex string: %v", head["blockNumber"])
|
t.Errorf("head.number not hex string: %v", head["number"])
|
||||||
}
|
}
|
||||||
if bh, ok := head["blockHash"].(string); !ok || len(bh) != 66 {
|
if hash, ok := head["hash"].(string); !ok || len(hash) != 66 {
|
||||||
t.Errorf("head.blockHash not 32-byte hex string: %v", head["blockHash"])
|
t.Errorf("head.hash not 32-byte hex string: %v", head["hash"])
|
||||||
|
}
|
||||||
|
if _, present := head["blockNumber"]; present {
|
||||||
|
t.Errorf("head must not include blockNumber")
|
||||||
|
}
|
||||||
|
if _, present := head["blockHash"]; present {
|
||||||
|
t.Errorf("head must not include blockHash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// blocks.deleteStrategy is "none" → must NOT contain retentionBlocks.
|
// blocks have a fixed oldest block but no deletion strategy.
|
||||||
blocks := generic["blocks"].(map[string]any)
|
blocks := generic["blocks"].(map[string]any)
|
||||||
bds := blocks["deleteStrategy"].(map[string]any)
|
if ob, ok := blocks["oldestBlock"].(string); !ok || len(ob) < 3 || ob[:2] != "0x" {
|
||||||
if bds["type"] != "none" {
|
t.Errorf("blocks.oldestBlock not hex string: %v", blocks["oldestBlock"])
|
||||||
t.Errorf("blocks.deleteStrategy.type = %v, want none", bds["type"])
|
|
||||||
}
|
}
|
||||||
if _, present := bds["retentionBlocks"]; present {
|
if _, present := blocks["deleteStrategy"]; present {
|
||||||
t.Errorf("blocks.deleteStrategy must not include retentionBlocks for type=none")
|
t.Errorf("blocks must not include deleteStrategy without sliding deletion")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disabled resources must not advertise an availability range.
|
||||||
|
logs := generic["logs"].(map[string]any)
|
||||||
|
if logs["disabled"] != true {
|
||||||
|
t.Errorf("logs.disabled = %v, want true", logs["disabled"])
|
||||||
|
}
|
||||||
|
if _, present := logs["oldestBlock"]; present {
|
||||||
|
t.Errorf("disabled logs must not include oldestBlock")
|
||||||
|
}
|
||||||
|
if _, present := logs["deleteStrategy"]; present {
|
||||||
|
t.Errorf("disabled logs must not include deleteStrategy")
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx.deleteStrategy is "window" → must contain retentionBlocks as a
|
// tx.deleteStrategy is "window" → must contain retentionBlocks as a
|
||||||
|
|
@ -385,5 +413,12 @@ func TestCapabilitiesJSONShape(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hexUint is a small helper to keep the test tables compact.
|
// hexUintPtr and windowStrategy keep the test tables compact.
|
||||||
func hexUint(n uint64) hexutil.Uint64 { return hexutil.Uint64(n) }
|
func hexUintPtr(n uint64) *hexutil.Uint64 {
|
||||||
|
v := hexutil.Uint64(n)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowStrategy(n uint64) *DeleteStrategy {
|
||||||
|
return &DeleteStrategy{Type: "window", RetentionBlocks: &n}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue