Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions consensus/parlia/stakehub.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,60 @@ func (p *Parlia) GetNodeIDsMap() (map[common.Address][]enode.ID, error) {

return nodeIDsMap, nil
}

// RemoveNodeIDs creates a signed transaction to remove node IDs from the StakeHub contract
func (p *Parlia) RemoveNodeIDs(nodeIDs []enode.ID, nonce uint64) (*types.Transaction, error) {
log.Debug("Removing node IDs", "count", len(nodeIDs), "nonce", nonce)

p.lock.RLock()
signTxFn := p.signTxFn
val := p.val
p.lock.RUnlock()

if signTxFn == nil {
log.Error("Signing function not set")
return nil, fmt.Errorf("signing function not set, call Authorize first")
}

// Create the call data for removeNodeIDs
data, err := p.stakeHubABI.Pack("removeNodeIDs", nodeIDs)
if err != nil {
log.Error("Failed to pack removeNodeIDs", "error", err)
return nil, fmt.Errorf("failed to pack removeNodeIDs: %v", err)
}

to := common.HexToAddress(systemcontracts.StakeHubContract)
hexData := hexutil.Bytes(data)
hexNonce := hexutil.Uint64(nonce)
gas, err := p.ethAPI.EstimateGas(context.Background(), ethapi.TransactionArgs{
From: &val,
To: &to,
Nonce: &hexNonce,
Data: &hexData,
}, nil, nil, nil)
if err != nil {
log.Error("Failed to estimate gas", "error", err)
return nil, fmt.Errorf("failed to estimate gas: %v", err)
}

// Create the transaction
tx := types.NewTransaction(
nonce,
common.HexToAddress(systemcontracts.StakeHubContract),
common.Big0,
uint64(gas),
big.NewInt(1000000000),
data,
)

// Sign the transaction with the node's private key
log.Debug("Signing transaction", "validator", val)
signedTx, err := signTxFn(accounts.Account{Address: val}, tx, p.chainConfig.ChainID)
if err != nil {
log.Error("Failed to sign transaction", "error", err)
return nil, fmt.Errorf("failed to sign transaction: %v", err)
}

log.Debug("Successfully created signed transaction", "hash", signedTx.Hash())
return signedTx, nil
}
130 changes: 98 additions & 32 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,61 +544,127 @@ func (s *Ethereum) waitForSyncAndMaxwell(parlia *parlia.Parlia) {
continue
}
log.Info("Node is synced and Maxwell fork is active, proceeding with node ID registration")
err := s.registerNodeID(parlia)
err := s.updateNodeID(parlia)
if err == nil {
return
}
retryCount++
if retryCount > 3 {
log.Error("Failed to register node ID exceed max retry count", "retryCount", retryCount, "err", err)
log.Error("Failed to update node ID exceed max retry count", "retryCount", retryCount, "err", err)
return
}
}
}
}

