Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for new asset proposals in batches #11147

Merged
merged 5 commits into from
Apr 22, 2024
Merged
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
- [11121](https://github.com/vegaprotocol/vega/issues/11121) - Remove auction trigger staleness functionality
- [11127](https://github.com/vegaprotocol/vega/issues/11127) - Price monitoring engine should record all observations with the same weight
- [10995](https://github.com/vegaprotocol/vega/issues/10995) - Liquidation range defined by its own parameter.
- [11167](https://github.com/vegaprotocol/vega/issues/11167) - Add realised return reward metric.
- [11167](https://github.com/vegaprotocol/vega/issues/11167) - Add realised return reward metric.
- [11165] (https://github.com/vegaprotocol/vega/issues/11165) - Include negative returns in relative returns reward metric.
- [11151](https://github.com/vegaprotocol/vega/issues/11151) - Remove name field from the spot markets.
- [11143](https://github.com/vegaprotocol/vega/issues/11143) - Add support for new asset proposal in batch governance proposal

### 🐛 Fixes

Expand Down
12 changes: 12 additions & 0 deletions commands/batch_proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ func checkBatchProposalChanges(terms *protoTypes.BatchProposalTermsChange) Error
errs.Merge(checkNetworkParameterUpdateBatchChanges(c))
case *protoTypes.BatchProposalTermsChange_UpdateAsset:
errs.Merge(checkUpdateAssetBatchChanges(c))
case *protoTypes.BatchProposalTermsChange_NewAsset:
errs.Merge(checkNewAssetBatchChanges(c))
case *protoTypes.BatchProposalTermsChange_NewFreeform:
errs.Merge(checkNewFreeformBatchChanges(c))
case *protoTypes.BatchProposalTermsChange_NewTransfer:
Expand Down Expand Up @@ -208,6 +210,16 @@ func checkUpdateAssetBatchChanges(change *protoTypes.BatchProposalTermsChange_Up
return checkUpdateAsset(change.UpdateAsset).AddPrefix("batch_proposal_submission.terms.changes.")
}

func checkNewAssetBatchChanges(change *protoTypes.BatchProposalTermsChange_NewAsset) Errors {
errs := NewErrors()

if change.NewAsset == nil {
return errs.FinalAddForProperty("batch_proposal_submission.terms.changes.new_asset", ErrIsRequired)
}

return checkBatchNewAssetChanges(change).AddPrefix("batch_proposal_submission.terms.changes.")
}

func checkNewFreeformBatchChanges(change *protoTypes.BatchProposalTermsChange_NewFreeform) Errors {
errs := NewErrors()

Expand Down
42 changes: 42 additions & 0 deletions commands/proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,48 @@ func checkNewAssetChanges(change *protoTypes.ProposalTerms_NewAsset) Errors {
return errs
}

func checkBatchNewAssetChanges(change *protoTypes.BatchProposalTermsChange_NewAsset) Errors {
errs := NewErrors()

if change.NewAsset == nil {
return errs.FinalAddForProperty("proposal_submission.terms.change.new_asset", ErrIsRequired)
}

if change.NewAsset.Changes == nil {
return errs.FinalAddForProperty("proposal_submission.terms.change.new_asset.changes", ErrIsRequired)
}

if len(change.NewAsset.Changes.Name) == 0 {
errs.AddForProperty("proposal_submission.terms.change.new_asset.changes.name", ErrIsRequired)
}
if len(change.NewAsset.Changes.Symbol) == 0 {
errs.AddForProperty("proposal_submission.terms.change.new_asset.changes.symbol", ErrIsRequired)
}

if len(change.NewAsset.Changes.Quantum) <= 0 {
errs.AddForProperty("proposal_submission.terms.change.new_asset.changes.quantum", ErrIsRequired)
} else if quantum, err := num.DecimalFromString(change.NewAsset.Changes.Quantum); err != nil {
errs.AddForProperty("proposal_submission.terms.change.new_asset.changes.quantum", ErrIsNotValidNumber)
} else if quantum.LessThanOrEqual(num.DecimalZero()) {
errs.AddForProperty("proposal_submission.terms.change.new_asset.changes.quantum", ErrMustBePositive)
}

if change.NewAsset.Changes.Source == nil {
return errs.FinalAddForProperty("proposal_submission.terms.change.new_asset.changes.source", ErrIsRequired)
}

switch s := change.NewAsset.Changes.Source.(type) {
case *protoTypes.AssetDetails_BuiltinAsset:
errs.Merge(checkBuiltinAssetSource(s))
case *protoTypes.AssetDetails_Erc20:
errs.Merge(checkERC20AssetSource(s))
default:
return errs.FinalAddForProperty("proposal_submission.terms.change.new_asset.changes.source", ErrIsNotValid)
}

return errs
}

func CheckNewFreeformChanges(change *protoTypes.ProposalTerms_NewFreeform) Errors {
errs := NewErrors()

Expand Down
46 changes: 46 additions & 0 deletions core/governance/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,44 @@ func (e *Engine) OnTick(ctx context.Context, t time.Time) ([]*ToEnact, []*VoteCl
}
}

// then do the same thing for batches
acceptedBatches, rejectedBatches := e.nodeProposalValidation.OnTickBatch(t)
for _, p := range acceptedBatches {
e.log.Info("proposal has been validated by nodes, starting now",
logging.String("proposal-id", p.ID))
p.Open()

e.broker.Send(events.NewProposalEventFromProto(ctx, p.ToProto()))
proposalsEvents := []events.Event{}
for _, v := range p.Proposals {
proposalsEvents = append(proposalsEvents, events.NewProposalEvent(ctx, *v))
}
e.broker.SendBatch(proposalsEvents)

e.startValidatedBatchProposal(p) // can't fail, and proposal has been validated at an ulterior time
}

for _, p := range rejectedBatches {
e.log.Info("proposal has not been validated by nodes",
logging.String("proposal-id", p.ID))
p.Reject(types.ProposalErrorNodeValidationFailed)
e.broker.Send(events.NewProposalEventFromProto(ctx, p.ToProto()))
proposalsEvents := []events.Event{}
for _, v := range p.Proposals {
proposalsEvents = append(proposalsEvents, events.NewProposalEvent(ctx, *v))
}
e.broker.SendBatch(proposalsEvents)

for _, v := range p.Proposals {
// if it's an asset proposal we need to update it's
// state in the asset engine
switch v.Terms.Change.GetTermType() {
case types.ProposalTermsTypeNewAsset:
e.assets.SetRejected(ctx, p.ID)
}
}
}

toBeEnacted := []*ToEnact{}
for i, ep := range preparedToEnact {
// this is the new market proposal, and should already be in the slice
Expand Down Expand Up @@ -645,10 +683,18 @@ func (e *Engine) startValidatedProposal(p *proposal) {
e.activeProposals = append(e.activeProposals, p)
}

func (e *Engine) startValidatedBatchProposal(p *batchProposal) {
e.activeBatchProposals[p.ID] = p
}

func (e *Engine) startTwoStepsProposal(ctx context.Context, p *types.Proposal) error {
return e.nodeProposalValidation.Start(ctx, p)
}

func (e *Engine) startTwoStepsBatchProposal(ctx context.Context, p *types.BatchProposal) error {
return e.nodeProposalValidation.StartBatch(ctx, p)
}

func (e *Engine) isTwoStepsProposal(p *types.Proposal) bool {
return e.nodeProposalValidation.IsNodeValidationRequired(p)
}
Expand Down
47 changes: 42 additions & 5 deletions core/governance/engine_batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ func (e *Engine) SubmitBatchProposal(
Reference: bp.Reference,
Rationale: bp.Rationale,
Terms: &types.ProposalTerms{
ClosingTimestamp: bp.ClosingTimestamp,
EnactmentTimestamp: change.EnactmentTime,
Change: change.Change,
ClosingTimestamp: bp.ClosingTimestamp,
EnactmentTimestamp: change.EnactmentTime,
ValidationTimestamp: change.ValidationTime,
Change: change.Change,
},
}

Expand Down Expand Up @@ -121,11 +122,38 @@ func (e *Engine) SubmitBatchProposal(
return nil, errs
}

e.startBatchProposal(bp)
if e.isTwoStepsBatchProposal(bp) {
// set all proposals as WaitForNodeVote then
bp.WaitForNodeVote()
// reset events here as we will need to send another updated one instead
proposalsEvents = []events.Event{}
for _, p := range bp.Proposals {
proposalsEvents = append(proposalsEvents, events.NewProposalEvent(ctx, *p))
}

if err := e.startTwoStepsBatchProposal(ctx, bp); err != nil {
bp.RejectWithErr(types.ProposalErrorNodeValidationFailed, err)
proposalsEvents = []events.Event{}
for _, p := range bp.Proposals {
proposalsEvents = append(proposalsEvents, events.NewProposalEvent(ctx, *p))
}
if e.log.IsDebug() {
e.log.Debug("Proposal rejected",
logging.String("batch-proposal-id", bp.ID))
}
return nil, err
}
} else {
e.startBatchProposal(bp)
}

return toSubmits, nil
}

func (e *Engine) isTwoStepsBatchProposal(p *types.BatchProposal) bool {
return e.nodeProposalValidation.IsNodeValidationRequiredBatch(p)
}

func (e *Engine) RejectBatchProposal(
ctx context.Context, proposalID string, r types.ProposalError, errorDetails error,
) error {
Expand Down Expand Up @@ -262,7 +290,16 @@ func (e *Engine) evaluateBatchProposals(

func (e *Engine) getBatchProposal(id string) (*batchProposal, bool) {
bp, ok := e.activeBatchProposals[id]
return bp, ok
if ok {
return bp, ok
}

nbp, ok := e.nodeProposalValidation.getBatchProposal(id)
if !ok {
return nil, false
}

return nbp.batchProposal, ok
}

func (e *Engine) validateProposalFromBatch(
Expand Down
106 changes: 106 additions & 0 deletions core/governance/engine_new_asset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,112 @@ func testVotingDuringValidationOfProposalForNewAssetSucceeds(t *testing.T) {
assert.EqualError(t, err, governance.ErrProposalDoesNotExist.Error())
}

func TestVotingDuringValidationOfProposalForNewAssetInBatchSucceeds(t *testing.T) {
eng := getTestEngine(t, time.Now())

now := eng.tsvc.GetTimeNow().Add(2 * time.Hour)

// when
proposer := vgrand.RandomStr(5)
batchID := eng.newProposalID()

newAssetProposal := eng.newProposalForNewAsset(proposer, now)

// setup
var bAsset *assets.Asset
var fcheck func(interface{}, bool)
var rescheck validators.Resource
eng.assets.EXPECT().NewAsset(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(_ context.Context, ref string, assetDetails *types.AssetDetails) (string, error) {
bAsset = assets.NewAsset(builtin.New(ref, assetDetails))
return ref, nil
})
eng.assets.EXPECT().Get(gomock.Any()).Times(1).DoAndReturn(func(id string) (*assets.Asset, error) {
return bAsset, nil
})
eng.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Do(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
fcheck = f
rescheck = r
return nil
})
eng.ensureStakingAssetTotalSupply(t, 9)
eng.ensureAllAssetEnabled(t)
eng.ensureTokenBalanceForParty(t, proposer, 1)

// expect
eng.expectProposalWaitingForNodeVoteEvent(t, proposer, batchID)

// eng.expectOpenProposalEvent(t, proposer, batchID)
eng.expectProposalEvents(t, []expectedProposal{
{
partyID: proposer,
proposalID: newAssetProposal.ID,
state: types.ProposalStateWaitingForNodeVote,
reason: types.ProposalErrorUnspecified,
},
})

batchClosingTime := now.Add(48 * time.Hour)

// when
_, err := eng.submitBatchProposal(t, eng.newBatchSubmission(
batchClosingTime.Unix(),
newAssetProposal,
), batchID, proposer)

assert.NoError(t, err)

// given
voter1 := vgrand.RandomStr(5)

// setup
eng.ensureTokenBalanceForParty(t, voter1, 7)

// expect
eng.expectVoteEvent(t, voter1, batchID)

// then
err = eng.addYesVote(t, voter1, batchID)
require.NoError(t, err)

_ = fcheck
_ = rescheck
// call success on the validation
fcheck(rescheck, true)

// then
afterValidation := time.Unix(newAssetProposal.Terms.ValidationTimestamp, 0).Add(time.Second)

// setup
eng.ensureTokenBalanceForParty(t, voter1, 7)

// expect
eng.expectOpenProposalEvent(t, proposer, batchID)
// and the inner proposals
eng.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
// eng.expectGetMarketState(t, ID)

// when
eng.OnTick(context.Background(), afterValidation)

// given
afterClosing := time.Unix(newAssetProposal.Terms.ClosingTimestamp, 0).Add(time.Second)

// // expect
eng.expectPassedProposalEvent(t, batchID)
// and the inner proposals
eng.broker.EXPECT().SendBatch(gomock.Any()).Times(1)

// // when
eng.OnTick(context.Background(), afterClosing)

eng.assets.EXPECT().SetPendingListing(gomock.Any(), newAssetProposal.ID).Times(1)
eng.OnTick(context.Background(), afterClosing.Add(1*time.Minute))

// when
toBeEnacted, _ := eng.OnTick(context.Background(), afterClosing.Add(48*time.Hour))
require.Len(t, toBeEnacted, 1)
}

func TestNoVotesAnd0RequiredFails(t *testing.T) {
eng := getTestEngine(t, time.Now())

Expand Down
7 changes: 4 additions & 3 deletions core/governance/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1994,9 +1994,10 @@ func (e *tstEngine) newBatchSubmission(

for _, proposal := range proposals {
sub.Terms.Changes = append(sub.Terms.Changes, types.BatchProposalChange{
ID: proposal.ID,
Change: proposal.Terms.Change,
EnactmentTime: proposal.Terms.EnactmentTimestamp,
ID: proposal.ID,
Change: proposal.Terms.Change,
EnactmentTime: proposal.Terms.EnactmentTimestamp,
ValidationTime: proposal.Terms.ValidationTimestamp,
})
}

Expand Down
Loading
Loading