Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BEP-221: implement cometBFT light block validation #1463

Merged
merged 7 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:

- name: Unit Test
env:
CGO_CFLAGS: "-O -D__BLST_PORTABLE__"
CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__"
ANDROID_HOME: "" # Skip android test
run: |
go mod download
Expand Down
7 changes: 7 additions & 0 deletions accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,13 @@ func TestGolangBindings(t *testing.T) {
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace tendermint dependency to bnb-chain source: %v\n%s", err, out)
}

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
replacer.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace cometbft dependency to bnb-chain source: %v\n%s", err, out)
}

tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.19")
tidier.Dir = pkg
if out, err := tidier.CombinedOutput(); err != nil {
Expand Down
1 change: 1 addition & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ var PrecompiledContractsBoneh = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{100}): &tmHeaderValidate{},
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidatePlanck{},
common.BytesToAddress([]byte{102}): &blsSignatureVerify{},
common.BytesToAddress([]byte{103}): &cometBFTLightBlockValidate{},
}

// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
Expand Down
67 changes: 52 additions & 15 deletions core/vm/contracts_lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"

"github.com/ethereum/go-ethereum/core/vm/lightclient"
v1 "github.com/ethereum/go-ethereum/core/vm/lightclient/v1"
v2 "github.com/ethereum/go-ethereum/core/vm/lightclient/v2"
"github.com/ethereum/go-ethereum/params"
)

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

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

cs, err := lightclient.DecodeConsensusState(input[consensusStateLengthBytesLength : consensusStateLengthBytesLength+csLen])
cs, err := v1.DecodeConsensusState(input[consensusStateLengthBytesLength : consensusStateLengthBytesLength+csLen])
if err != nil {
return nil, nil, err
}
header, err := lightclient.DecodeHeader(input[consensusStateLengthBytesLength+csLen:])
header, err := v1.DecodeHeader(input[consensusStateLengthBytesLength+csLen:])
if err != nil {
return nil, nil, err
}

return &cs, header, nil
}

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

func (c *tmHeaderValidate) RequiredGas(input []byte) uint64 {
Expand Down Expand Up @@ -169,7 +171,7 @@ func (c *iavlMerkleProofValidatePlanck) RequiredGas(_ []byte) uint64 {
}

func (c *iavlMerkleProofValidatePlanck) Run(input []byte) (result []byte, err error) {
c.basicIavlMerkleProofValidate.proofRuntime = lightclient.Ics23CompatibleProofRuntime()
c.basicIavlMerkleProofValidate.proofRuntime = v1.Ics23CompatibleProofRuntime()
c.basicIavlMerkleProofValidate.verifiers = []merkle.ProofOpVerifier{
forbiddenAbsenceOpVerifier,
singleValueOpVerifier,
Expand All @@ -188,7 +190,7 @@ func successfulMerkleResult() []byte {
}

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

kvmp, err := lightclient.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])
kvmp, err := v1.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])
if err != nil {
return nil, err
}
if c.proofRuntime == nil {
kvmp.SetProofRuntime(lightclient.DefaultProofRuntime())
kvmp.SetProofRuntime(v1.DefaultProofRuntime())
} else {
kvmp.SetProofRuntime(c.proofRuntime)
}
Expand Down Expand Up @@ -255,7 +257,7 @@ func multiStoreOpVerifier(op merkle.ProofOperator) error {
if op == nil {
return nil
}
if mop, ok := op.(lightclient.MultiStoreProofOp); ok {
if mop, ok := op.(v1.MultiStoreProofOp); ok {
storeNames := make(map[string]bool, len(mop.Proof.StoreInfos))
for _, store := range mop.Proof.StoreInfos {
if exist := storeNames[store.Name]; exist {
Expand Down Expand Up @@ -291,25 +293,25 @@ func proofOpsVerifier(poz merkle.ProofOperators) error {
}

// for legacy proof type
if _, ok := poz[1].(lightclient.MultiStoreProofOp); ok {
if _, ok := poz[1].(v1.MultiStoreProofOp); ok {
if _, ok := poz[0].(iavl.IAVLValueOp); !ok {
return cmn.NewError("invalid proof op")
}
return nil
}

// for ics23 proof type
if op2, ok := poz[1].(lightclient.CommitmentOp); ok {
if op2.Type != lightclient.ProofOpSimpleMerkleCommitment {
if op2, ok := poz[1].(v1.CommitmentOp); ok {
if op2.Type != v1.ProofOpSimpleMerkleCommitment {
return cmn.NewError("invalid proof op")
}

op1, ok := poz[0].(lightclient.CommitmentOp)
op1, ok := poz[0].(v1.CommitmentOp)
if !ok {
return cmn.NewError("invalid proof op")
}

if op1.Type != lightclient.ProofOpIAVLCommitment {
if op1.Type != v1.ProofOpIAVLCommitment {
return cmn.NewError("invalid proof op")
}
return nil
Expand All @@ -327,3 +329,38 @@ func keyVerifier(key string) error {
}
return nil
}

// cometBFTLightBlockValidate implemented as a native contract. Used to validate the light blocks for CometBFT v0.37.0
// and its compatible version. Besides, in order to support the BLS cross-chain infrastructure, the SetRelayerAddress
// and SetBlsKey methods should be implemented for the validator.
type cometBFTLightBlockValidate struct{}

func (c *cometBFTLightBlockValidate) RequiredGas(input []byte) uint64 {
return params.CometBFTLightBlockValidateGas
}

func (c *cometBFTLightBlockValidate) Run(input []byte) (result []byte, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("internal error: %v\n", r)
}
}()

cs, block, err := v2.DecodeLightBlockValidationInput(input)
if err != nil {
return nil, err
}

validatorSetChanged, err := cs.ApplyLightBlock(block)
if err != nil {
return nil, err
}

consensusStateBytes, err := cs.EncodeConsensusState()
if err != nil {
return nil, err
}

result = v2.EncodeLightBlockValidationResult(validatorSetChanged, consensusStateBytes)
return result, nil
}
Loading