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 core module API #12239

Closed
wants to merge 50 commits into from
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c59fef9
feat: add core module API
aaronc Mar 9, 2022
8251594
docs
aaronc Mar 9, 2022
204cd7a
add full example setup
aaronc Mar 9, 2022
afcdbcd
docs
aaronc Mar 9, 2022
964f481
docs
aaronc Mar 9, 2022
80fb734
docs
aaronc Mar 9, 2022
91c7b8f
Merge branch 'master' into aaronc/core-api
aaronc Mar 9, 2022
40d493a
add extension resolver
aaronc Mar 10, 2022
b432e37
Merge remote-tracking branch 'origin/aaronc/core-api' into aaronc/cor…
aaronc Mar 10, 2022
963bbae
update proto
aaronc Mar 10, 2022
81049cd
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/core-api
aaronc Jun 13, 2022
68abaa7
revert
aaronc Jun 13, 2022
8ade043
updates
aaronc Jun 13, 2022
4de0773
docs
aaronc Jun 13, 2022
024f366
docs
aaronc Jun 13, 2022
b7e175b
Merge branch 'main' into aaronc/core-api
aaronc Jun 13, 2022
33f99b2
Merge branch 'main' into aaronc/core-api
tac0turtle Jun 14, 2022
8e0f531
Update core/blockinfo/service.go
aaronc Jun 14, 2022
3c6b412
Update core/blockinfo/service.go
aaronc Jun 14, 2022
25acd02
add ValidatorUpdateService
aaronc Jun 14, 2022
807c2e0
Merge remote-tracking branch 'origin/aaronc/core-api' into aaronc/cor…
aaronc Jun 14, 2022
043b6fb
Merge branch 'main' into aaronc/core-api
aaronc Jun 14, 2022
7f9b15c
updates
aaronc Aug 19, 2022
da5e6c2
updates
aaronc Aug 19, 2022
be1e98c
Merge remote-tracking branch 'origin/aaronc/core-api' into aaronc/cor…
aaronc Aug 19, 2022
a6d2531
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/core-api
aaronc Aug 19, 2022
daac685
revert
aaronc Aug 19, 2022
b2e0881
Update core/gas/meter.go
aaronc Aug 19, 2022
fb3a5e6
revert
aaronc Aug 19, 2022
1081d45
Apply suggestions from code review
aaronc Aug 19, 2022
f9d2522
Apply suggestions from code review
aaronc Aug 19, 2022
3cbe6c5
Merge remote-tracking branch 'origin/aaronc/core-api' into aaronc/cor…
aaronc Aug 19, 2022
dfe781d
API simplifications
aaronc Aug 29, 2022
2635c0b
event updates
aaronc Aug 29, 2022
6a6fc51
ADR-033 updates
aaronc Aug 29, 2022
ac62df1
updates client
aaronc Aug 29, 2022
260077d
WIP on example
aaronc Sep 8, 2022
3ccd485
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/core-api
aaronc Oct 10, 2022
5328e62
fix import
aaronc Oct 10, 2022
002d9d2
buf mod update
aaronc Oct 10, 2022
b1ae3f9
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/core-api
aaronc Oct 10, 2022
f9a903d
fix tests
aaronc Oct 10, 2022
481d107
revert
aaronc Oct 10, 2022
6db3f0a
remove empty file
aaronc Oct 10, 2022
0438fdc
Merge branch 'main' into aaronc/core-api
alexanderbez Oct 12, 2022
8b27a49
Merge branch 'main' into aaronc/core-api
aaronc Oct 12, 2022
fdc9933
updates
aaronc Oct 20, 2022
f5dd33a
update core API to extension interfaces
aaronc Oct 20, 2022
0c28208
Merge remote-tracking branch 'origin/aaronc/core-api' into aaronc/cor…
aaronc Oct 20, 2022
6e5b9c7
update core API to extension interfaces
aaronc Oct 20, 2022
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
21 changes: 21 additions & 0 deletions core/appmodule/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package appmodule

