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

Codebase migration refactor #464

Closed
wants to merge 8 commits into from

Conversation

fedekunze
Copy link
Contributor

closes #457

Description

Motivation and Context

  • I have raised an issue to propose this change (required)
  • My issue has received approval from the maintainers or lead with the design/approved label

How Has This Been Tested?

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I've read the CONTRIBUTION guide
  • I have signed-off my commits with git commit -s
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Copy link
Contributor

@boz boz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation for these changes?

app/app.go Outdated
EvidenceKeeper evidence.Keeper
DeploymentKeeper deployment.Keeper
MarketKeeper market.Keeper
ProviderKeeper provider.Keeper
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

making the keepers public leverages the AkashApp as the source for integration tests (as it should be). See cosmos/cosmos-sdk#4875 for reference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I think that the reality is that they don't need to be fields in the struct at all

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to access all of the keepers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anyways, please keep them in a separate struct for organization/isolation and only export the keepers that you need to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I think that the reality is that they don't need to be fields in the struct at all?

As you prefer, I'd still need to make the keeper internal struct public tho.

You need to access all of the keepers?

I need access to all the keepers used by the Akash modules

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok please keep them in an isolated struct and export them on an as-needed basis.

app/app.go Outdated
}

// GetMaccPerms returns a copy of the module account permissions
func GetMaccPerms() map[string][]string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the previous definition?

app/app.go Outdated
EvidenceKeeper evidence.Keeper
DeploymentKeeper deployment.Keeper
MarketKeeper market.Keeper
ProviderKeeper provider.Keeper
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I think that the reality is that they don't need to be fields in the struct at all

