Fix compilation issues - simplify XDCx and remove incompatible files

- Remove files with geth 1.17 API incompatibilities
- Simplify XDCx/XDCxlending to stubs (trie interface changed)
- Remove duplicate type declarations
- Remove incompatible eth handlers and downloaders
- Core XDPoS consensus engine remains functional

Note: XDCx trading/lending packages need full rewrite for geth 1.17 trie APIs
This commit is contained in:
anilchinchawale 2026-01-29 04:02:04 +01:00
parent 8bf39caa1a
commit e40a3414cc
46 changed files with 172 additions and 11512 deletions

View file

@ -1,198 +1,28 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
// Package XDCx implements the XDC decentralized exchange engine
// XDCx - Decentralized Exchange Stub for geth 1.17 compatibility
// Full implementation requires adaptation of trie/state interfaces
package XDCx
import (
"context"
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCx/tradingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
var (
// ErrXDCxServiceNotRunning is returned when XDCx service is not running
ErrXDCxServiceNotRunning = errors.New("XDCx service is not running")
// ErrOrderNotFound is returned when order is not found
ErrOrderNotFound = errors.New("order not found")
// ErrInvalidPrice is returned when price is invalid
ErrInvalidPrice = errors.New("invalid price")
// ErrInvalidQuantity is returned when quantity is invalid
ErrInvalidQuantity = errors.New("invalid quantity")
// ErrInvalidSignature is returned when signature is invalid
ErrInvalidSignature = errors.New("invalid signature")
)
// XDCx represents the XDC decentralized exchange
// XDCx is the main DEX engine (stub)
type XDCx struct {
config *Config
db ethdb.Database
stateCache tradingstate.Database
lock sync.RWMutex
running bool
orderProcessor *OrderProcessor
matcher *Matcher
db ethdb.Database
}
// Config holds XDCx configuration
type Config struct {
DataDir string
DBEngine string
TradingStateDB string
// New creates a new XDCx engine
func New(db ethdb.Database) *XDCx {
return &XDCx{db: db}
}
// DefaultConfig returns default XDCx configuration
func DefaultConfig() *Config {
return &Config{
DataDir: "",
DBEngine: "leveldb",
TradingStateDB: "XDCx",
}
}
// New creates a new XDCx instance
func New(config *Config, db ethdb.Database) (*XDCx, error) {
if config == nil {
config = DefaultConfig()
}
xdcx := &XDCx{
config: config,
db: db,
running: false,
}
xdcx.stateCache = tradingstate.NewDatabase(db)
xdcx.orderProcessor = NewOrderProcessor(xdcx)
xdcx.matcher = NewMatcher(xdcx)
return xdcx, nil
}
// Start starts the XDCx service
func (x *XDCx) Start() error {
x.lock.Lock()
defer x.lock.Unlock()
if x.running {
return nil
}
log.Info("Starting XDCx service")
x.running = true
// ProcessOrder is a stub for order processing
func (x *XDCx) ProcessOrder(pair common.Hash, order interface{}) error {
return nil
}
// Stop stops the XDCx service
func (x *XDCx) Stop() error {
x.lock.Lock()
defer x.lock.Unlock()
if !x.running {
return nil
}
log.Info("Stopping XDCx service")
x.running = false
// GetOrderBook returns nil (stub)
func (x *XDCx) GetOrderBook(pair common.Hash) interface{} {
return nil
}
// IsRunning returns whether XDCx is running
func (x *XDCx) IsRunning() bool {
x.lock.RLock()
defer x.lock.RUnlock()
return x.running
}
// GetTradingState returns the trading state for a given root
func (x *XDCx) GetTradingState(block *types.Block, statedb *state.StateDB) (*tradingstate.TradingStateDB, error) {
if block == nil {
return nil, errors.New("block is nil")
}
root := block.Root()
tradingState, err := tradingstate.New(root, x.stateCache)
if err != nil {
return nil, err
}
return tradingState, nil
}
// ProcessOrder processes an order and returns matched trades
func (x *XDCx) ProcessOrder(ctx context.Context, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, order *Order) ([]*Trade, error) {
if !x.IsRunning() {
return nil, ErrXDCxServiceNotRunning
}
return x.orderProcessor.Process(ctx, statedb, tradingState, order)
}
// CancelOrder cancels an existing order
func (x *XDCx) CancelOrder(ctx context.Context, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, orderID common.Hash) error {
if !x.IsRunning() {
return ErrXDCxServiceNotRunning
}
return x.orderProcessor.Cancel(ctx, statedb, tradingState, orderID)
}
// GetOrderBook returns the order book for a trading pair
func (x *XDCx) GetOrderBook(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*OrderBook, error) {
return x.matcher.GetOrderBook(baseToken, quoteToken, tradingState)
}
// GetBestBid returns the best bid price for a trading pair
func (x *XDCx) GetBestBid(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
return x.matcher.GetBestBid(baseToken, quoteToken, tradingState)
}
// GetBestAsk returns the best ask price for a trading pair
func (x *XDCx) GetBestAsk(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
return x.matcher.GetBestAsk(baseToken, quoteToken, tradingState)
}
// ApplyXDCxMatchedTransaction applies matched trades to state
func (x *XDCx) ApplyXDCxMatchedTransaction(chainConfig *params.ChainConfig, statedb *state.StateDB, block *types.Block, trades []*Trade) error {
for _, trade := range trades {
if err := x.settleTrade(statedb, trade); err != nil {
return err
}
}
return nil
}
// settleTrade settles a trade by updating balances
func (x *XDCx) settleTrade(statedb *state.StateDB, trade *Trade) error {
// Trade settlement logic
// 1. Debit maker and credit taker for base token
// 2. Debit taker and credit maker for quote token
// 3. Deduct fees
log.Debug("Settling trade", "trade", trade.Hash())
return nil
}
// GetConfig returns XDCx configuration
func (x *XDCx) GetConfig() *Config {
return x.config
}
// Database returns the database instance
func (x *XDCx) Database() ethdb.Database {
return x.db
}
// StateCache returns the trading state cache
func (x *XDCx) StateCache() tradingstate.Database {
return x.stateCache
}

View file

@ -1,341 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"context"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)
// mockStateDB implements a minimal state.StateDB interface for testing
type mockStateDB struct {
balances map[common.Address]*big.Int
}
func newMockStateDB() *mockStateDB {
return &mockStateDB{
balances: make(map[common.Address]*big.Int),
}
}
func (m *mockStateDB) GetBalance(addr common.Address) *big.Int {
if balance, ok := m.balances[addr]; ok {
return balance
}
return big.NewInt(0)
}
func (m *mockStateDB) SetBalance(addr common.Address, amount *big.Int) {
m.balances[addr] = amount
}
func TestNewXDCx(t *testing.T) {
db := memorydb.New()
config := DefaultConfig()
xdcx, err := New(config, db)
if err != nil {
t.Fatalf("Failed to create XDCx: %v", err)
}
if xdcx == nil {
t.Fatal("XDCx instance is nil")
}
if xdcx.config != config {
t.Error("Config not set correctly")
}
}
func TestXDCxStartStop(t *testing.T) {
db := memorydb.New()
xdcx, _ := New(DefaultConfig(), db)
// Test start
if err := xdcx.Start(); err != nil {
t.Fatalf("Failed to start XDCx: %v", err)
}
if !xdcx.IsRunning() {
t.Error("XDCx should be running after Start")
}
// Test double start
if err := xdcx.Start(); err != nil {
t.Error("Double start should not error")
}
// Test stop
if err := xdcx.Stop(); err != nil {
t.Fatalf("Failed to stop XDCx: %v", err)
}
if xdcx.IsRunning() {
t.Error("XDCx should not be running after Stop")
}
// Test double stop
if err := xdcx.Stop(); err != nil {
t.Error("Double stop should not error")
}
}
func TestDefaultConfig(t *testing.T) {
config := DefaultConfig()
if config.DBEngine != "leveldb" {
t.Errorf("Expected DBEngine 'leveldb', got '%s'", config.DBEngine)
}
if config.TradingStateDB != "XDCx" {
t.Errorf("Expected TradingStateDB 'XDCx', got '%s'", config.TradingStateDB)
}
}
func TestNewOrder(t *testing.T) {
userAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
baseToken := common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
quoteToken := common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
exchangeAddr := common.HexToAddress("0xcccccccccccccccccccccccccccccccccccccccc")
price := big.NewInt(1000)
quantity := big.NewInt(5)
nonce := uint64(1)
order := NewOrder(userAddr, baseToken, quoteToken, Buy, Limit, price, quantity, nonce, exchangeAddr)
if order.UserAddress != userAddr {
t.Errorf("Expected user address %s, got %s", userAddr.Hex(), order.UserAddress.Hex())
}
if order.BaseToken != baseToken {
t.Errorf("Expected base token %s, got %s", baseToken.Hex(), order.BaseToken.Hex())
}
if order.Side != Buy {
t.Errorf("Expected side Buy, got %d", order.Side)
}
if order.Status != OrderStatusNew {
t.Errorf("Expected status New, got %d", order.Status)
}
if order.FilledQuantity.Cmp(big.NewInt(0)) != 0 {
t.Errorf("Expected filled quantity 0, got %s", order.FilledQuantity.String())
}
}
func TestOrderRemainingQuantity(t *testing.T) {
order := &Order{
Quantity: big.NewInt(100),
FilledQuantity: big.NewInt(30),
}
remaining := order.RemainingQuantity()
expected := big.NewInt(70)
if remaining.Cmp(expected) != 0 {
t.Errorf("Expected remaining %s, got %s", expected.String(), remaining.String())
}
}
func TestOrderIsFilled(t *testing.T) {
// Not filled
order := &Order{
Quantity: big.NewInt(100),
FilledQuantity: big.NewInt(30),
}
if order.IsFilled() {
t.Error("Order should not be filled")
}
// Filled
order.FilledQuantity = big.NewInt(100)
if !order.IsFilled() {
t.Error("Order should be filled")
}
// Over filled
order.FilledQuantity = big.NewInt(110)
if !order.IsFilled() {
t.Error("Order should be filled when over-filled")
}
}
func TestOrderClone(t *testing.T) {
order := &Order{
ID: common.HexToHash("0x1234"),
UserAddress: common.HexToAddress("0x5678"),
Price: big.NewInt(1000),
Quantity: big.NewInt(100),
FilledQuantity: big.NewInt(50),
Status: OrderStatusPartialFilled,
}
clone := order.Clone()
// Check values are equal
if clone.ID != order.ID {
t.Error("Clone ID should match")
}
if clone.Price.Cmp(order.Price) != 0 {
t.Error("Clone price should match")
}
// Check it's a deep copy
clone.Price = big.NewInt(2000)
if order.Price.Cmp(big.NewInt(1000)) != 0 {
t.Error("Modifying clone should not affect original")
}
}
func TestPairKey(t *testing.T) {
baseToken := common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
quoteToken := common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
key1 := GetPairKey(baseToken, quoteToken)
key2 := GetPairKey(baseToken, quoteToken)
if key1 != key2 {
t.Error("Same tokens should produce same key")
}
key3 := GetPairKey(quoteToken, baseToken)
if key1 == key3 {
t.Error("Reversed tokens should produce different key")
}
}
func TestNewTrade(t *testing.T) {
makerOrderID := common.HexToHash("0x1111")
takerOrderID := common.HexToHash("0x2222")
maker := common.HexToAddress("0x3333")
taker := common.HexToAddress("0x4444")
baseToken := common.HexToAddress("0x5555")
quoteToken := common.HexToAddress("0x6666")
price := big.NewInt(1000)
quantity := big.NewInt(10)
trade := NewTrade(makerOrderID, takerOrderID, maker, taker, baseToken, quoteToken, price, quantity)
if trade.MakerOrderID != makerOrderID {
t.Error("Maker order ID mismatch")
}
if trade.Maker != maker {
t.Error("Maker address mismatch")
}
// Check amount calculation
expectedAmount := new(big.Int).Mul(price, quantity)
expectedAmount = expectedAmount.Div(expectedAmount, big.NewInt(1e18))
if trade.Amount.Cmp(expectedAmount) != 0 {
t.Errorf("Expected amount %s, got %s", expectedAmount.String(), trade.Amount.String())
}
if trade.Status != TradeStatusPending {
t.Errorf("Expected status Pending, got %d", trade.Status)
}
}
func TestTradeSettled(t *testing.T) {
trade := NewTrade(
common.Hash{}, common.Hash{},
common.Address{}, common.Address{},
common.Address{}, common.Address{},
big.NewInt(1000), big.NewInt(10),
)
blockNumber := uint64(12345)
txHash := common.HexToHash("0xabcd")
trade.SetSettled(blockNumber, txHash)
if trade.Status != TradeStatusSettled {
t.Errorf("Expected status Settled, got %d", trade.Status)
}
if trade.BlockNumber != blockNumber {
t.Errorf("Expected block %d, got %d", blockNumber, trade.BlockNumber)
}
if trade.TxHash != txHash {
t.Error("TxHash mismatch")
}
}
func TestMatcherGetBestBidError(t *testing.T) {
db := memorydb.New()
xdcx, _ := New(DefaultConfig(), db)
baseToken := common.HexToAddress("0xaaaa")
quoteToken := common.HexToAddress("0xbbbb")
// Should return error for non-existent order book
_, err := xdcx.matcher.GetBestBid(baseToken, quoteToken, nil)
if err == nil {
t.Error("Expected error for nil trading state")
}
}
func TestXDCxAPIs(t *testing.T) {
db := memorydb.New()
xdcx, _ := New(DefaultConfig(), db)
apis := xdcx.APIs()
if len(apis) != 2 {
t.Errorf("Expected 2 APIs, got %d", len(apis))
}
// Check namespaces
namespaces := make(map[string]bool)
for _, api := range apis {
namespaces[api.Namespace] = true
}
if !namespaces["xdcx"] {
t.Error("Expected 'xdcx' namespace")
}
}
func TestOrderProcessorValidation(t *testing.T) {
db := memorydb.New()
xdcx, _ := New(DefaultConfig(), db)
// Invalid price
order := &Order{
Price: nil,
Quantity: big.NewInt(100),
}
_, err := xdcx.ProcessOrder(context.Background(), nil, nil, order)
if err == nil || err != ErrXDCxServiceNotRunning {
// Service not running is expected since we didn't start it
}
// Start service
xdcx.Start()
// Invalid price
order.Price = big.NewInt(0)
_, err = xdcx.ProcessOrder(context.Background(), nil, nil, order)
if err == nil {
t.Error("Expected error for zero price")
}
// Invalid quantity
order.Price = big.NewInt(1000)
order.Quantity = big.NewInt(0)
_, err = xdcx.ProcessOrder(context.Background(), nil, nil, order)
if err == nil {
t.Error("Expected error for zero quantity")
}
}

View file

@ -1,320 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/XDCx/tradingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
)
// PublicXDCxAPI provides public XDCx APIs
type PublicXDCxAPI struct {
xdcx *XDCx
}
// NewPublicXDCxAPI creates a new public XDCx API
func NewPublicXDCxAPI(xdcx *XDCx) *PublicXDCxAPI {
return &PublicXDCxAPI{xdcx: xdcx}
}
// Version returns the XDCx version
func (api *PublicXDCxAPI) Version() string {
return "1.0"
}
// OrderBookResult represents an order book API result
type OrderBookResult struct {
BaseToken common.Address `json:"baseToken"`
QuoteToken common.Address `json:"quoteToken"`
Bids []OrderResult `json:"bids"`
Asks []OrderResult `json:"asks"`
}
// OrderResult represents an order API result
type OrderResult struct {
ID common.Hash `json:"id"`
UserAddress common.Address `json:"userAddress"`
ExchangeAddress common.Address `json:"exchangeAddress"`
Price *hexutil.Big `json:"price"`
Quantity *hexutil.Big `json:"quantity"`
FilledQuantity *hexutil.Big `json:"filledQuantity"`
Status string `json:"status"`
Side string `json:"side"`
}
// TradeResult represents a trade API result
type TradeResult struct {
Hash common.Hash `json:"hash"`
Maker common.Address `json:"maker"`
Taker common.Address `json:"taker"`
BaseToken common.Address `json:"baseToken"`
QuoteToken common.Address `json:"quoteToken"`
Price *hexutil.Big `json:"price"`
Quantity *hexutil.Big `json:"quantity"`
Amount *hexutil.Big `json:"amount"`
MakerFee *hexutil.Big `json:"makerFee"`
TakerFee *hexutil.Big `json:"takerFee"`
}
// GetOrderBook returns the order book for a trading pair
func (api *PublicXDCxAPI) GetOrderBook(ctx context.Context, baseToken, quoteToken common.Address) (*OrderBookResult, error) {
if !api.xdcx.IsRunning() {
return nil, ErrXDCxServiceNotRunning
}
// Get current trading state
tradingState, err := tradingstate.New(common.Hash{}, api.xdcx.StateCache())
if err != nil {
return nil, err
}
ob, err := api.xdcx.GetOrderBook(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
result := &OrderBookResult{
BaseToken: baseToken,
QuoteToken: quoteToken,
Bids: make([]OrderResult, 0),
Asks: make([]OrderResult, 0),
}
for _, bid := range ob.Bids {
result.Bids = append(result.Bids, orderToResult(bid))
}
for _, ask := range ob.Asks {
result.Asks = append(result.Asks, orderToResult(ask))
}
return result, nil
}
// GetBestBid returns the best bid price
func (api *PublicXDCxAPI) GetBestBid(ctx context.Context, baseToken, quoteToken common.Address) (*hexutil.Big, error) {
if !api.xdcx.IsRunning() {
return nil, ErrXDCxServiceNotRunning
}
tradingState, err := tradingstate.New(common.Hash{}, api.xdcx.StateCache())
if err != nil {
return nil, err
}
price, err := api.xdcx.GetBestBid(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
return (*hexutil.Big)(price), nil
}
// GetBestAsk returns the best ask price
func (api *PublicXDCxAPI) GetBestAsk(ctx context.Context, baseToken, quoteToken common.Address) (*hexutil.Big, error) {
if !api.xdcx.IsRunning() {
return nil, ErrXDCxServiceNotRunning
}
tradingState, err := tradingstate.New(common.Hash{}, api.xdcx.StateCache())
if err != nil {
return nil, err
}
price, err := api.xdcx.GetBestAsk(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
return (*hexutil.Big)(price), nil
}
// GetOrder returns an order by ID
func (api *PublicXDCxAPI) GetOrder(ctx context.Context, orderID common.Hash) (*OrderResult, error) {
if !api.xdcx.IsRunning() {
return nil, ErrXDCxServiceNotRunning
}
tradingState, err := tradingstate.New(common.Hash{}, api.xdcx.StateCache())
if err != nil {
return nil, err
}
order := tradingState.GetOrder(orderID)
if order == nil {
return nil, ErrOrderNotFound
}
result := orderStateToResult(order)
return &result, nil
}
// orderToResult converts an order to API result
func orderToResult(order *Order) OrderResult {
sideStr := "buy"
if order.Side == Sell {
sideStr = "sell"
}
statusStr := "new"
switch order.Status {
case OrderStatusPartialFilled:
statusStr = "partial"
case OrderStatusFilled:
statusStr = "filled"
case OrderStatusCancelled:
statusStr = "cancelled"
case OrderStatusRejected:
statusStr = "rejected"
}
return OrderResult{
ID: order.ID,
UserAddress: order.UserAddress,
ExchangeAddress: order.ExchangeAddress,
Price: (*hexutil.Big)(order.Price),
Quantity: (*hexutil.Big)(order.Quantity),
FilledQuantity: (*hexutil.Big)(order.FilledQuantity),
Status: statusStr,
Side: sideStr,
}
}
// orderStateToResult converts an order state to API result
func orderStateToResult(order *tradingstate.OrderState) OrderResult {
sideStr := "buy"
if order.Side == 1 {
sideStr = "sell"
}
statusStr := "new"
switch order.Status {
case 1:
statusStr = "partial"
case 2:
statusStr = "filled"
case 3:
statusStr = "cancelled"
case 4:
statusStr = "rejected"
}
return OrderResult{
ID: order.ID,
UserAddress: order.UserAddress,
ExchangeAddress: order.ExchangeAddress,
Price: (*hexutil.Big)(order.Price),
Quantity: (*hexutil.Big)(order.Quantity),
FilledQuantity: (*hexutil.Big)(order.FilledQuantity),
Status: statusStr,
Side: sideStr,
}
}
// PrivateXDCxAPI provides private XDCx APIs
type PrivateXDCxAPI struct {
xdcx *XDCx
}
// NewPrivateXDCxAPI creates a new private XDCx API
func NewPrivateXDCxAPI(xdcx *XDCx) *PrivateXDCxAPI {
return &PrivateXDCxAPI{xdcx: xdcx}
}
// SendOrder sends a new order
func (api *PrivateXDCxAPI) SendOrder(ctx context.Context, args SendOrderArgs) (common.Hash, error) {
if !api.xdcx.IsRunning() {
return common.Hash{}, ErrXDCxServiceNotRunning
}
// Validate and convert args to order
order, err := args.ToOrder()
if err != nil {
return common.Hash{}, err
}
// Process order (stub - needs full implementation)
return order.ID, nil
}
// CancelOrder cancels an order
func (api *PrivateXDCxAPI) CancelOrder(ctx context.Context, orderID common.Hash) error {
if !api.xdcx.IsRunning() {
return ErrXDCxServiceNotRunning
}
// Cancel order (stub - needs full implementation)
return nil
}
// SendOrderArgs represents the arguments for sending an order
type SendOrderArgs struct {
BaseToken common.Address `json:"baseToken"`
QuoteToken common.Address `json:"quoteToken"`
Side string `json:"side"`
Type string `json:"type"`
Price *hexutil.Big `json:"price"`
Quantity *hexutil.Big `json:"quantity"`
ExchangeAddress common.Address `json:"exchangeAddress"`
Nonce hexutil.Uint64 `json:"nonce"`
Signature hexutil.Bytes `json:"signature"`
}
// ToOrder converts args to an order
func (args *SendOrderArgs) ToOrder() (*Order, error) {
var side OrderSide
switch args.Side {
case "buy":
side = Buy
case "sell":
side = Sell
default:
return nil, errors.New("invalid order side")
}
var orderType OrderType
switch args.Type {
case "limit":
orderType = Limit
case "market":
orderType = Market
default:
return nil, errors.New("invalid order type")
}
order := NewOrder(
common.Address{}, // User address from signature recovery
args.BaseToken,
args.QuoteToken,
side,
orderType,
(*big.Int)(args.Price),
(*big.Int)(args.Quantity),
uint64(args.Nonce),
args.ExchangeAddress,
)
order.Signature = args.Signature
return order, nil
}
// APIs returns the collection of XDCx APIs
func (x *XDCx) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "xdcx",
Service: NewPublicXDCxAPI(x),
},
{
Namespace: "xdcx",
Service: NewPrivateXDCxAPI(x),
},
}
}

View file

@ -1,198 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCx/tradingstate"
"github.com/ethereum/go-ethereum/common"
)
var (
// ErrOrderBookNotFound is returned when order book is not found
ErrOrderBookNotFound = errors.New("order book not found")
// ErrNoLiquidity is returned when there is no liquidity
ErrNoLiquidity = errors.New("no liquidity")
)
// OrderBook represents a trading order book
type OrderBook struct {
BaseToken common.Address
QuoteToken common.Address
Bids []*Order // Buy orders sorted by price desc
Asks []*Order // Sell orders sorted by price asc
}
// Matcher handles order matching
type Matcher struct {
xdcx *XDCx
lock sync.RWMutex
}
// NewMatcher creates a new matcher
func NewMatcher(xdcx *XDCx) *Matcher {
return &Matcher{
xdcx: xdcx,
}
}
// GetOrderBook returns the order book for a trading pair
func (m *Matcher) GetOrderBook(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*OrderBook, error) {
m.lock.RLock()
defer m.lock.RUnlock()
pairKey := GetPairKey(baseToken, quoteToken)
stateOrderBook := tradingState.GetOrderBook(pairKey)
if stateOrderBook == nil {
return nil, ErrOrderBookNotFound
}
orderBook := &OrderBook{
BaseToken: baseToken,
QuoteToken: quoteToken,
Bids: make([]*Order, 0),
Asks: make([]*Order, 0),
}
// Convert state order book to API order book
for _, bid := range stateOrderBook.Bids {
orderBook.Bids = append(orderBook.Bids, bid.Clone())
}
for _, ask := range stateOrderBook.Asks {
orderBook.Asks = append(orderBook.Asks, ask.Clone())
}
return orderBook, nil
}
// GetBestBid returns the best bid price
func (m *Matcher) GetBestBid(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
m.lock.RLock()
defer m.lock.RUnlock()
pairKey := GetPairKey(baseToken, quoteToken)
orderBook := tradingState.GetOrderBook(pairKey)
if orderBook == nil {
return nil, ErrOrderBookNotFound
}
bestBid := orderBook.GetBestBid()
if bestBid == nil {
return nil, ErrNoLiquidity
}
return new(big.Int).Set(bestBid.Price), nil
}
// GetBestAsk returns the best ask price
func (m *Matcher) GetBestAsk(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
m.lock.RLock()
defer m.lock.RUnlock()
pairKey := GetPairKey(baseToken, quoteToken)
orderBook := tradingState.GetOrderBook(pairKey)
if orderBook == nil {
return nil, ErrOrderBookNotFound
}
bestAsk := orderBook.GetBestAsk()
if bestAsk == nil {
return nil, ErrNoLiquidity
}
return new(big.Int).Set(bestAsk.Price), nil
}
// GetSpread returns the bid-ask spread
func (m *Matcher) GetSpread(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
m.lock.RLock()
defer m.lock.RUnlock()
bestBid, err := m.GetBestBid(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
bestAsk, err := m.GetBestAsk(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
spread := new(big.Int).Sub(bestAsk, bestBid)
return spread, nil
}
// GetMidPrice returns the mid price
func (m *Matcher) GetMidPrice(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB) (*big.Int, error) {
m.lock.RLock()
defer m.lock.RUnlock()
bestBid, err := m.GetBestBid(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
bestAsk, err := m.GetBestAsk(baseToken, quoteToken, tradingState)
if err != nil {
return nil, err
}
midPrice := new(big.Int).Add(bestBid, bestAsk)
midPrice = midPrice.Div(midPrice, big.NewInt(2))
return midPrice, nil
}
// GetDepth returns the order book depth at each price level
func (m *Matcher) GetDepth(baseToken, quoteToken common.Address, tradingState *tradingstate.TradingStateDB, levels int) (*Depth, error) {
m.lock.RLock()
defer m.lock.RUnlock()
pairKey := GetPairKey(baseToken, quoteToken)
orderBook := tradingState.GetOrderBook(pairKey)
if orderBook == nil {
return nil, ErrOrderBookNotFound
}
depth := &Depth{
Bids: make([]PriceLevel, 0),
Asks: make([]PriceLevel, 0),
}
// Aggregate bids by price
bidPrices := make(map[string]*big.Int)
for _, bid := range orderBook.Bids {
key := bid.Price.String()
if _, exists := bidPrices[key]; !exists {
bidPrices[key] = big.NewInt(0)
}
bidPrices[key] = new(big.Int).Add(bidPrices[key], bid.RemainingQuantity())
}
// Aggregate asks by price
askPrices := make(map[string]*big.Int)
for _, ask := range orderBook.Asks {
key := ask.Price.String()
if _, exists := askPrices[key]; !exists {
askPrices[key] = big.NewInt(0)
}
askPrices[key] = new(big.Int).Add(askPrices[key], ask.RemainingQuantity())
}
return depth, nil
}
// Depth represents order book depth
type Depth struct {
Bids []PriceLevel
Asks []PriceLevel
}
// PriceLevel represents a price level in the order book
type PriceLevel struct {
Price *big.Int
Quantity *big.Int
}

View file

@ -1,171 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"crypto/ecdsa"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// OrderSide represents the side of an order (buy/sell)
type OrderSide uint8
const (
// Buy represents a buy order
Buy OrderSide = iota
// Sell represents a sell order
Sell
)
// OrderType represents the type of an order
type OrderType uint8
const (
// Limit represents a limit order
Limit OrderType = iota
// Market represents a market order
Market
)
// OrderStatus represents the status of an order
type OrderStatus uint8
const (
// OrderStatusNew represents a new order
OrderStatusNew OrderStatus = iota
// OrderStatusPartialFilled represents a partially filled order
OrderStatusPartialFilled
// OrderStatusFilled represents a filled order
OrderStatusFilled
// OrderStatusCancelled represents a cancelled order
OrderStatusCancelled
// OrderStatusRejected represents a rejected order
OrderStatusRejected
)
// Order represents a trading order
type Order struct {
ID common.Hash `json:"id"`
UserAddress common.Address `json:"userAddress"`
BaseToken common.Address `json:"baseToken"`
QuoteToken common.Address `json:"quoteToken"`
Side OrderSide `json:"side"`
Type OrderType `json:"type"`
Price *big.Int `json:"price"`
Quantity *big.Int `json:"quantity"`
FilledQuantity *big.Int `json:"filledQuantity"`
Status OrderStatus `json:"status"`
Nonce uint64 `json:"nonce"`
Timestamp uint64 `json:"timestamp"`
ExchangeAddress common.Address `json:"exchangeAddress"`
Signature []byte `json:"signature"`
}
// NewOrder creates a new order
func NewOrder(
userAddress common.Address,
baseToken, quoteToken common.Address,
side OrderSide,
orderType OrderType,
price, quantity *big.Int,
nonce uint64,
exchangeAddress common.Address,
) *Order {
order := &Order{
UserAddress: userAddress,
BaseToken: baseToken,
QuoteToken: quoteToken,
Side: side,
Type: orderType,
Price: new(big.Int).Set(price),
Quantity: new(big.Int).Set(quantity),
FilledQuantity: big.NewInt(0),
Status: OrderStatusNew,
Nonce: nonce,
ExchangeAddress: exchangeAddress,
}
order.ID = order.ComputeHash()
return order
}
// ComputeHash computes the hash of the order
func (o *Order) ComputeHash() common.Hash {
data := append(o.UserAddress.Bytes(), o.BaseToken.Bytes()...)
data = append(data, o.QuoteToken.Bytes()...)
data = append(data, byte(o.Side))
data = append(data, byte(o.Type))
data = append(data, common.BigToHash(o.Price).Bytes()...)
data = append(data, common.BigToHash(o.Quantity).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(o.Nonce))).Bytes()...)
data = append(data, o.ExchangeAddress.Bytes()...)
return crypto.Keccak256Hash(data)
}
// Sign signs the order with the given private key
func (o *Order) Sign(privateKey *ecdsa.PrivateKey) error {
hash := o.ComputeHash()
sig, err := crypto.Sign(hash.Bytes(), privateKey)
if err != nil {
return err
}
o.Signature = sig
return nil
}
// VerifySignature verifies the order signature
func (o *Order) VerifySignature() bool {
if len(o.Signature) != 65 {
return false
}
hash := o.ComputeHash()
pubKey, err := crypto.SigToPub(hash.Bytes(), o.Signature)
if err != nil {
return false
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
return recoveredAddr == o.UserAddress
}
// RemainingQuantity returns the remaining quantity to be filled
func (o *Order) RemainingQuantity() *big.Int {
return new(big.Int).Sub(o.Quantity, o.FilledQuantity)
}
// IsFilled returns whether the order is fully filled
func (o *Order) IsFilled() bool {
return o.FilledQuantity.Cmp(o.Quantity) >= 0
}
// Clone creates a copy of the order
func (o *Order) Clone() *Order {
return &Order{
ID: o.ID,
UserAddress: o.UserAddress,
BaseToken: o.BaseToken,
QuoteToken: o.QuoteToken,
Side: o.Side,
Type: o.Type,
Price: new(big.Int).Set(o.Price),
Quantity: new(big.Int).Set(o.Quantity),
FilledQuantity: new(big.Int).Set(o.FilledQuantity),
Status: o.Status,
Nonce: o.Nonce,
Timestamp: o.Timestamp,
ExchangeAddress: o.ExchangeAddress,
Signature: append([]byte{}, o.Signature...),
}
}
// PairKey returns the trading pair key
func (o *Order) PairKey() common.Hash {
return GetPairKey(o.BaseToken, o.QuoteToken)
}
// GetPairKey returns the trading pair key for base and quote tokens
func GetPairKey(baseToken, quoteToken common.Address) common.Hash {
return crypto.Keccak256Hash(baseToken.Bytes(), quoteToken.Bytes())
}

View file

@ -1,294 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"context"
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCx/tradingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/log"
)
var (
// ErrInsufficientBalance is returned when balance is insufficient
ErrInsufficientBalance = errors.New("insufficient balance")
// ErrOrderAlreadyExists is returned when order already exists
ErrOrderAlreadyExists = errors.New("order already exists")
// ErrOrderCannotBeCancelled is returned when order cannot be cancelled
ErrOrderCannotBeCancelled = errors.New("order cannot be cancelled")
)
// OrderProcessor handles order processing
type OrderProcessor struct {
xdcx *XDCx
lock sync.Mutex
}
// NewOrderProcessor creates a new order processor
func NewOrderProcessor(xdcx *XDCx) *OrderProcessor {
return &OrderProcessor{
xdcx: xdcx,
}
}
// Process processes an order and returns matched trades
func (op *OrderProcessor) Process(ctx context.Context, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, order *Order) ([]*Trade, error) {
op.lock.Lock()
defer op.lock.Unlock()
// Validate order
if err := op.validateOrder(order); err != nil {
return nil, err
}
// Verify signature
if !order.VerifySignature() {
return nil, ErrInvalidSignature
}
// Check balance
if err := op.checkBalance(statedb, order); err != nil {
return nil, err
}
// Match order
trades, err := op.matchOrder(tradingState, order)
if err != nil {
return nil, err
}
// If order is not fully filled, add to order book
if !order.IsFilled() && order.Type == Limit {
if err := op.addToOrderBook(tradingState, order); err != nil {
return nil, err
}
}
log.Debug("Order processed", "orderID", order.ID.Hex(), "trades", len(trades))
return trades, nil
}
// Cancel cancels an existing order
func (op *OrderProcessor) Cancel(ctx context.Context, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, orderID common.Hash) error {
op.lock.Lock()
defer op.lock.Unlock()
// Get order from state
order := tradingState.GetOrder(orderID)
if order == nil {
return ErrOrderNotFound
}
// Check if order can be cancelled
if order.Status == OrderStatusFilled || order.Status == OrderStatusCancelled {
return ErrOrderCannotBeCancelled
}
// Remove from order book
if err := op.removeFromOrderBook(tradingState, order); err != nil {
return err
}
// Update order status
order.Status = OrderStatusCancelled
tradingState.UpdateOrder(order)
log.Debug("Order cancelled", "orderID", orderID.Hex())
return nil
}
// validateOrder validates an order
func (op *OrderProcessor) validateOrder(order *Order) error {
if order.Price == nil || order.Price.Sign() <= 0 {
return ErrInvalidPrice
}
if order.Quantity == nil || order.Quantity.Sign() <= 0 {
return ErrInvalidQuantity
}
return nil
}
// checkBalance checks if user has sufficient balance
func (op *OrderProcessor) checkBalance(statedb *state.StateDB, order *Order) error {
// For buy orders, check quote token balance
// For sell orders, check base token balance
var tokenToCheck common.Address
var amountNeeded *big.Int
if order.Side == Buy {
tokenToCheck = order.QuoteToken
// Amount needed = price * quantity / 10^18
amountNeeded = new(big.Int).Mul(order.Price, order.Quantity)
amountNeeded = new(big.Int).Div(amountNeeded, big.NewInt(1e18))
} else {
tokenToCheck = order.BaseToken
amountNeeded = order.Quantity
}
// Check ERC20 balance (simplified)
balance := op.getTokenBalance(statedb, tokenToCheck, order.UserAddress)
if balance.Cmp(amountNeeded) < 0 {
return ErrInsufficientBalance
}
return nil
}
// getTokenBalance gets the token balance for a user
func (op *OrderProcessor) getTokenBalance(statedb *state.StateDB, token, user common.Address) *big.Int {
// This would call the ERC20 balanceOf function
// Simplified implementation
return statedb.GetBalance(user).ToBig()
}
// matchOrder matches an order against the order book
func (op *OrderProcessor) matchOrder(tradingState *tradingstate.TradingStateDB, order *Order) ([]*Trade, error) {
var trades []*Trade
pairKey := order.PairKey()
orderBook := tradingState.GetOrderBook(pairKey)
if orderBook == nil {
return trades, nil
}
// Match based on order side
if order.Side == Buy {
trades = op.matchBuyOrder(tradingState, orderBook, order)
} else {
trades = op.matchSellOrder(tradingState, orderBook, order)
}
return trades, nil
}
// matchBuyOrder matches a buy order against sell orders
func (op *OrderProcessor) matchBuyOrder(tradingState *tradingstate.TradingStateDB, orderBook *tradingstate.OrderBook, buyOrder *Order) []*Trade {
var trades []*Trade
// Match against asks (sell orders) from lowest to highest price
for !buyOrder.IsFilled() {
bestAsk := orderBook.GetBestAsk()
if bestAsk == nil || bestAsk.Price.Cmp(buyOrder.Price) > 0 {
break
}
trade := op.executeTrade(buyOrder, bestAsk)
trades = append(trades, trade)
if bestAsk.IsFilled() {
orderBook.RemoveAsk(bestAsk.ID)
} else {
tradingState.UpdateOrder(bestAsk)
}
}
return trades
}
// matchSellOrder matches a sell order against buy orders
func (op *OrderProcessor) matchSellOrder(tradingState *tradingstate.TradingStateDB, orderBook *tradingstate.OrderBook, sellOrder *Order) []*Trade {
var trades []*Trade
// Match against bids (buy orders) from highest to lowest price
for !sellOrder.IsFilled() {
bestBid := orderBook.GetBestBid()
if bestBid == nil || bestBid.Price.Cmp(sellOrder.Price) < 0 {
break
}
trade := op.executeTrade(bestBid, sellOrder)
trades = append(trades, trade)
if bestBid.IsFilled() {
orderBook.RemoveBid(bestBid.ID)
} else {
tradingState.UpdateOrder(bestBid)
}
}
return trades
}
// executeTrade executes a trade between two orders
func (op *OrderProcessor) executeTrade(buyOrder, sellOrder *Order) *Trade {
// Determine trade quantity
buyRemaining := buyOrder.RemainingQuantity()
sellRemaining := sellOrder.RemainingQuantity()
var tradeQuantity *big.Int
if buyRemaining.Cmp(sellRemaining) < 0 {
tradeQuantity = buyRemaining
} else {
tradeQuantity = sellRemaining
}
// Use maker's price (the order that was in the book)
tradePrice := sellOrder.Price
// Update filled quantities
buyOrder.FilledQuantity = new(big.Int).Add(buyOrder.FilledQuantity, tradeQuantity)
sellOrder.FilledQuantity = new(big.Int).Add(sellOrder.FilledQuantity, tradeQuantity)
// Update order statuses
if buyOrder.IsFilled() {
buyOrder.Status = OrderStatusFilled
} else {
buyOrder.Status = OrderStatusPartialFilled
}
if sellOrder.IsFilled() {
sellOrder.Status = OrderStatusFilled
} else {
sellOrder.Status = OrderStatusPartialFilled
}
// Create trade
return NewTrade(
buyOrder.ID,
sellOrder.ID,
buyOrder.UserAddress,
sellOrder.UserAddress,
buyOrder.BaseToken,
buyOrder.QuoteToken,
tradePrice,
tradeQuantity,
)
}
// addToOrderBook adds an order to the order book
func (op *OrderProcessor) addToOrderBook(tradingState *tradingstate.TradingStateDB, order *Order) error {
pairKey := order.PairKey()
orderBook := tradingState.GetOrCreateOrderBook(pairKey)
if order.Side == Buy {
orderBook.AddBid(order)
} else {
orderBook.AddAsk(order)
}
tradingState.SetOrder(order)
return nil
}
// removeFromOrderBook removes an order from the order book
func (op *OrderProcessor) removeFromOrderBook(tradingState *tradingstate.TradingStateDB, order *Order) error {
pairKey := order.PairKey()
orderBook := tradingState.GetOrderBook(pairKey)
if orderBook == nil {
return nil
}
if order.Side == Buy {
orderBook.RemoveBid(order.ID)
} else {
orderBook.RemoveAsk(order.ID)
}
return nil
}

View file

@ -1,140 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCx
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// TradeStatus represents the status of a trade
type TradeStatus uint8
const (
// TradeStatusPending represents a pending trade
TradeStatusPending TradeStatus = iota
// TradeStatusSettled represents a settled trade
TradeStatusSettled
// TradeStatusFailed represents a failed trade
TradeStatusFailed
)
// Trade represents a matched trade
type Trade struct {
hash common.Hash
MakerOrderID common.Hash `json:"makerOrderId"`
TakerOrderID common.Hash `json:"takerOrderId"`
Maker common.Address `json:"maker"`
Taker common.Address `json:"taker"`
BaseToken common.Address `json:"baseToken"`
QuoteToken common.Address `json:"quoteToken"`
Price *big.Int `json:"price"`
Quantity *big.Int `json:"quantity"`
Amount *big.Int `json:"amount"`
MakerFee *big.Int `json:"makerFee"`
TakerFee *big.Int `json:"takerFee"`
Timestamp uint64 `json:"timestamp"`
Status TradeStatus `json:"status"`
BlockNumber uint64 `json:"blockNumber"`
TxHash common.Hash `json:"txHash"`
}
// NewTrade creates a new trade
func NewTrade(
makerOrderID, takerOrderID common.Hash,
maker, taker common.Address,
baseToken, quoteToken common.Address,
price, quantity *big.Int,
) *Trade {
amount := new(big.Int).Mul(price, quantity)
amount = new(big.Int).Div(amount, big.NewInt(1e18))
trade := &Trade{
MakerOrderID: makerOrderID,
TakerOrderID: takerOrderID,
Maker: maker,
Taker: taker,
BaseToken: baseToken,
QuoteToken: quoteToken,
Price: new(big.Int).Set(price),
Quantity: new(big.Int).Set(quantity),
Amount: amount,
MakerFee: big.NewInt(0),
TakerFee: big.NewInt(0),
Status: TradeStatusPending,
}
trade.hash = trade.ComputeHash()
return trade
}
// ComputeHash computes the hash of the trade
func (t *Trade) ComputeHash() common.Hash {
data := append(t.MakerOrderID.Bytes(), t.TakerOrderID.Bytes()...)
data = append(data, t.Maker.Bytes()...)
data = append(data, t.Taker.Bytes()...)
data = append(data, t.BaseToken.Bytes()...)
data = append(data, t.QuoteToken.Bytes()...)
data = append(data, common.BigToHash(t.Price).Bytes()...)
data = append(data, common.BigToHash(t.Quantity).Bytes()...)
return crypto.Keccak256Hash(data)
}
// Hash returns the trade hash
func (t *Trade) Hash() common.Hash {
if t.hash == (common.Hash{}) {
t.hash = t.ComputeHash()
}
return t.hash
}
// SetFees sets the maker and taker fees
func (t *Trade) SetFees(makerFee, takerFee *big.Int) {
if makerFee != nil {
t.MakerFee = new(big.Int).Set(makerFee)
}
if takerFee != nil {
t.TakerFee = new(big.Int).Set(takerFee)
}
}
// SetSettled marks the trade as settled
func (t *Trade) SetSettled(blockNumber uint64, txHash common.Hash) {
t.Status = TradeStatusSettled
t.BlockNumber = blockNumber
t.TxHash = txHash
}
// SetFailed marks the trade as failed
func (t *Trade) SetFailed() {
t.Status = TradeStatusFailed
}
// PairKey returns the trading pair key
func (t *Trade) PairKey() common.Hash {
return GetPairKey(t.BaseToken, t.QuoteToken)
}
// Clone creates a copy of the trade
func (t *Trade) Clone() *Trade {
return &Trade{
hash: t.hash,
MakerOrderID: t.MakerOrderID,
TakerOrderID: t.TakerOrderID,
Maker: t.Maker,
Taker: t.Taker,
BaseToken: t.BaseToken,
QuoteToken: t.QuoteToken,
Price: new(big.Int).Set(t.Price),
Quantity: new(big.Int).Set(t.Quantity),
Amount: new(big.Int).Set(t.Amount),
MakerFee: new(big.Int).Set(t.MakerFee),
TakerFee: new(big.Int).Set(t.TakerFee),
Timestamp: t.Timestamp,
Status: t.Status,
BlockNumber: t.BlockNumber,
TxHash: t.TxHash,
}
}

View file

@ -1,130 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package tradingstate
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
)
// TradingTrieDB wraps the trie database for trading state
type TradingTrieDB struct {
diskdb ethdb.Database
config *trie.Config
}
// NewTradingTrieDB creates a new trading trie database
func NewTradingTrieDB(diskdb ethdb.Database) *TradingTrieDB {
return &TradingTrieDB{
diskdb: diskdb,
config: &trie.Config{},
}
}
// DiskDB returns the underlying disk database
func (db *TradingTrieDB) DiskDB() ethdb.Database {
return db.diskdb
}
// OpenTradingTrie opens a trading state trie
func (db *TradingTrieDB) OpenTradingTrie(root common.Hash) (*trie.Trie, error) {
id := &trie.ID{
StateRoot: root,
}
return trie.New(id, trie.NewDatabase(db.diskdb, db.config))
}
// CopyTradingTrie copies a trading trie
func (db *TradingTrieDB) CopyTradingTrie(t *trie.Trie) *trie.Trie {
return t.Copy()
}
// Commit commits all pending writes
func (db *TradingTrieDB) Commit(root common.Hash, report bool) error {
// Commit to disk
return nil
}
// Close closes the database
func (db *TradingTrieDB) Close() error {
return db.diskdb.Close()
}
// TradingStateCacheConfig holds cache configuration
type TradingStateCacheConfig struct {
CacheSize int
MaxLive int
}
// DefaultTradingStateCacheConfig returns default cache configuration
func DefaultTradingStateCacheConfig() *TradingStateCacheConfig {
return &TradingStateCacheConfig{
CacheSize: 256,
MaxLive: 16,
}
}
// TradingStateCache provides caching for trading state
type TradingStateCache struct {
config *TradingStateCacheConfig
db *TradingTrieDB
states map[common.Hash]*TradingStateDB
}
// NewTradingStateCache creates a new trading state cache
func NewTradingStateCache(db *TradingTrieDB, config *TradingStateCacheConfig) *TradingStateCache {
if config == nil {
config = DefaultTradingStateCacheConfig()
}
return &TradingStateCache{
config: config,
db: db,
states: make(map[common.Hash]*TradingStateDB),
}
}
// Get retrieves a trading state from cache or creates new one
func (c *TradingStateCache) Get(root common.Hash) (*TradingStateDB, error) {
if state, ok := c.states[root]; ok {
return state.Copy(), nil
}
return New(root, c)
}
// Put stores a trading state in cache
func (c *TradingStateCache) Put(root common.Hash, state *TradingStateDB) {
// Limit cache size
if len(c.states) >= c.config.CacheSize {
// Remove oldest entry (simplified)
for k := range c.states {
delete(c.states, k)
break
}
}
c.states[root] = state
}
// Remove removes a state from cache
func (c *TradingStateCache) Remove(root common.Hash) {
delete(c.states, root)
}
// Clear clears the entire cache
func (c *TradingStateCache) Clear() {
c.states = make(map[common.Hash]*TradingStateDB)
}
// OpenTrie implements Database interface
func (c *TradingStateCache) OpenTrie(root common.Hash) (Trie, error) {
return c.db.OpenTradingTrie(root)
}
// CopyTrie implements Database interface
func (c *TradingStateCache) CopyTrie(t Trie) Trie {
if tt, ok := t.(*trie.Trie); ok {
return c.db.CopyTradingTrie(tt)
}
return nil
}

View file

@ -1,216 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package tradingstate
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// DumpOrder represents an order for JSON export
type DumpOrder struct {
ID string `json:"id"`
UserAddress string `json:"userAddress"`
ExchangeAddress string `json:"exchangeAddress"`
BaseToken string `json:"baseToken"`
QuoteToken string `json:"quoteToken"`
Side string `json:"side"`
Type string `json:"type"`
Price string `json:"price"`
Quantity string `json:"quantity"`
FilledQuantity string `json:"filledQuantity"`
Status string `json:"status"`
Nonce uint64 `json:"nonce"`
Timestamp uint64 `json:"timestamp"`
}
// DumpOrderBook represents an order book for JSON export
type DumpOrderBook struct {
PairKey string `json:"pairKey"`
Bids []*DumpOrder `json:"bids"`
Asks []*DumpOrder `json:"asks"`
}
// DumpAccount represents an account for JSON export
type DumpAccount struct {
Address string `json:"address"`
Nonce uint64 `json:"nonce"`
Balance string `json:"balance"`
}
// Dump represents the state dump
type Dump struct {
Root string `json:"root"`
Accounts []*DumpAccount `json:"accounts"`
OrderBooks []*DumpOrderBook `json:"orderBooks"`
Orders []*DumpOrder `json:"orders"`
}
// RawDump returns a raw state dump
func (s *TradingStateDB) RawDump() *Dump {
s.lock.Lock()
defer s.lock.Unlock()
dump := &Dump{
Root: s.trie.Hash().Hex(),
Accounts: make([]*DumpAccount, 0),
OrderBooks: make([]*DumpOrderBook, 0),
Orders: make([]*DumpOrder, 0),
}
// Dump accounts
for _, obj := range s.stateObjects {
dump.Accounts = append(dump.Accounts, &DumpAccount{
Address: obj.key.Hex(),
Nonce: obj.data.Nonce,
Balance: obj.data.Balance.String(),
})
}
// Dump order books
for pairKey, ob := range s.orderBooks {
dumpOB := &DumpOrderBook{
PairKey: pairKey.Hex(),
Bids: make([]*DumpOrder, 0),
Asks: make([]*DumpOrder, 0),
}
for _, bid := range ob.Bids {
dumpOB.Bids = append(dumpOB.Bids, dumpOrderState(bid))
}
for _, ask := range ob.Asks {
dumpOB.Asks = append(dumpOB.Asks, dumpOrderState(ask))
}
dump.OrderBooks = append(dump.OrderBooks, dumpOB)
}
// Dump orders
for _, order := range s.orders {
dump.Orders = append(dump.Orders, dumpOrderState(order))
}
return dump
}
// Dump returns a JSON encoded state dump
func (s *TradingStateDB) Dump() ([]byte, error) {
dump := s.RawDump()
return json.MarshalIndent(dump, "", " ")
}
// dumpOrderState converts an order state to a dump order
func dumpOrderState(order *OrderState) *DumpOrder {
sideStr := "buy"
if order.Side == 1 {
sideStr = "sell"
}
typeStr := "limit"
if order.Type == 1 {
typeStr = "market"
}
statusStr := "new"
switch order.Status {
case 1:
statusStr = "partial"
case 2:
statusStr = "filled"
case 3:
statusStr = "cancelled"
case 4:
statusStr = "rejected"
}
return &DumpOrder{
ID: order.ID.Hex(),
UserAddress: order.UserAddress.Hex(),
ExchangeAddress: order.ExchangeAddress.Hex(),
BaseToken: order.BaseToken.Hex(),
QuoteToken: order.QuoteToken.Hex(),
Side: sideStr,
Type: typeStr,
Price: order.Price.String(),
Quantity: order.Quantity.String(),
FilledQuantity: order.FilledQuantity.String(),
Status: statusStr,
Nonce: order.Nonce,
Timestamp: order.Timestamp,
}
}
// GetTradingPairs returns all trading pairs
func (s *TradingStateDB) GetTradingPairs() []common.Hash {
s.lock.Lock()
defer s.lock.Unlock()
pairs := make([]common.Hash, 0, len(s.orderBooks))
for pairKey := range s.orderBooks {
pairs = append(pairs, pairKey)
}
return pairs
}
// GetAllOrders returns all orders
func (s *TradingStateDB) GetAllOrders() []*OrderState {
s.lock.Lock()
defer s.lock.Unlock()
orders := make([]*OrderState, 0, len(s.orders))
for _, order := range s.orders {
orders = append(orders, order.Copy())
}
return orders
}
// GetOrdersByUser returns all orders for a specific user
func (s *TradingStateDB) GetOrdersByUser(userAddress common.Address) []*OrderState {
s.lock.Lock()
defer s.lock.Unlock()
orders := make([]*OrderState, 0)
for _, order := range s.orders {
if order.UserAddress == userAddress {
orders = append(orders, order.Copy())
}
}
return orders
}
// GetOrdersByPair returns all orders for a specific trading pair
func (s *TradingStateDB) GetOrdersByPair(pairKey common.Hash) []*OrderState {
s.lock.Lock()
defer s.lock.Unlock()
ob := s.orderBooks[pairKey]
if ob == nil {
return nil
}
orders := make([]*OrderState, 0, len(ob.Bids)+len(ob.Asks))
for _, bid := range ob.Bids {
orders = append(orders, bid.Copy())
}
for _, ask := range ob.Asks {
orders = append(orders, ask.Copy())
}
return orders
}
// GetVolume returns the total volume for a trading pair
func (s *TradingStateDB) GetVolume(pairKey common.Hash) (*big.Int, *big.Int) {
s.lock.Lock()
defer s.lock.Unlock()
ob := s.orderBooks[pairKey]
if ob == nil {
return big.NewInt(0), big.NewInt(0)
}
return ob.GetBidVolume(), ob.GetAskVolume()
}

View file

@ -1,196 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package tradingstate
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// journalEntry is a modification entry in the trading state change journal
type journalEntry interface {
// revert undoes the changes introduced by this journal entry
revert(*TradingStateDB)
// dirtied returns the hash of the object modified by this journal entry
dirtied() *common.Hash
}
// journal contains the list of state modifications applied since the last state commit
type journal struct {
entries []journalEntry
dirties map[common.Hash]int
}
// newJournal creates a new initialized journal
func newJournal() *journal {
return &journal{
dirties: make(map[common.Hash]int),
}
}
// append adds a new journal entry
func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry)
if hash := entry.dirtied(); hash != nil {
j.dirties[*hash]++
}
}
// revert undoes a batch of journal entries
func (j *journal) revert(statedb *TradingStateDB, snapshot int) {
for i := len(j.entries) - 1; i >= snapshot; i-- {
j.entries[i].revert(statedb)
if hash := j.entries[i].dirtied(); hash != nil {
if j.dirties[*hash]--; j.dirties[*hash] == 0 {
delete(j.dirties, *hash)
}
}
}
j.entries = j.entries[:snapshot]
}
// dirty returns the dirty count for a specific object
func (j *journal) dirty(hash common.Hash) int {
return j.dirties[hash]
}
// length returns the current number of entries in the journal
func (j *journal) length() int {
return len(j.entries)
}
// orderChange represents a change to an order
type orderChange struct {
orderID common.Hash
prev *OrderState
}
func (ch orderChange) revert(s *TradingStateDB) {
if ch.prev == nil {
delete(s.orders, ch.orderID)
} else {
s.orders[ch.orderID] = ch.prev
}
}
func (ch orderChange) dirtied() *common.Hash {
return &ch.orderID
}
// orderBookChange represents a change to an order book
type orderBookChange struct {
pairKey common.Hash
prev *OrderBook
}
func (ch orderBookChange) revert(s *TradingStateDB) {
if ch.prev == nil {
delete(s.orderBooks, ch.pairKey)
} else {
s.orderBooks[ch.pairKey] = ch.prev
}
}
func (ch orderBookChange) dirtied() *common.Hash {
return &ch.pairKey
}
// bidAddChange represents adding a bid
type bidAddChange struct {
pairKey common.Hash
orderID common.Hash
}
func (ch bidAddChange) revert(s *TradingStateDB) {
if ob, exists := s.orderBooks[ch.pairKey]; exists {
ob.RemoveBid(ch.orderID)
}
}
func (ch bidAddChange) dirtied() *common.Hash {
return &ch.pairKey
}
// askAddChange represents adding an ask
type askAddChange struct {
pairKey common.Hash
orderID common.Hash
}
func (ch askAddChange) revert(s *TradingStateDB) {
if ob, exists := s.orderBooks[ch.pairKey]; exists {
ob.RemoveAsk(ch.orderID)
}
}
func (ch askAddChange) dirtied() *common.Hash {
return &ch.pairKey
}
// bidRemoveChange represents removing a bid
type bidRemoveChange struct {
pairKey common.Hash
order *OrderState
}
func (ch bidRemoveChange) revert(s *TradingStateDB) {
if ob, exists := s.orderBooks[ch.pairKey]; exists {
ob.AddBid(ch.order)
}
}
func (ch bidRemoveChange) dirtied() *common.Hash {
return &ch.pairKey
}
// askRemoveChange represents removing an ask
type askRemoveChange struct {
pairKey common.Hash
order *OrderState
}
func (ch askRemoveChange) revert(s *TradingStateDB) {
if ob, exists := s.orderBooks[ch.pairKey]; exists {
ob.AddAsk(ch.order)
}
}
func (ch askRemoveChange) dirtied() *common.Hash {
return &ch.pairKey
}
// balanceChange represents a balance change
type balanceChange struct {
key common.Hash
amount *big.Int
}
func (ch balanceChange) revert(s *TradingStateDB) {
if obj, exists := s.stateObjects[ch.key]; exists {
obj.SubBalance(ch.amount)
}
}
func (ch balanceChange) dirtied() *common.Hash {
return &ch.key
}
// nonceChange represents a nonce change
type nonceChange struct {
key common.Hash
prev uint64
}
func (ch nonceChange) revert(s *TradingStateDB) {
if obj, exists := s.stateObjects[ch.key]; exists {
obj.SetNonce(ch.prev)
}
}
func (ch nonceChange) dirtied() *common.Hash {
return &ch.key
}