import (
"context"

"google.golang.org/protobuf/runtime/protoiface"
)

// EventListener is an empty interface to indicate an event listener type.
type EventListener interface{}

// AddEventListener adds an event listener for the provided type to the handler.
func AddEventListener[E protoiface.MessageV1](h *Handler, listener func(ctx context.Context, e E)) {
h.EventListeners = append(h.EventListeners, listener)
}

// AddEventInterceptor adds an event interceptor for the provided type to the handler which can
// return an error to interrupt state machine processing and revert uncommitted state changes.
func AddEventInterceptor[E protoiface.MessageV1](h *Handler, interceptor func(ctx context.Context, e E) error) {
h.EventListeners = append(h.EventListeners, interceptor)
}
20 changes: 20 additions & 0 deletions core/appmodule/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package appmodule

import (
"encoding/json"
"io"

"google.golang.org/protobuf/runtime/protoiface"
)

type GenesisSource interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: godocs on interfaces and their methods

ReadMessage(protoiface.MessageV1) error
OpenReader(field string) (io.ReadCloser, error)
ReadRawJSON() (json.RawMessage, error)
Copy link
Collaborator

Choose a reason for hiding this comment

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

is ReadMessage and ReadRawJSON going to read next record or all records? If latter, then we should have kind of an iterator pattern:.

}

type GenesisTarget interface {
WriteMessage(protoiface.MessageV1) error
OpenWriter(field string) (io.WriteCloser, error)
WriteRawJSON(json.RawMessage) error
}
62 changes: 62 additions & 0 deletions core/appmodule/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package appmodule

import (
"context"

"google.golang.org/grpc"

"github.com/cosmos/cosmos-sdk/depinject"
aaronc marked this conversation as resolved.
Show resolved Hide resolved
)

// Handler describes an ABCI app module handler. It can be injected into a
// depinject container as a one-per-module type (in the pointer variant).
aaronc marked this conversation as resolved.
Show resolved Hide resolved
type Handler struct {
// Services are the msg and query services for the module. Msg services
// must be annotated with the option cosmos.msg.v1.service = true.
Services []ServiceImpl

// BeginBlocker doesn't take or return any special arguments as this
// is the most stable across Tendermint versions and most common need
// for modules. Special parameters can be injected and/or returned
// using custom hooks that the app will provide which may vary from
// one Tendermint release to another.
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe you can mention some hooks here, or point where these hooks are defined?

Copy link
Member Author

@aaronc aaronc Aug 19, 2022

Choose a reason for hiding this comment

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

I added an example with the ValidatorUpdateService in this PR and described this more in the ADR

BeginBlocker func(context.Context) error

// EndBlocker doesn't take or return any special arguments as this
// is the most stable across Tendermint versions and most common need
// for modules. Special parameters can be injected and/or returned
// using custom hooks that the app will provide which may vary from
// one Tendermint release to another.
EndBlocker func(context.Context) error
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved

DefaultGenesis func(GenesisTarget)
ValidateGenesis func(GenesisSource) error
InitGenesis func(context.Context, GenesisSource) error
ExportGenesis func(context.Context, GenesisTarget)

EventListeners []EventListener

UpgradeHandlers []UpgradeHandler
}

// RegisterService registers a msg or query service. If the cosmos.msg.v1.service
// option is set to true on the service, then it is registered as a msg service,
// otherwise it is registered as a query service.
func (h *Handler) RegisterService(desc *grpc.ServiceDesc, impl interface{}) {
h.Services = append(h.Services, ServiceImpl{
Desc: desc,
Impl: impl,
})
}

// ServiceImpl describes a gRPC service implementation to be registered with
// grpc.ServiceRegistrar.
type ServiceImpl struct {
Desc *grpc.ServiceDesc
Impl interface{}
}

func (h *Handler) IsOnePerModuleType() {}

