-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
feat: add core module API #12239
Changes from 36 commits
c59fef9
8251594
204cd7a
afcdbcd
964f481
80fb734
91c7b8f
40d493a
b432e37
963bbae
81049cd
68abaa7
8ade043
4de0773
024f366
b7e175b
33f99b2
8e0f531
3c6b412
25acd02
807c2e0
043b6fb
7f9b15c
da5e6c2
be1e98c
a6d2531
daac685
b2e0881
fb3a5e6
1081d45
f9d2522
3cbe6c5
dfe781d
2635c0b
6a6fc51
ac62df1
260077d
3ccd485
5328e62
002d9d2
b1ae3f9
f9a903d
481d107
6db3f0a
0438fdc
8b27a49
fdc9933
f5dd33a
0c28208
6e5b9c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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) | ||
} |
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 { | ||
ReadMessage(protoiface.MessageV1) error | ||
OpenReader(field string) (io.ReadCloser, error) | ||
ReadRawJSON() (json.RawMessage, error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is |
||
} | ||
|
||
type GenesisTarget interface { | ||
WriteMessage(protoiface.MessageV1) error | ||
OpenWriter(field string) (io.WriteCloser, error) | ||
WriteRawJSON(json.RawMessage) error | ||
} |
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an example with the |
||
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{} |
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 | ||
} |
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this represents an |
||
store.KVStoreService | ||
store.MemoryStoreService | ||
store.TransientStoreService | ||
event.Service | ||
blockinfo.Service | ||
gas.Service | ||
RootInterModuleClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
} |
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, | ||
}) | ||
} |
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 | ||
} |
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 |
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 | ||
} |
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 | ||
} |
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 |
There was a problem hiding this comment.
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