// registerNodeID registers the node ID with the StakeHub contract
func (s *Ethereum) registerNodeID(parlia *parlia.Parlia) error {
// Check if node ID is already registered
nodeIDs, err := parlia.GetNodeIDs()
// updateNodeID registers the node ID with the StakeHub contract
func (s *Ethereum) updateNodeID(parlia *parlia.Parlia) error {
nonce, err := s.APIBackend.GetPoolNonce(context.Background(), s.etherbase)
if err != nil {
log.Error("Failed to get registered node IDs", "err", err)
return fmt.Errorf("failed to get nonce: %v", err)
}

// Handle removals first
if err := s.handleRemovals(parlia, nonce); err != nil {
return err
}
nonce++

// Handle additions
return s.handleAdditions(parlia, nonce)
}

func (s *Ethereum) handleRemovals(parlia *parlia.Parlia, nonce uint64) error {
if len(s.config.EVNNodeIDsToRemove) == 0 {
return nil
}

// Check which node IDs need to be registered
nodeIDsToAdd := make([]enode.ID, 0)
for _, idToRegister := range s.config.ValidatorNodeIDsToAdd {
isRegistered := false
for _, id := range nodeIDs {
if id == idToRegister {
isRegistered = true
break
// Handle wildcard removal
if len(s.config.EVNNodeIDsToRemove) == 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does removal need to check the registeredSet? It may avoid a repeat removal operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean get once this account can remove and check against that ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the nodeIDs have been removed, they may be removed again when the BSC is restarted next time. If here check the registeredSet from on-chain, it may avoid this repeat removal operation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a bug, if you agree, you could optimize it next time.

var zeroID enode.ID // This will be all zeros
if s.config.EVNNodeIDsToRemove[0] == zeroID {
trx, err := parlia.RemoveNodeIDs([]enode.ID{}, nonce)
if err != nil {
return fmt.Errorf("failed to create node ID removal transaction: %v", err)
}
}
if !isRegistered {
nodeIDsToAdd = append(nodeIDsToAdd, idToRegister)
if err := s.txPool.Add([]*types.Transaction{trx}, false); err != nil {
return fmt.Errorf("failed to add node ID removal transaction to pool: %v", err)
}
log.Info("Submitted node ID removal transaction for all node IDs")
return nil
}
}

if len(nodeIDsToAdd) > 0 {
// Get the current nonce for the validator address
nonce, err := s.APIBackend.GetPoolNonce(context.Background(), s.etherbase)
if err != nil {
return fmt.Errorf("failed to get nonce: %v", err)
}
// Create a set of node IDs to add for quick lookup
addSet := make(map[enode.ID]struct{}, len(s.config.EVNNodeIDsToAdd))
for _, id := range s.config.EVNNodeIDsToAdd {
addSet[id] = struct{}{}
}

trx, err := parlia.AddNodeIDs(nodeIDsToAdd, nonce)
if err != nil {
return fmt.Errorf("failed to create node ID registration transaction: %v", err)
// Filter out node IDs that are in the add set
nodeIDsToRemove := make([]enode.ID, 0, len(s.config.EVNNodeIDsToRemove))
for _, id := range s.config.EVNNodeIDsToRemove {
if _, exists := addSet[id]; !exists {
nodeIDsToRemove = append(nodeIDsToRemove, id)
} else {
log.Debug("Skipping node ID removal", "id", id, "reason", "also in EVNNodeIDsToAdd")
}
if err := s.txPool.Add([]*types.Transaction{trx}, false); err != nil {
return fmt.Errorf("failed to add node ID registration transaction to pool: %v", err)
}

if len(nodeIDsToRemove) == 0 {
return nil
}

trx, err := parlia.RemoveNodeIDs(nodeIDsToRemove, nonce)
if err != nil {
return fmt.Errorf("failed to create node ID removal transaction: %v", err)
}
if err := s.txPool.Add([]*types.Transaction{trx}, false); err != nil {
return fmt.Errorf("failed to add node ID removal transaction to pool: %v", err)
}
log.Info("Submitted node ID removal transaction", "nodeIDs", nodeIDsToRemove)
return nil
}

func (s *Ethereum) handleAdditions(parlia *parlia.Parlia, nonce uint64) error {
if len(s.config.EVNNodeIDsToAdd) == 0 {
return nil
}

// Get currently registered node IDs
registeredIDs, err := parlia.GetNodeIDs()
if err != nil {
log.Error("Failed to get registered node IDs", "err", err)
return err
}

// Create a set of registered IDs for quick lookup
registeredSet := make(map[enode.ID]struct{}, len(registeredIDs))
for _, id := range registeredIDs {
registeredSet[id] = struct{}{}
}

// Filter out already registered IDs in a single pass
nodeIDsToAdd := make([]enode.ID, 0, len(s.config.EVNNodeIDsToAdd))
for _, id := range s.config.EVNNodeIDsToAdd {
if _, exists := registeredSet[id]; !exists {
nodeIDsToAdd = append(nodeIDsToAdd, id)
}
log.Info("Submitted node ID registration transaction", "nodeIDs", nodeIDsToAdd)
} else {
log.Info("All node IDs already registered", "nodeIDs", s.config.ValidatorNodeIDsToAdd)
}

if len(nodeIDsToAdd) == 0 {
log.Info("No new node IDs to register after deduplication")
return nil
}

trx, err := parlia.AddNodeIDs(nodeIDsToAdd, nonce)
if err != nil {
return fmt.Errorf("failed to create node ID registration transaction: %v", err)
}
if err := s.txPool.Add([]*types.Transaction{trx}, false); err != nil {
return fmt.Errorf("failed to add node ID registration transaction to pool: %v", err)
}
log.Info("Submitted node ID registration transaction", "nodeIDs", nodeIDsToAdd)
return nil
}

Expand Down
3 changes: 2 additions & 1 deletion eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ type Config struct {
// transactions) or is continuously under high pressure (e.g., mempool is always full), then you can consider
// to turn it on.
DisablePeerTxBroadcast bool
ValidatorNodeIDsToAdd []enode.ID
EVNNodeIDsToAdd []enode.ID
EVNNodeIDsToRemove []enode.ID
// This can be set to list of enrtree:// URLs which will be queried for
// nodes to connect to.
EthDiscoveryURLs []string
Expand Down
16 changes: 11 additions & 5 deletions eth/ethconfig/gen_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.