var _ depinject.OnePerModuleType = &Handler{}
var _ grpc.ServiceRegistrar = &Handler{}
23 changes: 23 additions & 0 deletions core/appmodule/intermodule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package appmodule

import "google.golang.org/grpc"

// InterModuleClient is an inter-module client as specified in ADR-033. It
// allows one module to send msg's and queries to other modules provided
// that the request is valid and can be properly authenticated.
type InterModuleClient interface {
grpc.ClientConnInterface

// Address is the ADR-028 address of this client against which messages will be authenticated.
Address() []byte
}

// RootInterModuleClient is the root inter-module client of a module which
// uses the ADR-028 root module address.
type RootInterModuleClient interface {
InterModuleClient

// DerivedClient returns an inter-module client for the ADR-028 derived
// module address for the provided key.
DerivedClient(key []byte) InterModuleClient
}
24 changes: 24 additions & 0 deletions core/appmodule/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package appmodule

import (
"cosmossdk.io/core/blockinfo"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/store"
)

// Service bundles all the core services provided by a compliant runtime
// implementation into a single services.
//
// NOTE: If new core services are provided, they shouldn't be added to this
// interface which would be API breaking, but rather a cosmossdk.io/core/appmodule/v2.Service
// should be created which extends this Service interface with new services.
type Service interface {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this represents an App?

store.KVStoreService
store.MemoryStoreService
store.TransientStoreService
event.Service
blockinfo.Service
gas.Service
RootInterModuleClient
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there value in creating these subservices separately?

In practice, for a smooth transition, I envision module developers (including us) just replacing sdk.Context with this bundle service. We can also remove a lot of core/*/service.go packages, and just have a single one.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah it might be possible to have one meta service. I think it is still nice to have clear separation of concerns and separate packages.

}
19 changes: 19 additions & 0 deletions core/appmodule/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package appmodule

import (
"context"

"google.golang.org/protobuf/reflect/protoreflect"
)

type UpgradeHandler struct {
FromModule protoreflect.FullName
Handler func(context.Context) error
}

func (h *Handler) RegisterUpgradeHandler(fromModule protoreflect.FullName, handler func(context.Context) error) {
h.UpgradeHandlers = append(h.UpgradeHandlers, UpgradeHandler{
FromModule: fromModule,
Handler: handler,
})
}
36 changes: 36 additions & 0 deletions core/blockinfo/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Package blockinfo provides an API for app modules to get basic
// information about blocks that is available against any underlying Tendermint
// core version (or other consensus layer that could be used in the future).
package blockinfo

import (
"context"
"time"
)

// Service is a type which retrieves basic block info from a context independent
// of any specific Tendermint core version. Modules which need a specific
// Tendermint header should use a different service and should expect a need
// to update whenever Tendermint makes any changes. blockinfo.Service is a
// core API type that should be provided by the runtime module being used to
// build an app via depinject.
type Service interface {
// GetBlockInfo returns the current block info for the context.
GetBlockInfo(ctx context.Context) BlockInfo
}

// BlockInfo represents basic block info independent of any specific Tendermint
// core version.
type BlockInfo struct {
// ChainID is the chain ID.
ChainID string

// Height is the current block height.
Height int64

// Time is the current block timestamp.
Time time.Time

// Hash is the current block hash.
Hash []byte
}
8 changes: 8 additions & 0 deletions core/compat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package core

// RuntimeCompatibilityVersion indicates what semantic version of the runtime
// module is required to support the full API exposed in this version of core.
// The runtime module that is loaded can emit an error or warning if the version
// specified here is greater than what that runtime can support and some
// features may not work as expected.
const RuntimeCompatibilityVersion = 1
36 changes: 36 additions & 0 deletions core/event/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Package event provides a basic API for app modules to emit events.
package event

import (
"context"

"google.golang.org/protobuf/runtime/protoiface"
)

// Service represents an event service which can retrieve and set an event manager in a context.
// event.Service is a core API type that should be provided by the runtime module being used to
// build an app via depinject.
type Service interface {
// GetManager returns the event manager for the context or a no-op event manager if one isn't attached.
GetEventManager(context.Context) Manager
}

// Manager represents an event manager.
type Manager interface {
// Emit emits events to both clients and state machine listeners. These events MUST be emitted deterministically
// and should be assumed to be part of blockchain consensus.
Emit(protoiface.MessageV1) error

// EmitLegacy emits legacy untyped events to clients only. These events do not need to be emitted deterministically
// and are not part of blockchain consensus.
EmitLegacy(eventType string, attrs ...LegacyEventAttribute) error

// EmitClientOnly emits events only to clients. These events do not need to be emitted deterministically
// and are not part of blockchain consensus.
EmitClientOnly(protoiface.MessageV1) error
}

// LegacyEventAttribute is a legacy (untyped) event attribute.
type LegacyEventAttribute struct {
Key, Value string
}
40 changes: 40 additions & 0 deletions core/gas/meter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Package gas provides a basic API for app modules to track gas usage.
package gas

import "context"

type Gas = uint64

// Service represents a gas service which can retrieve and set a gas meter in a context.
// gas.Service is a core API type that should be provided by the runtime module being used to
// build an app via depinject.
type Service interface {
// GetGasMeter returns the current transaction-level gas meter. A non-nil meter
// is always returned. When one is unavailable in the context an infinite gas meter
// will be returned.
GetGasMeter(context.Context) Meter

// GetBlockGasMeter returns the current block-level gas meter. A non-nil meter
// is always returned. When one is unavailable in the context an infinite gas meter
// will be returned.
GetBlockGasMeter(context.Context) Meter

// WithGasMeter returns a new context with the provided transaction-level gas meter.
WithGasMeter(ctx context.Context, meter Meter) context.Context

// WithBlockGasMeter returns a new context with the provided block-level gas meter.
WithBlockGasMeter(ctx context.Context, meter Meter) context.Context
}

// Meter represents a gas meter.
type Meter interface {
GasConsumed() Gas
GasConsumedToLimit() Gas
GasRemaining() Gas
Limit() Gas
ConsumeGas(amount Gas, descriptor string)
RefundGas(amount Gas, descriptor string)
IsPastLimit() bool
IsOutOfGas() bool
String() string
}
7 changes: 7 additions & 0 deletions core/internal/buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ managed:
enabled: true
go_package_prefix:
default: cosmossdk.io/core/internal
except:
- buf.build/googleapis/googleapis
- buf.build/cosmos/gogo-proto
- buf.build/cosmos/cosmos-proto
override:
buf.build/cosmos/cosmos-sdk: cosmossdk.io/api
plugins:
- name: go-pulsar
out: .
opt: paths=source_relative
- name: go-grpc
out: .
opt: paths=source_relative
23 changes: 23 additions & 0 deletions core/internal/buf.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by buf. DO NOT EDIT.
version: v1
deps:
- remote: buf.build
owner: cosmos
repository: cosmos-proto
branch: main
commit: 1935555c206d4afb9e94615dfd0fad31
- remote: buf.build
owner: cosmos
repository: cosmos-sdk
branch: main
commit: 4fcdf4f6bbd84e4694c32c1c2a9c8815
- remote: buf.build
owner: cosmos
repository: gogo-proto
branch: main
commit: bee5511075b7499da6178d9e4aaa628b
- remote: buf.build
owner: googleapis
repository: googleapis
branch: main
commit: 2c4adef7af3d4b74b8a224260cf10cb9
5 changes: 5 additions & 0 deletions core/internal/buf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ lint:
breaking:
ignore:
- testpb
deps:
aaronc marked this conversation as resolved.
Show resolved Hide resolved
- buf.build/cosmos/cosmos-proto
- buf.build/cosmos/gogo-proto
- buf.build/googleapis/googleapis
- buf.build/cosmos/cosmos-sdk
Loading