Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
evlekht committed Sep 11, 2023
1 parent 52c020c commit c7c644c
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 67 deletions.
6 changes: 5 additions & 1 deletion vms/platformvm/blocks/builder/camino_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func caminoBuildBlock(
return nil, nil
}

// Ulocking expired deposits
depositsTxIDs, shouldUnlock, err := getNextDepositsToUnlock(parentState, timestamp)
if err != nil {
return nil, fmt.Errorf("could not find next deposits to unlock: %w", err)
Expand All @@ -90,6 +91,7 @@ func caminoBuildBlock(
)
}

// Finishing expired and early finished proposals
expiredProposalIDs, err := getExpiredProposals(parentState, timestamp)
if err != nil {
return nil, fmt.Errorf("could not find expired proposals: %w", err)
Expand All @@ -99,11 +101,13 @@ func caminoBuildBlock(
return nil, fmt.Errorf("could not find successful proposals: %w", err)
}
if len(expiredProposalIDs) > 0 || len(earlyFinishedProposalIDs) > 0 {
finishProposalsTx, err := txBuilder.FinishProposalsTx(earlyFinishedProposalIDs, expiredProposalIDs)
finishProposalsTx, err := txBuilder.FinishProposalsTx(parentState, earlyFinishedProposalIDs, expiredProposalIDs)
if err != nil {
return nil, fmt.Errorf("could not build tx to finish proposals: %w", err)
}

// FinishProposalsTx should never be in block with addVoteTx,
// because it can affect state of proposals.
return blocks.NewBanffStandardBlock(
timestamp,
parentID,
Expand Down
1 change: 1 addition & 0 deletions vms/platformvm/dac/camino_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type ProposalState interface {

EndTime() time.Time
IsActiveAt(time time.Time) bool
// Once a proposal has become Finishable, it cannot be undone by adding more votes.
CanBeFinished() bool
IsSuccessful() bool // should be called only for finished proposals
Outcome() any // should be called only for finished successful proposals
Expand Down
41 changes: 31 additions & 10 deletions vms/platformvm/txs/builder/camino_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type CaminoTxBuilder interface {
) (*txs.Tx, error)

FinishProposalsTx(
state state.Chain,
earlyFinishedProposalIDs []ids.ID,
expiredProposalIDs []ids.ID,
) (*txs.Tx, error)
Expand Down Expand Up @@ -689,27 +690,47 @@ func (b *caminoBuilder) NewSystemUnlockDepositTx(
}

func (b *caminoBuilder) FinishProposalsTx(
state state.Chain,
earlyFinishedProposalIDs []ids.ID,
expiredProposalIDs []ids.ID,
) (*txs.Tx, error) {
ins, outs, err := b.Unlock(
b.state,
state,
append(earlyFinishedProposalIDs, expiredProposalIDs...),
locked.StateBonded,
)
if err != nil {
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
}

utx := &txs.FinishProposalsTx{
BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
NetworkID: b.ctx.NetworkID,
BlockchainID: b.ctx.ChainID,
Ins: ins,
Outs: outs,
}},
EarlyFinishedProposalIDs: earlyFinishedProposalIDs,
ExpiredProposalIDs: expiredProposalIDs,
utx := &txs.FinishProposalsTx{BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
NetworkID: b.ctx.NetworkID,
BlockchainID: b.ctx.ChainID,
Ins: ins,
Outs: outs,
}}}

for _, proposalID := range earlyFinishedProposalIDs {
proposal, err := state.GetProposal(proposalID)
if err != nil {
return nil, fmt.Errorf("couldn't get proposal from state: %w", err)
}
if proposal.IsSuccessful() {
utx.EarlyFinishedSuccessfulProposalIDs = append(utx.EarlyFinishedSuccessfulProposalIDs, proposalID)
} else {
utx.EarlyFinishedFailedProposalIDs = append(utx.EarlyFinishedFailedProposalIDs, proposalID)
}
}
for _, proposalID := range expiredProposalIDs {
proposal, err := state.GetProposal(proposalID)
if err != nil {
return nil, fmt.Errorf("couldn't get proposal from state: %w", err)
}
if proposal.IsSuccessful() {
utx.ExpiredSuccessfulProposalIDs = append(utx.ExpiredSuccessfulProposalIDs, proposalID)
} else {
utx.ExpiredFailedProposalIDs = append(utx.ExpiredFailedProposalIDs, proposalID)
}
}

tx, err := txs.NewSigned(utx, txs.Codec, nil)
Expand Down
34 changes: 27 additions & 7 deletions vms/platformvm/txs/camino_finish_proposals_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ type FinishProposalsTx struct {
// Metadata, inputs and outputs
BaseTx `serialize:"true"`
// Proposals that were finished early.
EarlyFinishedProposalIDs []ids.ID `serialize:"true" json:"earlyFinishedProposalIDs"`
EarlyFinishedSuccessfulProposalIDs []ids.ID `serialize:"true" json:"earlyFinishedSuccessfulProposalIDs"`
// Proposals that were finished early.
EarlyFinishedFailedProposalIDs []ids.ID `serialize:"true" json:"earlyFinishedFailedProposalIDs"`
// Proposals that were expired.
ExpiredSuccessfulProposalIDs []ids.ID `serialize:"true" json:"expiredSuccessfulProposalIDs"`
// Proposals that were expired.
ExpiredProposalIDs []ids.ID `serialize:"true" json:"expiredProposalIDs"`
ExpiredFailedProposalIDs []ids.ID `serialize:"true" json:"expiredFailedProposalIDs"`
}

// SyntacticVerify returns nil if [tx] is valid
Expand All @@ -37,23 +41,39 @@ func (tx *FinishProposalsTx) SyntacticVerify(ctx *snow.Context) error {
return ErrNilTx
case tx.SyntacticallyVerified: // already passed syntactic verification
return nil
case len(tx.EarlyFinishedProposalIDs) == 0 && len(tx.ExpiredProposalIDs) == 0:
case len(tx.EarlyFinishedSuccessfulProposalIDs) == 0 &&
len(tx.EarlyFinishedFailedProposalIDs) == 0 &&
len(tx.ExpiredSuccessfulProposalIDs) == 0 &&
len(tx.ExpiredFailedProposalIDs) == 0:
return errNoFinishedProposals
}

if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
return fmt.Errorf("failed to verify BaseTx: %w", err)
}

uniqueProposals := set.NewSet[ids.ID](len(tx.EarlyFinishedProposalIDs) + len(tx.ExpiredProposalIDs))
for _, proposalID := range tx.EarlyFinishedProposalIDs {
totalProposalsCount := len(tx.EarlyFinishedSuccessfulProposalIDs) + len(tx.EarlyFinishedFailedProposalIDs) +
len(tx.ExpiredSuccessfulProposalIDs) + len(tx.ExpiredFailedProposalIDs)
uniqueProposals := set.NewSet[ids.ID](totalProposalsCount)
for _, proposalID := range tx.EarlyFinishedSuccessfulProposalIDs {
if uniqueProposals.Contains(proposalID) {
return errNotUniqueProposalID
}
uniqueProposals.Add(proposalID)
}

for _, proposalID := range tx.ExpiredProposalIDs {
for _, proposalID := range tx.EarlyFinishedFailedProposalIDs {
if uniqueProposals.Contains(proposalID) {
return errNotUniqueProposalID
}
uniqueProposals.Add(proposalID)
}
for _, proposalID := range tx.ExpiredSuccessfulProposalIDs {
if uniqueProposals.Contains(proposalID) {
return errNotUniqueProposalID
}
uniqueProposals.Add(proposalID)
}
for _, proposalID := range tx.ExpiredFailedProposalIDs {
if uniqueProposals.Contains(proposalID) {
return errNotUniqueProposalID
}
Expand Down
89 changes: 75 additions & 14 deletions vms/platformvm/txs/camino_finish_proposals_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func TestFinishProposalsTxSyntacticVerify(t *testing.T) {
proposalID2 := ids.ID{2}
proposalID3 := ids.ID{3}
proposalID4 := ids.ID{4}
proposalID5 := ids.ID{5}
proposalID6 := ids.ID{6}
proposalID7 := ids.ID{7}
proposalID8 := ids.ID{8}

baseTx := BaseTx{BaseTx: avax.BaseTx{
NetworkID: ctx.NetworkID,
Expand All @@ -40,25 +44,80 @@ func TestFinishProposalsTxSyntacticVerify(t *testing.T) {
},
expectedErr: errNoFinishedProposals,
},
"Not unique proposals in EarlyFinishedProposalIDs": {
// TODO@ unique
"Not unique proposals in EarlyFinishedSuccessfulProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedProposalIDs: []ids.ID{proposalID1, proposalID1},
BaseTx: baseTx,
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1, proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedFailedProposalIDs: []ids.ID{proposalID1, proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in ExpiredProposalIDs": {
"Not unique proposals in ExpiredSuccessfulProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
ExpiredProposalIDs: []ids.ID{proposalID1, proposalID1},
BaseTx: baseTx,
ExpiredSuccessfulProposalIDs: []ids.ID{proposalID1, proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedProposalIDs and ExpiredProposalIDs": {
"Not unique proposals in ExpiredFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedProposalIDs: []ids.ID{proposalID1},
ExpiredProposalIDs: []ids.ID{proposalID1},
ExpiredFailedProposalIDs: []ids.ID{proposalID1, proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedSuccessfulProposalIDs and EarlyFinishedFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1},
EarlyFinishedFailedProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedSuccessfulProposalIDs and ExpiredSuccessfulProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1},
ExpiredSuccessfulProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedSuccessfulProposalIDs and ExpiredFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1},
ExpiredFailedProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedFailedProposalIDs and ExpiredSuccessfulProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedFailedProposalIDs: []ids.ID{proposalID1},
ExpiredSuccessfulProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in EarlyFinishedFailedProposalIDs and ExpiredFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedFailedProposalIDs: []ids.ID{proposalID1},
ExpiredFailedProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
"Not unique proposals in ExpiredSuccessfulProposalIDs and ExpiredFailedProposalIDs": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
ExpiredSuccessfulProposalIDs: []ids.ID{proposalID1},
ExpiredFailedProposalIDs: []ids.ID{proposalID1},
},
expectedErr: errNotUniqueProposalID,
},
Expand All @@ -71,7 +130,7 @@ func TestFinishProposalsTxSyntacticVerify(t *testing.T) {
generateTestStakeableIn(ctx.AVAXAssetID, 1, 1, []uint32{0}),
},
}},
EarlyFinishedProposalIDs: []ids.ID{proposalID1},
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1},
},
expectedErr: locked.ErrWrongInType,
},
Expand All @@ -84,15 +143,17 @@ func TestFinishProposalsTxSyntacticVerify(t *testing.T) {
generateTestStakeableOut(ctx.AVAXAssetID, 1, 1, owner1),
},
}},
EarlyFinishedProposalIDs: []ids.ID{proposalID1},
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1},
},
expectedErr: locked.ErrWrongOutType,
},
"OK": {
tx: &FinishProposalsTx{
BaseTx: baseTx,
EarlyFinishedProposalIDs: []ids.ID{proposalID1, proposalID2},
ExpiredProposalIDs: []ids.ID{proposalID3, proposalID4},
BaseTx: baseTx,
EarlyFinishedSuccessfulProposalIDs: []ids.ID{proposalID1, proposalID2},
EarlyFinishedFailedProposalIDs: []ids.ID{proposalID3, proposalID4},
ExpiredSuccessfulProposalIDs: []ids.ID{proposalID5, proposalID6},
ExpiredFailedProposalIDs: []ids.ID{proposalID7, proposalID8},
},
},
}
Expand Down
41 changes: 28 additions & 13 deletions vms/platformvm/txs/executor/camino_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,9 @@ func (e *CaminoStandardTxExecutor) FinishProposalsTx(tx *txs.FinishProposalsTx)
return errWrongCredentialsNumber
}