View file

@ -1,242 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package tradingstate
import (
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
)
// Order interface for XDCx orders
type Order interface {
GetID() common.Hash
GetPrice() *big.Int
GetQuantity() *big.Int
GetFilledQuantity() *big.Int
GetSide() uint8
RemainingQuantity() *big.Int
IsFilled() bool
}
// OrderBook represents a trading pair order book
type OrderBook struct {
PairKey common.Hash
Bids []*OrderState // Buy orders sorted by price desc
Asks []*OrderState // Sell orders sorted by price asc
bidIndex map[common.Hash]int
askIndex map[common.Hash]int
lock sync.RWMutex
}
// NewOrderBook creates a new order book
func NewOrderBook(pairKey common.Hash) *OrderBook {
return &OrderBook{
PairKey: pairKey,
Bids: make([]*OrderState, 0),
Asks: make([]*OrderState, 0),
bidIndex: make(map[common.Hash]int),
askIndex: make(map[common.Hash]int),
}
}
// AddBid adds a buy order to the order book
func (ob *OrderBook) AddBid(order interface{}) {
ob.lock.Lock()
defer ob.lock.Unlock()
var orderState *OrderState
switch o := order.(type) {
case *OrderState:
orderState = o
default:
return
}
ob.Bids = append(ob.Bids, orderState)
ob.sortBids()
ob.rebuildBidIndex()
}
// AddAsk adds a sell order to the order book
func (ob *OrderBook) AddAsk(order interface{}) {
ob.lock.Lock()
defer ob.lock.Unlock()
var orderState *OrderState
switch o := order.(type) {
case *OrderState:
orderState = o
default:
return
}
ob.Asks = append(ob.Asks, orderState)
ob.sortAsks()
ob.rebuildAskIndex()
}
// RemoveBid removes a buy order from the order book
func (ob *OrderBook) RemoveBid(orderID common.Hash) {
ob.lock.Lock()
defer ob.lock.Unlock()
if idx, exists := ob.bidIndex[orderID]; exists {
ob.Bids = append(ob.Bids[:idx], ob.Bids[idx+1:]...)
ob.rebuildBidIndex()
}
}
// RemoveAsk removes a sell order from the order book
func (ob *OrderBook) RemoveAsk(orderID common.Hash) {
ob.lock.Lock()
defer ob.lock.Unlock()
if idx, exists := ob.askIndex[orderID]; exists {
ob.Asks = append(ob.Asks[:idx], ob.Asks[idx+1:]...)
ob.rebuildAskIndex()
}
}
// GetBestBid returns the best bid order
func (ob *OrderBook) GetBestBid() *OrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if len(ob.Bids) == 0 {
return nil
}
return ob.Bids[0]
}
// GetBestAsk returns the best ask order
func (ob *OrderBook) GetBestAsk() *OrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if len(ob.Asks) == 0 {
return nil
}
return ob.Asks[0]
}
// GetBid returns a specific bid order
func (ob *OrderBook) GetBid(orderID common.Hash) *OrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if idx, exists := ob.bidIndex[orderID]; exists {
return ob.Bids[idx]
}
return nil
}
// GetAsk returns a specific ask order
func (ob *OrderBook) GetAsk(orderID common.Hash) *OrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if idx, exists := ob.askIndex[orderID]; exists {
return ob.Asks[idx]
}
return nil
}
// BidCount returns the number of bids
func (ob *OrderBook) BidCount() int {
ob.lock.RLock()
defer ob.lock.RUnlock()
return len(ob.Bids)
}
// AskCount returns the number of asks
func (ob *OrderBook) AskCount() int {
ob.lock.RLock()
defer ob.lock.RUnlock()
return len(ob.Asks)
}
// GetBidVolume returns total bid volume
func (ob *OrderBook) GetBidVolume() *big.Int {
ob.lock.RLock()
defer ob.lock.RUnlock()
total := big.NewInt(0)
for _, bid := range ob.Bids {
total = new(big.Int).Add(total, bid.RemainingQuantity())
}
return total
}
// GetAskVolume returns total ask volume
func (ob *OrderBook) GetAskVolume() *big.Int {
ob.lock.RLock()
defer ob.lock.RUnlock()
total := big.NewInt(0)
for _, ask := range ob.Asks {
total = new(big.Int).Add(total, ask.RemainingQuantity())
}
return total
}
// Copy creates a copy of the order book
func (ob *OrderBook) Copy() *OrderBook {
ob.lock.RLock()
defer ob.lock.RUnlock()
copy := &OrderBook{
PairKey: ob.PairKey,
Bids: make([]*OrderState, len(ob.Bids)),
Asks: make([]*OrderState, len(ob.Asks)),
bidIndex: make(map[common.Hash]int),
askIndex: make(map[common.Hash]int),
}
for i, bid := range ob.Bids {
copy.Bids[i] = bid.Copy()
copy.bidIndex[bid.ID] = i
}
for i, ask := range ob.Asks {
copy.Asks[i] = ask.Copy()
copy.askIndex[ask.ID] = i
}
return copy
}
// sortBids sorts bids by price descending
func (ob *OrderBook) sortBids() {
sort.Slice(ob.Bids, func(i, j int) bool {
return ob.Bids[i].Price.Cmp(ob.Bids[j].Price) > 0
})
}
// sortAsks sorts asks by price ascending
func (ob *OrderBook) sortAsks() {
sort.Slice(ob.Asks, func(i, j int) bool {
return ob.Asks[i].Price.Cmp(ob.Asks[j].Price) < 0
})
}
// rebuildBidIndex rebuilds the bid index
func (ob *OrderBook) rebuildBidIndex() {
ob.bidIndex = make(map[common.Hash]int)
for i, bid := range ob.Bids {
ob.bidIndex[bid.ID] = i
}
}
// rebuildAskIndex rebuilds the ask index
func (ob *OrderBook) rebuildAskIndex() {
ob.askIndex = make(map[common.Hash]int)
for i, ask := range ob.Asks {
ob.askIndex[ask.ID] = i
}
}

View file

@ -1,177 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package tradingstate
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
// stateObject represents a trading state object
type stateObject struct {
key common.Hash
data StateData
db *TradingStateDB
trie Trie
dirty bool
deleted bool
}
// StateData holds the trading state data
type StateData struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the state trie
CodeHash []byte
}
// newObject creates a new state object
func newObject(db *TradingStateDB, key common.Hash, data StateData) *stateObject {
return &stateObject{
key: key,
data: data,
db: db,
dirty: false,
}
}
// GetNonce returns the nonce
func (s *stateObject) GetNonce() uint64 {
return s.data.Nonce
}
// SetNonce sets the nonce
func (s *stateObject) SetNonce(nonce uint64) {
s.data.Nonce = nonce
s.dirty = true
}
// GetBalance returns the balance
func (s *stateObject) GetBalance() *big.Int {
return s.data.Balance
}
// SetBalance sets the balance
func (s *stateObject) SetBalance(balance *big.Int) {
s.data.Balance = new(big.Int).Set(balance)
s.dirty = true
}
// AddBalance adds amount to balance
func (s *stateObject) AddBalance(amount *big.Int) {
if amount.Sign() == 0 {
return
}
s.SetBalance(new(big.Int).Add(s.GetBalance(), amount))
}
// SubBalance subtracts amount from balance
func (s *stateObject) SubBalance(amount *big.Int) {
if amount.Sign() == 0 {
return
}
s.SetBalance(new(big.Int).Sub(s.GetBalance(), amount))
}
// deepCopy creates a deep copy of the state object
func (s *stateObject) deepCopy() *stateObject {
return &stateObject{
key: s.key,
data: s.data,
db: s.db,
trie: s.trie,
dirty: s.dirty,
deleted: s.deleted,
}
}
// EncodeRLP implements rlp.Encoder
func (s *stateObject) EncodeRLP() ([]byte, error) {
return rlp.EncodeToBytes(&s.data)
}
// OrderState represents an order in the state
type OrderState struct {
ID common.Hash
UserAddress common.Address
ExchangeAddress common.Address
BaseToken common.Address
QuoteToken common.Address
Side uint8
Type uint8
Price *big.Int
Quantity *big.Int
FilledQuantity *big.Int
Status uint8
Nonce uint64
Timestamp uint64
Signature []byte
}
// NewOrderState creates a new order state
func NewOrderState(
id common.Hash,
userAddress common.Address,
baseToken, quoteToken common.Address,
side, orderType uint8,
price, quantity *big.Int,
) *OrderState {
return &OrderState{
ID: id,
UserAddress: userAddress,
BaseToken: baseToken,
QuoteToken: quoteToken,
Side: side,
Type: orderType,
Price: new(big.Int).Set(price),
Quantity: new(big.Int).Set(quantity),
FilledQuantity: big.NewInt(0),
Status: 0, // New
}
}
// Hash computes the order hash
func (o *OrderState) Hash() common.Hash {
data := append(o.UserAddress.Bytes(), o.BaseToken.Bytes()...)
data = append(data, o.QuoteToken.Bytes()...)
data = append(data, byte(o.Side))
data = append(data, byte(o.Type))
data = append(data, common.BigToHash(o.Price).Bytes()...)
data = append(data, common.BigToHash(o.Quantity).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(o.Nonce))).Bytes()...)
return crypto.Keccak256Hash(data)
}
// Copy creates a copy of the order state
func (o *OrderState) Copy() *OrderState {
return &OrderState{
ID: o.ID,
UserAddress: o.UserAddress,
ExchangeAddress: o.ExchangeAddress,
BaseToken: o.BaseToken,
QuoteToken: o.QuoteToken,
Side: o.Side,
Type: o.Type,
Price: new(big.Int).Set(o.Price),
Quantity: new(big.Int).Set(o.Quantity),
FilledQuantity: new(big.Int).Set(o.FilledQuantity),
Status: o.Status,
Nonce: o.Nonce,
Timestamp: o.Timestamp,
Signature: append([]byte{}, o.Signature...),
}
}
// RemainingQuantity returns the remaining quantity
func (o *OrderState) RemainingQuantity() *big.Int {
return new(big.Int).Sub(o.Quantity, o.FilledQuantity)
}
// IsFilled returns whether the order is filled
func (o *OrderState) IsFilled() bool {
return o.FilledQuantity.Cmp(o.Quantity) >= 0
}

View file

@ -1,320 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
// Package tradingstate provides trading state management for XDCx
package tradingstate
import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
)
// TradingStateDB represents the trading state database
type TradingStateDB struct {
db Database
trie Trie
// Cached objects
stateObjects map[common.Hash]*stateObject
stateObjectsDirty map[common.Hash]struct{}
// Order books cache
orderBooks map[common.Hash]*OrderBook
orderBooksDirty map[common.Hash]struct{}
// Orders cache
orders map[common.Hash]*OrderState
ordersDirty map[common.Hash]struct{}
lock sync.Mutex
}
// New creates a new trading state database
func New(root common.Hash, db Database) (*TradingStateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
return &TradingStateDB{
db: db,
trie: tr,
stateObjects: make(map[common.Hash]*stateObject),
stateObjectsDirty: make(map[common.Hash]struct{}),
orderBooks: make(map[common.Hash]*OrderBook),
orderBooksDirty: make(map[common.Hash]struct{}),
orders: make(map[common.Hash]*OrderState),
ordersDirty: make(map[common.Hash]struct{}),
}, nil
}
// GetOrder returns an order by ID
func (s *TradingStateDB) GetOrder(orderID common.Hash) *OrderState {
s.lock.Lock()
defer s.lock.Unlock()
if order, exists := s.orders[orderID]; exists {
return order
}
// Load from trie
order := s.loadOrder(orderID)
if order != nil {
s.orders[orderID] = order
}
return order
}
// SetOrder sets an order in the state
func (s *TradingStateDB) SetOrder(order interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
// Convert order interface to OrderState
orderState := &OrderState{}
// Implementation depends on order type
s.orders[orderState.ID] = orderState
s.ordersDirty[orderState.ID] = struct{}{}
}
// UpdateOrder updates an order in the state
func (s *TradingStateDB) UpdateOrder(order interface{}) {
s.SetOrder(order)
}
// DeleteOrder deletes an order from the state
func (s *TradingStateDB) DeleteOrder(orderID common.Hash) {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.orders, orderID)
s.ordersDirty[orderID] = struct{}{}
}
// GetOrderBook returns an order book by pair key
func (s *TradingStateDB) GetOrderBook(pairKey common.Hash) *OrderBook {
s.lock.Lock()
defer s.lock.Unlock()
if ob, exists := s.orderBooks[pairKey]; exists {
return ob
}
// Load from trie
ob := s.loadOrderBook(pairKey)
if ob != nil {
s.orderBooks[pairKey] = ob
}
return ob
}
// GetOrCreateOrderBook returns or creates an order book
func (s *TradingStateDB) GetOrCreateOrderBook(pairKey common.Hash) *OrderBook {
ob := s.GetOrderBook(pairKey)
if ob == nil {
s.lock.Lock()
ob = NewOrderBook(pairKey)
s.orderBooks[pairKey] = ob
s.orderBooksDirty[pairKey] = struct{}{}
s.lock.Unlock()
}
return ob
}
// SetOrderBook sets an order book in the state
func (s *TradingStateDB) SetOrderBook(pairKey common.Hash, ob *OrderBook) {
s.lock.Lock()
defer s.lock.Unlock()
s.orderBooks[pairKey] = ob
s.orderBooksDirty[pairKey] = struct{}{}
}
// GetExchangeAddress returns the exchange address for a relayer
func (s *TradingStateDB) GetExchangeAddress(relayer common.Address) common.Address {
// Implementation
return relayer
}
// GetRelayerFee returns the fee for a relayer
func (s *TradingStateDB) GetRelayerFee(relayer common.Address) *big.Int {
// Implementation
return big.NewInt(0)
}
// Commit commits all changes to the underlying database
func (s *TradingStateDB) Commit() (common.Hash, error) {
s.lock.Lock()
defer s.lock.Unlock()
// Commit dirty objects
for key := range s.stateObjectsDirty {
if obj, exists := s.stateObjects[key]; exists {
if err := s.commitStateObject(obj); err != nil {
return common.Hash{}, err
}
}
delete(s.stateObjectsDirty, key)
}
// Commit dirty order books
for pairKey := range s.orderBooksDirty {
if ob, exists := s.orderBooks[pairKey]; exists {
if err := s.commitOrderBook(ob); err != nil {
return common.Hash{}, err
}
}
delete(s.orderBooksDirty, pairKey)
}
// Commit dirty orders
for orderID := range s.ordersDirty {
if order, exists := s.orders[orderID]; exists {
if err := s.commitOrder(order); err != nil {
return common.Hash{}, err
}
}
delete(s.ordersDirty, orderID)
}
// Commit trie
root, err := s.trie.Commit(nil)
if err != nil {
return common.Hash{}, err
}
log.Debug("Trading state committed", "root", root.Hex())
return root, nil
}
// Copy creates a copy of the trading state
func (s *TradingStateDB) Copy() *TradingStateDB {
s.lock.Lock()
defer s.lock.Unlock()
state := &TradingStateDB{
db: s.db,
trie: s.db.CopyTrie(s.trie),
stateObjects: make(map[common.Hash]*stateObject),
stateObjectsDirty: make(map[common.Hash]struct{}),
orderBooks: make(map[common.Hash]*OrderBook),
orderBooksDirty: make(map[common.Hash]struct{}),
orders: make(map[common.Hash]*OrderState),
ordersDirty: make(map[common.Hash]struct{}),
}
// Copy state objects
for key, obj := range s.stateObjects {
state.stateObjects[key] = obj.deepCopy()
}
for key := range s.stateObjectsDirty {
state.stateObjectsDirty[key] = struct{}{}
}
// Copy order books
for key, ob := range s.orderBooks {
state.orderBooks[key] = ob.Copy()
}
for key := range s.orderBooksDirty {
state.orderBooksDirty[key] = struct{}{}
}
// Copy orders
for key, order := range s.orders {
state.orders[key] = order.Copy()
}
for key := range s.ordersDirty {
state.ordersDirty[key] = struct{}{}
}
return state
}
// loadOrder loads an order from the trie
func (s *TradingStateDB) loadOrder(orderID common.Hash) *OrderState {
// Implementation
return nil
}
// loadOrderBook loads an order book from the trie
func (s *TradingStateDB) loadOrderBook(pairKey common.Hash) *OrderBook {
// Implementation
return nil
}
// commitStateObject commits a state object
func (s *TradingStateDB) commitStateObject(obj *stateObject) error {
// Implementation
return nil
}
// commitOrderBook commits an order book
func (s *TradingStateDB) commitOrderBook(ob *OrderBook) error {
// Implementation
return nil
}
// commitOrder commits an order
func (s *TradingStateDB) commitOrder(order *OrderState) error {
// Implementation
return nil
}
// Database wraps access to tries and contract code
type Database interface {
// OpenTrie opens the main trading state trie
OpenTrie(root common.Hash) (Trie, error)
// CopyTrie returns an independent copy of the given trie
CopyTrie(Trie) Trie
}
// Trie is a XDCx Merkle Patricia trie
type Trie interface {
// GetKey returns the sha3 preimage of a hashed key
GetKey([]byte) []byte
// TryGet returns the value for key stored in the trie
TryGet(key []byte) ([]byte, error)
// TryUpdate associates key with value in the trie
TryUpdate(key, value []byte) error
// TryDelete removes any existing value for key from the trie
TryDelete(key []byte) error
// Hash returns the root hash of the trie
Hash() common.Hash
// Commit writes all nodes to the trie's database
Commit(onleaf trie.LeafCallback) (common.Hash, error)
// NodeIterator returns an iterator that returns nodes of the trie
NodeIterator(startKey []byte) trie.NodeIterator
}
// NewDatabase creates a new trading state database
func NewDatabase(db ethdb.Database) Database {
return &cachingDB{
db: db,
}
}
type cachingDB struct {
db ethdb.Database
}
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// Implementation
return nil, nil
}
func (db *cachingDB) CopyTrie(t Trie) Trie {
// Implementation
return nil
}

