add support for redelegation and bring back 7 epoch locking time (#3296)

* add support for redelegation

* Add test

* Revert "[go.mod] version upgrade of libp2p modules"

This reverts commit ce53468e3e.

* Fix bug that validator self delegation doesn't work

* Restore 7 epoch locking time at redelegation epoch

* Add debug log for chain context
pull/3303/head
Rongjian Lan 4 years ago committed by GitHub
parent 7a6fd23ff8
commit f2fa88a188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 119
      core/staking_verifier.go
  2. 242
      core/staking_verifier_test.go
  3. 15
      core/state_transition.go
  4. 17
      core/tx_pool.go
  5. 4
      internal/chain/engine.go
  6. 137
      internal/params/config.go

@ -200,8 +200,8 @@ var (
// //
// Note that this function never updates the stateDB, it only reads from stateDB. // Note that this function never updates the stateDB, it only reads from stateDB.
func VerifyAndDelegateFromMsg( func VerifyAndDelegateFromMsg(
stateDB vm.StateDB, msg *staking.Delegate, stateDB vm.StateDB, epoch *big.Int, msg *staking.Delegate, delegations []staking.DelegationIndex, redelegation bool,
) (*staking.ValidatorWrapper, *big.Int, error) { ) ([]*staking.ValidatorWrapper, *big.Int, error) {
if stateDB == nil { if stateDB == nil {
return nil, nil, errStateDBIsMissing return nil, nil, errStateDBIsMissing
} }
@ -214,40 +214,111 @@ func VerifyAndDelegateFromMsg(
if msg.Amount.Cmp(minimumDelegation) < 0 { if msg.Amount.Cmp(minimumDelegation) < 0 {
return nil, nil, errDelegationTooSmall return nil, nil, errDelegationTooSmall
} }
wrapper, err := stateDB.ValidatorWrapperCopy(msg.ValidatorAddress)
if err != nil { updatedValidatorWrappers := []*staking.ValidatorWrapper{}
return nil, nil, err delegateBalance := big.NewInt(0).Set(msg.Amount)
var delegateeWrapper *staking.ValidatorWrapper
if redelegation {
// Check if we can use tokens in undelegation to delegate (redelegate)
for i := range delegations {
delegationIndex := &delegations[i]
wrapper, err := stateDB.ValidatorWrapperCopy(delegationIndex.ValidatorAddress)
if err != nil {
return nil, nil, err
}
if uint64(len(wrapper.Delegations)) <= delegationIndex.Index {
utils.Logger().Warn().
Str("validator", delegationIndex.ValidatorAddress.String()).
Uint64("delegation index", delegationIndex.Index).
Int("delegations length", len(wrapper.Delegations)).
Msg("Delegation index out of bound")
return nil, nil, errors.New("Delegation index out of bound")
}
delegation := &wrapper.Delegations[delegationIndex.Index]
startBalance := big.NewInt(0).Set(delegateBalance)
// Start from the oldest undelegated tokens
curIndex := 0
for ; curIndex < len(delegation.Undelegations); curIndex++ {
if delegation.Undelegations[curIndex].Epoch.Cmp(epoch) >= 0 {
break
}
if delegation.Undelegations[curIndex].Amount.Cmp(delegateBalance) <= 0 {
delegateBalance.Sub(delegateBalance, delegation.Undelegations[curIndex].Amount)
} else {
delegation.Undelegations[curIndex].Amount.Sub(
delegation.Undelegations[curIndex].Amount, delegateBalance,
)
delegateBalance = big.NewInt(0)
break
}
}
if startBalance.Cmp(delegateBalance) > 0 {
// Used undelegated token for redelegation
delegation.Undelegations = delegation.Undelegations[curIndex:]
if err := wrapper.SanityCheck(); err != nil {
return nil, nil, err
}
if bytes.Equal(delegationIndex.ValidatorAddress[:], msg.ValidatorAddress[:]) {
delegateeWrapper = wrapper
}
updatedValidatorWrappers = append(updatedValidatorWrappers, wrapper)
}
}
} }
// Check if there is enough liquid token to delegate if delegateeWrapper == nil {
if !CanTransfer(stateDB, msg.DelegatorAddress, msg.Amount) { var err error
return nil, nil, errors.Wrapf( delegateeWrapper, err = stateDB.ValidatorWrapperCopy(msg.ValidatorAddress)
errInsufficientBalanceForStake, "had %v, tried to stake %v", if err != nil {
stateDB.GetBalance(msg.DelegatorAddress), msg.Amount) return nil, nil, err
}
updatedValidatorWrappers = append(updatedValidatorWrappers, delegateeWrapper)
} }
// Check for existing delegation // Add to existing delegation if any
for i := range wrapper.Delegations { found := false
delegation := &wrapper.Delegations[i] for i := range delegateeWrapper.Delegations {
delegation := &delegateeWrapper.Delegations[i]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), msg.DelegatorAddress.Bytes()) { if bytes.Equal(delegation.DelegatorAddress.Bytes(), msg.DelegatorAddress.Bytes()) {
delegation.Amount.Add(delegation.Amount, msg.Amount) delegation.Amount.Add(delegation.Amount, msg.Amount)
if err := wrapper.SanityCheck(); err != nil { if err := delegateeWrapper.SanityCheck(); err != nil {
return nil, nil, err return nil, nil, err
} }
return wrapper, msg.Amount, nil found = true
} }
} }
// Add new delegation if !found {
wrapper.Delegations = append( // Add new delegation
wrapper.Delegations, staking.NewDelegation( delegateeWrapper.Delegations = append(
msg.DelegatorAddress, msg.Amount, delegateeWrapper.Delegations, staking.NewDelegation(
), msg.DelegatorAddress, msg.Amount,
) ),
if err := wrapper.SanityCheck(); err != nil { )
return nil, nil, err if err := delegateeWrapper.SanityCheck(); err != nil {
return nil, nil, err
}
}
if delegateBalance.Cmp(big.NewInt(0)) == 0 {
// delegation fully from undelegated tokens, no need to deduct from balance.
return updatedValidatorWrappers, big.NewInt(0), nil
} }
return wrapper, msg.Amount, nil
// Still need to deduct tokens from balance for delegation
// Check if there is enough liquid token to delegate
if !CanTransfer(stateDB, msg.DelegatorAddress, delegateBalance) {
return nil, nil, errors.Wrapf(
errInsufficientBalanceForStake, "totalRedelegatable: %v, balance: %v; trying to stake %v",
big.NewInt(0).Sub(msg.Amount, delegateBalance), stateDB.GetBalance(msg.DelegatorAddress), msg.Amount)
}
return updatedValidatorWrappers, delegateBalance, nil
} }
// VerifyAndUndelegateFromMsg verifies the undelegate validator message // VerifyAndUndelegateFromMsg verifies the undelegate validator message

