go-ethereum/consensus/XDPoS/engines/engine_v1.go
anilchinchawale 85bb3e957c Add XDCx trading, lending, downloader, and documentation
XDCx Trading Package:
- XDCx.go - Main DEX engine
- order.go - Order types and handling
- order_processor.go - Order processing logic
- trade.go - Trade execution
- matcher.go - Order matching engine
- api.go - Public/Private APIs
- tradingstate/ - Trading state management

XDCxLending Package:
- XDCxlending.go - Main lending protocol
- order.go - Lending order types
- order_processor.go - Lending order processing
- trade.go - Lending trade execution
- liquidator.go - Loan liquidation logic
- api.go - Lending APIs
- lendingstate/ - Lending state management

eth/downloader Integration:
- downloader_xdc.go - XDC sync extension
- queue_xdc.go - XDC-specific download queue
- statesync_xdc.go - XDC state synchronization

eth/tracers Support:
- api_xdc.go - XDC tracing APIs
- native/xdc_tracer.go - XDC-specific tracer

Additional Consensus:
- engines/engine_v1.go - XDPoS v1 implementation
- engines/engine_v1_test.go - Engine tests
- core/rawdb/accessors_xdc_indexes.go - XDC database indexes

Configuration & Tools:
- cmd/utils/flags_xdc.go - XDC CLI flags
- eth/ethconfig/config_xdc.go - XDC configuration

Documentation:
- docs/XDPoS.md - Consensus documentation
- docs/XDCx.md - DEX documentation
- docs/XDCxLending.md - Lending documentation
- docs/SYNC.md - Sync guide

Docker & CI:
- Dockerfile.dev - Development image
- Dockerfile.testnet - Testnet image
- docker-compose.yml - Multi-service setup
- .github/workflows/xdc-test.yml - Test workflow
- prometheus.yml - Monitoring config
- scripts/ - Node management scripts

Total files in PR: ~172
2026-01-29 03:56:32 +01:00

400 lines
11 KiB
Go

