Compare commits
16 Commits
versa_debu
...
faucet-rat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a40ee2828a | ||
|
|
f219c080c7 | ||
|
|
75c18c9817 | ||
|
|
45c683fc1d | ||
|
|
c5f157cfad | ||
|
|
e18194720b | ||
|
|
a85215cd70 | ||
|
|
cd3539ab18 | ||
|
|
a9893492ba | ||
|
|
8fe7ca0b3b | ||
|
|
d98b22ba75 | ||
|
|
b844958a96 | ||
|
|
3cade73e40 | ||
|
|
4f38c78c6e | ||
|
|
7b8d28b425 | ||
|
|
74078e1dc4 |
@@ -49,6 +49,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -216,6 +217,8 @@ type faucet struct {
|
||||
|
||||
bep2eInfos map[string]bep2eInfo
|
||||
bep2eAbi abi.ABI
|
||||
|
||||
limiter *IPRateLimiter
|
||||
}
|
||||
|
||||
// wsConn wraps a websocket connection with a write mutex as the underlying
|
||||
@@ -235,6 +238,12 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Allow 1 request per minute with burst of 5, and cache up to 1000 IPs
|
||||
limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &faucet{
|
||||
config: genesis.Config,
|
||||
client: client,
|
||||
@@ -245,6 +254,7 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
update: make(chan struct{}, 1),
|
||||
bep2eInfos: bep2eInfos,
|
||||
bep2eAbi: bep2eAbi,
|
||||
limiter: limiter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -272,6 +282,20 @@ func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// apiHandler handles requests for Ether grants and transaction statuses.
|
||||
func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ip := r.RemoteAddr
|
||||
if len(r.Header.Get("X-Forwarded-For")) > 0 {
|
||||
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
|
||||
if len(ips) > 0 {
|
||||
ip = strings.TrimSpace(ips[len(ips)-1])
|
||||
}
|
||||
}
|
||||
|
||||
if !f.limiter.GetLimiter(ip).Allow() {
|
||||
log.Warn("Too many requests from client: ", "client", ip)
|
||||
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
@@ -625,19 +649,22 @@ func (f *faucet) loop() {
|
||||
balance := new(big.Int).Div(f.balance, ether)
|
||||
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
continue
|
||||
}
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
return // Exit the goroutine if the first send fails
|
||||
}
|
||||
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
@@ -656,10 +683,12 @@ func (f *faucet) loop() {
|
||||
// Pending requests updated, stream to clients
|
||||
f.lock.RLock()
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
|
||||
44
cmd/faucet/rate_limiter.go
Normal file
44
cmd/faucet/rate_limiter.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type IPRateLimiter struct {
|
||||
ips *lru.Cache // LRU cache to store IP addresses and their associated rate limiters
|
||||
r rate.Limit // the rate limit, e.g., 5 requests per second
|
||||
b int // the burst size, e.g., allowing a burst of 10 requests at once. The rate limiter gets into action
|
||||
// only after this number exceeds
|
||||
}
|
||||
|
||||
func NewIPRateLimiter(r rate.Limit, b int, size int) (*IPRateLimiter, error) {
|
||||
cache, err := lru.New(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := &IPRateLimiter{
|
||||
ips: cache,
|
||||
r: r,
|
||||
b: b,
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) addIP(ip string) *rate.Limiter {
|
||||
limiter := rate.NewLimiter(i.r, i.b)
|
||||
|
||||
i.ips.Add(ip, limiter)
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
|
||||
if limiter, exists := i.ips.Get(ip); exists {
|
||||
return limiter.(*rate.Limiter)
|
||||
}
|
||||
|
||||
return i.addIP(ip)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ node get_perf.js --rpc ${url} --startNum ${start} --endNum ${end}
|
||||
output as following
|
||||
```bash
|
||||
Get the performance between [ 19470 , 19670 )
|
||||
txCountPerBlock = 3142.81 txCountTotal = 628562 BlockCount = 200 avgBlockTime = 3.005 inturnBlocksRatio = 0.975
|
||||
txCountPerBlock = 3142.81 txCountTotal = 628562 BlockCount = 200 avgBlockTime = 3.005 inturnBlocksRatio = 0.975 justifiedBlocksRatio = 0.98
|
||||
txCountPerSecond = 1045.8602329450914 avgGasUsedPerBlock = 250.02062627 avgGasUsedPerSecond = 83.20153952412646
|
||||
```
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const main = async () => {
|
||||
let txCountTotal = 0;
|
||||
let gasUsedTotal = 0;
|
||||
let inturnBlocks = 0;
|
||||
let justifiedBlocks = 0;
|
||||
for (let i = program.startNum; i < program.endNum; i++) {
|
||||
let txCount = await provider.send("eth_getBlockTransactionCountByNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
@@ -26,6 +27,14 @@ const main = async () => {
|
||||
inturnBlocks += 1
|
||||
}
|
||||
let timestamp = eval(eval(header.timestamp).toString(10))
|
||||
|
||||
let justifiedNumber = await provider.send("parlia_getJustifiedNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
if (justifiedNumber + 1 == i) {
|
||||
justifiedBlocks += 1
|
||||
} else {
|
||||
console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber)
|
||||
}
|
||||
console.log("BlockNumber =", i, "mod =", i%4, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp)
|
||||
}
|
||||
|
||||
@@ -41,13 +50,14 @@ const main = async () => {
|
||||
let timeCost = endTime - startTime
|
||||
let avgBlockTime = timeCost/blockCount
|
||||
let inturnBlocksRatio = inturnBlocks/blockCount
|
||||
let justifiedBlocksRatio = justifiedBlocks/blockCount
|
||||
let tps = txCountTotal/timeCost
|
||||
let M = 1000000
|
||||
let avgGasUsedPerBlock = gasUsedTotal/blockCount/M
|
||||
let avgGasUsedPerSecond = gasUsedTotal/timeCost/M
|
||||
|
||||
console.log("Get the performance between [", program.startNum, ",", program.endNum, ")");
|
||||
console.log("txCountPerBlock =", txCountPerBlock, "txCountTotal =", txCountTotal, "BlockCount =", blockCount, "avgBlockTime =", avgBlockTime, "inturnBlocksRatio =", inturnBlocksRatio);
|
||||
console.log("txCountPerBlock =", txCountPerBlock, "txCountTotal =", txCountTotal, "BlockCount =", blockCount, "avgBlockTime =", avgBlockTime, "inturnBlocksRatio =", inturnBlocksRatio, "justifiedBlocksRatio =", justifiedBlocksRatio);
|
||||
console.log("txCountPerSecond =", tps, "avgGasUsedPerBlock =", avgGasUsedPerBlock, "avgGasUsedPerSecond =", avgGasUsedPerSecond);
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ type ChainHeaderReader interface {
|
||||
// GetHighestVerifiedHeader retrieves the highest header verified.
|
||||
GetHighestVerifiedHeader() *types.Header
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the highest verified block.
|
||||
GetVerifiedBlockByHash(hash common.Hash) *types.Header
|
||||
|
||||
// ChasingHead return the best chain head of peers.
|
||||
ChasingHead() *types.Header
|
||||
}
|
||||
|
||||
@@ -31,13 +31,7 @@ type API struct {
|
||||
|
||||
// GetSnapshot retrieves the state snapshot at a given block.
|
||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@@ -56,13 +50,7 @@ func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
|
||||
|
||||
// GetValidators retrieves the list of validators at the specified block.
|
||||
func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@@ -86,3 +74,52 @@ func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error)
|
||||
}
|
||||
return snap.validators(), nil
|
||||
}
|
||||
|
||||
func (api *API) GetJustifiedNumber(number *rpc.BlockNumber) (uint64, error) {
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return 0, errUnknownBlock
|
||||
}
|
||||
snap, err := api.parlia.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil || snap.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
return snap.Attestation.TargetNumber, nil
|
||||
}
|
||||
|
||||
func (api *API) GetFinalizedNumber(number *rpc.BlockNumber) (uint64, error) {
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return 0, errUnknownBlock
|
||||
}
|
||||
snap, err := api.parlia.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil || snap.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
return snap.Attestation.SourceNumber, nil
|
||||
}
|
||||
|
||||
func (api *API) getHeader(number *rpc.BlockNumber) (header *types.Header) {
|
||||
currentHeader := api.chain.CurrentHeader()
|
||||
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = currentHeader // current if none requested
|
||||
} else if *number == rpc.SafeBlockNumber {
|
||||
justifiedNumber, _, err := api.parlia.GetJustifiedNumberAndHash(api.chain, []*types.Header{currentHeader})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
header = api.chain.GetHeaderByNumber(justifiedNumber)
|
||||
} else if *number == rpc.FinalizedBlockNumber {
|
||||
header = api.parlia.GetFinalizedHeader(api.chain, currentHeader)
|
||||
} else if *number == rpc.PendingBlockNumber {
|
||||
return nil // no pending blocks on bsc
|
||||
} else if *number == rpc.EarliestBlockNumber {
|
||||
header = api.chain.GetHeaderByNumber(0)
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
@@ -604,15 +605,11 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
case header.BlobGasUsed != nil:
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case header.WithdrawalsHash != nil:
|
||||
return fmt.Errorf("invalid WithdrawalsHash, have %#x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case !header.EmptyWithdrawalsHash():
|
||||
return errors.New("header has wrong WithdrawalsHash")
|
||||
}
|
||||
@@ -621,6 +618,17 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
}
|
||||
}
|
||||
|
||||
bohr := chain.Config().IsBohr(header.Number, header.Time)
|
||||
if !bohr {
|
||||
if header.ParentBeaconRoot != nil {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
} else {
|
||||
if header.ParentBeaconRoot == nil || *header.ParentBeaconRoot != (common.Hash{}) {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot)
|
||||
}
|
||||
}
|
||||
|
||||
// All basic checks passed, verify cascading fields
|
||||
return p.verifyCascadingFields(chain, header, parents)
|
||||
}
|
||||
@@ -1362,7 +1370,7 @@ func (p *Parlia) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *
|
||||
func (p *Parlia) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
|
||||
targetNumber := vote.Data.TargetNumber
|
||||
targetHash := vote.Data.TargetHash
|
||||
header := chain.GetHeaderByHash(targetHash)
|
||||
header := chain.GetVerifiedBlockByHash(targetHash)
|
||||
if header == nil {
|
||||
log.Warn("BlockHeader at current voteBlockNumber is nil", "targetNumber", targetNumber, "targetHash", targetHash)
|
||||
return errors.New("BlockHeader at current voteBlockNumber is nil")
|
||||
@@ -1594,11 +1602,35 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
|
||||
return new(big.Int).Set(diffNoTurn)
|
||||
}
|
||||
|
||||
func encodeSigHeaderWithoutVoteAttestation(w io.Writer, header *types.Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeaderWithoutVoteAttestation
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block without vote attestation prior to it being sealed.
|
||||
// So it's not the real hash of a block, just used as unique id to distinguish task
|
||||
func (p *Parlia) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
types.EncodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
encodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
@@ -267,8 +268,19 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
snap.Recents[number] = validator
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
// change validator set
|
||||
if number > 0 && number%s.config.Epoch == uint64(len(snap.Validators)/2) {
|
||||
epochKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// after switching the validator set, snap.Validators may become larger,
|
||||
// then the unexpected second switch will happen, just skip it.
|
||||
if _, ok := snap.Recents[epochKey]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
checkpointHeader := FindAncientHeader(header, uint64(len(snap.Validators)/2), chain, parents)
|
||||
if checkpointHeader == nil {
|
||||
return nil, consensus.ErrUnknownAncestor
|
||||
@@ -289,15 +301,22 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// BEP-404: Clear Miner History when Switching Validators Set
|
||||
snap.Recents = make(map[uint64]common.Address)
|
||||
snap.Recents[epochKey] = common.Address{}
|
||||
log.Debug("Recents are cleared up", "blockNumber", number)
|
||||
} else {
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLimit = len(snap.Validators)
|
||||
newLimit = len(newVals)
|
||||
oldLimit := len(snap.Validators)
|
||||
newLimit := len(newVals)
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.RecentForkHashes, number-uint64(newLimit)-uint64(i))
|
||||
@@ -311,10 +330,6 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
}
|
||||
snap.Number += uint64(len(headers))
|
||||
snap.Hash = headers[len(headers)-1].Hash()
|
||||
|
||||
@@ -259,23 +259,25 @@ type BlockChain struct {
|
||||
triesInMemory uint64
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
chainBlockFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
finalizedHeaderFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
chainBlockFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
finalizedHeaderFeed event.Feed
|
||||
highestVerifiedBlockFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
|
||||
// This mutex synchronizes chain write operations.
|
||||
// Readers don't need to take it, they can just read the database.
|
||||
chainmu *syncx.ClosableMutex
|
||||
|
||||
highestVerifiedHeader atomic.Pointer[types.Header]
|
||||
highestVerifiedBlock atomic.Pointer[types.Header]
|
||||
currentBlock atomic.Pointer[types.Header] // Current head of the chain
|
||||
currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync
|
||||
currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block
|
||||
@@ -400,6 +402,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
}
|
||||
|
||||
bc.highestVerifiedHeader.Store(nil)
|
||||
bc.highestVerifiedBlock.Store(nil)
|
||||
bc.currentBlock.Store(nil)
|
||||
bc.currentSnapBlock.Store(nil)
|
||||
bc.chasingHead.Store(nil)
|
||||
@@ -1925,8 +1928,12 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if reorg && mux != nil {
|
||||
mux.Post(NewSealedBlockEvent{Block: block})
|
||||
if reorg {
|
||||
bc.highestVerifiedBlock.Store(types.CopyHeader(block.Header()))
|
||||
bc.highestVerifiedBlockFeed.Send(HighestVerifiedBlockEvent{Header: block.Header()})
|
||||
if mux != nil {
|
||||
mux.Post(NewSealedBlockEvent{Block: block})
|
||||
}
|
||||
}
|
||||
|
||||
if err := bc.writeBlockWithState(block, receipts, state); err != nil {
|
||||
|
||||
@@ -98,6 +98,15 @@ func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the header of a verified block, it may be only in memory.
|
||||
func (bc *BlockChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
highestVerifiedBlock := bc.highestVerifiedBlock.Load()
|
||||
if highestVerifiedBlock != nil && highestVerifiedBlock.Hash() == hash {
|
||||
return highestVerifiedBlock
|
||||
}
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
@@ -486,6 +495,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
|
||||
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeHighestVerifiedBlockEvent registers a subscription of HighestVerifiedBlockEvent.
|
||||
func (bc *BlockChain) SubscribeHighestVerifiedHeaderEvent(ch chan<- HighestVerifiedBlockEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.highestVerifiedBlockFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeChainBlockEvent registers a subscription of ChainBlockEvent.
|
||||
func (bc *BlockChain) SubscribeChainBlockEvent(ch chan<- ChainHeadEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.chainBlockFeed.Subscribe(ch))
|
||||
|
||||
@@ -486,7 +486,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
|
||||
if cm.config.Parlia != nil {
|
||||
header.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
if cm.config.Parlia == nil {
|
||||
if cm.config.Parlia == nil || cm.config.IsBohr(header.Number, header.Time) {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
}
|
||||
@@ -621,6 +621,10 @@ func (cm *chainMaker) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (cm *chainMaker) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return cm.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (cm *chainMaker) ChasingHead() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
@@ -365,6 +365,10 @@ func (r *mockDAHeaderReader) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func createMockDATx(config *params.ChainConfig, sidecar *types.BlobTxSidecar) *types.Transaction {
|
||||
if sidecar == nil {
|
||||
tx := &types.DynamicFeeTx{
|
||||
|
||||
@@ -53,3 +53,5 @@ type ChainSideEvent struct {
|
||||
}
|
||||
|
||||
type ChainHeadEvent struct{ Block *types.Block }
|
||||
|
||||
type HighestVerifiedBlockEvent struct{ Header *types.Header }
|
||||
|
||||
@@ -121,9 +121,12 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b
|
||||
if f.preserve != nil {
|
||||
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern)
|
||||
}
|
||||
doubleSign := (extern.Coinbase == current.Coinbase)
|
||||
reorg = !currentPreserve && (externPreserve ||
|
||||
extern.Time < current.Time ||
|
||||
extern.Time == current.Time && f.rand.Float64() < 0.5)
|
||||
extern.Time == current.Time &&
|
||||
((doubleSign && extern.Hash().Cmp(current.Hash()) < 0) ||
|
||||
(!doubleSign && f.rand.Float64() < 0.5)))
|
||||
}
|
||||
return reorg, nil
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
if conf.Parlia == nil {
|
||||
if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) {
|
||||
head.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
|
||||
|
||||
@@ -436,6 +436,10 @@ func (hc *HeaderChain) GetHighestVerifiedHeader() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) ChasingHead() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -673,10 +673,7 @@ type DiffAccountsInBlock struct {
|
||||
Transactions []DiffAccountsInTx
|
||||
}
|
||||
|
||||
var (
|
||||
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
)
|
||||
var extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func SealHash(header *Header, chainId *big.Int) (hash common.Hash) {
|
||||
@@ -687,48 +684,51 @@ func SealHash(header *Header, chainId *big.Int) (hash common.Hash) {
|
||||
}
|
||||
|
||||
func EncodeSigHeader(w io.Writer, header *Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeSigHeaderWithoutVoteAttestation(w io.Writer, header *Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeaderWithoutVoteAttestation
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
var err error
|
||||
if header.ParentBeaconRoot != nil && *header.ParentBeaconRoot == (common.Hash{}) {
|
||||
err = rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
header.BaseFee,
|
||||
header.WithdrawalsHash,
|
||||
header.BlobGasUsed,
|
||||
header.ExcessBlobGas,
|
||||
header.ParentBeaconRoot,
|
||||
})
|
||||
} else {
|
||||
err = rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-extraSeal], // this will panic if extra is too short, should check before calling encodeSigHeader
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ type VoteManager struct {
|
||||
|
||||
chain *core.BlockChain
|
||||
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
highestVerifiedBlockCh chan core.HighestVerifiedBlockEvent
|
||||
highestVerifiedBlockSub event.Subscription
|
||||
|
||||
// used for backup validators to sync votes from corresponding mining validator
|
||||
syncVoteCh chan core.NewVoteEvent
|
||||
@@ -49,12 +49,12 @@ type VoteManager struct {
|
||||
|
||||
func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journalPath, blsPasswordPath, blsWalletPath string, engine consensus.PoSA) (*VoteManager, error) {
|
||||
voteManager := &VoteManager{
|
||||
eth: eth,
|
||||
chain: chain,
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
syncVoteCh: make(chan core.NewVoteEvent, voteBufferForPut),
|
||||
pool: pool,
|
||||
engine: engine,
|
||||
eth: eth,
|
||||
chain: chain,
|
||||
highestVerifiedBlockCh: make(chan core.HighestVerifiedBlockEvent, highestVerifiedBlockChanSize),
|
||||
syncVoteCh: make(chan core.NewVoteEvent, voteBufferForPut),
|
||||
pool: pool,
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
// Create voteSigner.
|
||||
@@ -74,7 +74,7 @@ func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journal
|
||||
voteManager.journal = voteJournal
|
||||
|
||||
// Subscribe to chain head event.
|
||||
voteManager.chainHeadSub = voteManager.chain.SubscribeChainHeadEvent(voteManager.chainHeadCh)
|
||||
voteManager.highestVerifiedBlockSub = voteManager.chain.SubscribeHighestVerifiedHeaderEvent(voteManager.highestVerifiedBlockCh)
|
||||
voteManager.syncVoteSub = voteManager.pool.SubscribeNewVoteEvent(voteManager.syncVoteCh)
|
||||
|
||||
go voteManager.loop()
|
||||
@@ -84,7 +84,7 @@ func NewVoteManager(eth Backend, chain *core.BlockChain, pool *VotePool, journal
|
||||
|
||||
func (voteManager *VoteManager) loop() {
|
||||
log.Debug("vote manager routine loop started")
|
||||
defer voteManager.chainHeadSub.Unsubscribe()
|
||||
defer voteManager.highestVerifiedBlockSub.Unsubscribe()
|
||||
defer voteManager.syncVoteSub.Unsubscribe()
|
||||
|
||||
events := voteManager.eth.EventMux().Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
|
||||
@@ -119,7 +119,7 @@ func (voteManager *VoteManager) loop() {
|
||||
log.Debug("downloader is in DoneEvent mode, set the startVote flag to true")
|
||||
startVote = true
|
||||
}
|
||||
case cHead := <-voteManager.chainHeadCh:
|
||||
case cHead := <-voteManager.highestVerifiedBlockCh:
|
||||
if !startVote {
|
||||
log.Debug("startVote flag is false, continue")
|
||||
continue
|
||||
@@ -135,12 +135,12 @@ func (voteManager *VoteManager) loop() {
|
||||
continue
|
||||
}
|
||||
|
||||
if cHead.Block == nil {
|
||||
log.Debug("cHead.Block is nil, continue")
|
||||
if cHead.Header == nil {
|
||||
log.Debug("cHead.Header is nil, continue")
|
||||
continue
|
||||
}
|
||||
|
||||
curHead := cHead.Block.Header()
|
||||
curHead := cHead.Header
|
||||
if p, ok := voteManager.engine.(*parlia.Parlia); ok {
|
||||
nextBlockMinedTime := time.Unix(int64((curHead.Time + p.Period())), 0)
|
||||
timeForBroadcast := 50 * time.Millisecond // enough to broadcast a vote
|
||||
@@ -217,7 +217,7 @@ func (voteManager *VoteManager) loop() {
|
||||
case <-voteManager.syncVoteSub.Err():
|
||||
log.Debug("voteManager subscribed votes failed")
|
||||
return
|
||||
case <-voteManager.chainHeadSub.Err():
|
||||
case <-voteManager.highestVerifiedBlockSub.Err():
|
||||
log.Debug("voteManager subscribed chainHead failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const (
|
||||
lowerLimitOfVoteBlockNumber = 256
|
||||
upperLimitOfVoteBlockNumber = 11 // refer to fetcher.maxUncleDist
|
||||
|
||||
chainHeadChanSize = 10 // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||
highestVerifiedBlockChanSize = 10 // highestVerifiedBlockChanSize is the size of channel listening to HighestVerifiedBlockEvent.
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -57,8 +57,8 @@ type VotePool struct {
|
||||
curVotesPq *votesPriorityQueue
|
||||
futureVotesPq *votesPriorityQueue
|
||||
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
highestVerifiedBlockCh chan core.HighestVerifiedBlockEvent
|
||||
highestVerifiedBlockSub event.Subscription
|
||||
|
||||
votesCh chan *types.VoteEnvelope
|
||||
|
||||
@@ -69,19 +69,19 @@ type votesPriorityQueue []*types.VoteData
|
||||
|
||||
func NewVotePool(chain *core.BlockChain, engine consensus.PoSA) *VotePool {
|
||||
votePool := &VotePool{
|
||||
chain: chain,
|
||||
receivedVotes: mapset.NewSet[common.Hash](),
|
||||
curVotes: make(map[common.Hash]*VoteBox),
|
||||
futureVotes: make(map[common.Hash]*VoteBox),
|
||||
curVotesPq: &votesPriorityQueue{},
|
||||
futureVotesPq: &votesPriorityQueue{},
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
votesCh: make(chan *types.VoteEnvelope, voteBufferForPut),
|
||||
engine: engine,
|
||||
chain: chain,
|
||||
receivedVotes: mapset.NewSet[common.Hash](),
|
||||
curVotes: make(map[common.Hash]*VoteBox),
|
||||
futureVotes: make(map[common.Hash]*VoteBox),
|
||||
curVotesPq: &votesPriorityQueue{},
|
||||
futureVotesPq: &votesPriorityQueue{},
|
||||
highestVerifiedBlockCh: make(chan core.HighestVerifiedBlockEvent, highestVerifiedBlockChanSize),
|
||||
votesCh: make(chan *types.VoteEnvelope, voteBufferForPut),
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
// Subscribe events from blockchain and start the main event loop.
|
||||
votePool.chainHeadSub = votePool.chain.SubscribeChainHeadEvent(votePool.chainHeadCh)
|
||||
votePool.highestVerifiedBlockSub = votePool.chain.SubscribeHighestVerifiedHeaderEvent(votePool.highestVerifiedBlockCh)
|
||||
|
||||
go votePool.loop()
|
||||
return votePool
|
||||
@@ -89,18 +89,18 @@ func NewVotePool(chain *core.BlockChain, engine consensus.PoSA) *VotePool {
|
||||
|
||||
// loop is the vote pool's main even loop, waiting for and reacting to outside blockchain events and votes channel event.
|
||||
func (pool *VotePool) loop() {
|
||||
defer pool.chainHeadSub.Unsubscribe()
|
||||
defer pool.highestVerifiedBlockSub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
// Handle ChainHeadEvent.
|
||||
case ev := <-pool.chainHeadCh:
|
||||
if ev.Block != nil {
|
||||
latestBlockNumber := ev.Block.NumberU64()
|
||||
case ev := <-pool.highestVerifiedBlockCh:
|
||||
if ev.Header != nil {
|
||||
latestBlockNumber := ev.Header.Number.Uint64()
|
||||
pool.prune(latestBlockNumber)
|
||||
pool.transferVotesFromFutureToCur(ev.Block.Header())
|
||||
pool.transferVotesFromFutureToCur(ev.Header)
|
||||
}
|
||||
case <-pool.chainHeadSub.Err():
|
||||
case <-pool.highestVerifiedBlockSub.Err():
|
||||
return
|
||||
|
||||
// Handle votes channel and put the vote into vote pool.
|
||||
@@ -135,7 +135,7 @@ func (pool *VotePool) putIntoVotePool(vote *types.VoteEnvelope) bool {
|
||||
var votesPq *votesPriorityQueue
|
||||
isFutureVote := false
|
||||
|
||||
voteBlock := pool.chain.GetHeaderByHash(targetHash)
|
||||
voteBlock := pool.chain.GetVerifiedBlockByHash(targetHash)
|
||||
if voteBlock == nil {
|
||||
votes = pool.futureVotes
|
||||
votesPq = pool.futureVotesPq
|
||||
@@ -226,7 +226,7 @@ func (pool *VotePool) transferVotesFromFutureToCur(latestBlockHeader *types.Head
|
||||
futurePqBuffer := make([]*types.VoteData, 0)
|
||||
for futurePq.Len() > 0 && futurePq.Peek().TargetNumber <= latestBlockNumber {
|
||||
blockHash := futurePq.Peek().TargetHash
|
||||
header := pool.chain.GetHeaderByHash(blockHash)
|
||||
header := pool.chain.GetVerifiedBlockByHash(blockHash)
|
||||
if header == nil {
|
||||
// Put into pq buffer used for later put again into futurePq
|
||||
futurePqBuffer = append(futurePqBuffer, heap.Pop(futurePq).(*types.VoteData))
|
||||
|
||||
@@ -1032,6 +1032,8 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
||||
}
|
||||
if w.chainConfig.Parlia == nil {
|
||||
header.ParentBeaconRoot = genParams.beaconRoot
|
||||
} else if w.chainConfig.IsBohr(header.Number, header.Time) {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
}
|
||||
// Could potentially happen if starting to mine in an odd state.
|
||||
|
||||
Reference in New Issue
Block a user