Skip to content

Commit

Permalink
feat!: ABCI 1.0 Integration (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez committed Dec 13, 2022
1 parent 5a73dde commit 48fe175
Show file tree
Hide file tree
Showing 308 changed files with 7,565 additions and 13,717 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Expand Up @@ -173,7 +173,7 @@ build, in which case we can fall back on `go mod tidy -v`.

## Protobuf

We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use in Cosmos SDK.
We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/cosmos/gogoproto) to generate code for use in Cosmos SDK.

For determinstic behavior around Protobuf tooling, everything is containerized using Docker. Make sure to have Docker installed on your machine, or head to [Docker's website](https://docs.docker.com/get-docker/) to install it.

Expand Down
70 changes: 19 additions & 51 deletions Makefile
Expand Up @@ -152,7 +152,7 @@ mocks: $(MOCKS_DIR)
$(mockgen_cmd) -source=types/module/module.go -package mocks -destination tests/mocks/types_module_module.go
$(mockgen_cmd) -source=types/invariant.go -package mocks -destination tests/mocks/types_invariant.go
$(mockgen_cmd) -source=types/router.go -package mocks -destination tests/mocks/types_router.go
$(mockgen_cmd) -package mocks -destination tests/mocks/grpc_server.go github.com/gogo/protobuf/grpc Server
$(mockgen_cmd) -package mocks -destination tests/mocks/grpc_server.go github.com/cosmos/gogoproto/grpc Server
$(mockgen_cmd) -package mocks -destination tests/mocks/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger
.PHONY: mocks

Expand Down Expand Up @@ -382,69 +382,41 @@ devdoc-update:
### Protobuf ###
###############################################################################

containerProtoVer=v0.2
containerProtoImage=tendermintdev/sdk-proto-gen:$(containerProtoVer)
containerProtoGen=cosmos-sdk-proto-gen-$(containerProtoVer)
containerProtoGenSwagger=cosmos-sdk-proto-gen-swagger-$(containerProtoVer)
containerProtoFmt=cosmos-sdk-proto-fmt-$(containerProtoVer)
protoVer=0.11.2
protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer)
protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)

proto-all: proto-format proto-lint proto-gen

proto-gen:
@echo "Generating Protobuf files"
@if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGen}$$"; then docker start -a $(containerProtoGen); else docker run --name $(containerProtoGen) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \
sh ./scripts/protocgen.sh; fi

# This generates the SDK's custom wrapper for google.protobuf.Any. It should only be run manually when needed
proto-gen-any:
@echo "Generating Protobuf Any"
$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) sh ./scripts/protocgen-any.sh
@$(protoImage) sh ./scripts/protocgen.sh

proto-swagger-gen:
@echo "Generating Protobuf Swagger"
@if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGenSwagger}$$"; then docker start -a $(containerProtoGenSwagger); else docker run --name $(containerProtoGenSwagger) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \
sh ./scripts/protoc-swagger-gen.sh; fi
@$(protoImage) sh ./scripts/protoc-swagger-gen.sh

