Skip to content

Commit bbd4e92

Browse files
authored
BEP-221: implement cometBFT light block validation (#1463)
1 parent 79e1554 commit bbd4e92

17 files changed

+1036
-126
lines changed

.github/workflows/unit-test.yml

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ jobs:
4545
4646
- name: Unit Test
4747
env:
48+
CGO_CFLAGS: "-O -D__BLST_PORTABLE__"
49+
CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__"
4850
ANDROID_HOME: "" # Skip android test
4951
run: |
5052
go mod download

accounts/abi/bind/bind_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,13 @@ func TestGolangBindings(t *testing.T) {
21142114
if out, err := replacer.CombinedOutput(); err != nil {
21152115
t.Fatalf("failed to replace tendermint dependency to bnb-chain source: %v\n%s", err, out)
21162116
}
2117+
2118+
replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/cometbft/cometbft@v0.0.0", "-replace", "github.com/cometbft/cometbft=github.com/bnb-chain/greenfield-tendermint@v0.0.0-20230417032003-4cda1f296fb2") // Repo root
2119+
replacer.Dir = pkg
2120+
if out, err := replacer.CombinedOutput(); err != nil {
2121+
t.Fatalf("failed to replace cometbft dependency to bnb-chain source: %v\n%s", err, out)
2122+
}
2123+
21172124
tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.19")
21182125
tidier.Dir = pkg
21192126
if out, err := tidier.CombinedOutput(); err != nil {

core/vm/contracts.go

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ var PrecompiledContractsBoneh = map[common.Address]PrecompiledContract{
157157
common.BytesToAddress([]byte{100}): &tmHeaderValidate{},
158158
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidatePlanck{},
159159
common.BytesToAddress([]byte{102}): &blsSignatureVerify{},
160+
common.BytesToAddress([]byte{103}): &cometBFTLightBlockValidate{},
160161
}
161162

162163
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum

core/vm/contracts_lightclient.go

+52-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import (
1010
"github.com/tendermint/tendermint/crypto/merkle"
1111
cmn "github.com/tendermint/tendermint/libs/common"
1212

13-
"github.com/ethereum/go-ethereum/core/vm/lightclient"
13+
v1 "github.com/ethereum/go-ethereum/core/vm/lightclient/v1"
14+
v2 "github.com/ethereum/go-ethereum/core/vm/lightclient/v2"
1415
"github.com/ethereum/go-ethereum/params"
1516
)
1617

@@ -26,7 +27,7 @@ const (
2627
// input:
2728
// consensus state length | consensus state | tendermint header |
2829
// 32 bytes | | |
29-
func decodeTendermintHeaderValidationInput(input []byte) (*lightclient.ConsensusState, *lightclient.Header, error) {
30+
func decodeTendermintHeaderValidationInput(input []byte) (*v1.ConsensusState, *v1.Header, error) {
3031
csLen := binary.BigEndian.Uint64(input[consensusStateLengthBytesLength-uint64TypeLength : consensusStateLengthBytesLength])
3132

3233
if consensusStateLengthBytesLength+csLen < consensusStateLengthBytesLength {
@@ -37,19 +38,20 @@ func decodeTendermintHeaderValidationInput(input []byte) (*lightclient.Consensus
3738
return nil, nil, fmt.Errorf("expected payload size %d, actual size: %d", consensusStateLengthBytesLength+csLen, len(input))
3839
}
3940

40-
cs, err := lightclient.DecodeConsensusState(input[consensusStateLengthBytesLength : consensusStateLengthBytesLength+csLen])
41+
cs, err := v1.DecodeConsensusState(input[consensusStateLengthBytesLength : consensusStateLengthBytesLength+csLen])
4142
if err != nil {
4243
return nil, nil, err
4344
}
44-
header, err := lightclient.DecodeHeader(input[consensusStateLengthBytesLength+csLen:])
45+
header, err := v1.DecodeHeader(input[consensusStateLengthBytesLength+csLen:])
4546
if err != nil {
4647
return nil, nil, err
4748
}
4849

4950
return &cs, header, nil
5051
}
5152

52-
// tmHeaderValidate implemented as a native contract.
53+
// tmHeaderValidate implemented as a native contract. Used to validate the light
54+
// client's new header for tendermint v0.31.12 and its compatible version.
5355
type tmHeaderValidate struct{}
5456

5557
func (c *tmHeaderValidate) RequiredGas(input []byte) uint64 {
@@ -169,7 +171,7 @@ func (c *iavlMerkleProofValidatePlanck) RequiredGas(_ []byte) uint64 {
169171
}
170172

171173
func (c *iavlMerkleProofValidatePlanck) Run(input []byte) (result []byte, err error) {
172-
c.basicIavlMerkleProofValidate.proofRuntime = lightclient.Ics23CompatibleProofRuntime()
174+
c.basicIavlMerkleProofValidate.proofRuntime = v1.Ics23CompatibleProofRuntime()
173175
c.basicIavlMerkleProofValidate.verifiers = []merkle.ProofOpVerifier{
174176
forbiddenAbsenceOpVerifier,
175177
singleValueOpVerifier,
@@ -188,7 +190,7 @@ func successfulMerkleResult() []byte {
188190
}
189191

190192
type basicIavlMerkleProofValidate struct {
191-
keyVerifier lightclient.KeyVerifier
193+
keyVerifier v1.KeyVerifier
192194
opsVerifier merkle.ProofOpsVerifier
193195
verifiers []merkle.ProofOpVerifier
194196
proofRuntime *merkle.ProofRuntime
@@ -210,12 +212,12 @@ func (c *basicIavlMerkleProofValidate) Run(input []byte) (result []byte, err err
210212
return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength, len(input))
211213
}
212214

213-
kvmp, err := lightclient.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])
215+
kvmp, err := v1.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])
214216
if err != nil {
215217
return nil, err
216218
}
217219
if c.proofRuntime == nil {
218-
kvmp.SetProofRuntime(lightclient.DefaultProofRuntime())
220+
kvmp.SetProofRuntime(v1.DefaultProofRuntime())
219221
} else {
220222
kvmp.SetProofRuntime(c.proofRuntime)
221223
}
@@ -255,7 +257,7 @@ func multiStoreOpVerifier(op merkle.ProofOperator) error {
255257
if op == nil {
256258
return nil
257259
}
258-
if mop, ok := op.(lightclient.MultiStoreProofOp); ok {
260+
if mop, ok := op.(v1.MultiStoreProofOp); ok {
259261
storeNames := make(map[string]bool, len(mop.Proof.StoreInfos))
260262
for _, store := range mop.Proof.StoreInfos {
261263
if exist := storeNames[store.Name]; exist {
@@ -291,25 +293,25 @@ func proofOpsVerifier(poz merkle.ProofOperators) error {
291293
}
292294

293295
// for legacy proof type
294-
if _, ok := poz[1].(lightclient.MultiStoreProofOp); ok {
296+
if _, ok := poz[1].(v1.MultiStoreProofOp); ok {
295297
if _, ok := poz[0].(iavl.IAVLValueOp); !ok {
296298
return cmn.NewError("invalid proof op")
297299
}
298300
return nil
299301
}
300302

301303
// for ics23 proof type
302-
if op2, ok := poz[1].(lightclient.CommitmentOp); ok {
303-
if op2.Type != lightclient.ProofOpSimpleMerkleCommitment {
304+
if op2, ok := poz[1].(v1.CommitmentOp); ok {
305+
if op2.Type != v1.ProofOpSimpleMerkleCommitment {
304306
return cmn.NewError("invalid proof op")
305307
}
306308

307-
op1, ok := poz[0].(lightclient.CommitmentOp)
309+
op1, ok := poz[0].(v1.CommitmentOp)
308310
if !ok {
309311
return cmn.NewError("invalid proof op")
310312
}
311313

312-
if op1.Type != lightclient.ProofOpIAVLCommitment {
314+
if op1.Type != v1.ProofOpIAVLCommitment {
313315
return cmn.NewError("invalid proof op")
314316
}
315317
return nil
@@ -327,3 +329,38 @@ func keyVerifier(key string) error {
327329
}
328330
return nil
329331
}
332+
333+
// cometBFTLightBlockValidate implemented as a native contract. Used to validate the light blocks for CometBFT v0.37.0
334+
// and its compatible version. Besides, in order to support the BLS cross-chain infrastructure, the SetRelayerAddress
335+
// and SetBlsKey methods should be implemented for the validator.
336+
type cometBFTLightBlockValidate struct{}
337+
338+
func (c *cometBFTLightBlockValidate) RequiredGas(input []byte) uint64 {
339+
return params.CometBFTLightBlockValidateGas
340+
}
341+
342+
func (c *cometBFTLightBlockValidate) Run(input []byte) (result []byte, err error) {
343+
defer func() {
344+
if r := recover(); r != nil {
345+
err = fmt.Errorf("internal error: %v\n", r)
346+
}
347+
}()
348+
349+
cs, block, err := v2.DecodeLightBlockValidationInput(input)
350+
if err != nil {
351+
return nil, err
352+
}
353+
354+
validatorSetChanged, err := cs.ApplyLightBlock(block)
355+
if err != nil {
356+
return nil, err
357+
}
358+
359+
consensusStateBytes, err := cs.EncodeConsensusState()
360+
if err != nil {
361+
return nil, err
362+
}
363+
364+
result = v2.EncodeLightBlockValidationResult(validatorSetChanged, consensusStateBytes)
365+
return result, nil
366+
}

0 commit comments

Comments
 (0)