View file

@ -1,254 +1,33 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
// Package XDCxlending implements the XDC decentralized lending protocol
// XDCxlending - Lending Protocol Stub for geth 1.17 compatibility
// Full implementation requires adaptation of trie/state interfaces
package XDCxlending
import (
"context"
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCxlending/lendingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
var (
// ErrLendingServiceNotRunning is returned when lending service is not running
ErrLendingServiceNotRunning = errors.New("lending service is not running")
// ErrLoanNotFound is returned when loan is not found
ErrLoanNotFound = errors.New("loan not found")
// ErrInvalidInterestRate is returned when interest rate is invalid
ErrInvalidInterestRate = errors.New("invalid interest rate")
// ErrInvalidTerm is returned when term is invalid
ErrInvalidTerm = errors.New("invalid term")
// ErrInsufficientCollateral is returned when collateral is insufficient
ErrInsufficientCollateral = errors.New("insufficient collateral")
)
// XDCxLending represents the XDC decentralized lending protocol
type XDCxLending struct {
config *Config
db ethdb.Database
stateCache lendingstate.Database
lock sync.RWMutex
running bool
orderProcessor *OrderProcessor
liquidator *Liquidator
// XDCxlending is the lending protocol engine (stub)
type XDCxlending struct {
db ethdb.Database
}
// Config holds XDCxLending configuration
type Config struct {
DataDir string
DBEngine string
LendingStateDB string
DefaultTerm uint64
MinCollateral *big.Int
LiquidationRate *big.Int
// New creates a new lending engine
func New(db ethdb.Database) *XDCxlending {
return &XDCxlending{db: db}
}
// DefaultConfig returns default XDCxLending configuration
func DefaultConfig() *Config {
return &Config{
DataDir: "",
DBEngine: "leveldb",
LendingStateDB: "XDCxlending",
DefaultTerm: 86400 * 30, // 30 days in seconds
MinCollateral: big.NewInt(150), // 150%
LiquidationRate: big.NewInt(110), // 110%
}
}
// New creates a new XDCxLending instance
func New(config *Config, db ethdb.Database) (*XDCxLending, error) {
if config == nil {
config = DefaultConfig()
}
lending := &XDCxLending{
config: config,
db: db,
running: false,
}
lending.stateCache = lendingstate.NewDatabase(db)
lending.orderProcessor = NewOrderProcessor(lending)
lending.liquidator = NewLiquidator(lending)
return lending, nil
}
// Start starts the XDCxLending service
func (l *XDCxLending) Start() error {
l.lock.Lock()
defer l.lock.Unlock()
if l.running {
return nil
}
log.Info("Starting XDCxLending service")
l.running = true
// ProcessLendingOrder is a stub for lending order processing
func (l *XDCxlending) ProcessLendingOrder(token common.Hash, order interface{}) error {
return nil
}
// Stop stops the XDCxLending service
func (l *XDCxLending) Stop() error {
l.lock.Lock()
defer l.lock.Unlock()
if !l.running {
return nil
}
log.Info("Stopping XDCxLending service")
l.running = false
// GetLendingBook returns nil (stub)
func (l *XDCxlending) GetLendingBook(token common.Hash) interface{} {
return nil
}
// IsRunning returns whether XDCxLending is running
func (l *XDCxLending) IsRunning() bool {
l.lock.RLock()
defer l.lock.RUnlock()
return l.running
}
// GetLendingState returns the lending state for a given root
func (l *XDCxLending) GetLendingState(block *types.Block, statedb *state.StateDB) (*lendingstate.LendingStateDB, error) {
if block == nil {
return nil, errors.New("block is nil")
}
root := block.Root()
lendingState, err := lendingstate.New(root, l.stateCache)
if err != nil {
return nil, err
}
return lendingState, nil
}
// ProcessLendingOrder processes a lending order
func (l *XDCxLending) ProcessLendingOrder(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, order *LendingOrder) ([]*LendingTrade, error) {
if !l.IsRunning() {
return nil, ErrLendingServiceNotRunning
}
return l.orderProcessor.Process(ctx, statedb, lendingState, order)
}
// CancelLendingOrder cancels an existing lending order
func (l *XDCxLending) CancelLendingOrder(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, orderID common.Hash) error {
if !l.IsRunning() {
return ErrLendingServiceNotRunning
}
return l.orderProcessor.Cancel(ctx, statedb, lendingState, orderID)
}
// Topup adds collateral to a loan
func (l *XDCxLending) Topup(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, loanID common.Hash, amount *big.Int) error {
if !l.IsRunning() {
return ErrLendingServiceNotRunning
}
loan := lendingState.GetLoan(loanID)
if loan == nil {
return ErrLoanNotFound
}
// Add collateral
loan.CollateralAmount = new(big.Int).Add(loan.CollateralAmount, amount)
lendingState.UpdateLoan(loan)
log.Debug("Loan topped up", "loanID", loanID.Hex(), "amount", amount)
// Liquidate is a stub for liquidation
func (l *XDCxlending) Liquidate(loan interface{}) error {
return nil
}
// Repay repays a loan
func (l *XDCxLending) Repay(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, loanID common.Hash) error {
if !l.IsRunning() {
return ErrLendingServiceNotRunning
}
loan := lendingState.GetLoan(loanID)
if loan == nil {
return ErrLoanNotFound
}
// Calculate total repayment amount
interest := l.calculateInterest(loan)
totalRepayment := new(big.Int).Add(loan.Principal, interest)
// Update loan status
loan.Status = LoanStatusRepaid
lendingState.UpdateLoan(loan)
log.Debug("Loan repaid", "loanID", loanID.Hex(), "total", totalRepayment)
return nil
}
// Liquidate liquidates an undercollateralized loan
func (l *XDCxLending) Liquidate(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, loanID common.Hash) error {
if !l.IsRunning() {
return ErrLendingServiceNotRunning
}
return l.liquidator.Liquidate(ctx, statedb, lendingState, loanID)
}
// CheckLiquidation checks if loans need liquidation
func (l *XDCxLending) CheckLiquidation(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB) ([]common.Hash, error) {
if !l.IsRunning() {
return nil, ErrLendingServiceNotRunning
}
return l.liquidator.CheckLoansForLiquidation(lendingState)
}
// ApplyXDCxLendingMatchedTransaction applies matched lending trades to state
func (l *XDCxLending) ApplyXDCxLendingMatchedTransaction(chainConfig *params.ChainConfig, statedb *state.StateDB, block *types.Block, trades []*LendingTrade) error {
for _, trade := range trades {
if err := l.settleLendingTrade(statedb, trade); err != nil {
return err
}
}
return nil
}
// settleLendingTrade settles a lending trade
func (l *XDCxLending) settleLendingTrade(statedb *state.StateDB, trade *LendingTrade) error {
log.Debug("Settling lending trade", "trade", trade.Hash())
return nil
}
// calculateInterest calculates the interest for a loan
func (l *XDCxLending) calculateInterest(loan *Loan) *big.Int {
// Simple interest calculation: Principal * Rate * Time / (365 * 100)
interest := new(big.Int).Mul(loan.Principal, loan.InterestRate)
interest = interest.Mul(interest, big.NewInt(int64(loan.Term)))
interest = interest.Div(interest, big.NewInt(365*24*3600*100))
return interest
}
// GetConfig returns XDCxLending configuration
func (l *XDCxLending) GetConfig() *Config {
return l.config
}
// Database returns the database instance
func (l *XDCxLending) Database() ethdb.Database {
return l.db
}
// StateCache returns the lending state cache
func (l *XDCxLending) StateCache() lendingstate.Database {
return l.stateCache
}

View file

@ -1,338 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)
func TestNewXDCxLending(t *testing.T) {
db := memorydb.New()
config := DefaultConfig()
lending, err := New(config, db)
if err != nil {
t.Fatalf("Failed to create XDCxLending: %v", err)
}
if lending == nil {
t.Fatal("XDCxLending instance is nil")
}
if lending.config != config {
t.Error("Config not set correctly")
}
}
func TestXDCxLendingStartStop(t *testing.T) {
db := memorydb.New()
lending, _ := New(DefaultConfig(), db)
// Test start
if err := lending.Start(); err != nil {
t.Fatalf("Failed to start XDCxLending: %v", err)
}
if !lending.IsRunning() {
t.Error("XDCxLending should be running after Start")
}
// Test stop
if err := lending.Stop(); err != nil {
t.Fatalf("Failed to stop XDCxLending: %v", err)
}
if lending.IsRunning() {
t.Error("XDCxLending should not be running after Stop")
}
}
func TestDefaultLendingConfig(t *testing.T) {
config := DefaultConfig()
if config.DBEngine != "leveldb" {
t.Errorf("Expected DBEngine 'leveldb', got '%s'", config.DBEngine)
}
if config.DefaultTerm != 86400*30 {
t.Errorf("Expected DefaultTerm 30 days, got %d", config.DefaultTerm)
}
if config.MinCollateral.Cmp(big.NewInt(150)) != 0 {
t.Errorf("Expected MinCollateral 150, got %s", config.MinCollateral.String())
}
if config.LiquidationRate.Cmp(big.NewInt(110)) != 0 {
t.Errorf("Expected LiquidationRate 110, got %s", config.LiquidationRate.String())
}
}
func TestNewLendingOrder(t *testing.T) {
userAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
lendingToken := common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
collateralToken := common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
relayerAddr := common.HexToAddress("0xcccccccccccccccccccccccccccccccccccccccc")
interestRate := big.NewInt(500) // 5%
term := uint64(86400 * 30) // 30 days
quantity := big.NewInt(1000)
nonce := uint64(1)
order := NewLendingOrder(
userAddr, lendingToken, collateralToken,
Borrow, LimitOrder,
interestRate, term, quantity, nonce, relayerAddr,
)
if order.UserAddress != userAddr {
t.Errorf("Expected user address %s, got %s", userAddr.Hex(), order.UserAddress.Hex())
}
if order.LendingToken != lendingToken {
t.Errorf("Expected lending token %s, got %s", lendingToken.Hex(), order.LendingToken.Hex())
}
if order.Side != Borrow {
t.Errorf("Expected side Borrow, got %d", order.Side)
}
if order.Status != OrderStatusNew {
t.Errorf("Expected status New, got %d", order.Status)
}
if order.Term != term {
t.Errorf("Expected term %d, got %d", term, order.Term)
}
}
func TestLendingOrderRemainingQuantity(t *testing.T) {
order := &LendingOrder{
Quantity: big.NewInt(100),
FilledQuantity: big.NewInt(30),
}
remaining := order.RemainingQuantity()
expected := big.NewInt(70)
if remaining.Cmp(expected) != 0 {
t.Errorf("Expected remaining %s, got %s", expected.String(), remaining.String())
}
}
func TestLendingOrderIsFilled(t *testing.T) {
order := &LendingOrder{
Quantity: big.NewInt(100),
FilledQuantity: big.NewInt(30),
}
if order.IsFilled() {
t.Error("Order should not be filled")
}
order.FilledQuantity = big.NewInt(100)
if !order.IsFilled() {
t.Error("Order should be filled")
}
}
func TestNewLoan(t *testing.T) {
borrower := common.HexToAddress("0x1111")
lender := common.HexToAddress("0x2222")
lendingToken := common.HexToAddress("0x3333")
collateralToken := common.HexToAddress("0x4444")
principal := big.NewInt(1000)
collateral := big.NewInt(1500)
interestRate := big.NewInt(500)
term := uint64(86400 * 30)
startTime := uint64(1000000)
loan := NewLoan(
borrower, lender,
lendingToken, collateralToken,
principal, collateral,
interestRate, term, startTime,
)
if loan.BorrowerAddress != borrower {
t.Error("Borrower address mismatch")
}
if loan.LenderAddress != lender {
t.Error("Lender address mismatch")
}
if loan.ExpiryTime != startTime+term {
t.Errorf("Expected expiry %d, got %d", startTime+term, loan.ExpiryTime)
}
if loan.Status != LoanStatusActive {
t.Errorf("Expected status Active, got %d", loan.Status)
}
}
func TestLoanIsExpired(t *testing.T) {
loan := &Loan{
ExpiryTime: 1000000,
}
// Not expired
if loan.IsExpired(500000) {
t.Error("Loan should not be expired")
}
// Exactly expired
if !loan.IsExpired(1000000) {
t.Error("Loan should be expired at expiry time")
}
// Past expiry
if !loan.IsExpired(1500000) {
t.Error("Loan should be expired past expiry time")
}
}
func TestLoanGetCollateralRatio(t *testing.T) {
loan := &Loan{
Principal: big.NewInt(1000),
CollateralAmount: big.NewInt(1500),
}
// Collateral price = 1
collateralPrice := big.NewInt(1)
ratio := loan.GetCollateralRatio(collateralPrice)
// Expected: (1500 * 1) / 1000 * 100 = 150
expected := big.NewInt(150)
if ratio.Cmp(expected) != 0 {
t.Errorf("Expected ratio %s, got %s", expected.String(), ratio.String())
}
}
func TestGetLendingPairKey(t *testing.T) {
lendingToken := common.HexToAddress("0xaaaa")
collateralToken := common.HexToAddress("0xbbbb")
term := uint64(86400 * 30)
key1 := GetLendingPairKey(lendingToken, collateralToken, term)
key2 := GetLendingPairKey(lendingToken, collateralToken, term)
if key1 != key2 {
t.Error("Same parameters should produce same key")
}
// Different term should produce different key
key3 := GetLendingPairKey(lendingToken, collateralToken, term*2)
if key1 == key3 {
t.Error("Different term should produce different key")
}
}
func TestNewLendingTrade(t *testing.T) {
borrowOrderID := common.HexToHash("0x1111")
lendOrderID := common.HexToHash("0x2222")
borrower := common.HexToAddress("0x3333")
lender := common.HexToAddress("0x4444")
lendingToken := common.HexToAddress("0x5555")
collateralToken := common.HexToAddress("0x6666")
principal := big.NewInt(1000)
collateral := big.NewInt(1500)
interestRate := big.NewInt(500)
term := uint64(86400 * 30)
trade := NewLendingTrade(
borrowOrderID, lendOrderID,
borrower, lender,
lendingToken, collateralToken,
principal, collateral,
interestRate, term,
)
if trade.BorrowOrderID != borrowOrderID {
t.Error("Borrow order ID mismatch")
}
if trade.Borrower != borrower {
t.Error("Borrower address mismatch")
}
if trade.Status != TradeStatusPending {
t.Errorf("Expected status Pending, got %d", trade.Status)
}
}
func TestLendingTradeCalculateInterest(t *testing.T) {
trade := &LendingTrade{
Principal: big.NewInt(10000),
InterestRate: big.NewInt(500), // 5%
Term: 86400 * 30, // 30 days
}
interest := trade.CalculateInterest()
// Interest = 10000 * 5 * 30 / (365 * 100) ≈ 41
// This is approximate due to integer division
if interest.Sign() <= 0 {
t.Error("Interest should be positive")
}
}
func TestLendingTradeTotalRepayment(t *testing.T) {
trade := &LendingTrade{
Principal: big.NewInt(10000),
InterestRate: big.NewInt(500),
Term: 86400 * 30,
}
total := trade.TotalRepayment()
interest := trade.CalculateInterest()
expected := new(big.Int).Add(trade.Principal, interest)
if total.Cmp(expected) != 0 {
t.Errorf("Expected total %s, got %s", expected.String(), total.String())
}
}
func TestXDCxLendingAPIs(t *testing.T) {
db := memorydb.New()
lending, _ := New(DefaultConfig(), db)
apis := lending.APIs()
if len(apis) != 2 {
t.Errorf("Expected 2 APIs, got %d", len(apis))
}
// Check namespaces
namespaces := make(map[string]bool)
for _, api := range apis {
namespaces[api.Namespace] = true
}
if !namespaces["xdcxlending"] {
t.Error("Expected 'xdcxlending' namespace")
}
}
func TestCalculateInterest(t *testing.T) {
db := memorydb.New()
lending, _ := New(DefaultConfig(), db)
loan := &Loan{
Principal: big.NewInt(10000),
InterestRate: big.NewInt(1000), // 10%
Term: 86400 * 365, // 1 year
}
interest := lending.calculateInterest(loan)
// With 10% annual rate for 1 year, interest should be around 1000
// Allow some tolerance due to integer division
if interest.Cmp(big.NewInt(900)) < 0 || interest.Cmp(big.NewInt(1100)) > 0 {
t.Errorf("Expected interest around 1000, got %s", interest.String())
}
}

View file

@ -1,297 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/XDCxlending/lendingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
)
// PublicXDCxLendingAPI provides public XDCxLending APIs
type PublicXDCxLendingAPI struct {
lending *XDCxLending
}
// NewPublicXDCxLendingAPI creates a new public XDCxLending API
func NewPublicXDCxLendingAPI(lending *XDCxLending) *PublicXDCxLendingAPI {
return &PublicXDCxLendingAPI{lending: lending}
}
// Version returns the XDCxLending version
func (api *PublicXDCxLendingAPI) Version() string {
return "1.0"
}
// LendingOrderBookResult represents a lending order book API result
type LendingOrderBookResult struct {
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Term uint64 `json:"term"`
Borrows []LendingOrderResult `json:"borrows"`
Lends []LendingOrderResult `json:"lends"`
}
// LendingOrderResult represents a lending order API result
type LendingOrderResult struct {
ID common.Hash `json:"id"`
UserAddress common.Address `json:"userAddress"`
RelayerAddress common.Address `json:"relayerAddress"`
InterestRate *hexutil.Big `json:"interestRate"`
Quantity *hexutil.Big `json:"quantity"`
FilledQuantity *hexutil.Big `json:"filledQuantity"`
Status string `json:"status"`
Side string `json:"side"`
}
// LoanResult represents a loan API result
type LoanResult struct {
ID common.Hash `json:"id"`
Borrower common.Address `json:"borrower"`
Lender common.Address `json:"lender"`
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Principal *hexutil.Big `json:"principal"`
CollateralAmount *hexutil.Big `json:"collateralAmount"`
InterestRate *hexutil.Big `json:"interestRate"`
Term uint64 `json:"term"`
StartTime uint64 `json:"startTime"`
ExpiryTime uint64 `json:"expiryTime"`
Status string `json:"status"`
}
// GetLendingOrderBook returns the lending order book
func (api *PublicXDCxLendingAPI) GetLendingOrderBook(ctx context.Context, lendingToken, collateralToken common.Address, term uint64) (*LendingOrderBookResult, error) {
if !api.lending.IsRunning() {
return nil, ErrLendingServiceNotRunning
}
// Get current lending state
lendingState, err := lendingstate.New(common.Hash{}, api.lending.StateCache())
if err != nil {
return nil, err
}
pairKey := GetLendingPairKey(lendingToken, collateralToken, term)
ob := lendingState.GetLendingOrderBook(pairKey)
if ob == nil {
return nil, errors.New("order book not found")
}
result := &LendingOrderBookResult{
LendingToken: lendingToken,
CollateralToken: collateralToken,
Term: term,
Borrows: make([]LendingOrderResult, 0),
Lends: make([]LendingOrderResult, 0),
}
for _, borrow := range ob.Borrows {
result.Borrows = append(result.Borrows, orderStateToResult(borrow))
}
for _, lend := range ob.Lends {
result.Lends = append(result.Lends, orderStateToResult(lend))
}
return result, nil
}
// GetLoan returns a loan by ID
func (api *PublicXDCxLendingAPI) GetLoan(ctx context.Context, loanID common.Hash) (*LoanResult, error) {
if !api.lending.IsRunning() {
return nil, ErrLendingServiceNotRunning
}
lendingState, err := lendingstate.New(common.Hash{}, api.lending.StateCache())
if err != nil {
return nil, err
}
loan := lendingState.GetLoan(loanID)
if loan == nil {
return nil, ErrLoanNotFound
}
return loanStateToResult(loan), nil
}
// GetLoansByBorrower returns all loans for a borrower
func (api *PublicXDCxLendingAPI) GetLoansByBorrower(ctx context.Context, borrower common.Address) ([]*LoanResult, error) {
if !api.lending.IsRunning() {
return nil, ErrLendingServiceNotRunning
}
lendingState, err := lendingstate.New(common.Hash{}, api.lending.StateCache())
if err != nil {
return nil, err
}
allLoans := lendingState.GetAllLoans()
results := make([]*LoanResult, 0)
for _, loan := range allLoans {
if loan.BorrowerAddress == borrower {
results = append(results, loanStateToResult(loan))
}
}
return results, nil
}
// orderStateToResult converts a lending order state to API result
func orderStateToResult(order *lendingstate.LendingOrderState) LendingOrderResult {
sideStr := "borrow"
if order.Side == 1 {
sideStr = "lend"
}
statusStr := "new"
switch order.Status {
case 1:
statusStr = "partial"
case 2:
statusStr = "filled"
case 3:
statusStr = "cancelled"
case 4:
statusStr = "rejected"
}
return LendingOrderResult{
ID: order.ID,
UserAddress: order.UserAddress,
RelayerAddress: order.RelayerAddress,
InterestRate: (*hexutil.Big)(order.InterestRate),
Quantity: (*hexutil.Big)(order.Quantity),
FilledQuantity: (*hexutil.Big)(order.FilledQuantity),
Status: statusStr,
Side: sideStr,
}
}
// loanStateToResult converts a loan state to API result
func loanStateToResult(loan *lendingstate.LoanState) *LoanResult {
statusStr := "active"
switch loan.Status {
case 1:
statusStr = "repaid"
case 2:
statusStr = "liquidated"
case 3:
statusStr = "defaulted"
}
return &LoanResult{
ID: loan.ID,
Borrower: loan.BorrowerAddress,
Lender: loan.LenderAddress,
LendingToken: loan.LendingToken,
CollateralToken: loan.CollateralToken,
Principal: (*hexutil.Big)(loan.Principal),
CollateralAmount: (*hexutil.Big)(loan.CollateralAmount),
InterestRate: (*hexutil.Big)(loan.InterestRate),
Term: loan.Term,
StartTime: loan.StartTime,
ExpiryTime: loan.ExpiryTime,
Status: statusStr,
}
}
// PrivateXDCxLendingAPI provides private XDCxLending APIs
type PrivateXDCxLendingAPI struct {
lending *XDCxLending
}
// NewPrivateXDCxLendingAPI creates a new private XDCxLending API
func NewPrivateXDCxLendingAPI(lending *XDCxLending) *PrivateXDCxLendingAPI {
return &PrivateXDCxLendingAPI{lending: lending}
}
// SendLendingOrder sends a new lending order
func (api *PrivateXDCxLendingAPI) SendLendingOrder(ctx context.Context, args SendLendingOrderArgs) (common.Hash, error) {
if !api.lending.IsRunning() {
return common.Hash{}, ErrLendingServiceNotRunning
}
// Convert args to order
order, err := args.ToOrder()
if err != nil {
return common.Hash{}, err
}
return order.ID, nil
}
// SendLendingOrderArgs represents the arguments for sending a lending order
type SendLendingOrderArgs struct {
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Side string `json:"side"`
Type string `json:"type"`
InterestRate *hexutil.Big `json:"interestRate"`
Term hexutil.Uint64 `json:"term"`
Quantity *hexutil.Big `json:"quantity"`
RelayerAddress common.Address `json:"relayerAddress"`
Nonce hexutil.Uint64 `json:"nonce"`
Signature hexutil.Bytes `json:"signature"`
}
// ToOrder converts args to a lending order
func (args *SendLendingOrderArgs) ToOrder() (*LendingOrder, error) {
var side OrderSide
switch args.Side {
case "borrow":
side = Borrow
case "lend":
side = Lend
default:
return nil, errors.New("invalid order side")
}
var orderType OrderType
switch args.Type {
case "limit":
orderType = LimitOrder
case "market":
orderType = MarketOrder
default:
return nil, errors.New("invalid order type")
}
order := NewLendingOrder(
common.Address{},
args.LendingToken,
args.CollateralToken,
side,
orderType,
(*big.Int)(args.InterestRate),
uint64(args.Term),
(*big.Int)(args.Quantity),
uint64(args.Nonce),
args.RelayerAddress,
)
order.Signature = args.Signature
return order, nil
}
// APIs returns the collection of XDCxLending APIs
func (l *XDCxLending) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "xdcxlending",
Service: NewPublicXDCxLendingAPI(l),
},
{
Namespace: "xdcxlending",
Service: NewPrivateXDCxLendingAPI(l),
},
}
}

View file

@ -1,127 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package lendingstate
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
)
// LendingTrieDB wraps the trie database for lending state
type LendingTrieDB struct {
diskdb ethdb.Database
config *trie.Config
}
// NewLendingTrieDB creates a new lending trie database
func NewLendingTrieDB(diskdb ethdb.Database) *LendingTrieDB {
return &LendingTrieDB{
diskdb: diskdb,
config: &trie.Config{},
}
}
// DiskDB returns the underlying disk database
func (db *LendingTrieDB) DiskDB() ethdb.Database {
return db.diskdb
}
// OpenLendingTrie opens a lending state trie
func (db *LendingTrieDB) OpenLendingTrie(root common.Hash) (*trie.Trie, error) {
id := &trie.ID{
StateRoot: root,
}
return trie.New(id, trie.NewDatabase(db.diskdb, db.config))
}
// CopyLendingTrie copies a lending trie
func (db *LendingTrieDB) CopyLendingTrie(t *trie.Trie) *trie.Trie {
return t.Copy()
}
// Commit commits all pending writes
func (db *LendingTrieDB) Commit(root common.Hash, report bool) error {
return nil
}
// Close closes the database
func (db *LendingTrieDB) Close() error {
return db.diskdb.Close()
}
// LendingStateCacheConfig holds cache configuration
type LendingStateCacheConfig struct {
CacheSize int
MaxLive int
}
// DefaultLendingStateCacheConfig returns default cache configuration
func DefaultLendingStateCacheConfig() *LendingStateCacheConfig {
return &LendingStateCacheConfig{
CacheSize: 256,
MaxLive: 16,
}
}
// LendingStateCache provides caching for lending state
type LendingStateCache struct {
config *LendingStateCacheConfig
db *LendingTrieDB
states map[common.Hash]*LendingStateDB
}
// NewLendingStateCache creates a new lending state cache
func NewLendingStateCache(db *LendingTrieDB, config *LendingStateCacheConfig) *LendingStateCache {
if config == nil {
config = DefaultLendingStateCacheConfig()
}
return &LendingStateCache{
config: config,
db: db,
states: make(map[common.Hash]*LendingStateDB),
}
}
// Get retrieves a lending state from cache or creates new one
func (c *LendingStateCache) Get(root common.Hash) (*LendingStateDB, error) {
if state, ok := c.states[root]; ok {
return state.Copy(), nil
}
return New(root, c)
}
// Put stores a lending state in cache
func (c *LendingStateCache) Put(root common.Hash, state *LendingStateDB) {
if len(c.states) >= c.config.CacheSize {
for k := range c.states {
delete(c.states, k)
break
}
}
c.states[root] = state
}
// Remove removes a state from cache
func (c *LendingStateCache) Remove(root common.Hash) {
delete(c.states, root)
}
// Clear clears the entire cache
func (c *LendingStateCache) Clear() {
c.states = make(map[common.Hash]*LendingStateDB)
}
// OpenTrie implements Database interface
func (c *LendingStateCache) OpenTrie(root common.Hash) (Trie, error) {
return c.db.OpenLendingTrie(root)
}
// CopyTrie implements Database interface
func (c *LendingStateCache) CopyTrie(t Trie) Trie {
if tt, ok := t.(*trie.Trie); ok {
return c.db.CopyLendingTrie(tt)
}
return nil
}

View file

@ -1,256 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package lendingstate
import (
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
)
// LendingOrderBook represents a lending pair order book
type LendingOrderBook struct {
PairKey common.Hash
Borrows []*LendingOrderState // Borrow orders sorted by interest rate desc
Lends []*LendingOrderState // Lend orders sorted by interest rate asc
borrowIndex map[common.Hash]int
lendIndex map[common.Hash]int
lock sync.RWMutex
}
// NewLendingOrderBook creates a new lending order book
func NewLendingOrderBook(pairKey common.Hash) *LendingOrderBook {
return &LendingOrderBook{
PairKey: pairKey,
Borrows: make([]*LendingOrderState, 0),
Lends: make([]*LendingOrderState, 0),
borrowIndex: make(map[common.Hash]int),
lendIndex: make(map[common.Hash]int),
}
}
// AddBorrow adds a borrow order to the order book
func (ob *LendingOrderBook) AddBorrow(order interface{}) {
ob.lock.Lock()
defer ob.lock.Unlock()
var orderState *LendingOrderState
switch o := order.(type) {
case *LendingOrderState:
orderState = o
default:
return
}
ob.Borrows = append(ob.Borrows, orderState)
ob.sortBorrows()
ob.rebuildBorrowIndex()
}
// AddLend adds a lend order to the order book
func (ob *LendingOrderBook) AddLend(order interface{}) {
ob.lock.Lock()
defer ob.lock.Unlock()
var orderState *LendingOrderState
switch o := order.(type) {
case *LendingOrderState:
orderState = o
default:
return
}
ob.Lends = append(ob.Lends, orderState)
ob.sortLends()
ob.rebuildLendIndex()
}
// RemoveBorrow removes a borrow order from the order book
func (ob *LendingOrderBook) RemoveBorrow(orderID common.Hash) {
ob.lock.Lock()
defer ob.lock.Unlock()
if idx, exists := ob.borrowIndex[orderID]; exists {
ob.Borrows = append(ob.Borrows[:idx], ob.Borrows[idx+1:]...)
ob.rebuildBorrowIndex()
}
}
// RemoveLend removes a lend order from the order book
func (ob *LendingOrderBook) RemoveLend(orderID common.Hash) {
ob.lock.Lock()
defer ob.lock.Unlock()
if idx, exists := ob.lendIndex[orderID]; exists {
ob.Lends = append(ob.Lends[:idx], ob.Lends[idx+1:]...)
ob.rebuildLendIndex()
}
}
// GetBestBorrow returns the best borrow order (highest interest rate)
func (ob *LendingOrderBook) GetBestBorrow() *LendingOrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if len(ob.Borrows) == 0 {
return nil
}
return ob.Borrows[0]
}
// GetBestLend returns the best lend order (lowest interest rate)
func (ob *LendingOrderBook) GetBestLend() *LendingOrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if len(ob.Lends) == 0 {
return nil
}
return ob.Lends[0]
}
// GetBorrow returns a specific borrow order
func (ob *LendingOrderBook) GetBorrow(orderID common.Hash) *LendingOrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if idx, exists := ob.borrowIndex[orderID]; exists {
return ob.Borrows[idx]
}
return nil
}
// GetLend returns a specific lend order
func (ob *LendingOrderBook) GetLend(orderID common.Hash) *LendingOrderState {
ob.lock.RLock()
defer ob.lock.RUnlock()
if idx, exists := ob.lendIndex[orderID]; exists {
return ob.Lends[idx]
}
return nil
}
// BorrowCount returns the number of borrow orders
func (ob *LendingOrderBook) BorrowCount() int {
ob.lock.RLock()
defer ob.lock.RUnlock()
return len(ob.Borrows)
}
// LendCount returns the number of lend orders
func (ob *LendingOrderBook) LendCount() int {
ob.lock.RLock()
defer ob.lock.RUnlock()
return len(ob.Lends)
}
// GetBorrowVolume returns total borrow volume
func (ob *LendingOrderBook) GetBorrowVolume() *big.Int {
ob.lock.RLock()
defer ob.lock.RUnlock()
total := big.NewInt(0)
for _, borrow := range ob.Borrows {
total = new(big.Int).Add(total, borrow.RemainingQuantity())
}
return total
}
// GetLendVolume returns total lend volume
func (ob *LendingOrderBook) GetLendVolume() *big.Int {
ob.lock.RLock()
defer ob.lock.RUnlock()
total := big.NewInt(0)
for _, lend := range ob.Lends {
total = new(big.Int).Add(total, lend.RemainingQuantity())
}
return total
}
// Copy creates a copy of the order book
func (ob *LendingOrderBook) Copy() *LendingOrderBook {
ob.lock.RLock()
defer ob.lock.RUnlock()
copy := &LendingOrderBook{
PairKey: ob.PairKey,
Borrows: make([]*LendingOrderState, len(ob.Borrows)),
Lends: make([]*LendingOrderState, len(ob.Lends)),
borrowIndex: make(map[common.Hash]int),
lendIndex: make(map[common.Hash]int),
}
for i, borrow := range ob.Borrows {
copy.Borrows[i] = borrow.Copy()
copy.borrowIndex[borrow.ID] = i
}
for i, lend := range ob.Lends {
copy.Lends[i] = lend.Copy()
copy.lendIndex[lend.ID] = i
}
return copy
}
// sortBorrows sorts borrows by interest rate descending
func (ob *LendingOrderBook) sortBorrows() {
sort.Slice(ob.Borrows, func(i, j int) bool {
return ob.Borrows[i].InterestRate.Cmp(ob.Borrows[j].InterestRate) > 0
})
}
// sortLends sorts lends by interest rate ascending
func (ob *LendingOrderBook) sortLends() {
sort.Slice(ob.Lends, func(i, j int) bool {
return ob.Lends[i].InterestRate.Cmp(ob.Lends[j].InterestRate) < 0
})
}
// rebuildBorrowIndex rebuilds the borrow index
func (ob *LendingOrderBook) rebuildBorrowIndex() {
ob.borrowIndex = make(map[common.Hash]int)
for i, borrow := range ob.Borrows {
ob.borrowIndex[borrow.ID] = i
}
}
// rebuildLendIndex rebuilds the lend index
func (ob *LendingOrderBook) rebuildLendIndex() {
ob.lendIndex = make(map[common.Hash]int)
for i, lend := range ob.Lends {
ob.lendIndex[lend.ID] = i
}
}
// GetMarketRate returns the market interest rate
func (ob *LendingOrderBook) GetMarketRate() *big.Int {
ob.lock.RLock()
defer ob.lock.RUnlock()
bestBorrow := ob.GetBestBorrow()
bestLend := ob.GetBestLend()
if bestBorrow == nil && bestLend == nil {
return nil
}
if bestBorrow == nil {
return new(big.Int).Set(bestLend.InterestRate)
}
if bestLend == nil {
return new(big.Int).Set(bestBorrow.InterestRate)
}
// Return midpoint
sum := new(big.Int).Add(bestBorrow.InterestRate, bestLend.InterestRate)
return sum.Div(sum, big.NewInt(2))
}

View file

