├── mlc_config.json ├── pkg ├── consts │ ├── protocol_consts.go │ └── app_consts.go └── builder │ ├── builder_options.go │ ├── test │ └── builder_test.go │ └── builder.go ├── docker ├── priv_validator_state.json ├── entrypoint.sh ├── Dockerfile_ephemeral └── Dockerfile.test ├── README.md ├── .gitignore ├── .golangci.yml ├── .markdownlint.yaml ├── third_party └── proto │ ├── tendermint │ ├── libs │ │ └── bits │ │ │ └── types.proto │ ├── crypto │ │ ├── keys.proto │ │ └── proof.proto │ ├── types │ │ ├── evidence.proto │ │ ├── validator.proto │ │ ├── params.proto │ │ └── types.proto │ ├── version │ │ └── types.proto │ └── abci │ │ └── types.proto │ ├── cosmos_proto │ └── cosmos.proto │ ├── google │ ├── api │ │ ├── annotations.proto │ │ ├── httpbody.proto │ │ └── http.proto │ └── protobuf │ │ └── any.proto │ ├── gogoproto │ └── gogo.proto │ └── confio │ └── proofs.proto ├── proto ├── buf.gen.gogo.yaml └── cosmos │ └── base │ └── query │ └── v1beta1 │ └── pagination.proto ├── cmd └── metro │ ├── main.go │ └── cmd │ └── root.go ├── scripts ├── test_cover.sh ├── protocgen.sh ├── localnet-blocks-test.sh └── single-node.sh ├── .github ├── dependabot.yml ├── CODEOWNERS ├── workflows │ ├── lint.yml │ ├── proto.yml │ ├── ci_release.yml │ └── test.yml └── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── app ├── genesis.go ├── encoding │ └── encoding.go ├── antedecorators.go ├── module.go └── export.go ├── buf.yaml ├── testutil ├── testfactory │ ├── txs.go │ └── utils.go ├── testnode │ ├── node_interaction_api.go │ ├── full_node_test.go │ ├── rpc_client.go │ ├── node_init.go │ └── full_node.go └── test_app.go ├── docs └── architecture │ └── adr-template.md ├── Makefile ├── go.mod └── LICENSE /mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "retryOn429": true 3 | } 4 | -------------------------------------------------------------------------------- /pkg/consts/protocol_consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | ChainIDSeparator = "|" 5 | ) 6 | -------------------------------------------------------------------------------- /docker/priv_validator_state.json: -------------------------------------------------------------------------------- 1 | { 2 | "height": "0", 3 | "round": 0, 4 | "step": 0 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # metro 2 | 3 | shared aggregator chain that accepts transactions from many different "lazy" chains and post them on Celestia 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/node_modules 2 | frontend/dist 3 | frontend/.cache 4 | secret.yml 5 | build 6 | coverage.txt 7 | .idea 8 | .vscode 9 | tools-stamp 10 | __debug_bin 11 | profile.out 12 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | modules-download-mode: readonly 4 | 5 | linters: 6 | enable: 7 | - exportloopref 8 | - gofumpt 9 | - misspell 10 | - revive 11 | - prealloc 12 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | "default": true # Default state for all rules 2 | "MD010": 3 | "code_blocks": false # Disable rule for hard tabs in code blocks 4 | "MD013": false # Disable rule for line length 5 | "MD033": false # Disable rule banning inline HTML 6 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/libs/bits/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.libs.bits; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/libs/bits"; 5 | 6 | message BitArray { 7 | int64 bits = 1; 8 | repeated uint64 elems = 2; 9 | } 10 | -------------------------------------------------------------------------------- /proto/buf.gen.gogo.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: gocosmos 4 | out: .. 5 | opt: plugins=interfacetype+grpc,Mgoogle/protobuf/any.proto=github.com/histolabs/metro/codec/types # yamllint disable-line rule:line-length 6 | - name: grpc-gateway 7 | out: .. 8 | opt: logtostderr=true,allow_colon_final_segments=true 9 | -------------------------------------------------------------------------------- /cmd/metro/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/histolabs/metro/app" 7 | "github.com/histolabs/metro/cmd/metro/cmd" 8 | 9 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 10 | ) 11 | 12 | func main() { 13 | rootCmd := cmd.NewRootCmd() 14 | if err := svrcmd.Execute(rootCmd, cmd.EnvPrefix, app.DefaultNodeHome); err != nil { 15 | os.Exit(1) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scripts/test_cover.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PKGS=$(go list ./... | grep -v '/simapp') 5 | 6 | set -e 7 | echo "mode: atomic" > coverage.txt 8 | for pkg in ${PKGS[@]}; do 9 | go test -v -timeout 30m -race -test.short -coverprofile=profile.out -covermode=atomic "$pkg" 10 | if [ -f profile.out ]; then 11 | tail -n +2 profile.out >> coverage.txt; 12 | rm profile.out 13 | fi 14 | done 15 | -------------------------------------------------------------------------------- /third_party/proto/cosmos_proto/cosmos.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package cosmos_proto; 3 | 4 | import "google/protobuf/descriptor.proto"; 5 | 6 | option go_package = "github.com/regen-network/cosmos-proto"; 7 | 8 | extend google.protobuf.MessageOptions { 9 | string interface_type = 93001; 10 | 11 | string implements_interface = 93002; 12 | } 13 | 14 | extend google.protobuf.FieldOptions { 15 | string accepts_interface = 93001; 16 | } 17 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/crypto/keys.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.crypto; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | // PublicKey defines the keys available for use with Tendermint Validators 9 | message PublicKey { 10 | option (gogoproto.compare) = true; 11 | option (gogoproto.equal) = true; 12 | 13 | oneof sum { 14 | bytes ed25519 = 1; 15 | bytes secp256k1 = 2; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/evidence.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "tendermint/types/types.proto"; 8 | 9 | 10 | // EvidenceData contains any evidence of malicious wrong-doing by validators 11 | message EvidenceData { 12 | repeated tendermint.types.Evidence evidence = 1 [(gogoproto.nullable) = false]; 13 | bytes hash = 2; 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: docker 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: github-actions 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 10 13 | - package-ecosystem: gomod 14 | directory: "/" 15 | schedule: 16 | interval: daily 17 | open-pull-requests-limit: 10 18 | labels: 19 | - automerge 20 | - dependencies 21 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS: 2 | 3 | # Everything goes through the following "global owners" by default. Unless a later 4 | # match takes precedence, the "global owners" will be requested for review when 5 | # someone opens a PR. Note that the last matching pattern takes precedence, so 6 | # global owners are only requested if there isn't a more specific codeowner 7 | # specified below. For this reason, the global owners are often repeated in 8 | # directory-level definitions. 9 | 10 | # global owners 11 | * @evan-forbes 12 | -------------------------------------------------------------------------------- /app/genesis.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // The genesis state of the blockchain is represented here as a map of raw json 8 | // messages key'd by a identifier string. 9 | // The identifier is used to determine which module genesis information belongs 10 | // to so it may be appropriately routed during init chain. 11 | // Within this application default genesis information is retrieved from 12 | // the ModuleBasicManager which populates json from each BasicModule 13 | // object provided to it during init. 14 | type GenesisState map[string]json.RawMessage 15 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script creates the necessary files before starting Celestia-appd 4 | 5 | # only create the priv_validator_state.json if it doesn't exist and the command is start 6 | if [[ $1 == "start" && ! -f ${METRO_HOME}/data/priv_validator_state.json ]] 7 | then 8 | mkdir ${METRO_HOME}/data # it is alright if it fails, the script will continue executing 9 | cat < ${METRO_HOME}/data/priv_validator_state.json 10 | { 11 | "height": "0", 12 | "round": 0, 13 | "step": 0 14 | } 15 | EOF 16 | fi 17 | 18 | /bin/metro --home ${METRO_HOME} $@ 19 | -------------------------------------------------------------------------------- /pkg/consts/app_consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import ( 4 | sdkmath "cosmossdk.io/math" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | const ( 9 | AccountAddressPrefix = "metro" 10 | Name = "metro" 11 | // BondDenom defines the native staking token denomination. 12 | BondDenom = "utick" 13 | // BondDenomAlias defines an alias for BondDenom. 14 | BondDenomAlias = "microtick" 15 | // DisplayDenom defines the name, symbol, and display value of the Celestia token. 16 | DisplayDenom = "TICK" 17 | ) 18 | 19 | func NativeDenom(amount sdkmath.Int) sdk.Coin { 20 | return sdk.NewCoin(BondDenom, amount) 21 | } 22 | -------------------------------------------------------------------------------- /buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1beta1 2 | 3 | build: 4 | roots: 5 | - proto 6 | - third_party/proto 7 | excludes: 8 | - third_party/proto/google/protobuf 9 | lint: 10 | use: 11 | - DEFAULT 12 | - COMMENTS 13 | - FILE_LOWER_SNAKE_CASE 14 | except: 15 | - UNARY_RPC 16 | - COMMENT_FIELD 17 | - SERVICE_SUFFIX 18 | - PACKAGE_VERSION_SUFFIX 19 | - RPC_REQUEST_STANDARD_NAME 20 | ignore: 21 | - tendermint 22 | - gogoproto 23 | - cosmos_proto 24 | - google 25 | - confio 26 | breaking: 27 | use: 28 | - FILE 29 | ignore: 30 | - tendermint 31 | - gogoproto 32 | - cosmos_proto 33 | - google 34 | - confio 35 | -------------------------------------------------------------------------------- /testutil/testfactory/txs.go: -------------------------------------------------------------------------------- 1 | package testfactory 2 | 3 | import ( 4 | mrand "math/rand" 5 | 6 | "github.com/tendermint/tendermint/types" 7 | ) 8 | 9 | func GenerateRandomlySizedTxs(count, maxSize int) types.Txs { 10 | txs := make(types.Txs, count) 11 | for i := 0; i < count; i++ { 12 | size := mrand.Intn(maxSize) 13 | if size == 0 { 14 | size = 1 15 | } 16 | txs[i] = GenerateRandomTxs(1, size)[0] 17 | } 18 | return txs 19 | } 20 | 21 | func GenerateRandomTxs(count, size int) types.Txs { 22 | txs := make(types.Txs, count) 23 | for i := 0; i < count; i++ { 24 | tx := make([]byte, size) 25 | _, err := mrand.Read(tx) 26 | if err != nil { 27 | panic(err) 28 | } 29 | txs[i] = tx 30 | } 31 | return txs 32 | } 33 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/version/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.version; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | // App includes the protocol and software version for the application. 9 | // This information is included in ResponseInfo. The App.Protocol can be 10 | // updated in ResponseEndBlock. 11 | message App { 12 | uint64 protocol = 1; 13 | string software = 2; 14 | } 15 | 16 | // Consensus captures the consensus rules for processing a block in the blockchain, 17 | // including all blockchain data structures and the rules of the application's 18 | // state transition machine. 19 | message Consensus { 20 | option (gogoproto.equal) = true; 21 | 22 | uint64 block = 1; 23 | uint64 app = 2; 24 | } 25 | -------------------------------------------------------------------------------- /scripts/protocgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | protoc_gen_gocosmos() { 6 | if ! grep "github.com/gogo/protobuf => github.com/regen-network/protobuf" go.mod &>/dev/null ; then 7 | echo -e "\tPlease run this command from somewhere inside the cosmos-sdk folder." 8 | return 1 9 | fi 10 | } 11 | 12 | protoc_gen_gocosmos 13 | 14 | cd proto 15 | proto_dirs=$(find . -path ./cosmos -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 16 | for dir in $proto_dirs; do 17 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do 18 | if grep "option go_package" $file &> /dev/null ; then 19 | buf generate --template buf.gen.gogo.yaml $file 20 | fi 21 | done 22 | done 23 | 24 | cd .. 25 | 26 | # move proto files to the right places 27 | cp -r github.com/histolabs/metro/* ./ 28 | rm -rf github.com 29 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/validator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "tendermint/crypto/keys.proto"; 8 | 9 | message ValidatorSet { 10 | repeated Validator validators = 1; 11 | Validator proposer = 2; 12 | int64 total_voting_power = 3; 13 | } 14 | 15 | message Validator { 16 | bytes address = 1; 17 | tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; 18 | int64 voting_power = 3; 19 | int64 proposer_priority = 4; 20 | } 21 | 22 | message SimpleValidator { 23 | tendermint.crypto.PublicKey pub_key = 1; 24 | int64 voting_power = 2; 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | # Lint runs golangci-lint over the entire cosmos-sdk repository 3 | # This workflow is run on every pull request and push to main 4 | # The `golangci` will pass without running if no *.{go, mod, sum} files have 5 | # been changed. 6 | on: 7 | workflow_call: 8 | 9 | jobs: 10 | golangci: 11 | name: golangci-lint 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 8 14 | steps: 15 | - uses: actions/setup-go@v3 16 | with: 17 | go-version: 1.19 18 | - uses: actions/checkout@v3 19 | - uses: technote-space/get-diff-action@v6.1.2 20 | with: 21 | PATTERNS: | 22 | **/**.go 23 | go.mod 24 | go.sum 25 | - uses: golangci/golangci-lint-action@v3.3.1 26 | with: 27 | version: v1.47.2 28 | args: --timeout 10m 29 | github-token: ${{ secrets.github_token }} 30 | if: env.GIT_DIFF 31 | -------------------------------------------------------------------------------- /docker/Dockerfile_ephemeral: -------------------------------------------------------------------------------- 1 | # stage 1 Generate metro Binary 2 | FROM golang:1.19-alpine as builder 3 | 4 | RUN apk update && \ 5 | apk upgrade && \ 6 | apk --no-cache add make gcc musl-dev 7 | 8 | ENV HOME /metro 9 | 10 | COPY / ${HOME} 11 | WORKDIR ${HOME} 12 | 13 | RUN make build 14 | 15 | # stage 2 16 | FROM alpine 17 | 18 | RUN apk update && \ 19 | apk upgrade && \ 20 | apk --no-cache add curl jq bash 21 | 22 | ENV HOME /metro 23 | COPY --from=builder ${HOME}/build/metro ${HOME}/metro 24 | COPY docker/priv_validator_state.json ${HOME}/data/priv_validator_state.json 25 | WORKDIR $HOME 26 | 27 | # p2p, rpc and prometheus port 28 | EXPOSE 26656 26657 1317 9090 29 | 30 | # This allows us to always set the --home directory using an env 31 | # var while still capturing all arguments passed at runtime 32 | ENTRYPOINT [ "/bin/bash", "-c", "exec ./metro \ 33 | --home ${HOME} \ 34 | \"${@}\"", "--" ] 35 | # Default command to run if no arguments are passed 36 | CMD ["--help"] 37 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/crypto/proof.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.crypto; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | message Proof { 9 | int64 total = 1; 10 | int64 index = 2; 11 | bytes leaf_hash = 3; 12 | repeated bytes aunts = 4; 13 | } 14 | 15 | message ValueOp { 16 | // Encoded in ProofOp.Key. 17 | bytes key = 1; 18 | 19 | // To encode in ProofOp.Data 20 | Proof proof = 2; 21 | } 22 | 23 | message DominoOp { 24 | string key = 1; 25 | string input = 2; 26 | string output = 3; 27 | } 28 | 29 | // ProofOp defines an operation used for calculating Merkle root 30 | // The data could be arbitrary format, providing necessary data 31 | // for example neighbouring node hash 32 | message ProofOp { 33 | string type = 1; 34 | bytes key = 2; 35 | bytes data = 3; 36 | } 37 | 38 | // ProofOps is Merkle proof defined by the list of ProofOps 39 | message ProofOps { 40 | repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; 41 | } 42 | -------------------------------------------------------------------------------- /third_party/proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /docker/Dockerfile.test: -------------------------------------------------------------------------------- 1 | # Simple usage with a mounted data directory: 2 | # > docker build -t metro . 3 | # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.metro:/root/.metro -v ~/.metrocli:/root/.metrocli metro metro init 4 | # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.metro:/root/.metro -v ~/.metrocli:/root/.metrocli metro metro start 5 | FROM golang:1.19-alpine AS build-env 6 | 7 | # Set up dependencies 8 | ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 9 | 10 | # Set working directory for the build 11 | WORKDIR /go/src/github.com/histolabs/metro 12 | 13 | # Add source files 14 | COPY . . 15 | 16 | # Install minimum necessary dependencies, build Cosmos SDK, remove packages 17 | RUN apk add --no-cache $PACKAGES && \ 18 | make install 19 | 20 | # Final image 21 | FROM alpine:edge 22 | 23 | # Install ca-certificates 24 | RUN apk add --update ca-certificates 25 | WORKDIR /root 26 | 27 | # Copy over binaries from the build-env 28 | COPY --from=build-env /go/bin/metro /usr/bin/metro 29 | 30 | COPY ./scripts/single-node.sh . 31 | 32 | EXPOSE 26657 33 | 34 | ENTRYPOINT [ "./single-node.sh" ] 35 | # NOTE: to run this image, docker run -d -p 26657:26657 ./single-node.sh {{chain_id}} {{genesis_account}} 36 | -------------------------------------------------------------------------------- /.github/workflows/proto.yml: -------------------------------------------------------------------------------- 1 | name: Protobuf 2 | # Protobuf runs buf (https://buf.build/) lint and generate 3 | # This workflow is only run when a .proto file has been changed 4 | on: 5 | pull_request: 6 | paths: 7 | - "**.proto" 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 5 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: lint 15 | run: make proto-lint 16 | proto-gen: 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 5 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: "Check protobuf generated code matches committed code" 22 | # yamllint disable 23 | run: | 24 | set -euo pipefail 25 | 26 | make proto-gen 27 | 28 | if ! git diff --stat --exit-code ; then 29 | echo ">> ERROR:" 30 | echo ">>" 31 | echo ">> Protobuf generated code requires update (either tools or .proto files may have changed)." 32 | echo ">> Ensure your tools are up-to-date, re-run 'make proto-gen' and update this PR." 33 | echo ">>" 34 | exit 1 35 | fi 36 | # yamllint enable 37 | 38 | # add this back when we start using versioning 39 | # breakage: 40 | # runs-on: ubuntu-latest 41 | # steps: 42 | # - uses: actions/checkout@v3 43 | # - name: check-breakage 44 | # run: make proto-check-breaking 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us squash bugs! 3 | title: "" 4 | labels: ["bug"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | IMPORTANT: Prior to opening a bug report, check if it affects one of the 11 | core modules and if it's eligible for a bug bounty on `SECURITY.md`. 12 | Bugs that are not submitted through the appropriate channels won't 13 | receive any bounty. 14 | 15 | - type: textarea 16 | id: summary 17 | attributes: 18 | label: Summary of Bug 19 | description: Concisely describe the issue. 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: version 25 | attributes: 26 | label: Version 27 | description: git commit hash or release version 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | id: repro 33 | attributes: 34 | label: Steps to Reproduce 35 | description: > 36 | What commands in order should someone run to reproduce your problem? 37 | validations: 38 | required: true 39 | 40 | - type: checkboxes 41 | id: admin 42 | attributes: 43 | label: For Admin Use 44 | description: (do not edit) 45 | options: 46 | - label: Not duplicate issue 47 | - label: Appropriate labels applied 48 | - label: Appropriate contributors tagged 49 | - label: Contributor assigned/self-assigned 50 | -------------------------------------------------------------------------------- /scripts/localnet-blocks-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CNT=0 4 | ITER=$1 5 | SLEEP=$2 6 | NUMBLOCKS=$3 7 | NODEADDR=$4 8 | 9 | if [ -z "$1" ]; then 10 | echo "Need to input number of iterations to run..." 11 | exit 1 12 | fi 13 | 14 | if [ -z "$2" ]; then 15 | echo "Need to input number of seconds to sleep between iterations" 16 | exit 1 17 | fi 18 | 19 | if [ -z "$3" ]; then 20 | echo "Need to input block height to declare completion..." 21 | exit 1 22 | fi 23 | 24 | if [ -z "$4" ]; then 25 | echo "Need to input node address to poll..." 26 | exit 1 27 | fi 28 | 29 | docker_containers=( $(docker ps -q -f name=metro --format='{{.Names}}') ) 30 | 31 | while [ ${CNT} -lt $ITER ]; do 32 | curr_block=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height') 33 | 34 | if [ ! -z ${curr_block} ] ; then 35 | echo "Number of Blocks: ${curr_block}" 36 | fi 37 | 38 | if [ ! -z ${curr_block} ] && [ ${curr_block} -gt ${NUMBLOCKS} ]; then 39 | echo "Number of blocks reached. Success!" 40 | exit 0 41 | fi 42 | 43 | # Emulate network chaos: 44 | # 45 | # Every 10 blocks, pick a random container and restart it. 46 | if ! ((${CNT} % 10)); then 47 | rand_container=${docker_containers["$[RANDOM % ${#docker_containers[@]}]"]}; 48 | echo "Restarting random docker container ${rand_container}" 49 | docker restart ${rand_container} &>/dev/null & 50 | fi 51 | 52 | let CNT=CNT+1 53 | sleep $SLEEP 54 | done 55 | 56 | echo "Timeout reached. Failure!" 57 | exit 1 58 | -------------------------------------------------------------------------------- /scripts/single-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit -o nounset 4 | 5 | CHAINID="private" 6 | 7 | # Build genesis file incl account for passed address 8 | coins="1000000000000000utick" 9 | metro init $CHAINID --chain-id $CHAINID 10 | metro keys add validator --keyring-backend="test" 11 | # this won't work because the some proto types are decalared twice and the logs output to stdout (dependency hell involving iavl) 12 | metro add-genesis-account $(metro keys show validator -a --keyring-backend="test") $coins 13 | metro gentx validator 5000000000utick \ 14 | --keyring-backend="test" \ 15 | --chain-id $CHAINID \ 16 | --orchestrator-address $(metro keys show validator -a --keyring-backend="test") \ 17 | --evm-address 0x966e6f22781EF6a6A82BBB4DB3df8E225DfD9488 # private key: da6ed55cb2894ac2c9c10209c09de8e8b9d109b910338d5bf3d747a7e1fc9eb9 18 | 19 | metro collect-gentxs 20 | 21 | # Set proper defaults and change ports 22 | # If you encounter: `sed: -I or -i may not be used with stdin` on MacOS you can mitigate by installing gnu-sed 23 | # https://gist.github.com/andre3k1/e3a1a7133fded5de5a9ee99c87c6fa0d?permalink_comment_id=3082272#gistcomment-3082272 24 | sed -i'.bak' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:26657"#g' ~/.metro/config/config.toml 25 | sed -i'.bak' 's/timeout_commit = "1s"/timeout_commit = "1s"/g' ~/.metro/config/config.toml 26 | sed -i'.bak' 's/timeout_propose = "1s"/timeout_propose = "1s"/g' ~/.metro/config/config.toml 27 | sed -i'.bak' 's/index_all_keys = false/index_all_keys = true/g' ~/.metro/config/config.toml 28 | sed -i'.bak' 's/mode = "full"/mode = "validator"/g' ~/.metro/config/config.toml 29 | 30 | # Start the app 31 | metro start 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Create a proposal to request a feature 3 | title: "<title>" 4 | labels: ["enhancement"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ✰ Thanks for opening an issue! ✰ 11 | Before smashing the submit button please fill in the template. 12 | Word of caution: poorly thought-out proposals may be rejected without 13 | deliberation. 14 | 15 | - type: textarea 16 | id: summary 17 | attributes: 18 | label: Summary 19 | description: Short, concise description of the proposed feature. 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: problem 25 | attributes: 26 | label: Problem Definition 27 | description: | 28 | Why do we need this feature? 29 | What problems may be addressed by introducing this feature? 30 | What benefits does the SDK stand to gain by including this feature? 31 | Are there any disadvantages of including this feature? 32 | validations: 33 | required: true 34 | 35 | - type: textarea 36 | id: proposal 37 | attributes: 38 | label: Proposal 39 | description: Detailed description of requirements of implementation. 40 | validations: 41 | required: true 42 | 43 | - type: checkboxes 44 | id: admin 45 | attributes: 46 | label: For Admin Use 47 | description: (do not edit) 48 | options: 49 | - label: Not duplicate issue 50 | - label: Appropriate labels applied 51 | - label: Appropriate contributors tagged 52 | - label: Contributor assigned/self-assigned 53 | -------------------------------------------------------------------------------- /app/encoding/encoding.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/client" 5 | "github.com/cosmos/cosmos-sdk/codec" 6 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 7 | "github.com/cosmos/cosmos-sdk/std" 8 | "github.com/cosmos/cosmos-sdk/x/auth/tx" 9 | ) 10 | 11 | type ModuleRegister interface { 12 | RegisterLegacyAminoCodec(*codec.LegacyAmino) 13 | RegisterInterfaces(codectypes.InterfaceRegistry) 14 | } 15 | 16 | // Config specifies the concrete encoding types to use for a given app. 17 | // This is provided for compatibility between protobuf and amino implementations. 18 | type Config struct { 19 | InterfaceRegistry codectypes.InterfaceRegistry 20 | Codec codec.Codec 21 | TxConfig client.TxConfig 22 | Amino *codec.LegacyAmino 23 | } 24 | 25 | // MakeConfig creates an encoding config for the app. 26 | func MakeConfig(regs ...ModuleRegister) Config { 27 | // create the codec 28 | amino := codec.NewLegacyAmino() 29 | interfaceRegistry := codectypes.NewInterfaceRegistry() 30 | 31 | // register the standard types from the sdk 32 | std.RegisterLegacyAminoCodec(amino) 33 | std.RegisterInterfaces(interfaceRegistry) 34 | 35 | // register specific modules 36 | for _, reg := range regs { 37 | reg.RegisterInterfaces(interfaceRegistry) 38 | reg.RegisterLegacyAminoCodec(amino) 39 | } 40 | 41 | // create the final configuration 42 | marshaler := codec.NewProtoCodec(interfaceRegistry) 43 | txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) 44 | 45 | return Config{ 46 | InterfaceRegistry: interfaceRegistry, 47 | Codec: marshaler, 48 | TxConfig: txCfg, 49 | Amino: amino, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /proto/cosmos/base/query/v1beta1/pagination.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package cosmos.base.query.v1beta1; 3 | 4 | option go_package = "github.com/cosmos/cosmos-sdk/types/query"; 5 | 6 | // PageRequest is to be embedded in gRPC request messages for efficient 7 | // pagination. Ex: 8 | // 9 | // message SomeRequest { 10 | // Foo some_parameter = 1; 11 | // PageRequest pagination = 2; 12 | // } 13 | message PageRequest { 14 | // key is a value returned in PageResponse.next_key to begin 15 | // querying the next page most efficiently. Only one of offset or key 16 | // should be set. 17 | bytes key = 1; 18 | 19 | // offset is a numeric offset that can be used when key is unavailable. 20 | // It is less efficient than using key. Only one of offset or key should 21 | // be set. 22 | uint64 offset = 2; 23 | 24 | // limit is the total number of results to be returned in the result page. 25 | // If left empty it will default to a value to be set by each app. 26 | uint64 limit = 3; 27 | 28 | // count_total is set to true to indicate that the result set should include 29 | // a count of the total number of items available for pagination in UIs. 30 | // count_total is only respected when offset is used. It is ignored when key 31 | // is set. 32 | bool count_total = 4; 33 | } 34 | 35 | // PageResponse is to be embedded in gRPC response messages where the 36 | // corresponding request message has used PageRequest. 37 | // 38 | // message SomeResponse { 39 | // repeated Bar results = 1; 40 | // PageResponse page = 2; 41 | // } 42 | message PageResponse { 43 | // next_key is the key to be passed to PageRequest.key to 44 | // query the next page most efficiently 45 | bytes next_key = 1; 46 | 47 | // total is total number of results available if PageRequest.count_total 48 | // was set, its value is undefined otherwise 49 | uint64 total = 2; 50 | } 51 | -------------------------------------------------------------------------------- /app/antedecorators.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | "github.com/cosmos/cosmos-sdk/x/auth/ante" 7 | ) 8 | 9 | // NewAnteDecorators returns an AnteDecorators that check and increment 10 | // sequence numbers, checks signatures & account numbers, and deducts fees from 11 | // the first signer. 12 | func NewAnteDecorators(options ante.HandlerOptions) ([]sdk.AnteDecorator, error) { 13 | if options.AccountKeeper == nil { 14 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for ante builder") 15 | } 16 | 17 | if options.BankKeeper == nil { 18 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for ante builder") 19 | } 20 | 21 | if options.SignModeHandler == nil { 22 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") 23 | } 24 | 25 | anteDecorators := []sdk.AnteDecorator{ 26 | ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first 27 | ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), 28 | ante.NewValidateBasicDecorator(), 29 | ante.NewTxTimeoutHeightDecorator(), 30 | ante.NewValidateMemoDecorator(options.AccountKeeper), 31 | ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), 32 | ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker), 33 | ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators 34 | ante.NewValidateSigCountDecorator(options.AccountKeeper), 35 | ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), 36 | ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), 37 | ante.NewIncrementSequenceDecorator(options.AccountKeeper), 38 | } 39 | 40 | return anteDecorators, nil 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/ci_release.yml: -------------------------------------------------------------------------------- 1 | name: CI and Release 2 | 3 | # Trigger on push events to main (i.e. merges), pushing new semantic version 4 | # tags, all PRs, and manual triggers 5 | on: 6 | push: 7 | branches: 8 | - main 9 | tags: 10 | - "v[0-9]+.[0-9]+.[0-9]+" 11 | - "v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+" 12 | - "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" 13 | - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" 14 | pull_request: 15 | workflow_dispatch: 16 | # Inputs the workflow accepts. 17 | inputs: 18 | version: 19 | # Friendly description to be shown in the UI instead of 'name' 20 | description: "Semver type of new version (major / minor / patch)" 21 | # Input has to be provided for the workflow to run 22 | required: true 23 | type: choice 24 | options: 25 | - patch 26 | - minor 27 | - major 28 | 29 | jobs: 30 | # Dockerfile Linting 31 | hadolint: 32 | uses: histolabs/.github/.github/workflows/reusable_dockerfile_lint.yml@main # yamllint disable-line rule:line-length 33 | 34 | yamllint: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | - uses: histolabs/.github/.github/actions/yamllint@main 39 | 40 | markdown-lint: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v3 44 | - uses: histolabs/.github/.github/actions/markdown-lint@main 45 | 46 | lint: 47 | uses: ./.github/workflows/lint.yml 48 | 49 | test: 50 | needs: lint 51 | uses: ./.github/workflows/test.yml 52 | 53 | # Make a release if this is a manually trigger job, i.e. workflow_dispatch 54 | release: 55 | needs: [hadolint, yamllint, markdown-lint, lint, test] 56 | runs-on: ubuntu-latest 57 | if: ${{ github.event_name == 'workflow_dispatch' }} 58 | permissions: "write-all" 59 | steps: 60 | - uses: actions/checkout@v3 61 | - name: Version Release 62 | uses: histolabs/.github/.github/actions/version-release@main 63 | with: 64 | github-token: ${{secrets.GITHUB_TOKEN}} 65 | version-bump: ${{inputs.version}} 66 | 67 | -------------------------------------------------------------------------------- /testutil/testnode/node_interaction_api.go: -------------------------------------------------------------------------------- 1 | package testnode 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "time" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | ) 10 | 11 | type Context struct { 12 | rootCtx context.Context 13 | client.Context 14 | } 15 | 16 | func (c *Context) GoContext() context.Context { 17 | return c.rootCtx 18 | } 19 | 20 | // LatestHeight returns the latest height of the network or an error if the 21 | // query fails. 22 | func (c *Context) LatestHeight() (int64, error) { 23 | status, err := c.Client.Status(c.GoContext()) 24 | if err != nil { 25 | return 0, err 26 | } 27 | 28 | return status.SyncInfo.LatestBlockHeight, nil 29 | } 30 | 31 | // WaitForHeightWithTimeout is the same as WaitForHeight except the caller can 32 | // provide a custom timeout. 33 | func (c *Context) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { 34 | ticker := time.NewTicker(time.Second) 35 | defer ticker.Stop() 36 | 37 | ctx, cancel := context.WithTimeout(c.rootCtx, t) 38 | defer cancel() 39 | 40 | var latestHeight int64 41 | for { 42 | select { 43 | case <-ctx.Done(): 44 | return latestHeight, errors.New("timeout exceeded waiting for block") 45 | case <-ticker.C: 46 | latestHeight, err := c.LatestHeight() 47 | if err != nil { 48 | return 0, err 49 | } 50 | if latestHeight >= h { 51 | return latestHeight, nil 52 | } 53 | } 54 | } 55 | } 56 | 57 | // WaitForHeight performs a blocking check where it waits for a block to be 58 | // committed after a given block. If that height is not reached within a timeout, 59 | // an error is returned. Regardless, the latest height queried is returned. 60 | func (c *Context) WaitForHeight(h int64) (int64, error) { 61 | return c.WaitForHeightWithTimeout(h, 10*time.Second) 62 | } 63 | 64 | // WaitForNextBlock waits for the next block to be committed, returning an error 65 | // upon failure. 66 | func (c *Context) WaitForNextBlock() error { 67 | lastBlock, err := c.LatestHeight() 68 | if err != nil { 69 | return err 70 | } 71 | 72 | _, err = c.WaitForHeight(lastBlock + 1) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | return err 78 | } 79 | -------------------------------------------------------------------------------- /testutil/testnode/full_node_test.go: -------------------------------------------------------------------------------- 1 | package testnode 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | "github.com/stretchr/testify/suite" 9 | tmrand "github.com/tendermint/tendermint/libs/rand" 10 | coretypes "github.com/tendermint/tendermint/rpc/core/types" 11 | ) 12 | 13 | type IntegrationTestSuite struct { 14 | suite.Suite 15 | 16 | cleanups []func() error 17 | accounts []string 18 | cctx Context 19 | } 20 | 21 | func (s *IntegrationTestSuite) SetupSuite() { 22 | if testing.Short() { 23 | s.T().Skip("skipping test in unit-tests or race-detector mode.") 24 | } 25 | 26 | s.T().Log("setting up integration test suite") 27 | require := s.Require() 28 | 29 | // we create an arbitrary number of funded accounts 30 | for i := 0; i < 300; i++ { 31 | s.accounts = append(s.accounts, tmrand.Str(9)) 32 | } 33 | 34 | genState, kr, err := DefaultGenesisState(s.accounts...) 35 | require.NoError(err) 36 | 37 | tmNode, app, cctx, err := New(s.T(), DefaultParams(), DefaultTendermintConfig(), false, genState, kr) 38 | require.NoError(err) 39 | 40 | cctx, stopNode, err := StartNode(tmNode, cctx) 41 | require.NoError(err) 42 | s.cleanups = append(s.cleanups, stopNode) 43 | 44 | cctx, cleanupGRPC, err := StartGRPCServer(app, DefaultAppConfig(), cctx) 45 | require.NoError(err) 46 | s.cleanups = append(s.cleanups, cleanupGRPC) 47 | 48 | s.cctx = cctx 49 | } 50 | 51 | func (s *IntegrationTestSuite) TearDownSuite() { 52 | s.T().Log("tearing down integration test suite") 53 | for _, c := range s.cleanups { 54 | err := c() 55 | require.NoError(s.T(), err) 56 | } 57 | } 58 | 59 | func (s *IntegrationTestSuite) Test_Liveness() { 60 | require := s.Require() 61 | err := s.cctx.WaitForNextBlock() 62 | require.NoError(err) 63 | // check that we're actually able to set the consensus params 64 | var params *coretypes.ResultConsensusParams 65 | // this query can be flaky with fast block times, so we repeat it multiple 66 | // times in attempt to increase the probability of it working 67 | for i := 0; i < 20; i++ { 68 | params, err = s.cctx.Client.ConsensusParams(context.TODO(), nil) 69 | if err != nil || params == nil { 70 | continue 71 | } 72 | break 73 | } 74 | require.NotNil(params) 75 | require.Equal(int64(1), params.ConsensusParams.Block.TimeIotaMs) 76 | _, err = s.cctx.WaitForHeight(40) 77 | require.NoError(err) 78 | } 79 | 80 | func TestIntegrationTestSuite(t *testing.T) { 81 | suite.Run(t, new(IntegrationTestSuite)) 82 | } 83 | -------------------------------------------------------------------------------- /testutil/testnode/rpc_client.go: -------------------------------------------------------------------------------- 1 | package testnode 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | srvconfig "github.com/cosmos/cosmos-sdk/server/config" 8 | srvgrpc "github.com/cosmos/cosmos-sdk/server/grpc" 9 | srvtypes "github.com/cosmos/cosmos-sdk/server/types" 10 | "github.com/tendermint/tendermint/node" 11 | "github.com/tendermint/tendermint/rpc/client/local" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/credentials/insecure" 14 | ) 15 | 16 | // StartNode starts the tendermint node along with a local core rpc client. The 17 | // rpc is returned via the client.Context. The function returned should be 18 | // called during cleanup to teardown the node, core client, along with canceling 19 | // the internal context.Context in the returned Context. 20 | func StartNode(tmNode *node.Node, cctx Context) (Context, func() error, error) { 21 | if err := tmNode.Start(); err != nil { 22 | return cctx, func() error { return nil }, err 23 | } 24 | 25 | coreClient := local.New(tmNode) 26 | 27 | cctx.Context = cctx.WithClient(coreClient) 28 | goCtx, cancel := context.WithCancel(context.Background()) 29 | cctx.rootCtx = goCtx 30 | cleanup := func() error { 31 | cancel() 32 | err := tmNode.Stop() 33 | if err != nil { 34 | return err 35 | } 36 | tmNode.Wait() 37 | return nil 38 | } 39 | 40 | return cctx, cleanup, nil 41 | } 42 | 43 | // StartGRPCServer starts the grpc server using the provided application and 44 | // config. A grpc client connection to that server is also added to the client 45 | // context. The returned function should be used to shutdown the server. 46 | func StartGRPCServer(app srvtypes.Application, appCfg *srvconfig.Config, cctx Context) (Context, func() error, error) { 47 | emptycleanup := func() error { return nil } 48 | // Add the tx service in the gRPC router. 49 | app.RegisterTxService(cctx.Context) 50 | 51 | // Add the tendermint queries service in the gRPC router. 52 | app.RegisterTendermintService(cctx.Context) 53 | 54 | grpcSrv, err := srvgrpc.StartGRPCServer(cctx.Context, app, appCfg.GRPC) 55 | if err != nil { 56 | return Context{}, emptycleanup, err 57 | } 58 | 59 | nodeGRPCAddr := strings.Replace(appCfg.GRPC.Address, "0.0.0.0", "localhost", 1) 60 | conn, err := grpc.Dial(nodeGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) 61 | if err != nil { 62 | return Context{}, emptycleanup, err 63 | } 64 | 65 | cctx.Context = cctx.WithGRPCClient(conn) 66 | 67 | return cctx, func() error { 68 | grpcSrv.Stop() 69 | return nil 70 | }, nil 71 | } 72 | 73 | // DefaultAppConfig wraps the default config described in the server 74 | func DefaultAppConfig() *srvconfig.Config { 75 | return srvconfig.DefaultConfig() 76 | } 77 | -------------------------------------------------------------------------------- /docs/architecture/adr-template.md: -------------------------------------------------------------------------------- 1 | # ADR {ADR-NUMBER}: {TITLE} 2 | 3 | ## Changelog 4 | 5 | - {date}: {changelog} 6 | 7 | ## Context 8 | 9 | > This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high-level idea behind the solution. 10 | 11 | ## Alternative Approaches 12 | 13 | > This section contains information about alternative options that are considered before making a decision. It should contain an explanation of why the alternative approach(es) were not chosen. 14 | 15 | ## Decision 16 | 17 | > This section records the decision that was made. 18 | > It is best to record as much info as possible from the discussion that happened. This aids in not having to go back to the Pull Request to get the needed information. 19 | 20 | ## Detailed Design 21 | 22 | > This section does not need to be filled in at the start of the ADR but must be completed prior to the merging of the implementation. 23 | > 24 | > Here are some common questions that get answered as part of the detailed design: 25 | > 26 | > - What are the user requirements? 27 | > 28 | > - What systems will be affected? 29 | > 30 | > - What new data structures are needed, and what data structures will be changed? 31 | > 32 | > - What new APIs will be needed, and what APIs will be changed? 33 | > 34 | > - What are the efficiency considerations (time/space)? 35 | > 36 | > - What are the expected access patterns (load/throughput)? 37 | > 38 | > - Are there any logging, monitoring, or observability needs? 39 | > 40 | > - Are there any security considerations? 41 | > 42 | > - Are there any privacy considerations? 43 | > 44 | > - How will the changes be tested? 45 | > 46 | > - If the change is large, how will the changes be broken up for ease of review? 47 | > 48 | > - Will these changes require a breaking (major) release? 49 | > 50 | > - Does this change require coordination with the SDK or others? 51 | 52 | ## Status 53 | 54 | > A decision may be "proposed" if it hasn't been agreed upon yet, or "accepted" once it is agreed upon. Once the ADR has been implemented mark the ADR as "implemented". If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement. 55 | 56 | {Deprecated|Proposed|Accepted|Declined} 57 | 58 | ## Consequences 59 | 60 | > This section describes the consequences, after applying the decision. All consequences should be summarized here, not just the "positive" ones. 61 | 62 | ### Positive 63 | 64 | ### Negative 65 | 66 | ### Neutral 67 | 68 | ## References 69 | 70 | > Are there any relevant PR comments, issues that led up to this, or articles referenced for why we made the given design choice? If so link them here! 71 | 72 | - {reference link} 73 | -------------------------------------------------------------------------------- /testutil/testfactory/utils.go: -------------------------------------------------------------------------------- 1 | package testfactory 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | 7 | "github.com/cosmos/cosmos-sdk/client" 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | "github.com/cosmos/cosmos-sdk/crypto/hd" 10 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 11 | "github.com/cosmos/cosmos-sdk/simapp" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 14 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 15 | tmrand "github.com/tendermint/tendermint/libs/rand" 16 | rpctypes "github.com/tendermint/tendermint/rpc/core/types" 17 | ) 18 | 19 | const ( 20 | // nolint:lll 21 | TestAccName = "test-account" 22 | testMnemo = `ramp soldier connect gadget domain mutual staff unusual first midnight iron good deputy wage vehicle mutual spike unlock rocket delay hundred script tumble choose` 23 | bondDenom = "utick" 24 | ) 25 | 26 | func QueryWithoutProof(clientCtx client.Context, hashHexStr string) (*rpctypes.ResultTx, error) { 27 | hash, err := hex.DecodeString(hashHexStr) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | node, err := clientCtx.GetNode() 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return node.Tx(context.Background(), hash, false) 38 | } 39 | 40 | func GenerateKeyring(accounts ...string) keyring.Keyring { 41 | cdc := simapp.MakeTestEncodingConfig().Codec 42 | kb := keyring.NewInMemory(cdc) 43 | 44 | for _, acc := range accounts { 45 | _, _, err := kb.NewMnemonic(acc, keyring.English, "", "", hd.Secp256k1) 46 | if err != nil { 47 | panic(err) 48 | } 49 | } 50 | 51 | _, err := kb.NewAccount(TestAccName, testMnemo, "1234", "", hd.Secp256k1) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | return kb 57 | } 58 | 59 | func RandomAddress() sdk.Address { 60 | name := tmrand.Str(6) 61 | kr := GenerateKeyring(name) 62 | rec, err := kr.Key(name) 63 | if err != nil { 64 | panic(err) 65 | } 66 | addr, err := rec.GetAddress() 67 | if err != nil { 68 | panic(err) 69 | } 70 | return addr 71 | } 72 | 73 | func FundKeyringAccounts(cdc codec.Codec, accounts ...string) (keyring.Keyring, []banktypes.Balance, []authtypes.GenesisAccount) { 74 | kr := GenerateKeyring(accounts...) 75 | genAccounts := make([]authtypes.GenesisAccount, len(accounts)) 76 | genBalances := make([]banktypes.Balance, len(accounts)) 77 | 78 | for i, acc := range accounts { 79 | rec, err := kr.Key(acc) 80 | if err != nil { 81 | panic(err) 82 | } 83 | 84 | addr, err := rec.GetAddress() 85 | if err != nil { 86 | panic(err) 87 | } 88 | 89 | balances := sdk.NewCoins( 90 | sdk.NewCoin(bondDenom, sdk.NewInt(99999999999999999)), 91 | ) 92 | 93 | genBalances[i] = banktypes.Balance{Address: addr.String(), Coins: balances.Sort()} 94 | genAccounts[i] = authtypes.NewBaseAccount(addr, nil, 0, 0) 95 | } 96 | return kr, genBalances, genAccounts 97 | } 98 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/duration.proto"; 8 | 9 | option (gogoproto.equal_all) = true; 10 | 11 | // ConsensusParams contains consensus critical parameters that determine the 12 | // validity of blocks. 13 | message ConsensusParams { 14 | BlockParams block = 1 [(gogoproto.nullable) = false]; 15 | EvidenceParams evidence = 2 [(gogoproto.nullable) = false]; 16 | ValidatorParams validator = 3 [(gogoproto.nullable) = false]; 17 | VersionParams version = 4 [(gogoproto.nullable) = false]; 18 | } 19 | 20 | // BlockParams contains limits on the block size. 21 | message BlockParams { 22 | // Max block size, in bytes. 23 | // Note: must be greater than 0 24 | int64 max_bytes = 1; 25 | // Max gas per block. 26 | // Note: must be greater or equal to -1 27 | int64 max_gas = 2; 28 | // Minimum time increment between consecutive blocks (in milliseconds) If the 29 | // block header timestamp is ahead of the system clock, decrease this value. 30 | // 31 | // Not exposed to the application. 32 | int64 time_iota_ms = 3; 33 | } 34 | 35 | // EvidenceParams determine how we handle evidence of malfeasance. 36 | message EvidenceParams { 37 | // Max age of evidence, in blocks. 38 | // 39 | // The basic formula for calculating this is: MaxAgeDuration / {average block 40 | // time}. 41 | int64 max_age_num_blocks = 1; 42 | 43 | // Max age of evidence, in time. 44 | // 45 | // It should correspond with an app's "unbonding period" or other similar 46 | // mechanism for handling [Nothing-At-Stake 47 | // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). 48 | google.protobuf.Duration max_age_duration = 2 49 | [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; 50 | 51 | // This sets the maximum size of total evidence in bytes that can be committed in a single block. 52 | // and should fall comfortably under the max block bytes. 53 | // Default is 1048576 or 1MB 54 | int64 max_bytes = 3; 55 | } 56 | 57 | // ValidatorParams restrict the public key types validators can use. 58 | // NOTE: uses ABCI pubkey naming, not Amino names. 59 | message ValidatorParams { 60 | option (gogoproto.populate) = true; 61 | option (gogoproto.equal) = true; 62 | 63 | repeated string pub_key_types = 1; 64 | } 65 | 66 | // VersionParams contains the ABCI application version. 67 | message VersionParams { 68 | option (gogoproto.populate) = true; 69 | option (gogoproto.equal) = true; 70 | 71 | uint64 app_version = 1; 72 | } 73 | 74 | // HashedParams is a subset of ConsensusParams. 75 | // 76 | // It is hashed into the Header.ConsensusHash. 77 | message HashedParams { 78 | int64 block_max_bytes = 1; 79 | int64 block_max_gas = 2; 80 | } 81 | -------------------------------------------------------------------------------- /app/module.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/x/bank" 9 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 10 | "github.com/cosmos/cosmos-sdk/x/crisis" 11 | crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" 12 | "github.com/cosmos/cosmos-sdk/x/mint" 13 | minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" 14 | "github.com/cosmos/cosmos-sdk/x/staking" 15 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 16 | "github.com/histolabs/metro/pkg/consts" 17 | ) 18 | 19 | // bankModule defines a custom wrapper around the x/bank module's AppModuleBasic 20 | // implementation to provide custom default genesis state. 21 | type bankModule struct { 22 | bank.AppModuleBasic 23 | } 24 | 25 | // DefaultGenesis returns custom x/bank module genesis state. 26 | func (bankModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { 27 | metadata := banktypes.Metadata{ 28 | Description: "The native staking token of the Celestia network.", 29 | Base: consts.BondDenom, 30 | Name: consts.DisplayDenom, 31 | Display: consts.DisplayDenom, 32 | Symbol: consts.DisplayDenom, 33 | DenomUnits: []*banktypes.DenomUnit{ 34 | { 35 | Denom: consts.BondDenom, 36 | Exponent: 0, 37 | Aliases: []string{ 38 | consts.BondDenomAlias, 39 | }, 40 | }, 41 | { 42 | Denom: consts.DisplayDenom, 43 | Exponent: 6, 44 | Aliases: []string{}, 45 | }, 46 | }, 47 | } 48 | 49 | genState := banktypes.DefaultGenesisState() 50 | genState.DenomMetadata = append(genState.DenomMetadata, metadata) 51 | 52 | return cdc.MustMarshalJSON(genState) 53 | } 54 | 55 | // stakingModule wraps the x/staking module in order to overwrite specific 56 | // ModuleManager APIs. 57 | type stakingModule struct { 58 | staking.AppModuleBasic 59 | } 60 | 61 | // DefaultGenesis returns custom x/staking module genesis state. 62 | func (stakingModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { 63 | params := stakingtypes.DefaultParams() 64 | params.BondDenom = consts.BondDenom 65 | 66 | return cdc.MustMarshalJSON(&stakingtypes.GenesisState{ 67 | Params: params, 68 | }) 69 | } 70 | 71 | type crisisModule struct { 72 | crisis.AppModuleBasic 73 | } 74 | 75 | // DefaultGenesis returns custom x/crisis module genesis state. 76 | func (crisisModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { 77 | return cdc.MustMarshalJSON(&crisistypes.GenesisState{ 78 | ConstantFee: sdk.NewCoin(consts.BondDenom, sdk.NewInt(1000)), 79 | }) 80 | } 81 | 82 | type mintModule struct { 83 | mint.AppModuleBasic 84 | } 85 | 86 | // DefaultGenesis returns custom x/mint module genesis state. 87 | func (mintModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { 88 | genState := minttypes.DefaultGenesisState() 89 | genState.Params.MintDenom = consts.BondDenom 90 | 91 | return cdc.MustMarshalJSON(genState) 92 | } 93 | -------------------------------------------------------------------------------- /third_party/proto/google/api/httpbody.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 16 | syntax = "proto3"; 17 | 18 | package google.api; 19 | 20 | import "google/protobuf/any.proto"; 21 | 22 | option cc_enable_arenas = true; 23 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; 24 | option java_multiple_files = true; 25 | option java_outer_classname = "HttpBodyProto"; 26 | option java_package = "com.google.api"; 27 | option objc_class_prefix = "GAPI"; 28 | 29 | // Message that represents an arbitrary HTTP body. It should only be used for 30 | // payload formats that can't be represented as JSON, such as raw binary or 31 | // an HTML page. 32 | // 33 | // 34 | // This message can be used both in streaming and non-streaming API methods in 35 | // the request as well as the response. 36 | // 37 | // It can be used as a top-level request field, which is convenient if one 38 | // wants to extract parameters from either the URL or HTTP template into the 39 | // request fields and also want access to the raw HTTP body. 40 | // 41 | // Example: 42 | // 43 | // message GetResourceRequest { 44 | // // A unique request id. 45 | // string request_id = 1; 46 | // 47 | // // The raw HTTP body is bound to this field. 48 | // google.api.HttpBody http_body = 2; 49 | // } 50 | // 51 | // service ResourceService { 52 | // rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); 53 | // rpc UpdateResource(google.api.HttpBody) returns 54 | // (google.protobuf.Empty); 55 | // } 56 | // 57 | // Example with streaming methods: 58 | // 59 | // service CaldavService { 60 | // rpc GetCalendar(stream google.api.HttpBody) 61 | // returns (stream google.api.HttpBody); 62 | // rpc UpdateCalendar(stream google.api.HttpBody) 63 | // returns (stream google.api.HttpBody); 64 | // } 65 | // 66 | // Use of this type only changes how the request and response bodies are 67 | // handled, all other features will continue to work unchanged. 68 | message HttpBody { 69 | // The HTTP Content-Type header value specifying the content type of the body. 70 | string content_type = 1; 71 | 72 | // The HTTP request/response body as raw binary. 73 | bytes data = 2; 74 | 75 | // Application specific response metadata. Must be set in the first response 76 | // for streaming APIs. 77 | repeated google.protobuf.Any extensions = 3; 78 | } -------------------------------------------------------------------------------- /pkg/builder/builder_options.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | sdkclient "github.com/cosmos/cosmos-sdk/client" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | "github.com/cosmos/cosmos-sdk/types/tx" 7 | authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" 8 | ) 9 | 10 | type TxBuilderOption func(builder sdkclient.TxBuilder) sdkclient.TxBuilder 11 | 12 | func SetGasLimit(limit uint64) TxBuilderOption { 13 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 14 | builder.SetGasLimit(limit) 15 | return builder 16 | } 17 | } 18 | 19 | func SetFeeAmount(fees sdk.Coins) TxBuilderOption { 20 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 21 | builder.SetFeeAmount(fees) 22 | return builder 23 | } 24 | } 25 | 26 | func SetMemo(memo string) TxBuilderOption { 27 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 28 | builder.SetMemo(memo) 29 | return builder 30 | } 31 | } 32 | 33 | func SetFeePayer(feePayer sdk.AccAddress) TxBuilderOption { 34 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 35 | builder.SetFeePayer(feePayer) 36 | return builder 37 | } 38 | } 39 | 40 | func SetTip(tip *tx.Tip) TxBuilderOption { 41 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 42 | builder.SetTip(tip) 43 | return builder 44 | } 45 | } 46 | 47 | func SetTimeoutHeight(height uint64) TxBuilderOption { 48 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 49 | builder.SetTimeoutHeight(height) 50 | return builder 51 | } 52 | } 53 | 54 | func SetFeeGranter(feeGranter sdk.AccAddress) TxBuilderOption { 55 | return func(builder sdkclient.TxBuilder) sdkclient.TxBuilder { 56 | builder.SetFeeGranter(feeGranter) 57 | return builder 58 | } 59 | } 60 | 61 | // InheritTxConfig sets all of the accessible configurations from a given tx 62 | // into a a give client.TxBuilder 63 | func InheritTxConfig(builder sdkclient.TxBuilder, tx authsigning.Tx) sdkclient.TxBuilder { 64 | if gas := tx.GetGas(); gas != 0 { 65 | builder.SetGasLimit(gas) 66 | } 67 | 68 | if feeAmmount := tx.GetFee(); !feeAmmount.AmountOf("utia").Equal(sdk.NewInt(0)) { 69 | builder.SetFeeAmount(tx.GetFee()) 70 | } 71 | 72 | if memo := tx.GetMemo(); memo != "" { 73 | builder.SetMemo(tx.GetMemo()) 74 | } 75 | 76 | if tip := tx.GetTip(); tip != nil { 77 | builder.SetTip(tip) 78 | } 79 | 80 | if timeoutHeight := tx.GetTimeoutHeight(); timeoutHeight != 0 { 81 | builder.SetTimeoutHeight(timeoutHeight) 82 | } 83 | 84 | signers := tx.GetSigners() 85 | // Note: if there are multiple signers in a PFB, then this could create an 86 | // invalid signature. This is not an issue at this time because we currently 87 | // ignore pfbs with multiple signers 88 | if len(signers) == 1 { 89 | if feePayer := tx.FeeGranter(); !feePayer.Equals(signers[0]) { 90 | builder.SetFeeGranter(tx.FeeGranter()) 91 | } 92 | } 93 | 94 | if feeGranter := tx.FeeGranter(); !feeGranter.Empty() { 95 | builder.SetFeeGranter(tx.FeeGranter()) 96 | } 97 | 98 | return builder 99 | } 100 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') 4 | COMMIT := $(shell git log -1 --format='%H') 5 | DOCKER := $(shell which docker) 6 | DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf 7 | IMAGE := ghcr.io/tendermint/docker-build-proto:latest 8 | DOCKER_PROTO_BUILDER := docker run -v $(shell pwd):/workspace --workdir /workspace $(IMAGE) 9 | 10 | # process linker flags 11 | ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=metro \ 12 | -X github.com/cosmos/cosmos-sdk/version.AppName=metro \ 13 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ 14 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ 15 | -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" 16 | ldflags += $(LDFLAGS) 17 | 18 | BUILD_FLAGS := -ldflags '$(ldflags)' 19 | 20 | all: install 21 | 22 | install: go.sum 23 | @echo "--> Installing metro" 24 | @go install -mod=readonly $(BUILD_FLAGS) ./cmd/metro 25 | 26 | go.sum: mod 27 | @echo "--> Verifying dependencies have expected content" 28 | GO111MODULE=on go mod verify 29 | 30 | mod: 31 | @echo "--> Updating go.mod" 32 | @go mod tidy 33 | 34 | pre-build: 35 | @echo "--> Fetching latest git tags" 36 | @git fetch --tags 37 | 38 | build: mod 39 | @go install github.com/gobuffalo/packr/v2/packr2@latest 40 | @cd ./cmd/metro && packr2 41 | @mkdir -p build/ 42 | @go build $(BUILD_FLAGS) -o build/ ./cmd/metro 43 | @packr2 clean 44 | @go mod tidy 45 | 46 | proto-gen: 47 | @echo "--> Generating Protobuf files" 48 | $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen:v0.7 sh ./scripts/protocgen.sh 49 | .PHONY: proto-gen 50 | 51 | proto-lint: 52 | @echo "--> Linting Protobuf files" 53 | @$(DOCKER_BUF) lint --error-format=json 54 | .PHONY: proto-lint 55 | 56 | proto-format: 57 | @echo "--> Formatting Protobuf files" 58 | @$(DOCKER_PROTO_BUILDER) find . -name '*.proto' -path "./proto/*" -exec clang-format -i {} \; 59 | .PHONY: proto-format 60 | 61 | build-docker: 62 | @echo "--> Building Docker image" 63 | $(DOCKER) build -t histolabs/metro -f docker/Dockerfile . 64 | .PHONY: build-docker 65 | 66 | lint: 67 | @echo "--> Running golangci-lint" 68 | @golangci-lint run 69 | @echo "--> Running markdownlint" 70 | @markdownlint --config .markdownlint.yaml '**/*.md' 71 | .PHONY: lint 72 | 73 | fmt: 74 | @echo "--> Running golangci-lint --fix" 75 | @golangci-lint run --fix 76 | @echo "--> Running markdownlint --fix" 77 | @markdownlint --fix --quiet --config .markdownlint.yaml . 78 | .PHONY: fmt 79 | 80 | test: 81 | @echo "--> Running unit tests" 82 | @go test -mod=readonly ./... 83 | .PHONY: test 84 | 85 | test-all: test-race test-cover 86 | 87 | test-race: 88 | @echo "--> Running tests with -race" 89 | @VERSION=$(VERSION) go test -mod=readonly -race -test.short ./... 90 | .PHONY: test-race 91 | 92 | test-cover: 93 | @echo "--> Generating coverage.txt" 94 | @export VERSION=$(VERSION); bash -x scripts/test_cover.sh 95 | .PHONY: test-cover 96 | 97 | benchmark: 98 | @echo "--> Running tests with -bench" 99 | @go test -mod=readonly -bench=. ./... 100 | .PHONY: benchmark 101 | -------------------------------------------------------------------------------- /third_party/proto/gogoproto/gogo.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers for Go with Gadgets 2 | // 3 | // Copyright (c) 2013, The GoGo Authors. All rights reserved. 4 | // http://github.com/gogo/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | syntax = "proto2"; 30 | package gogoproto; 31 | 32 | import "google/protobuf/descriptor.proto"; 33 | 34 | option java_package = "com.google.protobuf"; 35 | option java_outer_classname = "GoGoProtos"; 36 | option go_package = "github.com/gogo/protobuf/gogoproto"; 37 | 38 | extend google.protobuf.EnumOptions { 39 | optional bool goproto_enum_prefix = 62001; 40 | optional bool goproto_enum_stringer = 62021; 41 | optional bool enum_stringer = 62022; 42 | optional string enum_customname = 62023; 43 | optional bool enumdecl = 62024; 44 | } 45 | 46 | extend google.protobuf.EnumValueOptions { 47 | optional string enumvalue_customname = 66001; 48 | } 49 | 50 | extend google.protobuf.FileOptions { 51 | optional bool goproto_getters_all = 63001; 52 | optional bool goproto_enum_prefix_all = 63002; 53 | optional bool goproto_stringer_all = 63003; 54 | optional bool verbose_equal_all = 63004; 55 | optional bool face_all = 63005; 56 | optional bool gostring_all = 63006; 57 | optional bool populate_all = 63007; 58 | optional bool stringer_all = 63008; 59 | optional bool onlyone_all = 63009; 60 | 61 | optional bool equal_all = 63013; 62 | optional bool description_all = 63014; 63 | optional bool testgen_all = 63015; 64 | optional bool benchgen_all = 63016; 65 | optional bool marshaler_all = 63017; 66 | optional bool unmarshaler_all = 63018; 67 | optional bool stable_marshaler_all = 63019; 68 | 69 | optional bool sizer_all = 63020; 70 | 71 | optional bool goproto_enum_stringer_all = 63021; 72 | optional bool enum_stringer_all = 63022; 73 | 74 | optional bool unsafe_marshaler_all = 63023; 75 | optional bool unsafe_unmarshaler_all = 63024; 76 | 77 | optional bool goproto_extensions_map_all = 63025; 78 | optional bool goproto_unrecognized_all = 63026; 79 | optional bool gogoproto_import = 63027; 80 | optional bool protosizer_all = 63028; 81 | optional bool compare_all = 63029; 82 | optional bool typedecl_all = 63030; 83 | optional bool enumdecl_all = 63031; 84 | 85 | optional bool goproto_registration = 63032; 86 | optional bool messagename_all = 63033; 87 | 88 | optional bool goproto_sizecache_all = 63034; 89 | optional bool goproto_unkeyed_all = 63035; 90 | } 91 | 92 | extend google.protobuf.MessageOptions { 93 | optional bool goproto_getters = 64001; 94 | optional bool goproto_stringer = 64003; 95 | optional bool verbose_equal = 64004; 96 | optional bool face = 64005; 97 | optional bool gostring = 64006; 98 | optional bool populate = 64007; 99 | optional bool stringer = 67008; 100 | optional bool onlyone = 64009; 101 | 102 | optional bool equal = 64013; 103 | optional bool description = 64014; 104 | optional bool testgen = 64015; 105 | optional bool benchgen = 64016; 106 | optional bool marshaler = 64017; 107 | optional bool unmarshaler = 64018; 108 | optional bool stable_marshaler = 64019; 109 | 110 | optional bool sizer = 64020; 111 | 112 | optional bool unsafe_marshaler = 64023; 113 | optional bool unsafe_unmarshaler = 64024; 114 | 115 | optional bool goproto_extensions_map = 64025; 116 | optional bool goproto_unrecognized = 64026; 117 | 118 | optional bool protosizer = 64028; 119 | optional bool compare = 64029; 120 | 121 | optional bool typedecl = 64030; 122 | 123 | optional bool messagename = 64033; 124 | 125 | optional bool goproto_sizecache = 64034; 126 | optional bool goproto_unkeyed = 64035; 127 | } 128 | 129 | extend google.protobuf.FieldOptions { 130 | optional bool nullable = 65001; 131 | optional bool embed = 65002; 132 | optional string customtype = 65003; 133 | optional string customname = 65004; 134 | optional string jsontag = 65005; 135 | optional string moretags = 65006; 136 | optional string casttype = 65007; 137 | optional string castkey = 65008; 138 | optional string castvalue = 65009; 139 | 140 | optional bool stdtime = 65010; 141 | optional bool stdduration = 65011; 142 | optional bool wktpointer = 65012; 143 | 144 | optional string castrepeated = 65013; 145 | } 146 | -------------------------------------------------------------------------------- /pkg/builder/test/builder_test.go: -------------------------------------------------------------------------------- 1 | package builder_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | "time" 7 | 8 | "github.com/histolabs/metro/app" 9 | "github.com/histolabs/metro/app/encoding" 10 | "github.com/histolabs/metro/pkg/builder" 11 | "github.com/histolabs/metro/pkg/consts" 12 | "github.com/histolabs/metro/testutil/testfactory" 13 | "github.com/histolabs/metro/testutil/testnode" 14 | "github.com/stretchr/testify/require" 15 | "github.com/stretchr/testify/suite" 16 | 17 | sdk "github.com/cosmos/cosmos-sdk/types" 18 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 19 | abci "github.com/tendermint/tendermint/abci/types" 20 | ) 21 | 22 | func TestIntegrationTestSuite(t *testing.T) { 23 | suite.Run(t, new(IntegrationTestSuite)) 24 | } 25 | 26 | type IntegrationTestSuite struct { 27 | suite.Suite 28 | 29 | cleanup func() error 30 | accounts []string 31 | cctx testnode.Context 32 | ecfg encoding.Config 33 | } 34 | 35 | func (s *IntegrationTestSuite) SetupSuite() { 36 | if testing.Short() { 37 | s.T().Skip("skipping test in unit-tests or race-detector mode.") 38 | } 39 | 40 | s.T().Log("setting up integration test suite") 41 | cleanup, accounts, cctx := testnode.DefaultNetwork(s.T(), time.Millisecond*500) 42 | 43 | s.cctx = cctx 44 | s.accounts = accounts 45 | s.cleanup = cleanup 46 | 47 | s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) 48 | } 49 | 50 | func (s *IntegrationTestSuite) TearDownSuite() { 51 | s.T().Log("tearing down integration test suite") 52 | err := s.cleanup() 53 | require.NoError(s.T(), err) 54 | } 55 | 56 | func (s *IntegrationTestSuite) TestPrimaryChainIDBuilder() { 57 | t := s.T() 58 | 59 | // define the tx builder options 60 | feeCoin := sdk.Coin{ 61 | Denom: consts.BondDenom, 62 | Amount: sdk.NewInt(1), 63 | } 64 | 65 | opts := []builder.TxBuilderOption{ 66 | builder.SetFeeAmount(sdk.NewCoins(feeCoin)), 67 | builder.SetGasLimit(1000000000), 68 | } 69 | 70 | signer := builder.NewKeyringSigner(s.ecfg, s.cctx.Keyring, s.accounts[0], s.cctx.ChainID) 71 | err := signer.UpdateAccount(s.cctx.GoContext(), s.cctx.GRPCClient) 72 | require.NoError(t, err) 73 | 74 | msg := createMsgSend(t, signer, s.accounts[1], sdk.NewInt(10000000)) 75 | 76 | sdkTx, err := signer.BuildSignedTx(signer.NewTxBuilder(opts...), false, msg) 77 | require.NoError(t, err) 78 | 79 | rawTx, err := s.ecfg.TxConfig.TxEncoder()(sdkTx) 80 | require.NoError(t, err) 81 | 82 | resp, err := s.cctx.BroadcastTxSync(rawTx) 83 | require.NoError(t, err) 84 | 85 | require.Equal(t, abci.CodeTypeOK, resp.Code) 86 | 87 | err = s.cctx.WaitForNextBlock() 88 | require.NoError(t, err) 89 | 90 | res, err := testfactory.QueryWithoutProof(s.cctx.Context, resp.TxHash) 91 | require.NoError(t, err) 92 | 93 | require.Equal(t, abci.CodeTypeOK, res.TxResult.Code) 94 | } 95 | 96 | func (s *IntegrationTestSuite) TestSecondaryChainIDBuilder() { 97 | t := s.T() 98 | 99 | // define the tx builder options 100 | feeCoin := sdk.Coin{ 101 | Denom: consts.BondDenom, 102 | Amount: sdk.NewInt(1), 103 | } 104 | 105 | opts := []builder.TxBuilderOption{ 106 | builder.SetFeeAmount(sdk.NewCoins(feeCoin)), 107 | builder.SetGasLimit(1000000000), 108 | } 109 | 110 | secondaryChainID := "taco" 111 | chainID := strings.Join([]string{s.cctx.ChainID, secondaryChainID}, consts.ChainIDSeparator) 112 | 113 | signer := builder.NewKeyringSigner(s.ecfg, s.cctx.Keyring, s.accounts[0], chainID) 114 | err := signer.UpdateAccount(s.cctx.GoContext(), s.cctx.GRPCClient) 115 | require.NoError(t, err) 116 | 117 | addr, err := signer.GetSignerInfo().GetAddress() 118 | require.NoError(t, err) 119 | 120 | initBal, err := queryBalance(s.cctx, addr) 121 | require.NoError(t, err) 122 | 123 | amount := sdk.NewInt(1000000000000) 124 | 125 | msg := createMsgSend(t, signer, s.accounts[1], amount) 126 | 127 | sdkTx, err := signer.BuildSignedTx(signer.NewTxBuilder(opts...), true, msg) 128 | require.NoError(t, err) 129 | 130 | rawTx, err := s.ecfg.TxConfig.TxEncoder()(sdkTx) 131 | require.NoError(t, err) 132 | 133 | resp, err := s.cctx.BroadcastTxSync(rawTx) 134 | require.NoError(t, err) 135 | 136 | require.Equal(t, abci.CodeTypeOK, resp.Code) 137 | 138 | err = s.cctx.WaitForNextBlock() 139 | require.NoError(t, err) 140 | 141 | res, err := testfactory.QueryWithoutProof(s.cctx.Context, resp.TxHash) 142 | require.NoError(t, err) 143 | 144 | require.Equal(t, abci.CodeTypeOK, res.TxResult.Code) 145 | 146 | currentBal, err := queryBalance(s.cctx, addr) 147 | require.NoError(t, err) 148 | 149 | // check that the balance only decreased from gas and that the send did not actually get executed 150 | diff := initBal.Sub(*currentBal) 151 | require.True(t, diff.Amount.LT(amount)) 152 | require.True(t, diff.Amount.GTE(sdk.NewInt(1))) 153 | } 154 | 155 | func createMsgSend(t *testing.T, signer *builder.KeyringSigner, receiver string, amount sdk.Int) *banktypes.MsgSend { 156 | // create a msg send transaction 157 | amountCoin := sdk.Coin{ 158 | Denom: consts.BondDenom, 159 | Amount: amount, 160 | } 161 | 162 | addr, err := signer.GetSignerInfo().GetAddress() 163 | if err != nil { 164 | panic(err) 165 | } 166 | 167 | sendAcc, err := signer.Key(receiver) 168 | require.NoError(t, err) 169 | 170 | sendAddr, err := sendAcc.GetAddress() 171 | require.NoError(t, err) 172 | 173 | return banktypes.NewMsgSend(addr, sendAddr, sdk.NewCoins(amountCoin)) 174 | } 175 | 176 | func queryBalance(cctx testnode.Context, addr sdk.AccAddress) (*sdk.Coin, error) { 177 | qc := banktypes.NewQueryClient(cctx.GRPCClient) 178 | 179 | res, err := qc.Balance(cctx.GoContext(), banktypes.NewQueryBalanceRequest(addr, consts.BondDenom)) 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | return res.Balance, nil 185 | } 186 | -------------------------------------------------------------------------------- /testutil/testnode/node_init.go: -------------------------------------------------------------------------------- 1 | package testnode 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | "os" 9 | "path/filepath" 10 | "testing" 11 | 12 | "github.com/cosmos/cosmos-sdk/client/tx" 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 15 | cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 16 | "github.com/cosmos/cosmos-sdk/server" 17 | sdk "github.com/cosmos/cosmos-sdk/types" 18 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 19 | "github.com/cosmos/cosmos-sdk/x/genutil" 20 | genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" 21 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 22 | "github.com/ethereum/go-ethereum/crypto" 23 | "github.com/histolabs/metro/app/encoding" 24 | "github.com/histolabs/metro/pkg/consts" 25 | "github.com/tendermint/tendermint/config" 26 | tmos "github.com/tendermint/tendermint/libs/os" 27 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 28 | "github.com/tendermint/tendermint/types" 29 | tmtime "github.com/tendermint/tendermint/types/time" 30 | ) 31 | 32 | func collectGenFiles(tmCfg *config.Config, encCfg encoding.Config, pubKey cryptotypes.PubKey, nodeID, chainID, rootDir string) error { 33 | genTime := tmtime.Now() 34 | 35 | gentxsDir := filepath.Join(rootDir, "gentxs") 36 | 37 | initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, pubKey) 38 | 39 | genFile := tmCfg.GenesisFile() 40 | genDoc, err := types.GenesisDocFromFile(genFile) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | appState, err := genutil.GenAppStateFromConfig( 46 | encCfg.Codec, 47 | encCfg.TxConfig, 48 | tmCfg, 49 | initCfg, 50 | *genDoc, 51 | banktypes.GenesisBalancesIterator{}, 52 | ) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | genDoc = &types.GenesisDoc{ 58 | GenesisTime: genTime, 59 | ChainID: chainID, 60 | Validators: nil, 61 | AppState: appState, 62 | ConsensusParams: genDoc.ConsensusParams, 63 | } 64 | 65 | if err := genDoc.ValidateAndComplete(); err != nil { 66 | return err 67 | } 68 | 69 | return genDoc.SaveAs(genFile) 70 | } 71 | 72 | func initGenFiles( 73 | cparams *tmproto.ConsensusParams, 74 | state map[string]json.RawMessage, 75 | codec codec.Codec, 76 | file, 77 | chainID string, 78 | ) error { 79 | appGenStateJSON, err := json.MarshalIndent(state, "", " ") 80 | if err != nil { 81 | return err 82 | } 83 | 84 | genDoc := types.GenesisDoc{ 85 | ChainID: chainID, 86 | AppState: appGenStateJSON, 87 | ConsensusParams: cparams, 88 | Validators: nil, 89 | } 90 | 91 | return genDoc.SaveAs(file) 92 | } 93 | 94 | func createValidator( 95 | kr keyring.Keyring, 96 | encCfg encoding.Config, 97 | pubKey cryptotypes.PubKey, 98 | valopAcc, 99 | nodeID, 100 | chainID, 101 | baseDir string, 102 | ) error { 103 | rec, err := kr.Key(valopAcc) 104 | if err != nil { 105 | return err 106 | } 107 | addr, err := rec.GetAddress() 108 | if err != nil { 109 | return err 110 | } 111 | p2pAddr, _, err := server.FreeTCPAddr() 112 | if err != nil { 113 | return err 114 | } 115 | p2pURL, err := url.Parse(p2pAddr) 116 | if err != nil { 117 | return err 118 | } 119 | commission, err := sdk.NewDecFromStr("0.5") 120 | if err != nil { 121 | return err 122 | } 123 | ethPrivateKey, err := crypto.GenerateKey() 124 | if err != nil { 125 | return err 126 | } 127 | orchEVMPublicKey := ethPrivateKey.Public().(*ecdsa.PublicKey) 128 | evmAddr := crypto.PubkeyToAddress(*orchEVMPublicKey) 129 | 130 | createValMsg, err := stakingtypes.NewMsgCreateValidator( 131 | sdk.ValAddress(addr), 132 | pubKey, 133 | sdk.NewCoin(consts.BondDenom, sdk.NewInt(100000000)), 134 | stakingtypes.NewDescription("test", "", "", "", ""), 135 | stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), 136 | sdk.OneInt(), 137 | addr, 138 | evmAddr, 139 | ) 140 | if err != nil { 141 | return err 142 | } 143 | 144 | memo := fmt.Sprintf("%s@%s:%s", nodeID, p2pURL.Hostname(), p2pURL.Port()) 145 | fee := sdk.NewCoins(sdk.NewCoin(consts.BondDenom, sdk.NewInt(1))) 146 | txBuilder := encCfg.TxConfig.NewTxBuilder() 147 | err = txBuilder.SetMsgs(createValMsg) 148 | if err != nil { 149 | return err 150 | } 151 | txBuilder.SetFeeAmount(fee) // Arbitrary fee 152 | txBuilder.SetGasLimit(1000000) // Need at least 100386 153 | txBuilder.SetMemo(memo) 154 | 155 | txFactory := tx.Factory{} 156 | txFactory = txFactory. 157 | WithChainID(chainID). 158 | WithMemo(memo). 159 | WithKeybase(kr). 160 | WithTxConfig(encCfg.TxConfig) 161 | 162 | err = tx.Sign(txFactory, valopAcc, txBuilder, true) 163 | if err != nil { 164 | return err 165 | } 166 | 167 | txBz, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) 168 | if err != nil { 169 | return err 170 | } 171 | gentxsDir := filepath.Join(baseDir, "gentxs") 172 | return writeFile(fmt.Sprintf("%v.json", "test"), gentxsDir, txBz) 173 | } 174 | 175 | func writeFile(name string, dir string, contents []byte) error { 176 | writePath := filepath.Join(dir) 177 | file := filepath.Join(writePath, name) 178 | 179 | err := tmos.EnsureDir(writePath, 0o755) 180 | if err != nil { 181 | return err 182 | } 183 | 184 | err = os.WriteFile(file, contents, 0o644) // nolint: gosec 185 | if err != nil { 186 | return err 187 | } 188 | 189 | return nil 190 | } 191 | 192 | func initFileStructure(t *testing.T, tmCfg *config.Config) (string, error) { 193 | basePath := filepath.Join(t.TempDir(), ".metro") 194 | tmCfg.SetRoot(basePath) 195 | configPath := filepath.Join(basePath, "config") 196 | err := os.MkdirAll(configPath, os.ModePerm) 197 | if err != nil { 198 | return "", err 199 | } 200 | if err != nil { 201 | return "", err 202 | } 203 | return basePath, nil 204 | } 205 | -------------------------------------------------------------------------------- /third_party/proto/google/protobuf/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | import "gogoproto/gogo.proto"; 36 | 37 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 38 | option go_package = "types"; 39 | option java_package = "com.google.protobuf"; 40 | option java_outer_classname = "AnyProto"; 41 | option java_multiple_files = true; 42 | option objc_class_prefix = "GPB"; 43 | 44 | // `Any` contains an arbitrary serialized protocol buffer message along with a 45 | // URL that describes the type of the serialized message. 46 | // 47 | // Protobuf library provides support to pack/unpack Any values in the form 48 | // of utility functions or additional generated methods of the Any type. 49 | // 50 | // Example 1: Pack and unpack a message in C++. 51 | // 52 | // Foo foo = ...; 53 | // Any any; 54 | // any.PackFrom(foo); 55 | // ... 56 | // if (any.UnpackTo(&foo)) { 57 | // ... 58 | // } 59 | // 60 | // Example 2: Pack and unpack a message in Java. 61 | // 62 | // Foo foo = ...; 63 | // Any any = Any.pack(foo); 64 | // ... 65 | // if (any.is(Foo.class)) { 66 | // foo = any.unpack(Foo.class); 67 | // } 68 | // 69 | // Example 3: Pack and unpack a message in Python. 70 | // 71 | // foo = Foo(...) 72 | // any = Any() 73 | // any.Pack(foo) 74 | // ... 75 | // if any.Is(Foo.DESCRIPTOR): 76 | // any.Unpack(foo) 77 | // ... 78 | // 79 | // Example 4: Pack and unpack a message in Go 80 | // 81 | // foo := &pb.Foo{...} 82 | // any, err := ptypes.MarshalAny(foo) 83 | // ... 84 | // foo := &pb.Foo{} 85 | // if err := ptypes.UnmarshalAny(any, foo); err != nil { 86 | // ... 87 | // } 88 | // 89 | // The pack methods provided by protobuf library will by default use 90 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 91 | // methods only use the fully qualified type name after the last '/' 92 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 93 | // name "y.z". 94 | // 95 | // 96 | // JSON 97 | // ==== 98 | // The JSON representation of an `Any` value uses the regular 99 | // representation of the deserialized, embedded message, with an 100 | // additional field `@type` which contains the type URL. Example: 101 | // 102 | // package google.profile; 103 | // message Person { 104 | // string first_name = 1; 105 | // string last_name = 2; 106 | // } 107 | // 108 | // { 109 | // "@type": "type.googleapis.com/google.profile.Person", 110 | // "firstName": <string>, 111 | // "lastName": <string> 112 | // } 113 | // 114 | // If the embedded message type is well-known and has a custom JSON 115 | // representation, that representation will be embedded adding a field 116 | // `value` which holds the custom JSON in addition to the `@type` 117 | // field. Example (for message [google.protobuf.Duration][]): 118 | // 119 | // { 120 | // "@type": "type.googleapis.com/google.protobuf.Duration", 121 | // "value": "1.212s" 122 | // } 123 | // 124 | message Any { 125 | // A URL/resource name that uniquely identifies the type of the serialized 126 | // protocol buffer message. This string must contain at least 127 | // one "/" character. The last segment of the URL's path must represent 128 | // the fully qualified name of the type (as in 129 | // `path/google.protobuf.Duration`). The name should be in a canonical form 130 | // (e.g., leading "." is not accepted). 131 | // 132 | // In practice, teams usually precompile into the binary all types that they 133 | // expect it to use in the context of Any. However, for URLs which use the 134 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 135 | // server that maps type URLs to message definitions as follows: 136 | // 137 | // * If no scheme is provided, `https` is assumed. 138 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 139 | // value in binary format, or produce an error. 140 | // * Applications are allowed to cache lookup results based on the 141 | // URL, or have them precompiled into a binary to avoid any 142 | // lookup. Therefore, binary compatibility needs to be preserved 143 | // on changes to types. (Use versioned type names to manage 144 | // breaking changes.) 145 | // 146 | // Note: this functionality is not currently available in the official 147 | // protobuf release, and it is not used for type URLs beginning with 148 | // type.googleapis.com. 149 | // 150 | // Schemes other than `http`, `https` (or the empty scheme) might be 151 | // used with implementation specific semantics. 152 | // 153 | string type_url = 1; 154 | 155 | // Must be a valid serialized protocol buffer of the above specified type. 156 | bytes value = 2; 157 | 158 | option (gogoproto.typedecl) = false; 159 | } 160 | 161 | option (gogoproto.goproto_registration) = false; 162 | -------------------------------------------------------------------------------- /app/export.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 8 | 9 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" 12 | "github.com/cosmos/cosmos-sdk/x/staking" 13 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 14 | ) 15 | 16 | // ExportAppStateAndValidators exports the state of the application for a genesis 17 | // file. 18 | func (app *App) ExportAppStateAndValidators( 19 | forZeroHeight bool, jailAllowedAddrs []string, 20 | ) (servertypes.ExportedApp, error) { 21 | // as if they could withdraw from the start of the next block 22 | ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) 23 | 24 | // We export at last height + 1, because that's the height at which 25 | // Tendermint will start InitChain. 26 | height := app.LastBlockHeight() + 1 27 | if forZeroHeight { 28 | height = 0 29 | app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) 30 | } 31 | 32 | genState := app.mm.ExportGenesis(ctx, app.appCodec) 33 | appState, err := json.MarshalIndent(genState, "", " ") 34 | if err != nil { 35 | return servertypes.ExportedApp{}, err 36 | } 37 | 38 | validators, err := staking.WriteValidators(ctx, app.StakingKeeper) 39 | return servertypes.ExportedApp{ 40 | AppState: appState, 41 | Validators: validators, 42 | Height: height, 43 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx), 44 | }, err 45 | } 46 | 47 | // prepForZeroHeightGenesis preps for fresh start at zero height. Zero height 48 | // genesis is a temporary feature which will be deprecated in favour of export 49 | // at a block height. 50 | func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { 51 | applyAllowedAddrs := false 52 | 53 | // check if there is a allowed address list 54 | if len(jailAllowedAddrs) > 0 { 55 | applyAllowedAddrs = true 56 | } 57 | 58 | allowedAddrsMap := make(map[string]bool) 59 | 60 | for _, addr := range jailAllowedAddrs { 61 | _, err := sdk.ValAddressFromBech32(addr) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | allowedAddrsMap[addr] = true 66 | } 67 | 68 | /* Just to be safe, assert the invariants on current state. */ 69 | app.CrisisKeeper.AssertInvariants(ctx) 70 | 71 | /* Handle fee distribution state. */ 72 | 73 | // withdraw all validator commission 74 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 75 | _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) 76 | return false 77 | }) 78 | 79 | // withdraw all delegator rewards 80 | dels := app.StakingKeeper.GetAllDelegations(ctx) 81 | for _, delegation := range dels { 82 | valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) 88 | if err != nil { 89 | panic(err) 90 | } 91 | _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) 92 | } 93 | 94 | // clear validator slash events 95 | app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) 96 | 97 | // clear validator historical rewards 98 | app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) 99 | 100 | // set context height to zero 101 | height := ctx.BlockHeight() 102 | ctx = ctx.WithBlockHeight(0) 103 | 104 | // reinitialize all validators 105 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 106 | // donate any unwithdrawn outstanding reward fraction tokens to the community pool 107 | scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) 108 | feePool := app.DistrKeeper.GetFeePool(ctx) 109 | feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) 110 | app.DistrKeeper.SetFeePool(ctx, feePool) 111 | 112 | if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()); err != nil { 113 | panic(err) 114 | } 115 | return false 116 | }) 117 | 118 | // reinitialize all delegations 119 | for _, del := range dels { 120 | valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) 121 | if err != nil { 122 | panic(err) 123 | } 124 | delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress) 125 | if err != nil { 126 | panic(err) 127 | } 128 | _ = app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) 129 | _ = app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) 130 | } 131 | 132 | // reset context height 133 | ctx = ctx.WithBlockHeight(height) 134 | 135 | /* Handle staking state. */ 136 | 137 | // iterate through redelegations, reset creation height 138 | app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { 139 | for i := range red.Entries { 140 | red.Entries[i].CreationHeight = 0 141 | } 142 | app.StakingKeeper.SetRedelegation(ctx, red) 143 | return false 144 | }) 145 | 146 | // iterate through unbonding delegations, reset creation height 147 | app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { 148 | for i := range ubd.Entries { 149 | ubd.Entries[i].CreationHeight = 0 150 | } 151 | app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) 152 | return false 153 | }) 154 | 155 | // Iterate through validators by power descending, reset bond heights, and 156 | // update bond intra-tx counters. 157 | store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) 158 | iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) 159 | counter := int16(0) 160 | 161 | for ; iter.Valid(); iter.Next() { 162 | addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) 163 | validator, found := app.StakingKeeper.GetValidator(ctx, addr) 164 | if !found { 165 | panic("expected validator, not found") 166 | } 167 | 168 | validator.UnbondingHeight = 0 169 | if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { 170 | validator.Jailed = true 171 | } 172 | 173 | app.StakingKeeper.SetValidator(ctx, validator) 174 | counter++ 175 | } 176 | 177 | iter.Close() 178 | 179 | _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) 180 | if err != nil { 181 | log.Fatal(err) 182 | } 183 | 184 | /* Handle slashing state. */ 185 | 186 | // reset start height on signing infos 187 | app.SlashingKeeper.IterateValidatorSigningInfos( 188 | ctx, 189 | func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { 190 | info.StartHeight = 0 191 | app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) 192 | return false 193 | }, 194 | ) 195 | } 196 | -------------------------------------------------------------------------------- /testutil/testnode/full_node.go: -------------------------------------------------------------------------------- 1 | package testnode 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 10 | pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" 11 | "github.com/cosmos/cosmos-sdk/server" 12 | srvtypes "github.com/cosmos/cosmos-sdk/server/types" 13 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 14 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 15 | "github.com/cosmos/cosmos-sdk/x/genutil" 16 | "github.com/histolabs/metro/app" 17 | "github.com/histolabs/metro/app/encoding" 18 | "github.com/histolabs/metro/cmd/metro/cmd" 19 | "github.com/histolabs/metro/testutil/testfactory" 20 | "github.com/stretchr/testify/require" 21 | "github.com/tendermint/tendermint/config" 22 | "github.com/tendermint/tendermint/libs/log" 23 | tmrand "github.com/tendermint/tendermint/libs/rand" 24 | "github.com/tendermint/tendermint/node" 25 | "github.com/tendermint/tendermint/p2p" 26 | "github.com/tendermint/tendermint/privval" 27 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 28 | "github.com/tendermint/tendermint/proxy" 29 | "github.com/tendermint/tendermint/types" 30 | dbm "github.com/tendermint/tm-db" 31 | ) 32 | 33 | // New creates a ready to use tendermint node that operates a single validator 34 | // metro network using the provided genesis state. The provided keyring 35 | // is stored in the client.Context that is returned. 36 | // 37 | // NOTE: the forced delay between blocks, TimeIotaMs in the consensus 38 | // parameters, is set to the lowest possible value (1ms). 39 | func New( 40 | t *testing.T, 41 | cparams *tmproto.ConsensusParams, 42 | tmCfg *config.Config, 43 | supressLog bool, 44 | genState map[string]json.RawMessage, 45 | kr keyring.Keyring, 46 | ) (*node.Node, srvtypes.Application, Context, error) { 47 | var logger log.Logger 48 | if supressLog { 49 | logger = log.NewNopLogger() 50 | } else { 51 | logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 52 | logger = log.NewFilter(logger, log.AllowError()) 53 | } 54 | 55 | baseDir, err := initFileStructure(t, tmCfg) 56 | if err != nil { 57 | return nil, nil, Context{}, err 58 | } 59 | 60 | chainID := tmrand.Str(6) 61 | 62 | encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) 63 | 64 | nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) 65 | if err != nil { 66 | return nil, nil, Context{}, err 67 | } 68 | 69 | nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) 70 | if err != nil { 71 | return nil, nil, Context{}, err 72 | } 73 | 74 | err = createValidator(kr, encCfg, pubKey, "validator", nodeID, chainID, baseDir) 75 | if err != nil { 76 | return nil, nil, Context{}, err 77 | } 78 | 79 | err = initGenFiles(cparams, genState, encCfg.Codec, tmCfg.GenesisFile(), chainID) 80 | if err != nil { 81 | return nil, nil, Context{}, err 82 | } 83 | 84 | err = collectGenFiles(tmCfg, encCfg, pubKey, nodeID, chainID, baseDir) 85 | if err != nil { 86 | return nil, nil, Context{}, err 87 | } 88 | 89 | db := dbm.NewMemDB() 90 | 91 | appOpts := appOptions{ 92 | options: map[string]interface{}{ 93 | server.FlagPruning: pruningtypes.PruningOptionNothing, 94 | }, 95 | } 96 | 97 | app := cmd.NewAppServer(logger, db, nil, appOpts) 98 | 99 | tmNode, err := node.NewNode( 100 | tmCfg, 101 | privval.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), 102 | nodeKey, 103 | proxy.NewLocalClientCreator(app), 104 | node.DefaultGenesisDocProviderFunc(tmCfg), 105 | node.DefaultDBProvider, 106 | node.DefaultMetricsProvider(tmCfg.Instrumentation), 107 | logger, 108 | ) 109 | 110 | cCtx := Context{}. 111 | WithKeyring(kr). 112 | WithHomeDir(tmCfg.RootDir). 113 | WithChainID(chainID). 114 | WithInterfaceRegistry(encCfg.InterfaceRegistry). 115 | WithCodec(encCfg.Codec). 116 | WithLegacyAmino(encCfg.Amino). 117 | WithTxConfig(encCfg.TxConfig). 118 | WithAccountRetriever(authtypes.AccountRetriever{}) 119 | 120 | return tmNode, app, Context{Context: cCtx}, err 121 | } 122 | 123 | type appOptions struct { 124 | options map[string]interface{} 125 | } 126 | 127 | // Get implements AppOptions 128 | func (ao appOptions) Get(o string) interface{} { 129 | return ao.options[o] 130 | } 131 | 132 | func DefaultParams() *tmproto.ConsensusParams { 133 | cparams := types.DefaultConsensusParams() 134 | return cparams 135 | } 136 | 137 | func DefaultTendermintConfig() *config.Config { 138 | tmCfg := config.DefaultConfig() 139 | tmCfg.Consensus.TimeoutCommit = time.Millisecond * 300 140 | tmCfg.Mempool.MaxTxBytes = 22020096 // 21MB 141 | return tmCfg 142 | } 143 | 144 | // DefaultGenesisState returns a default genesis state and a keyring with 145 | // accounts that have coins. The keyring accounts are based on the 146 | // fundedAccounts parameter. 147 | func DefaultGenesisState(fundedAccounts ...string) (map[string]json.RawMessage, keyring.Keyring, error) { 148 | encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) 149 | state := app.ModuleBasics.DefaultGenesis(encCfg.Codec) 150 | fundedAccounts = append(fundedAccounts, "validator") 151 | kr, bankBals, authAccs := testfactory.FundKeyringAccounts(encCfg.Codec, fundedAccounts...) 152 | 153 | // set the accounts in the genesis state 154 | var authGenState authtypes.GenesisState 155 | encCfg.Codec.MustUnmarshalJSON(state[authtypes.ModuleName], &authGenState) 156 | 157 | accounts, err := authtypes.PackAccounts(authAccs) 158 | if err != nil { 159 | return nil, nil, err 160 | } 161 | 162 | authGenState.Accounts = append(authGenState.Accounts, accounts...) 163 | state[authtypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&authGenState) 164 | 165 | // set the balances in the genesis state 166 | var bankGenState banktypes.GenesisState 167 | encCfg.Codec.MustUnmarshalJSON(state[banktypes.ModuleName], &bankGenState) 168 | 169 | bankGenState.Balances = append(bankGenState.Balances, bankBals...) 170 | state[banktypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&bankGenState) 171 | 172 | return state, kr, nil 173 | } 174 | 175 | // DefaultNetwork creates an in-process single validator metro network 176 | // using test friendly defaults. These defaults include fast block times and 177 | // funded accounts. The returned client.Context has a keyring with all of the 178 | // funded keys stored in it. 179 | func DefaultNetwork(t *testing.T, blockTime time.Duration) (cleanup func() error, accounts []string, cctx Context) { 180 | // we create an arbitrary number of funded accounts 181 | accounts = make([]string, 300) 182 | for i := 0; i < 300; i++ { 183 | accounts[i] = tmrand.Str(9) 184 | } 185 | 186 | tmCfg := DefaultTendermintConfig() 187 | tmCfg.Consensus.TimeoutCommit = blockTime 188 | 189 | genState, kr, err := DefaultGenesisState(accounts...) 190 | require.NoError(t, err) 191 | 192 | tmNode, app, cctx, err := New(t, DefaultParams(), tmCfg, false, genState, kr) 193 | require.NoError(t, err) 194 | 195 | cctx, stopNode, err := StartNode(tmNode, cctx) 196 | require.NoError(t, err) 197 | 198 | cctx, cleanupGRPC, err := StartGRPCServer(app, DefaultAppConfig(), cctx) 199 | require.NoError(t, err) 200 | 201 | return func() error { 202 | err := stopNode() 203 | if err != nil { 204 | return err 205 | } 206 | return cleanupGRPC() 207 | }, accounts, cctx 208 | } 209 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | import "tendermint/crypto/proof.proto"; 9 | import "tendermint/version/types.proto"; 10 | import "tendermint/types/validator.proto"; 11 | 12 | // BlockIdFlag indicates which BlcokID the signature is for 13 | enum BlockIDFlag { 14 | option (gogoproto.goproto_enum_stringer) = true; 15 | option (gogoproto.goproto_enum_prefix) = false; 16 | 17 | BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; 18 | BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; 19 | BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; 20 | BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; 21 | } 22 | 23 | // SignedMsgType is a type of signed message in the consensus. 24 | enum SignedMsgType { 25 | option (gogoproto.goproto_enum_stringer) = true; 26 | option (gogoproto.goproto_enum_prefix) = false; 27 | 28 | SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; 29 | // Votes 30 | SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; 31 | SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; 32 | 33 | // Proposals 34 | SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; 35 | } 36 | 37 | // PartsetHeader 38 | message PartSetHeader { 39 | uint32 total = 1; 40 | bytes hash = 2; 41 | } 42 | 43 | message Part { 44 | uint32 index = 1; 45 | bytes bytes = 2; 46 | tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; 47 | } 48 | 49 | // BlockID 50 | message BlockID { 51 | bytes hash = 1; 52 | PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; 53 | } 54 | 55 | // -------------------------------- 56 | 57 | // Header defines the structure of a Tendermint block header. 58 | message Header { 59 | // basic block info 60 | tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; 61 | string chain_id = 2 [(gogoproto.customname) = "ChainID"]; 62 | int64 height = 3; 63 | google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 64 | 65 | // prev block info 66 | BlockID last_block_id = 5 [(gogoproto.nullable) = false]; 67 | 68 | // hashes of block data 69 | bytes last_commit_hash = 6; // commit from validators from the last block 70 | bytes data_hash = 7; // transactions 71 | 72 | // hashes from the app output from the prev block 73 | bytes validators_hash = 8; // validators for the current block 74 | bytes next_validators_hash = 9; // validators for the next block 75 | bytes consensus_hash = 10; // consensus params for current block 76 | bytes app_hash = 11; // state after txs from the previous block 77 | bytes last_results_hash = 12; // root hash of all results from the txs from the previous block 78 | 79 | // consensus info 80 | bytes evidence_hash = 13; // evidence included in the block 81 | bytes proposer_address = 14; // original proposer of the block 82 | } 83 | 84 | // Data contains the set of transactions included in the block 85 | message Data { 86 | // Txs that will be applied by state @ block.Height+1. 87 | // NOTE: not all txs here are valid. We're just agreeing on the order first. 88 | // This means that block.AppHash does not include these txs. 89 | repeated bytes txs = 1; 90 | IntermediateStateRoots intermediate_state_roots = 2 [(gogoproto.nullable) = false]; 91 | EvidenceList evidence = 3 [(gogoproto.nullable) = false]; 92 | Messages messages = 4 [(gogoproto.nullable) = false]; 93 | } 94 | 95 | message Evidence { 96 | oneof sum { 97 | DuplicateVoteEvidence duplicate_vote_evidence = 1; 98 | LightClientAttackEvidence light_client_attack_evidence = 2; 99 | } 100 | } 101 | 102 | // DuplicateVoteEvidence contains evidence of a validator signed two conflicting votes. 103 | message DuplicateVoteEvidence { 104 | tendermint.types.Vote vote_a = 1; 105 | tendermint.types.Vote vote_b = 2; 106 | int64 total_voting_power = 3; 107 | int64 validator_power = 4; 108 | google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 109 | } 110 | 111 | // LightClientAttackEvidence contains evidence of a set of validators attempting to mislead a light client. 112 | message LightClientAttackEvidence { 113 | tendermint.types.LightBlock conflicting_block = 1; 114 | int64 common_height = 2; 115 | repeated tendermint.types.Validator byzantine_validators = 3; 116 | int64 total_voting_power = 4; 117 | google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 118 | } 119 | 120 | message EvidenceList { 121 | repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; 122 | } 123 | 124 | message IntermediateStateRoots { 125 | repeated bytes raw_roots_list = 1; 126 | } 127 | 128 | message Messages { 129 | repeated Message messages_list = 1; 130 | } 131 | 132 | message Message { 133 | bytes namespace_id = 1; 134 | bytes data = 2; 135 | } 136 | 137 | // Vote represents a prevote, precommit, or commit vote from validators for 138 | // consensus. 139 | message Vote { 140 | SignedMsgType type = 1; 141 | int64 height = 2; 142 | int32 round = 3; 143 | BlockID block_id = 4 144 | [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. 145 | google.protobuf.Timestamp timestamp = 5 146 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 147 | bytes validator_address = 6; 148 | int32 validator_index = 7; 149 | bytes signature = 8; 150 | } 151 | 152 | // Commit contains the evidence that a block was committed by a set of validators. 153 | message Commit { 154 | int64 height = 1; 155 | int32 round = 2; 156 | BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; 157 | repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; 158 | } 159 | 160 | // CommitSig is a part of the Vote included in a Commit. 161 | message CommitSig { 162 | BlockIDFlag block_id_flag = 1; 163 | bytes validator_address = 2; 164 | google.protobuf.Timestamp timestamp = 3 165 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 166 | bytes signature = 4; 167 | } 168 | 169 | message Proposal { 170 | SignedMsgType type = 1; 171 | int64 height = 2; 172 | int32 round = 3; 173 | int32 pol_round = 4; 174 | BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; 175 | google.protobuf.Timestamp timestamp = 6 176 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 177 | bytes signature = 7; 178 | } 179 | 180 | message SignedHeader { 181 | Header header = 1; 182 | Commit commit = 2; 183 | } 184 | 185 | message LightBlock { 186 | SignedHeader signed_header = 1; 187 | tendermint.types.ValidatorSet validator_set = 2; 188 | } 189 | 190 | message BlockMeta { 191 | BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; 192 | int64 block_size = 2; 193 | Header header = 3 [(gogoproto.nullable) = false]; 194 | int64 num_txs = 4; 195 | } 196 | 197 | // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. 198 | message TxProof { 199 | bytes root_hash = 1; 200 | bytes data = 2; 201 | tendermint.crypto.Proof proof = 3; 202 | } 203 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests / Code Coverage 2 | # Tests / Code Coverage workflow runs unit tests and uploads a code coverage 3 | # report 4 | # This workflow is run on pushes to main & every Pull Requests where a .go, 5 | # .mod, .sum have been changed 6 | on: 7 | workflow_call: 8 | 9 | jobs: 10 | cleanup-runs: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: rokroskar/workflow-run-cleanup-action@master 14 | env: 15 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 16 | if: | 17 | !startsWith(github.ref, 'refs/tags/') && 18 | github.ref != 'refs/heads/master' 19 | 20 | install-tparse: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/setup-go@v3 24 | with: 25 | go-version: 1.19 26 | - name: Display go version 27 | run: go version 28 | - name: install tparse 29 | run: > 30 | export GO111MODULE="on" && 31 | go install github.com/mfridman/tparse@v0.8.3 32 | - uses: actions/cache@v3.2.2 33 | with: 34 | path: ~/go/bin 35 | key: ${{ runner.os }}-go-tparse-binary 36 | 37 | # don't use cosmovisor until we have tests 38 | # test-cosmovisor: 39 | # runs-on: ubuntu-latest 40 | # steps: 41 | # - uses: actions/checkout@v3 42 | # - uses: actions/setup-go@v3 43 | # with: 44 | # go-version: 1.19 45 | # - name: Display go version 46 | # run: go version 47 | # - uses: technote-space/get-diff-action@v6.1.2 48 | # id: git_diff 49 | # with: 50 | # PREFIX_FILTER: | 51 | # cosmovisor 52 | # PATTERNS: | 53 | # **/**.go 54 | # go.mod 55 | # go.sum 56 | # - name: Run cosmovisor tests 57 | # run: cd cosmovisor; make 58 | # if: env.GIT_DIFF 59 | 60 | split-test-files: 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v3 64 | - name: Create a file with all the pkgs 65 | run: go list ./... > pkgs.txt 66 | - name: Split pkgs into 4 files 67 | run: split -d -n l/4 --elide-empty-files pkgs.txt pkgs.txt.part. 68 | # cache multiple 69 | - uses: actions/upload-artifact@v3 70 | with: 71 | name: "${{ github.sha }}-00" 72 | path: ./pkgs.txt.part.00 73 | - uses: actions/upload-artifact@v3 74 | with: 75 | name: "${{ github.sha }}-01" 76 | path: ./pkgs.txt.part.01 77 | - uses: actions/upload-artifact@v3 78 | with: 79 | name: "${{ github.sha }}-02" 80 | path: ./pkgs.txt.part.02 81 | - uses: actions/upload-artifact@v3 82 | with: 83 | name: "${{ github.sha }}-03" 84 | path: ./pkgs.txt.part.03 85 | 86 | tests: 87 | runs-on: ubuntu-latest 88 | needs: split-test-files 89 | strategy: 90 | fail-fast: false 91 | matrix: 92 | part: ["00", "01", "02", "03"] 93 | steps: 94 | - uses: actions/checkout@v3 95 | - uses: actions/setup-go@v3 96 | with: 97 | go-version: 1.19 98 | - uses: technote-space/get-diff-action@v6.1.2 99 | with: 100 | PATTERNS: | 101 | **/**.go 102 | go.mod 103 | go.sum 104 | - uses: actions/download-artifact@v3 105 | with: 106 | name: "${{ github.sha }}-${{ matrix.part }}" 107 | if: env.GIT_DIFF 108 | - name: test & coverage report creation 109 | run: > 110 | cat pkgs.txt.part.${{ matrix.part }} | 111 | xargs go test -mod=readonly -timeout 30m 112 | -coverprofile=${{ matrix.part }}profile.out 113 | -covermode=atomic -tags='norace ledger test_ledger_mock' 114 | if: env.GIT_DIFF 115 | - uses: actions/upload-artifact@v3 116 | with: 117 | name: "${{ github.sha }}-${{ matrix.part }}-coverage" 118 | path: ./${{ matrix.part }}profile.out 119 | 120 | upload-coverage-report: 121 | runs-on: ubuntu-latest 122 | needs: tests 123 | steps: 124 | - uses: actions/checkout@v3 125 | - uses: technote-space/get-diff-action@v6.1.2 126 | with: 127 | PATTERNS: | 128 | **/**.go 129 | go.mod 130 | go.sum 131 | - uses: actions/download-artifact@v3 132 | with: 133 | name: "${{ github.sha }}-00-coverage" 134 | if: env.GIT_DIFF 135 | - uses: actions/download-artifact@v3 136 | with: 137 | name: "${{ github.sha }}-01-coverage" 138 | if: env.GIT_DIFF 139 | - uses: actions/download-artifact@v3 140 | with: 141 | name: "${{ github.sha }}-02-coverage" 142 | if: env.GIT_DIFF 143 | - uses: actions/download-artifact@v3 144 | with: 145 | name: "${{ github.sha }}-03-coverage" 146 | if: env.GIT_DIFF 147 | - run: | 148 | cat ./*profile.out | grep -v "mode: atomic" >> coverage.txt 149 | if: env.GIT_DIFF 150 | - name: filter out DONTCOVER 151 | # yamllint disable 152 | run: | 153 | (find . -type f -name '*.go' | xargs grep -l 'DONTCOVER') > excludelist.txt 154 | find . -type f -name '*.pb.*' >> excludelist.txt 155 | find . -type f -path './tests/mocks/*.go' >> excludelist.txt 156 | while IFS= read -r filename; do 157 | # trim filename 158 | trimmedname=$(echo $filename | xargs) 159 | echo "Excluding ${trimmedname} from coverage report..." 160 | sed -i.bak "/$(echo $trimmedname | sed 's/\//\\\//g')/d" coverage.txt 161 | done < excludelist.txt 162 | # yamllint enable 163 | if: env.GIT_DIFF 164 | - uses: codecov/codecov-action@v3 165 | with: 166 | file: ./coverage.txt 167 | if: env.GIT_DIFF 168 | 169 | test-race: 170 | runs-on: ubuntu-latest 171 | needs: split-test-files 172 | strategy: 173 | fail-fast: false 174 | matrix: 175 | part: ["00", "01", "02", "03"] 176 | steps: 177 | - uses: actions/checkout@v3 178 | - uses: actions/setup-go@v3 179 | with: 180 | go-version: 1.19 181 | - uses: technote-space/get-diff-action@v6.1.2 182 | with: 183 | PATTERNS: | 184 | **/**.go 185 | go.mod 186 | go.sum 187 | - uses: actions/download-artifact@v3 188 | with: 189 | name: "${{ github.sha }}-${{ matrix.part }}" 190 | if: env.GIT_DIFF 191 | - name: test & coverage report creation 192 | run: > 193 | cat pkgs.txt.part.${{ matrix.part }} | 194 | xargs go test -mod=readonly -json -timeout 30m 195 | -race -test.short -tags='cgo' > ${{ matrix.part }}-race-output.txt 196 | if: env.GIT_DIFF 197 | - uses: actions/upload-artifact@v3 198 | with: 199 | name: "${{ github.sha }}-${{ matrix.part }}-race-output" 200 | path: ./${{ matrix.part }}-race-output.txt 201 | 202 | race-detector-report: 203 | runs-on: ubuntu-latest 204 | needs: [test-race, install-tparse] 205 | timeout-minutes: 5 206 | steps: 207 | - uses: actions/checkout@v3 208 | - uses: technote-space/get-diff-action@v6.1.2 209 | id: git_diff 210 | with: 211 | PATTERNS: | 212 | **/**.go 213 | go.mod 214 | go.sum 215 | - uses: actions/download-artifact@v3 216 | with: 217 | name: "${{ github.sha }}-00-race-output" 218 | if: env.GIT_DIFF 219 | - uses: actions/download-artifact@v3 220 | with: 221 | name: "${{ github.sha }}-01-race-output" 222 | if: env.GIT_DIFF 223 | - uses: actions/download-artifact@v3 224 | with: 225 | name: "${{ github.sha }}-02-race-output" 226 | if: env.GIT_DIFF 227 | - uses: actions/download-artifact@v3 228 | with: 229 | name: "${{ github.sha }}-03-race-output" 230 | if: env.GIT_DIFF 231 | - uses: actions/cache@v3.2.2 232 | with: 233 | path: ~/go/bin 234 | key: ${{ runner.os }}-go-tparse-binary 235 | if: env.GIT_DIFF 236 | - name: Generate test report (go test -race) 237 | run: cat ./*-race-output.txt | ~/go/bin/tparse 238 | if: env.GIT_DIFF 239 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/histolabs/metro 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/ethereum/go-ethereum v1.10.26 7 | github.com/gogo/protobuf v1.3.3 // indirect 8 | github.com/golang/protobuf v1.5.2 // indirect 9 | github.com/google/uuid v1.3.0 // indirect 10 | github.com/gorilla/mux v1.8.0 // indirect 11 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect 12 | github.com/pkg/errors v0.9.1 // indirect 13 | github.com/spf13/cast v1.5.0 14 | github.com/spf13/cobra v1.6.1 15 | github.com/stretchr/testify v1.8.1 16 | github.com/tendermint/tm-db v0.6.7 17 | golang.org/x/crypto v0.4.0 // indirect 18 | golang.org/x/net v0.4.0 // indirect 19 | golang.org/x/sys v0.3.0 // indirect 20 | golang.org/x/term v0.3.0 // indirect 21 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect 22 | google.golang.org/grpc v1.51.0 23 | ) 24 | 25 | require ( 26 | cosmossdk.io/math v1.0.0-beta.4 27 | github.com/cosmos/cosmos-sdk v0.46.6 28 | github.com/tendermint/tendermint v0.34.23 29 | ) 30 | 31 | require ( 32 | cloud.google.com/go v0.105.0 // indirect 33 | cloud.google.com/go/compute v1.12.1 // indirect 34 | cloud.google.com/go/compute/metadata v0.2.1 // indirect 35 | cloud.google.com/go/iam v0.7.0 // indirect 36 | cloud.google.com/go/storage v1.27.0 // indirect 37 | cosmossdk.io/errors v1.0.0-beta.7 // indirect 38 | filippo.io/edwards25519 v1.0.0-rc.1 // indirect 39 | github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect 40 | github.com/99designs/keyring v1.2.1 // indirect 41 | github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect 42 | github.com/Workiva/go-datastructures v1.0.53 // indirect 43 | github.com/armon/go-metrics v0.4.1 // indirect 44 | github.com/aws/aws-sdk-go v1.40.45 // indirect 45 | github.com/beorn7/perks v1.0.1 // indirect 46 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 47 | github.com/bgentry/speakeasy v0.1.0 // indirect 48 | github.com/btcsuite/btcd v0.22.1 // indirect 49 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 50 | github.com/celestiaorg/nmt v0.12.0 // indirect 51 | github.com/cenkalti/backoff/v4 v4.1.3 // indirect 52 | github.com/cespare/xxhash v1.1.0 // indirect 53 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 54 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect 55 | github.com/cockroachdb/apd/v2 v2.0.2 // indirect 56 | github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect 57 | github.com/confio/ics23/go v0.9.0 // indirect 58 | github.com/cosmos/btcutil v1.0.5 // indirect 59 | github.com/cosmos/cosmos-proto v1.0.0-beta.1 // indirect 60 | github.com/cosmos/go-bip39 v1.0.0 // indirect 61 | github.com/cosmos/gogoproto v1.4.3 // indirect 62 | github.com/cosmos/gorocksdb v1.2.0 // indirect 63 | github.com/cosmos/iavl v0.19.4 // indirect 64 | github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect 65 | github.com/creachadair/taskgroup v0.3.2 // indirect 66 | github.com/danieljoos/wincred v1.1.2 // indirect 67 | github.com/davecgh/go-spew v1.1.1 // indirect 68 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect 69 | github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect 70 | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect 71 | github.com/dgraph-io/ristretto v0.1.1 // indirect 72 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 73 | github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect 74 | github.com/dvsekhvalnov/jose2go v1.5.0 // indirect 75 | github.com/felixge/httpsnoop v1.0.2 // indirect 76 | github.com/fsnotify/fsnotify v1.6.0 // indirect 77 | github.com/gin-gonic/gin v1.7.0 // indirect 78 | github.com/go-kit/kit v0.12.0 // indirect 79 | github.com/go-kit/log v0.2.1 // indirect 80 | github.com/go-logfmt/logfmt v0.5.1 // indirect 81 | github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect 82 | github.com/gogo/gateway v1.1.0 // indirect 83 | github.com/golang/glog v1.0.0 // indirect 84 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 85 | github.com/golang/snappy v0.0.4 // indirect 86 | github.com/google/btree v1.1.2 // indirect 87 | github.com/google/go-cmp v0.5.9 // indirect 88 | github.com/google/orderedcode v0.0.1 // indirect 89 | github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect 90 | github.com/googleapis/gax-go/v2 v2.6.0 // indirect 91 | github.com/gorilla/handlers v1.5.1 // indirect 92 | github.com/gorilla/websocket v1.5.0 // indirect 93 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect 94 | github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect 95 | github.com/gtank/merlin v0.1.1 // indirect 96 | github.com/gtank/ristretto255 v0.1.2 // indirect 97 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 98 | github.com/hashicorp/go-getter v1.6.2 // indirect 99 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect 100 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 101 | github.com/hashicorp/go-version v1.6.0 // indirect 102 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect 103 | github.com/hashicorp/hcl v1.0.0 // indirect 104 | github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect 105 | github.com/improbable-eng/grpc-web v0.15.0 // indirect 106 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 107 | github.com/jmespath/go-jmespath v0.4.0 // indirect 108 | github.com/jmhodges/levigo v1.0.0 // indirect 109 | github.com/klauspost/compress v1.15.12 // indirect 110 | github.com/lib/pq v1.10.7 // indirect 111 | github.com/libp2p/go-buffer-pool v0.1.0 // indirect 112 | github.com/magiconair/properties v1.8.7 // indirect 113 | github.com/manifoldco/promptui v0.9.0 // indirect 114 | github.com/mattn/go-colorable v0.1.13 // indirect 115 | github.com/mattn/go-isatty v0.0.16 // indirect 116 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 117 | github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect 118 | github.com/minio/highwayhash v1.0.2 // indirect 119 | github.com/mitchellh/go-homedir v1.1.0 // indirect 120 | github.com/mitchellh/go-testing-interface v1.0.0 // indirect 121 | github.com/mitchellh/mapstructure v1.5.0 // indirect 122 | github.com/mtibben/percent v0.2.1 // indirect 123 | github.com/nxadm/tail v1.4.8 // indirect 124 | github.com/pelletier/go-toml v1.9.5 // indirect 125 | github.com/pelletier/go-toml/v2 v2.0.5 // indirect 126 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect 127 | github.com/pmezard/go-difflib v1.0.0 // indirect 128 | github.com/prometheus/client_golang v1.14.0 // indirect 129 | github.com/prometheus/client_model v0.3.0 // indirect 130 | github.com/prometheus/common v0.39.0 // indirect 131 | github.com/prometheus/procfs v0.8.0 // indirect 132 | github.com/rakyll/statik v0.1.7 // indirect 133 | github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect 134 | github.com/regen-network/cosmos-proto v0.3.1 // indirect 135 | github.com/rs/cors v1.8.2 // indirect 136 | github.com/rs/zerolog v1.28.0 // indirect 137 | github.com/sasha-s/go-deadlock v0.3.1 // indirect 138 | github.com/spf13/afero v1.9.2 // indirect 139 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 140 | github.com/spf13/pflag v1.0.5 // indirect 141 | github.com/spf13/viper v1.14.0 // indirect 142 | github.com/subosito/gotenv v1.4.1 // indirect 143 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect 144 | github.com/tendermint/btcd v0.1.1 // indirect 145 | github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect 146 | github.com/tendermint/go-amino v0.16.0 // indirect 147 | github.com/ulikunitz/xz v0.5.8 // indirect 148 | github.com/zondax/hid v0.9.1 // indirect 149 | github.com/zondax/ledger-go v0.14.0 // indirect 150 | go.etcd.io/bbolt v1.3.6 // indirect 151 | go.opencensus.io v0.23.0 // indirect 152 | golang.org/x/exp v0.0.0-20221019170559-20944726eadf // indirect 153 | golang.org/x/oauth2 v0.3.0 // indirect 154 | golang.org/x/text v0.5.0 // indirect 155 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 156 | google.golang.org/api v0.102.0 // indirect 157 | google.golang.org/appengine v1.6.7 // indirect 158 | google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect 159 | gopkg.in/ini.v1 v1.67.0 // indirect 160 | gopkg.in/yaml.v2 v2.4.0 // indirect 161 | gopkg.in/yaml.v3 v3.0.1 // indirect 162 | nhooyr.io/websocket v1.8.6 // indirect 163 | sigs.k8s.io/yaml v1.3.0 // indirect 164 | ) 165 | 166 | replace ( 167 | github.com/cosmos/cosmos-sdk => github.com/histolabs/cosmos-sdk v0.0.0-20221230021044-779df3c9a0f5 168 | github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 169 | github.com/tendermint/tendermint => github.com/histolabs/tendermint v0.0.0-20221227204023-ec49dd7d580f 170 | ) 171 | -------------------------------------------------------------------------------- /third_party/proto/confio/proofs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package ics23; 4 | option go_package = "github.com/confio/ics23/go"; 5 | 6 | enum HashOp { 7 | // NO_HASH is the default if no data passed. Note this is an illegal argument some places. 8 | NO_HASH = 0; 9 | SHA256 = 1; 10 | SHA512 = 2; 11 | KECCAK = 3; 12 | RIPEMD160 = 4; 13 | BITCOIN = 5; // ripemd160(sha256(x)) 14 | } 15 | 16 | /** 17 | LengthOp defines how to process the key and value of the LeafOp 18 | to include length information. After encoding the length with the given 19 | algorithm, the length will be prepended to the key and value bytes. 20 | (Each one with it's own encoded length) 21 | */ 22 | enum LengthOp { 23 | // NO_PREFIX don't include any length info 24 | NO_PREFIX = 0; 25 | // VAR_PROTO uses protobuf (and go-amino) varint encoding of the length 26 | VAR_PROTO = 1; 27 | // VAR_RLP uses rlp int encoding of the length 28 | VAR_RLP = 2; 29 | // FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer 30 | FIXED32_BIG = 3; 31 | // FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer 32 | FIXED32_LITTLE = 4; 33 | // FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer 34 | FIXED64_BIG = 5; 35 | // FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer 36 | FIXED64_LITTLE = 6; 37 | // REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output) 38 | REQUIRE_32_BYTES = 7; 39 | // REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output) 40 | REQUIRE_64_BYTES = 8; 41 | } 42 | 43 | /** 44 | ExistenceProof takes a key and a value and a set of steps to perform on it. 45 | The result of peforming all these steps will provide a "root hash", which can 46 | be compared to the value in a header. 47 | 48 | Since it is computationally infeasible to produce a hash collision for any of the used 49 | cryptographic hash functions, if someone can provide a series of operations to transform 50 | a given key and value into a root hash that matches some trusted root, these key and values 51 | must be in the referenced merkle tree. 52 | 53 | The only possible issue is maliablity in LeafOp, such as providing extra prefix data, 54 | which should be controlled by a spec. Eg. with lengthOp as NONE, 55 | prefix = FOO, key = BAR, value = CHOICE 56 | and 57 | prefix = F, key = OOBAR, value = CHOICE 58 | would produce the same value. 59 | 60 | With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field 61 | in the ProofSpec is valuable to prevent this mutability. And why all trees should 62 | length-prefix the data before hashing it. 63 | */ 64 | message ExistenceProof { 65 | bytes key = 1; 66 | bytes value = 2; 67 | LeafOp leaf = 3; 68 | repeated InnerOp path = 4; 69 | } 70 | 71 | /* 72 | NonExistenceProof takes a proof of two neighbors, one left of the desired key, 73 | one right of the desired key. If both proofs are valid AND they are neighbors, 74 | then there is no valid proof for the given key. 75 | */ 76 | message NonExistenceProof { 77 | bytes key = 1; // TODO: remove this as unnecessary??? we prove a range 78 | ExistenceProof left = 2; 79 | ExistenceProof right = 3; 80 | } 81 | 82 | /* 83 | CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages 84 | */ 85 | message CommitmentProof { 86 | oneof proof { 87 | ExistenceProof exist = 1; 88 | NonExistenceProof nonexist = 2; 89 | BatchProof batch = 3; 90 | CompressedBatchProof compressed = 4; 91 | } 92 | } 93 | 94 | /** 95 | LeafOp represents the raw key-value data we wish to prove, and 96 | must be flexible to represent the internal transformation from 97 | the original key-value pairs into the basis hash, for many existing 98 | merkle trees. 99 | 100 | key and value are passed in. So that the signature of this operation is: 101 | leafOp(key, value) -> output 102 | 103 | To process this, first prehash the keys and values if needed (ANY means no hash in this case): 104 | hkey = prehashKey(key) 105 | hvalue = prehashValue(value) 106 | 107 | Then combine the bytes, and hash it 108 | output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue) 109 | */ 110 | message LeafOp { 111 | HashOp hash = 1; 112 | HashOp prehash_key = 2; 113 | HashOp prehash_value = 3; 114 | LengthOp length = 4; 115 | // prefix is a fixed bytes that may optionally be included at the beginning to differentiate 116 | // a leaf node from an inner node. 117 | bytes prefix = 5; 118 | } 119 | 120 | /** 121 | InnerOp represents a merkle-proof step that is not a leaf. 122 | It represents concatenating two children and hashing them to provide the next result. 123 | 124 | The result of the previous step is passed in, so the signature of this op is: 125 | innerOp(child) -> output 126 | 127 | The result of applying InnerOp should be: 128 | output = op.hash(op.prefix || child || op.suffix) 129 | 130 | where the || operator is concatenation of binary data, 131 | and child is the result of hashing all the tree below this step. 132 | 133 | Any special data, like prepending child with the length, or prepending the entire operation with 134 | some value to differentiate from leaf nodes, should be included in prefix and suffix. 135 | If either of prefix or suffix is empty, we just treat it as an empty string 136 | */ 137 | message InnerOp { 138 | HashOp hash = 1; 139 | bytes prefix = 2; 140 | bytes suffix = 3; 141 | } 142 | 143 | 144 | /** 145 | ProofSpec defines what the expected parameters are for a given proof type. 146 | This can be stored in the client and used to validate any incoming proofs. 147 | 148 | verify(ProofSpec, Proof) -> Proof | Error 149 | 150 | As demonstrated in tests, if we don't fix the algorithm used to calculate the 151 | LeafHash for a given tree, there are many possible key-value pairs that can 152 | generate a given hash (by interpretting the preimage differently). 153 | We need this for proper security, requires client knows a priori what 154 | tree format server uses. But not in code, rather a configuration object. 155 | */ 156 | message ProofSpec { 157 | // any field in the ExistenceProof must be the same as in this spec. 158 | // except Prefix, which is just the first bytes of prefix (spec can be longer) 159 | LeafOp leaf_spec = 1; 160 | InnerSpec inner_spec = 2; 161 | // max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries) 162 | int32 max_depth = 3; 163 | // min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries) 164 | int32 min_depth = 4; 165 | } 166 | 167 | /* 168 | InnerSpec contains all store-specific structure info to determine if two proofs from a 169 | given store are neighbors. 170 | 171 | This enables: 172 | 173 | isLeftMost(spec: InnerSpec, op: InnerOp) 174 | isRightMost(spec: InnerSpec, op: InnerOp) 175 | isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp) 176 | */ 177 | message InnerSpec { 178 | // Child order is the ordering of the children node, must count from 0 179 | // iavl tree is [0, 1] (left then right) 180 | // merk is [0, 2, 1] (left, right, here) 181 | repeated int32 child_order = 1; 182 | int32 child_size = 2; 183 | int32 min_prefix_length = 3; 184 | int32 max_prefix_length = 4; 185 | // empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0) 186 | bytes empty_child = 5; 187 | // hash is the algorithm that must be used for each InnerOp 188 | HashOp hash = 6; 189 | } 190 | 191 | /* 192 | BatchProof is a group of multiple proof types than can be compressed 193 | */ 194 | message BatchProof { 195 | repeated BatchEntry entries = 1; 196 | } 197 | 198 | // Use BatchEntry not CommitmentProof, to avoid recursion 199 | message BatchEntry { 200 | oneof proof { 201 | ExistenceProof exist = 1; 202 | NonExistenceProof nonexist = 2; 203 | } 204 | } 205 | 206 | 207 | /****** all items here are compressed forms *******/ 208 | 209 | message CompressedBatchProof { 210 | repeated CompressedBatchEntry entries = 1; 211 | repeated InnerOp lookup_inners = 2; 212 | } 213 | 214 | // Use BatchEntry not CommitmentProof, to avoid recursion 215 | message CompressedBatchEntry { 216 | oneof proof { 217 | CompressedExistenceProof exist = 1; 218 | CompressedNonExistenceProof nonexist = 2; 219 | } 220 | } 221 | 222 | message CompressedExistenceProof { 223 | bytes key = 1; 224 | bytes value = 2; 225 | LeafOp leaf = 3; 226 | // these are indexes into the lookup_inners table in CompressedBatchProof 227 | repeated int32 path = 4; 228 | } 229 | 230 | message CompressedNonExistenceProof { 231 | bytes key = 1; // TODO: remove this as unnecessary??? we prove a range 232 | CompressedExistenceProof left = 2; 233 | CompressedExistenceProof right = 3; 234 | } 235 | -------------------------------------------------------------------------------- /pkg/builder/builder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | sdkclient "github.com/cosmos/cosmos-sdk/client" 11 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 12 | sdktypes "github.com/cosmos/cosmos-sdk/types" 13 | "github.com/cosmos/cosmos-sdk/types/tx" 14 | "github.com/cosmos/cosmos-sdk/types/tx/signing" 15 | authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" 16 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 17 | "github.com/histolabs/metro/app/encoding" 18 | "github.com/histolabs/metro/pkg/consts" 19 | "google.golang.org/grpc" 20 | ) 21 | 22 | // KeyringSigner uses a keyring to sign and build celestia-app transactions 23 | type KeyringSigner struct { 24 | keyring.Keyring 25 | keyringAccName string 26 | 27 | chainID string 28 | secondaryChainID string 29 | encCfg encoding.Config 30 | 31 | sync.RWMutex 32 | acc authtypes.AccountI 33 | } 34 | 35 | // NewKeyringSigner returns a new KeyringSigner using the provided keyring 36 | func NewKeyringSigner(encCfg encoding.Config, ring keyring.Keyring, name string, chainID string) *KeyringSigner { 37 | ids := strings.SplitN(chainID, consts.ChainIDSeparator, 2) 38 | secondaryChainID := "" 39 | if len(ids) > 1 { 40 | secondaryChainID = ids[1] 41 | } 42 | 43 | return &KeyringSigner{ 44 | Keyring: ring, 45 | keyringAccName: name, 46 | chainID: chainID, 47 | secondaryChainID: secondaryChainID, 48 | encCfg: encCfg, 49 | } 50 | } 51 | 52 | // Update queries the application to find the latest account number and 53 | // sequence, updating the respective internal fields. The internal account 54 | // number must be set by this method or by manually calling k.SetAccountNumber 55 | // in order for any built transactions to be valid 56 | func (k *KeyringSigner) UpdateAccount(ctx context.Context, conn *grpc.ClientConn) error { 57 | info, err := k.Key(k.keyringAccName) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | addr, err := info.GetAddress() 63 | if err != nil { 64 | return err 65 | } 66 | 67 | acc, err := QueryAccount(ctx, conn, k.encCfg, addr.String()) 68 | if err != nil { 69 | return err 70 | } 71 | k.Lock() 72 | defer k.Unlock() 73 | 74 | k.acc = acc 75 | return nil 76 | } 77 | 78 | func (k *KeyringSigner) UpdateAccountFromClient(clientCtx client.Context) error { 79 | rec := k.GetSignerInfo() 80 | 81 | addr, err := rec.GetAddress() 82 | if err != nil { 83 | return err 84 | } 85 | 86 | accNum, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | k.SetAccountNumber(accNum) 92 | k.SetSequence(seq) 93 | 94 | return nil 95 | } 96 | 97 | // NewTxBuilder returns the default sdk Tx builder using the celestia-app encoding config 98 | func (k *KeyringSigner) NewTxBuilder(opts ...TxBuilderOption) sdkclient.TxBuilder { 99 | builder := k.encCfg.TxConfig.NewTxBuilder() 100 | for _, opt := range opts { 101 | builder = opt(builder) 102 | } 103 | return builder 104 | } 105 | 106 | // BuildSignedTx creates and signs a sdk.Tx that contains the provided message. The interal 107 | // account number must be set by calling k.QueryAccountNumber or by manually setting it via 108 | // k.SetAccountNumber for the built transactions to be valid. 109 | func (k *KeyringSigner) BuildSignedTx(builder sdkclient.TxBuilder, isSecondary bool, msg ...sdktypes.Msg) (authsigning.Tx, error) { 110 | k.RLock() 111 | sequence := k.acc.GetSequence() 112 | if isSecondary { 113 | sequence = k.acc.GetSecondarySequence(k.secondaryChainID) 114 | builder.SetSecondaryChainID(k.secondaryChainID) 115 | } 116 | k.RUnlock() 117 | 118 | // set the msg 119 | err := builder.SetMsgs(msg...) 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | // lookup account info 125 | keyInfo, err := k.Key(k.keyringAccName) 126 | if err != nil { 127 | return nil, err 128 | } 129 | 130 | pub, err := keyInfo.GetPubKey() 131 | if err != nil { 132 | return nil, err 133 | } 134 | 135 | // we must first set an empty signature in order generate 136 | // the correct sign bytes 137 | sigV2 := signing.SignatureV2{ 138 | PubKey: pub, 139 | Data: &signing.SingleSignatureData{ 140 | SignMode: signing.SignMode_SIGN_MODE_DIRECT, 141 | Signature: nil, 142 | }, 143 | Sequence: sequence, 144 | } 145 | 146 | // set the empty signature 147 | err = builder.SetSignatures(sigV2) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | signerData, err := k.GetSignerData(isSecondary) 153 | if err != nil { 154 | return nil, err 155 | } 156 | 157 | // Generate the bytes to be signed. 158 | bytesToSign, err := k.encCfg.TxConfig.SignModeHandler().GetSignBytes( 159 | signing.SignMode_SIGN_MODE_DIRECT, 160 | signerData, 161 | builder.GetTx(), 162 | ) 163 | if err != nil { 164 | return nil, err 165 | } 166 | 167 | addr, err := keyInfo.GetAddress() 168 | if err != nil { 169 | return nil, err 170 | } 171 | 172 | // Sign those bytes using the keyring. we are ignoring the returned public key 173 | sigBytes, _, err := k.SignByAddress(addr, bytesToSign) 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | // Construct the SignatureV2 struct, this time including a real signature 179 | sigV2 = signing.SignatureV2{ 180 | PubKey: pub, 181 | Data: &signing.SingleSignatureData{ 182 | SignMode: signing.SignMode_SIGN_MODE_DIRECT, 183 | Signature: sigBytes, 184 | }, 185 | Sequence: sequence, 186 | } 187 | 188 | // set the final signature 189 | err = builder.SetSignatures(sigV2) 190 | if err != nil { 191 | return nil, err 192 | } 193 | 194 | // return the signed transaction 195 | return builder.GetTx(), nil 196 | } 197 | 198 | // SetAccountNumber manually sets the underlying account number 199 | func (k *KeyringSigner) SetAccountNumber(n uint64) error { 200 | k.Lock() 201 | defer k.Unlock() 202 | 203 | return k.acc.SetAccountNumber(n) 204 | } 205 | 206 | // SetSequence manually sets the underlying sequence number 207 | func (k *KeyringSigner) SetSequence(n uint64) error { 208 | k.Lock() 209 | defer k.Unlock() 210 | 211 | return k.acc.SetSequence(n) 212 | } 213 | 214 | // SetSecondarySequence manually sets the underlying secondary sequence number 215 | func (k *KeyringSigner) SetSecondarySequence(id string, n uint64) error { 216 | k.Lock() 217 | defer k.Unlock() 218 | 219 | return k.acc.SetSecondarySequence(id, n) 220 | } 221 | 222 | // SetKeyringAccName manually sets the underlying keyring account name 223 | func (k *KeyringSigner) SetKeyringAccName(name string) { 224 | k.keyringAccName = name 225 | } 226 | 227 | // GetSignerInfo returns the signer info for the KeyringSigner's account. panics 228 | // if the account in KeyringSigner does not exist. 229 | func (k *KeyringSigner) GetSignerInfo() *keyring.Record { 230 | info, err := k.Key(k.keyringAccName) 231 | if err != nil { 232 | panic(err) 233 | } 234 | return info 235 | } 236 | 237 | func (k *KeyringSigner) GetSignerData(isSecondary bool) (authsigning.SignerData, error) { 238 | k.RLock() 239 | accountNumber := k.acc.GetAccountNumber() 240 | sequence := k.acc.GetSequence() 241 | if isSecondary { 242 | if k.secondaryChainID == "" { 243 | k.RUnlock() 244 | return authsigning.SignerData{}, errors.New("no secondary chain-id set for secondary tx") 245 | } 246 | accountNumber = 0 247 | sequence = k.acc.GetSecondarySequence(k.secondaryChainID) 248 | } 249 | k.RUnlock() 250 | 251 | record, err := k.Key(k.keyringAccName) 252 | if err != nil { 253 | return authsigning.SignerData{}, err 254 | } 255 | 256 | pubKey, err := record.GetPubKey() 257 | if err != nil { 258 | return authsigning.SignerData{}, err 259 | } 260 | 261 | address := pubKey.Address() 262 | 263 | return authsigning.SignerData{ 264 | Address: address.String(), 265 | ChainID: k.chainID, 266 | AccountNumber: accountNumber, 267 | Sequence: sequence, 268 | PubKey: pubKey, 269 | }, nil 270 | } 271 | 272 | // EncodeTx uses the keyring signer's encoding config to encode the provided sdk transaction 273 | func (k *KeyringSigner) EncodeTx(tx sdktypes.Tx) ([]byte, error) { 274 | return k.encCfg.TxConfig.TxEncoder()(tx) 275 | } 276 | 277 | // BroadcastTx uses the provided grpc connection to broadcast a signed and encoded transaction 278 | func BroadcastTx(ctx context.Context, conn *grpc.ClientConn, mode tx.BroadcastMode, txBytes []byte) (*tx.BroadcastTxResponse, error) { 279 | txClient := tx.NewServiceClient(conn) 280 | 281 | return txClient.BroadcastTx( 282 | ctx, 283 | &tx.BroadcastTxRequest{ 284 | Mode: mode, 285 | TxBytes: txBytes, 286 | }, 287 | ) 288 | } 289 | 290 | // QueryAccount fetches the account number and sequence number from the celestia-app node. 291 | func QueryAccount(ctx context.Context, conn *grpc.ClientConn, encCfg encoding.Config, address string) (authtypes.AccountI, error) { 292 | qclient := authtypes.NewQueryClient(conn) 293 | resp, err := qclient.Account( 294 | ctx, 295 | &authtypes.QueryAccountRequest{Address: address}, 296 | ) 297 | if err != nil { 298 | return nil, err 299 | } 300 | 301 | var acc authtypes.AccountI 302 | err = encCfg.InterfaceRegistry.UnpackAny(resp.Account, &acc) 303 | if err != nil { 304 | return nil, err 305 | } 306 | 307 | return acc, nil 308 | } 309 | -------------------------------------------------------------------------------- /cmd/metro/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "time" 8 | 9 | "github.com/cosmos/cosmos-sdk/simapp/simd/cmd" 10 | "github.com/cosmos/cosmos-sdk/x/crisis" 11 | "github.com/histolabs/metro/app" 12 | "github.com/histolabs/metro/app/encoding" 13 | "github.com/histolabs/metro/pkg/consts" 14 | 15 | "github.com/cosmos/cosmos-sdk/baseapp" 16 | "github.com/cosmos/cosmos-sdk/client" 17 | "github.com/cosmos/cosmos-sdk/client/config" 18 | "github.com/cosmos/cosmos-sdk/client/debug" 19 | "github.com/cosmos/cosmos-sdk/client/flags" 20 | "github.com/cosmos/cosmos-sdk/client/keys" 21 | "github.com/cosmos/cosmos-sdk/client/rpc" 22 | "github.com/cosmos/cosmos-sdk/codec" 23 | "github.com/cosmos/cosmos-sdk/server" 24 | serverconfig "github.com/cosmos/cosmos-sdk/server/config" 25 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 26 | "github.com/cosmos/cosmos-sdk/store" 27 | sdk "github.com/cosmos/cosmos-sdk/types" 28 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" 29 | "github.com/cosmos/cosmos-sdk/x/auth/types" 30 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 31 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" 32 | "github.com/spf13/cast" 33 | "github.com/spf13/cobra" 34 | tmcmds "github.com/tendermint/tendermint/cmd/tendermint/commands" 35 | tmcfg "github.com/tendermint/tendermint/config" 36 | tmcli "github.com/tendermint/tendermint/libs/cli" 37 | "github.com/tendermint/tendermint/libs/log" 38 | dbm "github.com/tendermint/tm-db" 39 | ) 40 | 41 | const EnvPrefix = "CELESTIA" 42 | 43 | // NewRootCmd creates a new root command for metro. It is called once in the 44 | // main function. 45 | func NewRootCmd() *cobra.Command { 46 | encodingConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...) 47 | 48 | cfg := sdk.GetConfig() 49 | cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) 50 | cfg.SetBech32PrefixForValidator(app.Bech32PrefixValAddr, app.Bech32PrefixValPub) 51 | cfg.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) 52 | cfg.Seal() 53 | 54 | initClientCtx := client.Context{}. 55 | WithCodec(encodingConfig.Codec). 56 | WithInterfaceRegistry(encodingConfig.InterfaceRegistry). 57 | WithTxConfig(encodingConfig.TxConfig). 58 | WithLegacyAmino(encodingConfig.Amino). 59 | WithInput(os.Stdin). 60 | WithAccountRetriever(types.AccountRetriever{}). 61 | WithBroadcastMode(flags.BroadcastBlock). 62 | WithHomeDir(app.DefaultNodeHome). 63 | WithViper(EnvPrefix) 64 | 65 | rootCmd := &cobra.Command{ 66 | Use: "metro", 67 | Short: "Start metro app", 68 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { 69 | initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) 70 | if err != nil { 71 | return err 72 | } 73 | initClientCtx, err = config.ReadFromClientConfig(initClientCtx) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { 79 | return err 80 | } 81 | 82 | // Override the default tendermint config for metro 83 | tmCfg := tmcfg.DefaultConfig() 84 | 85 | // Set broadcast timeout to be 50 seconds in order to avoid timeouts for long block times 86 | tmCfg.RPC.TimeoutBroadcastTxCommit = 50 * time.Second 87 | tmCfg.RPC.MaxBodyBytes = int64(8388608) // 8 MiB 88 | tmCfg.Mempool.TTLNumBlocks = 10 89 | tmCfg.Mempool.MaxTxBytes = 2 * 1024 * 1024 // 2 MiB 90 | tmCfg.Mempool.Version = "v1" // prioritized mempool 91 | tmCfg.Consensus.TimeoutPropose = time.Second * 10 92 | tmCfg.Consensus.TimeoutCommit = time.Second * 8 93 | tmCfg.Consensus.SkipTimeoutCommit = false 94 | 95 | customAppTemplate, customAppConfig := initAppConfig() 96 | return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, tmCfg) 97 | }, 98 | SilenceUsage: true, 99 | } 100 | 101 | initRootCmd(rootCmd, encodingConfig) 102 | 103 | return rootCmd 104 | } 105 | 106 | // initAppConfig helps to override default appConfig template and configs. 107 | // return "", nil if no custom configuration is required for the application. 108 | func initAppConfig() (string, interface{}) { 109 | type CustomAppConfig struct { 110 | serverconfig.Config 111 | } 112 | 113 | // Optionally allow the chain developer to overwrite the SDK's default 114 | // server config. 115 | srvCfg := serverconfig.DefaultConfig() 116 | srvCfg.API.Enable = true 117 | 118 | // the default snapshot interval was determined by picking a large enough 119 | // value as to not dramatically increase resource requirements while also 120 | // being greater than zero so that there are more nodes that will serve 121 | // snapshots to nodes that state sync 122 | srvCfg.StateSync.SnapshotInterval = 1500 123 | srvCfg.StateSync.SnapshotKeepRecent = 2 124 | srvCfg.MinGasPrices = fmt.Sprintf("0.001%s", consts.BondDenom) 125 | 126 | CelestiaAppCfg := CustomAppConfig{Config: *srvCfg} 127 | 128 | CelestiaAppTemplate := serverconfig.DefaultConfigTemplate 129 | 130 | return CelestiaAppTemplate, CelestiaAppCfg 131 | } 132 | 133 | func initRootCmd(rootCmd *cobra.Command, encodingConfig encoding.Config) { 134 | cfg := sdk.GetConfig() 135 | cfg.Seal() 136 | 137 | debugCmd := debug.Cmd() 138 | 139 | rootCmd.AddCommand( 140 | genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome), 141 | genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), 142 | genutilcli.MigrateGenesisCmd(), 143 | cmd.AddGenesisAccountCmd(app.DefaultNodeHome), 144 | genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), 145 | genutilcli.ValidateGenesisCmd(app.ModuleBasics), 146 | tmcli.NewCompletionCmd(rootCmd, true), 147 | tmcmds.RollbackStateCmd, 148 | debugCmd, 149 | config.Cmd(), 150 | ) 151 | 152 | server.AddCommands(rootCmd, app.DefaultNodeHome, NewAppServer, createAppAndExport, addModuleInitFlags) 153 | 154 | // set the default keyring backend to `file` 155 | keybase := keys.Commands(app.DefaultNodeHome) 156 | keybase.Flag(flags.FlagKeyringBackend).DefValue = "file" 157 | 158 | // add keybase, auxiliary RPC, query, and tx child commands 159 | rootCmd.AddCommand( 160 | rpc.StatusCommand(), 161 | queryCommand(), 162 | txCommand(), 163 | keybase, 164 | ) 165 | } 166 | 167 | func addModuleInitFlags(startCmd *cobra.Command) { 168 | crisis.AddModuleInitFlags(startCmd) 169 | } 170 | 171 | func queryCommand() *cobra.Command { 172 | cmd := &cobra.Command{ 173 | Use: "query", 174 | Aliases: []string{"q"}, 175 | Short: "Querying subcommands", 176 | DisableFlagParsing: true, 177 | SuggestionsMinimumDistance: 2, 178 | RunE: client.ValidateCmd, 179 | } 180 | 181 | cmd.AddCommand( 182 | authcmd.GetAccountCmd(), 183 | rpc.ValidatorCommand(), 184 | rpc.BlockCommand(), 185 | authcmd.QueryTxsByEventsCmd(), 186 | authcmd.QueryTxCmd(), 187 | ) 188 | 189 | app.ModuleBasics.AddQueryCommands(cmd) 190 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") 191 | 192 | return cmd 193 | } 194 | 195 | func txCommand() *cobra.Command { 196 | cmd := &cobra.Command{ 197 | Use: "tx", 198 | Short: "Transactions subcommands", 199 | DisableFlagParsing: true, 200 | SuggestionsMinimumDistance: 2, 201 | RunE: client.ValidateCmd, 202 | } 203 | 204 | cmd.AddCommand( 205 | authcmd.GetSignCommand(), 206 | authcmd.GetSignBatchCommand(), 207 | authcmd.GetMultiSignCommand(), 208 | authcmd.GetValidateSignaturesCommand(), 209 | flags.LineBreak, 210 | authcmd.GetBroadcastCommand(), 211 | authcmd.GetEncodeCommand(), 212 | authcmd.GetDecodeCommand(), 213 | ) 214 | 215 | app.ModuleBasics.AddTxCommands(cmd) 216 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") 217 | 218 | return cmd 219 | } 220 | 221 | func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { 222 | var cache sdk.MultiStorePersistentCache 223 | 224 | if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { 225 | cache = store.NewCommitKVStoreCacheManager() 226 | } 227 | 228 | skipUpgradeHeights := make(map[int64]bool) 229 | for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { 230 | skipUpgradeHeights[int64(h)] = true 231 | } 232 | 233 | pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) 234 | if err != nil { 235 | panic(err) 236 | } 237 | 238 | return app.New( 239 | logger, db, traceStore, true, skipUpgradeHeights, 240 | cast.ToString(appOpts.Get(flags.FlagHome)), 241 | cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), 242 | encoding.MakeConfig(app.ModuleEncodingRegisters...), // Ideally, we would reuse the one created by NewRootCmd. 243 | appOpts, 244 | baseapp.SetPruning(pruningOpts), 245 | baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), 246 | baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), 247 | baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), 248 | baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), 249 | baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), 250 | baseapp.SetInterBlockCache(cache), 251 | baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), 252 | baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), 253 | ) 254 | } 255 | 256 | func createAppAndExport( 257 | logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, 258 | appOpts servertypes.AppOptions, 259 | ) (servertypes.ExportedApp, error) { 260 | encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) // Ideally, we would reuse the one created by NewRootCmd. 261 | encCfg.Codec = codec.NewProtoCodec(encCfg.InterfaceRegistry) 262 | var capp *app.App 263 | if height != -1 { 264 | capp = app.New(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), encCfg, appOpts) 265 | 266 | if err := capp.LoadHeight(height); err != nil { 267 | return servertypes.ExportedApp{}, err 268 | } 269 | } else { 270 | capp = app.New(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), encCfg, appOpts) 271 | } 272 | 273 | return capp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) 274 | } 275 | -------------------------------------------------------------------------------- /testutil/test_app.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/cosmos/cosmos-sdk/client/flags" 9 | "github.com/cosmos/cosmos-sdk/codec" 10 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 11 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 12 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 13 | "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" 14 | "github.com/cosmos/cosmos-sdk/server" 15 | "github.com/cosmos/cosmos-sdk/simapp" 16 | "github.com/cosmos/cosmos-sdk/testutil/mock" 17 | sdk "github.com/cosmos/cosmos-sdk/types" 18 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 19 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 20 | "github.com/histolabs/metro/app" 21 | "github.com/histolabs/metro/app/encoding" 22 | "github.com/histolabs/metro/pkg/consts" 23 | "github.com/histolabs/metro/testutil/testfactory" 24 | "github.com/spf13/cast" 25 | abci "github.com/tendermint/tendermint/abci/types" 26 | "github.com/tendermint/tendermint/libs/log" 27 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 28 | tmtypes "github.com/tendermint/tendermint/types" 29 | dbm "github.com/tendermint/tm-db" 30 | 31 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 32 | ) 33 | 34 | const ( 35 | ChainID = "testapp" 36 | ) 37 | 38 | // Get flags every time the simulator is run 39 | func init() { 40 | simapp.GetSimulatorFlags() 41 | } 42 | 43 | // DefaultConsensusParams defines the default Tendermint consensus params used in 44 | // SimApp testing. 45 | var DefaultConsensusParams = &abci.ConsensusParams{ 46 | Block: &abci.BlockParams{ 47 | MaxBytes: 200000, 48 | MaxGas: 2000000, 49 | }, 50 | Evidence: &tmproto.EvidenceParams{ 51 | MaxAgeNumBlocks: 302400, 52 | MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration 53 | MaxBytes: 10000, 54 | }, 55 | Validator: &tmproto.ValidatorParams{ 56 | PubKeyTypes: []string{ 57 | tmtypes.ABCIPubKeyTypeEd25519, 58 | }, 59 | }, 60 | } 61 | 62 | type emptyAppOptions struct{} 63 | 64 | // Get implements AppOptions 65 | func (ao emptyAppOptions) Get(o string) interface{} { 66 | return nil 67 | } 68 | 69 | // SetupTestAppWithGenesisValSet initializes a new app with a validator set and genesis accounts 70 | // that also act as delegators. For simplicity, each validator is bonded with a delegation 71 | // of one consensus engine unit in the default token of the app from first genesis 72 | // account. A Nop logger is set in app. 73 | func SetupTestAppWithGenesisValSet(genAccounts ...string) (*app.App, keyring.Keyring) { 74 | // var cache sdk.MultiStorePersistentCache 75 | // EmptyAppOptions is a stub implementing AppOptions 76 | emptyOpts := emptyAppOptions{} 77 | // var anteOpt = func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(nil) } 78 | db := dbm.NewMemDB() 79 | skipUpgradeHeights := make(map[int64]bool) 80 | 81 | encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) 82 | 83 | testApp := app.New( 84 | log.NewNopLogger(), db, nil, true, skipUpgradeHeights, 85 | cast.ToString(emptyOpts.Get(flags.FlagHome)), 86 | cast.ToUint(emptyOpts.Get(server.FlagInvCheckPeriod)), 87 | encCfg, 88 | emptyOpts, 89 | ) 90 | 91 | genesisState, valSet, kr := GenesisStateWithSingleValidator(testApp, genAccounts...) 92 | 93 | stateBytes, err := json.MarshalIndent(genesisState, "", " ") 94 | if err != nil { 95 | panic(err) 96 | } 97 | 98 | // init chain will set the validator set and initialize the genesis accounts 99 | testApp.InitChain( 100 | abci.RequestInitChain{ 101 | Validators: []abci.ValidatorUpdate{}, 102 | ConsensusParams: DefaultConsensusParams, 103 | AppStateBytes: stateBytes, 104 | ChainId: ChainID, 105 | }, 106 | ) 107 | 108 | // commit genesis changes 109 | testApp.Commit() 110 | testApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ 111 | Height: testApp.LastBlockHeight() + 1, 112 | AppHash: testApp.LastCommitID().Hash, 113 | ValidatorsHash: valSet.Hash(), 114 | NextValidatorsHash: valSet.Hash(), 115 | }}) 116 | 117 | return testApp, kr 118 | } 119 | 120 | // AddGenesisAccount mimics the cli addGenesisAccount command, providing an 121 | // account with an allocation of to "token" and "tia" tokens in the genesis 122 | // state 123 | func AddGenesisAccount(addr sdk.AccAddress, appState app.GenesisState, cdc codec.Codec) (map[string]json.RawMessage, error) { 124 | // create concrete account type based on input parameters 125 | var genAccount authtypes.GenesisAccount 126 | 127 | coins := sdk.Coins{ 128 | sdk.NewCoin("token", sdk.NewInt(1000000)), 129 | sdk.NewCoin(consts.BondDenom, sdk.NewInt(1000000)), 130 | } 131 | 132 | balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} 133 | baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) 134 | 135 | genAccount = baseAccount 136 | 137 | if err := genAccount.Validate(); err != nil { 138 | return appState, fmt.Errorf("failed to validate new genesis account: %w", err) 139 | } 140 | 141 | authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) 142 | 143 | accs, err := authtypes.UnpackAccounts(authGenState.Accounts) 144 | if err != nil { 145 | return appState, fmt.Errorf("failed to get accounts from any: %w", err) 146 | } 147 | 148 | if accs.Contains(addr) { 149 | return appState, fmt.Errorf("cannot add account at existing address %s", addr) 150 | } 151 | 152 | // Add the new account to the set of genesis accounts and sanitize the 153 | // accounts afterwards. 154 | accs = append(accs, genAccount) 155 | accs = authtypes.SanitizeGenesisAccounts(accs) 156 | 157 | genAccs, err := authtypes.PackAccounts(accs) 158 | if err != nil { 159 | return appState, fmt.Errorf("failed to convert accounts into any's: %w", err) 160 | } 161 | authGenState.Accounts = genAccs 162 | 163 | authGenStateBz, err := cdc.MarshalJSON(&authGenState) 164 | if err != nil { 165 | return appState, fmt.Errorf("failed to marshal auth genesis state: %w", err) 166 | } 167 | 168 | appState[authtypes.ModuleName] = authGenStateBz 169 | 170 | bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) 171 | bankGenState.Balances = append(bankGenState.Balances, balances) 172 | bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) 173 | 174 | bankGenStateBz, err := cdc.MarshalJSON(bankGenState) 175 | if err != nil { 176 | return appState, fmt.Errorf("failed to marshal bank genesis state: %w", err) 177 | } 178 | 179 | appState[banktypes.ModuleName] = bankGenStateBz 180 | return appState, nil 181 | } 182 | 183 | // GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts 184 | // that also act as delegators. 185 | func GenesisStateWithSingleValidator(testApp *app.App, genAccounts ...string) (app.GenesisState, *tmtypes.ValidatorSet, keyring.Keyring) { 186 | privVal := mock.NewPV() 187 | pubKey, err := privVal.GetPubKey() 188 | if err != nil { 189 | panic(err) 190 | } 191 | 192 | // create validator set with single validator 193 | validator := tmtypes.NewValidator(pubKey, 1) 194 | valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) 195 | 196 | // generate genesis account 197 | senderPrivKey := secp256k1.GenPrivKey() 198 | accs := make([]authtypes.GenesisAccount, 0, len(genAccounts)+1) 199 | acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) 200 | accs = append(accs, acc) 201 | balances := make([]banktypes.Balance, 0, len(genAccounts)+1) 202 | balances = append(balances, banktypes.Balance{ 203 | Address: acc.GetAddress().String(), 204 | Coins: sdk.NewCoins(sdk.NewCoin(consts.BondDenom, sdk.NewInt(100000000000000))), 205 | }) 206 | 207 | kr, fundedBankAccs, fundedAuthAccs := testfactory.FundKeyringAccounts(testApp.AppCodec(), genAccounts...) 208 | accs = append(accs, fundedAuthAccs...) 209 | balances = append(balances, fundedBankAccs...) 210 | 211 | genesisState := NewDefaultGenesisState(testApp.AppCodec()) 212 | genesisState = genesisStateWithValSet(testApp, genesisState, valSet, accs, balances...) 213 | 214 | return genesisState, valSet, kr 215 | } 216 | 217 | func genesisStateWithValSet( 218 | mapp *app.App, 219 | genesisState app.GenesisState, 220 | valSet *tmtypes.ValidatorSet, 221 | genAccs []authtypes.GenesisAccount, 222 | balances ...banktypes.Balance, 223 | ) app.GenesisState { 224 | // set genesis accounts 225 | authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) 226 | genesisState[authtypes.ModuleName] = mapp.AppCodec().MustMarshalJSON(authGenesis) 227 | 228 | validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) 229 | delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) 230 | 231 | bondAmt := sdk.DefaultPowerReduction 232 | 233 | for _, val := range valSet.Validators { 234 | pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) 235 | if err != nil { 236 | panic(err) 237 | } 238 | pkAny, err := codectypes.NewAnyWithValue(pk) 239 | if err != nil { 240 | panic(err) 241 | } 242 | validator := stakingtypes.Validator{ 243 | OperatorAddress: sdk.ValAddress(val.Address).String(), 244 | ConsensusPubkey: pkAny, 245 | Jailed: false, 246 | Status: stakingtypes.Bonded, 247 | Tokens: bondAmt, 248 | DelegatorShares: sdk.OneDec(), 249 | Description: stakingtypes.Description{}, 250 | UnbondingHeight: int64(0), 251 | UnbondingTime: time.Unix(0, 0).UTC(), 252 | Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), 253 | MinSelfDelegation: sdk.ZeroInt(), 254 | } 255 | validators = append(validators, validator) 256 | delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) 257 | 258 | } 259 | // set validators and delegations 260 | stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) 261 | genesisState[stakingtypes.ModuleName] = mapp.AppCodec().MustMarshalJSON(stakingGenesis) 262 | 263 | totalSupply := sdk.NewCoins() 264 | for _, b := range balances { 265 | // add genesis acc tokens to total supply 266 | totalSupply = totalSupply.Add(b.Coins...) 267 | } 268 | 269 | for range delegations { 270 | // add delegated tokens to total supply 271 | totalSupply = totalSupply.Add(sdk.NewCoin(consts.BondDenom, bondAmt)) 272 | } 273 | 274 | // add bonded amount to bonded pool module account 275 | balances = append(balances, banktypes.Balance{ 276 | Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), 277 | Coins: sdk.Coins{sdk.NewCoin(consts.BondDenom, bondAmt)}, 278 | }) 279 | 280 | // update total supply 281 | bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) 282 | genesisState[banktypes.ModuleName] = mapp.AppCodec().MustMarshalJSON(bankGenesis) 283 | 284 | return genesisState 285 | } 286 | 287 | // NewDefaultGenesisState generates the default state for the application. 288 | func NewDefaultGenesisState(cdc codec.JSONCodec) app.GenesisState { 289 | return app.ModuleBasics.DefaultGenesis(cdc) 290 | } 291 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Evan Forbes 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /third_party/proto/google/api/http.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | option cc_enable_arenas = true; 20 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 21 | option java_multiple_files = true; 22 | option java_outer_classname = "HttpProto"; 23 | option java_package = "com.google.api"; 24 | option objc_class_prefix = "GAPI"; 25 | 26 | 27 | // Defines the HTTP configuration for an API service. It contains a list of 28 | // [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method 29 | // to one or more HTTP REST API methods. 30 | message Http { 31 | // A list of HTTP configuration rules that apply to individual API methods. 32 | // 33 | // **NOTE:** All service configuration rules follow "last one wins" order. 34 | repeated HttpRule rules = 1; 35 | 36 | // When set to true, URL path parmeters will be fully URI-decoded except in 37 | // cases of single segment matches in reserved expansion, where "%2F" will be 38 | // left encoded. 39 | // 40 | // The default behavior is to not decode RFC 6570 reserved characters in multi 41 | // segment matches. 42 | bool fully_decode_reserved_expansion = 2; 43 | } 44 | 45 | // `HttpRule` defines the mapping of an RPC method to one or more HTTP 46 | // REST API methods. The mapping specifies how different portions of the RPC 47 | // request message are mapped to URL path, URL query parameters, and 48 | // HTTP request body. The mapping is typically specified as an 49 | // `google.api.http` annotation on the RPC method, 50 | // see "google/api/annotations.proto" for details. 51 | // 52 | // The mapping consists of a field specifying the path template and 53 | // method kind. The path template can refer to fields in the request 54 | // message, as in the example below which describes a REST GET 55 | // operation on a resource collection of messages: 56 | // 57 | // 58 | // service Messaging { 59 | // rpc GetMessage(GetMessageRequest) returns (Message) { 60 | // option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; 61 | // } 62 | // } 63 | // message GetMessageRequest { 64 | // message SubMessage { 65 | // string subfield = 1; 66 | // } 67 | // string message_id = 1; // mapped to the URL 68 | // SubMessage sub = 2; // `sub.subfield` is url-mapped 69 | // } 70 | // message Message { 71 | // string text = 1; // content of the resource 72 | // } 73 | // 74 | // The same http annotation can alternatively be expressed inside the 75 | // `GRPC API Configuration` YAML file. 76 | // 77 | // http: 78 | // rules: 79 | // - selector: <proto_package_name>.Messaging.GetMessage 80 | // get: /v1/messages/{message_id}/{sub.subfield} 81 | // 82 | // This definition enables an automatic, bidrectional mapping of HTTP 83 | // JSON to RPC. Example: 84 | // 85 | // HTTP | RPC 86 | // -----|----- 87 | // `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` 88 | // 89 | // In general, not only fields but also field paths can be referenced 90 | // from a path pattern. Fields mapped to the path pattern cannot be 91 | // repeated and must have a primitive (non-message) type. 92 | // 93 | // Any fields in the request message which are not bound by the path 94 | // pattern automatically become (optional) HTTP query 95 | // parameters. Assume the following definition of the request message: 96 | // 97 | // 98 | // service Messaging { 99 | // rpc GetMessage(GetMessageRequest) returns (Message) { 100 | // option (google.api.http).get = "/v1/messages/{message_id}"; 101 | // } 102 | // } 103 | // message GetMessageRequest { 104 | // message SubMessage { 105 | // string subfield = 1; 106 | // } 107 | // string message_id = 1; // mapped to the URL 108 | // int64 revision = 2; // becomes a parameter 109 | // SubMessage sub = 3; // `sub.subfield` becomes a parameter 110 | // } 111 | // 112 | // 113 | // This enables a HTTP JSON to RPC mapping as below: 114 | // 115 | // HTTP | RPC 116 | // -----|----- 117 | // `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` 118 | // 119 | // Note that fields which are mapped to HTTP parameters must have a 120 | // primitive type or a repeated primitive type. Message types are not 121 | // allowed. In the case of a repeated type, the parameter can be 122 | // repeated in the URL, as in `...?param=A¶m=B`. 123 | // 124 | // For HTTP method kinds which allow a request body, the `body` field 125 | // specifies the mapping. Consider a REST update method on the 126 | // message resource collection: 127 | // 128 | // 129 | // service Messaging { 130 | // rpc UpdateMessage(UpdateMessageRequest) returns (Message) { 131 | // option (google.api.http) = { 132 | // put: "/v1/messages/{message_id}" 133 | // body: "message" 134 | // }; 135 | // } 136 | // } 137 | // message UpdateMessageRequest { 138 | // string message_id = 1; // mapped to the URL 139 | // Message message = 2; // mapped to the body 140 | // } 141 | // 142 | // 143 | // The following HTTP JSON to RPC mapping is enabled, where the 144 | // representation of the JSON in the request body is determined by 145 | // protos JSON encoding: 146 | // 147 | // HTTP | RPC 148 | // -----|----- 149 | // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` 150 | // 151 | // The special name `*` can be used in the body mapping to define that 152 | // every field not bound by the path template should be mapped to the 153 | // request body. This enables the following alternative definition of 154 | // the update method: 155 | // 156 | // service Messaging { 157 | // rpc UpdateMessage(Message) returns (Message) { 158 | // option (google.api.http) = { 159 | // put: "/v1/messages/{message_id}" 160 | // body: "*" 161 | // }; 162 | // } 163 | // } 164 | // message Message { 165 | // string message_id = 1; 166 | // string text = 2; 167 | // } 168 | // 169 | // 170 | // The following HTTP JSON to RPC mapping is enabled: 171 | // 172 | // HTTP | RPC 173 | // -----|----- 174 | // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` 175 | // 176 | // Note that when using `*` in the body mapping, it is not possible to 177 | // have HTTP parameters, as all fields not bound by the path end in 178 | // the body. This makes this option more rarely used in practice of 179 | // defining REST APIs. The common usage of `*` is in custom methods 180 | // which don't use the URL at all for transferring data. 181 | // 182 | // It is possible to define multiple HTTP methods for one RPC by using 183 | // the `additional_bindings` option. Example: 184 | // 185 | // service Messaging { 186 | // rpc GetMessage(GetMessageRequest) returns (Message) { 187 | // option (google.api.http) = { 188 | // get: "/v1/messages/{message_id}" 189 | // additional_bindings { 190 | // get: "/v1/users/{user_id}/messages/{message_id}" 191 | // } 192 | // }; 193 | // } 194 | // } 195 | // message GetMessageRequest { 196 | // string message_id = 1; 197 | // string user_id = 2; 198 | // } 199 | // 200 | // 201 | // This enables the following two alternative HTTP JSON to RPC 202 | // mappings: 203 | // 204 | // HTTP | RPC 205 | // -----|----- 206 | // `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` 207 | // `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` 208 | // 209 | // # Rules for HTTP mapping 210 | // 211 | // The rules for mapping HTTP path, query parameters, and body fields 212 | // to the request message are as follows: 213 | // 214 | // 1. The `body` field specifies either `*` or a field path, or is 215 | // omitted. If omitted, it indicates there is no HTTP request body. 216 | // 2. Leaf fields (recursive expansion of nested messages in the 217 | // request) can be classified into three types: 218 | // (a) Matched in the URL template. 219 | // (b) Covered by body (if body is `*`, everything except (a) fields; 220 | // else everything under the body field) 221 | // (c) All other fields. 222 | // 3. URL query parameters found in the HTTP request are mapped to (c) fields. 223 | // 4. Any body sent with an HTTP request can contain only (b) fields. 224 | // 225 | // The syntax of the path template is as follows: 226 | // 227 | // Template = "/" Segments [ Verb ] ; 228 | // Segments = Segment { "/" Segment } ; 229 | // Segment = "*" | "**" | LITERAL | Variable ; 230 | // Variable = "{" FieldPath [ "=" Segments ] "}" ; 231 | // FieldPath = IDENT { "." IDENT } ; 232 | // Verb = ":" LITERAL ; 233 | // 234 | // The syntax `*` matches a single path segment. The syntax `**` matches zero 235 | // or more path segments, which must be the last part of the path except the 236 | // `Verb`. The syntax `LITERAL` matches literal text in the path. 237 | // 238 | // The syntax `Variable` matches part of the URL path as specified by its 239 | // template. A variable template must not contain other variables. If a variable 240 | // matches a single path segment, its template may be omitted, e.g. `{var}` 241 | // is equivalent to `{var=*}`. 242 | // 243 | // If a variable contains exactly one path segment, such as `"{var}"` or 244 | // `"{var=*}"`, when such a variable is expanded into a URL path, all characters 245 | // except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the 246 | // Discovery Document as `{var}`. 247 | // 248 | // If a variable contains one or more path segments, such as `"{var=foo/*}"` 249 | // or `"{var=**}"`, when such a variable is expanded into a URL path, all 250 | // characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables 251 | // show up in the Discovery Document as `{+var}`. 252 | // 253 | // NOTE: While the single segment variable matches the semantics of 254 | // [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 255 | // Simple String Expansion, the multi segment variable **does not** match 256 | // RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion 257 | // does not expand special characters like `?` and `#`, which would lead 258 | // to invalid URLs. 259 | // 260 | // NOTE: the field paths in variables and in the `body` must not refer to 261 | // repeated fields or map fields. 262 | message HttpRule { 263 | // Selects methods to which this rule applies. 264 | // 265 | // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. 266 | string selector = 1; 267 | 268 | // Determines the URL pattern is matched by this rules. This pattern can be 269 | // used with any of the {get|put|post|delete|patch} methods. A custom method 270 | // can be defined using the 'custom' field. 271 | oneof pattern { 272 | // Used for listing and getting information about resources. 273 | string get = 2; 274 | 275 | // Used for updating a resource. 276 | string put = 3; 277 | 278 | // Used for creating a resource. 279 | string post = 4; 280 | 281 | // Used for deleting a resource. 282 | string delete = 5; 283 | 284 | // Used for updating a resource. 285 | string patch = 6; 286 | 287 | // The custom pattern is used for specifying an HTTP method that is not 288 | // included in the `pattern` field, such as HEAD, or "*" to leave the 289 | // HTTP method unspecified for this rule. The wild-card rule is useful 290 | // for services that provide content to Web (HTML) clients. 291 | CustomHttpPattern custom = 8; 292 | } 293 | 294 | // The name of the request field whose value is mapped to the HTTP body, or 295 | // `*` for mapping all fields not captured by the path pattern to the HTTP 296 | // body. NOTE: the referred field must not be a repeated field and must be 297 | // present at the top-level of request message type. 298 | string body = 7; 299 | 300 | // Optional. The name of the response field whose value is mapped to the HTTP 301 | // body of response. Other response fields are ignored. When 302 | // not set, the response message will be used as HTTP body of response. 303 | string response_body = 12; 304 | 305 | // Additional HTTP bindings for the selector. Nested bindings must 306 | // not contain an `additional_bindings` field themselves (that is, 307 | // the nesting may only be one level deep). 308 | repeated HttpRule additional_bindings = 11; 309 | } 310 | 311 | // A custom pattern is used for defining custom HTTP verb. 312 | message CustomHttpPattern { 313 | // The name of this custom HTTP verb. 314 | string kind = 1; 315 | 316 | // The path matched by this custom verb. 317 | string path = 2; 318 | } 319 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/abci/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.abci; 3 | 4 | option go_package = "github.com/tendermint/tendermint/abci/types"; 5 | 6 | // For more information on gogo.proto, see: 7 | // https://github.com/gogo/protobuf/blob/master/extensions.md 8 | import "tendermint/crypto/proof.proto"; 9 | import "tendermint/types/types.proto"; 10 | import "tendermint/crypto/keys.proto"; 11 | import "tendermint/types/params.proto"; 12 | import "google/protobuf/timestamp.proto"; 13 | import "gogoproto/gogo.proto"; 14 | 15 | // This file is copied from http://github.com/tendermint/abci 16 | // NOTE: When using custom types, mind the warnings. 17 | // https://github.com/gogo/protobuf/blob/master/custom_types.md#warnings-and-issues 18 | 19 | //---------------------------------------- 20 | // Request types 21 | 22 | message Request { 23 | oneof value { 24 | RequestEcho echo = 1; 25 | RequestFlush flush = 2; 26 | RequestInfo info = 3; 27 | RequestInitChain init_chain = 4; 28 | RequestQuery query = 5; 29 | RequestBeginBlock begin_block = 6; 30 | RequestCheckTx check_tx = 7; 31 | RequestDeliverTx deliver_tx = 8; 32 | RequestEndBlock end_block = 9; 33 | RequestCommit commit = 10; 34 | RequestListSnapshots list_snapshots = 11; 35 | RequestOfferSnapshot offer_snapshot = 12; 36 | RequestLoadSnapshotChunk load_snapshot_chunk = 13; 37 | RequestApplySnapshotChunk apply_snapshot_chunk = 14; 38 | RequestPreprocessTxs preprocess_txs = 15; 39 | } 40 | } 41 | 42 | message RequestEcho { 43 | string message = 1; 44 | } 45 | 46 | message RequestFlush {} 47 | 48 | message RequestInfo { 49 | string version = 1; 50 | uint64 block_version = 2; 51 | uint64 p2p_version = 3; 52 | string abci_version = 4; 53 | } 54 | 55 | message RequestInitChain { 56 | google.protobuf.Timestamp time = 1 57 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 58 | string chain_id = 2; 59 | ConsensusParams consensus_params = 3; 60 | repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; 61 | bytes app_state_bytes = 5; 62 | int64 initial_height = 6; 63 | } 64 | 65 | message RequestQuery { 66 | bytes data = 1; 67 | string path = 2; 68 | int64 height = 3; 69 | bool prove = 4; 70 | } 71 | 72 | message RequestBeginBlock { 73 | bytes hash = 1; 74 | tendermint.types.Header header = 2 [(gogoproto.nullable) = false]; 75 | LastCommitInfo last_commit_info = 3 [(gogoproto.nullable) = false]; 76 | repeated Evidence byzantine_validators = 4 [(gogoproto.nullable) = false]; 77 | } 78 | 79 | enum CheckTxType { 80 | NEW = 0 [(gogoproto.enumvalue_customname) = "New"]; 81 | RECHECK = 1 [(gogoproto.enumvalue_customname) = "Recheck"]; 82 | } 83 | 84 | message RequestCheckTx { 85 | bytes tx = 1; 86 | CheckTxType type = 2; 87 | } 88 | 89 | message RequestDeliverTx { 90 | bytes tx = 1; 91 | } 92 | 93 | message RequestEndBlock { 94 | int64 height = 1; 95 | } 96 | 97 | message RequestCommit {} 98 | 99 | // lists available snapshots 100 | message RequestListSnapshots { 101 | } 102 | 103 | // offers a snapshot to the application 104 | message RequestOfferSnapshot { 105 | Snapshot snapshot = 1; // snapshot offered by peers 106 | bytes app_hash = 2; // light client-verified app hash for snapshot height 107 | } 108 | 109 | // loads a snapshot chunk 110 | message RequestLoadSnapshotChunk { 111 | uint64 height = 1; 112 | uint32 format = 2; 113 | uint32 chunk = 3; 114 | } 115 | 116 | // Applies a snapshot chunk 117 | message RequestApplySnapshotChunk { 118 | uint32 index = 1; 119 | bytes chunk = 2; 120 | string sender = 3; 121 | } 122 | 123 | message RequestPreprocessTxs { 124 | repeated bytes txs = 1; 125 | } 126 | 127 | //---------------------------------------- 128 | // Response types 129 | 130 | message Response { 131 | oneof value { 132 | ResponseException exception = 1; 133 | ResponseEcho echo = 2; 134 | ResponseFlush flush = 3; 135 | ResponseInfo info = 4; 136 | ResponseInitChain init_chain = 5; 137 | ResponseQuery query = 6; 138 | ResponseBeginBlock begin_block = 7; 139 | ResponseCheckTx check_tx = 8; 140 | ResponseDeliverTx deliver_tx = 9; 141 | ResponseEndBlock end_block = 10; 142 | ResponseCommit commit = 11; 143 | ResponseListSnapshots list_snapshots = 12; 144 | ResponseOfferSnapshot offer_snapshot = 13; 145 | ResponseLoadSnapshotChunk load_snapshot_chunk = 14; 146 | ResponseApplySnapshotChunk apply_snapshot_chunk = 15; 147 | ResponsePreprocessTxs preprocess_txs = 16; 148 | } 149 | } 150 | 151 | // nondeterministic 152 | message ResponseException { 153 | string error = 1; 154 | } 155 | 156 | message ResponseEcho { 157 | string message = 1; 158 | } 159 | 160 | message ResponseFlush {} 161 | 162 | message ResponseInfo { 163 | string data = 1; 164 | 165 | // this is the software version of the application. TODO: remove? 166 | string version = 2; 167 | uint64 app_version = 3; 168 | 169 | int64 last_block_height = 4; 170 | bytes last_block_app_hash = 5; 171 | } 172 | 173 | message ResponseInitChain { 174 | ConsensusParams consensus_params = 1; 175 | repeated ValidatorUpdate validators = 2 [(gogoproto.nullable) = false]; 176 | bytes app_hash = 3; 177 | } 178 | 179 | message ResponseQuery { 180 | uint32 code = 1; 181 | // bytes data = 2; // use "value" instead. 182 | string log = 3; // nondeterministic 183 | string info = 4; // nondeterministic 184 | int64 index = 5; 185 | bytes key = 6; 186 | bytes value = 7; 187 | tendermint.crypto.ProofOps proof_ops = 8; 188 | int64 height = 9; 189 | string codespace = 10; 190 | } 191 | 192 | message ResponseBeginBlock { 193 | repeated Event events = 1 194 | [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; 195 | } 196 | 197 | message ResponseCheckTx { 198 | uint32 code = 1; 199 | bytes data = 2; 200 | string log = 3; // nondeterministic 201 | string info = 4; // nondeterministic 202 | int64 gas_wanted = 5 [json_name = "gas_wanted"]; 203 | int64 gas_used = 6 [json_name = "gas_used"]; 204 | repeated Event events = 7 205 | [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; 206 | string codespace = 8; 207 | } 208 | 209 | message ResponseDeliverTx { 210 | uint32 code = 1; 211 | bytes data = 2; 212 | string log = 3; // nondeterministic 213 | string info = 4; // nondeterministic 214 | int64 gas_wanted = 5 [json_name = "gas_wanted"]; 215 | int64 gas_used = 6 [json_name = "gas_used"]; 216 | repeated Event events = 7 217 | [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; 218 | string codespace = 8; 219 | } 220 | 221 | message ResponseEndBlock { 222 | repeated ValidatorUpdate validator_updates = 1 223 | [(gogoproto.nullable) = false]; 224 | ConsensusParams consensus_param_updates = 2; 225 | repeated Event events = 3 226 | [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; 227 | } 228 | 229 | message ResponseCommit { 230 | // reserve 1 231 | bytes data = 2; 232 | int64 retain_height = 3; 233 | } 234 | 235 | message ResponseListSnapshots { 236 | repeated Snapshot snapshots = 1; 237 | } 238 | 239 | message ResponseOfferSnapshot { 240 | Result result = 1; 241 | 242 | enum Result { 243 | UNKNOWN = 0; // Unknown result, abort all snapshot restoration 244 | ACCEPT = 1; // Snapshot accepted, apply chunks 245 | ABORT = 2; // Abort all snapshot restoration 246 | REJECT = 3; // Reject this specific snapshot, try others 247 | REJECT_FORMAT = 4; // Reject all snapshots of this format, try others 248 | REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others 249 | } 250 | } 251 | 252 | message ResponseLoadSnapshotChunk { 253 | bytes chunk = 1; 254 | } 255 | 256 | message ResponseApplySnapshotChunk { 257 | Result result = 1; 258 | repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply 259 | repeated string reject_senders = 3; // Chunk senders to reject and ban 260 | 261 | enum Result { 262 | UNKNOWN = 0; // Unknown result, abort all snapshot restoration 263 | ACCEPT = 1; // Chunk successfully accepted 264 | ABORT = 2; // Abort all snapshot restoration 265 | RETRY = 3; // Retry chunk (combine with refetch and reject) 266 | RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) 267 | REJECT_SNAPSHOT = 5; // Reject this snapshot, try others 268 | } 269 | } 270 | 271 | message ResponsePreprocessTxs { 272 | repeated bytes txs = 1; 273 | tendermint.types.Messages messages = 2; 274 | } 275 | 276 | //---------------------------------------- 277 | // Misc. 278 | 279 | // ConsensusParams contains all consensus-relevant parameters 280 | // that can be adjusted by the abci app 281 | message ConsensusParams { 282 | BlockParams block = 1; 283 | tendermint.types.EvidenceParams evidence = 2; 284 | tendermint.types.ValidatorParams validator = 3; 285 | tendermint.types.VersionParams version = 4; 286 | } 287 | 288 | // BlockParams contains limits on the block size. 289 | message BlockParams { 290 | // Note: must be greater than 0 291 | int64 max_bytes = 1; 292 | // Note: must be greater or equal to -1 293 | int64 max_gas = 2; 294 | } 295 | 296 | message LastCommitInfo { 297 | int32 round = 1; 298 | repeated VoteInfo votes = 2 [(gogoproto.nullable) = false]; 299 | } 300 | 301 | // Event allows application developers to attach additional information to 302 | // ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx. 303 | // Later, transactions may be queried using these events. 304 | message Event { 305 | string type = 1; 306 | repeated EventAttribute attributes = 2 [ 307 | (gogoproto.nullable) = false, 308 | (gogoproto.jsontag) = "attributes,omitempty" 309 | ]; 310 | } 311 | 312 | // EventAttribute is a single key-value pair, associated with an event. 313 | message EventAttribute { 314 | bytes key = 1; 315 | bytes value = 2; 316 | bool index = 3; // nondeterministic 317 | } 318 | 319 | // TxResult contains results of executing the transaction. 320 | // 321 | // One usage is indexing transaction results. 322 | message TxResult { 323 | int64 height = 1; 324 | uint32 index = 2; 325 | bytes tx = 3; 326 | ResponseDeliverTx result = 4 [(gogoproto.nullable) = false]; 327 | bytes original_hash = 5; 328 | } 329 | 330 | //---------------------------------------- 331 | // Blockchain Types 332 | 333 | // Validator 334 | message Validator { 335 | bytes address = 1; // The first 20 bytes of SHA256(public key) 336 | // PubKey pub_key = 2 [(gogoproto.nullable)=false]; 337 | int64 power = 3; // The voting power 338 | } 339 | 340 | // ValidatorUpdate 341 | message ValidatorUpdate { 342 | tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = false]; 343 | int64 power = 2; 344 | } 345 | 346 | // VoteInfo 347 | message VoteInfo { 348 | Validator validator = 1 [(gogoproto.nullable) = false]; 349 | bool signed_last_block = 2; 350 | } 351 | 352 | enum EvidenceType { 353 | UNKNOWN = 0; 354 | DUPLICATE_VOTE = 1; 355 | LIGHT_CLIENT_ATTACK = 2; 356 | } 357 | 358 | message Evidence { 359 | EvidenceType type = 1; 360 | // The offending validator 361 | Validator validator = 2 [(gogoproto.nullable) = false]; 362 | // The height when the offense occurred 363 | int64 height = 3; 364 | // The corresponding time where the offense occurred 365 | google.protobuf.Timestamp time = 4 [ 366 | (gogoproto.nullable) = false, 367 | (gogoproto.stdtime) = true 368 | ]; 369 | // Total voting power of the validator set in case the ABCI application does 370 | // not store historical validators. 371 | // https://github.com/tendermint/tendermint/issues/4581 372 | int64 total_voting_power = 5; 373 | } 374 | 375 | //---------------------------------------- 376 | // State Sync Types 377 | 378 | message Snapshot { 379 | uint64 height = 1; // The height at which the snapshot was taken 380 | uint32 format = 2; // The application-specific snapshot format 381 | uint32 chunks = 3; // Number of chunks in the snapshot 382 | bytes hash = 4; // Arbitrary snapshot hash, equal only if identical 383 | bytes metadata = 5; // Arbitrary application metadata 384 | } 385 | 386 | //---------------------------------------- 387 | // Service Definition 388 | 389 | service ABCIApplication { 390 | rpc Echo(RequestEcho) returns (ResponseEcho); 391 | rpc Flush(RequestFlush) returns (ResponseFlush); 392 | rpc Info(RequestInfo) returns (ResponseInfo); 393 | rpc DeliverTx(RequestDeliverTx) returns (ResponseDeliverTx); 394 | rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx); 395 | rpc Query(RequestQuery) returns (ResponseQuery); 396 | rpc Commit(RequestCommit) returns (ResponseCommit); 397 | rpc InitChain(RequestInitChain) returns (ResponseInitChain); 398 | rpc BeginBlock(RequestBeginBlock) returns (ResponseBeginBlock); 399 | rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); 400 | rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); 401 | rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); 402 | rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); 403 | rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); 404 | rpc PreprocessTxs(RequestPreprocessTxs) returns (ResponsePreprocessTxs); 405 | } 406 | --------------------------------------------------------------------------------