Skip to content

Commit

Permalink
feat: add precompiled contracts for Greenfield link (bnb-chain#7)
Browse files Browse the repository at this point in the history
* feat: add precompiled contracts for Greenfield link

* fix bug in lightclient

* fix the comments

* feature: add hardfork logic (bnb-chain#1)

* feature: update hard fork info (bnb-chain#3)

* Fix/fix dockerfile (bnb-chain#4)

* fix: add libc-dev to support greenfield

* feature: update devnet fork block number

* feature: update chain config info (bnb-chain#5)

---------

Co-authored-by: redhdx <[email protected]>
  • Loading branch information
yutianwu and redhdx authored Oct 18, 2023
1 parent d69d749 commit cdeca04
Show file tree
Hide file tree
Showing 11 changed files with 1,999 additions and 104 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ARG BUILDNUM=""
# Build Geth in a stock Go builder container
FROM golang:1.20-alpine as builder

RUN apk add --no-cache build-base libc-dev
RUN apk add --no-cache gcc musl-dev linux-headers git

# Get dependencies - will also be cached if we won't change go.mod/go.sum
Expand Down
9 changes: 8 additions & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
// chain config as that would be AllProtocolChanges (applying any new fork
// on top of an existing private network genesis block). In that case, only
// apply the overrides.
if genesis == nil && stored != params.MainnetGenesisHash {
if genesis == nil && stored != params.MainnetGenesisHash && stored != params.OPBNBMainNetGenesisHash &&
stored != params.OPBNBTestNetGenesisHash && stored != params.OPBNBDevNetGenesisHash {
newcfg = storedcfg
applyOverrides(newcfg)
}
Expand Down Expand Up @@ -453,6 +454,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
return params.RinkebyChainConfig
case ghash == params.GoerliGenesisHash:
return params.GoerliChainConfig
case ghash == params.OPBNBMainNetGenesisHash:
return params.OPBNBMainNetConfig
case ghash == params.OPBNBTestNetGenesisHash:
return params.OPBNBTestNetConfig
case ghash == params.OPBNBDevNetGenesisHash:
return params.OPBNBDevNetConfig
default:
return params.AllEthashProtocolChanges
}
Expand Down
134 changes: 133 additions & 1 deletion core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"math/big"

"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"golang.org/x/crypto/ripemd160"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm/lightclient"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bls12381"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)

// PrecompiledContract is the basic interface for native Go contracts. The implementation
Expand Down Expand Up @@ -90,6 +95,23 @@ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{},
}

// PrecompiledContractsFermat contains the default set of pre-compiled Ethereum
// contracts used in the Fermat release.
var PrecompiledContractsFermat = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},

common.BytesToAddress([]byte{102}): &blsSignatureVerify{},
common.BytesToAddress([]byte{103}): &cometBFTLightBlockValidate{},
}

// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
Expand All @@ -105,6 +127,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
}

var (
PrecompiledAddressesFermat []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
PrecompiledAddressesByzantium []common.Address
Expand All @@ -124,11 +147,16 @@ func init() {
for k := range PrecompiledContractsBerlin {
PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k)
}
for k := range PrecompiledContractsFermat {
PrecompiledAddressesFermat = append(PrecompiledAddressesFermat, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsFermat:
return PrecompiledAddressesFermat
case rules.IsBerlin:
return PrecompiledAddressesBerlin
case rules.IsIstanbul:
Expand Down Expand Up @@ -1048,3 +1076,107 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
// Encode the G2 point to 256 bytes
return g.EncodePoint(r), nil
}

// blsSignatureVerify implements bls signature verification precompile.
type blsSignatureVerify struct{}

const (
msgHashLength = uint64(32)
signatureLength = uint64(96)
singleBlsPubkeyLength = uint64(48)
)

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *blsSignatureVerify) RequiredGas(input []byte) uint64 {
msgAndSigLength := msgHashLength + signatureLength
inputLen := uint64(len(input))
if inputLen <= msgAndSigLength ||
(inputLen-msgAndSigLength)%singleBlsPubkeyLength != 0 {
return params.BlsSignatureVerifyBaseGas
}
pubKeyNumber := (inputLen - msgAndSigLength) / singleBlsPubkeyLength
return params.BlsSignatureVerifyBaseGas + pubKeyNumber*params.BlsSignatureVerifyPerKeyGas
}

// Run input:
// msg | signature | [{bls pubkey}] |
// 32 bytes | 96 bytes | [{48 bytes}] |
func (c *blsSignatureVerify) Run(input []byte) ([]byte, error) {
msgAndSigLength := msgHashLength + signatureLength
inputLen := uint64(len(input))
if inputLen <= msgAndSigLength ||
(inputLen-msgAndSigLength)%singleBlsPubkeyLength != 0 {
log.Debug("blsSignatureVerify input size wrong", "inputLen", inputLen)
return nil, ErrExecutionReverted
}

var msg [32]byte
msgBytes := getData(input, 0, msgHashLength)
copy(msg[:], msgBytes)

signatureBytes := getData(input, msgHashLength, signatureLength)
sig, err := bls.SignatureFromBytes(signatureBytes)
if err != nil {
log.Debug("blsSignatureVerify invalid signature", "err", err)
return nil, ErrExecutionReverted
}

pubKeyNumber := (inputLen - msgAndSigLength) / singleBlsPubkeyLength
pubKeys := make([]bls.PublicKey, pubKeyNumber)
for i := uint64(0); i < pubKeyNumber; i++ {
pubKeyBytes := getData(input, msgAndSigLength+i*singleBlsPubkeyLength, singleBlsPubkeyLength)
pubKey, err := bls.PublicKeyFromBytes(pubKeyBytes)
if err != nil {
log.Debug("blsSignatureVerify invalid pubKey", "err", err)
return nil, ErrExecutionReverted
}
pubKeys[i] = pubKey
}

if pubKeyNumber > 1 {
if !sig.FastAggregateVerify(pubKeys, msg) {
return big0.Bytes(), nil
}
} else {
if !sig.Verify(pubKeys[0], msgBytes) {
return big0.Bytes(), nil
}
}

return big1.Bytes(), 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 := lightclient.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 = lightclient.EncodeLightBlockValidationResult(validatorSetChanged, consensusStateBytes)
return result, nil
}
16 changes: 16 additions & 0 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ package vm

import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/ethereum/go-ethereum/common"
)

Expand Down Expand Up @@ -391,3 +394,16 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
}
benchmarkPrecompiled("0f", testcase, b)
}