app/app.go Outdated
func NewApp(
logger log.Logger, db dbm.DB, tio io.Writer, options ...func(*bam.BaseApp),
// NewAkashApp creates and returns a new Akash App.
func NewAkashApp(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you changing the name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the constructor name should match the returned value (i.e AkashApp)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay. would have preferred to change the struct name to App.

app/app.go Outdated
auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName,
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
crisis.ModuleName, genutil.ModuleName, evidence.ModuleName,
deployment.ModuleName, provider.ModuleName, market.ModuleName, // akash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you changing the format here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uses too many lines. I usually break the line at ~80 chars

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the previous format.

app/app.go Outdated
func (app *AkashApp) beginBlocker(
ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
// BeginBlocker application updates every begin block
func (app *AkashApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you making these public?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One reason is, it helps in simapp tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the standard of the SDK framework. It doesn´t make much difference since the function calls a private function. Otherwise, you can call the module manager, mm, directly so there's no need to have a private function call another private function imho.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporting symbols comes with many costs. Please don't export any symbols unless absolutely necessary.

@gosuri
Copy link
Member

gosuri commented Mar 19, 2020

great! @fedekunze please also to make sure to document your findings and reasons, especially for major interface changes (names, etc) since we have other members in the team that need to rebase and we want to make sure practices are documented well to reduce knowledge asymmetry.

add a section "refactor considerations" for [DCS-5].(https://github.com/ovrclk/dcs/blob/master/spec/dcs-5/README.md)

@anilcse
Copy link
Contributor

anilcse commented Mar 19, 2020

great! @fedekunze please also to make sure to document your findings and reasons, especially for major interface changes (names, etc) since we have other members in the team that need to rebase and we want to make sure practices are documented well to reduce knowledge asymmetry.

add a section "refactor considerations" for [DCS-5].(https://github.com/ovrclk/dcs/blob/master/spec/dcs-5/README.md)

I checked all these updates and are in sync with the standards sdk/gaia follows. There are a plenty of discussions before making all these changes in gaia and simapp.

I also feel that, keeping akash code close to gaia will help in reducing redundant work for upgrades (sdk changes). Otherwise, it will be an overhead to upgrade.

@boz
Copy link
Contributor

boz commented Mar 19, 2020

The Gia and cosmos-sdk "standards" are constantly-moving targets. They also tend to diverge from idiomatic go patterns.

Refactoring for the sake of refactoring can be necessary at times, but it is not here.

app/app.go Outdated

// akash
deployment.AppModuleBasic{},
market.AppModuleBasic{},
provider.AppModuleBasic{},
)

// module account permissions
maccPerms = map[string][]string{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason for having these be functions was to prevent mutation - the functions return a new copy every time. please revert to the previous pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's what GetMaccPerms does too 🙂

Copy link
Contributor

@boz boz Mar 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you change it then? before there was no way that it could be unintentionally mutated, and now there is.

@@ -12,19 +12,19 @@ import (
func NewHandler(keeper keeper.Keeper, mkeeper MarketKeeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
switch msg := msg.(type) {
case types.MsgCreate:
case types.MsgCreateDeployment:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Contributor

@boz boz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great, thank you!

const (
TypeMsgCreateDeployment = "create_deployment"
TypeMsgUpdateDeployment = "update_deployment"
TypeMsgCloseDeployment = "close_deployment"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make these values be the same as those in the codec registration and use these constants there?

If they don't need to be exported, please make them private.

if id.Owner.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "owner cannot be empty")
}
if id.DSeq == 0 && id.GSeq == 0 && id.OSeq == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you can use return id.GroupID().Validate(). Same goes for the rest of them, I'll illustrate below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't bc OSeq is not defined for GroupID

Copy link
Contributor

@boz boz Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • BidID = OrderID + Provider; Validate(BidID) = Validate(OrderID) + Validate(Provider)
  • OrderID = GroupID + OSeq; Validate(OrderID) = Validate(GroupID) + Validate(OSeq)
  • GroupID = DeploymentID + GSeq; Validate(GroupID) = Validate(DeploymentID) + Validate(GSeq)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, re-use the validations from the contained structures. Unrolling at every level will lead to bugs, missed validations, and make refactoring more difficult. There are utilities for converting between the various IDs.

See the Equals() methods for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I respectfully disagree, not having any validation or the lack of testing leads to bugs. The same applies for private functions and vars, they don't provide any extra security guarantee if you define the public functions within the scope of the module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not having any validation or the lack of testing leads to bugs

That's why we have a task to do those things.

Those things are, of course, completely separate from the topic of the benefits of abstraction, unless you count the benefits of re-using already-tested code (when OrderID.Validate() is tested, BidID.Validate() would use that tested code).

The same applies for private functions and vars, they don't provide any extra security guarantee if you define the public functions within the scope of the module.

I don't really know what you mean by that last part, but keeping the surface area of a package to a minimum interface is for abstraction (internal implementation can change without affecting users of the package), isolation (prevents and/or minimizes paths to state mutation), and yes, security (vars can be re-assigned). This is software engineering 101 stuff.

x/market/types/id.go Show resolved Hide resolved
x/market/types/msgs.go Outdated Show resolved Hide resolved
x/provider/types/msgs.go Outdated Show resolved Hide resolved
)

// MaccPerms returns the module account permissions
func MaccPerms() map[string][]string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please just revert this to the original. Does it need to be exported?

// ModuleBasics defines the module BasicManager is in charge of setting up basic,
// non-dependant module elements, such as codec registration
// and genesis verification.
ModuleBasics = module.NewBasicManager(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this to the original. Exporting it as a function prevents other modules from re-assigning it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporting it as a function prevents other modules from re-assigning it.

modules don't have access to other modules, nor the module manager or other application-level fields unless explicitly given as arguments to the keepers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I meant packages as in golang packages.

The fact of the matter is that there was absolutely no reason to change this. The same is true for most of this PR. I won't approve any of these (not necessarily) stylistic changes.

bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
gov.StoreKey, params.StoreKey, evidence.StoreKey,
deployment.StoreKey, market.StoreKey, provider.StoreKey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert to original format for all of these, please.


// ModuleAccountAddrs returns all the app's module account addresses.
func (app *App) ModuleAccountAddrs() map[string]bool {
modAccAddrs := make(map[string]bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

general template like was done originally

coll := generator()
vals := make(map[string]bool,len(coll))
for k, v := range coll {
  // ...
}
return vals

if id.Owner.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "owner cannot be empty")
}
if id.DSeq == 0 && id.GSeq == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return id.DeploymentID().Validate()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, I can't because GSeq is not defined for DeploymentID

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GroupID = DeploymentID + GSeq; Validate(GroupID) = Validate(DeploymentID) + Validate(GSeq)

TypeMsgCloseDeployment = "close_deployment"
typeMsgCreateDeployment = "create_deployment"
typeMsgUpdateDeployment = "update_deployment"
typeMsgCloseDeployment = "close_deployment"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either

// msgs.go
typeMsgCreateDeployment = ModuleName + "/msg-create-deployment"

// codec.go
cdc.RegisterConcrete(MsgCreateDeployment{}, typeMsgCreateDeployment, nil)

or

// msgs.go
typeMsgCreateDeployment = msg-create-deployment"

// codec.go
cdc.RegisterConcrete(MsgCreateDeployment{}, ModuleName + "/" + typeMsgCreateDeployment, nil)

Same goes for the rest of these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you check the godoc for the RegisterConcrete the name must match the type name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great then use

// msgs.go
typeMsgCreateDeployment = `MsgCreateDeployment`

// codec.go
cdc.RegisterConcrete(MsgCreateDeployment{}, ModuleName + "/" + typeMsgCreateDeployment, nil)

I feel like the request was pretty obvious, I don't know why we're still talking about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Events have different formats (they use snake case) than amino registration, hence the difference.

I feel like the request was pretty obvious, I don't know why we're still talking about it.

I totally agree, idk why are you still nitpicking on these small things...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree, idk why are you still nitpicking on these small things...

because you resolved the conversation without saying why you didn't address it.

Copy link
Contributor

@boz boz Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why you think these two things need to have different formats?

The documentation for Type() says:

Returns a human-readable string for the message, intended for utilization within tags

It doesn't say anything about snake case.

Also, it doesn't look like RegisterConcrete() needs to be camel case. In fact, this was working fine before without it. Indeed, the go-amino examples have "anything can go in here if its unique"
in the main README.md:

amino.RegisterConcrete(&MyStruct3{}, "anythingcangoinhereifitsunique", nil)

In the future, please provide sources for your arguments. If you say that the documentation says it must be some way or another, provide a link to that documentation. In this case, it'd be the go-amio godoc for RegisterConcrete() and that Type() should return snake case. Nobody should need to hunt these sources down for you.

idk why are you still nitpicking on these small things...

This was an incredibly innocuous request - use one constant in two places so that the message type name is standardized.

cdc.RegisterConcrete(MsgCloseBid{}, ModuleName+"/msg-close-bid", nil)
cdc.RegisterConcrete(MsgCloseOrder{}, ModuleName+"/MsgCloseOrder", nil)
cdc.RegisterConcrete(MsgCreateBid{}, ModuleName+"/MsgCreateBid", nil)
cdc.RegisterConcrete(MsgCloseBid{}, ModuleName+"/MsgCloseBid", nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • use the constants defined in msgs.go.
  • use the original names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

OrderMatched OrderState = iota
// OrderClosed is used when state of order is closed
OrderClosed OrderState = iota
OrderMatched
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Closed is not the initial state.
  • Some linters will complain about this formatting. Why are you changing it to begin with?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the default value for uint8 is 0, that means that all default states would have been open

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@fedekunze fedekunze Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some linters will complain about this formatting.

which linters complain about this format?

Copy link
Contributor

@boz boz Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes of course I know the initial value for iota.

When orders are created they are open, hence that being the default state. closed is an end state.

I think it would be reasonable to not use 0 as a value at all to make sure things are always explicit. To do this, replace iota with iota + 1.

I don't remember what linter doesn't like it.

BidLost BidState = iota
// BidClosed is used when state of bid is closed
BidClosed BidState = iota
BidLost
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see reply

// LeaseActive is used when state of lease is active
LeaseActive
// LeaseInsufficientFunds is used when lease has insufficient funds
LeaseInsufficientFunds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again.

)

// Setup initializes a new SimApp. A Nop logger is set in SimApp.
func Setup(isCheckTx bool) *App {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used for anything other than tests? Does it need access to private fields or functions?

Not sure it should be here. If nothing else, call it SetupTest() and put it in a file that ends in _test.go so that it's only available during testing.

@boz
Copy link
Contributor

boz commented Mar 26, 2020

I think it'd be best to re-start this PR - create a fresh branch and include only the changes necessary for one isolated change - "support using app.App in simulation tests", for instance.

@fedekunze fedekunze closed this Mar 30, 2020
@fedekunze fedekunze mentioned this pull request Mar 31, 2020
12 tasks
@boz boz deleted the fedekunze/457-codebase-migration-refactor branch June 10, 2020 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DCS-5: Cosmos SDK Codebase Migration
4 participants