Skip to content
Open
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
4 changes: 4 additions & 0 deletions chain/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const (
TxHashWithType = "txHashWithType"
LondonFix = "londonfix"
PriceOracleFix = "priceOracleFix"
DoubleSignSlashing = "doubleSignSlashing"
)

// Forks is map which contains all forks and their starting blocks from genesis
Expand Down Expand Up @@ -130,6 +131,7 @@ func (f *Forks) At(block uint64) ForksInTime {
TxHashWithType: f.IsActive(TxHashWithType, block),
LondonFix: f.IsActive(LondonFix, block),
PriceOracleFix: f.IsActive(PriceOracleFix, block),
DoubleSignSlashing: f.IsActive(DoubleSignSlashing, block),
}
}

Expand Down Expand Up @@ -184,6 +186,7 @@ type ForksInTime struct {
TxHashWithType,
LondonFix,
PriceOracleFix bool
DoubleSignSlashing bool
}

// AllForksEnabled should contain all supported forks by current edge version
Expand All @@ -201,4 +204,5 @@ var AllForksEnabled = &Forks{
TxHashWithType: NewFork(0),
LondonFix: NewFork(0),
PriceOracleFix: NewFork(0),
DoubleSignSlashing: NewFork(0),
}
3 changes: 3 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,7 @@ type BridgeDataProvider interface {

// GetStateSyncProof retrieves the StateSync proof
GetStateSyncProof(stateSyncID uint64) (types.Proof, error)

// GetPendingSlashProofs retrieves executable slashing exit event proofs
GetPendingSlashProofs() ([]types.Proof, error)
}
8 changes: 7 additions & 1 deletion consensus/ibft/ibft.go
Copy link
Collaborator

@R-Santev R-Santev Oct 24, 2025

Choose a reason for hiding this comment

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

We don't support the IBFT consensus, so no need to make updates there

Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,13 @@ func (i *backendIBFT) logFork(
lastSigner, signer signer.Signer,
) {
if lastSigner != nil && signer != nil && lastSigner.Type() != signer.Type() {
i.logger.Info("IBFT validation type switched", "old", lastSigner.Type(), "new", signer.Type())
i.logger.Info("IBFT validation type switched",
"old", lastSigner.Type(),
"new", signer.Type())

fmt.Printf("[FORK] IBFT validation type switched from '%s' to '%s'\n",
lastSigner.Type(),
signer.Type())
}
}

Expand Down
4 changes: 2 additions & 2 deletions consensus/ibft/signer/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func Test_wrapCommitHash(t *testing.T) {
assert.Equal(t, expectedOutput, output)
}

//nolint
// nolint
func Test_getOrCreateECDSAKey(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -184,7 +184,7 @@ func Test_getOrCreateECDSAKey(t *testing.T) {
}
}