lockTxIDs := append(tx.EarlyFinishedProposalIDs, tx.ExpiredProposalIDs...) //nolint:gocritic
lockTxIDs := append(tx.EarlyFinishedSuccessfulProposalIDs, tx.EarlyFinishedFailedProposalIDs...) //nolint:gocritic
lockTxIDs = append(lockTxIDs, tx.ExpiredSuccessfulProposalIDs...)
lockTxIDs = append(lockTxIDs, tx.ExpiredFailedProposalIDs...)
expectedIns, expectedOuts, err := e.FlowChecker.Unlock(e.State, lockTxIDs, locked.StateBonded)
if err != nil {
return err
Expand All @@ -1933,36 +1935,49 @@ func (e *CaminoStandardTxExecutor) FinishProposalsTx(tx *txs.FinishProposalsTx)
return fmt.Errorf("%w: invalid outputs", errInvalidSystemTxBody)
}

for _, proposalID := range tx.EarlyFinishedProposalIDs {
for _, proposalID := range tx.EarlyFinishedSuccessfulProposalIDs {
proposal, err := e.State.GetProposal(proposalID)
if err != nil {
return err
}

if proposal.IsSuccessful() {
// try to execute proposal
if err := proposal.Visit(e.proposalExecutor()); err != nil {
return err
}
// try to execute proposal
if err := proposal.Visit(e.proposalExecutor()); err != nil {
return err
}

e.State.RemoveProposal(proposalID, proposal)
e.State.RemoveProposalIDToFinish(proposalID)
}

for _, proposalID := range tx.ExpiredProposalIDs {
for _, proposalID := range tx.EarlyFinishedFailedProposalIDs {
proposal, err := e.State.GetProposal(proposalID)
if err != nil {
return err
}
e.State.RemoveProposal(proposalID, proposal)
e.State.RemoveProposalIDToFinish(proposalID)
}

if proposal.IsSuccessful() {
// try to execute proposal
if err := proposal.Visit(e.proposalExecutor()); err != nil {
return err
}
for _, proposalID := range tx.ExpiredSuccessfulProposalIDs {
proposal, err := e.State.GetProposal(proposalID)
if err != nil {
return err
}

// try to execute proposal
if err := proposal.Visit(e.proposalExecutor()); err != nil {
return err
}

e.State.RemoveProposal(proposalID, proposal)
}

for _, proposalID := range tx.ExpiredFailedProposalIDs {
proposal, err := e.State.GetProposal(proposalID)
if err != nil {
return err
}
e.State.RemoveProposal(proposalID, proposal)
}

Expand Down
Loading

0 comments on commit c7c644c

Please sign in to comment.