internal/ethapi: make GetFinalizedHeader monotonically increasing (#2655)

This commit is contained in:
buddho 2024-08-20 11:51:32 +08:00 committed by GitHub
parent 99a2dd5ed9
commit 5d19f2182b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 58 additions and 40 deletions

@ -441,14 +441,14 @@ func (b *EthAPIBackend) Engine() consensus.Engine {
return b.eth.engine return b.eth.engine
} }
func (b *EthAPIBackend) CurrentTurnLength() (turnLength uint8, err error) { func (b *EthAPIBackend) CurrentValidators() ([]common.Address, error) {
if p, ok := b.eth.engine.(*parlia.Parlia); ok { if p, ok := b.eth.engine.(*parlia.Parlia); ok {
service := p.APIs(b.Chain())[0].Service service := p.APIs(b.Chain())[0].Service
currentHead := rpc.LatestBlockNumber currentHead := rpc.LatestBlockNumber
return service.(*parlia.API).GetTurnLength(&currentHead) return service.(*parlia.API).GetValidators(&currentHead)
} }
return 1, nil return []common.Address{}, errors.New("not supported")
} }
func (b *EthAPIBackend) CurrentHeader() *types.Header { func (b *EthAPIBackend) CurrentHeader() *types.Header {

@ -862,54 +862,72 @@ func (s *BlockChainAPI) Health() bool {
return true return true
} }
// GetFinalizedHeader returns the requested finalized block header. func (s *BlockChainAPI) getFinalizedNumber(ctx context.Context, verifiedValidatorNum int64) (int64, error) {
// - probabilisticFinalized should be in range [2,21], parliaConfig := s.b.ChainConfig().Parlia
// then the block header with number `max(fastFinalized, latest-probabilisticFinalized)` is returned if parliaConfig == nil {
func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, probabilisticFinalized int64) (map[string]interface{}, error) { return 0, fmt.Errorf("only parlia engine supported")
if probabilisticFinalized < 2 || probabilisticFinalized > 21 {
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized)
} }
currentTurnLength, err := s.b.CurrentTurnLength() curValidators, err := s.b.CurrentValidators()
if err != nil { // impossible if err != nil { // impossible
return nil, err return 0, err
} }
valLen := int64(len(curValidators))
if verifiedValidatorNum < 1 || verifiedValidatorNum > valLen {
return 0, fmt.Errorf("%d out of range [1,%d]", verifiedValidatorNum, valLen)
}
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber) fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
if err != nil { // impossible if err != nil { // impossible
return nil, err return 0, err
} }
latestHeader, err := s.b.HeaderByNumber(ctx, rpc.LatestBlockNumber) latestHeader, err := s.b.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil { // impossible
return 0, err
}
lastHeader := latestHeader
confirmedValSet := make(map[common.Address]struct{}, valLen)
confirmedValSet[lastHeader.Coinbase] = struct{}{}
for count := 1; int64(len(confirmedValSet)) < verifiedValidatorNum && count <= int(parliaConfig.Epoch) && lastHeader.Number.Int64() > max(fastFinalizedHeader.Number.Int64(), 1); count++ {
lastHeader, err = s.b.HeaderByHash(ctx, lastHeader.ParentHash)
if err != nil { // impossible
return 0, err
}
confirmedValSet[lastHeader.Coinbase] = struct{}{}
}
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), lastHeader.Number.Int64())
log.Debug("getFinalizedNumber", "LatestBlockNumber", latestHeader.Number.Int64(), "fastFinalizedHeight", fastFinalizedHeader.Number.Int64(),
"lastHeader", lastHeader.Number.Int64(), "finalizedBlockNumber", finalizedBlockNumber, "len(confirmedValSet)", len(confirmedValSet))
return finalizedBlockNumber, nil
}
// GetFinalizedHeader returns the finalized block header based on the specified parameters.
// - `verifiedValidatorNum` must be within the range [1, len(currentValidators)].
// - The function calculates `probabilisticFinalizedHeight` as the highest height of the block verified by `verifiedValidatorNum` validators,
// it then returns the block header with a height equal to `max(fastFinalizedHeight, probabilisticFinalizedHeight)`.
// - The height of the returned block header is guaranteed to be monotonically increasing.
func (s *BlockChainAPI) GetFinalizedHeader(ctx context.Context, verifiedValidatorNum int64) (map[string]interface{}, error) {
finalizedBlockNumber, err := s.getFinalizedNumber(ctx, verifiedValidatorNum)
if err != nil { // impossible if err != nil { // impossible
return nil, err return nil, err
} }
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength))
return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber)) return s.GetHeaderByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber))
} }
// GetFinalizedBlock returns the requested finalized block. // GetFinalizedBlock returns the finalized block based on the specified parameters.
// - probabilisticFinalized should be in range [2,21], // - `verifiedValidatorNum` must be within the range [1, len(currentValidators)].
// then the block with number `max(fastFinalized, latest-probabilisticFinalized)` is returned // - The function calculates `probabilisticFinalizedHeight` as the highest height of the block verified by `verifiedValidatorNum` validators,
// - When fullTx is true all transactions in the block are returned, otherwise // it then returns the block with a height equal to `max(fastFinalizedHeight, probabilisticFinalizedHeight)`.
// only the transaction hash is returned. // - If `fullTx` is true, the block includes all transactions; otherwise, only transaction hashes are included.
func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, probabilisticFinalized int64, fullTx bool) (map[string]interface{}, error) { // - The height of the returned block is guaranteed to be monotonically increasing.
if probabilisticFinalized < 2 || probabilisticFinalized > 21 { func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, verifiedValidatorNum int64, fullTx bool) (map[string]interface{}, error) {
return nil, fmt.Errorf("%d out of range [2,21]", probabilisticFinalized) finalizedBlockNumber, err := s.getFinalizedNumber(ctx, verifiedValidatorNum)
}
currentTurnLength, err := s.b.CurrentTurnLength()
if err != nil { // impossible if err != nil { // impossible
return nil, err return nil, err
} }
fastFinalizedHeader, err := s.b.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
if err != nil { // impossible
return nil, err
}
latestHeader, err := s.b.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil { // impossible
return nil, err
}
finalizedBlockNumber := max(fastFinalizedHeader.Number.Int64(), latestHeader.Number.Int64()-probabilisticFinalized*int64(currentTurnLength))
return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx) return s.GetBlockByNumber(ctx, rpc.BlockNumber(finalizedBlockNumber), fullTx)
} }

