Skip to content

Commit

Permalink
vms/platformvm: Permit usage of the Transactions field in `BanffP…
Browse files Browse the repository at this point in the history
…roposalBlock` (#2451)

Co-authored-by: Stephen Buttolph <[email protected]>
  • Loading branch information
dhrubabasu and StephenButtolph authored Dec 12, 2023
1 parent 54abd9a commit dc472ec
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 81 deletions.
16 changes: 1 addition & 15 deletions vms/avm/block/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (b *builder) BuildBlock(context.Context) (snowman.Block, error) {

// Invariant: [tx] has already been syntactically verified.

txDiff, err := wrapState(stateDiff)
txDiff, err := state.NewDiffOn(stateDiff)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -170,17 +170,3 @@ func (b *builder) BuildBlock(context.Context) (snowman.Block, error) {

return b.manager.NewBlock(statelessBlk), nil
}

type stateGetter struct {
state state.Chain
}

func (s stateGetter) GetState(ids.ID) (state.Chain, bool) {
return s.state, true
}

func wrapState(parentState state.Chain) (state.Diff, error) {
return state.NewDiff(ids.Empty, stateGetter{
state: parentState,
})
}
17 changes: 16 additions & 1 deletion vms/avm/state/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
)

var (
_ Diff = (*diff)(nil)
_ Diff = (*diff)(nil)
_ Versions = stateGetter{}

ErrMissingParentState = errors.New("missing parent state")
)
Expand Down Expand Up @@ -61,6 +62,20 @@ func NewDiff(
}, nil
}

type stateGetter struct {
state Chain
}

func (s stateGetter) GetState(ids.ID) (Chain, bool) {
return s.state, true
}

func NewDiffOn(parentState Chain) (Diff, error) {
return NewDiff(ids.Empty, stateGetter{
state: parentState,
})
}