func TestCometBFTLightBlockValidate(t *testing.T) {
inputStr := "000000000000000000000000000000000000000000000000000000000000018c677265656e6669656c645f393030302d3132310000000000000000000000000000000000000000013c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb40e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e8423000000000098968015154514f68ce65a0d9eecc578c0ab12da0a2a28a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b0000000000989680432f6c4908a9aa5f3444421f466b11645235c99b831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b0a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a0000000000989680864cb9828254d712f8e59b164fc6a9402dc4e6c59065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca0aeb060adb030a02080b1213677265656e6669656c645f393030302d3132311802220c08b2d7f3a10610e8d2adb3032a480a20ec6ecb5db4ffb17fabe40c60ca7b8441e9c5d77585d0831186f3c37aa16e9c15122408011220a2ab9e1eb9ea52812f413526e424b326aff2f258a56e00d690db9f805b60fe7e32200f40aeff672e8309b7b0aefbb9a1ae3d4299b5c445b7d54e8ff398488467f0053a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85542203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb404a203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb405220294d8fbd0b94b767a7eba9840f299a3586da7fe6b5dead3b7eecba193c400f935a20bc50557c12d7392b0d07d75df0b61232d48f86a74fdea6d1485d9be6317d268c6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85572146699336aa109d1beab3946198c8e59f3b2cbd92f7a4065e3cd89e315ca39d87dee92835b98f8b8ec0861d6d9bb2c60156df5d375b3ceb1fbe71af6a244907d62548a694165caa660fec7a9b4e7b9198191361c71be0b128a0308021a480a20726abd0fdbfb6f779b0483e6e4b4b6f12241f6ea2bf374233ab1a316692b6415122408011220159f10ff15a8b58fc67a92ffd7f33c8cd407d4ce81b04ca79177dfd00ca19a67226808021214050cff76cc632760ba9db796c046004c900967361a0c08b3d7f3a10610808cadba03224080713027ffb776a702d78fd0406205c629ba473e1f8d6af646190f6eb9262cd67d69be90d10e597b91e06d7298eb6fa4b8f1eb7752ebf352a1f51560294548042268080212146699336aa109d1beab3946198c8e59f3b2cbd92f1a0c08b3d7f3a10610b087c1c00322405e2ddb70acfe4904438be3d9f4206c0ace905ac4fc306a42cfc9e86268950a0fbfd6ec5f526d3e41a3ef52bf9f9f358e3cb4c3feac76c762fa3651c1244fe004226808021214c55765fd2d0570e869f6ac22e7f2916a35ea300d1a0c08b3d7f3a10610f0b3d492032240ca17898bd22232fc9374e1188636ee321a396444a5b1a79f7628e4a11f265734b2ab50caf21e8092c55d701248e82b2f011426cb35ba22043b497a6b4661930612a0050aa8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da880aa2010a146699336aa109d1beab3946198c8e59f3b2cbd92f12220a20451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b1880ade2042080ade2042a30831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b3214432f6c4908a9aa5f3444421f466b11645235c99b3a14a0a7769429468054e19059af4867da0a495567e50aa2010a14c55765fd2d0570e869f6ac22e7f2916a35ea300d12220a200a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a1880ade2042080ade2042a309065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca3214864cb9828254d712f8e59b164fc6a9402dc4e6c53a143139916d97df0c589312b89950b6ab9795f34d1a12a8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da88"
expectOutputStr := "000000000000000000000000000000000000000000000000000000000000018c677265656e6669656c645f393030302d3132310000000000000000000000000000000000000000023c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb40e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e8423000000000098968015154514f68ce65a0d9eecc578c0ab12da0a2a28a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b0000000000989680432f6c4908a9aa5f3444421f466b11645235c99b831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b0a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a0000000000989680864cb9828254d712f8e59b164fc6a9402dc4e6c59065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca"

input, err := hex.DecodeString(inputStr)
require.NoError(t, err)

contract := &cometBFTLightBlockValidate{}
res, err := contract.Run(input)
require.NoError(t, err)
require.Equal(t, expectOutputStr, hex.EncodeToString(res))
}
2 changes: 2 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type (
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
case evm.chainRules.IsFermat:
precompiles = PrecompiledContractsFermat
case evm.chainRules.IsBerlin:
precompiles = PrecompiledContractsBerlin
case evm.chainRules.IsIstanbul:
Expand Down
Loading

0 comments on commit cdeca04

Please sign in to comment.