@ -633,7 +633,7 @@ func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event.
} }
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() } func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
func (b testBackend) CurrentTurnLength() (uint8, error) { return 1, nil } func (b testBackend) CurrentValidators() ([]common.Address, error) { return []common.Address{}, nil }
func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
panic("implement me") panic("implement me")
} }

@ -89,8 +89,8 @@ type Backend interface {
ChainConfig() *params.ChainConfig ChainConfig() *params.ChainConfig
Engine() consensus.Engine Engine() consensus.Engine
// CurrentTurnLength return the turnLength at the latest block // CurrentValidators return the list of validator at the latest block
CurrentTurnLength() (uint8, error) CurrentValidators() ([]common.Address, error)
// This is copied from filters.Backend // This is copied from filters.Backend
// eth/filters needs to be initialized from this backend type, so methods needed by // eth/filters needs to be initialized from this backend type, so methods needed by

@ -416,7 +416,7 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
func (b *backendMock) Engine() consensus.Engine { return nil } func (b *backendMock) Engine() consensus.Engine { return nil }
func (b *backendMock) CurrentTurnLength() (uint8, error) { return 1, nil } func (b *backendMock) CurrentValidators() ([]common.Address, error) { return []common.Address{}, nil }
func (b *backendMock) MevRunning() bool { return false } func (b *backendMock) MevRunning() bool { return false }
func (b *backendMock) HasBuilder(builder common.Address) bool { return false } func (b *backendMock) HasBuilder(builder common.Address) bool { return false }