func (d *diff) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) {
if utxo, modified := d.modifiedUTXOs[utxoID]; modified {
if utxo == nil {
Expand Down
14 changes: 12 additions & 2 deletions vms/platformvm/block/executor/acceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ func (a *acceptor) optionBlock(b, parent block.Block, blockType string) error {
return err
}

parentState, ok := a.blkIDToState[parentID]
if !ok {
return fmt.Errorf("%w %s", errMissingBlockState, parentID)
}
if parentState.onDecisionState != nil {
if err := parentState.onDecisionState.Apply(a.state); err != nil {
return err
}
}

blkState, ok := a.blkIDToState[blkID]
if !ok {
return fmt.Errorf("%w %s", errMissingBlockState, blkID)
Expand All @@ -191,11 +201,11 @@ func (a *acceptor) optionBlock(b, parent block.Block, blockType string) error {
}

// Note that this method writes [batch] to the database.
if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil {
if err := a.ctx.SharedMemory.Apply(parentState.atomicRequests, batch); err != nil {
return fmt.Errorf("failed to apply vm's state to shared memory: %w", err)
}

if onAcceptFunc := blkState.onAcceptFunc; onAcceptFunc != nil {
if onAcceptFunc := parentState.onAcceptFunc; onAcceptFunc != nil {
onAcceptFunc()
}

Expand Down
8 changes: 4 additions & 4 deletions vms/platformvm/block/executor/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@ func (b *backend) GetState(blkID ids.ID) (state.Chain, bool) {
return b.state, blkID == b.state.GetLastAccepted()
}

func (b *backend) getBlkWithOnAbortState(blkID ids.ID) (*blockState, bool) {
func (b *backend) getOnAbortState(blkID ids.ID) (state.Diff, bool) {
state, ok := b.blkIDToState[blkID]
if !ok || state.onAbortState == nil {
return nil, false
}
return state, true
return state.onAbortState, true
}

func (b *backend) getBlkWithOnCommitState(blkID ids.ID) (*blockState, bool) {
func (b *backend) getOnCommitState(blkID ids.ID) (state.Diff, bool) {
state, ok := b.blkIDToState[blkID]
if !ok || state.onCommitState == nil {
return nil, false
}
return state, true
return state.onCommitState, true
}

func (b *backend) GetBlock(blkID ids.ID) (block.Block, error) {
Expand Down
1 change: 1 addition & 0 deletions vms/platformvm/block/executor/block_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

type proposalBlockState struct {
initiallyPreferCommit bool
onDecisionState state.Diff
onCommitState state.Diff
onAbortState state.Diff
}
Expand Down
171 changes: 170 additions & 1 deletion vms/platformvm/block/executor/proposal_block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/ava-labs/avalanchego/snow/consensus/snowman"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/timer/mockable"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/platformvm/block"
"github.com/ava-labs/avalanchego/vms/platformvm/reward"
Expand Down Expand Up @@ -151,7 +152,8 @@ func TestBanffProposalBlockTimeVerification(t *testing.T) {
require.NoError(shutdownEnvironment(env))
}()
env.clk.Set(defaultGenesisTime)
env.config.BanffTime = time.Time{} // activate Banff
env.config.BanffTime = time.Time{} // activate Banff
env.config.DurangoTime = mockable.MaxTime // deactivate Durango

// create parentBlock. It's a standard one for simplicity
parentTime := defaultGenesisTime
Expand Down Expand Up @@ -1335,3 +1337,170 @@ func TestBanffProposalBlockDelegatorStakers(t *testing.T) {
vdrWeight = env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID)
require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight)
}

func TestAddValidatorProposalBlock(t *testing.T) {
require := require.New(t)
env := newEnvironment(t, nil)
defer func() {
require.NoError(shutdownEnvironment(env))
}()
env.config.BanffTime = time.Time{} // activate Banff
env.config.DurangoTime = time.Time{} // activate Durango

now := env.clk.Time()

// Create validator tx
var (
validatorStartTime = now.Add(2 * executor.SyncBound)
validatorEndTime = validatorStartTime.Add(env.config.MinStakeDuration)
nodeID = ids.GenerateTestNodeID()
)

addValidatorTx, err := env.txBuilder.NewAddValidatorTx(
env.config.MinValidatorStake,
uint64(validatorStartTime.Unix()),
uint64(validatorEndTime.Unix()),
nodeID,
preFundedKeys[0].PublicKey().Address(),
10000,
[]*secp256k1.PrivateKey{
preFundedKeys[0],
preFundedKeys[1],
preFundedKeys[4],
},
ids.ShortEmpty,
)
require.NoError(err)

// Add validator through a [StandardBlock]
preferredID := env.blkManager.Preferred()
preferred, err := env.blkManager.GetStatelessBlock(preferredID)
require.NoError(err)

statelessBlk, err := block.NewBanffStandardBlock(
now.Add(executor.SyncBound),
preferredID,
preferred.Height()+1,
[]*txs.Tx{addValidatorTx},
)
require.NoError(err)
blk := env.blkManager.NewBlock(statelessBlk)
require.NoError(blk.Verify(context.Background()))
require.NoError(blk.Accept(context.Background()))
require.True(env.blkManager.SetPreference(statelessBlk.ID()))

// Should be pending
staker, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, nodeID)
require.NoError(err)
require.NotNil(staker)

// Promote validator from pending to current
env.clk.Set(validatorStartTime)
now = env.clk.Time()

preferredID = env.blkManager.Preferred()
preferred, err = env.blkManager.GetStatelessBlock(preferredID)
require.NoError(err)

statelessBlk, err = block.NewBanffStandardBlock(
now,
preferredID,
preferred.Height()+1,
nil,
)
require.NoError(err)
blk = env.blkManager.NewBlock(statelessBlk)
require.NoError(blk.Verify(context.Background()))
require.NoError(blk.Accept(context.Background()))
require.True(env.blkManager.SetPreference(statelessBlk.ID()))

// Should be current
staker, err = env.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID)
require.NoError(err)
require.NotNil(staker)

// Advance time until next staker change time is [validatorEndTime]
for {
nextStakerChangeTime, err := executor.GetNextStakerChangeTime(env.state)
require.NoError(err)
if nextStakerChangeTime.Equal(validatorEndTime) {
break
}

preferredID = env.blkManager.Preferred()
preferred, err = env.blkManager.GetStatelessBlock(preferredID)
require.NoError(err)

statelessBlk, err = block.NewBanffStandardBlock(
nextStakerChangeTime,
preferredID,
preferred.Height()+1,
nil,
)
require.NoError(err)
blk = env.blkManager.NewBlock(statelessBlk)
require.NoError(blk.Verify(context.Background()))
require.NoError(blk.Accept(context.Background()))
require.True(env.blkManager.SetPreference(statelessBlk.ID()))
}

env.clk.Set(validatorEndTime)
now = env.clk.Time()

// Create another validator tx
validatorStartTime = now.Add(2 * executor.SyncBound)
validatorEndTime = validatorStartTime.Add(env.config.MinStakeDuration)
nodeID = ids.GenerateTestNodeID()

addValidatorTx2, err := env.txBuilder.NewAddValidatorTx(
env.config.MinValidatorStake,
uint64(validatorStartTime.Unix()),
uint64(validatorEndTime.Unix()),
nodeID,
preFundedKeys[0].PublicKey().Address(),
10000,
[]*secp256k1.PrivateKey{
preFundedKeys[0],
preFundedKeys[1],
preFundedKeys[4],
},
ids.ShortEmpty,
)
require.NoError(err)

// Add validator through a [ProposalBlock] and reward the last one
preferredID = env.blkManager.Preferred()
preferred, err = env.blkManager.GetStatelessBlock(preferredID)
require.NoError(err)

rewardValidatorTx, err := env.txBuilder.NewRewardValidatorTx(addValidatorTx.ID())
require.NoError(err)

statelessProposalBlk, err := block.NewBanffProposalBlock(
now,
preferredID,
preferred.Height()+1,
rewardValidatorTx,
[]*txs.Tx{addValidatorTx2},
)
require.NoError(err)
blk = env.blkManager.NewBlock(statelessProposalBlk)
require.NoError(blk.Verify(context.Background()))

options, err := blk.(snowman.OracleBlock).Options(context.Background())
require.NoError(err)
commitBlk := options[0]
require.NoError(commitBlk.Verify(context.Background()))

require.NoError(blk.Accept(context.Background()))
require.NoError(commitBlk.Accept(context.Background()))

// Should be pending
staker, err = env.state.GetPendingValidator(constants.PrimaryNetworkID, nodeID)
require.NoError(err)
require.NotNil(staker)

rewardUTXOs, err := env.state.GetRewardUTXOs(addValidatorTx.ID())
require.NoError(err)
require.NotEmpty(rewardUTXOs)
}
Loading

0 comments on commit dc472ec

Please sign in to comment.