Skip to content
Draft
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
19 changes: 19 additions & 0 deletions vms/components/avax/utxo_fetching.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ func GetAllUTXOs(db UTXOReader, addrs set.Set[ids.ShortID]) ([]*UTXO, error) {
return utxos, err
}

//func GetNextOutputIndex(utxos UTXOGetter, txID ids.ID) (uint32, error) {
// for i := uint32(0); i < math.MaxUint32; i++ {
// utxoID := UTXOID{
// TxID: txID,
// OutputIndex: i,
// }
//
// _, err := utxos.GetUTXO(utxoID.InputID())
// switch {
// case errors.Is(err, database.ErrNotFound):
// return i, nil
// case err != nil:
// return 0, err
// }
// }
//
// panic("output index out of range")
//}

// GetPaginatedUTXOs returns UTXOs such that at least one of the addresses in
// [addrs] is referenced.
//
Expand Down
56 changes: 56 additions & 0 deletions vms/components/avax/utxo_fetching_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,59 @@
require.NoError(err)
require.Len(notPaginatedUTXOs, len(totalUTXOs))
}

func TestGetNextOutputIndex(t *testing.T) {
require := require.New(t)

c := linearcodec.NewDefault()
manager := codec.NewDefaultManager()

require.NoError(c.RegisterType(&secp256k1fx.TransferOutput{}))
require.NoError(manager.RegisterCodec(codecVersion, c))

db := memdb.New()
s, err := NewUTXOState(db, manager, trackChecksum)
require.NoError(err)

txID := ids.GenerateTestID()

utxo := &UTXO{
Asset: Asset{ID: ids.GenerateTestID()},
Out: &secp256k1fx.TransferOutput{
Amt: 12345,
OutputOwners: secp256k1fx.OutputOwners{
Locktime: 54321,
Threshold: 1,
Addrs: []ids.ShortID{ids.GenerateTestShortID()},
},
},
}

utxo.UTXOID = UTXOID{
TxID: txID,
OutputIndex: 0,
}
require.NoError(s.PutUTXO(utxo))

utxo.UTXOID = UTXOID{
TxID: txID,
OutputIndex: 1,
}
require.NoError(s.PutUTXO(utxo))

utxo.UTXOID = UTXOID{
TxID: txID,
OutputIndex: 2,
}
require.NoError(s.PutUTXO(utxo))

nextOutputIndex, err := GetNextOutputIndex(s, txID)

Check failure on line 207 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

undefined: GetNextOutputIndex

Check failure on line 207 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

undefined: GetNextOutputIndex

Check failure on line 207 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

undefined: GetNextOutputIndex

Check failure on line 207 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

undefined: GetNextOutputIndex

Check failure on line 207 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

undefined: GetNextOutputIndex
require.NoError(err)
require.Equal(uint32(3), nextOutputIndex)

require.NoError(s.DeleteUTXO(utxo.InputID()))

nextOutputIndex, err = GetNextOutputIndex(s, txID)

Check failure on line 213 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

undefined: GetNextOutputIndex

Check failure on line 213 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

undefined: GetNextOutputIndex

Check failure on line 213 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

undefined: GetNextOutputIndex

Check failure on line 213 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

undefined: GetNextOutputIndex

Check failure on line 213 in vms/components/avax/utxo_fetching_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

undefined: GetNextOutputIndex
require.NoError(err)
require.Equal(uint32(2), nextOutputIndex)
}
27 changes: 25 additions & 2 deletions vms/platformvm/block/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,23 @@ func buildBlock(
return nil, fmt.Errorf("could not find next staker to reward: %w", err)
}
if shouldReward {
rewardValidatorTx, err := NewRewardValidatorTx(builder.txExecutorBackend.Ctx, stakerTxID)
var rewardValidatorTx *txs.Tx

stakerTx, _, err := parentState.GetTx(stakerTxID)
if err != nil {
return nil, fmt.Errorf("could not build tx to reward staker: %w", err)
return nil, err
}

if _, ok := stakerTx.Unsigned.(txs.ContinuousStaker); ok {
rewardValidatorTx, err = NewRewardContinuousValidatorTx(builder.txExecutorBackend.Ctx, stakerTxID, uint64(timestamp.Unix()))
if err != nil {
return nil, fmt.Errorf("could not build tx to reward staker: %w", err)
}
} else {
rewardValidatorTx, err = NewRewardValidatorTx(builder.txExecutorBackend.Ctx, stakerTxID)
if err != nil {
return nil, fmt.Errorf("could not build tx to reward staker: %w", err)
}
}

return block.NewBanffProposalBlock(
Expand Down Expand Up @@ -633,3 +647,12 @@ func NewRewardValidatorTx(ctx *snow.Context, txID ids.ID) (*txs.Tx, error) {
}
return tx, tx.SyntacticVerify(ctx)
}

func NewRewardContinuousValidatorTx(ctx *snow.Context, txID ids.ID, timestamp uint64) (*txs.Tx, error) {
utx := &txs.RewardContinuousValidatorTx{TxID: txID, Timestamp: timestamp}
tx, err := txs.NewSigned(utx, txs.Codec, nil)
if err != nil {
return nil, err
}
return tx, tx.SyntacticVerify(ctx)
}
21 changes: 21 additions & 0 deletions vms/platformvm/metrics/tx_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ func (m *txMetrics) RewardValidatorTx(*txs.RewardValidatorTx) error {
return nil
}

func (m *txMetrics) RewardContinuousValidatorTx(tx *txs.RewardContinuousValidatorTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "reward_continuous_validator",
}).Inc()
return nil
}