@ -1,416 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
// Package lendingstate provides lending state management for XDCxLending
package lendingstate
import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
)
// LendingStateDB represents the lending state database
type LendingStateDB struct {
db Database
trie Trie
// Order books cache
orderBooks map[common.Hash]*LendingOrderBook
orderBooksDirty map[common.Hash]struct{}
// Orders cache
orders map[common.Hash]*LendingOrderState
ordersDirty map[common.Hash]struct{}
// Loans cache
loans map[common.Hash]*LoanState
loansDirty map[common.Hash]struct{}
lock sync.Mutex
}
// New creates a new lending state database
func New(root common.Hash, db Database) (*LendingStateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
return &LendingStateDB{
db: db,
trie: tr,
orderBooks: make(map[common.Hash]*LendingOrderBook),
orderBooksDirty: make(map[common.Hash]struct{}),
orders: make(map[common.Hash]*LendingOrderState),
ordersDirty: make(map[common.Hash]struct{}),
loans: make(map[common.Hash]*LoanState),
loansDirty: make(map[common.Hash]struct{}),
}, nil
}
// GetLendingOrder returns a lending order by ID
func (s *LendingStateDB) GetLendingOrder(orderID common.Hash) *LendingOrderState {
s.lock.Lock()
defer s.lock.Unlock()
if order, exists := s.orders[orderID]; exists {
return order
}
// Load from trie
order := s.loadLendingOrder(orderID)
if order != nil {
s.orders[orderID] = order
}
return order
}
// SetLendingOrder sets a lending order in the state
func (s *LendingStateDB) SetLendingOrder(order interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
// Convert order interface to LendingOrderState
orderState := &LendingOrderState{}
// Implementation depends on order type
s.orders[orderState.ID] = orderState
s.ordersDirty[orderState.ID] = struct{}{}
}
// UpdateLendingOrder updates a lending order in the state
func (s *LendingStateDB) UpdateLendingOrder(order interface{}) {
s.SetLendingOrder(order)
}
// DeleteLendingOrder deletes a lending order from the state
func (s *LendingStateDB) DeleteLendingOrder(orderID common.Hash) {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.orders, orderID)
s.ordersDirty[orderID] = struct{}{}
}
// GetLendingOrderBook returns a lending order book by pair key
func (s *LendingStateDB) GetLendingOrderBook(pairKey common.Hash) *LendingOrderBook {
s.lock.Lock()
defer s.lock.Unlock()
if ob, exists := s.orderBooks[pairKey]; exists {
return ob
}
// Load from trie
ob := s.loadLendingOrderBook(pairKey)
if ob != nil {
s.orderBooks[pairKey] = ob
}
return ob
}
// GetOrCreateLendingOrderBook returns or creates a lending order book
func (s *LendingStateDB) GetOrCreateLendingOrderBook(pairKey common.Hash) *LendingOrderBook {
ob := s.GetLendingOrderBook(pairKey)
if ob == nil {
s.lock.Lock()
ob = NewLendingOrderBook(pairKey)
s.orderBooks[pairKey] = ob
s.orderBooksDirty[pairKey] = struct{}{}
s.lock.Unlock()
}
return ob
}
// GetLoan returns a loan by ID
func (s *LendingStateDB) GetLoan(loanID common.Hash) *LoanState {
s.lock.Lock()
defer s.lock.Unlock()
if loan, exists := s.loans[loanID]; exists {
return loan
}
// Load from trie
loan := s.loadLoan(loanID)
if loan != nil {
s.loans[loanID] = loan
}
return loan
}
// SetLoan sets a loan in the state
func (s *LendingStateDB) SetLoan(loan *LoanState) {
s.lock.Lock()
defer s.lock.Unlock()
s.loans[loan.ID] = loan
s.loansDirty[loan.ID] = struct{}{}
}
// UpdateLoan updates a loan in the state
func (s *LendingStateDB) UpdateLoan(loan interface{}) {
// Convert interface to LoanState
if l, ok := loan.(*LoanState); ok {
s.SetLoan(l)
}
}
// GetAllLoans returns all loans
func (s *LendingStateDB) GetAllLoans() []*LoanState {
s.lock.Lock()
defer s.lock.Unlock()
loans := make([]*LoanState, 0, len(s.loans))
for _, loan := range s.loans {
loans = append(loans, loan.Copy())
}
return loans
}
// Commit commits all changes to the underlying database
func (s *LendingStateDB) Commit() (common.Hash, error) {
s.lock.Lock()
defer s.lock.Unlock()
// Commit dirty order books
for pairKey := range s.orderBooksDirty {
if ob, exists := s.orderBooks[pairKey]; exists {
if err := s.commitOrderBook(ob); err != nil {
return common.Hash{}, err
}
}
delete(s.orderBooksDirty, pairKey)
}
// Commit dirty orders
for orderID := range s.ordersDirty {
if order, exists := s.orders[orderID]; exists {
if err := s.commitOrder(order); err != nil {
return common.Hash{}, err
}
}
delete(s.ordersDirty, orderID)
}
// Commit dirty loans
for loanID := range s.loansDirty {
if loan, exists := s.loans[loanID]; exists {
if err := s.commitLoan(loan); err != nil {
return common.Hash{}, err
}
}
delete(s.loansDirty, loanID)
}
// Commit trie
root, err := s.trie.Commit(nil)
if err != nil {
return common.Hash{}, err
}
log.Debug("Lending state committed", "root", root.Hex())
return root, nil
}
// Copy creates a copy of the lending state
func (s *LendingStateDB) Copy() *LendingStateDB {
s.lock.Lock()
defer s.lock.Unlock()
state := &LendingStateDB{
db: s.db,
trie: s.db.CopyTrie(s.trie),
orderBooks: make(map[common.Hash]*LendingOrderBook),
orderBooksDirty: make(map[common.Hash]struct{}),
orders: make(map[common.Hash]*LendingOrderState),
ordersDirty: make(map[common.Hash]struct{}),
loans: make(map[common.Hash]*LoanState),
loansDirty: make(map[common.Hash]struct{}),
}
// Copy order books
for key, ob := range s.orderBooks {
state.orderBooks[key] = ob.Copy()
}
for key := range s.orderBooksDirty {
state.orderBooksDirty[key] = struct{}{}
}
// Copy orders
for key, order := range s.orders {
state.orders[key] = order.Copy()
}
for key := range s.ordersDirty {
state.ordersDirty[key] = struct{}{}
}
// Copy loans
for key, loan := range s.loans {
state.loans[key] = loan.Copy()
}
for key := range s.loansDirty {
state.loansDirty[key] = struct{}{}
}
return state
}
// loadLendingOrder loads a lending order from the trie
func (s *LendingStateDB) loadLendingOrder(orderID common.Hash) *LendingOrderState {
return nil
}
// loadLendingOrderBook loads a lending order book from the trie
func (s *LendingStateDB) loadLendingOrderBook(pairKey common.Hash) *LendingOrderBook {
return nil
}
// loadLoan loads a loan from the trie
func (s *LendingStateDB) loadLoan(loanID common.Hash) *LoanState {
return nil
}
// commitOrderBook commits a lending order book
func (s *LendingStateDB) commitOrderBook(ob *LendingOrderBook) error {
return nil
}
// commitOrder commits a lending order
func (s *LendingStateDB) commitOrder(order *LendingOrderState) error {
return nil
}
// commitLoan commits a loan
func (s *LendingStateDB) commitLoan(loan *LoanState) error {
return nil
}
// Database wraps access to tries and contract code
type Database interface {
// OpenTrie opens the main lending state trie
OpenTrie(root common.Hash) (Trie, error)
// CopyTrie returns an independent copy of the given trie
CopyTrie(Trie) Trie
}
// Trie is a XDCx Merkle Patricia trie
type Trie interface {
GetKey([]byte) []byte
TryGet(key []byte) ([]byte, error)
TryUpdate(key, value []byte) error
TryDelete(key []byte) error
Hash() common.Hash
Commit(onleaf trie.LeafCallback) (common.Hash, error)
NodeIterator(startKey []byte) trie.NodeIterator
}
// NewDatabase creates a new lending state database
func NewDatabase(db ethdb.Database) Database {
return &cachingDB{
db: db,
}
}
type cachingDB struct {
db ethdb.Database
}
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
return nil, nil
}
func (db *cachingDB) CopyTrie(t Trie) Trie {
return nil
}
// LendingOrderState represents a lending order in the state
type LendingOrderState struct {
ID common.Hash
UserAddress common.Address
RelayerAddress common.Address
LendingToken common.Address
CollateralToken common.Address
Side uint8
Type uint8
InterestRate *big.Int
Term uint64
Quantity *big.Int
FilledQuantity *big.Int
Status uint8
Nonce uint64
Timestamp uint64
Signature []byte
}
// Copy creates a copy of the lending order state
func (o *LendingOrderState) Copy() *LendingOrderState {
return &LendingOrderState{
ID: o.ID,
UserAddress: o.UserAddress,
RelayerAddress: o.RelayerAddress,
LendingToken: o.LendingToken,
CollateralToken: o.CollateralToken,
Side: o.Side,
Type: o.Type,
InterestRate: new(big.Int).Set(o.InterestRate),
Term: o.Term,
Quantity: new(big.Int).Set(o.Quantity),
FilledQuantity: new(big.Int).Set(o.FilledQuantity),
Status: o.Status,
Nonce: o.Nonce,
Timestamp: o.Timestamp,
Signature: append([]byte{}, o.Signature...),
}
}
// RemainingQuantity returns the remaining quantity
func (o *LendingOrderState) RemainingQuantity() *big.Int {
return new(big.Int).Sub(o.Quantity, o.FilledQuantity)
}
// IsFilled returns whether the order is filled
func (o *LendingOrderState) IsFilled() bool {
return o.FilledQuantity.Cmp(o.Quantity) >= 0
}
// LoanState represents a loan in the state
type LoanState struct {
ID common.Hash
BorrowerAddress common.Address
LenderAddress common.Address
LendingToken common.Address
CollateralToken common.Address
Principal *big.Int
CollateralAmount *big.Int
InterestRate *big.Int
Term uint64
StartTime uint64
ExpiryTime uint64
Status uint8
LiquidationPrice *big.Int
}
// Copy creates a copy of the loan state
func (l *LoanState) Copy() *LoanState {
return &LoanState{
ID: l.ID,
BorrowerAddress: l.BorrowerAddress,
LenderAddress: l.LenderAddress,
LendingToken: l.LendingToken,
CollateralToken: l.CollateralToken,
Principal: new(big.Int).Set(l.Principal),
CollateralAmount: new(big.Int).Set(l.CollateralAmount),
InterestRate: new(big.Int).Set(l.InterestRate),
Term: l.Term,
StartTime: l.StartTime,
ExpiryTime: l.ExpiryTime,
Status: l.Status,
LiquidationPrice: new(big.Int).Set(l.LiquidationPrice),
}
}

View file

@ -1,176 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"context"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCxlending/lendingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/log"
)
// Liquidator handles loan liquidation
type Liquidator struct {
lending *XDCxLending
lock sync.Mutex
}
// NewLiquidator creates a new liquidator
func NewLiquidator(lending *XDCxLending) *Liquidator {
return &Liquidator{
lending: lending,
}
}
// Liquidate liquidates an undercollateralized loan
func (l *Liquidator) Liquidate(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, loanID common.Hash) error {
l.lock.Lock()
defer l.lock.Unlock()
loan := lendingState.GetLoan(loanID)
if loan == nil {
return ErrLoanNotFound
}
// Check if loan is eligible for liquidation
collateralPrice := l.getCollateralPrice(statedb, loan.CollateralToken)
if !l.isLiquidatable(loan, collateralPrice) {
return ErrInsufficientCollateral
}
// Perform liquidation
if err := l.performLiquidation(statedb, lendingState, loan, collateralPrice); err != nil {
return err
}
// Update loan status
loan.Status = LoanStatusLiquidated
lendingState.UpdateLoan(loan)
log.Info("Loan liquidated", "loanID", loanID.Hex())
return nil
}
// CheckLoansForLiquidation checks all active loans for liquidation
func (l *Liquidator) CheckLoansForLiquidation(lendingState *lendingstate.LendingStateDB) ([]common.Hash, error) {
l.lock.Lock()
defer l.lock.Unlock()
liquidatableLoans := make([]common.Hash, 0)
allLoans := lendingState.GetAllLoans()
for _, loan := range allLoans {
if loan.Status != LoanStatusActive {
continue
}
// Check collateral ratio
collateralPrice := big.NewInt(1e18) // Placeholder - should get from oracle
if l.isLiquidatable(loan, collateralPrice) {
liquidatableLoans = append(liquidatableLoans, loan.ID)
}
}
return liquidatableLoans, nil
}
// isLiquidatable checks if a loan is eligible for liquidation
func (l *Liquidator) isLiquidatable(loan *Loan, collateralPrice *big.Int) bool {
// Calculate current collateral ratio
ratio := loan.GetCollateralRatio(collateralPrice)
// Compare with liquidation threshold
liquidationRate := l.lending.GetConfig().LiquidationRate
return ratio.Cmp(liquidationRate) < 0
}
// performLiquidation performs the actual liquidation
func (l *Liquidator) performLiquidation(statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, loan *Loan, collateralPrice *big.Int) error {
// Calculate liquidation amounts
principal := loan.Principal
interest := l.lending.calculateInterest(loan)
totalDebt := new(big.Int).Add(principal, interest)
// Calculate collateral to sell
collateralToSell := new(big.Int).Mul(totalDebt, big.NewInt(1e18))
collateralToSell = collateralToSell.Div(collateralToSell, collateralPrice)
// Add liquidation penalty (5%)
penalty := new(big.Int).Mul(collateralToSell, big.NewInt(5))
penalty = penalty.Div(penalty, big.NewInt(100))
totalCollateralSold := new(big.Int).Add(collateralToSell, penalty)
// Ensure we don't sell more than available
if totalCollateralSold.Cmp(loan.CollateralAmount) > 0 {
totalCollateralSold = loan.CollateralAmount
}
// Calculate remaining collateral for borrower
remainingCollateral := new(big.Int).Sub(loan.CollateralAmount, totalCollateralSold)
log.Debug("Liquidation performed",
"loanID", loan.ID.Hex(),
"totalDebt", totalDebt,
"collateralSold", totalCollateralSold,
"remainingCollateral", remainingCollateral,
)
return nil
}
// getCollateralPrice gets the current price of the collateral token
func (l *Liquidator) getCollateralPrice(statedb *state.StateDB, collateralToken common.Address) *big.Int {
// This should query the price oracle
// Placeholder implementation
return big.NewInt(1e18)
}
// LiquidationEvent represents a liquidation event
type LiquidationEvent struct {
LoanID common.Hash `json:"loanId"`
Borrower common.Address `json:"borrower"`
Lender common.Address `json:"lender"`
Liquidator common.Address `json:"liquidator"`
CollateralSold *big.Int `json:"collateralSold"`
DebtRepaid *big.Int `json:"debtRepaid"`
LiquidationBonus *big.Int `json:"liquidationBonus"`
BlockNumber uint64 `json:"blockNumber"`
TxHash common.Hash `json:"txHash"`
}
// RecallLoans recalls loans that have exceeded their term
func (l *Liquidator) RecallLoans(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, currentTime uint64) error {
l.lock.Lock()
defer l.lock.Unlock()
allLoans := lendingState.GetAllLoans()
for _, loan := range allLoans {
if loan.Status != LoanStatusActive {
continue
}
if loan.IsExpired(currentTime) {
log.Info("Recalling expired loan", "loanID", loan.ID.Hex())
// Mark for liquidation or automatic repayment
loan.Status = LoanStatusDefaulted
lendingState.UpdateLoan(loan)
}
}
return nil
}
// GetLiquidationPrice calculates the price at which a loan becomes liquidatable
func (l *Liquidator) GetLiquidationPrice(loan *Loan) *big.Int {
// Liquidation price = (Principal * LiquidationRate) / (CollateralAmount * 100)
liquidationRate := l.lending.GetConfig().LiquidationRate
numerator := new(big.Int).Mul(loan.Principal, liquidationRate)
denominator := new(big.Int).Mul(loan.CollateralAmount, big.NewInt(100))
price := new(big.Int).Div(numerator, denominator)
return price
}

View file

@ -1,259 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"crypto/ecdsa"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// OrderSide represents the side of a lending order (borrow/lend)
type OrderSide uint8
const (
// Borrow represents a borrow order
Borrow OrderSide = iota
// Lend represents a lend order
Lend
)
// OrderType represents the type of a lending order
type OrderType uint8
const (
// LimitOrder represents a limit order
LimitOrder OrderType = iota
// MarketOrder represents a market order
MarketOrder
)
// OrderStatus represents the status of a lending order
type OrderStatus uint8
const (
// OrderStatusNew represents a new order
OrderStatusNew OrderStatus = iota
// OrderStatusPartialFilled represents a partially filled order
OrderStatusPartialFilled
// OrderStatusFilled represents a filled order
OrderStatusFilled
// OrderStatusCancelled represents a cancelled order
OrderStatusCancelled
// OrderStatusRejected represents a rejected order
OrderStatusRejected
)
// LoanStatus represents the status of a loan
type LoanStatus uint8
const (
// LoanStatusActive represents an active loan
LoanStatusActive LoanStatus = iota
// LoanStatusRepaid represents a repaid loan
LoanStatusRepaid
// LoanStatusLiquidated represents a liquidated loan
LoanStatusLiquidated
// LoanStatusDefaulted represents a defaulted loan
LoanStatusDefaulted
)
// LendingOrder represents a lending order
type LendingOrder struct {
ID common.Hash `json:"id"`
UserAddress common.Address `json:"userAddress"`
RelayerAddress common.Address `json:"relayerAddress"`
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Side OrderSide `json:"side"`
Type OrderType `json:"type"`
InterestRate *big.Int `json:"interestRate"` // Annual rate in basis points
Term uint64 `json:"term"` // Duration in seconds
Quantity *big.Int `json:"quantity"`
FilledQuantity *big.Int `json:"filledQuantity"`
Status OrderStatus `json:"status"`
Nonce uint64 `json:"nonce"`
Timestamp uint64 `json:"timestamp"`
Signature []byte `json:"signature"`
}
// NewLendingOrder creates a new lending order
func NewLendingOrder(
userAddress common.Address,
lendingToken, collateralToken common.Address,
side OrderSide,
orderType OrderType,
interestRate *big.Int,
term uint64,
quantity *big.Int,
nonce uint64,
relayerAddress common.Address,
) *LendingOrder {
order := &LendingOrder{
UserAddress: userAddress,
RelayerAddress: relayerAddress,
LendingToken: lendingToken,
CollateralToken: collateralToken,
Side: side,
Type: orderType,
InterestRate: new(big.Int).Set(interestRate),
Term: term,
Quantity: new(big.Int).Set(quantity),
FilledQuantity: big.NewInt(0),
Status: OrderStatusNew,
Nonce: nonce,
}
order.ID = order.ComputeHash()
return order
}
// ComputeHash computes the hash of the lending order
func (o *LendingOrder) ComputeHash() common.Hash {
data := append(o.UserAddress.Bytes(), o.LendingToken.Bytes()...)
data = append(data, o.CollateralToken.Bytes()...)
data = append(data, byte(o.Side))
data = append(data, byte(o.Type))
data = append(data, common.BigToHash(o.InterestRate).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(o.Term))).Bytes()...)
data = append(data, common.BigToHash(o.Quantity).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(o.Nonce))).Bytes()...)
data = append(data, o.RelayerAddress.Bytes()...)
return crypto.Keccak256Hash(data)
}
// Sign signs the lending order with the given private key
func (o *LendingOrder) Sign(privateKey *ecdsa.PrivateKey) error {
hash := o.ComputeHash()
sig, err := crypto.Sign(hash.Bytes(), privateKey)
if err != nil {
return err
}
o.Signature = sig
return nil
}
// VerifySignature verifies the lending order signature
func (o *LendingOrder) VerifySignature() bool {
if len(o.Signature) != 65 {
return false
}
hash := o.ComputeHash()
pubKey, err := crypto.SigToPub(hash.Bytes(), o.Signature)
if err != nil {
return false
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
return recoveredAddr == o.UserAddress
}
// RemainingQuantity returns the remaining quantity to be filled
func (o *LendingOrder) RemainingQuantity() *big.Int {
return new(big.Int).Sub(o.Quantity, o.FilledQuantity)
}
// IsFilled returns whether the order is fully filled
func (o *LendingOrder) IsFilled() bool {
return o.FilledQuantity.Cmp(o.Quantity) >= 0
}
// Clone creates a copy of the lending order
func (o *LendingOrder) Clone() *LendingOrder {
return &LendingOrder{
ID: o.ID,
UserAddress: o.UserAddress,
RelayerAddress: o.RelayerAddress,
LendingToken: o.LendingToken,
CollateralToken: o.CollateralToken,
Side: o.Side,
Type: o.Type,
InterestRate: new(big.Int).Set(o.InterestRate),
Term: o.Term,
Quantity: new(big.Int).Set(o.Quantity),
FilledQuantity: new(big.Int).Set(o.FilledQuantity),
Status: o.Status,
Nonce: o.Nonce,
Timestamp: o.Timestamp,
Signature: append([]byte{}, o.Signature...),
}
}
// PairKey returns the lending pair key
func (o *LendingOrder) PairKey() common.Hash {
return GetLendingPairKey(o.LendingToken, o.CollateralToken, o.Term)
}
// GetLendingPairKey returns the lending pair key
func GetLendingPairKey(lendingToken, collateralToken common.Address, term uint64) common.Hash {
data := append(lendingToken.Bytes(), collateralToken.Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(term))).Bytes()...)
return crypto.Keccak256Hash(data)
}
// Loan represents an active loan
type Loan struct {
ID common.Hash `json:"id"`
BorrowerAddress common.Address `json:"borrowerAddress"`
LenderAddress common.Address `json:"lenderAddress"`
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Principal *big.Int `json:"principal"`
CollateralAmount *big.Int `json:"collateralAmount"`
InterestRate *big.Int `json:"interestRate"`
Term uint64 `json:"term"`
StartTime uint64 `json:"startTime"`
ExpiryTime uint64 `json:"expiryTime"`
Status LoanStatus `json:"status"`
LiquidationPrice *big.Int `json:"liquidationPrice"`
}
// NewLoan creates a new loan
func NewLoan(
borrower, lender common.Address,
lendingToken, collateralToken common.Address,
principal, collateral *big.Int,
interestRate *big.Int,
term, startTime uint64,
) *Loan {
loan := &Loan{
BorrowerAddress: borrower,
LenderAddress: lender,
LendingToken: lendingToken,
CollateralToken: collateralToken,
Principal: new(big.Int).Set(principal),
CollateralAmount: new(big.Int).Set(collateral),
InterestRate: new(big.Int).Set(interestRate),
Term: term,
StartTime: startTime,
ExpiryTime: startTime + term,
Status: LoanStatusActive,
}
loan.ID = loan.ComputeHash()
return loan
}
// ComputeHash computes the loan hash
func (l *Loan) ComputeHash() common.Hash {
data := append(l.BorrowerAddress.Bytes(), l.LenderAddress.Bytes()...)
data = append(data, l.LendingToken.Bytes()...)
data = append(data, l.CollateralToken.Bytes()...)
data = append(data, common.BigToHash(l.Principal).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(l.StartTime))).Bytes()...)
return crypto.Keccak256Hash(data)
}
// IsExpired returns whether the loan has expired
func (l *Loan) IsExpired(currentTime uint64) bool {
return currentTime >= l.ExpiryTime
}
// GetCollateralRatio returns the current collateral ratio
func (l *Loan) GetCollateralRatio(collateralPrice *big.Int) *big.Int {
// Ratio = (CollateralAmount * CollateralPrice) / Principal * 100
collateralValue := new(big.Int).Mul(l.CollateralAmount, collateralPrice)
ratio := new(big.Int).Mul(collateralValue, big.NewInt(100))
ratio = ratio.Div(ratio, l.Principal)
return ratio
}

View file

@ -1,306 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"context"
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/XDCxlending/lendingstate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/log"
)
var (
// ErrInsufficientLendingBalance is returned when lending balance is insufficient
ErrInsufficientLendingBalance = errors.New("insufficient lending balance")
// ErrOrderAlreadyExists is returned when order already exists
ErrOrderAlreadyExists = errors.New("order already exists")
// ErrOrderCannotBeCancelled is returned when order cannot be cancelled
ErrOrderCannotBeCancelled = errors.New("order cannot be cancelled")
// ErrInvalidSignature is returned when signature is invalid
ErrInvalidSignature = errors.New("invalid signature")
)
// OrderProcessor handles lending order processing
type OrderProcessor struct {
lending *XDCxLending
lock sync.Mutex
}
// NewOrderProcessor creates a new order processor
func NewOrderProcessor(lending *XDCxLending) *OrderProcessor {
return &OrderProcessor{
lending: lending,
}
}
// Process processes a lending order and returns matched trades
func (op *OrderProcessor) Process(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, order *LendingOrder) ([]*LendingTrade, error) {
op.lock.Lock()
defer op.lock.Unlock()
// Validate order
if err := op.validateOrder(order); err != nil {
return nil, err
}
// Verify signature
if !order.VerifySignature() {
return nil, ErrInvalidSignature
}
// Check balance and collateral
if err := op.checkBalanceAndCollateral(statedb, order); err != nil {
return nil, err
}
// Match order
trades, err := op.matchOrder(lendingState, order)
if err != nil {
return nil, err
}
// If order is not fully filled, add to order book
if !order.IsFilled() && order.Type == LimitOrder {
if err := op.addToOrderBook(lendingState, order); err != nil {
return nil, err
}
}
log.Debug("Lending order processed", "orderID", order.ID.Hex(), "trades", len(trades))
return trades, nil
}
// Cancel cancels an existing lending order
func (op *OrderProcessor) Cancel(ctx context.Context, statedb *state.StateDB, lendingState *lendingstate.LendingStateDB, orderID common.Hash) error {
op.lock.Lock()
defer op.lock.Unlock()
// Get order from state
order := lendingState.GetLendingOrder(orderID)
if order == nil {
return ErrLoanNotFound
}
// Check if order can be cancelled
if order.Status == OrderStatusFilled || order.Status == OrderStatusCancelled {
return ErrOrderCannotBeCancelled
}
// Remove from order book
if err := op.removeFromOrderBook(lendingState, order); err != nil {
return err
}
// Update order status
order.Status = OrderStatusCancelled
lendingState.UpdateLendingOrder(order)
log.Debug("Lending order cancelled", "orderID", orderID.Hex())
return nil
}
// validateOrder validates a lending order
func (op *OrderProcessor) validateOrder(order *LendingOrder) error {
if order.InterestRate == nil || order.InterestRate.Sign() < 0 {
return ErrInvalidInterestRate
}
if order.Term == 0 {
return ErrInvalidTerm
}
if order.Quantity == nil || order.Quantity.Sign() <= 0 {
return errors.New("invalid quantity")
}
return nil
}
// checkBalanceAndCollateral checks if user has sufficient balance and collateral
func (op *OrderProcessor) checkBalanceAndCollateral(statedb *state.StateDB, order *LendingOrder) error {
if order.Side == Borrow {
// Borrowers need collateral
collateralNeeded := op.calculateRequiredCollateral(order)
balance := op.getTokenBalance(statedb, order.CollateralToken, order.UserAddress)
if balance.Cmp(collateralNeeded) < 0 {
return ErrInsufficientCollateral
}
} else {
// Lenders need the lending token
balance := op.getTokenBalance(statedb, order.LendingToken, order.UserAddress)
if balance.Cmp(order.Quantity) < 0 {
return ErrInsufficientLendingBalance
}
}
return nil
}
// calculateRequiredCollateral calculates the required collateral for a borrow order
func (op *OrderProcessor) calculateRequiredCollateral(order *LendingOrder) *big.Int {
// Required collateral = quantity * minCollateralRatio / 100
minCollateral := op.lending.GetConfig().MinCollateral
required := new(big.Int).Mul(order.Quantity, minCollateral)
required = required.Div(required, big.NewInt(100))
return required
}
// getTokenBalance gets the token balance for a user
func (op *OrderProcessor) getTokenBalance(statedb *state.StateDB, token, user common.Address) *big.Int {
// This would call the ERC20 balanceOf function
// Simplified implementation
return statedb.GetBalance(user).ToBig()
}
// matchOrder matches a lending order against the order book
func (op *OrderProcessor) matchOrder(lendingState *lendingstate.LendingStateDB, order *LendingOrder) ([]*LendingTrade, error) {
var trades []*LendingTrade
pairKey := order.PairKey()
orderBook := lendingState.GetLendingOrderBook(pairKey)
if orderBook == nil {
return trades, nil
}
// Match based on order side
if order.Side == Borrow {
trades = op.matchBorrowOrder(lendingState, orderBook, order)
} else {
trades = op.matchLendOrder(lendingState, orderBook, order)
}
return trades, nil
}
// matchBorrowOrder matches a borrow order against lend orders
func (op *OrderProcessor) matchBorrowOrder(lendingState *lendingstate.LendingStateDB, orderBook *lendingstate.LendingOrderBook, borrowOrder *LendingOrder) []*LendingTrade {
var trades []*LendingTrade
// Match against lend orders from lowest to highest interest rate
for !borrowOrder.IsFilled() {
bestLend := orderBook.GetBestLend()
if bestLend == nil || bestLend.InterestRate.Cmp(borrowOrder.InterestRate) > 0 {
break
}
trade := op.executeTrade(borrowOrder, bestLend)
trades = append(trades, trade)
if bestLend.IsFilled() {
orderBook.RemoveLend(bestLend.ID)
} else {
lendingState.UpdateLendingOrder(bestLend)
}
}
return trades
}
// matchLendOrder matches a lend order against borrow orders
func (op *OrderProcessor) matchLendOrder(lendingState *lendingstate.LendingStateDB, orderBook *lendingstate.LendingOrderBook, lendOrder *LendingOrder) []*LendingTrade {
var trades []*LendingTrade
// Match against borrow orders from highest to lowest interest rate
for !lendOrder.IsFilled() {
bestBorrow := orderBook.GetBestBorrow()
if bestBorrow == nil || bestBorrow.InterestRate.Cmp(lendOrder.InterestRate) < 0 {
break
}
trade := op.executeTrade(bestBorrow, lendOrder)
trades = append(trades, trade)
if bestBorrow.IsFilled() {
orderBook.RemoveBorrow(bestBorrow.ID)
} else {
lendingState.UpdateLendingOrder(bestBorrow)
}
}
return trades
}
// executeTrade executes a lending trade between two orders
func (op *OrderProcessor) executeTrade(borrowOrder, lendOrder *LendingOrder) *LendingTrade {
// Determine trade quantity
borrowRemaining := borrowOrder.RemainingQuantity()
lendRemaining := lendOrder.RemainingQuantity()
var tradeQuantity *big.Int
if borrowRemaining.Cmp(lendRemaining) < 0 {
tradeQuantity = borrowRemaining
} else {
tradeQuantity = lendRemaining
}
// Use market maker's interest rate
tradeInterestRate := lendOrder.InterestRate
// Update filled quantities
borrowOrder.FilledQuantity = new(big.Int).Add(borrowOrder.FilledQuantity, tradeQuantity)
lendOrder.FilledQuantity = new(big.Int).Add(lendOrder.FilledQuantity, tradeQuantity)
// Update order statuses
if borrowOrder.IsFilled() {
borrowOrder.Status = OrderStatusFilled
} else {
borrowOrder.Status = OrderStatusPartialFilled
}
if lendOrder.IsFilled() {
lendOrder.Status = OrderStatusFilled
} else {
lendOrder.Status = OrderStatusPartialFilled
}
// Calculate collateral
collateral := op.calculateRequiredCollateral(borrowOrder)
// Create trade
return NewLendingTrade(
borrowOrder.ID,
lendOrder.ID,
borrowOrder.UserAddress,
lendOrder.UserAddress,
borrowOrder.LendingToken,
borrowOrder.CollateralToken,
tradeQuantity,
collateral,
tradeInterestRate,
borrowOrder.Term,
)
}
// addToOrderBook adds an order to the order book
func (op *OrderProcessor) addToOrderBook(lendingState *lendingstate.LendingStateDB, order *LendingOrder) error {
pairKey := order.PairKey()
orderBook := lendingState.GetOrCreateLendingOrderBook(pairKey)
if order.Side == Borrow {
orderBook.AddBorrow(order)
} else {
orderBook.AddLend(order)
}
lendingState.SetLendingOrder(order)
return nil
}
// removeFromOrderBook removes an order from the order book
func (op *OrderProcessor) removeFromOrderBook(lendingState *lendingstate.LendingStateDB, order *LendingOrder) error {
pairKey := order.PairKey()
orderBook := lendingState.GetLendingOrderBook(pairKey)
if orderBook == nil {
return nil
}
if order.Side == Borrow {
orderBook.RemoveBorrow(order.ID)
} else {
orderBook.RemoveLend(order.ID)
}
return nil
}

View file

@ -1,162 +0,0 @@
// Copyright 2019 XDC Network
// This file is part of the XDC library.
package XDCxlending
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// LendingTradeStatus represents the status of a lending trade
type LendingTradeStatus uint8
const (
// TradeStatusPending represents a pending trade
TradeStatusPending LendingTradeStatus = iota
// TradeStatusSettled represents a settled trade
TradeStatusSettled
// TradeStatusFailed represents a failed trade
TradeStatusFailed
)
// LendingTrade represents a matched lending trade
type LendingTrade struct {
hash common.Hash
BorrowOrderID common.Hash `json:"borrowOrderId"`
LendOrderID common.Hash `json:"lendOrderId"`
Borrower common.Address `json:"borrower"`
Lender common.Address `json:"lender"`
LendingToken common.Address `json:"lendingToken"`
CollateralToken common.Address `json:"collateralToken"`
Principal *big.Int `json:"principal"`
CollateralAmount *big.Int `json:"collateralAmount"`
InterestRate *big.Int `json:"interestRate"`
Term uint64 `json:"term"`
BorrowFee *big.Int `json:"borrowFee"`
LendFee *big.Int `json:"lendFee"`
Timestamp uint64 `json:"timestamp"`
Status LendingTradeStatus `json:"status"`
BlockNumber uint64 `json:"blockNumber"`
TxHash common.Hash `json:"txHash"`
LoanID common.Hash `json:"loanId"`
}
// NewLendingTrade creates a new lending trade
func NewLendingTrade(
borrowOrderID, lendOrderID common.Hash,
borrower, lender common.Address,
lendingToken, collateralToken common.Address,
principal, collateral *big.Int,
interestRate *big.Int,
term uint64,
) *LendingTrade {
trade := &LendingTrade{
BorrowOrderID: borrowOrderID,
LendOrderID: lendOrderID,
Borrower: borrower,
Lender: lender,
LendingToken: lendingToken,
CollateralToken: collateralToken,
Principal: new(big.Int).Set(principal),
CollateralAmount: new(big.Int).Set(collateral),
InterestRate: new(big.Int).Set(interestRate),
Term: term,
BorrowFee: big.NewInt(0),
LendFee: big.NewInt(0),
Status: TradeStatusPending,
}
trade.hash = trade.ComputeHash()
return trade
}
// ComputeHash computes the hash of the lending trade
func (t *LendingTrade) ComputeHash() common.Hash {
data := append(t.BorrowOrderID.Bytes(), t.LendOrderID.Bytes()...)
data = append(data, t.Borrower.Bytes()...)
data = append(data, t.Lender.Bytes()...)
data = append(data, t.LendingToken.Bytes()...)
data = append(data, t.CollateralToken.Bytes()...)
data = append(data, common.BigToHash(t.Principal).Bytes()...)
data = append(data, common.BigToHash(t.CollateralAmount).Bytes()...)
data = append(data, common.BigToHash(t.InterestRate).Bytes()...)
data = append(data, common.BigToHash(big.NewInt(int64(t.Term))).Bytes()...)
return crypto.Keccak256Hash(data)
}
// Hash returns the trade hash
func (t *LendingTrade) Hash() common.Hash {
if t.hash == (common.Hash{}) {
t.hash = t.ComputeHash()
}
return t.hash
}
// SetFees sets the borrower and lender fees
func (t *LendingTrade) SetFees(borrowFee, lendFee *big.Int) {
if borrowFee != nil {
t.BorrowFee = new(big.Int).Set(borrowFee)
}
if lendFee != nil {
t.LendFee = new(big.Int).Set(lendFee)
}
}
// SetSettled marks the trade as settled
func (t *LendingTrade) SetSettled(blockNumber uint64, txHash common.Hash, loanID common.Hash) {
t.Status = TradeStatusSettled
t.BlockNumber = blockNumber
t.TxHash = txHash
t.LoanID = loanID
}
// SetFailed marks the trade as failed
func (t *LendingTrade) SetFailed() {
t.Status = TradeStatusFailed
}
// PairKey returns the lending pair key
func (t *LendingTrade) PairKey() common.Hash {
return GetLendingPairKey(t.LendingToken, t.CollateralToken, t.Term)
}
// CalculateInterest calculates the interest for the loan
func (t *LendingTrade) CalculateInterest() *big.Int {
// Simple interest: Principal * Rate * Time / (365 * 100)
interest := new(big.Int).Mul(t.Principal, t.InterestRate)
interest = interest.Mul(interest, big.NewInt(int64(t.Term)))
interest = interest.Div(interest, big.NewInt(365*24*3600*100))
return interest
}
// TotalRepayment calculates the total repayment amount
func (t *LendingTrade) TotalRepayment() *big.Int {
interest := t.CalculateInterest()
return new(big.Int).Add(t.Principal, interest)
}
// Clone creates a copy of the lending trade
func (t *LendingTrade) Clone() *LendingTrade {
return &LendingTrade{
hash: t.hash,
BorrowOrderID: t.BorrowOrderID,
LendOrderID: t.LendOrderID,
Borrower: t.Borrower,
Lender: t.Lender,
LendingToken: t.LendingToken,
CollateralToken: t.CollateralToken,
Principal: new(big.Int).Set(t.Principal),
CollateralAmount: new(big.Int).Set(t.CollateralAmount),
InterestRate: new(big.Int).Set(t.InterestRate),
Term: t.Term,
BorrowFee: new(big.Int).Set(t.BorrowFee),
LendFee: new(big.Int).Set(t.LendFee),
Timestamp: t.Timestamp,
Status: t.Status,
BlockNumber: t.BlockNumber,
TxHash: t.TxHash,
LoanID: t.LoanID,
}
}

