Skip to content

Commit

Permalink
[PVM] Returns pre-Athens rewardsImportTx logic for pre-Athens txs
Browse files Browse the repository at this point in the history
  • Loading branch information
evlekht authored and evlekht committed May 27, 2024
1 parent 0c5035f commit f2dacf2
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
with:
go-version: 1.19
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v5
with:
version: v1.51
- name: Run static analysis tests
Expand Down
42 changes: 30 additions & 12 deletions vms/platformvm/txs/executor/camino_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,8 @@ func (e *CaminoStandardTxExecutor) RewardsImportTx(tx *txs.RewardsImportTx) erro
return err
}

chainTime := e.State.GetTimestamp()

if e.Bootstrapped.Get() {
// Getting all treasury utxos exported from c-chain, collecting ones that are old enough

Expand All @@ -1265,7 +1267,7 @@ func (e *CaminoStandardTxExecutor) RewardsImportTx(tx *txs.RewardsImportTx) erro
return fmt.Errorf("error fetching atomic UTXOs: %w", err)
}

chainTimestamp := uint64(e.State.GetTimestamp().Unix())
chainTimestamp := uint64(chainTime.Unix())

utxos := []*avax.UTXO{}
for _, utxoBytes := range allUTXOBytes {
Expand Down Expand Up @@ -1327,18 +1329,34 @@ func (e *CaminoStandardTxExecutor) RewardsImportTx(tx *txs.RewardsImportTx) erro
continue
}

addValidatorTx, _, err := e.State.GetTx(staker.TxID)
if err != nil {
return err
}
unsignedAddValidatorTx, ok := addValidatorTx.Unsigned.(*txs.CaminoAddValidatorTx)
if !ok {
return errWrongTxType
}
txRewardOwner, ok := unsignedAddValidatorTx.RewardsOwner.(*secp256k1fx.OutputOwners)
if !ok {
return errWrongOwnerType
var txRewardOwner *secp256k1fx.OutputOwners
if e.Config.IsAthensPhaseActivated(chainTime) {
addValidatorTx, _, err := e.State.GetTx(staker.TxID)
if err != nil {
return err
}
unsignedAddValidatorTx, ok := addValidatorTx.Unsigned.(*txs.CaminoAddValidatorTx)
if !ok {
return errWrongTxType
}
txRewardOwner, ok = unsignedAddValidatorTx.RewardsOwner.(*secp256k1fx.OutputOwners)
if !ok {
return errWrongOwnerType
}
} else {
validatorAddr, err := e.State.GetShortIDLink(
ids.ShortID(staker.NodeID),
state.ShortLinkKeyRegisterNode,
)
if err != nil {
return err
}
txRewardOwner = &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{validatorAddr},
}
}

ownerID, err := txs.GetOwnerID(txRewardOwner)
if err != nil {
return err
Expand Down
172 changes: 152 additions & 20 deletions vms/platformvm/txs/executor/camino_tx_executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4603,7 +4603,9 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
LockModeBondDeposit: caminoGenesisConf.LockModeBondDeposit,
}

blockTime := time.Unix(1000, 0)
defaultCfg := defaultCaminoConfig(true)
athensBlockTime := defaultCfg.AthensPhaseTime
beforeAthensBlockTime := defaultCfg.AthensPhaseTime.Add(-1 * time.Second)

shmWithUTXOs := func(t *testing.T, c *gomock.Controller, utxos []*avax.TimedUTXO) *atomic.MockSharedMemory {
shm := atomic.NewMockSharedMemory(c)
Expand All @@ -4629,7 +4631,7 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
}