@ -721,44 +721,59 @@ func defaultExpWrapperEditValidator() staking.ValidatorWrapper {
func TestVerifyAndDelegateFromMsg(t *testing.T) { func TestVerifyAndDelegateFromMsg(t *testing.T) {
tests := []struct { tests := []struct {
sdb vm.StateDB sdb vm.StateDB
msg staking.Delegate msg staking.Delegate
ds []staking.DelegationIndex
expVWrapper staking.ValidatorWrapper epoch *big.Int
expAmt *big.Int redelegate bool
expErr error
expVWrappers []staking.ValidatorWrapper
expAmt *big.Int
expErr error
}{ }{
{ {
// 0: new delegate // 0: new delegate
sdb: makeStateDBForStake(t), sdb: makeStateDBForStake(t),
msg: defaultMsgDelegate(), msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
expVWrapper: defaultExpVWrapperDelegate(), epoch: big.NewInt(0),
expAmt: tenKOnes, redelegate: false,
expVWrappers: []staking.ValidatorWrapper{defaultExpVWrapperDelegate()},
expAmt: tenKOnes,
}, },
{ {
// 1: add amount to current delegate // 1: add amount to current delegate
sdb: makeStateDBForStake(t), sdb: makeStateDBForStake(t),
msg: defaultMsgSelfDelegate(), msg: defaultMsgSelfDelegate(),
ds: makeMsgCollectRewards(),
expVWrapper: defaultExpVWrapperSelfDelegate(), epoch: big.NewInt(0),
expAmt: tenKOnes, redelegate: false,
expVWrappers: []staking.ValidatorWrapper{defaultExpVWrapperSelfDelegate()},
expAmt: tenKOnes,
}, },
{ {
// 2: nil state db // 2: nil state db
sdb: nil, sdb: nil,
msg: defaultMsgDelegate(), msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errStateDBIsMissing, expErr: errStateDBIsMissing,
}, },
{ {
// 3: validatorFlag not set // 3: validatorFlag not set
sdb: makeStateDBForStake(t), sdb: makeStateDBForStake(t),
ds: makeMsgCollectRewards(),
msg: func() staking.Delegate { msg: func() staking.Delegate {
msg := defaultMsgDelegate() msg := defaultMsgDelegate()
msg.ValidatorAddress = makeTestAddr("not in state") msg.ValidatorAddress = makeTestAddr("not in state")
return msg return msg
}(), }(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errValidatorNotExist, expErr: errValidatorNotExist,
}, },
@ -770,6 +785,9 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
msg.Amount = big.NewInt(-1) msg.Amount = big.NewInt(-1)
return msg return msg
}(), }(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errNegativeAmount, expErr: errNegativeAmount,
}, },
@ -781,6 +799,9 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
msg.Amount = big.NewInt(100) msg.Amount = big.NewInt(100)
return msg return msg
}(), }(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errDelegationTooSmall, expErr: errDelegationTooSmall,
}, },
@ -796,6 +817,9 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
d.ValidatorAddress = createValidatorAddr d.ValidatorAddress = createValidatorAddr
return d return d
}(), }(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errors.New("address not present in state"), expErr: errors.New("address not present in state"),
}, },
@ -806,7 +830,10 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
sdb.SetBalance(delegatorAddr, big.NewInt(100)) sdb.SetBalance(delegatorAddr, big.NewInt(100))
return sdb return sdb
}(), }(),
msg: defaultMsgDelegate(), msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errInsufficientBalanceForStake, expErr: errInsufficientBalanceForStake,
}, },
@ -818,6 +845,9 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
d.Amount = hundredKOnes d.Amount = hundredKOnes
return d return d
}(), }(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errors.New(" total delegation can not be bigger than max_total_delegation"), expErr: errors.New(" total delegation can not be bigger than max_total_delegation"),
}, },
@ -829,12 +859,137 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
d.Amount = hundredKOnes d.Amount = hundredKOnes
return d return d
}(), }(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(0),
redelegate: false,
expErr: errors.New(" total delegation can not be bigger than max_total_delegation"), expErr: errors.New(" total delegation can not be bigger than max_total_delegation"),
}, },
{
// 10: full redelegate without using balance
sdb: makeStateForRedelegate(t),
msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(7),
redelegate: true,
expVWrappers: defaultExpVWrappersRedelegate(),
expAmt: big.NewInt(0),
},
{
// 11: redelegate with undelegation epoch too recent, have to use some balance
sdb: makeStateForRedelegate(t),
msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(6),
redelegate: true,
expVWrappers: func() []staking.ValidatorWrapper {
wrappers := defaultExpVWrappersRedelegate()
wrappers[1].Delegations[1].Undelegations = append(
wrappers[1].Delegations[1].Undelegations,
staking.Undelegation{Amount: fiveKOnes, Epoch: big.NewInt(defaultNextEpoch)})
return wrappers
}(),
expAmt: fiveKOnes,
},
{
// 12: redelegate with not enough undelegated token, have to use some balance
sdb: makeStateForRedelegate(t),
msg: func() staking.Delegate {
del := defaultMsgDelegate()
del.Amount = twentyKOnes
return del
}(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(7),
redelegate: true,
expVWrappers: func() []staking.ValidatorWrapper {
wrappers := defaultExpVWrappersRedelegate()
wrappers[0].Delegations[1].Amount = big.NewInt(0).Add(twentyKOnes, twentyKOnes)
return wrappers
}(),
expAmt: tenKOnes,
},
{
// 13: no redelegation and full balance used
sdb: makeStateForRedelegate(t),
msg: defaultMsgDelegate(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(5),
redelegate: true,
expVWrappers: func() []staking.ValidatorWrapper {
wrappers := defaultExpVWrappersRedelegate()
wrappers[0].Delegations[1].Undelegations = append(
wrappers[0].Delegations[1].Undelegations,
staking.Undelegation{Amount: fiveKOnes, Epoch: big.NewInt(defaultEpoch)})
wrappers[1].Delegations[1].Undelegations = append(
wrappers[1].Delegations[1].Undelegations,
staking.Undelegation{Amount: fiveKOnes, Epoch: big.NewInt(defaultNextEpoch)})
return wrappers
}(),
expAmt: tenKOnes,
},
{
// 14: redelegate error delegation index out of bound
sdb: makeStateForRedelegate(t),
msg: defaultMsgSelfDelegate(),
ds: func() []staking.DelegationIndex {
dis := makeMsgCollectRewards()
dis[1].Index = 2
return dis
}(),
epoch: big.NewInt(0),
redelegate: true,
expErr: errors.New("index out of bound"),
},
{
// 15: redelegate error delegation index out of bound
sdb: makeStateForRedelegate(t),
msg: defaultMsgSelfDelegate(),
ds: func() []staking.DelegationIndex {
dis := makeMsgCollectRewards()
dis[1].Index = 2
return dis
}(),
epoch: big.NewInt(0),
redelegate: true,
expErr: errors.New("index out of bound"),
},
{
// 16: no redelegation and full balance used (validator delegate to self)
sdb: makeStateForRedelegate(t),
msg: func() staking.Delegate {
del := defaultMsgDelegate()
del.DelegatorAddress = del.ValidatorAddress
return del
}(),
ds: makeMsgCollectRewards(),
epoch: big.NewInt(5),
redelegate: true,
expVWrappers: func() []staking.ValidatorWrapper {
wrappers := defaultExpVWrappersRedelegate()
wrappers[0].Delegations[1].Undelegations = append(
wrappers[0].Delegations[1].Undelegations,
staking.Undelegation{Amount: fiveKOnes, Epoch: big.NewInt(defaultEpoch)})
wrappers[1].Delegations[1].Undelegations = append(
wrappers[1].Delegations[1].Undelegations,
staking.Undelegation{Amount: fiveKOnes, Epoch: big.NewInt(defaultNextEpoch)})
wrappers[0].Delegations[1].Amount = twentyKOnes
wrappers[0].Delegations[0].Amount = big.NewInt(0).Add(twentyKOnes, tenKOnes)
return wrappers
}(),
expAmt: tenKOnes,
},
} }
for i, test := range tests { for i, test := range tests {
w, amt, err := VerifyAndDelegateFromMsg(test.sdb, &test.msg) ws, amt, err := VerifyAndDelegateFromMsg(test.sdb, test.epoch, &test.msg, test.ds, test.redelegate)
if assErr := assertError(err, test.expErr); assErr != nil { if assErr := assertError(err, test.expErr); assErr != nil {
t.Errorf("Test %v: %v", i, assErr) t.Errorf("Test %v: %v", i, assErr)
@ -846,12 +1001,55 @@ func TestVerifyAndDelegateFromMsg(t *testing.T) {
if amt.Cmp(test.expAmt) != 0 { if amt.Cmp(test.expAmt) != 0 {
t.Errorf("Test %v: unexpected amount %v / %v", i, amt, test.expAmt) t.Errorf("Test %v: unexpected amount %v / %v", i, amt, test.expAmt)
} }
if err := staketest.CheckValidatorWrapperEqual(*w, test.expVWrapper); err != nil {
t.Errorf("Test %v: %v", i, err) for j := range ws {
if err := staketest.CheckValidatorWrapperEqual(*ws[j], test.expVWrappers[j]); err != nil {
t.Errorf("Test %v: %v", i, err)
}
} }
} }
} }
func makeStateForRedelegate(t *testing.T) *state.DB {
sdb := makeStateDBForStake(t)
if err := addStateUndelegationForAddr(sdb, validatorAddr, big.NewInt(defaultEpoch)); err != nil {
t.Fatal(err)
}
if err := addStateUndelegationForAddr(sdb, validatorAddr2, big.NewInt(defaultNextEpoch)); err != nil {
t.Fatal(err)
}
sdb.IntermediateRoot(true)
return sdb
}
func addStateUndelegationForAddr(sdb *state.DB, addr common.Address, epoch *big.Int) error {
w, err := sdb.ValidatorWrapper(addr)
if err != nil {
return err
}
w.Delegations = append(w.Delegations,
staking.NewDelegation(delegatorAddr, new(big.Int).Set(twentyKOnes)),
)
w.Delegations[1].Undelegations = staking.Undelegations{staking.Undelegation{Amount: fiveKOnes, Epoch: epoch}}
return sdb.UpdateValidatorWrapper(addr, w)
}
func defaultExpVWrappersRedelegate() []staking.ValidatorWrapper {
w1 := makeVWrapperByIndex(validatorIndex)
w1.Delegations = append(w1.Delegations,
staking.NewDelegation(delegatorAddr, new(big.Int).Set(thirtyKOnes)),
)
w2 := makeVWrapperByIndex(validator2Index)
w2.Delegations = append(w2.Delegations,
staking.NewDelegation(delegatorAddr, new(big.Int).Set(twentyKOnes)),
)
return []staking.ValidatorWrapper{w1, w2}
}
func defaultMsgDelegate() staking.Delegate { func defaultMsgDelegate() staking.Delegate {
return staking.Delegate{ return staking.Delegate{
DelegatorAddress: delegatorAddr, DelegatorAddress: delegatorAddr,

@ -416,14 +416,25 @@ func (st *StateTransition) verifyAndApplyEditValidatorTx(
} }
func (st *StateTransition) verifyAndApplyDelegateTx(delegate *staking.Delegate) error { func (st *StateTransition) verifyAndApplyDelegateTx(delegate *staking.Delegate) error {
wrapper, balanceToBeDeducted, err := VerifyAndDelegateFromMsg(st.state, delegate) delegations, err := st.bc.ReadDelegationsByDelegator(delegate.DelegatorAddress)
if err != nil { if err != nil {
return err return err
} }
updatedValidatorWrappers, balanceToBeDeducted, err := VerifyAndDelegateFromMsg(
st.state, st.evm.EpochNumber, delegate, delegations, st.evm.ChainConfig().IsRedelegation(st.evm.EpochNumber))
if err != nil {
return err
}
for _, wrapper := range updatedValidatorWrappers {
if err := st.state.UpdateValidatorWrapper(wrapper.Address, wrapper); err != nil {
return err
}
}
st.state.SubBalance(delegate.DelegatorAddress, balanceToBeDeducted) st.state.SubBalance(delegate.DelegatorAddress, balanceToBeDeducted)
return st.state.UpdateValidatorWrapper(wrapper.Address, wrapper) return nil
} }
func (st *StateTransition) verifyAndApplyUndelegateTx( func (st *StateTransition) verifyAndApplyUndelegateTx(

@ -823,7 +823,21 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error {
return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32) return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32)
} }
_, _, err = VerifyAndDelegateFromMsg(pool.currentState, stkMsg) chain, ok := pool.chain.(ChainContext)
if !ok {
utils.Logger().Debug().Msg("Missing chain context in txPool")
return nil // for testing, chain could be testing blockchain
}
delegations, err := chain.ReadDelegationsByDelegator(stkMsg.DelegatorAddress)
if err != nil {
return err
}
pendingEpoch := pool.chain.CurrentBlock().Epoch()
if shard.Schedule.IsLastBlock(pool.chain.CurrentBlock().Number().Uint64()) {
pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1))
}
_, _, err = VerifyAndDelegateFromMsg(
pool.currentState, pendingEpoch, stkMsg, delegations, pool.chainconfig.IsRedelegation(pendingEpoch))
return err return err
case staking.DirectiveUndelegate: case staking.DirectiveUndelegate:
msg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate) msg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate)
@ -858,6 +872,7 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error {
} }
chain, ok := pool.chain.(ChainContext) chain, ok := pool.chain.(ChainContext)
if !ok { if !ok {
utils.Logger().Debug().Msg("Missing chain context in txPool")
return nil // for testing, chain could be testing blockchain return nil // for testing, chain could be testing blockchain
} }
delegations, err := chain.ReadDelegationsByDelegator(stkMsg.DelegatorAddress) delegations, err := chain.ReadDelegationsByDelegator(stkMsg.DelegatorAddress)

