Skip to content

Commit e5b0562

Browse files
zlacfzyclaude
andauthored
core/vm: deprecate legacy v1 Tendermint precompiles, reprice v2 light block at Pasteur (#3726)
Builds on the Pasteur precompile set introduced in #3623. The legacy v1 Tendermint light-client precompiles 0x64 (tmHeaderValidate) and 0x65 (iavlMerkleProofValidate) are only reachable via the deprecated BC<->BSC cross-chain stack (contracts/deprecated/), which no longer exists. Rename the existing *Nano suspend handlers to tmHeaderValidateDeprecated / iavlMerkleProofValidateDeprecated (error "deprecated") and map 0x64/0x65 to them in the Pasteur set, deprecating them from Pasteur. For the v2 cometBFT light-client (0x67), the verification work scales with the light block's validator/signature count while the gas was flat. Price the Pasteur variant's RequiredGas per input byte (CometBFTLightBlockValidatePerByteGas) so gas tracks the real CPU/memory cost (cf. the per-key pricing of 0x66 blsSignatureVerify). The pre-Pasteur Hertz variant keeps the flat gas so earlier blocks replay identically. Note: the per-byte rate is tunable; raising 0x67 gas affects the live Greenfield/op-stack relayer gas budget and should be coordinated. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3150087 commit e5b0562

4 files changed

Lines changed: 89 additions & 20 deletions

File tree

core/vm/contracts.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ var PrecompiledContractsNano = PrecompiledContracts{
112112
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
113113
common.BytesToAddress([]byte{0x9}): &blake2F{},
114114

115-
common.BytesToAddress([]byte{0x64}): &tmHeaderValidateNano{},
116-
common.BytesToAddress([]byte{0x65}): &iavlMerkleProofValidateNano{},
115+
common.BytesToAddress([]byte{0x64}): &tmHeaderValidateDeprecated{},
116+
common.BytesToAddress([]byte{0x65}): &iavlMerkleProofValidateDeprecated{},
117117
}
118118

119119
var PrecompiledContractsMoran = PrecompiledContracts{
@@ -371,8 +371,8 @@ var PrecompiledContractsPasteur = PrecompiledContracts{
371371
common.BytesToAddress([]byte{0x10}): &bls12381MapG1{},
372372
common.BytesToAddress([]byte{0x11}): &bls12381MapG2{},
373373

374-
common.BytesToAddress([]byte{0x64}): &tmHeaderValidate{},
375-
common.BytesToAddress([]byte{0x65}): &iavlMerkleProofValidatePlato{},
374+
common.BytesToAddress([]byte{0x64}): &tmHeaderValidateDeprecated{},
375+
common.BytesToAddress([]byte{0x65}): &iavlMerkleProofValidateDeprecated{},
376376
common.BytesToAddress([]byte{0x66}): &blsSignatureVerify{},
377377
common.BytesToAddress([]byte{0x67}): &cometBFTLightBlockValidatePasteur{},
378378
common.BytesToAddress([]byte{0x68}): &verifyDoubleSignEvidence{},

core/vm/contracts_lightclient.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,33 +134,34 @@ func (c *iavlMerkleProofValidate) Name() string {
134134
return "IAVL_MERKLE_PROOF_VALIDATE"
135135
}
136136

137-
// tmHeaderValidateNano implemented as a native contract.
138-
type tmHeaderValidateNano struct{}
137+
// tmHeaderValidateDeprecated implemented as a native contract that disables the
138+
// legacy v1 Tendermint header-validate precompile (returns an error for any input).
139+
type tmHeaderValidateDeprecated struct{}
139140

140-
func (c *tmHeaderValidateNano) RequiredGas(input []byte) uint64 {
141+
func (c *tmHeaderValidateDeprecated) RequiredGas(input []byte) uint64 {
141142
return params.TendermintHeaderValidateGas
142143
}
143144

144-
func (c *tmHeaderValidateNano) Run(input []byte) (result []byte, err error) {
145-
return nil, errors.New("suspend")
145+
func (c *tmHeaderValidateDeprecated) Run(input []byte) (result []byte, err error) {
146+
return nil, errors.New("deprecated")
146147
}
147148

148-
func (c *tmHeaderValidateNano) Name() string {
149-
return "HEADER_VALIDATE_NANO"
149+
func (c *tmHeaderValidateDeprecated) Name() string {
150+
return "HEADER_VALIDATE_DEPRECATED"
150151
}
151152

152-
type iavlMerkleProofValidateNano struct{}
153+
type iavlMerkleProofValidateDeprecated struct{}
153154

154-
func (c *iavlMerkleProofValidateNano) RequiredGas(_ []byte) uint64 {
155+
func (c *iavlMerkleProofValidateDeprecated) RequiredGas(_ []byte) uint64 {
155156
return params.IAVLMerkleProofValidateGas
156157
}
157158

158-
func (c *iavlMerkleProofValidateNano) Run(_ []byte) (result []byte, err error) {
159-
return nil, errors.New("suspend")
159+
func (c *iavlMerkleProofValidateDeprecated) Run(_ []byte) (result []byte, err error) {
160+
return nil, errors.New("deprecated")
160161
}
161162

162-
func (c *iavlMerkleProofValidateNano) Name() string {
163-
return "IAVL_MERKLE_PROOF_VALIDATE_NANO"
163+
func (c *iavlMerkleProofValidateDeprecated) Name() string {
164+
return "IAVL_MERKLE_PROOF_VALIDATE_DEPRECATED"
164165
}
165166

166167
// ------------------------------------------------------------------------------------------------------------------------------------------------
@@ -446,6 +447,11 @@ type cometBFTLightBlockValidatePasteur struct {
446447
cometBFTLightBlockValidate
447448
}
448449

450+
// Price per input byte so cost scales with the validator/signature count.
451+
func (c *cometBFTLightBlockValidatePasteur) RequiredGas(input []byte) uint64 {
452+
return params.CometBFTLightBlockValidateGas + uint64(len(input))*params.CometBFTLightBlockValidatePerByteGas
453+
}
454+
449455
func (c *cometBFTLightBlockValidatePasteur) Run(input []byte) (result []byte, err error) {
450456
return c.run(input, true, true)
451457
}

core/vm/precompile_pasteur_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package vm
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
)
8+
9+
// 0x64/0x65 are deprecated from Pasteur; 0x67 switches to a per-byte-priced variant.
10+
func TestPasteurSuspendsLegacyTendermintPrecompiles(t *testing.T) {
11+
addr64 := common.BytesToAddress([]byte{0x64})
12+
addr65 := common.BytesToAddress([]byte{0x65})
13+
14+
// Under Pasteur, 0x64/0x65 return "deprecated" for any input.
15+
for _, addr := range []common.Address{addr64, addr65} {
16+
p := PrecompiledContractsPasteur[addr]
17+
if p == nil {
18+
t.Fatalf("%s missing from Pasteur precompile set", addr.Hex())
19+
}
20+
if _, err := p.Run([]byte("x")); err == nil || err.Error() != "deprecated" {
21+
t.Fatalf("%s under Pasteur: expected deprecated error, got %v", addr.Hex(), err)
22+
}
23+
}
24+
25+
// Pre-Pasteur (Osaka) they are still the live, active implementations.
26+
if _, ok := PrecompiledContractsOsaka[addr64].(*tmHeaderValidate); !ok {
27+
t.Fatalf("0x64 must remain active (tmHeaderValidate) pre-Pasteur")
28+
}
29+
if _, ok := PrecompiledContractsOsaka[addr65].(*iavlMerkleProofValidatePlato); !ok {
30+
t.Fatalf("0x65 must remain active (iavlMerkleProofValidatePlato) pre-Pasteur")
31+
}
32+
33+
// 0x67 stays active but switches to the per-byte-priced Pasteur variant
34+
// (Hertz/flat-gas pre-Pasteur, for replay of earlier blocks).
35+
addr67 := common.BytesToAddress([]byte{0x67})
36+
if _, ok := PrecompiledContractsPasteur[addr67].(*cometBFTLightBlockValidatePasteur); !ok {
37+
t.Fatalf("0x67 must be the Pasteur variant under Pasteur")
38+
}
39+
if _, ok := PrecompiledContractsOsaka[addr67].(*cometBFTLightBlockValidateHertz); !ok {
40+
t.Fatalf("0x67 must remain the Hertz variant pre-Pasteur")
41+
}
42+
43+
// The still-needed precompiles (0x66, 0x68, 0x69) remain present under Pasteur.
44+
for _, b := range []byte{0x66, 0x68, 0x69} {
45+
addr := common.BytesToAddress([]byte{b})
46+
if PrecompiledContractsPasteur[addr] == nil {
47+
t.Fatalf("%s must remain present under Pasteur", addr.Hex())
48+
}
49+
}
50+
51+
// 0x67 gas scales with input size under Pasteur, but is flat pre-Pasteur.
52+
small := make([]byte, 1024)
53+
large := make([]byte, 16*1024)
54+
pasteur67 := PrecompiledContractsPasteur[addr67]
55+
if pasteur67.RequiredGas(large) <= pasteur67.RequiredGas(small) {
56+
t.Fatalf("Pasteur 0x67 gas must scale with input size")
57+
}
58+
osaka67 := PrecompiledContractsOsaka[addr67]
59+
if osaka67.RequiredGas(large) != osaka67.RequiredGas(small) {
60+
t.Fatalf("pre-Pasteur 0x67 gas must stay flat")
61+
}
62+
}

params/protocol_params.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,10 @@ const (
143143

144144
// Precompiled contract gas prices
145145

146-
TendermintHeaderValidateGas uint64 = 3000 // Gas for validate tendermiint consensus state
147-
IAVLMerkleProofValidateGas uint64 = 3000 // Gas for validate merkle proof
148-
CometBFTLightBlockValidateGas uint64 = 3000 // Gas for validate cometBFT light block
146+
TendermintHeaderValidateGas uint64 = 3000 // Gas for validate tendermiint consensus state
147+
IAVLMerkleProofValidateGas uint64 = 3000 // Gas for validate merkle proof
148+
CometBFTLightBlockValidateGas uint64 = 3000 // Base gas for validate cometBFT light block
149+
CometBFTLightBlockValidatePerByteGas uint64 = 16 // Per-input-byte gas for cometBFT light block (Pasteur)
149150

150151
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
151152
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation

0 commit comments

Comments
 (0)