func (m *txMetrics) RemoveSubnetValidatorTx(*txs.RemoveSubnetValidatorTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "remove_subnet_validator",
Expand Down Expand Up @@ -173,3 +180,17 @@ func (m *txMetrics) DisableL1ValidatorTx(*txs.DisableL1ValidatorTx) error {
}).Inc()
return nil
}

func (m *txMetrics) AddContinuousValidatorTx(tx *txs.AddContinuousValidatorTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "add_continuous_validator",
}).Inc()
return nil
}

func (m *txMetrics) StopContinuousValidatorTx(tx *txs.StopContinuousValidatorTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "stop_continuous_validator",
}).Inc()
return nil
}
64 changes: 62 additions & 2 deletions vms/platformvm/state/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (
type Diff interface {
Chain

Apply(Chain) error
Apply(Chain) error // todo: test commit with the new stuff added
}

type diff struct {
Expand Down Expand Up @@ -78,6 +78,7 @@ func NewDiff(
if !ok {
return nil, fmt.Errorf("%w: %s", ErrMissingParentState, parentID)
}

return &diff{
parentID: parentID,
stateVersions: stateVersions,
Expand Down Expand Up @@ -267,7 +268,7 @@ func (d *diff) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker,
// validator.
newValidator, status := d.currentStakerDiffs.GetValidator(subnetID, nodeID)
switch status {
case added:
case added, modified:
return newValidator, nil
case deleted:
return nil, database.ErrNotFound
Expand Down Expand Up @@ -310,6 +311,59 @@ func (d *diff) PutCurrentValidator(staker *Staker) error {
return d.currentStakerDiffs.PutValidator(staker)
}

func (d *diff) UpdateCurrentValidator(staker *Staker) error {
parentState, ok := d.stateVersions.GetState(d.parentID)
if !ok {
return fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID)
}

return d.currentStakerDiffs.updateValidator(parentState, staker.SubnetID, staker.NodeID, func(validator Staker) (*Staker, error) {
return staker, nil
})
}

// todo: add test for this
func (d *diff) StopContinuousValidator(subnetID ids.ID, nodeID ids.NodeID) error {
parentState, ok := d.stateVersions.GetState(d.parentID)
if !ok {
return fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID)
}

return d.currentStakerDiffs.updateValidator(parentState, subnetID, nodeID, func(validator Staker) (*Staker, error) {
if validator.ContinuationPeriod == 0 {
return nil, errIncompatibleContinuousStaker
}

validator.ContinuationPeriod = 0

return &validator, nil
})
}

func (d *diff) ResetContinuousValidatorCycle(
subnetID ids.ID,
nodeID ids.NodeID,
weight uint64,
potentialReward, totalAccruedRewards, totalAccruedDelegateeRewards uint64,
) error {
parentState, ok := d.stateVersions.GetState(d.parentID)
if !ok {
return fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID)
}

return d.currentStakerDiffs.updateValidator(parentState, subnetID, nodeID, func(validator Staker) (*Staker, error) {
if validator.ContinuationPeriod == 0 {
return nil, errIncompatibleContinuousStaker
}

if err := validator.resetContinuationStakerCycle(weight, potentialReward, totalAccruedRewards, totalAccruedDelegateeRewards); err != nil {
return nil, err
}

return &validator, nil
})
}

func (d *diff) DeleteCurrentValidator(staker *Staker) {
d.currentStakerDiffs.DeleteValidator(staker)
}
Expand Down Expand Up @@ -601,6 +655,10 @@ func (d *diff) Apply(baseState Chain) error {
}
case deleted:
baseState.DeleteCurrentValidator(validatorDiff.validator)
case modified:
if err := baseState.UpdateCurrentValidator(validatorDiff.validator); err != nil {
return err
}
}

addedDelegatorIterator := iterator.FromTree(validatorDiff.addedDelegators)
Expand Down Expand Up @@ -630,6 +688,8 @@ func (d *diff) Apply(baseState Chain) error {
}
case deleted:
baseState.DeletePendingValidator(validatorDiff.validator)
case modified:
return fmt.Errorf("pending stakers cannot be modified")
}

addedDelegatorIterator := iterator.FromTree(validatorDiff.addedDelegators)
Expand Down
Loading
Loading