proto-format:
@echo "Formatting Protobuf files"
@if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoFmt}$$"; then docker start -a $(containerProtoFmt); else docker run --name $(containerProtoFmt) -v $(CURDIR):/workspace --workdir /workspace tendermintdev/docker-build-proto \
find ./ -not -path "./third_party/*" -name "*.proto" -exec clang-format -i {} \; ; fi

@$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \;

proto-lint:
@$(DOCKER_BUF) lint --error-format=json
@$(protoImage) buf lint --error-format=json

proto-check-breaking:
@$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=v0.44.3x-osmo-v5

@$(protoImage) buf breaking --against $(HTTPS_GIT)#branch=osmosis-main

TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3
TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.37.0-rc1/proto/tendermint

TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto
TM_ABCI_TYPES = third_party/proto/tendermint/abci
TM_TYPES = third_party/proto/tendermint/types
TM_VERSION = third_party/proto/tendermint/version
TM_LIBS = third_party/proto/tendermint/libs/bits
TM_P2P = third_party/proto/tendermint/p2p

GOGO_PROTO_TYPES = third_party/proto/gogoproto
COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto
CONFIO_TYPES = third_party/proto/confio
TM_CRYPTO_TYPES = proto/tendermint/crypto
TM_ABCI_TYPES = proto/tendermint/abci
TM_TYPES = proto/tendermint/types
TM_VERSION = proto/tendermint/version
TM_LIBS = proto/tendermint/libs/bits
TM_P2P = proto/tendermint/p2p

proto-update-deps:
@mkdir -p $(GOGO_PROTO_TYPES)
@curl -sSL $(GOGO_PROTO_URL)/gogoproto/gogo.proto > $(GOGO_PROTO_TYPES)/gogo.proto

@mkdir -p $(COSMOS_PROTO_TYPES)
@curl -sSL $(COSMOS_PROTO_URL)/cosmos.proto > $(COSMOS_PROTO_TYPES)/cosmos.proto
@echo "Updating Protobuf dependencies"

## Importing of tendermint protobuf definitions currently requires the
## use of `sed` in order to build properly with cosmos-sdk's proto file layout
## (which is the standard Buf.build FILE_LAYOUT)
## Issue link: https://github.com/tendermint/tendermint/issues/5021
@mkdir -p $(TM_ABCI_TYPES)
@curl -sSL $(TM_URL)/abci/types.proto > $(TM_ABCI_TYPES)/types.proto

Expand All @@ -468,13 +440,9 @@ proto-update-deps:
@mkdir -p $(TM_P2P)
@curl -sSL $(TM_URL)/p2p/types.proto > $(TM_P2P)/types.proto

@mkdir -p $(CONFIO_TYPES)
@curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto
## insert go package option into proofs.proto file
## Issue link: https://github.com/confio/ics23/issues/32
@sed -i '4ioption go_package = "github.com/confio/ics23/go";' $(CONFIO_TYPES)/proofs.proto
$(DOCKER) run --rm -v $(CURDIR)/proto:/workspace --workdir /workspace $(protoImageName) buf mod update

.PHONY: proto-all proto-gen proto-gen-any proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps
.PHONY: proto-all proto-gen proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps

###############################################################################
### Localnet ###
Expand Down
92 changes: 72 additions & 20 deletions baseapp/abci.go
Expand Up @@ -11,7 +11,7 @@ import (
"syscall"
"time"

"github.com/gogo/protobuf/proto"
"github.com/cosmos/gogoproto/proto"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -43,6 +43,8 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// req.InitialHeight is 1 by default.
initHeader := tmproto.Header{ChainID: req.ChainId, Time: req.Time}

app.logger.Info("InitChain", "initialHeight", req.InitialHeight, "chainID", req.ChainId)

// If req.InitialHeight is > 1, then we set the initial version in the
// stores.
if req.InitialHeight > 1 {
Expand All @@ -54,9 +56,11 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
}
}

// initialize the deliver state and check state with a correct header
// initialize states with a correct header
app.setDeliverState(initHeader)
app.setCheckState(initHeader)
app.setPrepareProposalState(initHeader)
app.setProcessProposalState(initHeader)

if err := app.SetAppVersion(initialAppVersion); err != nil {
panic(err)
Expand All @@ -66,10 +70,10 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// done after the deliver state and context have been set as it's persisted
// to state.
if req.ConsensusParams != nil {
// When InitChain is called, the app version should either be absent and determined by the application
// or set to 0. Panic if it's not.
if req.ConsensusParams.Version != nil && req.ConsensusParams.Version.AppVersion != initialAppVersion {
panic(AppVersionError{Actual: req.ConsensusParams.Version.AppVersion, Initial: initialAppVersion})
// When InitChain is called, the app version should either be absent and
// determined by the application or set to 0. Panic if it's not.
if req.ConsensusParams.Version != nil && req.ConsensusParams.Version.App != initialAppVersion {
panic(AppVersionError{Actual: req.ConsensusParams.Version.App, Initial: initialAppVersion})
}

app.StoreConsensusParams(app.deliverState.ctx, req.ConsensusParams)
Expand Down Expand Up @@ -147,12 +151,6 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
}
}

// SetOption implements the ABCI interface.
func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) {
// TODO: Implement!
return
}

// FilterPeerByAddrPort filters peers by address/port.
func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
if app.addrPeerFilter != nil {
Expand Down Expand Up @@ -200,21 +198,20 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg

// add block gas meter
var gasMeter sdk.GasMeter
if maxGas := app.getMaximumBlockGas(app.deliverState.ctx); maxGas > 0 {
if maxGas := app.GetMaximumBlockGas(app.deliverState.ctx); maxGas > 0 {
gasMeter = sdk.NewGasMeter(maxGas)
} else {
gasMeter = sdk.NewInfiniteGasMeter()
}

// NOTE: header hash is not set in NewContext, so we manually set it here

app.deliverState.ctx = app.deliverState.ctx.
WithBlockGasMeter(gasMeter).
WithHeaderHash(req.Hash).
WithConsensusParams(app.GetConsensusParams(app.deliverState.ctx))

// we also set block gas meter to checkState in case the application needs to
// verify gas consumption during (Re)CheckTx
// We also set block gas meter to checkState in case the application needs to
// verify gas consumption during (Re)CheckTx.
if app.checkState != nil {
app.checkState.ctx = app.checkState.ctx.
WithBlockGasMeter(gasMeter).
Expand Down Expand Up @@ -250,6 +247,59 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
return res
}

// PrepareProposal implements the PrepareProposal ABCI method and returns a
// ResponsePrepareProposal object to the client. The PrepareProposal method is
// responsible for allowing the block proposer to perform application-dependent
// work in a block before proposing it.
//
// Transactions can be modified, removed, or added by the application. Since the
// application maintains its own local mempool, it will ignore the transactions
// provided to it in RequestPrepareProposal. Instead, it will determine which
// transactions to return based on the mempool's semantics and the MaxTxBytes
// provided by the client's request.
//
// Note, there is no need to execute the transactions for validity as they have
// already passed CheckTx.
//
// Ref: https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-060-abci-1.0.md
// Ref: https://github.com/tendermint/tendermint/blob/main/spec/abci/abci%2B%2B_basic_concepts.md
func (app *BaseApp) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
ctx := app.getContextForTx(runTxPrepareProposal, []byte{})
if app.prepareProposal == nil {
panic("PrepareProposal method not set")
}

return app.prepareProposal(ctx, req)
}

// ProcessProposal implements the ProcessProposal ABCI method and returns a
// ResponseProcessProposal object to the client. The ProcessProposal method is
// responsible for allowing execution of application-dependent work in a proposed
// block. Note, the application defines the exact implementation details of
// ProcessProposal. In general, the application must at the very least ensure
// that all transactions are valid. If all transactions are valid, then we inform
// Tendermint that the Status is ACCEPT. However, the application is also able
// to implement optimizations such as executing the entire proposed block
// immediately. It may even execute the block in parallel.
//
// Ref: https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-060-abci-1.0.md
// Ref: https://github.com/tendermint/tendermint/blob/main/spec/abci/abci%2B%2B_basic_concepts.md
func (app *BaseApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
if app.processProposal == nil {
panic("app.ProcessProposal is not set")
}

ctx := app.processProposalState.ctx.
WithVoteInfos(app.voteInfos).
WithBlockHeight(req.Height).
WithBlockTime(req.Time).
WithHeaderHash(req.Hash).
WithProposer(req.ProposerAddress).
WithConsensusParams(app.GetConsensusParams(app.processProposalState.ctx))

return app.processProposal(ctx, req)
}

// CheckTx implements the ABCI interface and executes a tx in CheckTx mode. In
// CheckTx mode, messages are not executed. This means messages are only validated
// and only the AnteHandler is executed. State is persisted to the BaseApp's
Expand Down Expand Up @@ -343,6 +393,8 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// NOTE: This is safe because Tendermint holds a lock on the mempool for
// Commit. Use the header from this latest block.
app.setCheckState(header)
app.setPrepareProposalState(header)
app.setProcessProposalState(header)

// empty/reset the deliver state
app.deliverState = nil
Expand Down Expand Up @@ -568,7 +620,7 @@ func (app *BaseApp) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.
}

func (app *BaseApp) handleQueryGRPC(handler GRPCQueryHandler, req abci.RequestQuery) abci.ResponseQuery {
ctx, err := app.createQueryContext(req.Height, req.Prove)
ctx, err := app.CreateQueryContext(req.Height, req.Prove)
if err != nil {
return sdkerrors.QueryResultWithDebug(err, app.trace)
}
Expand Down Expand Up @@ -614,9 +666,9 @@ func checkNegativeHeight(height int64) error {
return nil
}

// createQueryContext creates a new sdk.Context for a query, taking as args
// CreateQueryContext creates a new sdk.Context for a query, taking as args
// the block height and whether the query needs a proof or not.
func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, error) {
func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, error) {
if err := checkNegativeHeight(height); err != nil {
return sdk.Context{}, err
}
Expand Down Expand Up @@ -897,7 +949,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) abci.
return sdkerrors.QueryResultWithDebug(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no custom querier found for route %s", path[1]), app.trace)
}

ctx, err := app.createQueryContext(req.Height, req.Prove)
ctx, err := app.CreateQueryContext(req.Height, req.Prove)
if err != nil {
return sdkerrors.QueryResultWithDebug(err, app.trace)
}
Expand Down

0 comments on commit 48fe175

Please sign in to comment.