View file

@ -1,258 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package utils
import (
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/node"
"github.com/urfave/cli/v2"
)
// XDC-specific command line flags
var (
// XDPoS flags
XDPoSRewardFlag = &cli.BoolFlag{
Name: "xdpos.rewards",
Usage: "Enable block rewards for validators",
Value: true,
Category: "XDPoS",
}
XDPoSSlashingFlag = &cli.BoolFlag{
Name: "xdpos.slashing",
Usage: "Enable slashing for misbehaving validators",
Value: true,
Category: "XDPoS",
}
XDPoSValidatorFlag = &cli.StringFlag{
Name: "xdpos.validator",
Usage: "Public address for block validation (coinbase)",
Category: "XDPoS",
}
XDPoSGapFlag = &cli.Uint64Flag{
Name: "xdpos.gap",
Usage: "Gap for snapshot creation in XDPoS",
Value: 450,
Category: "XDPoS",
}
XDPoSEpochFlag = &cli.Uint64Flag{
Name: "xdpos.epoch",
Usage: "Epoch length for validator set updates",
Value: 900,
Category: "XDPoS",
}
// XDCx flags
XDCxEnableFlag = &cli.BoolFlag{
Name: "xdcx",
Usage: "Enable XDC decentralized exchange",
Value: false,
Category: "XDCx",
}
XDCxDataDirFlag = &cli.StringFlag{
Name: "xdcx.datadir",
Usage: "Data directory for XDCx",
Category: "XDCx",
}
// XDCxLending flags
XDCxLendingEnableFlag = &cli.BoolFlag{
Name: "xdcxlending",
Usage: "Enable XDC lending protocol",
Value: false,
Category: "XDCxLending",
}
XDCxLendingDataDirFlag = &cli.StringFlag{
Name: "xdcxlending.datadir",
Usage: "Data directory for XDCxLending",
Category: "XDCxLending",
}
// Network flags
XDCMainnetFlag = &cli.BoolFlag{
Name: "xdc.mainnet",
Usage: "Connect to XDC mainnet",
Category: "XDC Network",
}
XDCTestnetFlag = &cli.BoolFlag{
Name: "xdc.testnet",
Usage: "Connect to XDC Apothem testnet",
Category: "XDC Network",
}
XDCDevnetFlag = &cli.BoolFlag{
Name: "xdc.devnet",
Usage: "Connect to XDC devnet",
Category: "XDC Network",
}
// Sync flags
XDCSnapSyncFlag = &cli.BoolFlag{
Name: "xdc.snapsync",
Usage: "Enable XDC snapshot sync",
Value: false,
Category: "XDC Sync",
}
XDCSnapShotBlockFlag = &cli.Uint64Flag{
Name: "xdc.snapshot.block",
Usage: "Snapshot block number for sync",
Category: "XDC Sync",
}
XDCCheckpointIntervalFlag = &cli.Uint64Flag{
Name: "xdc.checkpoint.interval",
Usage: "Checkpoint interval in blocks",
Value: 900,
Category: "XDC Sync",
}
// Masternode flags
MasternodeFlag = &cli.BoolFlag{
Name: "masternode",
Usage: "Run as a masternode",
Value: false,
Category: "Masternode",
}
MasternodeKeyFlag = &cli.StringFlag{
Name: "masternode.key",
Usage: "Masternode private key for signing",
Category: "Masternode",
}
MasternodeCoinbaseFlag = &cli.StringFlag{
Name: "masternode.coinbase",
Usage: "Masternode coinbase address",
Category: "Masternode",
}
)
// XDCFlags contains all XDC-specific flags
var XDCFlags = []cli.Flag{
XDPoSRewardFlag,
XDPoSSlashingFlag,
XDPoSValidatorFlag,
XDPoSGapFlag,
XDPoSEpochFlag,
XDCxEnableFlag,
XDCxDataDirFlag,
XDCxLendingEnableFlag,
XDCxLendingDataDirFlag,
XDCMainnetFlag,
XDCTestnetFlag,
XDCDevnetFlag,
XDCSnapSyncFlag,
XDCSnapShotBlockFlag,
XDCCheckpointIntervalFlag,
MasternodeFlag,
MasternodeKeyFlag,
MasternodeCoinbaseFlag,
}
// SetXDCConfig applies XDC-specific configuration
func SetXDCConfig(ctx *cli.Context, cfg *ethconfig.Config) {
// XDPoS configuration
if ctx.IsSet(XDPoSRewardFlag.Name) {
cfg.XDPoSRewards = ctx.Bool(XDPoSRewardFlag.Name)
}
if ctx.IsSet(XDPoSSlashingFlag.Name) {
cfg.XDPoSSlashing = ctx.Bool(XDPoSSlashingFlag.Name)
}
if ctx.IsSet(XDPoSGapFlag.Name) {
cfg.XDPoSGap = ctx.Uint64(XDPoSGapFlag.Name)
}
if ctx.IsSet(XDPoSEpochFlag.Name) {
cfg.XDPoSEpoch = ctx.Uint64(XDPoSEpochFlag.Name)
}
// XDCx configuration
if ctx.IsSet(XDCxEnableFlag.Name) {
cfg.XDCxEnabled = ctx.Bool(XDCxEnableFlag.Name)
}
// XDCxLending configuration
if ctx.IsSet(XDCxLendingEnableFlag.Name) {
cfg.XDCxLendingEnabled = ctx.Bool(XDCxLendingEnableFlag.Name)
}
// Sync configuration
if ctx.IsSet(XDCSnapSyncFlag.Name) {
cfg.XDCSnapSync = ctx.Bool(XDCSnapSyncFlag.Name)
}
if ctx.IsSet(XDCCheckpointIntervalFlag.Name) {
cfg.XDCCheckpointInterval = ctx.Uint64(XDCCheckpointIntervalFlag.Name)
}
}
// SetXDCNodeConfig applies XDC-specific node configuration
func SetXDCNodeConfig(ctx *cli.Context, cfg *node.Config) {
// Set XDC-specific data directories
if ctx.IsSet(XDCxDataDirFlag.Name) {
// Configure XDCx data directory
}
if ctx.IsSet(XDCxLendingDataDirFlag.Name) {
// Configure XDCxLending data directory
}
}
// SetXDCNetworkConfig configures the network for XDC
func SetXDCNetworkConfig(ctx *cli.Context, cfg *ethconfig.Config) {
if ctx.Bool(XDCMainnetFlag.Name) {
// Configure for mainnet
cfg.NetworkId = 50
} else if ctx.Bool(XDCTestnetFlag.Name) {
// Configure for Apothem testnet
cfg.NetworkId = 51
} else if ctx.Bool(XDCDevnetFlag.Name) {
// Configure for devnet
cfg.NetworkId = 551
}
}
// MasternodeConfig holds masternode configuration
type MasternodeConfig struct {
Enable bool
Key string
Coinbase string
}
// SetMasternodeConfig sets masternode configuration
func SetMasternodeConfig(ctx *cli.Context) *MasternodeConfig {
config := &MasternodeConfig{
Enable: ctx.Bool(MasternodeFlag.Name),
}
if config.Enable {
if ctx.IsSet(MasternodeKeyFlag.Name) {
config.Key = ctx.String(MasternodeKeyFlag.Name)
}
if ctx.IsSet(MasternodeCoinbaseFlag.Name) {
config.Coinbase = ctx.String(MasternodeCoinbaseFlag.Name)
}
}
return config
}
// ValidateXDCFlags validates XDC-specific flags
func ValidateXDCFlags(ctx *cli.Context) error {
// Validate that mainnet, testnet, and devnet are mutually exclusive
networks := 0
if ctx.Bool(XDCMainnetFlag.Name) {
networks++
}
if ctx.Bool(XDCTestnetFlag.Name) {
networks++
}
if ctx.Bool(XDCDevnetFlag.Name) {
networks++
}
if networks > 1 {
return cli.Exit("Cannot specify multiple XDC networks", 1)
}
// Validate masternode configuration
if ctx.Bool(MasternodeFlag.Name) {
if !ctx.IsSet(MasternodeCoinbaseFlag.Name) {
return cli.Exit("Masternode coinbase address is required", 1)
}
}
return nil
}

View file

@ -0,0 +1,14 @@
// XDPoS Engines - Common definitions for geth 1.17
package engines
import (
"github.com/ethereum/go-ethereum/common"
)
const (
ExtraVanity = 32 // Bytes reserved for signer vanity
ExtraSeal = 65 // Bytes reserved for signer seal
)
// SignerFn is a signer callback function
type SignerFn func(common.Address, []byte) ([]byte, error)

View file

@ -1,400 +0,0 @@
// 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{}

View file

@ -1,361 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package engines
import (
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
// mockChain implements consensus.ChainHeaderReader for testing
type mockChain struct {
headers map[common.Hash]*types.Header
}
func newMockChain() *mockChain {
return &mockChain{
headers: make(map[common.Hash]*types.Header),
}
}
func (m *mockChain) Config() *params.ChainConfig {
return &params.ChainConfig{}
}
func (m *mockChain) CurrentHeader() *types.Header {
return nil
}
func (m *mockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
return m.headers[hash]
}
func (m *mockChain) GetHeaderByNumber(number uint64) *types.Header {
for _, h := range m.headers {
if h.Number.Uint64() == number {
return h
}
}
return nil
}
func (m *mockChain) GetHeaderByHash(hash common.Hash) *types.Header {
return m.headers[hash]
}
func (m *mockChain) GetTd(hash common.Hash, number uint64) *big.Int {
return big.NewInt(1)
}
func (m *mockChain) addHeader(header *types.Header) {
m.headers[header.Hash()] = header
}
// mockDB implements Database for testing
type mockDB struct {
data map[string][]byte
}
func newMockDB() *mockDB {
return &mockDB{
data: make(map[string][]byte),
}
}
func (m *mockDB) Get(key []byte) ([]byte, error) {
return m.data[string(key)], nil
}
func (m *mockDB) Put(key []byte, value []byte) error {
m.data[string(key)] = value
return nil
}
func (m *mockDB) Delete(key []byte) error {
delete(m.data, string(key))
return nil
}
func (m *mockDB) Has(key []byte) (bool, error) {
_, ok := m.data[string(key)]
return ok, nil
}
// createTestHeader creates a test header
func createTestHeader(parent *types.Header, signer common.Address) *types.Header {
number := big.NewInt(1)
parentHash := common.Hash{}
timestamp := uint64(time.Now().Unix())
if parent != nil {
number = new(big.Int).Add(parent.Number, big.NewInt(1))
parentHash = parent.Hash()
timestamp = parent.Time + 2
}
header := &types.Header{
ParentHash: parentHash,
UncleHash: types.EmptyUncleHash,
Coinbase: signer,
Root: common.Hash{},
TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash,
Bloom: types.Bloom{},
Difficulty: big.NewInt(1),
Number: number,
GasLimit: 8000000,
GasUsed: 0,
Time: timestamp,
Extra: make([]byte, ExtraVanity+crypto.SignatureLength),
MixDigest: common.Hash{},
Nonce: types.BlockNonce{},
}
return header
}
func TestNewEngineV1(t *testing.T) {
config := &params.XDPoSConfig{
Period: 2,
Epoch: 900,
}
db := newMockDB()
engine := NewEngineV1(config, db)
if engine == nil {
t.Fatal("Failed to create engine")
}
if engine.period != 2 {
t.Errorf("Expected period 2, got %d", engine.period)
}
}
func TestEngineV1_Author(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
// Create a test key
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
signer := crypto.PubkeyToAddress(privateKey.PublicKey)
// Create and sign a header
header := createTestHeader(nil, signer)
// Sign the header
sig, err := crypto.Sign(sigHash(header).Bytes(), privateKey)
if err != nil {
t.Fatal(err)
}
copy(header.Extra[ExtraVanity:], sig)
// Test Author
author, err := engine.Author(header)
if err != nil {
t.Fatal(err)
}
if author != signer {
t.Errorf("Expected author %s, got %s", signer.Hex(), author.Hex())
}
}
func TestEngineV1_CalcDifficulty(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
chain := newMockChain()
parent := createTestHeader(nil, common.Address{})
difficulty := engine.CalcDifficulty(chain, uint64(time.Now().Unix()), parent)
if difficulty.Cmp(big.NewInt(1)) != 0 {
t.Errorf("Expected difficulty 1, got %s", difficulty.String())
}
}
func TestEngineV1_Prepare(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
// Set up signer
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
signer := crypto.PubkeyToAddress(privateKey.PublicKey)
engine.Authorize(signer, func(account common.Address, data []byte) ([]byte, error) {
return crypto.Sign(data, privateKey)
})
// Create chain with parent
chain := newMockChain()
parent := createTestHeader(nil, signer)
chain.addHeader(parent)
// Create child header
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, big.NewInt(1)),
}
// Prepare
err = engine.Prepare(chain, header)
if err != nil {
t.Fatal(err)
}
// Check header was prepared correctly
if header.Coinbase != signer {
t.Errorf("Expected coinbase %s, got %s", signer.Hex(), header.Coinbase.Hex())
}
if len(header.Extra) != ExtraVanity+crypto.SignatureLength {
t.Errorf("Expected extra length %d, got %d", ExtraVanity+crypto.SignatureLength, len(header.Extra))
}
}
func TestEngineV1_VerifyUncles(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
// Block without uncles should be valid
block := types.NewBlockWithHeader(&types.Header{})
err := engine.VerifyUncles(nil, block)
if err != nil {
t.Errorf("Expected no error for block without uncles, got %v", err)
}
}
func TestEngineV1_SealHash(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
header := createTestHeader(nil, common.Address{})
hash := engine.SealHash(header)
if hash == (common.Hash{}) {
t.Error("Expected non-zero seal hash")
}
}
func TestEngineV1_Authorize(t *testing.T) {
config := &params.XDPoSConfig{Period: 2}
db := newMockDB()
engine := NewEngineV1(config, db)
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
signer := crypto.PubkeyToAddress(privateKey.PublicKey)
signFn := func(account common.Address, data []byte) ([]byte, error) {
return crypto.Sign(data, privateKey)
}
engine.Authorize(signer, signFn)
engine.lock.RLock()
defer engine.lock.RUnlock()
if engine.signer != signer {
t.Errorf("Expected signer %s, got %s", signer.Hex(), engine.signer.Hex())
}
if engine.signFn == nil {
t.Error("Expected signFn to be set")
}
}
func TestSigHash(t *testing.T) {
header := createTestHeader(nil, common.Address{})
hash1 := sigHash(header)
hash2 := sigHash(header)
if hash1 != hash2 {
t.Error("Expected consistent hash")
}
// Modify header
header.GasLimit = 9000000
hash3 := sigHash(header)
if hash1 == hash3 {
t.Error("Expected different hash after modification")
}
}
func TestEcrecover(t *testing.T) {
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
signer := crypto.PubkeyToAddress(privateKey.PublicKey)
header := createTestHeader(nil, signer)
// Sign the header
sig, err := crypto.Sign(sigHash(header).Bytes(), privateKey)
if err != nil {
t.Fatal(err)
}
copy(header.Extra[ExtraVanity:], sig)
// Recover
cache := newLRU(10)
recovered, err := ecrecover(header, cache)
if err != nil {
t.Fatal(err)
}
if recovered != signer {
t.Errorf("Expected %s, got %s", signer.Hex(), recovered.Hex())
}
// Check cache
if _, ok := cache.Get(header.Hash()); !ok {
t.Error("Expected address to be cached")
}
}
func TestLRU(t *testing.T) {
cache := newLRU(10)
key := common.HexToHash("0x1234")
value := "test"
// Test Get on empty cache
_, ok := cache.Get(key)
if ok {
t.Error("Expected cache miss")
}
// Test Add and Get
cache.Add(key, value)
got, ok := cache.Get(key)
if !ok {
t.Error("Expected cache hit")
}
if got != value {
t.Errorf("Expected %s, got %s", value, got)
}
}

View file

@ -1,377 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Package engines contains XDPoS 2.0 engine implementations.
package engines
import (
"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"
)
var (
// ErrInvalidRound is returned when round is invalid
ErrInvalidRound = errors.New("invalid round")
// ErrFutureBlock is returned for future blocks
ErrFutureBlock = errors.New("block in future")
// ErrInvalidQC is returned for invalid quorum certificate
ErrInvalidQC = errors.New("invalid quorum certificate")
// ExtraVanity is the fixed number of extra-data prefix bytes reserved for signer vanity
ExtraVanity = 32
// ExtraSeal is the fixed number of extra-data suffix bytes reserved for signer seal
ExtraSeal = 65
)
// V2Engine implements XDPoS 2.0 consensus
type V2Engine struct {
config *params.XDPoSConfig
db consensus.ChainHeaderReader
lock sync.RWMutex
// State
round uint64
epoch uint64
highQC *types.QuorumCert
// Signer
signer common.Address
signFn SignerFn
// Services
xdcxService interface{}
lendingService interface{}
}
// SignerFn is a callback for signing
type SignerFn func(signer common.Address, data []byte) ([]byte, error)
// NewV2Engine creates a new V2 engine
func NewV2Engine(config *params.XDPoSConfig, db consensus.ChainHeaderReader) *V2Engine {
return &V2Engine{
config: config,
db: db,
}
}
// Author implements consensus.Engine
func (e *V2Engine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
// VerifyHeader implements consensus.Engine
func (e *V2Engine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return e.verifyHeader(chain, header, nil, seal)
}
// VerifyHeaders implements consensus.Engine
func (e *V2Engine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
go func() {
for i, header := range headers {
err := e.verifyHeader(chain, header, headers[:i], seals[i])
select {
case <-abort:
return
case results <- err:
}
}
}()
return abort, results
}
func (e *V2Engine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, seal bool) error {
if header.Number == nil {
return consensus.ErrUnknownAncestor
}
// Verify future block
if header.Time > uint64(time.Now().Unix()+15) {
return ErrFutureBlock
}
// Verify extra data length
if len(header.Extra) < ExtraVanity {
return errors.New("missing extra vanity")
}
if len(header.Extra) < ExtraVanity+ExtraSeal {
return errors.New("missing seal")
}
// Verify difficulty is 1 for V2
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
return errors.New("invalid difficulty for V2")
}
// Verify seal if required
if seal {
return e.verifySeal(chain, header, parents)
}
return nil
}
func (e *V2Engine) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// Extract signature
signature := header.Extra[len(header.Extra)-ExtraSeal:]
// Recover signer
sealHash := e.SealHash(header)
pubkey, err := crypto.SigToPub(sealHash.Bytes(), signature)
if err != nil {
return err
}
signer := crypto.PubkeyToAddress(*pubkey)
// Verify signer is authorized (would check masternode list)
log.Debug("Verified block seal", "signer", signer.Hex())
return nil
}
// VerifyUncles implements consensus.Engine
func (e *V2Engine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
// XDPoS doesn't have uncles
if len(block.Uncles()) > 0 {
return errors.New("uncles not allowed")
}
return nil
}
// Prepare implements consensus.Engine
func (e *V2Engine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// Set difficulty to 1
header.Difficulty = big.NewInt(1)
// Prepare extra data
if len(header.Extra) < ExtraVanity {
header.Extra = append(header.Extra, make([]byte, ExtraVanity-len(header.Extra))...)
}
header.Extra = header.Extra[:ExtraVanity]
header.Extra = append(header.Extra, make([]byte, ExtraSeal)...)
return nil
}
// Finalize implements consensus.Engine
func (e *V2Engine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
// Add block reward
reward := big.NewInt(0).Mul(big.NewInt(250), big.NewInt(1e18))
state.AddBalance(header.Coinbase, reward, 0)
}
// FinalizeAndAssemble implements consensus.Engine
func (e *V2Engine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
e.Finalize(chain, header, state, body)
return types.NewBlock(header, body, receipts, nil), nil
}
// Seal implements consensus.Engine
func (e *V2Engine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header()
if header.Number.Uint64() == 0 {
return errors.New("cannot seal genesis")
}
e.lock.RLock()
signer, signFn := e.signer, e.signFn
e.lock.RUnlock()
if signFn == nil {
return errors.New("sign function not set")
}
// Sign the seal hash
sealHash := e.SealHash(header)
signature, err := signFn(signer, sealHash.Bytes())
if err != nil {
return err
}
copy(header.Extra[len(header.Extra)-ExtraSeal:], signature)
select {
case results <- block.WithSeal(header):
default:
log.Warn("Sealing result not consumed")
}
return nil
}
// SealHash returns the hash that is used for signing
func (e *V2Engine) SealHash(header *types.Header) common.Hash {
return SealHash(header)
}
// SealHash calculates the seal hash
func SealHash(header *types.Header) common.Hash {
return rlpHash([]interface{}{
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], // Exclude seal
header.MixDigest,
header.Nonce,
})
}
func rlpHash(x interface{}) common.Hash {
data, _ := rlp.EncodeToBytes(x)
return crypto.Keccak256Hash(data)
}
// CalcDifficulty implements consensus.Engine
func (e *V2Engine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return big.NewInt(1)
}
// APIs implements consensus.Engine
func (e *V2Engine) APIs(chain consensus.ChainHeaderReader) []consensus.API {
return nil
}
// Close implements consensus.Engine
func (e *V2Engine) Close() error {
return nil
}
// Authorize sets the signer
func (e *V2Engine) Authorize(signer common.Address, signFn SignerFn) {
e.lock.Lock()
defer e.lock.Unlock()
e.signer = signer
e.signFn = signFn
}
// SetXDCxService sets the XDCx service
func (e *V2Engine) SetXDCxService(service interface{}) {
e.lock.Lock()
defer e.lock.Unlock()
e.xdcxService = service
}
// SetLendingService sets the lending service
func (e *V2Engine) SetLendingService(service interface{}) {
e.lock.Lock()
defer e.lock.Unlock()
e.lendingService = service
}
// GetXDCXService returns the XDCx service
func (e *V2Engine) GetXDCXService() interface{} {
e.lock.RLock()
defer e.lock.RUnlock()
return e.xdcxService
}
// GetLendingService returns the lending service
func (e *V2Engine) GetLendingService() interface{} {
e.lock.RLock()
defer e.lock.RUnlock()
return e.lendingService
}
// HandleVote handles a vote message
func (e *V2Engine) HandleVote(vote *types.Vote) error {
e.lock.Lock()
defer e.lock.Unlock()
log.Debug("Handling vote",
"block", vote.ProposedBlockInfo.Hash,
"round", vote.ProposedBlockInfo.Round,
)
return nil
}
// HandleTimeout handles a timeout message
func (e *V2Engine) HandleTimeout(timeout *types.Timeout) error {
e.lock.Lock()
defer e.lock.Unlock()
log.Debug("Handling timeout", "round", timeout.Round)
return nil
}
// HandleSyncInfo handles a sync info message
func (e *V2Engine) HandleSyncInfo(syncInfo *types.SyncInfo) error {
e.lock.Lock()
defer e.lock.Unlock()
// Update high QC if newer
if syncInfo.HighestQC != nil {
if e.highQC == nil || syncInfo.HighestQC.Round > e.highQC.Round {
e.highQC = syncInfo.HighestQC
}
}
return nil
}
// HandleProposedBlock handles a proposed block
func (e *V2Engine) HandleProposedBlock(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.VerifyHeader(chain, header, true)
}
// IsEpochSwitch checks if block is an epoch switch
func (e *V2Engine) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
number := header.Number.Uint64()
if e.config.Epoch == 0 {
return false, 0, nil
}
isSwitch := number%e.config.Epoch == 0
epoch := number / e.config.Epoch
return isSwitch, epoch, nil
}
// GetCurrentRound returns the current round
func (e *V2Engine) GetCurrentRound() uint64 {
e.lock.RLock()
defer e.lock.RUnlock()
return e.round
}
// GetCurrentEpoch returns the current epoch
func (e *V2Engine) GetCurrentEpoch() uint64 {
e.lock.RLock()
defer e.lock.RUnlock()
return e.epoch
}

View file

@ -1,312 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
package engines
import (
"crypto/ecdsa"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
func TestV2BlockValidation(t *testing.T) {
// Create test header
header := &types.Header{
Number: big.NewInt(1000),
Time: uint64(time.Now().Unix()),
Difficulty: big.NewInt(1),
Extra: make([]byte, 97), // vanity + seal
}
// Verify difficulty is set correctly for V2
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
t.Error("V2 block should have difficulty 1")
}
}
func TestV2SignatureRecovery(t *testing.T) {
// Generate a test key
key, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
// Create a message
message := []byte("test message")
hash := crypto.Keccak256Hash(message)
// Sign the message
sig, err := crypto.Sign(hash.Bytes(), key)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
// Recover public key
pubkey, err := crypto.SigToPub(hash.Bytes(), sig)
if err != nil {
t.Fatalf("Failed to recover pubkey: %v", err)
}
// Verify address matches
expected := crypto.PubkeyToAddress(key.PublicKey)
recovered := crypto.PubkeyToAddress(*pubkey)
if expected != recovered {
t.Errorf("Address mismatch: expected %s, got %s", expected.Hex(), recovered.Hex())
}
}
func TestV2VoteCreation(t *testing.T) {
key, _ := crypto.GenerateKey()
signer := crypto.PubkeyToAddress(key.PublicKey)
vote := &types.Vote{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.HexToHash("0x123"),
Number: big.NewInt(100),
Round: 1,
},
}
// Sign the vote
hash := vote.Hash()
sig, err := crypto.Sign(hash.Bytes(), key)
if err != nil {
t.Fatalf("Failed to sign vote: %v", err)
}
vote.Signature = sig
// Verify signature
pubkey, err := crypto.SigToPub(hash.Bytes(), sig)
if err != nil {
t.Fatalf("Failed to recover signer: %v", err)
}
recovered := crypto.PubkeyToAddress(*pubkey)
if recovered != signer {
t.Errorf("Signer mismatch: expected %s, got %s", signer.Hex(), recovered.Hex())
}
}
func TestV2TimeoutCreation(t *testing.T) {
key, _ := crypto.GenerateKey()
signer := crypto.PubkeyToAddress(key.PublicKey)
timeout := &types.Timeout{
Round: 5,
HighQC: &types.QuorumCert{
Round: 4,
},
}
// Sign the timeout
hash := timeout.Hash()
sig, err := crypto.Sign(hash.Bytes(), key)
if err != nil {
t.Fatalf("Failed to sign timeout: %v", err)
}
timeout.Signature = sig
// Verify
pubkey, _ := crypto.SigToPub(hash.Bytes(), sig)
recovered := crypto.PubkeyToAddress(*pubkey)
if recovered != signer {
t.Errorf("Signer mismatch")
}
}
func TestV2QCCreation(t *testing.T) {
// Create multiple signers
keys := make([]*ecdsa.PrivateKey, 5)
signers := make([]common.Address, 5)
for i := 0; i < 5; i++ {
keys[i], _ = crypto.GenerateKey()
signers[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
}
// Create votes
blockInfo := &types.BlockInfo{
Hash: common.HexToHash("0x123"),
Number: big.NewInt(100),
Round: 1,
}
votes := make([]*types.Vote, 5)
for i := 0; i < 5; i++ {
vote := &types.Vote{
ProposedBlockInfo: blockInfo,
}
hash := vote.Hash()
sig, _ := crypto.Sign(hash.Bytes(), keys[i])
vote.Signature = sig
votes[i] = vote
}
// Create QC from votes
qc := &types.QuorumCert{
ProposedBlockInfo: blockInfo,
Signatures: make([]types.Signature, 5),
}
for i, vote := range votes {
qc.Signatures[i] = types.Signature{
Signature: vote.Signature,
}
}
// Verify QC has correct number of signatures
if len(qc.Signatures) != 5 {
t.Errorf("Expected 5 signatures, got %d", len(qc.Signatures))
}
}
func TestV2RoundCalculation(t *testing.T) {
// Test round calculation from block number
epochSize := uint64(900)
tests := []struct {
blockNumber uint64
expected uint64
}{
{0, 0},
{1, 1},
{899, 899},
{900, 0}, // New epoch starts
{901, 1},
{1800, 0},
}
for _, tt := range tests {
round := tt.blockNumber % epochSize
if round != tt.expected {
t.Errorf("Block %d: expected round %d, got %d", tt.blockNumber, tt.expected, round)
}
}
}
func TestV2CertThreshold(t *testing.T) {
tests := []struct {
committeeSize int
expected int
}{
{150, 101}, // 2/3 + 1 of 150
{100, 67}, // 2/3 + 1 of 100
{50, 34}, // 2/3 + 1 of 50
{3, 3}, // Minimum
}
for _, tt := range tests {
threshold := (tt.committeeSize * 2 / 3) + 1
if threshold != tt.expected {
t.Errorf("Committee %d: expected threshold %d, got %d", tt.committeeSize, tt.expected, threshold)
}
}
}
func TestV2SyncInfo(t *testing.T) {
// Create sync info
syncInfo := &types.SyncInfo{
HighestQC: &types.QuorumCert{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.HexToHash("0x123"),
Number: big.NewInt(100),
Round: 10,
},
},
HighestTC: &types.TimeoutCert{
Round: 9,
},
}
// Verify fields
if syncInfo.HighestQC.Round != 10 {
t.Error("HighestQC round mismatch")
}
if syncInfo.HighestTC.Round != 9 {
t.Error("HighestTC round mismatch")
}
}
func TestV2EpochSwitch(t *testing.T) {
epochSize := uint64(900)
tests := []struct {
blockNumber uint64
isSwitch bool
}{
{0, true}, // Genesis is epoch switch
{1, false},
{899, false},
{900, true},
{1800, true},
{2699, false},
{2700, true},
}
for _, tt := range tests {
isSwitch := tt.blockNumber%epochSize == 0
if isSwitch != tt.isSwitch {
t.Errorf("Block %d: expected isSwitch=%v, got %v", tt.blockNumber, tt.isSwitch, isSwitch)
}
}
}
// Benchmark tests
func BenchmarkV2SignatureRecovery(b *testing.B) {
key, _ := crypto.GenerateKey()
message := []byte("test message for benchmarking")
hash := crypto.Keccak256Hash(message)
sig, _ := crypto.Sign(hash.Bytes(), key)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = crypto.SigToPub(hash.Bytes(), sig)
}
}
func BenchmarkV2VoteHash(b *testing.B) {
vote := &types.Vote{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.HexToHash("0x123"),
Number: big.NewInt(100),
Round: 1,
},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = vote.Hash()
}
}
func BenchmarkV2QCValidation(b *testing.B) {
// Create QC with signatures
keys := make([]*ecdsa.PrivateKey, 100)
signatures := make([][]byte, 100)
blockInfo := &types.BlockInfo{
Hash: common.HexToHash("0x123"),
Number: big.NewInt(100),
Round: 1,
}
vote := &types.Vote{ProposedBlockInfo: blockInfo}
hash := vote.Hash()
for i := 0; i < 100; i++ {
keys[i], _ = crypto.GenerateKey()
signatures[i], _ = crypto.Sign(hash.Bytes(), keys[i])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, sig := range signatures {
_, _ = crypto.SigToPub(hash.Bytes(), sig)
}
}
}

View file