tests := map[string]struct {
state func(*gomock.Controller, *txs.RewardsImportTx, ids.ID) *state.MockDiff
state func(*gomock.Controller, *txs.RewardsImportTx, ids.ID, *config.Config) *state.MockDiff
sharedMemory func(*testing.T, *gomock.Controller, []*avax.TimedUTXO) *atomic.MockSharedMemory
utx func([]*avax.TimedUTXO) *txs.RewardsImportTx
signers [][]*secp256k1.PrivateKey
Expand All @@ -4639,10 +4641,10 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
expectedErr error
}{
"Imported inputs don't contain all reward utxos that are ready to be imported": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID) *state.MockDiff {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID, cfg *config.Config) *state.MockDiff {
s := state.NewMockDiff(c)
s.EXPECT().CaminoConfig().Return(caminoStateConf, nil)
s.EXPECT().GetTimestamp().Return(blockTime)
s.EXPECT().GetTimestamp().Return(athensBlockTime)
return s
},
sharedMemory: shmWithUTXOs,
Expand All @@ -4659,24 +4661,24 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
utxos: []*avax.TimedUTXO{
{
UTXO: *generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{
UTXO: *generateTestUTXO(ids.ID{2}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{
UTXO: *generateTestUTXO(ids.ID{3}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
},
expectedErr: errInputsUTXOSMismatch,
},
"Imported input doesn't match reward utxo": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID) *state.MockDiff {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID, cfg *config.Config) *state.MockDiff {
s := state.NewMockDiff(c)
s.EXPECT().CaminoConfig().Return(caminoStateConf, nil)
s.EXPECT().GetTimestamp().Return(blockTime)
s.EXPECT().GetTimestamp().Return(athensBlockTime)
return s
},
sharedMemory: shmWithUTXOs,
Expand All @@ -4691,15 +4693,15 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
},
utxos: []*avax.TimedUTXO{{
UTXO: *generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
}},
expectedErr: errImportedUTXOMismatch,
},
"Input & utxo amount mismatch": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID) *state.MockDiff {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID, cfg *config.Config) *state.MockDiff {
s := state.NewMockDiff(c)
s.EXPECT().CaminoConfig().Return(caminoStateConf, nil)
s.EXPECT().GetTimestamp().Return(blockTime)
s.EXPECT().GetTimestamp().Return(athensBlockTime)
return s
},
sharedMemory: shmWithUTXOs,
Expand All @@ -4719,15 +4721,15 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
},
utxos: []*avax.TimedUTXO{{
UTXO: *generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
}},
expectedErr: errInputAmountMismatch,
},
"OK": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID) *state.MockDiff {
"OK: after AthensPhase": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID, cfg *config.Config) *state.MockDiff {
s := state.NewMockDiff(c)
s.EXPECT().CaminoConfig().Return(caminoStateConf, nil)
s.EXPECT().GetTimestamp().Return(blockTime)
s.EXPECT().GetTimestamp().Return(athensBlockTime)

rewardOwner1 := &secp256k1fx.OutputOwners{
Threshold: 1,
Expand Down Expand Up @@ -4823,18 +4825,148 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
utxos: []*avax.TimedUTXO{
{ // timed utxo, old enough
UTXO: *generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, 3, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{ // not timed utxo
UTXO: *generateTestUTXO(ids.ID{2}, ctx.AVAXAssetID, 5, *treasury.Owner, ids.Empty, ids.Empty),
},
{ // timed utxo, old enough
UTXO: *generateTestUTXO(ids.ID{3}, ctx.AVAXAssetID, 2, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{ // timed utxo, not old enough
UTXO: *generateTestUTXO(ids.ID{4}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(athensBlockTime.Unix()) - atomic.SharedMemorySyncBound + 1,
},
},
expectedAtomicInputs: func(utxos []*avax.TimedUTXO) set.Set[ids.ID] {
return set.Set[ids.ID]{
utxos[0].InputID(): struct{}{},
utxos[2].InputID(): struct{}{},
}
},
expectedAtomicRequests: func(utxos []*avax.TimedUTXO) map[ids.ID]*atomic.Requests {
utxoID0 := utxos[0].InputID()
utxoID2 := utxos[2].InputID()
return map[ids.ID]*atomic.Requests{ctx.CChainID: {
RemoveRequests: [][]byte{utxoID0[:], utxoID2[:]},
}}
},
},
"OK: before AthensPhase": {
state: func(c *gomock.Controller, utx *txs.RewardsImportTx, txID ids.ID, cfg *config.Config) *state.MockDiff {
s := state.NewMockDiff(c)
s.EXPECT().CaminoConfig().Return(caminoStateConf, nil)
s.EXPECT().GetTimestamp().Return(beforeAthensBlockTime)

nodeID1 := ids.ShortID{0, 0, 1}
nodeID2 := ids.ShortID{0, 0, 2}
nodeID3 := ids.ShortID{0, 0, 3}
nodeID4 := ids.ShortID{0, 0, 4}
nodeID5 := ids.ShortID{0, 0, 5}

cMemberAddr1 := ids.ShortID{0, 0, 11}
cMemberAddr2 := ids.ShortID{0, 0, 12}
cMemberAddr4 := ids.ShortID{0, 0, 14}

rewardOwner1 := &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{cMemberAddr1},
}
rewardOwner2 := &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{cMemberAddr2},
}
rewardOwner4 := &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{cMemberAddr4},
}

staker1 := &state.Staker{TxID: ids.ID{0, 1}, NodeID: ids.NodeID(nodeID1), SubnetID: constants.PrimaryNetworkID}
staker2 := &state.Staker{TxID: ids.ID{0, 2}, NodeID: ids.NodeID(nodeID2), SubnetID: constants.PrimaryNetworkID}
staker3 := &state.Staker{TxID: ids.ID{0, 3}, NodeID: ids.NodeID(nodeID3), SubnetID: ids.ID{0, 0, 1}}
staker4 := &state.Staker{TxID: ids.ID{0, 4}, NodeID: ids.NodeID(nodeID4), SubnetID: constants.PrimaryNetworkID}
staker5 := &state.Staker{TxID: ids.ID{0, 5}, NodeID: ids.NodeID(nodeID5), SubnetID: constants.PrimaryNetworkID}

currentStakerIterator := state.NewMockStakerIterator(c)
currentStakerIterator.EXPECT().Next().Return(true).Times(5)
currentStakerIterator.EXPECT().Value().Return(staker1)
currentStakerIterator.EXPECT().Value().Return(staker2)
currentStakerIterator.EXPECT().Value().Return(staker3)
currentStakerIterator.EXPECT().Value().Return(staker4)
currentStakerIterator.EXPECT().Value().Return(staker5)
currentStakerIterator.EXPECT().Next().Return(false)
currentStakerIterator.EXPECT().Release()

s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIterator, nil)
s.EXPECT().GetShortIDLink(nodeID1, state.ShortLinkKeyRegisterNode).Return(cMemberAddr1, nil)
s.EXPECT().GetShortIDLink(nodeID2, state.ShortLinkKeyRegisterNode).Return(cMemberAddr2, nil)
s.EXPECT().GetShortIDLink(nodeID4, state.ShortLinkKeyRegisterNode).Return(cMemberAddr4, nil)
s.EXPECT().GetShortIDLink(nodeID5, state.ShortLinkKeyRegisterNode).Return(cMemberAddr1, nil)
s.EXPECT().GetNotDistributedValidatorReward().Return(uint64(1), nil) // old
s.EXPECT().SetNotDistributedValidatorReward(uint64(2)) // new
rewardOwnerID1, err := txs.GetOwnerID(rewardOwner1)
require.NoError(t, err)
rewardOwnerID2, err := txs.GetOwnerID(rewardOwner2)
require.NoError(t, err)
rewardOwnerID4, err := txs.GetOwnerID(rewardOwner4)
require.NoError(t, err)

s.EXPECT().GetClaimable(rewardOwnerID1).Return(&state.Claimable{
Owner: rewardOwner1,
ValidatorReward: 10,
ExpiredDepositReward: 100,
}, nil)
s.EXPECT().GetClaimable(rewardOwnerID2).Return(&state.Claimable{
Owner: rewardOwner2,
ValidatorReward: 20,
ExpiredDepositReward: 200,
}, nil)
s.EXPECT().GetClaimable(rewardOwnerID4).Return(nil, database.ErrNotFound)

s.EXPECT().SetClaimable(rewardOwnerID1, &state.Claimable{
Owner: rewardOwner1,
ValidatorReward: 12,
ExpiredDepositReward: 100,
})
s.EXPECT().SetClaimable(rewardOwnerID2, &state.Claimable{
Owner: rewardOwner2,
ValidatorReward: 21,
ExpiredDepositReward: 200,
})
s.EXPECT().SetClaimable(rewardOwnerID4, &state.Claimable{
Owner: rewardOwner4,
ValidatorReward: 1,
})

return s
},
sharedMemory: shmWithUTXOs,
utx: func(utxos []*avax.TimedUTXO) *txs.RewardsImportTx {
return &txs.RewardsImportTx{BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
NetworkID: ctx.NetworkID,
BlockchainID: ctx.ChainID,
Ins: []*avax.TransferableInput{
generateTestInFromUTXO(&utxos[0].UTXO, []uint32{0}),
generateTestInFromUTXO(&utxos[2].UTXO, []uint32{0}),
},
}}}
},
utxos: []*avax.TimedUTXO{
{ // timed utxo, old enough
UTXO: *generateTestUTXO(ids.ID{1}, ctx.AVAXAssetID, 3, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(beforeAthensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{ // not timed utxo
UTXO: *generateTestUTXO(ids.ID{2}, ctx.AVAXAssetID, 5, *treasury.Owner, ids.Empty, ids.Empty),
},
{ // timed utxo, old enough
UTXO: *generateTestUTXO(ids.ID{3}, ctx.AVAXAssetID, 2, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound,
Timestamp: uint64(beforeAthensBlockTime.Unix()) - atomic.SharedMemorySyncBound,
},
{ // timed utxo, not old enough
UTXO: *generateTestUTXO(ids.ID{4}, ctx.AVAXAssetID, 1, *treasury.Owner, ids.Empty, ids.Empty),
Timestamp: uint64(blockTime.Unix()) - atomic.SharedMemorySyncBound + 1,
Timestamp: uint64(beforeAthensBlockTime.Unix()) - atomic.SharedMemorySyncBound + 1,
},
},
expectedAtomicInputs: func(utxos []*avax.TimedUTXO) set.Set[ids.ID] {
Expand Down Expand Up @@ -4871,7 +5003,7 @@ func TestCaminoStandardTxExecutorRewardsImportTx(t *testing.T) {
e := &CaminoStandardTxExecutor{
StandardTxExecutor{
Backend: &env.backend,
State: tt.state(ctrl, utx, tx.ID()),
State: tt.state(ctrl, utx, tx.ID(), env.config),
Tx: tx,
},
}
Expand Down

0 comments on commit f2dacf2

Please sign in to comment.