@ -293,7 +293,9 @@ func payoutUndelegations(
) )
} }
lockPeriod := staking.LockPeriodInEpoch lockPeriod := staking.LockPeriodInEpoch
if chain.Config().IsQuickUnlock(header.Epoch()) { if chain.Config().IsRedelegation(header.Epoch()) {
lockPeriod = staking.LockPeriodInEpoch
} else if chain.Config().IsQuickUnlock(header.Epoch()) {
lockPeriod = staking.LockPeriodInEpochV2 lockPeriod = staking.LockPeriodInEpochV2
} }
for i := range wrapper.Delegations { for i := range wrapper.Delegations {

@ -25,89 +25,95 @@ var EpochTBD = big.NewInt(10000000)
var ( var (
// MainnetChainConfig is the chain parameters to run a node on the main network. // MainnetChainConfig is the chain parameters to run a node on the main network.
MainnetChainConfig = &ChainConfig{ MainnetChainConfig = &ChainConfig{
ChainID: MainnetChainID, ChainID: MainnetChainID,
CrossTxEpoch: big.NewInt(28), CrossTxEpoch: big.NewInt(28),
CrossLinkEpoch: big.NewInt(186), CrossLinkEpoch: big.NewInt(186),
StakingEpoch: big.NewInt(186), StakingEpoch: big.NewInt(186),
PreStakingEpoch: big.NewInt(185), PreStakingEpoch: big.NewInt(185),
QuickUnlockEpoch: big.NewInt(191), QuickUnlockEpoch: big.NewInt(191),
FiveSecondsEpoch: big.NewInt(230), FiveSecondsEpoch: big.NewInt(230),
EIP155Epoch: big.NewInt(28), RedelegationEpoch: big.NewInt(10000000),
S3Epoch: big.NewInt(28), EIP155Epoch: big.NewInt(28),
ReceiptLogEpoch: big.NewInt(101), S3Epoch: big.NewInt(28),
ReceiptLogEpoch: big.NewInt(101),
} }
// TestnetChainConfig contains the chain parameters to run a node on the harmony test network. // TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
TestnetChainConfig = &ChainConfig{ TestnetChainConfig = &ChainConfig{
ChainID: TestnetChainID, ChainID: TestnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1), PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0), QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(16500), FiveSecondsEpoch: big.NewInt(16500),
EIP155Epoch: big.NewInt(0), RedelegationEpoch: big.NewInt(10000000),
S3Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), S3Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
} }
// PangaeaChainConfig contains the chain parameters for the Pangaea network. // PangaeaChainConfig contains the chain parameters for the Pangaea network.
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
PangaeaChainConfig = &ChainConfig{ PangaeaChainConfig = &ChainConfig{
ChainID: PangaeaChainID, ChainID: PangaeaChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1), PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0), QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0), FiveSecondsEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
S3Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), S3Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
} }
// PartnerChainConfig contains the chain parameters for the Partner network. // PartnerChainConfig contains the chain parameters for the Partner network.
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
PartnerChainConfig = &ChainConfig{ PartnerChainConfig = &ChainConfig{
ChainID: PartnerChainID, ChainID: PartnerChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1), PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0), QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0), FiveSecondsEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
S3Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), S3Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
} }
// StressnetChainConfig contains the chain parameters for the Stress test network. // StressnetChainConfig contains the chain parameters for the Stress test network.
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
StressnetChainConfig = &ChainConfig{ StressnetChainConfig = &ChainConfig{
ChainID: StressnetChainID, ChainID: StressnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1), PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0), QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0), FiveSecondsEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
S3Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), S3Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
} }
// LocalnetChainConfig contains the chain parameters to run for local development. // LocalnetChainConfig contains the chain parameters to run for local development.
LocalnetChainConfig = &ChainConfig{ LocalnetChainConfig = &ChainConfig{
ChainID: TestnetChainID, ChainID: TestnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(0), PreStakingEpoch: big.NewInt(0),
QuickUnlockEpoch: big.NewInt(0), QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0), FiveSecondsEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
S3Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), S3Epoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
} }
// AllProtocolChanges ... // AllProtocolChanges ...
@ -121,6 +127,7 @@ var (
big.NewInt(0), // PreStakingEpoch big.NewInt(0), // PreStakingEpoch
big.NewInt(0), // QuickUnlockEpoch big.NewInt(0), // QuickUnlockEpoch
big.NewInt(0), // FiveSecondsEpoch big.NewInt(0), // FiveSecondsEpoch
big.NewInt(0), // RedelegationEpoch
big.NewInt(0), // EIP155Epoch big.NewInt(0), // EIP155Epoch
big.NewInt(0), // S3Epoch big.NewInt(0), // S3Epoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
@ -137,6 +144,7 @@ var (
big.NewInt(0), // PreStakingEpoch big.NewInt(0), // PreStakingEpoch
big.NewInt(0), // QuickUnlockEpoch big.NewInt(0), // QuickUnlockEpoch
big.NewInt(0), // FiveSecondsEpoch big.NewInt(0), // FiveSecondsEpoch
big.NewInt(0), // RedelegationEpoch
big.NewInt(0), // EIP155Epoch big.NewInt(0), // EIP155Epoch
big.NewInt(0), // S3Epoch big.NewInt(0), // S3Epoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
@ -188,6 +196,10 @@ type ChainConfig struct {
// and block rewards adjusted to 17.5 ONE/block // and block rewards adjusted to 17.5 ONE/block
FiveSecondsEpoch *big.Int `json:"five-seconds-epoch,omitempty"` FiveSecondsEpoch *big.Int `json:"five-seconds-epoch,omitempty"`
// RedelegationEpoch is the epoch when redelegation is supported and undelegation locking time
// is restored to 7 epoch
RedelegationEpoch *big.Int `json:"redelegation-epoch,omitempty"`
// EIP155 hard fork epoch (include EIP158 too) // EIP155 hard fork epoch (include EIP158 too)
EIP155Epoch *big.Int `json:"eip155-epoch,omitempty"` EIP155Epoch *big.Int `json:"eip155-epoch,omitempty"`
@ -245,6 +257,11 @@ func (c *ChainConfig) IsFiveSeconds(epoch *big.Int) bool {
return isForked(c.FiveSecondsEpoch, epoch) return isForked(c.FiveSecondsEpoch, epoch)
} }
// IsRedelegation determines whether it is the epoch to support redelegation
func (c *ChainConfig) IsRedelegation(epoch *big.Int) bool {
return isForked(c.RedelegationEpoch, epoch)
}
// IsPreStaking determines whether staking transactions are allowed // IsPreStaking determines whether staking transactions are allowed
func (c *ChainConfig) IsPreStaking(epoch *big.Int) bool { func (c *ChainConfig) IsPreStaking(epoch *big.Int) bool {
return isForked(c.PreStakingEpoch, epoch) return isForked(c.PreStakingEpoch, epoch)

Loading…
Cancel
Save