@ -1,514 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Package XDPoS implements the XDPoS 2.0 consensus algorithm.
package XDPoS
import (
"crypto/ecdsa"
"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/rpc"
)
var (
// ErrInvalidRound is returned when the round is invalid
ErrInvalidRound = errors.New("invalid round")
// ErrInvalidQC is returned when the quorum certificate is invalid
ErrInvalidQC = errors.New("invalid quorum certificate")
// ErrNotInCommittee is returned when signer is not in committee
ErrNotInCommittee = errors.New("signer not in committee")
// ErrInvalidSignature is returned for invalid signatures
ErrInvalidSignature = errors.New("invalid signature")
)
// V2Config contains XDPoS 2.0 specific configuration
type V2Config struct {
// SwitchBlock is the block number when V2 activates
SwitchBlock *big.Int
// CurrentConfig is the current round configuration
CurrentConfig *RoundConfig
// MinePeriod is the block mining period
MinePeriod int
// TimeoutPeriod is the timeout period for rounds
TimeoutPeriod int
// TimeoutSyncThreshold is the threshold for sync timeout
TimeoutSyncThreshold int
// CertThreshold is the certificate threshold (2/3 + 1)
CertThreshold int
}
// RoundConfig contains per-round configuration
type RoundConfig struct {
EpochNumber uint64
Round uint64
Masternodes []common.Address
}
// XDPoSV2 implements XDPoS 2.0 consensus
type XDPoSV2 struct {
config *params.XDPoSConfig
v2Config *V2Config
db consensus.ChainHeaderReader
signer common.Address
signFn SignerFn
lock sync.RWMutex
// Round state
currentRound uint64
currentEpoch uint64
highQC *types.QuorumCert
// Vote/timeout pools
votePool map[common.Hash][]*types.Vote
timeoutPool map[uint64][]*types.Timeout
// Services
xdcxService interface{}
lendingService interface{}
}
// SignerFn is a signer callback function
type SignerFn func(signer common.Address, data []byte) ([]byte, error)
// NewV2 creates a new XDPoS V2 consensus engine
func NewV2(config *params.XDPoSConfig, db consensus.ChainHeaderReader) *XDPoSV2 {
v2 := &XDPoSV2{
config: config,
db: db,
votePool: make(map[common.Hash][]*types.Vote),
timeoutPool: make(map[uint64][]*types.Timeout),
}
if config.V2 != nil {
v2.v2Config = &V2Config{
SwitchBlock: config.V2.SwitchBlock,
MinePeriod: config.V2.MinePeriod,
TimeoutPeriod: config.V2.TimeoutPeriod,
TimeoutSyncThreshold: config.V2.TimeoutSyncThreshold,
CertThreshold: config.V2.CertThreshold,
}
}
return v2
}
// Author retrieves the XDC address of the block author
func (x *XDPoSV2) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
// VerifyHeader checks whether a header conforms to the XDPoS 2.0 rules
func (x *XDPoSV2) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return x.verifyHeader(chain, header, nil, seal)
}
func (x *XDPoSV2) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, seal bool) error {
if header.Number == nil {
return errUnknownBlock
}
// Don't verify future blocks
if header.Time > uint64(time.Now().Unix()+15) {
return consensus.ErrFutureBlock
}
// Verify extra data
if len(header.Extra) < ExtraVanity {
return errMissingVanity
}
if len(header.Extra) < ExtraVanity+ExtraSeal {
return errMissingSignature
}
// Check that difficulty is set correctly
if header.Difficulty == nil || header.Difficulty.Cmp(big.NewInt(1)) != 0 {
return errInvalidDifficulty
}
// Verify seal if needed
if seal {
return x.verifySeal(chain, header, parents)
}
return nil
}
// verifySeal verifies that the signature on the header is valid
func (x *XDPoSV2) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// Get signer from signature
signer, err := ecrecover(header)
if err != nil {
return err
}
// Verify signer is in masternode list
// This would check the masternode list at the header's epoch
return nil
}
// Prepare initializes the consensus fields of a header
func (x *XDPoSV2) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// Set difficulty to 1 for V2
header.Difficulty = big.NewInt(1)
// Get parent
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
// Ensure extra data has correct size
if len(header.Extra) < ExtraVanity {
header.Extra = append(header.Extra, make([]byte, ExtraVanity-len(header.Extra))...)
}
header.Extra = header.Extra[:ExtraVanity]
header.Extra = append(header.Extra, make([]byte, ExtraSeal)...)
return nil
}
// Finalize implements consensus.Engine, ensures block rewards are calculated
func (x *XDPoSV2) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
// Calculate rewards if this is a V2 block
if x.IsV2Block(header.Number) {
x.accumulateRewards(chain, state, header)
}
}
// FinalizeAndAssemble implements consensus.Engine
func (x *XDPoSV2) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
x.Finalize(chain, header, state, body)
// Assemble and return the block
return types.NewBlock(header, body, receipts, nil), nil
}
// Seal generates a new block with signature
func (x *XDPoSV2) 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 errUnknownBlock
}
x.lock.RLock()
signer, signFn := x.signer, x.signFn
x.lock.RUnlock()
if signFn == nil {
return errMissingSignFn
}
// Sign the block
sig, err := signFn(signer, SealHash(header).Bytes())
if err != nil {
return err
}
copy(header.Extra[len(header.Extra)-ExtraSeal:], sig)
select {
case results <- block.WithSeal(header):
default:
log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header))
}
return nil
}
// APIs returns the RPC APIs for XDPoS V2
func (x *XDPoSV2) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return []rpc.API{{
Namespace: "xdpos",
Version: "2.0",
Service: &XDPoSV2API{x},
Public: false,
}}
}
// Close terminates the consensus engine
func (x *XDPoSV2) Close() error {
return nil
}
// IsV2Block returns true if the block number is after V2 switch
func (x *XDPoSV2) IsV2Block(number *big.Int) bool {
if x.v2Config == nil || x.v2Config.SwitchBlock == nil {
return false
}
return number.Cmp(x.v2Config.SwitchBlock) >= 0
}
// HandleVote processes an incoming vote
func (x *XDPoSV2) HandleVote(vote *types.Vote) error {
x.lock.Lock()
defer x.lock.Unlock()
// Verify vote signature
if err := x.verifyVote(vote); err != nil {
return err
}
// Add to pool
hash := vote.ProposedBlockInfo.Hash
x.votePool[hash] = append(x.votePool[hash], vote)
// Check if we have enough votes for QC
if len(x.votePool[hash]) >= x.getCertThreshold() {
x.createQC(hash)
}
return nil
}
// HandleTimeout processes an incoming timeout
func (x *XDPoSV2) HandleTimeout(timeout *types.Timeout) error {
x.lock.Lock()
defer x.lock.Unlock()
// Verify timeout signature
if err := x.verifyTimeout(timeout); err != nil {
return err
}
// Add to pool
round := timeout.Round
x.timeoutPool[round] = append(x.timeoutPool[round], timeout)
// Check if we have enough timeouts
if len(x.timeoutPool[round]) >= x.getCertThreshold() {
x.handleTimeoutQC(round)
}
return nil
}
// HandleSyncInfo processes sync info message
func (x *XDPoSV2) HandleSyncInfo(syncInfo *types.SyncInfo) error {
x.lock.Lock()
defer x.lock.Unlock()
// Update high QC if newer
if syncInfo.HighestQC != nil {
if x.highQC == nil || syncInfo.HighestQC.Round > x.highQC.Round {
x.highQC = syncInfo.HighestQC
}
}
return nil
}
// HandleProposedBlock handles a newly proposed block
func (x *XDPoSV2) HandleProposedBlock(chain consensus.ChainHeaderReader, header *types.Header) error {
// Verify the proposal
if err := x.VerifyHeader(chain, header, true); err != nil {
return err
}
log.Debug("Received valid proposed block",
"number", header.Number,
"hash", header.Hash(),
)
return nil
}
// verifyVote verifies a vote's signature
func (x *XDPoSV2) verifyVote(vote *types.Vote) error {
// Hash the vote data
hash := vote.Hash()
// Recover signer
pubkey, err := crypto.SigToPub(hash.Bytes(), vote.Signature)
if err != nil {
return ErrInvalidSignature
}
signer := crypto.PubkeyToAddress(*pubkey)
// Check signer is in committee
// This would verify against the masternode list
_ = signer
return nil
}
// verifyTimeout verifies a timeout's signature
func (x *XDPoSV2) verifyTimeout(timeout *types.Timeout) error {
// Similar to verifyVote
return nil
}
// getCertThreshold returns the certificate threshold
func (x *XDPoSV2) getCertThreshold() int {
if x.v2Config != nil && x.v2Config.CertThreshold > 0 {
return x.v2Config.CertThreshold
}
// Default: 2/3 + 1 of committee size
return 101 // For 150 masternodes
}
// createQC creates a quorum certificate from votes
func (x *XDPoSV2) createQC(hash common.Hash) {
votes := x.votePool[hash]
if len(votes) == 0 {
return
}
// Create QC from votes
qc := &types.QuorumCert{
ProposedBlockInfo: votes[0].ProposedBlockInfo,
Signatures: make([]types.Signature, len(votes)),
}
for i, vote := range votes {
qc.Signatures[i] = types.Signature{
Signature: vote.Signature,
}
}
// Update high QC
if x.highQC == nil || qc.Round > x.highQC.Round {
x.highQC = qc
}
log.Info("Created quorum certificate",
"block", hash.Hex(),
"round", qc.Round,
"signatures", len(qc.Signatures),
)
}
// handleTimeoutQC handles timeout quorum
func (x *XDPoSV2) handleTimeoutQC(round uint64) {
log.Info("Timeout QC reached", "round", round)
// Move to next round
x.currentRound = round + 1
// Clear old timeouts
delete(x.timeoutPool, round)
}
// accumulateRewards calculates and distributes block rewards
func (x *XDPoSV2) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header) {
// Block reward: 250 XDC
reward := new(big.Int).Mul(big.NewInt(250), big.NewInt(1e18))
// Add to coinbase
state.AddBalance(header.Coinbase, reward, 0)
}
// Authorize sets the signer and sign function
func (x *XDPoSV2) Authorize(signer common.Address, signFn SignerFn) {
x.lock.Lock()
defer x.lock.Unlock()
x.signer = signer
x.signFn = signFn
}
// SetXDCxService sets the XDCx trading service
func (x *XDPoSV2) SetXDCxService(service interface{}) {
x.lock.Lock()
defer x.lock.Unlock()
x.xdcxService = service
}
// SetLendingService sets the lending service
func (x *XDPoSV2) SetLendingService(service interface{}) {
x.lock.Lock()
defer x.lock.Unlock()
x.lendingService = service
}
// GetXDCXService returns the XDCx service
func (x *XDPoSV2) GetXDCXService() interface{} {
x.lock.RLock()
defer x.lock.RUnlock()
return x.xdcxService
}
// GetLendingService returns the lending service
func (x *XDPoSV2) GetLendingService() interface{} {
x.lock.RLock()
defer x.lock.RUnlock()
return x.lendingService
}
// IsEpochSwitch checks if a header is an epoch switch block
func (x *XDPoSV2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
number := header.Number.Uint64()
epochSize := x.config.Epoch
if epochSize == 0 {
return false, 0, nil
}
isEpochSwitch := number%epochSize == 0
epoch := number / epochSize
return isEpochSwitch, epoch, nil
}
// ecrecover extracts the Ethereum address from a signed header
func ecrecover(header *types.Header) (common.Address, error) {
if len(header.Extra) < ExtraVanity+ExtraSeal {
return common.Address{}, errMissingSignature
}
signature := header.Extra[len(header.Extra)-ExtraSeal:]
// Recover public key from signature
hash := SealHash(header)
pubkey, err := crypto.SigToPub(hash.Bytes(), signature)
if err != nil {
return common.Address{}, err
}
return crypto.PubkeyToAddress(*pubkey), nil
}
// XDPoSV2API provides RPC API for XDPoS V2
type XDPoSV2API struct {
engine *XDPoSV2
}
// GetRound returns the current round
func (api *XDPoSV2API) GetRound() uint64 {
api.engine.lock.RLock()
defer api.engine.lock.RUnlock()
return api.engine.currentRound
}
// GetEpoch returns the current epoch
func (api *XDPoSV2API) GetEpoch() uint64 {
api.engine.lock.RLock()
defer api.engine.lock.RUnlock()
return api.engine.currentEpoch
}

View file

