Skip to content

Commit

Permalink
BEP-221: implement cometBFT light block validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Keefe Liu committed Apr 14, 2023
1 parent f912462 commit f7f829e
Show file tree
Hide file tree
Showing 15 changed files with 1,007 additions and 179 deletions.
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
66 changes: 51 additions & 15 deletions core/vm/contracts_lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package vm
import (
"encoding/binary"
"fmt"
"github.com/ethereum/go-ethereum/core/vm/lightclient/v1"
v2 "github.com/ethereum/go-ethereum/core/vm/lightclient/v2"
"net/url"
"strings"

"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"

"github.com/ethereum/go-ethereum/core/vm/lightclient"
"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,37 @@ func keyVerifier(key string) error {
}
return nil
}

// cometBFTLightBlockValidate implemented as a native contract. Used to validate the light
// blocks for CometBFT and its compatible version.
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
}
60 changes: 37 additions & 23 deletions core/vm/contracts_lightclient_test.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package lightclient
package v1

import (
"fmt"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package lightclient
package v1

import (
"bytes"
"fmt"

"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package lightclient
package v1

import (
"fmt"

"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package lightclient
package v1

import (
"bytes"
"encoding/binary"
"fmt"

"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/merkle"
lerr "github.com/tendermint/tendermint/lite/errors"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package lightclient
package v1

import (
"fmt"

rpcclient "github.com/tendermint/tendermint/rpc/client"
tmtypes "github.com/tendermint/tendermint/types"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package lightclient
package v1

import (
"github.com/tendermint/go-amino"
Expand Down
Loading

0 comments on commit f7f829e

Please sign in to comment.