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
45 changes: 42 additions & 3 deletions blockchain/system/permissionless.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ import (
// DefaultEpochBlockInterval is the default number of blocks per epoch for ABv2.
const DefaultEpochBlockInterval = int64(params.DefaultVRankEpoch)

// initialLockup mirrors CnStakingV4Factory.INITIAL_LOCKUP, which the factory
// stakes as dead shares when deploying with public delegation.
const initialLockup = int64(1e9)

// AllocPermissionlessConfig holds parameters for genesis permissionless allocation.
type AllocPermissionlessConfig struct {
Owner common.Address // Owner of beacons and Registry registrant.
Expand All @@ -52,6 +56,7 @@ type AllocPermissionlessConfig struct {
StakeAmts []*big.Int // Stake amounts per validator.
DataConfig abv2data.IABv2DataContractInitData // ABv2DataContract constructor data.
EpochBlockInterval int64 // Blocks per epoch baked into ABv2 bytecode. 0 uses DefaultEpochBlockInterval.
UsePD bool // Deploy every validator's CnStaking with a PublicDelegation.
}

// allocPermissionlessResult holds intermediate deployed addresses passed between internal steps.
Expand Down Expand Up @@ -103,8 +108,12 @@ func allocPermissionless(config *AllocPermissionlessConfig, installABv2 bool) (m
if common.EmptyAddress(deployer) {
deployer = config.Owner
}
lockup := new(big.Int)
if config.UsePD {
lockup = big.NewInt(initialLockup)
}
for i, amt := range config.StakeAmts {
statedb.AddBalance(config.NodeInfos[i].Manager, amt)
statedb.AddBalance(config.NodeInfos[i].Manager, new(big.Int).Add(amt, lockup))
}

cfg := &runtime.Config{
Expand Down Expand Up @@ -274,10 +283,40 @@ func deployCnStakingFactory(cfg *runtime.Config, owner common.Address, activatio
func deployCnStakingPerValidator(cfg *runtime.Config, config *AllocPermissionlessConfig, result *allocPermissionlessResult) error {
factoryABI, _ := cnstakingv4factory.CnStakingV4FactoryMetaData.GetAbi()
cnStakingABI, _ := cnstakingv4.CnStakingV4MetaData.GetAbi()
pdABI, _ := pdcontract.PublicDelegationMetaData.GetAbi()

for i := range config.NodeIds {
cfg.Origin = config.NodeInfos[i].Manager
retData, err := evmCallABIReturn(cfg, result.factory, factoryABI, "deployCnStaking", config.NodeInfos[i].Manager)
manager := config.NodeInfos[i].Manager
cfg.Origin = manager

if config.UsePD {
cfg.Value = big.NewInt(initialLockup)
retData, err := evmCallABIReturn(cfg, result.factory, factoryABI, "deployCnStakingWithPD", manager,
cnstakingv4factory.IPublicDelegationPDConstructorArgs{
Owner: manager,
CommissionTo: manager,
CommissionRate: new(big.Int),
GcName: fmt.Sprintf("GC%d", i+1),
})
if err != nil {
return fmt.Errorf("deployCnStakingWithPD[%d]: %w", i, err)
}
proxyAddr := common.BytesToAddress(retData[12:32])
pdAddr := common.BytesToAddress(retData[44:64])

cfg.Value = config.StakeAmts[i]
if err := evmCallABI(cfg, pdAddr, pdABI, "stake"); err != nil {
return fmt.Errorf("stake[%d]: %w", i, err)
}
cfg.Value = new(big.Int)

config.NodeInfos[i].StakingContract = proxyAddr
// ABv2 requires a PD-backed node to pay its rewards to that PD.
config.NodeInfos[i].RewardAddress = pdAddr
continue
}

retData, err := evmCallABIReturn(cfg, result.factory, factoryABI, "deployCnStaking", manager)
if err != nil {
return fmt.Errorf("deployCnStaking[%d]: %w", i, err)
}
Expand Down
29 changes: 29 additions & 0 deletions blockchain/system/permissionless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/kaiachain/kaia/common"
abv2data "github.com/kaiachain/kaia/contracts/bindings/abv2data"
addressbookv2contract "github.com/kaiachain/kaia/contracts/bindings/addressbookv2"
cnstakingv4 "github.com/kaiachain/kaia/contracts/bindings/cnstakingv4"
"github.com/kaiachain/kaia/crypto"
"github.com/kaiachain/kaia/crypto/bls"
"github.com/kaiachain/kaia/kaiax/valset"
Expand Down Expand Up @@ -213,6 +214,34 @@ func TestAllocPermissionless_SingleValidator(t *testing.T) {
verifyPermissionlessAlloc(t, config, alloc)
}

func TestAllocPermissionless_UsePD(t *testing.T) {
log.EnableLogForTest(log.LvlCrit, log.LvlWarn)
config := makeTestPermissionlessConfig(4)
config.UsePD = true

alloc, err := AllocPermissionless(config)
require.NoError(t, err)

// One PD proxy per validator on top of what the non-PD alloc holds.
assert.Len(t, alloc, 10+2*len(config.NodeIds))

backend := backends.NewSimulatedBackend(blockchain.GenesisAlloc(alloc))
for i, info := range config.NodeInfos {
caller, err := cnstakingv4.NewCnStakingV4Caller(info.StakingContract, backend)
require.NoError(t, err)
pd, err := caller.PublicDelegation(nil)
require.NoError(t, err)

assert.NotEqual(t, common.Address{}, pd, "node[%d] must be deployed with a PD", i)
assert.Equal(t, pd, info.RewardAddress, "node[%d] must pay its rewards to its own PD", i)
require.Contains(t, alloc, pd)
assert.NotEmpty(t, alloc[pd].Code)

want := new(big.Int).Add(config.StakeAmts[i], big.NewInt(initialLockup))
assert.Equal(t, want, alloc[info.StakingContract].Balance, "node[%d] stake plus the factory lockup", i)
}
}

func TestAllocPermissionlessPrerequisites(t *testing.T) {
log.EnableLogForTest(log.LvlCrit, log.LvlWarn)
config := makeTestPermissionlessConfig(4)
Expand Down
2 changes: 2 additions & 0 deletions cmd/homi/setup/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ var HomiFlags = []cli.Flag{
altsrc.NewInt64Flag(osakaCompatibleBlockNumberFlag),
altsrc.NewInt64Flag(permissionlessCompatibleBlockNumberFlag),
altsrc.NewUint64Flag(vrankEpochFlag),
altsrc.NewBoolFlag(publicDelegationFlag),
altsrc.NewInt64Flag(pfsThresholdFlag),
altsrc.NewInt64Flag(cfsThresholdFlag),
altsrc.NewStringFlag(permissionlessGenesisDeployerFlag),
Expand Down Expand Up @@ -624,6 +625,7 @@ func makePermissionlessConfig(ctx *cli.Context, validatorAddrs []common.Address,
NodeInfos: nodeInfos,
StakeAmts: stakeAmts,
EpochBlockInterval: int64(ctx.Uint64(vrankEpochFlag.Name)),
UsePD: ctx.Bool(publicDelegationFlag.Name),
DataConfig: abv2data.IABv2DataContractInitData{
InitialOwner: owner,
InitialSuspender: owner,
Expand Down
6 changes: 6 additions & 0 deletions cmd/homi/setup/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,12 @@ var (
Aliases: []string{"genesis.vrank-epoch"},
}

publicDelegationFlag = &cli.BoolFlag{
Name: "public-delegation",
Usage: "deploy each validator's CnStaking with a PublicDelegation in permissionless mode",
Aliases: []string{"genesis.public-delegation"},
}

pfsThresholdFlag = &cli.Int64Flag{
Name: "pfs-threshold",
Usage: "PFS threshold for severe violation in permissionless mode (default: 2)",
Expand Down
Loading