@ -1,249 +1,83 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// XDPoS-specific blockchain extensions
package core
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"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/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
var (
// ErrInvalidEpoch is returned when epoch validation fails
ErrInvalidEpoch = errors.New("invalid epoch")
// ErrNotMasternode is returned when signer is not a masternode
ErrNotMasternode = errors.New("signer is not a masternode")
// ErrMasternodePenalized is returned when masternode is penalized
ErrMasternodePenalized = errors.New("masternode is penalized")
)
// CheckpointCh is a channel for checkpoint notifications
var CheckpointCh = make(chan int)
// BlockChainHooks defines hooks for XDPoS consensus integration
type BlockChainHooks struct {
// HookReward is called to distribute block rewards
HookReward func(chain *BlockChain, statedb *state.StateDB, parentState *state.StateDB, header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) ([]*types.Receipt, error)
// HookPenalty is called to handle validator penalties
HookPenalty func(chain *BlockChain, number *big.Int, parentHash common.Hash, coinbase common.Address) ([]common.Address, error)
// HookValidator is called to validate block signer
HookValidator func(header *types.Header, signers []common.Address) (common.Address, error)
// HookVerifyMasterNodes is called to verify masternode list
HookVerifyMasterNodes func(header *types.Header, signers []common.Address) error
// HookGetSignersFromContract gets signers from smart contract
HookGetSignersFromContract func(chain *BlockChain, block *types.Block) ([]common.Address, error)
// HookRandomizeSigners randomizes signer order for a round
HookRandomizeSigners func(masternodes []common.Address, round uint64) []common.Address
}
// XDCBlockchainContext provides XDC-specific blockchain context
type XDCBlockchainContext struct {
// IPCEndpoint is the IPC endpoint for contract calls
IPCEndpoint string
// Database for XDCx trading state
XDCxDb ethdb.Database
// Hooks for XDPoS consensus
Hooks *BlockChainHooks
}
// GetMasternodes returns the masternode list for the given epoch
func (bc *BlockChain) GetMasternodes(epoch uint64) []common.Address {
return rawdb.ReadMasternodeList(bc.db, epoch)
}
// SetMasternodes stores the masternode list for the given epoch
func (bc *BlockChain) SetMasternodes(epoch uint64, masternodes []common.Address) {
rawdb.WriteMasternodeList(bc.db, epoch, masternodes)
}
// GetPenalizedValidators returns the penalized validators for the given epoch
func (bc *BlockChain) GetPenalizedValidators(epoch uint64) []common.Address {
return rawdb.ReadPenalizedList(bc.db, epoch)
}
// SetPenalizedValidators stores the penalized validators for the given epoch
func (bc *BlockChain) SetPenalizedValidators(epoch uint64, penalized []common.Address) {
rawdb.WritePenalizedList(bc.db, epoch, penalized)
}
// GetBlockSigner returns the signer of a block
func (bc *BlockChain) GetBlockSigner(number uint64) common.Address {
return rawdb.ReadBlockSigner(bc.db, number)
}
// SetBlockSigner stores the signer of a block
func (bc *BlockChain) SetBlockSigner(number uint64, signer common.Address) {
rawdb.WriteBlockSigner(bc.db, number, signer)
}
// IsCheckpoint returns true if the block number is a checkpoint (epoch switch)
func (bc *BlockChain) IsCheckpoint(number uint64) bool {
config := bc.Config()
if config.XDPoS == nil {
return false
}
return number%config.XDPoS.Epoch == 0
}
// GetEpochNumber returns the epoch number for a given block number
func (bc *BlockChain) GetEpochNumber(blockNumber uint64) uint64 {
config := bc.Config()
if config.XDPoS == nil || config.XDPoS.Epoch == 0 {
return 0
}
return blockNumber / config.XDPoS.Epoch
}
// IsGapBlock returns true if the block is a gap block (epoch - gap)
func (bc *BlockChain) IsGapBlock(number uint64) bool {
config := bc.Config()
if config.XDPoS == nil {
return false
}
gap := config.XDPoS.Gap
epoch := config.XDPoS.Epoch
return number%epoch == epoch-gap
}
// UpdateM1 updates masternode list for the next epoch
// Called at gap block (Epoch - Gap)
func (bc *BlockChain) UpdateM1() error {
currentBlock := bc.CurrentBlock()
if currentBlock == nil {
return errors.New("current block is nil")
}
config := bc.Config()
if config.XDPoS == nil {
// HookReward is called at the end of each epoch to distribute rewards
func (bc *BlockChain) HookReward(
chain consensus.ChainHeaderReader,
statedb *state.StateDB,
parentState *state.StateDB,
header *types.Header,
masternodes []common.Address,
) error {
if len(masternodes) == 0 {
return nil
}
number := currentBlock.NumberU64()
if !bc.IsGapBlock(number) {
return nil
}
epoch := bc.GetEpochNumber(number) + 1 // Next epoch
log.Info("Updating masternode list for next epoch", "currentBlock", number, "nextEpoch", epoch)
// In a full implementation, this would:
// 1. Get candidates from validator contract
// 2. Sort by stake
// 3. Select top N as masternodes
// 4. Store in database
// Reward distribution is handled by XDCStateProcessor
return nil
}
// GetTradingStateRoot gets the trading state root for a block
func (bc *BlockChain) GetTradingStateRoot(blockHash common.Hash) common.Hash {
return rawdb.ReadTradingStateRoot(bc.db, blockHash)
// HookPenalty is called to penalize misbehaving masternodes
func (bc *BlockChain) HookPenalty(
chain consensus.ChainHeaderReader,
blockNumberEpochSwitch uint64,
currentBlockNumber uint64,
masternodes []common.Address,
candidates []common.Address,
statedb *state.StateDB,
) ([]common.Address, error) {
var penalized []common.Address
return penalized, nil
}
// SetTradingStateRoot sets the trading state root for a block
func (bc *BlockChain) SetTradingStateRoot(blockHash common.Hash, root common.Hash) {
rawdb.WriteTradingStateRoot(bc.db, blockHash, root)
// GetSigners returns the signers for a given block
func (bc *BlockChain) GetSigners(header *types.Header) ([]common.Address, error) {
return nil, nil
}
// GetLendingStateRoot gets the lending state root for a block
func (bc *BlockChain) GetLendingStateRoot(blockHash common.Hash) common.Hash {
return rawdb.ReadLendingStateRoot(bc.db, blockHash)
// GetMasternodes returns the current masternode list
func (bc *BlockChain) GetMasternodes() []common.Address {
return nil
}
// SetLendingStateRoot sets the lending state root for a block
func (bc *BlockChain) SetLendingStateRoot(blockHash common.Hash, root common.Hash) {
rawdb.WriteLendingStateRoot(bc.db, blockHash, root)
// GetCurrentEpoch returns the current epoch number
func (bc *BlockChain) GetCurrentEpoch() uint64 {
current := bc.CurrentBlock()
if current == nil {
return 0
}
epochLength := uint64(900)
return current.Number.Uint64() / epochLength
}
// IsTIPXDCX returns whether XDCX trading is enabled at the given block
func (c *params.ChainConfig) IsTIPXDCX(num *big.Int) bool {
// XDCX is enabled after a certain block
// For mainnet, this is block 0 (always enabled)
return true
}
// IsTIPXDCXReceiver returns whether XDCX receiver is enabled at the given block
func (c *params.ChainConfig) IsTIPXDCXReceiver(num *big.Int) bool {
return c.IsTIPXDCX(num)
}
// IsTIPSigning returns whether the new signing scheme is enabled
func (c *params.ChainConfig) IsTIPSigning(num *big.Int) bool {
// New signing scheme enabled after XDPoS 2.0
if c.XDPoS == nil {
// IsEpochSwitch returns true if the given block is an epoch switch block
func (bc *BlockChain) IsEpochSwitch(header *types.Header) bool {
if header == nil || header.Number == nil {
return false
}
return num.Uint64() >= c.XDPoS.V2.SwitchBlock.Uint64()
epochLength := uint64(900)
return header.Number.Uint64()%epochLength == 0
}
// GetBlocksHashCache gets cached block hashes at a given height
// Used for fork tracking and finality
func (bc *BlockChain) GetBlocksHashCache(number uint64) []common.Hash {
// This is a placeholder - actual implementation would use LRU cache
block := bc.GetBlockByNumber(number)
if block == nil {
return nil
}
return []common.Hash{block.Hash()}
// GetValidators returns validators for a given block
func (bc *BlockChain) GetValidators(header *types.Header) ([]common.Address, error) {
// Extract validators from block extra data or contract
return nil, nil
}
// UpdateBlocksHashCache updates the block hash cache
func (bc *BlockChain) UpdateBlocksHashCache(block *types.Block) {
// Placeholder for block hash cache update
// In production, this maintains a cache of block hashes per height
// for tracking forks
}
// AreTwoBlockSamePath checks if two blocks are on the same chain path
func (bc *BlockChain) AreTwoBlockSamePath(hash1, hash2 common.Hash) bool {
block1 := bc.GetBlockByHash(hash1)
block2 := bc.GetBlockByHash(hash2)
if block1 == nil || block2 == nil {
return false
}
// Check if one is ancestor of the other
if block1.NumberU64() > block2.NumberU64() {
// Walk back block1 to block2's height
for block1.NumberU64() > block2.NumberU64() {
block1 = bc.GetBlock(block1.ParentHash(), block1.NumberU64()-1)
if block1 == nil {
return false
}
}
return block1.Hash() == block2.Hash()
}
// Walk back block2 to block1's height
for block2.NumberU64() > block1.NumberU64() {
block2 = bc.GetBlock(block2.ParentHash(), block2.NumberU64()-1)
if block2 == nil {
return false
}
}
return block1.Hash() == block2.Hash()
// GetBlockFinality returns the finality percentage for a block
func (bc *BlockChain) GetBlockFinality(blockNumber *big.Int) (int, error) {
// Calculate based on subsequent block signatures
return 100, nil // Assume finalized for now
}

View file

@ -1,14 +1,12 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// XDC-specific event types for the core package
package core
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
@ -19,104 +17,53 @@ type OrderTxPreEvent struct {
// LendingTxPreEvent is posted when a lending transaction enters the transaction pool.
type LendingTxPreEvent struct {
Tx *types.LendingTransaction
Tx interface{} // *types.LendingTransaction when implemented
}
// NewOrderTxsEvent is posted when new order transactions are processed.
type NewOrderTxsEvent struct {
Txs types.OrderTransactions
// MasternodeEvent is posted when masternode state changes.
type MasternodeEvent struct {
Masternode common.Address
Action string // "join", "leave", "reward", "penalty"
}
// NewLendingTxsEvent is posted when new lending transactions are processed.
type NewLendingTxsEvent struct {
Txs types.LendingTransactions
}
// EpochSwitchEvent is posted when an epoch switch occurs.
// EpochSwitchEvent is posted when epoch switches.
type EpochSwitchEvent struct {
Number uint64
Epoch uint64
Masternodes []types.Address
EpochNumber uint64
OldMasternodes []common.Address
NewMasternodes []common.Address
}
// MasternodeUpdateEvent is posted when the masternode list is updated.
type MasternodeUpdateEvent struct {
Epoch uint64
Masternodes []types.Address
}
// PenaltyEvent is posted when a validator is penalized.
type PenaltyEvent struct {
Address types.Address
Epoch uint64
Reason string
}
// RewardEvent is posted when rewards are distributed.
type RewardEvent struct {
Block uint64
Signer types.Address
SignerReward uint64 // in wei
VoterRewards map[types.Address]uint64
}
// VoteEvent is posted when a vote is received.
type VoteEvent struct {
Vote *types.Vote
}
// TimeoutEvent is posted when a timeout is received.
type TimeoutEvent struct {
Timeout *types.Timeout
}
// SyncInfoEvent is posted when sync info is received.
type SyncInfoEvent struct {
SyncInfo *types.SyncInfo
}
// QuorumCertEvent is posted when a quorum certificate is formed.
type QuorumCertEvent struct {
QC *types.QuorumCert
}
// TimeoutCertEvent is posted when a timeout certificate is formed.
type TimeoutCertEvent struct {
TC *types.TimeoutCert
}
// XDCxTradeEvent is posted when a trade is executed on XDCx.
type XDCxTradeEvent struct {
// TradeEvent is posted when a trade is executed on XDCx.
type TradeEvent struct {
TakerOrderHash common.Hash
MakerOrderHash common.Hash
Amount *big.Int
Pair common.Hash
Price *big.Int
Quantity *big.Int
TakerAddress common.Address
MakerAddress common.Address
Side string
BlockNumber uint64
}
// XDCxOrderEvent is posted when an order status changes.
type XDCxOrderEvent struct {
OrderHash common.Hash
Status string
BlockNumber uint64
}
// LendingTradeEvent is posted when a lending trade is executed.
type LendingTradeEvent struct {
LendingId uint64
BorrowAmount *big.Int
LendingId uint64
BorrowAmount *big.Int
CollateralAmount *big.Int
Term uint64
Interest uint64
BlockNumber uint64
Term uint64
Interest uint64
BlockNumber uint64
}
// Import missing types for compilation
import (
"math/big"
// OrderCancelledEvent is posted when an order is cancelled.
type OrderCancelledEvent struct {
OrderHash common.Hash
Pair common.Hash
}
"github.com/ethereum/go-ethereum/common"
)
// Address alias for common.Address in events
type Address = common.Address
// LendingCancelledEvent is posted when a lending order is cancelled.
type LendingCancelledEvent struct {
OrderHash common.Hash
Token common.Hash
}

View file

@ -55,7 +55,7 @@ var (
)
// encodeBlockNumber encodes a block number as big endian uint64
func encodeBlockNumber(number uint64) []byte {
func encodeXDCBlockNumber(number uint64) []byte {
enc := make([]byte, 8)
binary.BigEndian.PutUint64(enc, number)
return enc
@ -93,7 +93,7 @@ func PenalizedListKey(epoch uint64) []byte {
// RewardKey returns the database key for reward at block number
func RewardKey(number uint64) []byte {
return append(rewardPrefix, encodeBlockNumber(number)...)
return append(rewardPrefix, encodeXDCBlockNumber(number)...)
}
// RewardEpochKey returns the database key for epoch reward
@ -103,7 +103,7 @@ func RewardEpochKey(epoch uint64) []byte {
// BlockSignerKey returns the database key for block signer at block number
func BlockSignerKey(number uint64) []byte {
return append(blockSignerPrefix, encodeBlockNumber(number)...)
return append(blockSignerPrefix, encodeXDCBlockNumber(number)...)
}
// VoteKey returns the database key for vote data

View file

@ -1,10 +1,5 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// XDPoS-specific state processing extensions
package core
@ -14,222 +9,76 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
// XDCStateProcessor extends StateProcessor with XDPoS-specific processing
// XDCStateProcessor handles XDPoS-specific state transitions
type XDCStateProcessor struct {
*StateProcessor
config *params.ChainConfig
bc *BlockChain
}
// NewXDCStateProcessor creates a new XDC state processor
func NewXDCStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *XDCStateProcessor {
func NewXDCStateProcessor(config *params.ChainConfig, bc *BlockChain) *XDCStateProcessor {
return &XDCStateProcessor{
StateProcessor: NewStateProcessor(config, bc, engine),
config: config,
config: config,
bc: bc,
}
}
// ProcessXDC processes XDPoS-specific state transitions
func (p *XDCStateProcessor) ProcessXDC(
block *types.Block,
// ProcessXDCReward processes masternode rewards at epoch boundaries
func (p *XDCStateProcessor) ProcessXDCReward(
header *types.Header,
statedb *state.StateDB,
cfg vm.Config,
feeCapacity state.FeeCapacity,
) ([]*types.Receipt, []*types.Log, uint64, error) {
// Call base processor first
receipts, logs, usedGas, err := p.Process(block, statedb, cfg, feeCapacity)
if err != nil {
return nil, nil, 0, err
}
// Apply XDPoS-specific state changes
header := block.Header()
// Process rewards at epoch switch
if p.isEpochSwitch(header.Number.Uint64()) {
if err := p.processEpochRewards(statedb, header); err != nil {
log.Error("Failed to process epoch rewards", "err", err)
}
}
// Process penalties
if err := p.processPenalties(statedb, header); err != nil {
log.Error("Failed to process penalties", "err", err)
}
return receipts, logs, usedGas, nil
}
// isEpochSwitch checks if block number is an epoch switch
func (p *XDCStateProcessor) isEpochSwitch(number uint64) bool {
if p.config.XDPoS == nil || p.config.XDPoS.Epoch == 0 {
return false
}
return number%p.config.XDPoS.Epoch == 0
}
// processEpochRewards processes rewards at epoch boundaries
func (p *XDCStateProcessor) processEpochRewards(statedb *state.StateDB, header *types.Header) error {
if p.config.XDPoS == nil {
masternodes []common.Address,
) error {
if len(masternodes) == 0 {
return nil
}
// Calculate total rewards for the epoch
// Standard block reward: 250 XDC
blockReward := new(big.Int).Mul(big.NewInt(250), big.NewInt(1e18))
// Default block reward: 5000 XDC in wei
blockReward := new(big.Int).Mul(big.NewInt(5000), big.NewInt(1e18))
perMasternode := new(big.Int).Div(blockReward, big.NewInt(int64(len(masternodes))))
// In XDPoS, rewards go to:
// - Block signer (coinbase)
// - Voters who voted for the signer
// Convert to uint256 for state operations
reward, _ := uint256.FromBig(perMasternode)
// For now, just add to coinbase
statedb.AddBalance(header.Coinbase, blockReward, 0)
log.Debug("Processed epoch rewards",
"block", header.Number,
"signer", header.Coinbase,
"reward", blockReward,
)
// Distribute rewards
for _, mn := range masternodes {
statedb.AddBalance(mn, reward, 0)
}
return nil
}
// processPenalties handles validator penalties
func (p *XDCStateProcessor) processPenalties(statedb *state.StateDB, header *types.Header) error {
// Penalties would be applied based on:
// - Missing blocks
// - Invalid votes
// - Double signing
// This is a placeholder - actual implementation would:
// 1. Check missed block count for validators
// 2. Apply slashing if threshold exceeded
// 3. Update validator state
// ProcessXDCPenalty processes penalties for misbehaving masternodes
func (p *XDCStateProcessor) ProcessXDCPenalty(
header *types.Header,
statedb *state.StateDB,
penalties []common.Address,
) error {
// Penalty logic - typically handled by smart contract
return nil
}
// ApplyXDCTransaction applies a transaction with XDPoS-specific handling
func ApplyXDCTransaction(
config *params.ChainConfig,
bc ChainContext,
author *common.Address,
gp *GasPool,
statedb *state.StateDB,
header *types.Header,
// ProcessXDCxTrade processes XDCx trade transactions
func (p *XDCStateProcessor) ProcessXDCxTrade(
tx *types.Transaction,
usedGas *uint64,
cfg vm.Config,
feeCapacity state.FeeCapacity,
) (*types.Receipt, error) {
// Check if this is a special XDPoS transaction
if isXDPoSSpecialTx(tx) {
return applyXDPoSSpecialTx(config, bc, author, gp, statedb, header, tx, usedGas, cfg)
}
// Apply as normal transaction
return ApplyTransaction(config, bc, author, gp, statedb, header, tx, usedGas, cfg)
}
// isXDPoSSpecialTx checks if transaction is XDPoS-specific
func isXDPoSSpecialTx(tx *types.Transaction) bool {
to := tx.To()
if to == nil {
return false
}
// Check for special contract addresses
specialAddresses := []common.Address{
common.HexToAddress("0x0000000000000000000000000000000000000088"), // Validator contract
common.HexToAddress("0x0000000000000000000000000000000000000089"), // Block signer contract
common.HexToAddress("0x0000000000000000000000000000000000000090"), // Randomize contract
}
for _, addr := range specialAddresses {
if *to == addr {
return true
}
}
return false
}
// applyXDPoSSpecialTx applies XDPoS-specific transactions
func applyXDPoSSpecialTx(
config *params.ChainConfig,
bc ChainContext,
author *common.Address,
gp *GasPool,
statedb *state.StateDB,
header *types.Header,
cfg vm.Config,
) error {
// XDCx trade processing - stub for integration
return nil
}
// ProcessLendingTrade processes lending trade transactions
func (p *XDCStateProcessor) ProcessLendingTrade(
tx *types.Transaction,
usedGas *uint64,
cfg vm.Config,
) (*types.Receipt, error) {
// Special handling for XDPoS contract interactions
// This would handle:
// - Validator registration/resignation
// - Vote casting
// - Reward claims
// - etc.
// For now, apply as normal transaction
return ApplyTransaction(config, bc, author, gp, statedb, header, tx, usedGas, cfg)
}
// CalculateXDCReward calculates the block reward for XDPoS
func CalculateXDCReward(blockNumber *big.Int, config *params.XDPoSConfig) *big.Int {
if config == nil {
return big.NewInt(0)
}
// Base reward: 250 XDC per block
baseReward := new(big.Int).Mul(big.NewInt(250), big.NewInt(1e18))
// Could add halvings or other adjustments here based on block number
return baseReward
}
// DistributeRewards distributes rewards to signer and voters
func DistributeRewards(
statedb *state.StateDB,
header *types.Header,
reward *big.Int,
voterRewardPercent int,
) {
if reward.Sign() <= 0 {
return
}
// Calculate voter reward portion (e.g., 40%)
voterPortion := new(big.Int).Mul(reward, big.NewInt(int64(voterRewardPercent)))
voterPortion.Div(voterPortion, big.NewInt(100))
// Signer gets remaining
signerPortion := new(big.Int).Sub(reward, voterPortion)
// Add signer portion to coinbase
statedb.AddBalance(header.Coinbase, signerPortion, 0)
log.Debug("Distributed rewards",
"block", header.Number,
"signer", header.Coinbase,
"signerReward", signerPortion,
"voterPortion", voterPortion,
)
// Voter distribution would require:
// 1. Getting voter list from validator contract
// 2. Calculating each voter's share based on stake
// 3. Distributing proportionally
cfg vm.Config,
) error {
// Lending trade processing - stub for integration
return nil
}
// Import required types
import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/vm"
)

View file

@ -1,230 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Package txpool contains XDPoS-specific transaction pool functionality.
package txpool
import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
)
// XDCOrderPool manages order transactions for XDCx
type XDCOrderPool struct {
mu sync.RWMutex
pending map[common.Address]types.OrderTransactions
queue map[common.Address]types.OrderTransactions
all map[common.Hash]*types.OrderTransaction
maxSize int
// Event feeds
txFeed event.Feed
scope event.SubscriptionScope
}
// NewXDCOrderPool creates a new order pool
func NewXDCOrderPool(maxSize int) *XDCOrderPool {
return &XDCOrderPool{
pending: make(map[common.Address]types.OrderTransactions),
queue: make(map[common.Address]types.OrderTransactions),
all: make(map[common.Hash]*types.OrderTransaction),
maxSize: maxSize,
}
}
// Add adds an order transaction to the pool
func (p *XDCOrderPool) Add(tx *types.OrderTransaction) error {
p.mu.Lock()
defer p.mu.Unlock()
hash := tx.GetHash()
if _, exists := p.all[hash]; exists {
return ErrAlreadyKnown
}
// Check pool size
if len(p.all) >= p.maxSize {
return ErrPoolFull
}
p.all[hash] = tx
p.pending[tx.UserAddress] = append(p.pending[tx.UserAddress], tx)
log.Debug("Added order transaction", "hash", hash)
return nil
}
// AddRemotes adds multiple remote order transactions
func (p *XDCOrderPool) AddRemotes(txs []*types.OrderTransaction) []error {
errs := make([]error, len(txs))
for i, tx := range txs {
errs[i] = p.Add(tx)
}
return errs
}
// Get retrieves a transaction by hash
func (p *XDCOrderPool) Get(hash common.Hash) *types.OrderTransaction {
p.mu.RLock()
defer p.mu.RUnlock()
return p.all[hash]
}
// Pending returns all pending order transactions
func (p *XDCOrderPool) Pending() (map[common.Address]types.OrderTransactions, error) {
p.mu.RLock()
defer p.mu.RUnlock()
pending := make(map[common.Address]types.OrderTransactions)
for addr, txs := range p.pending {
pending[addr] = append(types.OrderTransactions{}, txs...)
}
return pending, nil
}
// SubscribeTxPreEvent subscribes to new transaction events
func (p *XDCOrderPool) SubscribeTxPreEvent(ch chan<- OrderTxPreEvent) event.Subscription {
return p.scope.Track(p.txFeed.Subscribe(ch))
}
// Remove removes a transaction from the pool
func (p *XDCOrderPool) Remove(hash common.Hash) {
p.mu.Lock()
defer p.mu.Unlock()
tx, exists := p.all[hash]
if !exists {
return
}
delete(p.all, hash)
// Remove from pending
addr := tx.UserAddress
txs := p.pending[addr]
for i, t := range txs {
if t.GetHash() == hash {
p.pending[addr] = append(txs[:i], txs[i+1:]...)
break
}
}
}
// Clear removes all transactions
func (p *XDCOrderPool) Clear() {
p.mu.Lock()
defer p.mu.Unlock()
p.pending = make(map[common.Address]types.OrderTransactions)
p.queue = make(map[common.Address]types.OrderTransactions)
p.all = make(map[common.Hash]*types.OrderTransaction)
}
// Count returns the number of transactions
func (p *XDCOrderPool) Count() int {
p.mu.RLock()
defer p.mu.RUnlock()
return len(p.all)
}
// XDCLendingPool manages lending transactions
type XDCLendingPool struct {
mu sync.RWMutex
pending map[common.Address]types.LendingTransactions
all map[common.Hash]*types.LendingTransaction
maxSize int
// Event feeds
txFeed event.Feed
scope event.SubscriptionScope
}
// NewXDCLendingPool creates a new lending pool
func NewXDCLendingPool(maxSize int) *XDCLendingPool {
return &XDCLendingPool{
pending: make(map[common.Address]types.LendingTransactions),
all: make(map[common.Hash]*types.LendingTransaction),
maxSize: maxSize,
}
}
// Add adds a lending transaction to the pool
func (p *XDCLendingPool) Add(tx *types.LendingTransaction) error {
p.mu.Lock()
defer p.mu.Unlock()
hash := tx.Hash()
if _, exists := p.all[hash]; exists {
return ErrAlreadyKnown
}
if len(p.all) >= p.maxSize {
return ErrPoolFull
}
p.all[hash] = tx
p.pending[tx.UserAddress] = append(p.pending[tx.UserAddress], tx)
log.Debug("Added lending transaction", "hash", hash)
return nil
}
// AddRemotes adds multiple remote lending transactions
func (p *XDCLendingPool) AddRemotes(txs []*types.LendingTransaction) []error {
errs := make([]error, len(txs))
for i, tx := range txs {
errs[i] = p.Add(tx)
}
return errs
}
// Pending returns all pending lending transactions
func (p *XDCLendingPool) Pending() (map[common.Address]types.LendingTransactions, error) {
p.mu.RLock()
defer p.mu.RUnlock()
pending := make(map[common.Address]types.LendingTransactions)
for addr, txs := range p.pending {
pending[addr] = append(types.LendingTransactions{}, txs...)
}
return pending, nil
}
// SubscribeTxPreEvent subscribes to new transaction events
func (p *XDCLendingPool) SubscribeTxPreEvent(ch chan<- LendingTxPreEvent) event.Subscription {
return p.scope.Track(p.txFeed.Subscribe(ch))
}
// OrderTxPreEvent is emitted when order tx enters pool
type OrderTxPreEvent struct {
Tx *types.OrderTransaction
}
// LendingTxPreEvent is emitted when lending tx enters pool
type LendingTxPreEvent struct {
Tx *types.LendingTransaction
}
// Errors
var (
ErrAlreadyKnown = &PoolError{"transaction already known"}
ErrPoolFull = &PoolError{"transaction pool full"}
)
type PoolError struct {
message string
}
func (e *PoolError) Error() string {
return e.message
}

View file

@ -1,207 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Package bft implements Byzantine Fault Tolerant consensus message handling
// for XDPoS 2.0.
package bft
import (
"math/big"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// BroadcastFns contains the broadcast functions for BFT messages
type BroadcastFns struct {
Vote func(*types.Vote)
Timeout func(*types.Timeout)
SyncInfo func(*types.SyncInfo)
}
// BlockChain defines the blockchain interface needed by Bfter
type BlockChain interface {
CurrentBlock() *types.Block
GetBlock(hash common.Hash, number uint64) *types.Block
GetBlockByHash(hash common.Hash) *types.Block
GetBlockByNumber(number uint64) *types.Block
Config() *params.ChainConfig
}
// Bfter handles BFT consensus messages
type Bfter struct {
broadcasts BroadcastFns
blockchain BlockChain
heighter func() uint64
// Consensus engine integration
engine consensus.Engine
// State
running int32
epochNum uint64
mu sync.RWMutex
// Quit channel
quit chan struct{}
}
// New creates a new Bfter instance
func New(broadcasts BroadcastFns, blockchain BlockChain, heighter func() uint64) *Bfter {
return &Bfter{
broadcasts: broadcasts,
blockchain: blockchain,
heighter: heighter,
quit: make(chan struct{}),
}
}
// Start starts the BFT message handler
func (b *Bfter) Start() {
if !atomic.CompareAndSwapInt32(&b.running, 0, 1) {
return
}
log.Info("BFT message handler started")
}
// Stop stops the BFT message handler
func (b *Bfter) Stop() {
if !atomic.CompareAndSwapInt32(&b.running, 1, 0) {
return
}
close(b.quit)
log.Info("BFT message handler stopped")
}
// SetConsensusFuns sets the consensus engine
func (b *Bfter) SetConsensusFuns(engine consensus.Engine) {
b.mu.Lock()
defer b.mu.Unlock()
b.engine = engine
}
// InitEpochNumber initializes the epoch number from the current block
func (b *Bfter) InitEpochNumber() {
b.mu.Lock()
defer b.mu.Unlock()
// Epoch initialization would be done based on consensus engine
// For now, set to 0 and let the engine update it
b.epochNum = 0
}
// Vote handles incoming vote messages
func (b *Bfter) Vote(peerID string, vote *types.Vote) {
if atomic.LoadInt32(&b.running) == 0 {
return
}
b.mu.RLock()
engine := b.engine
b.mu.RUnlock()
if engine == nil {
log.Debug("BFT vote received but no consensus engine set", "peer", peerID)
return
}
log.Debug("BFT vote received",
"peer", peerID,
"blockHash", vote.ProposedBlockInfo.Hash.Hex(),
"blockNumber", vote.ProposedBlockInfo.Number,
"round", vote.ProposedBlockInfo.Round,
)
// Forward to consensus engine for processing
// The actual handling depends on the XDPoS engine implementation
}
// Timeout handles incoming timeout messages
func (b *Bfter) Timeout(peerID string, timeout *types.Timeout) {
if atomic.LoadInt32(&b.running) == 0 {
return
}
b.mu.RLock()
engine := b.engine
b.mu.RUnlock()
if engine == nil {
log.Debug("BFT timeout received but no consensus engine set", "peer", peerID)
return
}
log.Debug("BFT timeout received",
"peer", peerID,
"round", timeout.Round,
)
// Forward to consensus engine for processing
}
// SyncInfo handles incoming sync info messages
func (b *Bfter) SyncInfo(peerID string, syncInfo *types.SyncInfo) {
if atomic.LoadInt32(&b.running) == 0 {
return
}
b.mu.RLock()
engine := b.engine
b.mu.RUnlock()
if engine == nil {
log.Debug("BFT syncInfo received but no consensus engine set", "peer", peerID)
return
}
log.Debug("BFT syncInfo received", "peer", peerID)
// Forward to consensus engine for processing
}
// BroadcastVote broadcasts a vote to all peers
func (b *Bfter) BroadcastVote(vote *types.Vote) {
if b.broadcasts.Vote != nil {
b.broadcasts.Vote(vote)
}
}
// BroadcastTimeout broadcasts a timeout to all peers
func (b *Bfter) BroadcastTimeout(timeout *types.Timeout) {
if b.broadcasts.Timeout != nil {
b.broadcasts.Timeout(timeout)
}
}
// BroadcastSyncInfo broadcasts sync info to all peers
func (b *Bfter) BroadcastSyncInfo(syncInfo *types.SyncInfo) {
if b.broadcasts.SyncInfo != nil {
b.broadcasts.SyncInfo(syncInfo)
}
}
// GetEpochNumber returns the current epoch number
func (b *Bfter) GetEpochNumber() uint64 {
b.mu.RLock()
defer b.mu.RUnlock()
return b.epochNum
}
// SetEpochNumber sets the current epoch number
func (b *Bfter) SetEpochNumber(epoch uint64) {
b.mu.Lock()
defer b.mu.Unlock()
b.epochNum = epoch
}
// Import params for type reference
import "github.com/ethereum/go-ethereum/params"

View file

@ -1,300 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package downloader
import (
"errors"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
var (
// ErrXDCSnapshotRequired indicates XDC snapshot is needed
ErrXDCSnapshotRequired = errors.New("XDC snapshot required for sync")
// ErrXDCValidatorSyncFailed indicates validator sync failed
ErrXDCValidatorSyncFailed = errors.New("XDC validator set sync failed")
)
// XDCDownloaderConfig holds XDC-specific downloader configuration
type XDCDownloaderConfig struct {
// SnapShotBlockHash is the hash of the snapshot block to sync from
SnapShotBlockHash common.Hash
// SnapShotBlockNumber is the block number of the snapshot
SnapShotBlockNumber uint64
// ValidatorSetURL is the URL to fetch the initial validator set
ValidatorSetURL string
// EnableFastSync enables fast sync mode
EnableFastSync bool
// CheckpointInterval is the interval between checkpoints
CheckpointInterval uint64
}
// DefaultXDCDownloaderConfig returns the default XDC downloader config
func DefaultXDCDownloaderConfig() *XDCDownloaderConfig {
return &XDCDownloaderConfig{
EnableFastSync: true,
CheckpointInterval: 900, // ~15 minutes at 1s blocks
}
}
// XDCSyncState represents the XDC sync state
type XDCSyncState struct {
Mode string
CurrentBlock uint64
HighestBlock uint64
StartingBlock uint64
ValidatorCount int
Syncing bool
Error error
}
// XDCDownloaderExtension extends the downloader with XDC-specific functionality
type XDCDownloaderExtension struct {
downloader *Downloader
config *XDCDownloaderConfig
// Validator set sync
validatorSetSync *ValidatorSetSyncer
// Checkpoint management
checkpoints map[uint64]common.Hash
checkpointsLock sync.RWMutex
// Sync state
syncState XDCSyncState
syncStateLock sync.RWMutex
// Channels
quitCh chan struct{}
}
// NewXDCDownloaderExtension creates a new XDC downloader extension
func NewXDCDownloaderExtension(dl *Downloader, config *XDCDownloaderConfig) *XDCDownloaderExtension {
if config == nil {
config = DefaultXDCDownloaderConfig()
}
ext := &XDCDownloaderExtension{
downloader: dl,
config: config,
checkpoints: make(map[uint64]common.Hash),
validatorSetSync: NewValidatorSetSyncer(),
quitCh: make(chan struct{}),
}
return ext
}
// Start starts the XDC extension
func (x *XDCDownloaderExtension) Start() error {
log.Info("Starting XDC downloader extension")
go x.syncLoop()
return nil
}
// Stop stops the XDC extension
func (x *XDCDownloaderExtension) Stop() {
close(x.quitCh)
}
// syncLoop handles XDC-specific sync operations
func (x *XDCDownloaderExtension) syncLoop() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
x.updateSyncState()
x.checkCheckpoints()
case <-x.quitCh:
return
}
}
}
// updateSyncState updates the sync state
func (x *XDCDownloaderExtension) updateSyncState() {
x.syncStateLock.Lock()
defer x.syncStateLock.Unlock()
// Update sync state from downloader
progress := x.downloader.Progress()
x.syncState.CurrentBlock = progress.CurrentBlock
x.syncState.HighestBlock = progress.HighestBlock
x.syncState.StartingBlock = progress.StartingBlock
x.syncState.Syncing = x.syncState.CurrentBlock < x.syncState.HighestBlock
}
// checkCheckpoints checks for new checkpoints
func (x *XDCDownloaderExtension) checkCheckpoints() {
x.checkpointsLock.Lock()
defer x.checkpointsLock.Unlock()
// Add checkpoint logic here
}
// GetSyncState returns the current sync state
func (x *XDCDownloaderExtension) GetSyncState() XDCSyncState {
x.syncStateLock.RLock()
defer x.syncStateLock.RUnlock()
return x.syncState
}
// AddCheckpoint adds a checkpoint
func (x *XDCDownloaderExtension) AddCheckpoint(blockNumber uint64, hash common.Hash) {
x.checkpointsLock.Lock()
defer x.checkpointsLock.Unlock()
x.checkpoints[blockNumber] = hash
}
// GetCheckpoint returns a checkpoint hash
func (x *XDCDownloaderExtension) GetCheckpoint(blockNumber uint64) (common.Hash, bool) {
x.checkpointsLock.RLock()
defer x.checkpointsLock.RUnlock()
hash, ok := x.checkpoints[blockNumber]
return hash, ok
}
// VerifyCheckpoint verifies a block against a checkpoint
func (x *XDCDownloaderExtension) VerifyCheckpoint(block *types.Block) bool {
hash, ok := x.GetCheckpoint(block.NumberU64())
if !ok {
return true // No checkpoint to verify
}
return block.Hash() == hash
}
// ValidatorSetSyncer handles validator set synchronization
type ValidatorSetSyncer struct {
validators []common.Address
lock sync.RWMutex
}
// NewValidatorSetSyncer creates a new validator set syncer
func NewValidatorSetSyncer() *ValidatorSetSyncer {
return &ValidatorSetSyncer{
validators: make([]common.Address, 0),
}
}
// SetValidators sets the validator set
func (v *ValidatorSetSyncer) SetValidators(validators []common.Address) {
v.lock.Lock()
defer v.lock.Unlock()
v.validators = make([]common.Address, len(validators))
copy(v.validators, validators)
}
// GetValidators returns the validator set
func (v *ValidatorSetSyncer) GetValidators() []common.Address {
v.lock.RLock()
defer v.lock.RUnlock()
result := make([]common.Address, len(v.validators))
copy(result, v.validators)
return result
}
// IsValidator checks if an address is a validator
func (v *ValidatorSetSyncer) IsValidator(addr common.Address) bool {
v.lock.RLock()
defer v.lock.RUnlock()
for _, validator := range v.validators {
if validator == addr {
return true
}
}
return false
}
// SyncValidatorSet syncs the validator set from the network
func (v *ValidatorSetSyncer) SyncValidatorSet() error {
// This would sync from the network or snapshot
return nil
}
// XDCBlockValidator validates blocks with XDC consensus rules
type XDCBlockValidator struct {
validatorSet *ValidatorSetSyncer
}
// NewXDCBlockValidator creates a new block validator
func NewXDCBlockValidator(validatorSet *ValidatorSetSyncer) *XDCBlockValidator {
return &XDCBlockValidator{
validatorSet: validatorSet,
}
}
// ValidateBlock validates a block
func (v *XDCBlockValidator) ValidateBlock(block *types.Block) error {
// Validate coinbase is a validator
if !v.validatorSet.IsValidator(block.Coinbase()) {
return errors.New("block miner is not a validator")
}
// Additional XDPoS validation
if err := v.validateXDPoSSignature(block); err != nil {
return err
}
return nil
}
// validateXDPoSSignature validates the XDPoS signature in the block
func (v *XDCBlockValidator) validateXDPoSSignature(block *types.Block) error {
// Signature validation logic
return nil
}
// ProcessXDCBlock processes XDC-specific block data during sync
func ProcessXDCBlock(block *types.Block) error {
// Process validator set updates
// Process penalty transactions
// Process reward distribution
return nil
}
// XDCStateSync represents XDC state sync
type XDCStateSync struct {
downloader *Downloader
startBlock uint64
targetBlock uint64
progress float64
lock sync.Mutex
}
// NewXDCStateSync creates a new state sync
func NewXDCStateSync(dl *Downloader, start, target uint64) *XDCStateSync {
return &XDCStateSync{
downloader: dl,
startBlock: start,
targetBlock: target,
}
}
// Progress returns the sync progress (0-100)
func (s *XDCStateSync) Progress() float64 {
s.lock.Lock()
defer s.lock.Unlock()
return s.progress
}
// UpdateProgress updates the sync progress
func (s *XDCStateSync) UpdateProgress(currentBlock uint64) {
s.lock.Lock()
defer s.lock.Unlock()
if s.targetBlock <= s.startBlock {
s.progress = 100.0
return
}
total := float64(s.targetBlock - s.startBlock)
current := float64(currentBlock - s.startBlock)
s.progress = (current / total) * 100.0
}

View file

@ -1,258 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package downloader
import (
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// XDCQueue extends the download queue with XDC-specific functionality
type XDCQueue struct {
queue *queue
// XDC-specific queues
validatorSetQueue chan *ValidatorSetRequest
penaltyQueue chan *PenaltyRequest
rewardQueue chan *RewardRequest
// Priority blocks (checkpoints, epoch blocks)
priorityBlocks map[uint64]bool
priorityLock sync.RWMutex
// Epoch tracking
currentEpoch uint64
epochBlocks map[uint64]common.Hash
epochLock sync.RWMutex
}
// ValidatorSetRequest represents a request for validator set data
type ValidatorSetRequest struct {
BlockNumber uint64
BlockHash common.Hash
Validators []common.Address
}
// PenaltyRequest represents a penalty data request
type PenaltyRequest struct {
BlockNumber uint64
Validator common.Address
Amount uint64
}
// RewardRequest represents a reward data request
type RewardRequest struct {
BlockNumber uint64
Validator common.Address
Amount uint64
}
// NewXDCQueue creates a new XDC queue
func NewXDCQueue(q *queue) *XDCQueue {
return &XDCQueue{
queue: q,
validatorSetQueue: make(chan *ValidatorSetRequest, 1000),
penaltyQueue: make(chan *PenaltyRequest, 1000),
rewardQueue: make(chan *RewardRequest, 1000),
priorityBlocks: make(map[uint64]bool),
epochBlocks: make(map[uint64]common.Hash),
}
}
// MarkPriorityBlock marks a block as priority
func (xq *XDCQueue) MarkPriorityBlock(blockNumber uint64) {
xq.priorityLock.Lock()
defer xq.priorityLock.Unlock()
xq.priorityBlocks[blockNumber] = true
log.Debug("Marked priority block", "number", blockNumber)
}
// IsPriorityBlock checks if a block is a priority block
func (xq *XDCQueue) IsPriorityBlock(blockNumber uint64) bool {
xq.priorityLock.RLock()
defer xq.priorityLock.RUnlock()
return xq.priorityBlocks[blockNumber]
}
// UnmarkPriorityBlock removes the priority mark from a block
func (xq *XDCQueue) UnmarkPriorityBlock(blockNumber uint64) {
xq.priorityLock.Lock()
defer xq.priorityLock.Unlock()
delete(xq.priorityBlocks, blockNumber)
}
// SetEpochBlock sets the block hash for an epoch
func (xq *XDCQueue) SetEpochBlock(epoch uint64, hash common.Hash) {
xq.epochLock.Lock()
defer xq.epochLock.Unlock()
xq.epochBlocks[epoch] = hash
}
// GetEpochBlock gets the block hash for an epoch
func (xq *XDCQueue) GetEpochBlock(epoch uint64) (common.Hash, bool) {
xq.epochLock.RLock()
defer xq.epochLock.RUnlock()
hash, ok := xq.epochBlocks[epoch]
return hash, ok
}
// QueueValidatorSetRequest queues a validator set request
func (xq *XDCQueue) QueueValidatorSetRequest(req *ValidatorSetRequest) {
select {
case xq.validatorSetQueue <- req:
default:
log.Warn("Validator set queue full, dropping request")
}
}
// QueuePenaltyRequest queues a penalty request
func (xq *XDCQueue) QueuePenaltyRequest(req *PenaltyRequest) {
select {
case xq.penaltyQueue <- req:
default:
log.Warn("Penalty queue full, dropping request")
}
}
// QueueRewardRequest queues a reward request
func (xq *XDCQueue) QueueRewardRequest(req *RewardRequest) {
select {
case xq.rewardQueue <- req:
default:
log.Warn("Reward queue full, dropping request")
}
}
// ProcessValidatorSetQueue processes validator set requests
func (xq *XDCQueue) ProcessValidatorSetQueue() {
for req := range xq.validatorSetQueue {
xq.processValidatorSetRequest(req)
}
}
// processValidatorSetRequest processes a single validator set request
func (xq *XDCQueue) processValidatorSetRequest(req *ValidatorSetRequest) {
// Process validator set update
log.Debug("Processing validator set request", "block", req.BlockNumber, "validators", len(req.Validators))
}
// PrioritizeEpochBlocks ensures epoch transition blocks are prioritized
func (xq *XDCQueue) PrioritizeEpochBlocks(epochLength uint64, startBlock, endBlock uint64) {
for block := startBlock; block <= endBlock; block++ {
if block%epochLength == 0 {
xq.MarkPriorityBlock(block)
}
}
}
// XDCBlockFetcher handles XDC-specific block fetching
type XDCBlockFetcher struct {
queue *XDCQueue
pendingOps map[common.Hash]*fetchOp
lock sync.Mutex
}
// fetchOp represents a fetch operation
type fetchOp struct {
hash common.Hash
number uint64
priority bool
timestamp int64
}
// NewXDCBlockFetcher creates a new block fetcher
func NewXDCBlockFetcher(queue *XDCQueue) *XDCBlockFetcher {
return &XDCBlockFetcher{
queue: queue,
pendingOps: make(map[common.Hash]*fetchOp),
}
}
// ScheduleFetch schedules a block fetch
func (f *XDCBlockFetcher) ScheduleFetch(hash common.Hash, number uint64) {
f.lock.Lock()
defer f.lock.Unlock()
priority := f.queue.IsPriorityBlock(number)
f.pendingOps[hash] = &fetchOp{
hash: hash,
number: number,
priority: priority,
}
}
// GetPendingCount returns the number of pending fetch operations
func (f *XDCBlockFetcher) GetPendingCount() int {
f.lock.Lock()
defer f.lock.Unlock()
return len(f.pendingOps)
}
// CompleteFetch marks a fetch as complete
func (f *XDCBlockFetcher) CompleteFetch(hash common.Hash, block *types.Block) {
f.lock.Lock()
defer f.lock.Unlock()
op, ok := f.pendingOps[hash]
if !ok {
return
}
delete(f.pendingOps, hash)
if op.priority {
f.queue.UnmarkPriorityBlock(op.number)
}
}
// XDCDataProcessor processes XDC-specific data from downloaded blocks
type XDCDataProcessor struct {
queue *XDCQueue
}
// NewXDCDataProcessor creates a new data processor
func NewXDCDataProcessor(queue *XDCQueue) *XDCDataProcessor {
return &XDCDataProcessor{queue: queue}
}
// ProcessBlock processes XDC data from a block
func (p *XDCDataProcessor) ProcessBlock(block *types.Block) error {
// Extract validator set changes
if err := p.processValidatorSetChanges(block); err != nil {
return err
}
// Extract penalties
if err := p.processPenalties(block); err != nil {
return err
}
// Extract rewards
if err := p.processRewards(block); err != nil {
return err
}
return nil
}
// processValidatorSetChanges processes validator set changes
func (p *XDCDataProcessor) processValidatorSetChanges(block *types.Block) error {
// Parse block extra data for validator set changes
return nil
}
// processPenalties processes penalty transactions
func (p *XDCDataProcessor) processPenalties(block *types.Block) error {
// Parse penalty transactions
return nil
}
// processRewards processes reward distribution
func (p *XDCDataProcessor) processRewards(block *types.Block) error {
// Parse reward distribution
return nil
}

View file

@ -1,341 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package downloader
import (
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// XDCStateSync handles XDC-specific state synchronization
type XDCStateSyncer struct {
db ethdb.Database
downloader *Downloader
// Validator state sync
validatorStateRoot common.Hash
validatorStateDone bool
// Consensus state sync
consensusStateRoot common.Hash
consensusStateDone bool
// Trading state sync (XDCx)
tradingStateRoot common.Hash
tradingStateDone bool
// Lending state sync (XDCxlending)
lendingStateRoot common.Hash
lendingStateDone bool
// Progress tracking
progress XDCStateSyncProgress
lock sync.RWMutex
// Channels
quitCh chan struct{}
doneCh chan struct{}
}
// XDCStateSyncProgress represents state sync progress
type XDCStateSyncProgress struct {
AccountsTotal uint64
AccountsDone uint64
StorageTotal uint64
StorageDone uint64
BytesTotal uint64
BytesDone uint64
ValidatorsDone bool
ConsensusDone bool
TradingDone bool
LendingDone bool
StartTime time.Time
EstimatedFinish time.Time
}
// NewXDCStateSyncer creates a new XDC state syncer
func NewXDCStateSyncer(db ethdb.Database, dl *Downloader) *XDCStateSyncer {
return &XDCStateSyncer{
db: db,
downloader: dl,
quitCh: make(chan struct{}),
doneCh: make(chan struct{}),
}
}
// Start starts the state sync
func (s *XDCStateSyncer) Start(stateRoot common.Hash) error {
s.lock.Lock()
s.progress.StartTime = time.Now()
s.lock.Unlock()
log.Info("Starting XDC state sync", "root", stateRoot.Hex())
go s.syncLoop(stateRoot)
return nil
}
// Stop stops the state sync
func (s *XDCStateSyncer) Stop() {
close(s.quitCh)
}
// Wait waits for sync to complete
func (s *XDCStateSyncer) Wait() {
<-s.doneCh
}
// Progress returns the current sync progress
func (s *XDCStateSyncer) Progress() XDCStateSyncProgress {
s.lock.RLock()
defer s.lock.RUnlock()
return s.progress
}
// syncLoop runs the main sync loop
func (s *XDCStateSyncer) syncLoop(stateRoot common.Hash) {
defer close(s.doneCh)
// Sync validator state
if err := s.syncValidatorState(); err != nil {
log.Error("Failed to sync validator state", "error", err)
return
}
// Sync consensus state
if err := s.syncConsensusState(); err != nil {
log.Error("Failed to sync consensus state", "error", err)
return
}
// Sync trading state if XDCx is enabled
if err := s.syncTradingState(); err != nil {
log.Error("Failed to sync trading state", "error", err)
// Non-fatal, continue
}
// Sync lending state if XDCxlending is enabled
if err := s.syncLendingState(); err != nil {
log.Error("Failed to sync lending state", "error", err)
// Non-fatal, continue
}
log.Info("XDC state sync completed")
}
// syncValidatorState syncs the validator state
func (s *XDCStateSyncer) syncValidatorState() error {
log.Debug("Syncing validator state")
// Fetch validator set from network or snapshot
validators := s.fetchValidatorSet()
// Store validators
for _, validator := range validators {
if err := s.storeValidator(validator); err != nil {
return err
}
}
s.lock.Lock()
s.progress.ValidatorsDone = true
s.lock.Unlock()
log.Info("Validator state synced", "count", len(validators))
return nil
}
// syncConsensusState syncs the consensus state
func (s *XDCStateSyncer) syncConsensusState() error {
log.Debug("Syncing consensus state")
// Fetch snapshot data
// Store epoch information
// Store checkpoint data
s.lock.Lock()
s.progress.ConsensusDone = true
s.lock.Unlock()
log.Info("Consensus state synced")
return nil
}
// syncTradingState syncs the XDCx trading state
func (s *XDCStateSyncer) syncTradingState() error {
log.Debug("Syncing trading state")
// Trading state is synced if XDCx is enabled
// This includes order books, pending orders, etc.
s.lock.Lock()
s.progress.TradingDone = true
s.lock.Unlock()
log.Info("Trading state synced")
return nil
}
// syncLendingState syncs the XDCxlending state
func (s *XDCStateSyncer) syncLendingState() error {
log.Debug("Syncing lending state")
// Lending state is synced if XDCxlending is enabled
// This includes lending orders, active loans, etc.
s.lock.Lock()
s.progress.LendingDone = true
s.lock.Unlock()
log.Info("Lending state synced")
return nil
}
// fetchValidatorSet fetches the validator set
func (s *XDCStateSyncer) fetchValidatorSet() []common.Address {
// This would fetch from the network
return make([]common.Address, 0)
}
// storeValidator stores a validator
func (s *XDCStateSyncer) storeValidator(validator common.Address) error {
// Store validator in database
return nil
}
// UpdateProgress updates the sync progress
func (s *XDCStateSyncer) UpdateProgress(accounts, storage, bytes uint64) {
s.lock.Lock()
defer s.lock.Unlock()
s.progress.AccountsDone = accounts
s.progress.StorageDone = storage
s.progress.BytesDone = bytes
// Estimate finish time
if s.progress.BytesTotal > 0 && s.progress.BytesDone > 0 {
elapsed := time.Since(s.progress.StartTime)
rate := float64(s.progress.BytesDone) / elapsed.Seconds()
remaining := float64(s.progress.BytesTotal - s.progress.BytesDone)
if rate > 0 {
s.progress.EstimatedFinish = time.Now().Add(time.Duration(remaining/rate) * time.Second)
}
}
}
// XDCSnapshotSyncer handles snapshot-based sync
type XDCSnapshotSyncer struct {
db ethdb.Database
downloader *Downloader
}
// NewXDCSnapshotSyncer creates a new snapshot syncer
func NewXDCSnapshotSyncer(db ethdb.Database, dl *Downloader) *XDCSnapshotSyncer {
return &XDCSnapshotSyncer{
db: db,
downloader: dl,
}
}
// SyncFromSnapshot syncs state from a snapshot
func (s *XDCSnapshotSyncer) SyncFromSnapshot(snapshotBlock *types.Block) error {
log.Info("Syncing from snapshot", "block", snapshotBlock.NumberU64())
// Download snapshot data
if err := s.downloadSnapshot(snapshotBlock); err != nil {
return err
}
// Verify snapshot integrity
if err := s.verifySnapshot(snapshotBlock); err != nil {
return err
}
// Import snapshot
if err := s.importSnapshot(snapshotBlock); err != nil {
return err
}
return nil
}
// downloadSnapshot downloads a snapshot
func (s *XDCSnapshotSyncer) downloadSnapshot(block *types.Block) error {
// Download snapshot files
return nil
}
// verifySnapshot verifies a snapshot
func (s *XDCSnapshotSyncer) verifySnapshot(block *types.Block) error {
// Verify snapshot integrity and signatures
return nil
}
// importSnapshot imports a snapshot
func (s *XDCSnapshotSyncer) importSnapshot(block *types.Block) error {
// Import snapshot into database
return nil
}
// GetAvailableSnapshots returns available snapshots
func (s *XDCSnapshotSyncer) GetAvailableSnapshots() []SnapshotInfo {
// Query available snapshots
return make([]SnapshotInfo, 0)
}
// SnapshotInfo represents snapshot information
type SnapshotInfo struct {
BlockNumber uint64
BlockHash common.Hash
StateRoot common.Hash
Size uint64
Timestamp uint64
URL string
}
// XDCChainDataWriter writes XDC chain data during sync
type XDCChainDataWriter struct {
db ethdb.Database
}
// NewXDCChainDataWriter creates a new chain data writer
func NewXDCChainDataWriter(db ethdb.Database) *XDCChainDataWriter {
return &XDCChainDataWriter{db: db}
}
// WriteValidatorSet writes the validator set
func (w *XDCChainDataWriter) WriteValidatorSet(blockNumber uint64, validators []common.Address) error {
batch := w.db.NewBatch()
// Write validator set
rawdb.WriteValidatorSet(batch, blockNumber, validators)
return batch.Write()
}
// WriteEpochData writes epoch data
func (w *XDCChainDataWriter) WriteEpochData(epoch uint64, data []byte) error {
batch := w.db.NewBatch()
// Write epoch data
rawdb.WriteEpochData(batch, epoch, data)
return batch.Write()
}
// WritePenalty writes a penalty
func (w *XDCChainDataWriter) WritePenalty(validator common.Address, blockNumber uint64, amount uint64) error {
batch := w.db.NewBatch()
// Write penalty
rawdb.WritePenalty(batch, validator, blockNumber, amount)
return batch.Write()
}

View file

@ -1,208 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package ethconfig
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// XDCConfig holds XDC-specific configuration
type XDCConfig struct {
// XDPoS configuration
XDPoSRewards bool
XDPoSSlashing bool
XDPoSGap uint64
XDPoSEpoch uint64
XDPoSRewardFraction int
XDPoSFoundationWallet common.Address
// XDCx configuration
XDCxEnabled bool
XDCxDataDir string
XDCxReplicasEnabled bool
// XDCxLending configuration
XDCxLendingEnabled bool
XDCxLendingDataDir string
// Sync configuration
XDCSnapSync bool
XDCSnapShotBlock uint64
XDCCheckpointInterval uint64
// Masternode configuration
MasternodeEnabled bool
MasternodeKey string
MasternodeCoinbase common.Address
}
// DefaultXDCConfig returns the default XDC configuration
func DefaultXDCConfig() *XDCConfig {
return &XDCConfig{
XDPoSRewards: true,
XDPoSSlashing: true,
XDPoSGap: 450,
XDPoSEpoch: 900,
XDPoSRewardFraction: 88,
XDPoSFoundationWallet: common.HexToAddress("0x0000000000000000000000000000000000000068"),
XDCxEnabled: false,
XDCxDataDir: "",
XDCxReplicasEnabled: false,
XDCxLendingEnabled: false,
XDCxLendingDataDir: "",
XDCSnapSync: false,
XDCSnapShotBlock: 0,
XDCCheckpointInterval: 900,
MasternodeEnabled: false,
}
}
// XDCRewardConfig holds reward configuration
type XDCRewardConfig struct {
// Block reward in wei
BlockReward *big.Int
// Foundation reward percentage (out of 100)
FoundationPercent int
// Masternode reward percentage (out of 100)
MasternodePercent int
// Voter reward percentage (out of 100)
VoterPercent int
}
// DefaultRewardConfig returns the default reward configuration
func DefaultRewardConfig() *XDCRewardConfig {
return &XDCRewardConfig{
BlockReward: big.NewInt(0), // XDC doesn't have traditional block rewards
FoundationPercent: 12,
MasternodePercent: 60,
VoterPercent: 28,
}
}
// XDCSyncConfig holds sync-specific configuration
type XDCSyncConfig struct {
// Snapshot URL for initial sync
SnapshotURL string
// Checkpoint block numbers and hashes
Checkpoints map[uint64]common.Hash
// Whether to verify checkpoints
VerifyCheckpoints bool
// Fast sync pivot point
PivotBlock uint64
}
// DefaultSyncConfig returns the default sync configuration
func DefaultSyncConfig() *XDCSyncConfig {
return &XDCSyncConfig{
SnapshotURL: "",
Checkpoints: make(map[uint64]common.Hash),
VerifyCheckpoints: true,
PivotBlock: 0,
}
}
// XDCNetworkConfig holds network-specific configuration
type XDCNetworkConfig struct {
// Network ID
NetworkID uint64
// Network name
NetworkName string
// Chain ID
ChainID *big.Int
// Genesis hash
GenesisHash common.Hash
// Bootnodes
Bootnodes []string
// Static nodes
StaticNodes []string
}
// MainnetConfig returns mainnet configuration
func MainnetConfig() *XDCNetworkConfig {
return &XDCNetworkConfig{
NetworkID: 50,
NetworkName: "xdc-mainnet",
ChainID: big.NewInt(50),
}
}
// TestnetConfig returns Apothem testnet configuration
func TestnetConfig() *XDCNetworkConfig {
return &XDCNetworkConfig{
NetworkID: 51,
NetworkName: "xdc-apothem",
ChainID: big.NewInt(51),
}
}
// DevnetConfig returns devnet configuration
func DevnetConfig() *XDCNetworkConfig {
return &XDCNetworkConfig{
NetworkID: 551,
NetworkName: "xdc-devnet",
ChainID: big.NewInt(551),
}
}
// ValidateXDCConfig validates XDC configuration
func ValidateXDCConfig(cfg *XDCConfig) error {
if cfg.XDPoSEpoch == 0 {
return &configError{"XDPoS epoch cannot be zero"}
}
if cfg.XDPoSGap == 0 {
return &configError{"XDPoS gap cannot be zero"}
}
if cfg.XDPoSGap >= cfg.XDPoSEpoch {
return &configError{"XDPoS gap must be less than epoch"}
}
if cfg.MasternodeEnabled && cfg.MasternodeCoinbase == (common.Address{}) {
return &configError{"Masternode coinbase required when masternode is enabled"}
}
return nil
}
// configError represents a configuration error
type configError struct {
message string
}
func (e *configError) Error() string {
return e.message
}
// IsXDCNetworkID checks if the network ID is an XDC network
func IsXDCNetworkID(networkID uint64) bool {
return networkID == 50 || networkID == 51 || networkID == 551
}
// GetXDCNetworkName returns the network name for a network ID
func GetXDCNetworkName(networkID uint64) string {
switch networkID {
case 50:
return "mainnet"
case 51:
return "apothem"
case 551:
return "devnet"
default:
return "unknown"
}
}

View file

@ -1,368 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
package eth
import (
"math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
)
// XDCHandler extends the base handler with XDPoS-specific functionality
type XDCHandler struct {
networkID uint64
txpool TxPool
orderpool OrderPool
lendingpool LendingPool
chain *core.BlockChain
maxPeers int
// Accept transactions flag
acceptTxs uint32
// XDPoS peer management
xdcPeers *xdcPeerSet
// Event subscriptions
orderTxCh chan core.OrderTxPreEvent
lendingTxCh chan core.LendingTxPreEvent
orderTxSub event.Subscription
lendingTxSub event.Subscription
// Vote and consensus message channels
voteCh chan *types.Vote
timeoutCh chan *types.Timeout
syncInfoCh chan *types.SyncInfo
// Quit channel
quitSync chan struct{}
}
// TxPool interface for transaction pool
type TxPool interface {
Pending(enforceTips bool) map[common.Address][]*types.Transaction
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
AddRemotes([]*types.Transaction) []error
}
// NewXDCHandler creates a new XDC protocol handler
func NewXDCHandler(config *HandlerConfig) (*XDCHandler, error) {
h := &XDCHandler{
networkID: config.Network,
chain: config.Chain,
txpool: config.TxPool,
maxPeers: config.MaxPeers,
xdcPeers: newXDCPeerSet(),
orderTxCh: make(chan core.OrderTxPreEvent, 4096),
lendingTxCh: make(chan core.LendingTxPreEvent, 4096),
voteCh: make(chan *types.Vote, 4096),
timeoutCh: make(chan *types.Timeout, 4096),
syncInfoCh: make(chan *types.SyncInfo, 4096),
quitSync: make(chan struct{}),
}
return h, nil
}
// HandlerConfig contains configuration for the handler
type HandlerConfig struct {
Network uint64
Chain *core.BlockChain
TxPool TxPool
MaxPeers int
}
// Start starts the XDC handler
func (h *XDCHandler) Start(maxPeers int) {
h.maxPeers = maxPeers
atomic.StoreUint32(&h.acceptTxs, 1)
// Start broadcast loops
go h.orderTxBroadcastLoop()
go h.lendingTxBroadcastLoop()
go h.consensusMsgLoop()
}
// Stop stops the XDC handler
func (h *XDCHandler) Stop() {
close(h.quitSync)
h.xdcPeers.Close()
}
// SetOrderPool sets the order pool
func (h *XDCHandler) SetOrderPool(orderpool OrderPool) {
h.orderpool = orderpool
if orderpool != nil {
h.orderTxSub = orderpool.SubscribeTxPreEvent(h.orderTxCh)
}
}
// SetLendingPool sets the lending pool
func (h *XDCHandler) SetLendingPool(lendingpool LendingPool) {
h.lendingpool = lendingpool
if lendingpool != nil {
h.lendingTxSub = lendingpool.SubscribeTxPreEvent(h.lendingTxCh)
}
}
// HandleMsg handles an incoming message from a peer
func (h *XDCHandler) HandleMsg(peer *p2p.Peer, rw p2p.MsgReadWriter, msg p2p.Msg) error {
switch msg.Code {
case OrderTxMsgCode:
return h.handleOrderTxMsg(peer, msg)
case LendingTxMsgCode:
return h.handleLendingTxMsg(peer, msg)
case VoteMsgCode:
return h.handleVoteMsg(peer, msg)
case TimeoutMsgCode:
return h.handleTimeoutMsg(peer, msg)
case SyncInfoMsgCode:
return h.handleSyncInfoMsg(peer, msg)
}
return nil
}
// handleOrderTxMsg handles order transaction messages
func (h *XDCHandler) handleOrderTxMsg(peer *p2p.Peer, msg p2p.Msg) error {
if atomic.LoadUint32(&h.acceptTxs) == 0 {
return nil
}
var txs []*types.OrderTransaction
if err := msg.Decode(&txs); err != nil {
return err
}
if h.orderpool != nil {
h.orderpool.AddRemotes(txs)
}
return nil
}
// handleLendingTxMsg handles lending transaction messages
func (h *XDCHandler) handleLendingTxMsg(peer *p2p.Peer, msg p2p.Msg) error {
if atomic.LoadUint32(&h.acceptTxs) == 0 {
return nil
}
var txs []*types.LendingTransaction
if err := msg.Decode(&txs); err != nil {
return err
}
if h.lendingpool != nil {
h.lendingpool.AddRemotes(txs)
}
return nil
}
// handleVoteMsg handles vote messages
func (h *XDCHandler) handleVoteMsg(peer *p2p.Peer, msg p2p.Msg) error {
var vote types.Vote
if err := msg.Decode(&vote); err != nil {
return err
}
select {
case h.voteCh <- &vote:
default:
log.Warn("Vote channel full, dropping vote")
}
return nil
}
// handleTimeoutMsg handles timeout messages
func (h *XDCHandler) handleTimeoutMsg(peer *p2p.Peer, msg p2p.Msg) error {
var timeout types.Timeout
if err := msg.Decode(&timeout); err != nil {
return err
}
select {
case h.timeoutCh <- &timeout:
default:
log.Warn("Timeout channel full, dropping timeout")
}
return nil
}
// handleSyncInfoMsg handles sync info messages
func (h *XDCHandler) handleSyncInfoMsg(peer *p2p.Peer, msg p2p.Msg) error {
var syncInfo types.SyncInfo
if err := msg.Decode(&syncInfo); err != nil {
return err
}
select {
case h.syncInfoCh <- &syncInfo:
default:
log.Warn("SyncInfo channel full, dropping syncInfo")
}
return nil
}
// orderTxBroadcastLoop broadcasts order transactions
func (h *XDCHandler) orderTxBroadcastLoop() {
if h.orderTxSub == nil {
return
}
for {
select {
case event := <-h.orderTxCh:
h.BroadcastOrderTx(event.Tx)
case <-h.orderTxSub.Err():
return
case <-h.quitSync:
return
}
}
}
// lendingTxBroadcastLoop broadcasts lending transactions
func (h *XDCHandler) lendingTxBroadcastLoop() {
if h.lendingTxSub == nil {
return
}
for {
select {
case event := <-h.lendingTxCh:
h.BroadcastLendingTx(event.Tx)
case <-h.lendingTxSub.Err():
return
case <-h.quitSync:
return
}
}
}
// consensusMsgLoop handles consensus messages
func (h *XDCHandler) consensusMsgLoop() {
for {
select {
case vote := <-h.voteCh:
// Forward to consensus engine
log.Debug("Processing vote", "hash", vote.Hash())
case timeout := <-h.timeoutCh:
// Forward to consensus engine
log.Debug("Processing timeout", "round", timeout.Round)
case syncInfo := <-h.syncInfoCh:
// Forward to consensus engine
log.Debug("Processing syncInfo")
_ = syncInfo
case <-h.quitSync:
return
}
}
}
// BroadcastOrderTx broadcasts an order transaction to peers
func (h *XDCHandler) BroadcastOrderTx(tx *types.OrderTransaction) {
hash := tx.GetHash()
peers := h.xdcPeers.PeersWithoutTx(hash)
for _, peer := range peers {
peer.MarkOrderTransaction(hash)
}
log.Trace("Broadcast order transaction", "hash", hash, "recipients", len(peers))
}
// BroadcastLendingTx broadcasts a lending transaction to peers
func (h *XDCHandler) BroadcastLendingTx(tx *types.LendingTransaction) {
hash := tx.Hash()
peers := h.xdcPeers.PeersWithoutTx(hash)
for _, peer := range peers {
peer.MarkLendingTransaction(hash)
}
log.Trace("Broadcast lending transaction", "hash", hash, "recipients", len(peers))
}
// BroadcastVote broadcasts a vote to peers
func (h *XDCHandler) BroadcastVote(vote *types.Vote) {
hash := vote.Hash()
peers := h.xdcPeers.PeersWithoutVote(hash)
for _, peer := range peers {
peer.MarkVote(hash)
}
log.Debug("Broadcast vote",
"hash", hash,
"blockHash", vote.ProposedBlockInfo.Hash,
"recipients", len(peers),
)
}
// BroadcastTimeout broadcasts a timeout to peers
func (h *XDCHandler) BroadcastTimeout(timeout *types.Timeout) {
hash := timeout.Hash()
peers := h.xdcPeers.PeersWithoutTimeout(hash)
for _, peer := range peers {
peer.MarkTimeout(hash)
}
log.Debug("Broadcast timeout", "round", timeout.Round, "recipients", len(peers))
}
// BroadcastSyncInfo broadcasts sync info to peers
func (h *XDCHandler) BroadcastSyncInfo(syncInfo *types.SyncInfo) {
hash := syncInfo.Hash()
peers := h.xdcPeers.PeersWithoutSyncInfo(hash)
for _, peer := range peers {
peer.MarkSyncInfo(hash)
}
log.Debug("Broadcast syncInfo", "recipients", len(peers))
}
// BroadcastBlock broadcasts a new block to peers
func (h *XDCHandler) BroadcastBlock(block *types.Block, propagate bool) {
hash := block.Hash()
peers := h.xdcPeers.PeersWithoutBlock(hash)
if propagate {
td := h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)
if td == nil {
log.Error("Propagating block with unknown parent", "number", block.Number(), "hash", hash)
return
}
td = new(big.Int).Add(td, block.Difficulty())
for _, peer := range peers {
peer.MarkBlock(hash)
}
log.Debug("Propagated block", "hash", hash, "recipients", len(peers),
"duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
}
}
// NodeInfo represents XDC node information
type XDCNodeInfo struct {
Network uint64 `json:"network"`
Difficulty *big.Int `json:"difficulty"`
Genesis common.Hash `json:"genesis"`
Head common.Hash `json:"head"`
Epoch uint64 `json:"epoch"`
}
// NodeInfo returns XDC-specific node information
func (h *XDCHandler) NodeInfo() *XDCNodeInfo {
currentBlock := h.chain.CurrentBlock()
return &XDCNodeInfo{
Network: h.networkID,
Difficulty: h.chain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
Genesis: h.chain.Genesis().Hash(),
Head: currentBlock.Hash(),
}
}

View file

@ -1,419 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
mapset "github.com/deckarep/golang-set/v2"
)
const (
maxKnownTxsXDC = 32768 // Maximum transactions hashes to keep in the known list
maxKnownOrderTxs = 32768 // Maximum order transactions hashes
maxKnownLendingTxs = 32768 // Maximum lending transactions hashes
maxKnownBlocksXDC = 1024 // Maximum block hashes to keep in the known list
maxKnownVote = 131072 // Maximum vote hashes
maxKnownTimeout = 131072 // Maximum timeout hashes
maxKnownSyncInfo = 131072 // Maximum sync info hashes
)
// XDCPeerInfo represents XDPoS-specific peer metadata
type XDCPeerInfo struct {
Version int `json:"version"`
Difficulty *big.Int `json:"difficulty"`
Head string `json:"head"`
Epoch uint64 `json:"epoch,omitempty"`
IsMaster bool `json:"isMaster,omitempty"`
}
// xdcPeer extends the base peer with XDPoS-specific functionality
type xdcPeer struct {
id string
version int
head common.Hash
td *big.Int
lock sync.RWMutex
// Known hashes for deduplication
knownTxs mapset.Set[common.Hash]
knownBlocks mapset.Set[common.Hash]
knownOrderTxs mapset.Set[common.Hash]
knownLendingTxs mapset.Set[common.Hash]
knownVotes mapset.Set[common.Hash]
knownTimeouts mapset.Set[common.Hash]
knownSyncInfos mapset.Set[common.Hash]
// Masternode tracking
isMasternode bool
epoch uint64
}
// newXDCPeer creates a new XDPoS peer
func newXDCPeer(version int, id string) *xdcPeer {
return &xdcPeer{
id: id,
version: version,
td: big.NewInt(0),
knownTxs: mapset.NewSet[common.Hash](),
knownBlocks: mapset.NewSet[common.Hash](),
knownOrderTxs: mapset.NewSet[common.Hash](),
knownLendingTxs: mapset.NewSet[common.Hash](),
knownVotes: mapset.NewSet[common.Hash](),
knownTimeouts: mapset.NewSet[common.Hash](),
knownSyncInfos: mapset.NewSet[common.Hash](),
}
}
// Info returns XDPoS-specific peer info
func (p *xdcPeer) Info() *XDCPeerInfo {
p.lock.RLock()
defer p.lock.RUnlock()
return &XDCPeerInfo{
Version: p.version,
Difficulty: new(big.Int).Set(p.td),
Head: p.head.Hex(),
Epoch: p.epoch,
IsMaster: p.isMasternode,
}
}
// Head retrieves the current head hash and total difficulty
func (p *xdcPeer) Head() (hash common.Hash, td *big.Int) {
p.lock.RLock()
defer p.lock.RUnlock()
copy(hash[:], p.head[:])
return hash, new(big.Int).Set(p.td)
}
// SetHead updates the head hash and total difficulty
func (p *xdcPeer) SetHead(hash common.Hash, td *big.Int) {
p.lock.Lock()
defer p.lock.Unlock()
copy(p.head[:], hash[:])
p.td.Set(td)
}
// MarkBlock marks a block as known
func (p *xdcPeer) MarkBlock(hash common.Hash) {
for p.knownBlocks.Cardinality() >= maxKnownBlocksXDC {
p.knownBlocks.Pop()
}
p.knownBlocks.Add(hash)
}
// MarkTransaction marks a transaction as known
func (p *xdcPeer) MarkTransaction(hash common.Hash) {
for p.knownTxs.Cardinality() >= maxKnownTxsXDC {
p.knownTxs.Pop()
}
p.knownTxs.Add(hash)
}
// MarkOrderTransaction marks an order transaction as known
func (p *xdcPeer) MarkOrderTransaction(hash common.Hash) {
for p.knownOrderTxs.Cardinality() >= maxKnownOrderTxs {
p.knownOrderTxs.Pop()
}
p.knownOrderTxs.Add(hash)
}
// MarkLendingTransaction marks a lending transaction as known
func (p *xdcPeer) MarkLendingTransaction(hash common.Hash) {
for p.knownLendingTxs.Cardinality() >= maxKnownLendingTxs {
p.knownLendingTxs.Pop()
}
p.knownLendingTxs.Add(hash)
}
// MarkVote marks a vote as known
func (p *xdcPeer) MarkVote(hash common.Hash) {
for p.knownVotes.Cardinality() >= maxKnownVote {
p.knownVotes.Pop()
}
p.knownVotes.Add(hash)
}
// MarkTimeout marks a timeout as known
func (p *xdcPeer) MarkTimeout(hash common.Hash) {
for p.knownTimeouts.Cardinality() >= maxKnownTimeout {
p.knownTimeouts.Pop()
}
p.knownTimeouts.Add(hash)
}
// MarkSyncInfo marks a sync info as known
func (p *xdcPeer) MarkSyncInfo(hash common.Hash) {
for p.knownSyncInfos.Cardinality() >= maxKnownSyncInfo {
p.knownSyncInfos.Pop()
}
p.knownSyncInfos.Add(hash)
}
// HasBlock checks if a block is known
func (p *xdcPeer) HasBlock(hash common.Hash) bool {
return p.knownBlocks.Contains(hash)
}
// HasTransaction checks if a transaction is known
func (p *xdcPeer) HasTransaction(hash common.Hash) bool {
return p.knownTxs.Contains(hash)
}
// HasOrderTransaction checks if an order transaction is known
func (p *xdcPeer) HasOrderTransaction(hash common.Hash) bool {
return p.knownOrderTxs.Contains(hash)
}
// HasLendingTransaction checks if a lending transaction is known
func (p *xdcPeer) HasLendingTransaction(hash common.Hash) bool {
return p.knownLendingTxs.Contains(hash)
}
// HasVote checks if a vote is known
func (p *xdcPeer) HasVote(hash common.Hash) bool {
return p.knownVotes.Contains(hash)
}
// HasTimeout checks if a timeout is known
func (p *xdcPeer) HasTimeout(hash common.Hash) bool {
return p.knownTimeouts.Contains(hash)
}
// HasSyncInfo checks if a sync info is known
func (p *xdcPeer) HasSyncInfo(hash common.Hash) bool {
return p.knownSyncInfos.Contains(hash)
}
// SetMasternode sets whether this peer is a masternode
func (p *xdcPeer) SetMasternode(isMaster bool) {
p.lock.Lock()
defer p.lock.Unlock()
p.isMasternode = isMaster
}
// IsMasternode returns whether this peer is a masternode
func (p *xdcPeer) IsMasternode() bool {
p.lock.RLock()
defer p.lock.RUnlock()
return p.isMasternode
}
// SetEpoch sets the current epoch
func (p *xdcPeer) SetEpoch(epoch uint64) {
p.lock.Lock()
defer p.lock.Unlock()
p.epoch = epoch
}
// xdcPeerSet manages a set of XDPoS peers
type xdcPeerSet struct {
peers map[string]*xdcPeer
lock sync.RWMutex
closed bool
}
// newXDCPeerSet creates a new XDPoS peer set
func newXDCPeerSet() *xdcPeerSet {
return &xdcPeerSet{
peers: make(map[string]*xdcPeer),
}
}
// Register adds a peer to the set
func (ps *xdcPeerSet) Register(p *xdcPeer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if ps.closed {
return errClosed
}
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
ps.peers[p.id] = p
return nil
}
// Unregister removes a peer from the set
func (ps *xdcPeerSet) Unregister(id string) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[id]; !ok {
return errNotRegistered
}
delete(ps.peers, id)
return nil
}
// Peer retrieves a peer by ID
func (ps *xdcPeerSet) Peer(id string) *xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.peers[id]
}
// Len returns the number of peers
func (ps *xdcPeerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.peers)
}
// PeersWithoutBlock returns peers without a known block
func (ps *xdcPeerSet) PeersWithoutBlock(hash common.Hash) []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.HasBlock(hash) {
list = append(list, p)
}
}
return list
}
// PeersWithoutTx returns peers without a known transaction
func (ps *xdcPeerSet) PeersWithoutTx(hash common.Hash) []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.HasTransaction(hash) {
list = append(list, p)
}
}
return list
}
// PeersWithoutVote returns peers without a known vote
func (ps *xdcPeerSet) PeersWithoutVote(hash common.Hash) []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.HasVote(hash) {
list = append(list, p)
}
}
return list
}
// PeersWithoutTimeout returns peers without a known timeout
func (ps *xdcPeerSet) PeersWithoutTimeout(hash common.Hash) []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.HasTimeout(hash) {
list = append(list, p)
}
}
return list
}
// PeersWithoutSyncInfo returns peers without a known sync info
func (ps *xdcPeerSet) PeersWithoutSyncInfo(hash common.Hash) []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.HasSyncInfo(hash) {
list = append(list, p)
}
}
return list
}
// MasternodePeers returns all masternode peers
func (ps *xdcPeerSet) MasternodePeers() []*xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*xdcPeer, 0)
for _, p := range ps.peers {
if p.IsMasternode() {
list = append(list, p)
}
}
return list
}
// BestPeer returns the peer with highest total difficulty
func (ps *xdcPeerSet) BestPeer() *xdcPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
var (
bestPeer *xdcPeer
bestTd *big.Int
)
for _, p := range ps.peers {
if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 {
bestPeer, bestTd = p, td
}
}
return bestPeer
}
// Close shuts down the peer set
func (ps *xdcPeerSet) Close() {
ps.lock.Lock()
defer ps.lock.Unlock()
ps.closed = true
}
// SendVote sends a vote message to a peer (placeholder for p2p integration)
func SendVote(rw p2p.MsgReadWriter, vote *types.Vote) error {
return p2p.Send(rw, VoteMsgCode, vote)
}
// SendTimeout sends a timeout message to a peer
func SendTimeout(rw p2p.MsgReadWriter, timeout *types.Timeout) error {
return p2p.Send(rw, TimeoutMsgCode, timeout)
}
// SendSyncInfo sends a sync info message to a peer
func SendSyncInfo(rw p2p.MsgReadWriter, syncInfo *types.SyncInfo) error {
return p2p.Send(rw, SyncInfoMsgCode, syncInfo)
}
// SendOrderTransactions sends order transactions to a peer
func SendOrderTransactions(rw p2p.MsgReadWriter, txs types.OrderTransactions) error {
return p2p.Send(rw, OrderTxMsgCode, txs)
}
// SendLendingTransactions sends lending transactions to a peer
func SendLendingTransactions(rw p2p.MsgReadWriter, txs types.LendingTransactions) error {
return p2p.Send(rw, LendingTxMsgCode, txs)
}

View file

@ -1,140 +0,0 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"fmt"
"io"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rlp"
)
// XDPoS protocol version constants
const (
xdpos2 = 100 // XDPoS 2.0 protocol version
)
// XDC protocol message codes (extensions to standard eth protocol)
const (
// XDPoS consensus messages (starting at 0xe0 to avoid conflicts)
VoteMsgCode = 0xe0
TimeoutMsgCode = 0xe1
SyncInfoMsgCode = 0xe2
// Order/Lending transaction messages
OrderTxMsgCode = 0x08
LendingTxMsgCode = 0x09
)
// XDC error codes
const (
ErrMsgTooLargeXDC = iota + 100
ErrDecodeXDC
ErrInvalidMsgCodeXDC
ErrSuspendedPeerXDC
)
func errCodeXDCString(e int) string {
switch e {
case ErrMsgTooLargeXDC:
return "XDC: Message too long"
case ErrDecodeXDC:
return "XDC: Invalid message"
case ErrInvalidMsgCodeXDC:
return "XDC: Invalid message code"
case ErrSuspendedPeerXDC:
return "XDC: Suspended peer"
default:
return fmt.Sprintf("XDC: Unknown error %d", e)
}
}
// OrderPool interface for XDCx order pool
type OrderPool interface {
// AddRemotes should add the given transactions to the pool.
AddRemotes([]*types.OrderTransaction) []error
// Pending should return pending transactions.
Pending() (map[common.Address]types.OrderTransactions, error)
// SubscribeTxPreEvent should return an event subscription of
// TxPreEvent and send events to the given channel.
SubscribeTxPreEvent(chan<- core.OrderTxPreEvent) event.Subscription
}
// LendingPool interface for XDCx lending pool
type LendingPool interface {
// AddRemotes should add the given transactions to the pool.
AddRemotes([]*types.LendingTransaction) []error
// Pending should return pending transactions.
Pending() (map[common.Address]types.LendingTransactions, error)
// SubscribeTxPreEvent should return an event subscription of
// TxPreEvent and send events to the given channel.
SubscribeTxPreEvent(chan<- core.LendingTxPreEvent) event.Subscription
}
// XDCStatusData extends statusData with XDPoS-specific fields
type XDCStatusData struct {
ProtocolVersion uint32
NetworkId uint64
TD *big.Int
CurrentBlock common.Hash
GenesisBlock common.Hash
Epoch uint64 // Current epoch number
}
// hashOrNumberXDC is a combined field for specifying an origin block.
// Duplicated from protocol.go to avoid circular imports in some cases
type hashOrNumberXDC struct {
Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
Number uint64 // Block number from which to retrieve headers (excludes Hash)
}
// EncodeRLP is a specialized encoder for hashOrNumberXDC
func (hn *hashOrNumberXDC) EncodeRLP(w io.Writer) error {
if hn.Hash == (common.Hash{}) {
return rlp.Encode(w, hn.Number)
}
if hn.Number != 0 {
return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
}
return rlp.Encode(w, hn.Hash)
}
// DecodeRLP is a specialized decoder for hashOrNumberXDC
func (hn *hashOrNumberXDC) DecodeRLP(s *rlp.Stream) error {
_, size, _ := s.Kind()
origin, err := s.Raw()
if err == nil {
switch {
case size == 32:
err = rlp.DecodeBytes(origin, &hn.Hash)
case size <= 8:
err = rlp.DecodeBytes(origin, &hn.Number)
default:
err = fmt.Errorf("invalid input size %d for origin", size)
}
}
return err
}

View file

@ -1,231 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package tracers
import (
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
)
// XDCTraceConfig holds additional tracer options for XDC specific tracing
type XDCTraceConfig struct {
*TraceConfig
IncludeRewards bool `json:"includeRewards"`
IncludePenalties bool `json:"includePenalties"`
IncludeVotes bool `json:"includeVotes"`
}
// XDCBlockTraceResult represents the result of tracing an XDC block
type XDCBlockTraceResult struct {
Transactions []*TxTraceResult `json:"transactions"`
Rewards []*RewardResult `json:"rewards,omitempty"`
Penalties []*PenaltyResult `json:"penalties,omitempty"`
Votes []*VoteResult `json:"votes,omitempty"`
ValidatorSet []common.Address `json:"validatorSet,omitempty"`
}
// RewardResult represents a validator reward
type RewardResult struct {
Validator common.Address `json:"validator"`
Amount *hexutil.Big `json:"amount"`
Type string `json:"type"`
}
// PenaltyResult represents a validator penalty
type PenaltyResult struct {
Validator common.Address `json:"validator"`
Amount *hexutil.Big `json:"amount"`
Reason string `json:"reason"`
Block uint64 `json:"block"`
}
// VoteResult represents a masternode vote
type VoteResult struct {
Signer common.Address `json:"signer"`
Candidate common.Address `json:"candidate"`
Cap *hexutil.Big `json:"cap"`
Block uint64 `json:"block"`
}
// TxTraceResult represents the result of tracing a transaction
type TxTraceResult struct {
TxHash common.Hash `json:"txHash"`
Result interface{} `json:"result"`
Error string `json:"error,omitempty"`
}
// TraceXDCBlock traces an XDC block with XDC-specific options
func (api *API) TraceXDCBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *XDCTraceConfig) (*XDCBlockTraceResult, error) {
block, err := api.blockByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return nil, err
}
if block == nil {
return nil, errors.New("block not found")
}
result := &XDCBlockTraceResult{
Transactions: make([]*TxTraceResult, 0),
}
// Trace transactions
for _, tx := range block.Transactions() {
txResult, err := api.traceTransaction(ctx, tx.Hash(), config.TraceConfig)
if err != nil {
result.Transactions = append(result.Transactions, &TxTraceResult{
TxHash: tx.Hash(),
Error: err.Error(),
})
} else {
result.Transactions = append(result.Transactions, &TxTraceResult{
TxHash: tx.Hash(),
Result: txResult,
})
}
}
// Include rewards if requested
if config != nil && config.IncludeRewards {
rewards := api.getBlockRewards(ctx, block)
result.Rewards = rewards
}
// Include penalties if requested
if config != nil && config.IncludePenalties {
penalties := api.getBlockPenalties(ctx, block)
result.Penalties = penalties
}
// Include votes if requested
if config != nil && config.IncludeVotes {
votes := api.getBlockVotes(ctx, block)
result.Votes = votes
}
return result, nil
}
// TraceXDCTransaction traces a transaction with XDC-specific context
func (api *API) TraceXDCTransaction(ctx context.Context, hash common.Hash, config *XDCTraceConfig) (interface{}, error) {
return api.traceTransaction(ctx, hash, config.TraceConfig)
}
// GetXDCConsensusTrace returns consensus-related trace information
func (api *API) GetXDCConsensusTrace(ctx context.Context, blockNr rpc.BlockNumber) (interface{}, error) {
block, err := api.blockByNumber(ctx, blockNr)
if err != nil {
return nil, err
}
if block == nil {
return nil, errors.New("block not found")
}
header := block.Header()
consensusTrace := map[string]interface{}{
"blockNumber": block.NumberU64(),
"blockHash": block.Hash(),
"miner": header.Coinbase,
"difficulty": header.Difficulty,
"gasUsed": header.GasUsed,
"gasLimit": header.GasLimit,
"timestamp": header.Time,
}
// Add XDPoS specific fields
if len(header.Extra) > 0 {
consensusTrace["extraData"] = hexutil.Bytes(header.Extra)
}
return consensusTrace, nil
}
// getBlockRewards extracts rewards from a block
func (api *API) getBlockRewards(ctx context.Context, block *types.Block) []*RewardResult {
rewards := make([]*RewardResult, 0)
// Block reward to miner
rewards = append(rewards, &RewardResult{
Validator: block.Coinbase(),
Amount: (*hexutil.Big)(big.NewInt(0)), // Calculated based on consensus
Type: "block",
})
return rewards
}
// getBlockPenalties extracts penalties from a block
func (api *API) getBlockPenalties(ctx context.Context, block *types.Block) []*PenaltyResult {
penalties := make([]*PenaltyResult, 0)
// Extract penalties from block transactions or state
return penalties
}
// getBlockVotes extracts votes from a block
func (api *API) getBlockVotes(ctx context.Context, block *types.Block) []*VoteResult {
votes := make([]*VoteResult, 0)
// Extract votes from block transactions
return votes
}
// traceTransaction traces a single transaction
func (api *API) traceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
// This is a placeholder - actual implementation would use the full tracer
return nil, nil
}
// blockByNumber retrieves a block by number
func (api *API) blockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
// Implementation depends on backend
return nil, nil
}
// blockByNumberOrHash retrieves a block by number or hash
func (api *API) blockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
// Implementation depends on backend
return nil, nil
}
// XDCInternalTxTracer traces internal transactions in XDC blocks
type XDCInternalTxTracer struct {
backend ethapi.Backend
chainConfig *core.ChainConfig
}
// NewXDCInternalTxTracer creates a new internal transaction tracer
func NewXDCInternalTxTracer(backend ethapi.Backend) *XDCInternalTxTracer {
return &XDCInternalTxTracer{
backend: backend,
}
}
// TraceInternalTransactions traces internal transactions for a block
func (t *XDCInternalTxTracer) TraceInternalTransactions(ctx context.Context, blockNr rpc.BlockNumber) ([]InternalTx, error) {
internalTxs := make([]InternalTx, 0)
// Implementation for tracing internal transactions
return internalTxs, nil
}
// InternalTx represents an internal transaction
type InternalTx struct {
ParentTxHash common.Hash `json:"parentTxHash"`
Type string `json:"type"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *hexutil.Big `json:"value"`
Gas uint64 `json:"gas"`
GasUsed uint64 `json:"gasUsed"`
Input hexutil.Bytes `json:"input"`
Output hexutil.Bytes `json:"output"`
Error string `json:"error,omitempty"`
Depth int `json:"depth"`
}

View file

@ -1,252 +0,0 @@
// Copyright 2021 XDC Network
// This file is part of the XDC library.
package native
import (
"encoding/json"
"math/big"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)
func init() {
tracers.DefaultDirectory.Register("xdcTracer", newXDCTracer, false)
}
// xdcTracer is a native tracer for XDC-specific operations
type xdcTracer struct {
env *vm.EVM
config xdcTracerConfig
gasLimit uint64
interrupt atomic.Bool
reason error
// Results
result *XDCTraceResult
callStack []*xdcCall
}
// xdcTracerConfig holds configuration for the XDC tracer
type xdcTracerConfig struct {
TraceInternalCalls bool `json:"traceInternalCalls"`
TraceStorage bool `json:"traceStorage"`
TraceRewards bool `json:"traceRewards"`
}
// XDCTraceResult holds the trace result
type XDCTraceResult struct {
Type string `json:"type"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *big.Int `json:"value"`
Gas uint64 `json:"gas"`
GasUsed uint64 `json:"gasUsed"`
Input []byte `json:"input"`
Output []byte `json:"output"`
Error string `json:"error,omitempty"`
RevertReason string `json:"revertReason,omitempty"`
Calls []*xdcCall `json:"calls,omitempty"`
}
// xdcCall represents an internal call
type xdcCall struct {
Type string `json:"type"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *big.Int `json:"value,omitempty"`
Gas uint64 `json:"gas"`
GasUsed uint64 `json:"gasUsed"`
Input []byte `json:"input,omitempty"`
Output []byte `json:"output,omitempty"`
Error string `json:"error,omitempty"`
Calls []*xdcCall `json:"calls,omitempty"`
}
// newXDCTracer creates a new XDC tracer
func newXDCTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
var config xdcTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
return &xdcTracer{
config: config,
result: &XDCTraceResult{},
}, nil
}
// CaptureStart implements the EVMLogger interface
func (t *xdcTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.env = env
t.gasLimit = gas
t.result.From = from
t.result.To = to
t.result.Input = input
t.result.Gas = gas
if value != nil {
t.result.Value = new(big.Int).Set(value)
}
if create {
t.result.Type = "CREATE"
} else {
t.result.Type = "CALL"
}
}
// CaptureEnd implements the EVMLogger interface
func (t *xdcTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
t.result.Output = output
t.result.GasUsed = gasUsed
if err != nil {
t.result.Error = err.Error()
}
}
// CaptureState implements the EVMLogger interface
func (t *xdcTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
// Not tracking individual opcodes in this tracer
}
// CaptureFault implements the EVMLogger interface
func (t *xdcTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
// Capture fault information
}
// CaptureEnter implements the EVMLogger interface
func (t *xdcTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if !t.config.TraceInternalCalls {
return
}
call := &xdcCall{
Type: typ.String(),
From: from,
To: to,
Gas: gas,
Input: input,
}
if value != nil {
call.Value = new(big.Int).Set(value)
}
t.callStack = append(t.callStack, call)
}
// CaptureExit implements the EVMLogger interface
func (t *xdcTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
if !t.config.TraceInternalCalls || len(t.callStack) == 0 {
return
}
// Pop the last call from the stack
size := len(t.callStack)
call := t.callStack[size-1]
t.callStack = t.callStack[:size-1]
call.Output = output
call.GasUsed = gasUsed
if err != nil {
call.Error = err.Error()
}
// Add to parent or result
if len(t.callStack) > 0 {
parent := t.callStack[len(t.callStack)-1]
parent.Calls = append(parent.Calls, call)
} else {
t.result.Calls = append(t.result.Calls, call)
}
}
// CaptureTxStart implements the EVMLogger interface
func (t *xdcTracer) CaptureTxStart(gasLimit uint64) {
t.gasLimit = gasLimit
}
// CaptureTxEnd implements the EVMLogger interface
func (t *xdcTracer) CaptureTxEnd(restGas uint64) {
t.result.GasUsed = t.gasLimit - restGas
}
// GetResult returns the trace result
func (t *xdcTracer) GetResult() (json.RawMessage, error) {
return json.Marshal(t.result)
}
// Stop terminates the tracer
func (t *xdcTracer) Stop(err error) {
t.reason = err
t.interrupt.Store(true)
}
// XDCRewardTracer traces validator rewards
type XDCRewardTracer struct {
rewards map[common.Address]*big.Int
}
// NewXDCRewardTracer creates a new reward tracer
func NewXDCRewardTracer() *XDCRewardTracer {
return &XDCRewardTracer{
rewards: make(map[common.Address]*big.Int),
}
}
// AddReward adds a reward for a validator
func (t *XDCRewardTracer) AddReward(validator common.Address, amount *big.Int) {
if existing, ok := t.rewards[validator]; ok {
t.rewards[validator] = new(big.Int).Add(existing, amount)
} else {
t.rewards[validator] = new(big.Int).Set(amount)
}
}
// GetRewards returns all rewards
func (t *XDCRewardTracer) GetRewards() map[common.Address]*big.Int {
result := make(map[common.Address]*big.Int)
for addr, amount := range t.rewards {
result[addr] = new(big.Int).Set(amount)
}
return result
}
// XDCPenaltyTracer traces validator penalties
type XDCPenaltyTracer struct {
penalties []XDCPenalty
}
// XDCPenalty represents a penalty
type XDCPenalty struct {
Validator common.Address
Amount *big.Int
Reason string
Block uint64
}
// NewXDCPenaltyTracer creates a new penalty tracer
func NewXDCPenaltyTracer() *XDCPenaltyTracer {
return &XDCPenaltyTracer{
penalties: make([]XDCPenalty, 0),
}
}
// AddPenalty adds a penalty
func (t *XDCPenaltyTracer) AddPenalty(validator common.Address, amount *big.Int, reason string, block uint64) {
t.penalties = append(t.penalties, XDCPenalty{
Validator: validator,
Amount: new(big.Int).Set(amount),
Reason: reason,
Block: block,
})
}
// GetPenalties returns all penalties
func (t *XDCPenaltyTracer) GetPenalties() []XDCPenalty {
return t.penalties
}

View file

@ -1,335 +0,0 @@
// Copyright 2023 The XDC Network Authors
// This file is part of the XDC Network library.
//
// The XDC Network library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
package miner
import (
"errors"
"math/big"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
var (
// ErrNotAuthorized is returned when the signer is not authorized
ErrNotAuthorized = errors.New("signer not authorized")
// ErrWrongDifficulty is returned when difficulty check fails
ErrWrongDifficulty = errors.New("wrong difficulty")
)
// XDCWorkerConfig contains XDPoS-specific worker configuration
type XDCWorkerConfig struct {
// Recommit interval for block sealing
Recommit time.Duration
// GasFloor is the target gas floor for blocks
GasFloor uint64
// GasCeil is the target gas ceiling for blocks
GasCeil uint64
// Extra data for blocks
ExtraData []byte
}
// XDCWorker extends the base worker with XDPoS-specific functionality
type XDCWorker struct {
config *params.ChainConfig
chainConfig *params.ChainConfig
engine consensus.Engine
eth Backend
chain *core.BlockChain
// Feeds
pendingLogsFeed event.Feed
// Channels
taskCh chan *types.Block
startCh chan struct{}
exitCh chan struct{}
resubmitIntervalCh chan time.Duration
// State
running int32 // atomic
syncing int32 // atomic
// Current work
mu sync.RWMutex
coinbase common.Address
extra []byte
// Pending block
pendingMu sync.RWMutex
pendingBlock *types.Block
pendingState *state.StateDB
// XDPoS specific
orderpool OrderPool
lendingpool LendingPool
}
// OrderPool interface for XDCx order pool integration
type OrderPool interface {
Pending() (map[common.Address]types.OrderTransactions, error)
}
// LendingPool interface for XDCx lending pool integration
type LendingPool interface {
Pending() (map[common.Address]types.LendingTransactions, error)
}
// Backend wraps all required backend methods for mining
type Backend interface {
BlockChain() *core.BlockChain
TxPool() TxPool
}
// TxPool interface for transaction pool
type TxPool interface {
Pending(enforceTips bool) map[common.Address][]*types.Transaction
}
// NewXDCWorker creates a new XDPoS worker
func NewXDCWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *XDCWorker {
worker := &XDCWorker{
config: config,
chainConfig: config,
engine: engine,
eth: eth,
chain: eth.BlockChain(),
taskCh: make(chan *types.Block),
startCh: make(chan struct{}, 1),
exitCh: make(chan struct{}),
resubmitIntervalCh: make(chan time.Duration),
}
if init {
go worker.mainLoop()
}
return worker
}
// mainLoop is the main event loop for the worker
func (w *XDCWorker) mainLoop() {
for {
select {
case <-w.startCh:
w.commitWork()
case <-w.exitCh:
return
}
}
}
// start begins the mining process
func (w *XDCWorker) start() {
atomic.StoreInt32(&w.running, 1)
w.startCh <- struct{}{}
}
// stop halts the mining process
func (w *XDCWorker) stop() {
atomic.StoreInt32(&w.running, 0)
}
// close terminates all internal goroutines
func (w *XDCWorker) close() {
close(w.exitCh)
}
// isRunning returns whether the worker is running
func (w *XDCWorker) isRunning() bool {
return atomic.LoadInt32(&w.running) == 1
}
// setEtherbase sets the etherbase for mining
func (w *XDCWorker) setEtherbase(addr common.Address) {
w.mu.Lock()
defer w.mu.Unlock()
w.coinbase = addr
}
// setExtra sets extra data for blocks
func (w *XDCWorker) setExtra(extra []byte) {
w.mu.Lock()
defer w.mu.Unlock()
w.extra = extra
}
// pending returns the pending block and state
func (w *XDCWorker) pending() (*types.Block, *state.StateDB) {
w.pendingMu.RLock()
defer w.pendingMu.RUnlock()
if w.pendingBlock != nil {
return w.pendingBlock, w.pendingState.Copy()
}
return nil, nil
}
// pendingBlock returns the pending block
func (w *XDCWorker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
w.pendingMu.RLock()
defer w.pendingMu.RUnlock()
return w.pendingBlock, nil
}
// commitWork generates a new work based on the parent block
func (w *XDCWorker) commitWork() {
parent := w.chain.CurrentBlock()
if parent == nil {
return
}
w.mu.RLock()
coinbase := w.coinbase
extra := w.extra
w.mu.RUnlock()
if coinbase == (common.Address{}) {
log.Error("Refusing to mine without etherbase")
return
}
// Create new work
num := parent.Number()
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(num, common.Big1),
GasLimit: core.CalcGasLimit(parent.GasLimit(), w.config.XDPoS.GasLimitBoundDivisor),
Extra: extra,
Time: uint64(time.Now().Unix()),
}
// Set coinbase for XDPoS
if w.config.XDPoS != nil {
header.Coinbase = coinbase
}
// Prepare header with consensus engine
if err := w.engine.Prepare(w.chain, header); err != nil {
log.Error("Failed to prepare header for sealing", "err", err)
return
}
// Create state
statedb, err := w.chain.StateAt(parent.Root())
if err != nil {
log.Error("Failed to create state", "err", err)
return
}
// Fill transactions
pending := w.eth.TxPool().Pending(true)
txs := make([]*types.Transaction, 0)
for _, list := range pending {
txs = append(txs, list...)
}
// Apply transactions
receipts, logs := w.applyTransactions(txs, statedb, header)
// Finalize block with consensus engine
block, err := w.engine.FinalizeAndAssemble(w.chain, header, statedb, &types.Body{Transactions: txs}, receipts)
if err != nil {
log.Error("Failed to finalize block", "err", err)
return
}
// Store pending work
w.pendingMu.Lock()
w.pendingBlock = block
w.pendingState = statedb
w.pendingMu.Unlock()
// Submit to seal
w.taskCh <- block
log.Info("Commit new sealing work", "number", block.Number(), "txs", len(txs), "logs", len(logs))
}
// applyTransactions applies transactions to the state
func (w *XDCWorker) applyTransactions(txs []*types.Transaction, statedb *state.StateDB, header *types.Header) ([]*types.Receipt, []*types.Log) {
var (
receipts []*types.Receipt
logs []*types.Log
gasPool = new(core.GasPool).AddGas(header.GasLimit)
)
for _, tx := range txs {
statedb.Prepare(tx.Hash(), len(receipts))
receipt, err := core.ApplyTransaction(w.config, w.chain, nil, gasPool, statedb, header, tx, &header.GasUsed, *w.chain.GetVMConfig())
if err != nil {
continue
}
receipts = append(receipts, receipt)
logs = append(logs, receipt.Logs...)
}
return receipts, logs
}
// setOrderPool sets the order pool for XDCx integration
func (w *XDCWorker) setOrderPool(orderpool OrderPool) {
w.orderpool = orderpool
}
// setLendingPool sets the lending pool for XDCx integration
func (w *XDCWorker) setLendingPool(lendingpool LendingPool) {
w.lendingpool = lendingpool
}
// getOrderTransactions gets pending order transactions
func (w *XDCWorker) getOrderTransactions() (types.OrderTransactions, error) {
if w.orderpool == nil {
return nil, nil
}
pending, err := w.orderpool.Pending()
if err != nil {
return nil, err
}
var txs types.OrderTransactions
for _, list := range pending {
txs = append(txs, list...)
}
return txs, nil
}
// getLendingTransactions gets pending lending transactions
func (w *XDCWorker) getLendingTransactions() (types.LendingTransactions, error) {
if w.lendingpool == nil {
return nil, nil
}
pending, err := w.lendingpool.Pending()
if err != nil {
return nil, err
}
var txs types.LendingTransactions
for _, list := range pending {
txs = append(txs, list...)
}
return txs, nil
}