// Copyright 2021 XDC Network
// This file is part of the XDC library.
package engines
import (
"bytes"
"errors"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
var (
// ErrInvalidTimestampV1 is returned if the timestamp is not in correct range
ErrInvalidTimestampV1 = errors.New("invalid timestamp for XDPoS v1")
// ErrInvalidDifficultyV1 is returned if the difficulty is invalid
ErrInvalidDifficultyV1 = errors.New("invalid difficulty for XDPoS v1")
// ErrUnauthorizedSignerV1 is returned if the signer is not authorized
ErrUnauthorizedSignerV1 = errors.New("unauthorized signer for XDPoS v1")
// ErrMissingSignatureV1 is returned if signature is missing
ErrMissingSignatureV1 = errors.New("missing signature in XDPoS v1 block")
)
// EngineV1 implements the XDPoS v1 consensus engine
type EngineV1 struct {
config *params.XDPoSConfig
db Database
// Snapshot cache
recents *lru
signatures *lru
// Signing
signer common.Address
signFn SignerFn
lock sync.RWMutex
// Block time
period uint64
}
// Database interface for engine
type Database interface {
Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error
Delete(key []byte) error
Has(key []byte) (bool, error)
}
// SignerFn is a signature function
type SignerFn func(account common.Address, data []byte) ([]byte, error)
// lru is a simple LRU cache (placeholder)
type lru struct {
items map[common.Hash]interface{}
lock sync.Mutex
}
func newLRU(size int) *lru {
return &lru{items: make(map[common.Hash]interface{})}
}
func (l *lru) Get(key common.Hash) (interface{}, bool) {
l.lock.Lock()
defer l.lock.Unlock()
v, ok := l.items[key]
return v, ok
}
func (l *lru) Add(key common.Hash, value interface{}) {
l.lock.Lock()
defer l.lock.Unlock()
l.items[key] = value
}
// NewEngineV1 creates a new XDPoS v1 engine
func NewEngineV1(config *params.XDPoSConfig, db Database) *EngineV1 {
period := uint64(2)
if config != nil && config.Period > 0 {
period = config.Period
}
return &EngineV1{
config: config,
db: db,
recents: newLRU(100),
signatures: newLRU(1000),
period: period,
}
}
// Author returns the signer of the block
func (e *EngineV1) Author(header *types.Header) (common.Address, error) {
return ecrecover(header, e.signatures)
}
// VerifyHeader checks whether a header conforms to the consensus rules
func (e *EngineV1) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.verifyHeader(chain, header, nil)
}
// VerifyHeaders verifies a batch of headers concurrently
func (e *EngineV1) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
go func() {
for i, header := range headers {
var parent *types.Header
if i > 0 {
parent = headers[i-1]
}
err := e.verifyHeader(chain, header, parent)
select {
case <-abort:
return
case results <- err:
}
}
}()
return abort, results
}
// verifyHeader checks header validity
func (e *EngineV1) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header) error {
if header.Number == nil {
return errors.New("missing block number")
}
// Don't verify genesis block
if header.Number.Uint64() == 0 {
return nil
}
// Get parent if not provided
if parent == nil {
parent = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
}
// Verify timestamp
if header.Time <= parent.Time {
return ErrInvalidTimestampV1
}
// Verify that the block doesn't come from the future
if header.Time > uint64(time.Now().Unix())+30 {
return consensus.ErrFutureBlock
}
// Verify extra data length
if len(header.Extra) < crypto.SignatureLength {
return ErrMissingSignatureV1
}
// Verify signer
signer, err := ecrecover(header, e.signatures)
if err != nil {
return err
}
// Check if signer is authorized
if !e.isAuthorized(chain, header, signer) {
return ErrUnauthorizedSignerV1
}
return nil
}
// VerifyUncles implements consensus.Engine, always returning an error for any
// uncles as this consensus mechanism doesn't permit uncles.
func (e *EngineV1) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
if len(block.Uncles()) > 0 {
return errors.New("uncles not allowed")
}
return nil
}
// Prepare initializes the consensus fields of a block header
func (e *EngineV1) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
header.Coinbase = e.signer
header.Nonce = types.BlockNonce{}
header.Difficulty = big.NewInt(1)
// Set extra data
if len(header.Extra) < ExtraVanity {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, ExtraVanity-len(header.Extra))...)
}
header.Extra = header.Extra[:ExtraVanity]
// Add space for signature
header.Extra = append(header.Extra, make([]byte, crypto.SignatureLength)...)
// Mix digest is not used
header.MixDigest = common.Hash{}
// Set the correct timestamp
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
header.Time = parent.Time + e.period
return nil
}
// Finalize implements consensus.Engine
func (e *EngineV1) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
// Accumulate block rewards
e.accumulateRewards(chain, state, header)
}
// FinalizeAndAssemble implements consensus.Engine
func (e *EngineV1) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) {
// Finalize block
e.Finalize(chain, header, state, txs, uncles, withdrawals)
// Compute state root
header.Root = state.IntermediateRoot(true)
// Assemble and return the final block
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil
}
// Seal implements consensus.Engine
func (e *EngineV1) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header()
// Don't seal genesis block
if header.Number.Uint64() == 0 {
return errors.New("cannot seal genesis block")
}
e.lock.RLock()
signer, signFn := e.signer, e.signFn
e.lock.RUnlock()
// Sign the header
sighash, err := signHash(header)
if err != nil {
return err
}
signature, err := signFn(signer, sighash)
if err != nil {
return err
}
// Copy signature to extra data
copy(header.Extra[len(header.Extra)-crypto.SignatureLength:], signature)
select {
case results <- block.WithSeal(header):
case <-stop:
return nil
}
return nil
}
// SealHash returns the hash of a block prior to it being sealed
func (e *EngineV1) SealHash(header *types.Header) common.Hash {
return sigHash(header)
}
// CalcDifficulty returns the difficulty for a block
func (e *EngineV1) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return big.NewInt(1)
}
// APIs returns the RPC APIs this consensus engine provides
func (e *EngineV1) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return nil
}
// Close implements consensus.Engine
func (e *EngineV1) Close() error {
return nil
}
// Authorize injects a private key for signing
func (e *EngineV1) Authorize(signer common.Address, signFn SignerFn) {
e.lock.Lock()
defer e.lock.Unlock()
e.signer = signer
e.signFn = signFn
}
// isAuthorized checks if a signer is authorized
func (e *EngineV1) isAuthorized(chain consensus.ChainHeaderReader, header *types.Header, signer common.Address) bool {
// Get snapshot at parent
// Check if signer is in the validator set
// For now, return true
return true
}
// accumulateRewards accumulates the block and uncle rewards
func (e *EngineV1) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header) {
// Block reward is handled by the foundation reward contract
log.Debug("Accumulating rewards", "block", header.Number)
}
// ecrecover extracts the signer from a header
func ecrecover(header *types.Header, sigcache *lru) (common.Address, error) {
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
if len(header.Extra) < crypto.SignatureLength {
return common.Address{}, ErrMissingSignatureV1
}
signature := header.Extra[len(header.Extra)-crypto.SignatureLength:]
// Recover the public key
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
if err != nil {
return common.Address{}, err
}
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}
// sigHash returns the hash to be signed
func sigHash(header *types.Header) common.Hash {
return sigHashWithExtra(header, header.Extra[:len(header.Extra)-crypto.SignatureLength])
}
// sigHashWithExtra returns the hash to be signed with specific extra data
func sigHashWithExtra(header *types.Header, extra []byte) common.Hash {
type sigHeader struct {
ParentHash common.Hash
UncleHash common.Hash
Coinbase common.Address
Root common.Hash
TxHash common.Hash
ReceiptHash common.Hash
Bloom types.Bloom
Difficulty *big.Int
Number *big.Int
GasLimit uint64
GasUsed uint64
Time uint64
Extra []byte
MixDigest common.Hash
Nonce types.BlockNonce
}
data, _ := rlp.EncodeToBytes(&sigHeader{
ParentHash: header.ParentHash,
UncleHash: header.UncleHash,
Coinbase: header.Coinbase,
Root: header.Root,
TxHash: header.TxHash,
ReceiptHash: header.ReceiptHash,
Bloom: header.Bloom,
Difficulty: header.Difficulty,
Number: header.Number,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Time: header.Time,
Extra: extra,
MixDigest: header.MixDigest,
Nonce: header.Nonce,
})
return crypto.Keccak256Hash(data)
}
// signHash returns a hash for signing
func signHash(header *types.Header) ([]byte, error) {
return sigHash(header).Bytes(), nil
}
// ExtraVanity is the fixed number of extra-data prefix bytes
const ExtraVanity = 32
// rpc.API placeholder
type rpc struct{}
type API struct{}