//nolint
// nolint
func Test_getOrCreateBLSKey(t *testing.T) {
t.Parallel()

Expand Down
17 changes: 17 additions & 0 deletions consensus/polybft/block_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type BlockBuilderParams struct {

// BaseFee is the base fee
BaseFee uint64
// Add fork block number for slashing contract
SlashingForkBlock uint64
}

func NewBlockBuilder(params *BlockBuilderParams) *BlockBuilder {
Expand Down Expand Up @@ -113,6 +115,21 @@ func (b *BlockBuilder) Build(handler func(h *types.Header)) (*types.FullBlock, e
handler(b.header)
}

// Check if this is the fork block and deploy slashing contract if needed
// This ensures deterministic deployment during block creation
if b.header.Number == b.params.SlashingForkBlock && b.params.SlashingForkBlock > 0 {
// Use shared deployment function to ensure consistency with PreCommitState
if err := deploySlashingContractShared(
b.header.Number,
b.params.SlashingForkBlock,
b.state,
b.params.Logger,
b.params.Coinbase,
); err != nil {
return nil, err
}
}

_, stateRoot, err := b.state.Commit()
if err != nil {
return nil, fmt.Errorf("failed to commit the state changes: %w", err)
Expand Down
33 changes: 25 additions & 8 deletions consensus/polybft/blockchain_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/go-hclog"

"github.com/0xPolygon/polygon-edge/blockchain"
"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/consensus"
"github.com/0xPolygon/polygon-edge/contracts"
"github.com/0xPolygon/polygon-edge/state"
Expand Down Expand Up @@ -123,6 +124,12 @@ func (p *blockchainWrapper) ProcessBlock(
}
}

// Call PreCommitState before committing the transition
// This ensures contract deployment and other state hooks are executed
if err := p.blockchain.GetConsensus().PreCommitState(block, transition); err != nil {
return nil, fmt.Errorf("failed to execute PreCommitState: %w", err)
}

_, root, err := transition.Commit()
if err != nil {
return nil, fmt.Errorf("failed to commit the state changes: %w", err)
Expand Down Expand Up @@ -188,15 +195,25 @@ func (p *blockchainWrapper) NewBlockBuilder(
return nil, err
}

// Safely extract slashing fork block number
var slashingForkBlock uint64

if p.blockchain.Config().Forks != nil {
if fork, ok := (*p.blockchain.Config().Forks)[chain.DoubleSignSlashing]; ok {
slashingForkBlock = fork.Block
}
}

return NewBlockBuilder(&BlockBuilderParams{
BlockTime: blockTime,
Parent: parent,
Coinbase: coinbase,
Executor: p.executor,
GasLimit: gasLimit,
BaseFee: p.blockchain.CalculateBaseFee(parent),
TxPool: txPool,
Logger: logger,
BlockTime: blockTime,
Parent: parent,
Coinbase: coinbase,
Executor: p.executor,
GasLimit: gasLimit,
BaseFee: p.blockchain.CalculateBaseFee(parent),
TxPool: txPool,
Logger: logger,
SlashingForkBlock: slashingForkBlock,
}), nil
}

Expand Down
5 changes: 5 additions & 0 deletions consensus/polybft/checkpoint_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type CheckpointManager interface {
PostBlock(req *PostBlockRequest) error
BuildEventRoot(epoch uint64) (types.Hash, error)
GenerateExitProof(exitID uint64) (types.Proof, error)
GenerateSlashExitProofs() ([]types.Proof, error)
}

var _ CheckpointManager = (*dummyCheckpointManager)(nil)
Expand All @@ -25,6 +26,10 @@ func (d *dummyCheckpointManager) GenerateExitProof(exitID uint64) (types.Proof,
return types.Proof{}, nil
}

func (d *dummyCheckpointManager) GenerateSlashExitProofs() ([]types.Proof, error) {
return nil, nil
}

// EventSubscriber implementation
func (d *dummyCheckpointManager) GetLogFilters() map[types.Address][]types.Hash {
return make(map[types.Address][]types.Hash)
Expand Down
18 changes: 18 additions & 0 deletions consensus/polybft/common/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package common

import (
"github.com/0xPolygon/polygon-edge/types"
bolt "go.etcd.io/bbolt"
)

type PostBlockRequest struct {
// FullBlock is a reference of the executed block
FullBlock *types.FullBlock
// Epoch is the epoch number of the executed block
Epoch uint64
// IsEpochEndingBlock indicates if this was the last block of given epoch
IsEpochEndingBlock bool
// DBTx is the opened transaction on state store (in our case boltDB)
// used to save necessary data on PostBlock
DBTx *bolt.Tx
}
45 changes: 45 additions & 0 deletions consensus/polybft/consensus_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"github.com/0xPolygon/polygon-edge/types"
bolt "go.etcd.io/bbolt"

"github.com/0xPolygon/polygon-edge/consensus/polybft/common"
"github.com/0xPolygon/polygon-edge/consensus/polybft/slashing"
"github.com/Hydra-Chain/go-ibft/messages"
"github.com/Hydra-Chain/go-ibft/messages/proto"
hcf "github.com/hashicorp/go-hclog"
Expand Down Expand Up @@ -130,6 +132,8 @@ type consensusRuntime struct {
// rewardWalletCalculator is the object which handles the calculation of the required HYDRA
// that needs to be sent to the reward in order to have enough funds
rewardWalletCalculator RewardWalletCalculator
// doubleSigningTracker tracks IBFT messages and detects double signing
doubleSigningTracker slashing.DoubleSigningTracker
}

// newConsensusRuntime creates and starts a new consensus runtime instance with event tracking
Expand Down Expand Up @@ -250,6 +254,20 @@ func (c *consensusRuntime) initStakeManager(logger hcf.Logger, dbTx *bolt.Tx) er
return err
}

// initDoubleSigningTracker initializes double signing tracker
//
// (which is used for creating slashing evidence).
func (c *consensusRuntime) initDoubleSigningTracker(logger hcf.Logger, store *StakeStore) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This method is unused

tracker, err := slashing.NewDoubleSigningTracker(logger.Named("double_sign_tracker"), store)
if err != nil {
return fmt.Errorf("failed to initialize double signing tracker: %w", err)
}

c.doubleSigningTracker = tracker

return nil
}

// getGuardedData returns last build block, proposer snapshot and current epochMetadata in a thread-safe manner.
func (c *consensusRuntime) getGuardedData() (guardedDataDTO, error) {
c.lock.RLock()
Expand Down Expand Up @@ -366,6 +384,19 @@ func (c *consensusRuntime) OnBlockInserted(fullBlock *types.FullBlock) {
c.logger.Error("post block callback failed in state sync relayer", "err", err)
}

// handle double signing tracker events that happened in block
if c.doubleSigningTracker != nil {
commonPostBlock := &common.PostBlockRequest{
FullBlock: fullBlock,
Epoch: epoch.Number,
IsEpochEndingBlock: isEndOfEpoch,
DBTx: dbTx,
}
if err := c.doubleSigningTracker.PostBlock(commonPostBlock); err != nil {
c.logger.Error("post block callback failed in double signing tracker", "err", err)
}
}

if isEndOfEpoch {
if epoch, err = c.restartEpoch(fullBlock.Block.Header, dbTx); err != nil {
c.logger.Error("failed to restart epoch after block inserted", "error", err)
Expand Down Expand Up @@ -456,9 +487,18 @@ func (c *consensusRuntime) FSM() error {
isEndOfSprint: isEndOfSprint,
isStartOfEpoch: isStartOfEpoch,
proposerSnapshot: proposerSnapshot,
forks: c.config.consensusConfig.Params.Forks,
logger: c.logger.Named("fsm"),
}

// Populate double signers for the parent block
if c.doubleSigningTracker != nil {
ff.doubleSigners = c.doubleSigningTracker.GetDoubleSigners(parent.Number)
if len(ff.doubleSigners) > 0 {
c.logger.Info("Found double signers", "block", parent.Number, "doubleSigners", ff.doubleSigners)
}
}

if isEndOfSprint {
commitment, err := c.stateSyncManager.Commitment(pendingBlockNumber)
if err != nil {
Expand Down Expand Up @@ -752,6 +792,11 @@ func (c *consensusRuntime) GetStateSyncProof(stateSyncID uint64) (types.Proof, e
return c.stateSyncManager.GetStateSyncProof(stateSyncID)
}

// GetPendingSlashProofs retrieves executable slashing exit event proofs
func (c *consensusRuntime) GetPendingSlashProofs() ([]types.Proof, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is unused?

return c.checkpointManager.GenerateSlashExitProofs()
}

// setIsActiveValidator updates the activeValidatorFlag field
func (c *consensusRuntime) setIsActiveValidator(isActiveValidator bool) {
c.activeValidatorFlag.Store(isActiveValidator)
Expand Down
15 changes: 15 additions & 0 deletions consensus/polybft/consensus_runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"
"time"

"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/consensus"
"github.com/0xPolygon/polygon-edge/consensus/polybft/bitmap"
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
Expand Down Expand Up @@ -434,6 +435,11 @@ func TestConsensusRuntime_FSM_NotEndOfEpoch_NotEndOfSprint(t *testing.T) {
},
Key: wallet.NewKey(validators.GetPrivateIdentities()[0]),
blockchain: blockchainMock,
consensusConfig: &consensus.Config{
Params: &chain.Params{
Forks: &chain.Forks{},
},
},
}
runtime := &consensusRuntime{
proposerCalculator: NewProposerCalculatorFromSnapshot(
Expand All @@ -452,6 +458,10 @@ func TestConsensusRuntime_FSM_NotEndOfEpoch_NotEndOfSprint(t *testing.T) {
state: newTestState(t),
stateSyncManager: &dummyStateSyncManager{},
checkpointManager: &dummyCheckpointManager{},
stakeManager: &dummyStakeManager{},
rewardWalletCalculator: &rewardWalletCalculator{
blockchain: blockchainMock,
},
}
runtime.setIsActiveValidator(true)

Expand Down Expand Up @@ -518,6 +528,11 @@ func TestConsensusRuntime_FSM_EndOfEpoch_BuildCommitEpoch(t *testing.T) {
},
Key: validatorAccounts.GetValidator("A").Key(),
blockchain: blockchainMock,
consensusConfig: &consensus.Config{
Params: &chain.Params{
Forks: &chain.Forks{},
},
},
}

snapshot := NewProposerSnapshot(1, nil)
Expand Down
Loading