Compare commits
55 Commits
dboreham/s
...
main
Author | SHA1 | Date | |
---|---|---|---|
81c0402803 | |||
322d6f188b | |||
1ea6d6b647 | |||
d65dcce302 | |||
1833b14e25 | |||
02991c7135 | |||
348ae680c6 | |||
965d7dbb66 | |||
e7bab9b971 | |||
28b9207bbe | |||
055cfb2255 | |||
776799ea02 | |||
9c240f1a0c | |||
ec70c9f36b | |||
ed81bba3ed | |||
ae07fc0147 | |||
588c52c0b2 | |||
c407145c31 | |||
8077a7a85a | |||
47d086111d | |||
880367d5df | |||
0adc932400 | |||
d57743bfd5 | |||
20a6828bb5 | |||
976a97234f | |||
072b174c9b | |||
|
a5681191a5 | ||
|
1cdf15595e | ||
|
2cc5df02e0 | ||
|
703f146c5b | ||
|
763dab712f | ||
|
e2178894ef | ||
|
dca3ccb47c | ||
|
2c883282bc | ||
|
b8d6300e4d | ||
|
42fdc97c1c | ||
|
5f6a9f56bb | ||
|
71c15cd2eb | ||
|
c227a38e9a | ||
|
15c9ade06d | ||
|
4b318822d9 | ||
|
c61f268329 | ||
|
facd64a14b | ||
|
6945792aea | ||
|
5af22471a8 | ||
|
5e7a890adb | ||
|
9ec237e41f | ||
b57ad7d5fa | |||
|
c3138a2615 | ||
02e9080094 | |||
|
747088deb0 | ||
|
ab2ea51aac | ||
|
cb104e0b10 | ||
|
fac5a95679 | ||
78c513d0fd |
111
.clang-format
111
.clang-format
@ -1,115 +1,6 @@
|
||||
---
|
||||
Language: Proto
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
BasedOnStyle: google
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
RawStringFormats:
|
||||
- Delimiters:
|
||||
- pb
|
||||
Language: TextProto
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
@ -1,5 +1,7 @@
|
||||
# localnet-setup
|
||||
localnet-setup
|
||||
Dockerfile
|
||||
|
||||
**/node_modules
|
||||
init.sh
|
||||
|
||||
# build
|
||||
build
|
||||
localnet-setup
|
5
.flake8
Normal file
5
.flake8
Normal file
@ -0,0 +1,5 @@
|
||||
[flake8]
|
||||
ignore = BLK100, W503
|
||||
max-line-length = 88
|
||||
extend-ignore = E203
|
||||
exclude = .git,__pycache__,node_modules,.direnv
|
17
.gitea/workflows/build.yml
Normal file
17
.gitea/workflows/build.yml
Normal file
@ -0,0 +1,17 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- run: |
|
||||
make build
|
20
.gitea/workflows/deploy-contract.yml
Normal file
20
.gitea/workflows/deploy-contract.yml
Normal file
@ -0,0 +1,20 @@
|
||||
name: Deploy Contract
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- name: Test contract
|
||||
run: |
|
||||
make contract-tools
|
||||
# This seems to be an empty placeholder.
|
||||
make test-contract
|
@ -1,4 +1,4 @@
|
||||
name: Publish onn release
|
||||
name: Publish on release
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
@ -20,7 +20,7 @@ jobs:
|
||||
- name: Tag docker image
|
||||
run: docker tag git.vdb.to/cerc-io/laconicd/laconicd:${{steps.vars.outputs.sha}} git.vdb.to/cerc-io/laconicd/laconicd:${{steps.vars.outputs.tag}}
|
||||
- name: Docker Login
|
||||
run: echo ${{ secrets.GITEA_TOKEN }} | docker login https://git.vdb.to -u cerccicd --password-stdin
|
||||
run: echo ${{ secrets.CICD_PUBLISH_TOKEN }} | docker login https://git.vdb.to -u cerccicd --password-stdin
|
||||
- name: Docker Push
|
||||
run: docker push git.vdb.to/cerc-io/laconicd/laconicd:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Push TAGGED
|
@ -13,15 +13,16 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- name: release dry run
|
||||
run: make release-dry-run
|
||||
- name: setup release environment
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.CICD_PUBLISH_TOKEN }}
|
||||
run: |-
|
||||
echo 'GITHUB_TOKEN=${{secrets.GITHUB_TOKEN}}' > .release-env
|
||||
echo 'GITHUB_TOKEN=${{secrets.CICD_PUBLISH_TOKEN}}' > .release-env
|
||||
- name: release publish
|
||||
run: make release
|
42
.gitea/workflows/lint.yml
Normal file
42
.gitea/workflows/lint.yml
Normal file
@ -0,0 +1,42 @@
|
||||
name: Lint
|
||||
# Lint runs golangci-lint over the entire ethermint repository This workflow is
|
||||
# run on every pull request and push to main The `golangci` will pass without
|
||||
# running if no *.{go, mod, sum} files have been changed.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
golangci:
|
||||
name: Run golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
# Required: setup-go, for all versions v3.0.0+ of golangci-lint
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: golangci/golangci-lint-action@v3.3.1
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: latest
|
||||
args: --timeout 10m
|
||||
github-token: ${{ secrets.github_token }}
|
||||
|
||||
python-lint:
|
||||
# For compatibility with Gitea
|
||||
env:
|
||||
USER: root
|
||||
name: Run flake8 on python integration tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cachix/install-nix-action@v20
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: ethermint
|
||||
- run: |
|
||||
nix-shell -I nixpkgs=./nix -p test-env --run "make lint-py"
|
28
.gitea/workflows/proto.yml
Normal file
28
.gitea/workflows/proto.yml
Normal file
@ -0,0 +1,28 @@
|
||||
name: Protobuf
|
||||
# Protobuf runs buf (https://buf.build/) lint and check-breakage
|
||||
# This workflow is only run when a .proto file has been changed
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "proto/**"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: bufbuild/buf-setup-action@v1.9.0
|
||||
- uses: bufbuild/buf-lint-action@v1
|
||||
with:
|
||||
input: "proto"
|
||||
|
||||
break-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: bufbuild/buf-setup-action@v1.9.0
|
||||
- uses: bufbuild/buf-breaking-action@v1
|
||||
with:
|
||||
input: "proto"
|
||||
against: "https://github.com/${{ github.repository }}.git#branch=${{ github.event.pull_request.base.ref }},ref=HEAD~1,subdir=proto"
|
21
.gitea/workflows/test-importer.yml
Normal file
21
.gitea/workflows/test-importer.yml
Normal file
@ -0,0 +1,21 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
test-importer:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- name: test-importer
|
||||
run: |
|
||||
make test-import
|
21
.gitea/workflows/test-rpc.yml
Normal file
21
.gitea/workflows/test-rpc.yml
Normal file
@ -0,0 +1,21 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
test-rpc:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- name: Test rpc endpoint
|
||||
run: |
|
||||
make test-rpc
|
40
.gitea/workflows/test-sdk-auction.yml
Normal file
40
.gitea/workflows/test-sdk-auction.yml
Normal file
@ -0,0 +1,40 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
sdk_tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout laconic-sdk
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: "./laconic-sdk/"
|
||||
repository: cerc-io/laconic-sdk
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
- name: Environment
|
||||
run: ls -tlh && env
|
||||
|
||||
- name: Build laconicd container
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./build-laconicd-container.sh
|
||||
|
||||
- name: Build laconic-sdk container
|
||||
working-directory: laconic-sdk
|
||||
run: ./scripts/build-sdk-test-container.sh
|
||||
|
||||
- name: Start containers (auctions enabled)
|
||||
working-directory: tests/sdk_tests
|
||||
env:
|
||||
TEST_AUCTION_ENABLED: true
|
||||
run: docker compose up -d
|
||||
|
||||
- name: Run auction tests
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./run-tests.sh test:auctions
|
40
.gitea/workflows/test-sdk-nameservice.yml
Normal file
40
.gitea/workflows/test-sdk-nameservice.yml
Normal file
@ -0,0 +1,40 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
sdk_tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout laconic-sdk
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: "./laconic-sdk/"
|
||||
repository: cerc-io/laconic-sdk
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
- name: Environment
|
||||
run: ls -tlh && env
|
||||
|
||||
- name: Build laconicd container
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./build-laconicd-container.sh
|
||||
|
||||
- name: Build laconic-sdk container
|
||||
working-directory: laconic-sdk
|
||||
run: ./scripts/build-sdk-test-container.sh
|
||||
|
||||
- name: Start containers (expiry enabled)
|
||||
working-directory: tests/sdk_tests
|
||||
env:
|
||||
TEST_REGISTRY_EXPIRY: true
|
||||
run: docker compose up -d
|
||||
|
||||
- name: Run nameservice expiry tests
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./run-tests.sh test:nameservice-expiry
|
38
.gitea/workflows/test-sdk.yml
Normal file
38
.gitea/workflows/test-sdk.yml
Normal file
@ -0,0 +1,38 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
sdk_tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout laconic-sdk
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: "./laconic-sdk/"
|
||||
repository: cerc-io/laconic-sdk
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
- name: Environment
|
||||
run: ls -tlh && env
|
||||
|
||||
- name: Build laconicd container
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./build-laconicd-container.sh
|
||||
|
||||
- name: Build laconic-sdk container
|
||||
working-directory: laconic-sdk
|
||||
run: ./scripts/build-sdk-test-container.sh
|
||||
|
||||
- name: Start containers
|
||||
working-directory: tests/sdk_tests
|
||||
run: docker compose up -d
|
||||
|
||||
- name: Run tests
|
||||
working-directory: tests/sdk_tests
|
||||
run: ./run-tests.sh
|
23
.gitea/workflows/test-unit.yml
Normal file
23
.gitea/workflows/test-unit.yml
Normal file
@ -0,0 +1,23 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
test-unit:
|
||||
# This test case doesn't work in CI, run as root.
|
||||
env:
|
||||
SKIP_UNIT_TESTS: TestInitConfigNonNotExistError
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- name: Test
|
||||
run: |
|
||||
make test-unit
|
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -1,4 +0,0 @@
|
||||
# CODEOWNERS: https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# Primary repo maintainers
|
||||
* @fedekunze @khoslaventures @jolube
|
18
.github/ISSUE_TEMPLATE/bug-report.md
vendored
18
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -1,18 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: create a bug report
|
||||
---
|
||||
|
||||
__System info:__ [Include Ethermint commit, operating system name, and other relevant details]
|
||||
|
||||
__Steps to reproduce:__
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [and so on...]
|
||||
|
||||
__Expected behavior:__ [What you expected to happen]
|
||||
|
||||
__Actual behavior:__ [What actually happened]
|
||||
|
||||
__Additional info:__ [Include gist of relevant config, logs, etc.]
|
15
.github/ISSUE_TEMPLATE/feature-request.md
vendored
15
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@ -1,15 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Opening a feature request kicks off a discussion
|
||||
|
||||
---
|
||||
|
||||
__Proposal:__ [Description of the feature]
|
||||
|
||||
__Current behavior:__ [What currently happens]
|
||||
|
||||
__Desired behavior:__ [What you would like to happen]
|
||||
|
||||
__Use case:__ [Why is this important (helps with prioritizing requests)]
|
||||
|
||||
Requests may be closed if we're not actively planning to work on them.
|
34
.github/PULL_REQUEST_TEMPLATE.md
vendored
34
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,34 +0,0 @@
|
||||
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
|
||||
v ✰ Thanks for creating a PR! ✰
|
||||
v Before smashing the submit button please review the checkboxes.
|
||||
v If a checkbox is n/a - please still include it but + a little note why
|
||||
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
|
||||
|
||||
Closes: #XXX
|
||||
|
||||
## Description
|
||||
|
||||
<!-- Add a description of the changes that this PR introduces and the files that
|
||||
are the most critical to review.
|
||||
-->
|
||||
|
||||
______
|
||||
|
||||
For contributor use:
|
||||
|
||||
- [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
|
||||
- [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
|
||||
- [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md).
|
||||
- [ ] Wrote unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
|
||||
- [ ] Updated relevant documentation (`docs/`) or specification (`x/<module>/spec/`)
|
||||
- [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code).
|
||||
- [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md`
|
||||
- [ ] Re-reviewed `Files changed` in the Github PR explorer
|
||||
|
||||
______
|
||||
|
||||
For admin use:
|
||||
|
||||
- [ ] Added appropriate labels to PR (ex. `WIP`, `R4R`, `docs`, etc)
|
||||
- [ ] Reviewers assigned
|
||||
- [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr))
|
37
.github/labeler.yml
vendored
37
.github/labeler.yml
vendored
@ -1,37 +0,0 @@
|
||||
"C:Crypto":
|
||||
- crypto/**/*
|
||||
"C:Encoding":
|
||||
- encoding/**/*
|
||||
"C:JSON-RPC":
|
||||
- ethereum/rpc/**/*
|
||||
"C:Proto":
|
||||
- proto/**/*
|
||||
- third_party/**/*
|
||||
- /**/*.pb.go
|
||||
- /**/*.pb.gw.go
|
||||
"C:Types":
|
||||
- types/**/*
|
||||
"C:x/evm":
|
||||
- x/evm/**/*/
|
||||
"Type: Build":
|
||||
- Makefile
|
||||
- Dockerfile
|
||||
- docker-compose.yml
|
||||
- scripts/*
|
||||
- config.yml
|
||||
"Type: CI":
|
||||
- .github/**/*.yml
|
||||
- buf.yaml
|
||||
- .mergify.yml
|
||||
- .golangci.yml
|
||||
"C:CLI":
|
||||
- client/**/*
|
||||
- x/*/client/**/*
|
||||
"Type: Tests":
|
||||
- tests/**/*
|
||||
- /**/*/*_test.go
|
||||
"Type: Docs":
|
||||
- docs/**/*
|
||||
- x/*/spec/**/*
|
||||
"Type: ADR":
|
||||
- docs/architecture/**/*
|
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
cleanup-runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rokroskar/workflow-run-cleanup-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'"
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
- uses: technote-space/get-diff-action@v6.0.1
|
||||
id: git_diff
|
||||
with:
|
||||
SUFFIX_FILTER: |
|
||||
.go
|
||||
.mod
|
||||
.sum
|
||||
- run: |
|
||||
make build
|
||||
if: env.GIT_DIFF
|
71
.github/workflows/codeql-analysis.yml
vendored
71
.github/workflows/codeql-analysis.yml
vendored
@ -1,71 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '37 21 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
38
.github/workflows/deploy-contract.yml
vendored
38
.github/workflows/deploy-contract.yml
vendored
@ -1,38 +0,0 @@
|
||||
name: Deploy Contract
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
cleanup-runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rokroskar/workflow-run-cleanup-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'"
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- uses: technote-space/get-diff-action@v6.0.1
|
||||
id: git_diff
|
||||
with:
|
||||
SUFFIX_FILTER: |
|
||||
.go
|
||||
.mod
|
||||
.sum
|
||||
.sol
|
||||
- name: Test contract
|
||||
run: |
|
||||
sudo make contract-tools
|
||||
sudo make test-contract
|
||||
if: env.GIT_DIFF
|
29
.github/workflows/issues-notion-sync.yml
vendored
29
.github/workflows/issues-notion-sync.yml
vendored
@ -1,29 +0,0 @@
|
||||
name: Notion Sync
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issues:
|
||||
types:
|
||||
[
|
||||
opened,
|
||||
edited,
|
||||
labeled,
|
||||
unlabeled,
|
||||
assigned,
|
||||
unassigned,
|
||||
milestoned,
|
||||
demilestoned,
|
||||
reopened,
|
||||
closed,
|
||||
]
|
||||
|
||||
jobs:
|
||||
notion_job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Add GitHub Issues to Notion
|
||||
steps:
|
||||
- name: Add GitHub Issues to Notion
|
||||
uses: vulcanize/notion-github-action@v1.2.4-issueid
|
||||
with:
|
||||
notion-token: ${{ secrets.NOTION_TOKEN }}
|
||||
notion-db: ${{ secrets.NOTION_DATABASE }}
|
14
.github/workflows/labeler.yml
vendored
14
.github/workflows/labeler.yml
vendored
@ -1,14 +0,0 @@
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
12
.github/workflows/linkchecker.yml
vendored
12
.github/workflows/linkchecker.yml
vendored
@ -1,12 +0,0 @@
|
||||
name: Check Markdown links
|
||||
on:
|
||||
schedule:
|
||||
- cron: "* */24 * * *"
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@1.0.14
|
||||
with:
|
||||
folder-path: "docs"
|
32
.github/workflows/lint.yml
vendored
32
.github/workflows/lint.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: Lint
|
||||
# Lint runs golangci-lint over the entire ethermint repository This workflow is
|
||||
# run on every pull request and push to main The `golangci` will pass without
|
||||
# running if no *.{go, mod, sum} files have been changed.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
golangci:
|
||||
name: Run golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.0
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.48.0
|
||||
args: --timeout 10m
|
||||
github-token: ${{ secrets.github_token }}
|
||||
# Check only if there are differences in the source code
|
||||
if: "env.GIT_DIFF"
|
30
.github/workflows/proto.yml
vendored
30
.github/workflows/proto.yml
vendored
@ -1,30 +0,0 @@
|
||||
name: Protobuf
|
||||
# Protobuf runs buf (https://buf.build/) lint and check-breakage
|
||||
# This workflow is only run when a .proto file has been changed
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.0.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.proto
|
||||
- name: lint
|
||||
run: make proto-lint
|
||||
if: env.GIT_DIFF
|
||||
breakage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.0.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.proto
|
||||
- name: check-breakage
|
||||
run: make proto-check-breaking
|
||||
if: env.GIT_DIFF
|
34
.github/workflows/security.yml
vendored
34
.github/workflows/security.yml
vendored
@ -1,34 +0,0 @@
|
||||
name: Run Gosec
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
Gosec:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
- name: Get Diff
|
||||
uses: technote-space/get-diff-action@v6.0.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/*.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: cosmos/gosec@master
|
||||
with:
|
||||
# we let the report trigger content trigger a failure using the GitHub Security features.
|
||||
args: '-no-fail -fmt sarif -out results.sarif -exclude=G701,G703 ./...'
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: results.sarif
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
41
.github/workflows/semgrep.yml
vendored
41
.github/workflows/semgrep.yml
vendored
@ -1,41 +0,0 @@
|
||||
name: Semgrep
|
||||
on:
|
||||
# Scan changed files in PRs, block on new issues only (existing issues ignored)
|
||||
pull_request: {}
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/semgrep.yml
|
||||
schedule:
|
||||
- cron: '0 0 * * 0'
|
||||
jobs:
|
||||
semgrep:
|
||||
name: Scan
|
||||
runs-on: ubuntu-latest
|
||||
if: (github.actor != 'dependabot[bot]')
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get Diff
|
||||
uses: technote-space/get-diff-action@v6.0.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/*.go
|
||||
**/*.js
|
||||
**/*.ts
|
||||
**/*.sol
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: returntocorp/semgrep-action@v1
|
||||
with:
|
||||
publishToken: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||||
# Upload findings to GitHub Advanced Security Dashboard [step 1/2]
|
||||
# See also the next step.
|
||||
generateSarif: "1"
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
||||
# Upload findings to GitHub Advanced Security Dashboard [step 2/2]
|
||||
- name: Upload SARIF file for GitHub Advanced Security Dashboard
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: semgrep.sarif
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
230
.github/workflows/test.yml
vendored
230
.github/workflows/test.yml
vendored
@ -1,230 +0,0 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
cleanup-runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rokroskar/workflow-run-cleanup-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'"
|
||||
|
||||
test-unit-cover:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test and Create Coverage Report
|
||||
run: |
|
||||
make test-unit-cover
|
||||
if: env.GIT_DIFF
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage.txt
|
||||
fail_ci_if_error: true
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-importer:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: test-importer
|
||||
run: |
|
||||
make test-import
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-rpc:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.sol
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test rpc endpoint
|
||||
run: |
|
||||
make test-rpc
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.sol
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test e2e
|
||||
run: |
|
||||
make test-integration
|
||||
if: env.GIT_DIFF
|
||||
|
||||
# integration_tests:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: cachix/install-nix-action@v18
|
||||
# - uses: cachix/cachix-action@v11
|
||||
# with:
|
||||
# name: ethermint
|
||||
# signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}"
|
||||
# - uses: technote-space/get-diff-action@v6.1.1
|
||||
# with:
|
||||
# PATTERNS: |
|
||||
# **/**.sol
|
||||
# **/**.go
|
||||
# go.mod
|
||||
# go.sum
|
||||
# tests/integration_tests/**
|
||||
# - name: Run integration tests
|
||||
# run: make run-integration-tests
|
||||
# if: env.GIT_DIFF
|
||||
# - name: 'Tar debug files'
|
||||
# if: failure()
|
||||
# run: tar cfz debug_files.tar.gz -C /tmp/pytest-of-runner .
|
||||
# - uses: actions/upload-artifact@v3
|
||||
# if: failure()
|
||||
# with:
|
||||
# name: debug-files
|
||||
# path: debug_files.tar.gz
|
||||
# if-no-files-found: ignore
|
||||
|
||||
# upload-cache:
|
||||
# if: github.event_name == 'push'
|
||||
# needs: ["integration_tests"]
|
||||
# strategy:
|
||||
# matrix:
|
||||
# os: [macos-latest]
|
||||
# runs-on: ${{ matrix.os }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: cachix/install-nix-action@v18
|
||||
# - uses: cachix/cachix-action@v11
|
||||
# with:
|
||||
# name: ethermint
|
||||
# signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}"
|
||||
# - name: 'instantiate integration test env'
|
||||
# run: nix-store -r $(nix-instantiate tests/integration_tests/shell.nix)
|
||||
|
||||
test-sim-nondeterminism:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test simulation nondeterminism
|
||||
run: |
|
||||
make test-sim-nondeterminism
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-sim-random-genesis-fast:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test simulation with random genesis
|
||||
run: |
|
||||
make test-sim-random-genesis-fast
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-sim-import-export:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Simulation of import and export genesis
|
||||
run: |
|
||||
make test-sim-import-export
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test-sim-after-import:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.1
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test simulation after import
|
||||
run: |
|
||||
make test-sim-after-import
|
||||
if: env.GIT_DIFF
|
15
.gitignore
vendored
15
.gitignore
vendored
@ -13,6 +13,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
.dccache
|
||||
|
||||
# Build
|
||||
*.test
|
||||
@ -34,10 +35,14 @@ keyring_test_cosmos
|
||||
./**/node_modules
|
||||
./**/dist
|
||||
secret.yml
|
||||
artifacts/*
|
||||
tmp-swagger-gen
|
||||
github.com/
|
||||
# vue/
|
||||
|
||||
# Local docker volume mappings
|
||||
localnet-setup
|
||||
.testnets
|
||||
|
||||
# Testing
|
||||
coverage.txt
|
||||
@ -45,6 +50,7 @@ coverage.txt
|
||||
sim_log_file
|
||||
tests/**/tmp/*
|
||||
yarn.lock
|
||||
x/auction/client/testutil/bidder-bafyre*
|
||||
|
||||
# Vagrant
|
||||
.vagrant/
|
||||
@ -55,6 +61,7 @@ vagrant
|
||||
# IDE
|
||||
.idea/
|
||||
*.iml
|
||||
*.code-workspace
|
||||
|
||||
# Graphviz
|
||||
dependency-graph.png
|
||||
@ -69,6 +76,8 @@ dependency-graph.png
|
||||
*.abi
|
||||
|
||||
# Node.js
|
||||
tests/**/node_modules/*
|
||||
tests-solidity/**/node_modules/*
|
||||
**/**.json
|
||||
**/node_modules
|
||||
|
||||
|
||||
# OpenZeppelin contracts
|
||||
contracts/@openzeppelin/*
|
||||
|
2766
.gitleaks.toml
Normal file
2766
.gitleaks.toml
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,47 +6,34 @@ run:
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
# - depguard # 20231120 disable until https://github.com/golangci/golangci-lint/issues/3906 is released
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- goconst
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goimports
|
||||
- revive
|
||||
- gofumpt
|
||||
# - revive # 20231120 overly sensitive unused detection
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
# - lll TODO: enable
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- prealloc
|
||||
- exportloopref
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
# - typecheck #TODO: enable
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- nolintlint
|
||||
- asciicheck
|
||||
# - exhaustive
|
||||
- exportloopref
|
||||
- gofumpt
|
||||
- gomodguard
|
||||
# - nestif
|
||||
# - nlreturn
|
||||
- noctx
|
||||
- rowserrcheck
|
||||
# - whitespace
|
||||
# - wsl
|
||||
disable:
|
||||
- typecheck
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
@ -59,6 +46,8 @@ issues:
|
||||
max-same-issues: 50
|
||||
|
||||
linters-settings:
|
||||
lll:
|
||||
line-length: 150
|
||||
dogsled:
|
||||
max-blank-identifiers: 3
|
||||
golint:
|
||||
@ -73,7 +62,7 @@ linters-settings:
|
||||
require-explanation: false
|
||||
require-specific: false
|
||||
gofumpt:
|
||||
lang-version: "1.17"
|
||||
lang-version: "1.19"
|
||||
gomodguard:
|
||||
blocked:
|
||||
versions: # List of blocked module version constraints
|
||||
|
21
.markdownlint.yml
Normal file
21
.markdownlint.yml
Normal file
@ -0,0 +1,21 @@
|
||||
"default": true
|
||||
"MD001": false
|
||||
"MD004": false
|
||||
"MD007":
|
||||
"indent": 4
|
||||
"MD013": false
|
||||
"MD024":
|
||||
"siblings_only": true
|
||||
"MD025": false
|
||||
"MD026":
|
||||
"punctuation": ".;:"
|
||||
"MD029": false
|
||||
"MD033": false
|
||||
"MD034": false
|
||||
"MD036": false
|
||||
"MD040": false
|
||||
"MD041": false
|
||||
"MD051": false
|
||||
"MD049":
|
||||
"style": "asterisk"
|
||||
"no-hard-tabs": false
|
3
.markdownlintignore
Normal file
3
.markdownlintignore
Normal file
@ -0,0 +1,3 @@
|
||||
CHANGELOG.md
|
||||
docs/api/proto-docs.md
|
||||
docs/node_modules
|
28
.mergify.yml
28
.mergify.yml
@ -4,7 +4,7 @@ queue_rules:
|
||||
- "#approved-reviews-by>1"
|
||||
|
||||
pull_request_rules:
|
||||
- name: automerge to the base branch with label automerge and branch protection passing
|
||||
- name: automerge to main with label "automerge" and branch protection passing
|
||||
conditions:
|
||||
- "#approved-reviews-by>1"
|
||||
- base=main
|
||||
@ -12,22 +12,30 @@ pull_request_rules:
|
||||
actions:
|
||||
queue:
|
||||
name: default
|
||||
merge:
|
||||
method: squash
|
||||
commit_message: title+body
|
||||
- name: backport patches to v0.9.x branch
|
||||
commit_message_template: |
|
||||
{{ title }} (#{{ number }})
|
||||
{{ body }}
|
||||
- name: backport patches to main branch
|
||||
conditions:
|
||||
- base=main
|
||||
- label=backport/0.9.x
|
||||
- label=backport/main
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- release/v0.9.x
|
||||
- name: backport patches to v0.7.x branch
|
||||
- main
|
||||
- name: backport patches to v0.20.x branch
|
||||
conditions:
|
||||
- base=main
|
||||
- label=backport/0.7.x
|
||||
- label=backport/0.20.x
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- release/v0.7.x
|
||||
- release/v0.20.x
|
||||
- name: backport patches to v0.19.x branch
|
||||
conditions:
|
||||
- base=main
|
||||
- label=backport/0.19.x
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- release/v0.19.x
|
||||
|
174
.protolint.yml
Normal file
174
.protolint.yml
Normal file
@ -0,0 +1,174 @@
|
||||
---
|
||||
# Lint directives.
|
||||
lint:
|
||||
# # Linter files to ignore.
|
||||
# ignores:
|
||||
# - id: MESSAGE_NAMES_UPPER_CAMEL_CASE
|
||||
# files:
|
||||
# # NOTE: UNIX paths will be properly accepted by both UNIX and Windows.
|
||||
# - _example/proto/simple.proto
|
||||
# - id: ENUM_NAMES_UPPER_CAMEL_CASE
|
||||
# files:
|
||||
# - path/to/foo.proto
|
||||
|
||||
# # Linter files to walk.
|
||||
# files:
|
||||
# # The specific files to exclude.
|
||||
# exclude:
|
||||
# # NOTE: UNIX paths will be properly accepted by both UNIX and Windows.
|
||||
# - path/to/file
|
||||
|
||||
# # Linter directories to walk.
|
||||
# directories:
|
||||
# # The specific directories to exclude.
|
||||
# exclude:
|
||||
# # NOTE: UNIX paths will be properly accepted by both UNIX and Windows.
|
||||
# - path/to/dir
|
||||
|
||||
# Linter rules.
|
||||
# Run `protolint list` to see all available rules.
|
||||
rules:
|
||||
# Determines whether or not to include the default set of linters.
|
||||
no_default: true
|
||||
|
||||
# Set the default to all linters. This option works the other way around as no_default does.
|
||||
# If you want to enable this option, delete the comment out below and no_default.
|
||||
# all_default: true
|
||||
|
||||
# The specific linters to add.
|
||||
add:
|
||||
- FIELD_NAMES_LOWER_SNAKE_CASE
|
||||
- MESSAGE_NAMES_UPPER_CAMEL_CASE
|
||||
- MAX_LINE_LENGTH
|
||||
- INDENT
|
||||
# - SERVICE_NAMES_END_WITH
|
||||
- FIELD_NAMES_EXCLUDE_PREPOSITIONS
|
||||
- MESSAGE_NAMES_EXCLUDE_PREPOSITIONS
|
||||
- FILE_NAMES_LOWER_SNAKE_CASE
|
||||
- IMPORTS_SORTED
|
||||
- PACKAGE_NAME_LOWER_CASE
|
||||
- ORDER
|
||||
- MESSAGES_HAVE_COMMENT
|
||||
- SERVICES_HAVE_COMMENT
|
||||
- RPCS_HAVE_COMMENT
|
||||
- FIELDS_HAVE_COMMENT
|
||||
- PROTO3_FIELDS_AVOID_REQUIRED
|
||||
- PROTO3_GROUPS_AVOID
|
||||
# - REPEATED_FIELD_NAMES_PLURALIZED
|
||||
- ENUMS_HAVE_COMMENT
|
||||
- ENUM_FIELDS_HAVE_COMMENT
|
||||
- SYNTAX_CONSISTENT
|
||||
- RPC_NAMES_UPPER_CAMEL_CASE
|
||||
# - FILE_HAS_COMMENT
|
||||
- QUOTE_CONSISTENT
|
||||
|
||||
# # The specific linters to remove.
|
||||
# remove:
|
||||
# - RPC_NAMES_UPPER_CAMEL_CASE
|
||||
|
||||
# Linter rules option.
|
||||
rules_option:
|
||||
# MAX_LINE_LENGTH rule option.
|
||||
max_line_length:
|
||||
# Enforces a maximum line length
|
||||
max_chars: 120
|
||||
# Specifies the character count for tab characters
|
||||
tab_chars: 2
|
||||
|
||||
# INDENT rule option.
|
||||
indent:
|
||||
# Available styles are 4(4-spaces), 2(2-spaces) or tab.
|
||||
style: 2
|
||||
# Specifies if it should stop considering and inserting new lines at the appropriate positions
|
||||
# when the inner elements are on the same line. Default is false.
|
||||
not_insert_newline: true
|
||||
|
||||
# # FILE_NAMES_LOWER_SNAKE_CASE rule option.
|
||||
# file_names_lower_snake_case:
|
||||
# excludes:
|
||||
# - ../proto/invalidFileName.proto
|
||||
|
||||
# QUOTE_CONSISTENT rule option.
|
||||
quote_consistent:
|
||||
# Available quote are "double" or "single".
|
||||
quote: double
|
||||
|
||||
# ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH rule option.
|
||||
enum_field_names_zero_value_end_with:
|
||||
suffix: INVALID
|
||||
|
||||
# # SERVICE_NAMES_END_WITH rule option.
|
||||
# service_names_end_with:
|
||||
# text: Service
|
||||
|
||||
# FIELD_NAMES_EXCLUDE_PREPOSITIONS rule option.
|
||||
field_names_exclude_prepositions:
|
||||
# The specific prepositions to determine if the field name includes.
|
||||
prepositions:
|
||||
- for
|
||||
- at
|
||||
- of
|
||||
# The specific keywords including prepositions to ignore. E.g. end_of_support is a term you would like to use, and skip checking.
|
||||
excludes:
|
||||
- duration_of_decay
|
||||
|
||||
# # REPEATED_FIELD_NAMES_PLURALIZED rule option.
|
||||
# ## The spec for each rules follows the implementation of https://github.com/gertd/go-pluralize.
|
||||
# ## Plus, you can refer to this rule's test code.
|
||||
# repeated_field_names_pluralized:
|
||||
# uncountable_rules:
|
||||
# - paper
|
||||
# irregular_rules:
|
||||
# Irregular: Regular
|
||||
|
||||
# MESSAGE_NAMES_EXCLUDE_PREPOSITIONS rule option.
|
||||
message_names_exclude_prepositions:
|
||||
# The specific prepositions to determine if the message name includes.
|
||||
prepositions:
|
||||
- With
|
||||
- For
|
||||
- Of
|
||||
# # The specific keywords including prepositions to ignore. E.g. EndOfSupport is a term you would like to use, and skip checking.
|
||||
# excludes:
|
||||
# - EndOfSupport
|
||||
|
||||
# # RPC_NAMES_CASE rule option.
|
||||
# rpc_names_case:
|
||||
# # The specific convention the name should conforms to.
|
||||
# ## Available conventions are "lower_camel_case", "upper_snake_case", or "lower_snake_case".
|
||||
# convention: upper_snake_case
|
||||
|
||||
# MESSAGES_HAVE_COMMENT rule option.
|
||||
messages_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# SERVICES_HAVE_COMMENT rule option.
|
||||
services_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# RPCS_HAVE_COMMENT rule option.
|
||||
rpcs_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# FIELDS_HAVE_COMMENT rule option.
|
||||
fields_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# ENUMS_HAVE_COMMENT rule option.
|
||||
enums_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# ENUM_FIELDS_HAVE_COMMENT rule option.
|
||||
enum_fields_have_comment:
|
||||
# Comments need to begin with the name of the thing being described. default is false.
|
||||
should_follow_golang_style: true
|
||||
|
||||
# # SYNTAX_CONSISTENT rule option.
|
||||
# syntax_consistent:
|
||||
# # Default is proto3.
|
||||
# version: proto2
|
@ -19,6 +19,9 @@ test/
|
||||
tests/
|
||||
*_test.go
|
||||
|
||||
# false positive; TODO: https://github.com/cerc-io/laconicd/issues/104
|
||||
testutil/network/network.go
|
||||
|
||||
# Semgrep rules folder
|
||||
.semgrep
|
||||
|
||||
|
3
.solhint.json
Normal file
3
.solhint.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "solhint:default"
|
||||
}
|
31
.yamllint
Normal file
31
.yamllint
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
|
||||
yaml-files:
|
||||
- '*.yaml'
|
||||
- '*.yml'
|
||||
- '.yamllint'
|
||||
|
||||
rules:
|
||||
braces: enable
|
||||
brackets: enable
|
||||
colons: enable
|
||||
commas: enable
|
||||
comments:
|
||||
level: warning
|
||||
comments-indentation: disable
|
||||
document-end: disable
|
||||
document-start: disable
|
||||
empty-lines: disable
|
||||
empty-values: disable
|
||||
float-values: disable
|
||||
hyphens: enable
|
||||
indentation: enable
|
||||
key-duplicates: enable
|
||||
key-ordering: disable
|
||||
line-length: disable
|
||||
new-line-at-end-of-file: enable
|
||||
new-lines: enable
|
||||
octal-values: disable
|
||||
quoted-strings: disable
|
||||
trailing-spaces: disable
|
||||
truthy: disable
|
75
CHANGELOG.md
75
CHANGELOG.md
@ -36,7 +36,57 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
## [v0.20.0] - 2022-12-28
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (deps) [#1564](https://github.com/evmos/ethermint/pull/1564) Bump ibc-go to [`v5.2.0`](https://github.com/cosmos/ibc-go/releases/tag/v5.2.0)
|
||||
* (evm) [\#1272](https://github.com/evmos/ethermint/pull/1272) Implement modular interface for the EVM.
|
||||
* (deps) [#1551](https://github.com/evmos/ethermint/pull/1551) Upgrade Cosmos SDK to [`v0.46.7`](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.7).
|
||||
* (feemarket) [#1194](https://github.com/evmos/ethermint/pull/1194) Apply feemarket to native cosmos tx.
|
||||
* (eth) [#1346](https://github.com/evmos/ethermint/pull/1346) Added support for `sdk.Dec` and `ed25519` type on eip712.
|
||||
* (evm) [#1452](https://github.com/evmos/ethermint/pull/1452) Simplify Gas Math in `ApplyTransaction`.
|
||||
* (eth) [#1430](https://github.com/evmos/ethermint/pull/1430) Added support for array of type `Any` on eip712.
|
||||
* (ante) [1460](https://github.com/evmos/ethermint/pull/1460) Add KV Gas config on ethereum Txs.
|
||||
* (eth) [#1459](https://github.com/evmos/ethermint/pull/1459) Added support for messages with optional types omitted on eip712.
|
||||
* (geth) [#1413](https://github.com/evmos/ethermint/pull/1413) Update go-ethereum version to [`v1.10.26`](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.26).
|
||||
* (deps) [#1419](https://github.com/evmos/ethermint/pull/1419) Add ics23 patch for dragonberry and bump iavl to v0.19.4
|
||||
|
||||
### API Breaking
|
||||
|
||||
* (ante) [#1521](https://github.com/evmos/ethermint/pull/1521) Deprecate support for legacy EIP-712 signature verification implementation via AnteHandler decorator.
|
||||
* (ante) [#1214](https://github.com/evmos/ethermint/pull/1214) Set mempool priority to EVM transactions.
|
||||
* (evm) [#1405](https://github.com/evmos/ethermint/pull/1405) Add parameter `chainID` to evm keeper's `EVMConfig` method, so caller can choose to not use the cached `eip155ChainID`.
|
||||
|
||||
### Features
|
||||
|
||||
* (ci) [#1528](https://github.com/evmos/ethermint/pull/1528) Add Golang dependency vulnerability checker.
|
||||
* (app) [#1501](https://github.com/evmos/ethermint/pull/1501) Set default File store listener for application from [ADR38](https://docs.cosmos.network/v0.47/architecture/adr-038-state-listening)
|
||||
|
||||
### Improvements
|
||||
|
||||
* (tests) [#1507](https://github.com/evmos/ethermint/pull/1507) Remove legacy sim tests
|
||||
* (feemarket) [#1508](https://github.com/evmos/ethermint/pull/1508) Remove old x/params migration logic
|
||||
* (evm) [#1499](https://github.com/evmos/ethermint/pull/1499) Add Shanghai and Cancun block
|
||||
* (ante) [#1455](https://github.com/evmos/ethermint/pull/1455) Refactor `AnteHandler` logic
|
||||
* (evm) [#1444](https://github.com/evmos/ethermint/pull/1444) Improve performance of `eth_estimateGas`
|
||||
* (ante) [\#1388](https://github.com/evmos/ethermint/pull/1388) Optimize AnteHandler gas consumption
|
||||
* (lint) [#1298](https://github.com/evmos/ethermint/pull/1298) 150 character line length limit, `gofumpt`, and linting
|
||||
* (feemarket) [\#1165](https://github.com/evmos/ethermint/pull/1165) Add hint in specs about different gas terminology in Cosmos and Ethereum.
|
||||
* (cli) [#1226](https://github.com/evmos/ethermint/pull/1226) Add custom app db backend flag.
|
||||
* (ante) [#1289](https://github.com/evmos/ethermint/pull/1289) Change the fallback tx priority mechanism to be based on gas price.
|
||||
* (test) [#1311](https://github.com/evmos/ethermint/pull/1311) Add integration test for the `rollback` cmd
|
||||
* (ledger) [#1277](https://github.com/evmos/ethermint/pull/1277) Add Ledger preprocessing transaction hook for EIP-712-signed Cosmos payloads.
|
||||
* (rpc) [#1296](https://github.com/evmos/ethermint/pull/1296) Add RPC Backend unit tests.
|
||||
* (rpc) [#1352](https://github.com/evmos/ethermint/pull/1352) Make the grpc queries run concurrently, don't block the consensus state machine.
|
||||
* (cli) [#1360](https://github.com/evmos/ethermint/pull/1360) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag.
|
||||
* (rpc) [#1378](https://github.com/evmos/ethermint/pull/1378) Add support for EVM RPC metrics
|
||||
* (ante) [#1390](https://github.com/evmos/ethermint/pull/1390) Added multisig tx support.
|
||||
* (test) [#1396](https://github.com/evmos/ethermint/pull/1396) Increase test coverage for the EVM module `keeper`
|
||||
* (ante) [#1397](https://github.com/evmos/ethermint/pull/1397) Refactor EIP-712 signature verification to support EIP-712 multi-signing.
|
||||
* (deps) [#1416](https://github.com/evmos/ethermint/pull/1416) Bump Go version to `1.19`
|
||||
* (cmd) [\#1417](https://github.com/evmos/ethermint/pull/1417) Apply Google CLI Syntax for required and optional args.
|
||||
* (deps) [#1456](https://github.com/evmos/ethermint/pull/1456) Migrate errors-related functionality from "github.com/cosmos/cosmos-sdk/types/errors" (deprecated) to "cosmossdk.io/errors"
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
@ -63,11 +113,30 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (rpc) [#1561](https://github.com/evmos/ethermint/pull/1561) Fix call to `NewMnemonic` in `personal_newAccount`
|
||||
* (cli) [#1550](https://github.com/evmos/ethermint/pull/1550) Fix signature algorithm validation and default for Ledger.
|
||||
* (eip712) [#1543](https://github.com/evmos/ethermint/pull/1543) Improve error handling for EIP-712 encoding config initialization.
|
||||
* (app) [#1505](https://github.com/evmos/ethermint/pull/1505) Setup gRPC node service with the application.
|
||||
* (server) [#1497](https://github.com/evmos/ethermint/pull/1497) Fix telemetry server setup for observability
|
||||
* (rpc) [#1442](https://github.com/evmos/ethermint/pull/1442) Fix decoding of `finalized` block number.
|
||||
* (rpc) [#1179](https://github.com/evmos/ethermint/pull/1179) Fix gas used in traceTransaction response.
|
||||
* (rpc) [#1284](https://github.com/evmos/ethermint/pull/1284) Fix internal trace response upon incomplete `eth_sendTransaction` call.
|
||||
* (rpc) [#1340](https://github.com/evmos/ethermint/pull/1340) Fix error response when `eth_estimateGas` height provided is not found.
|
||||
* (rpc) [#1354](https://github.com/evmos/ethermint/pull/1354) Fix grpc query failure(`BaseFee` and `EthCall`) on legacy block states.
|
||||
* (cli) [#1362](https://github.com/evmos/ethermint/pull/1362) Fix `index-eth-tx` error when the indexer db is empty.
|
||||
* (state) [#1320](https://github.com/evmos/ethermint/pull/1320) Fix codehash check mismatch when the code has been deleted in the evm state.
|
||||
* (rpc) [#1392](https://github.com/evmos/ethermint/pull/1392) Allow fill the proposer address in json-rpc through tendermint api, and pass explicitly to grpc query handler.
|
||||
* (rpc) [#1431](https://github.com/evmos/ethermint/pull/1431) Align hex-strings proof fields in `eth_getProof` as Ethereum.
|
||||
* (proto) [#1466](https://github.com/evmos/ethermint/pull/1466) Fix proto scripts and upgrade them to mirror current cosmos-sdk scripts
|
||||
* (rpc) [#1405](https://github.com/evmos/ethermint/pull/1405) Fix uninitialized chain ID field in gRPC requests.
|
||||
* (analytics) [#1434](https://github.com/evmos/ethermint/pull/1434) Remove unbound labels from custom tendermint metrics.
|
||||
* (rpc) [#1484](https://github.com/evmos/ethermint/pull/1484) Align empty account result for old blocks as ethereum instead of return account not found error.
|
||||
* (rpc) [#1503](https://github.com/evmos/ethermint/pull/1503) Fix block hashes returned on JSON-RPC filter `eth_newBlockFilter`.
|
||||
* (ante) [#1566](https://github.com/evmos/ethermint/pull/1566) Fix `gasWanted` on `EthGasConsumeDecorator` ante handler when running transaction in `ReCheckMode`
|
||||
|
||||
## [v0.19.3] - 2022-10-14
|
||||
|
||||
* (deps) [1381](https://github.com/evmos/ethermint/pull/1381) Bump sdk to `v0.45.9`
|
||||
|
||||
## [v0.19.2] - 2022-08-29
|
||||
|
||||
@ -197,7 +266,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (evm) [tharsis#1088](https://github.com/evmos/ethermint/pull/1088) Fix ability to append log in tx post processing.
|
||||
* (rpc) [tharsis#1081](https://github.com/evmos/ethermint/pull/1081) fix `debug_getBlockRlp`/`debug_printBlock` don't filter failed transactions.
|
||||
* (ante) [tharsis#1111](https://github.com/evmos/ethermint/pull/1111) Move CanTransfer decorator before GasConsume decorator
|
||||
* (types) [tharsis#1112](https://github.com/cosmos/ethermint/pull/1112) Add `GetBaseAccount` to avoid invalid account error when create vesting account.
|
||||
* (types) [tharsis#1112](https://github.com/evmos/ethermint/pull/1112) Add `GetBaseAccount` to avoid invalid account error when create vesting account.
|
||||
|
||||
## [v0.15.0] - 2022-05-09
|
||||
|
||||
@ -633,7 +702,7 @@ the Tracer type used to collect execution traces from the EVM transaction execut
|
||||
|
||||
### Improvements
|
||||
|
||||
* (deps) [tharsis#615](https://github.com/cosmos/ethermint/pull/615) Bump Cosmos SDK version to [v0.39.2](https://github.com/cosmos/cosmos-sdk/tag/v0.39.2)
|
||||
* (deps) [tharsis#615](https://github.com/cosmos/ethermint/pull/615) Bump Cosmos SDK version to [v0.39.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.2)
|
||||
* (deps) [tharsis#610](https://github.com/cosmos/ethermint/pull/610) Update Go dependency to 1.15+.
|
||||
* (evm) [tharsis#603](https://github.com/cosmos/ethermint/pull/603) Add state transition params that enable or disable the EVM `Call` and `Create` operations.
|
||||
* (deps) [tharsis#602](https://github.com/cosmos/ethermint/pull/602) Bump tendermint version to [v0.33.9](https://github.com/tendermint/tendermint/releases/tag/v0.33.9)
|
||||
|
16
Dockerfile
16
Dockerfile
@ -1,14 +1,14 @@
|
||||
FROM golang:alpine AS build-env
|
||||
|
||||
# Set up dependencies
|
||||
ENV PACKAGES git build-base
|
||||
# Install dependencies
|
||||
RUN apk add --update git build-base linux-headers
|
||||
|
||||
# Set working directory for the build
|
||||
WORKDIR /go/src/github.com/cerc-io/laconicd
|
||||
|
||||
# Install dependencies
|
||||
RUN apk add --update $PACKAGES
|
||||
RUN apk add linux-headers
|
||||
# Cache Go modules
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Add source files
|
||||
COPY . .
|
||||
@ -17,14 +17,14 @@ COPY . .
|
||||
RUN make build
|
||||
|
||||
# Final image
|
||||
FROM alpine:3.16.2
|
||||
FROM alpine:3.17.0
|
||||
|
||||
# Install ca-certificates
|
||||
RUN apk add --update ca-certificates jq
|
||||
WORKDIR /
|
||||
RUN apk add --update ca-certificates jq curl
|
||||
|
||||
# Copy over binaries from the build-env
|
||||
COPY --from=build-env /go/src/github.com/cerc-io/laconicd/build/laconicd /usr/bin/laconicd
|
||||
|
||||
WORKDIR /
|
||||
# Run laconicd by default
|
||||
CMD ["laconicd"]
|
||||
|
167
Makefile
Executable file → Normal file
167
Makefile
Executable file → Normal file
@ -14,7 +14,6 @@ SIMAPP = ./app
|
||||
HTTPS_GIT := https://github.com/cerc-io/laconicd.git
|
||||
PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git)
|
||||
DOCKER := $(shell which docker)
|
||||
DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf:1.0.0-rc8
|
||||
# RocksDB is a native dependency, so we don't assume the library is installed.
|
||||
# Instead, it must be explicitly enabled and we warn when it is not.
|
||||
ENABLE_ROCKSDB ?= false
|
||||
@ -73,7 +72,7 @@ ifeq ($(ENABLE_ROCKSDB),true)
|
||||
BUILD_TAGS += rocksdb_build
|
||||
test_tags += rocksdb_build
|
||||
else
|
||||
$(warning RocksDB support is disabled; to build and test with RocksDB support, set ENABLE_ROCKSDB=true)
|
||||
$(info RocksDB support is disabled; to build and test with RocksDB support, set ENABLE_ROCKSDB=true)
|
||||
endif
|
||||
|
||||
# DB backend selection
|
||||
@ -111,6 +110,12 @@ ifeq (,$(findstring nostrip,$(COSMOS_BUILD_OPTIONS)))
|
||||
BUILD_FLAGS += -trimpath
|
||||
endif
|
||||
|
||||
# check if no optimization option is passed
|
||||
# used for remote debugging
|
||||
ifneq (,$(findstring nooptimization,$(COSMOS_BUILD_OPTIONS)))
|
||||
BUILD_FLAGS += -gcflags "all=-N -l"
|
||||
endif
|
||||
|
||||
# # The below include contains the tools and runsim targets.
|
||||
# include contrib/devtools/Makefile
|
||||
|
||||
@ -140,7 +145,7 @@ docker-build:
|
||||
# update old container
|
||||
docker rm laconicd || true
|
||||
# create a new container from the latest image
|
||||
docker create --name laconic -t -i cerc-io/laconicd:latest laconicd
|
||||
docker create --name laconic -t -i ${DOCKER_IMAGE}:${DOCKER_TAG} laconicd
|
||||
# move the binaries to the ./build directory
|
||||
mkdir -p ./build/
|
||||
docker cp laconic:/usr/bin/laconicd ./build/
|
||||
@ -158,7 +163,7 @@ clean:
|
||||
|
||||
all: build
|
||||
|
||||
build-all: tools build lint test
|
||||
build-all: tools build lint test vulncheck
|
||||
|
||||
.PHONY: distclean clean build-all
|
||||
|
||||
@ -167,7 +172,7 @@ build-all: tools build lint test
|
||||
###############################################################################
|
||||
|
||||
PACKAGE_NAME:=github.com/cerc-io/laconicd
|
||||
GOLANG_CROSS_VERSION = v1.18
|
||||
GOLANG_CROSS_VERSION = v1.19
|
||||
GOPATH ?= '$(HOME)/go'
|
||||
release-dry-run:
|
||||
docker run \
|
||||
@ -232,7 +237,7 @@ endif
|
||||
|
||||
ifeq (, $(shell which go-bindata))
|
||||
@echo "Installing go-bindata..."
|
||||
@go get github.com/kevinburke/go-bindata/go-bindata
|
||||
@go get github.com/kevinburke/go-bindata/go-bindata@v3
|
||||
else
|
||||
@echo "go-bindata already installed; skipping..."
|
||||
endif
|
||||
@ -276,6 +281,10 @@ go.sum: go.mod
|
||||
go mod verify
|
||||
go mod tidy
|
||||
|
||||
vulncheck: $(BUILDDIR)/
|
||||
GOBIN=$(BUILDDIR) go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
$(BUILDDIR)/govulncheck ./...
|
||||
|
||||
###############################################################################
|
||||
### Documentation ###
|
||||
###############################################################################
|
||||
@ -307,7 +316,7 @@ TEST_TARGETS := test-unit test-unit-cover test-race
|
||||
# Test runs-specific rules. To add a new test target, just add
|
||||
# a new rule, customise ARGS or TEST_PACKAGES ad libitum, and
|
||||
# append the new rule to the TEST_TARGETS list.
|
||||
test-unit: ARGS=-timeout=10m -race
|
||||
test-unit: ARGS=-timeout=10m -race -test.v -skip $(SKIP_UNIT_TESTS)
|
||||
test-unit: TEST_PACKAGES=$(PACKAGES_UNIT)
|
||||
|
||||
test-race: ARGS=-race
|
||||
@ -325,13 +334,10 @@ else
|
||||
endif
|
||||
|
||||
test-import:
|
||||
go test -run TestImporterTestSuite -v --vet=off github.com/cerc-io/laconicd/tests/importer
|
||||
go test -run TestImporterTestSuite -timeout=20m -v --vet=off github.com/cerc-io/laconicd/tests/importer
|
||||
|
||||
test-rpc:
|
||||
./scripts/integration-test-all.sh -t "rpc" -q 1 -z 1 -s 2 -m "rpc" -r "true"
|
||||
|
||||
test-integration:
|
||||
./scripts/integration-test-all.sh -t "integration" -q 1 -z 1 -s 2 -m "integration" -r "true"
|
||||
./scripts/integration-test-all.sh -t "rpc" -q 1 -z 1 -s 10 -m "rpc" -r "true"
|
||||
|
||||
run-integration-tests:
|
||||
@nix-shell ./tests/integration_tests/shell.nix --run ./scripts/run-integration-tests.sh
|
||||
@ -349,51 +355,6 @@ test-solidity:
|
||||
|
||||
.PHONY: run-tests test test-all test-import test-rpc test-contract test-solidity $(TEST_TARGETS)
|
||||
|
||||
test-sim-nondeterminism:
|
||||
@echo "Running non-determinism test..."
|
||||
@go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \
|
||||
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h
|
||||
|
||||
test-sim-random-genesis-fast:
|
||||
@echo "Running random genesis simulation..."
|
||||
@go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation \
|
||||
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h
|
||||
|
||||
test-sim-import-export: runsim
|
||||
@echo "Running application import/export simulation. This may take several minutes..."
|
||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppImportExport
|
||||
|
||||
test-sim-after-import: runsim
|
||||
@echo "Running application simulation-after-import. This may take several minutes..."
|
||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport
|
||||
|
||||
test-sim-random-genesis-multi-seed: runsim
|
||||
@echo "Running multi-seed custom genesis simulation..."
|
||||
@$(BINDIR)/runsim -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation
|
||||
|
||||
test-sim-multi-seed-long: runsim
|
||||
@echo "Running long multi-seed application simulation. This may take awhile!"
|
||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation
|
||||
|
||||
test-sim-multi-seed-short: runsim
|
||||
@echo "Running short multi-seed application simulation. This may take awhile!"
|
||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 10 TestFullAppSimulation
|
||||
|
||||
test-sim-benchmark-invariants:
|
||||
@echo "Running simulation invariant benchmarks..."
|
||||
@go test -mod=readonly $(SIMAPP) -benchmem -bench=BenchmarkInvariants -run=^$ \
|
||||
-Enabled=true -NumBlocks=1000 -BlockSize=200 \
|
||||
-Period=1 -Commit=true -Seed=57 -v -timeout 24h
|
||||
|
||||
.PHONY: \
|
||||
test-sim-nondeterminism \
|
||||
test-sim-custom-genesis-fast \
|
||||
test-sim-import-export \
|
||||
test-sim-after-import \
|
||||
test-sim-custom-genesis-multi-seed \
|
||||
test-sim-multi-seed-short \
|
||||
test-sim-multi-seed-long \
|
||||
test-sim-benchmark-invariants
|
||||
|
||||
benchmark:
|
||||
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
||||
@ -427,74 +388,62 @@ format-fix:
|
||||
### Protobuf ###
|
||||
###############################################################################
|
||||
|
||||
protoVer=v0.2
|
||||
# ------
|
||||
# NOTE: Link to the tendermintdev/sdk-proto-gen docker images:
|
||||
# https://hub.docker.com/r/tendermintdev/sdk-proto-gen/tags
|
||||
#
|
||||
protoVer=v0.7
|
||||
protoImageName=tendermintdev/sdk-proto-gen:$(protoVer)
|
||||
containerProtoGen=$(PROJECT_NAME)-proto-gen-$(protoVer)
|
||||
containerProtoGenAny=$(PROJECT_NAME)-proto-gen-any-$(protoVer)
|
||||
containerProtoGenSwagger=$(PROJECT_NAME)-proto-gen-swagger-$(protoVer)
|
||||
containerProtoFmt=$(PROJECT_NAME)-proto-fmt-$(protoVer)
|
||||
protoImage=$(DOCKER) run --network host --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)
|
||||
# ------
|
||||
# NOTE: cosmos/proto-builder image is needed because clang-format is not installed
|
||||
# on the tendermintdev/sdk-proto-gen docker image.
|
||||
# Link to the cosmos/proto-builder docker images:
|
||||
# https://github.com/cosmos/cosmos-sdk/pkgs/container/proto-builder
|
||||
#
|
||||
protoCosmosVer=0.11.2
|
||||
protoCosmosName=ghcr.io/cosmos/proto-builder:$(protoCosmosVer)
|
||||
protoCosmosImage=$(DOCKER) run --network host --rm -v $(CURDIR):/workspace --workdir /workspace $(protoCosmosName)
|
||||
# ------
|
||||
# NOTE: Link to the yoheimuta/protolint docker images:
|
||||
# https://hub.docker.com/r/yoheimuta/protolint/tags
|
||||
#
|
||||
protolintVer=0.42.2
|
||||
protolintName=yoheimuta/protolint:$(protolintVer)
|
||||
protolintImage=$(DOCKER) run --network host --rm -v $(CURDIR):/workspace --workdir /workspace $(protolintName)
|
||||
|
||||
|
||||
# ------
|
||||
# NOTE: If you are experiencing problems running these commands, try deleting
|
||||
# the docker images and execute the desired command again.
|
||||
#
|
||||
proto-all: proto-format proto-lint proto-gen
|
||||
|
||||
proto-gen:
|
||||
@echo "Generating Protobuf files"
|
||||
@if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGen}$$"; then docker start -a $(containerProtoGen); else docker run --name $(containerProtoGen) -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) \
|
||||
sh ./scripts/protocgen.sh; fi
|
||||
$(protoImage) sh ./scripts/protocgen.sh
|
||||
|
||||
proto-swagger-gen:
|
||||
@echo "Generating Protobuf Swagger"
|
||||
@./scripts/proto-tools-installer.sh
|
||||
@./scripts/protoc-swagger-gen.sh
|
||||
|
||||
# TODO: Rethink API docs generation
|
||||
# proto-swagger-gen:
|
||||
# @echo "Generating Protobuf Swagger"
|
||||
# $(protoImage) sh ./scripts/protoc-swagger-gen.sh
|
||||
|
||||
proto-format:
|
||||
@echo "Formatting Protobuf files"
|
||||
@if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoFmt}$$"; then docker start -a $(containerProtoFmt); else docker run --name $(containerProtoFmt) -v $(CURDIR):/workspace --workdir /workspace tendermintdev/docker-build-proto \
|
||||
find ./ -not -path "./third_party/*" -name "*.proto" -exec clang-format -i {} \; ; fi
|
||||
$(protoCosmosImage) find ./ -name *.proto -exec clang-format -i {} \;
|
||||
|
||||
# NOTE: The linter configuration lives in .protolint.yaml
|
||||
proto-lint:
|
||||
@$(DOCKER_BUF) lint --error-format=json
|
||||
@echo "Linting Protobuf files"
|
||||
$(protolintImage) lint ./proto
|
||||
|
||||
proto-check-breaking:
|
||||
@$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=main
|
||||
@echo "Checking Protobuf files for breaking changes"
|
||||
$(protoImage) buf breaking --against $(HTTPS_GIT)#branch=main
|
||||
|
||||
|
||||
TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.12/proto/tendermint
|
||||
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
|
||||
COSMOS_SDK_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/v0.43.0
|
||||
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
|
||||
|
||||
TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto
|
||||
TM_ABCI_TYPES = third_party/proto/tendermint/abci
|
||||
TM_TYPES = third_party/proto/tendermint/types
|
||||
|
||||
GOGO_PROTO_TYPES = third_party/proto/gogoproto
|
||||
|
||||
COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto
|
||||
|
||||
proto-update-deps:
|
||||
@mkdir -p $(GOGO_PROTO_TYPES)
|
||||
@curl -sSL $(GOGO_PROTO_URL)/gogoproto/gogo.proto > $(GOGO_PROTO_TYPES)/gogo.proto
|
||||
|
||||
@mkdir -p $(COSMOS_PROTO_TYPES)
|
||||
@curl -sSL $(COSMOS_PROTO_URL)/cosmos.proto > $(COSMOS_PROTO_TYPES)/cosmos.proto
|
||||
|
||||
## Importing of tendermint protobuf definitions currently requires the
|
||||
## use of `sed` in order to build properly with cosmos-sdk's proto file layout
|
||||
## (which is the standard Buf.build FILE_LAYOUT)
|
||||
## Issue link: https://github.com/tendermint/tendermint/issues/5021
|
||||
@mkdir -p $(TM_ABCI_TYPES)
|
||||
@curl -sSL $(TM_URL)/abci/types.proto > $(TM_ABCI_TYPES)/types.proto
|
||||
|
||||
@mkdir -p $(TM_TYPES)
|
||||
@curl -sSL $(TM_URL)/types/types.proto > $(TM_TYPES)/types.proto
|
||||
|
||||
@mkdir -p $(TM_CRYPTO_TYPES)
|
||||
@curl -sSL $(TM_URL)/crypto/proof.proto > $(TM_CRYPTO_TYPES)/proof.proto
|
||||
@curl -sSL $(TM_URL)/crypto/keys.proto > $(TM_CRYPTO_TYPES)/keys.proto
|
||||
|
||||
|
||||
|
||||
.PHONY: proto-all proto-gen proto-gen-any proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps
|
||||
.PHONY: proto-all proto-gen proto-gen-any proto-format proto-lint proto-check-breaking
|
||||
|
||||
###############################################################################
|
||||
### Localnet ###
|
||||
|
10
README.md
10
README.md
@ -7,7 +7,7 @@
|
||||
|
||||
The Source of Proof. Laconic is a next generation data availability & verifiability layer with cryptographic proofs, powering internet-scale Web3 applications, built on Proof-of-Stake with fast-finality using the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint Core](https://github.com/tendermint/tendermint) consensus engine.
|
||||
|
||||
**Note**: Requires [Go 1.18+](https://golang.org/dl/)
|
||||
**Note**: Requires [Go 1.19+](https://golang.org/dl/)
|
||||
|
||||
## Installation
|
||||
|
||||
@ -15,6 +15,14 @@ The Source of Proof. Laconic is a next generation data availability & verifiabil
|
||||
make install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To quickly get started with a single node fixture, run:
|
||||
|
||||
```bash
|
||||
./init.sh
|
||||
```
|
||||
|
||||
## Community
|
||||
|
||||
The following chat channels and forums are a great spot to ask questions about Ethermint:
|
||||
|
@ -6,8 +6,10 @@ import (
|
||||
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
@ -44,14 +46,14 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
// handle as *evmtypes.MsgEthereumTx
|
||||
anteHandler = newEthAnteHandler(options)
|
||||
case "/ethermint.types.v1.ExtensionOptionsWeb3Tx":
|
||||
// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
|
||||
anteHandler = newCosmosAnteHandlerEip712(options)
|
||||
// Deprecated: Handle as normal Cosmos SDK tx, except signature is checked for Legacy EIP712 representation
|
||||
anteHandler = NewLegacyCosmosAnteHandlerEip712(options)
|
||||
case "/ethermint.types.v1.ExtensionOptionDynamicFeeTx":
|
||||
// cosmos-sdk tx with dynamic fee extension
|
||||
anteHandler = newCosmosAnteHandler(options)
|
||||
default:
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrUnknownExtensionOptions,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrUnknownExtensionOptions,
|
||||
"rejecting tx with unsupported extension option: %s", typeURL,
|
||||
)
|
||||
}
|
||||
@ -65,7 +67,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
case sdk.Tx:
|
||||
anteHandler = newCosmosAnteHandler(options)
|
||||
default:
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid transaction type: %T", tx)
|
||||
}
|
||||
|
||||
return anteHandler(ctx, tx, sim)
|
||||
@ -74,7 +76,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
|
||||
func Recover(logger tmlog.Logger, err *error) {
|
||||
if r := recover(); r != nil {
|
||||
*err = sdkerrors.Wrapf(sdkerrors.ErrPanic, "%v", r)
|
||||
*err = errorsmod.Wrapf(errortypes.ErrPanic, "%v", r)
|
||||
|
||||
if e, ok := r.(error); ok {
|
||||
logger.Error(
|
||||
@ -99,12 +101,48 @@ var _ authante.SignatureVerificationGasConsumer = DefaultSigVerificationGasConsu
|
||||
func DefaultSigVerificationGasConsumer(
|
||||
meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params,
|
||||
) error {
|
||||
// support for ethereum ECDSA secp256k1 keys
|
||||
_, ok := sig.PubKey.(*ethsecp256k1.PubKey)
|
||||
if ok {
|
||||
pubkey := sig.PubKey
|
||||
switch pubkey := pubkey.(type) {
|
||||
case *ethsecp256k1.PubKey:
|
||||
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: eth_secp256k1")
|
||||
return nil
|
||||
|
||||
case multisig.PubKey:
|
||||
// Multisig keys
|
||||
multisignature, ok := sig.Data.(*signing.MultiSignatureData)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
|
||||
}
|
||||
return ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
|
||||
|
||||
default:
|
||||
return authante.DefaultSigVerificationGasConsumer(meter, sig, params)
|
||||
}
|
||||
}
|
||||
|
||||
// ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
|
||||
func ConsumeMultisignatureVerificationGas(
|
||||
meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey,
|
||||
params authtypes.Params, accSeq uint64,
|
||||
) error {
|
||||
size := sig.BitArray.Count()
|
||||
sigIndex := 0
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
if !sig.BitArray.GetIndex(i) {
|
||||
continue
|
||||
}
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: pubkey.GetPubKeys()[i],
|
||||
Data: sig.Signatures[sigIndex],
|
||||
Sequence: accSeq,
|
||||
}
|
||||
err := DefaultSigVerificationGasConsumer(meter, sigV2, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigIndex++
|
||||
}
|
||||
|
||||
return authante.DefaultSigVerificationGasConsumer(meter, sig, params)
|
||||
return nil
|
||||
}
|
||||
|
@ -2,24 +2,36 @@ package ante_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/authz"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cerc-io/laconicd/tests"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
ethparams "github.com/ethereum/go-ethereum/params"
|
||||
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
)
|
||||
|
||||
func (suite AnteTestSuite) TestAnteHandler() {
|
||||
@ -337,6 +349,17 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 create validator (with blank fields)",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MsgCreateValidator2(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 MsgSubmitProposal",
|
||||
func() sdk.Tx {
|
||||
@ -364,7 +387,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt,
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
|
||||
@ -397,10 +420,65 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MsgEditValidator(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
txBuilder := suite.CreateTestEIP712MsgSubmitEvidence(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 submit proposal v1",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712SubmitProposalV1(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 MsgExec",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MsgExec(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 MsgVoteV1",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MsgVoteV1(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success- DeliverTx EIP712 Multiple MsgSend",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MultipleMsgSend(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 Multiple Signers",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712MultipleSignerMsgs(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID",
|
||||
func() sdk.Tx {
|
||||
@ -496,6 +574,301 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
return tx
|
||||
}, true, false, false,
|
||||
},
|
||||
{
|
||||
"passes - Single-signer EIP-712",
|
||||
func() sdk.Tx {
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(privKey.PubKey().Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSingleSignedTx(
|
||||
privKey,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000000,
|
||||
"EIP-712",
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"passes - EIP-712 multi-key",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000000,
|
||||
"EIP-712",
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"passes - Mixed multi-key",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000000,
|
||||
"mixed", // Combine EIP-712 and standard signatures
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"passes - Mixed multi-key with MsgVote",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := govtypes.NewMsgVote(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
1,
|
||||
govtypes.OptionYes,
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000000,
|
||||
"mixed", // Combine EIP-712 and standard signatures
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"Fails - Multi-Key with incorrect Chain ID",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
msg,
|
||||
"ethermint_9005-1",
|
||||
2000000,
|
||||
"mixed",
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"Fails - Multi-Key with incorrect sign mode",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000000,
|
||||
"mixed",
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"Fails - Multi-Key with too little gas",
|
||||
func() sdk.Tx {
|
||||
numKeys := 5
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000,
|
||||
"mixed", // Combine EIP-712 and standard signatures
|
||||
)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"Fails - Multi-Key with different payload than one signed",
|
||||
func() sdk.Tx {
|
||||
numKeys := 1
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000,
|
||||
"EIP-712",
|
||||
)
|
||||
|
||||
msg.Amount[0].Amount = sdk.NewInt(5)
|
||||
txBuilder.SetMsgs(msg)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"Fails - Multi-Key with messages added after signing",
|
||||
func() sdk.Tx {
|
||||
numKeys := 1
|
||||
privKeys, pubKeys := suite.GenerateMultipleKeys(numKeys)
|
||||
pk := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(pk.Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSignedMultisigTx(
|
||||
privKeys,
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000,
|
||||
"EIP-712",
|
||||
)
|
||||
|
||||
// Duplicate
|
||||
txBuilder.SetMsgs(msg, msg)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"Fails - Single-Signer EIP-712 with messages added after signing",
|
||||
func() sdk.Tx {
|
||||
msg := banktypes.NewMsgSend(
|
||||
sdk.AccAddress(privKey.PubKey().Address()),
|
||||
addr[:],
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"photon",
|
||||
sdk.NewInt(1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
txBuilder := suite.CreateTestSingleSignedTx(
|
||||
privKey,
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
msg,
|
||||
"ethermint_9000-1",
|
||||
2000,
|
||||
"EIP-712",
|
||||
)
|
||||
|
||||
txBuilder.SetMsgs(msg, msg)
|
||||
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -914,3 +1287,93 @@ func (suite AnteTestSuite) TestAnteHandlerWithParams() {
|
||||
}
|
||||
suite.evmParamsOption = nil
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestConsumeSignatureVerificationGas() {
|
||||
params := authtypes.DefaultParams()
|
||||
msg := []byte{1, 2, 3, 4}
|
||||
cdc := simapp.MakeTestEncodingConfig().Amino
|
||||
|
||||
p := authtypes.DefaultParams()
|
||||
skR1, _ := secp256r1.GenPrivKey()
|
||||
pkSet1, sigSet1, err := generatePubKeysAndSignatures(5, msg, false)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
multisigKey1 := kmultisig.NewLegacyAminoPubKey(2, pkSet1)
|
||||
multisignature1 := multisig.NewMultisig(len(pkSet1))
|
||||
expectedCost1 := expectedGasCostByKeys(pkSet1)
|
||||
|
||||
for i := 0; i < len(pkSet1); i++ {
|
||||
stdSig := legacytx.StdSignature{PubKey: pkSet1[i], Signature: sigSet1[i]}
|
||||
sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig)
|
||||
suite.Require().NoError(err)
|
||||
err = multisig.AddSignatureV2(multisignature1, sigV2, pkSet1)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
meter sdk.GasMeter
|
||||
sig signing.SignatureData
|
||||
pubkey cryptotypes.PubKey
|
||||
params authtypes.Params
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
gasConsumed uint64
|
||||
shouldErr bool
|
||||
}{
|
||||
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, p.SigVerifyCostED25519, true},
|
||||
{"PubKeyEthSecp256k1", args{sdk.NewInfiniteGasMeter(), nil, pkSet1[0], params}, 21_000, false},
|
||||
{"PubKeySecp256r1", args{sdk.NewInfiniteGasMeter(), nil, skR1.PubKey(), params}, p.SigVerifyCostSecp256r1(), false},
|
||||
{"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1, multisigKey1, params}, expectedCost1, false},
|
||||
{"unknown key", args{sdk.NewInfiniteGasMeter(), nil, nil, params}, 0, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: tt.args.pubkey,
|
||||
Data: tt.args.sig,
|
||||
Sequence: 0, // Arbitrary account sequence
|
||||
}
|
||||
err := ante.DefaultSigVerificationGasConsumer(tt.args.meter, sigV2, tt.args.params)
|
||||
|
||||
if tt.shouldErr {
|
||||
suite.Require().NotNil(err)
|
||||
} else {
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Equal(tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generatePubKeysAndSignatures(n int, msg []byte, _ bool) (pubkeys []cryptotypes.PubKey, signatures [][]byte, err error) {
|
||||
pubkeys = make([]cryptotypes.PubKey, n)
|
||||
signatures = make([][]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
privkey, err := ethsecp256k1.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pubkeys[i] = privkey.PubKey()
|
||||
signatures[i], _ = privkey.Sign(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func expectedGasCostByKeys(pubkeys []cryptotypes.PubKey) uint64 {
|
||||
cost := uint64(0)
|
||||
for _, pubkey := range pubkeys {
|
||||
pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey))
|
||||
switch {
|
||||
case strings.Contains(pubkeyType, "ed25519"):
|
||||
cost += authtypes.DefaultParams().SigVerifyCostED25519
|
||||
case strings.Contains(pubkeyType, "ethsecp256k1"):
|
||||
cost += 21_000
|
||||
case strings.Contains(pubkeyType, "secp256k1"):
|
||||
cost += authtypes.DefaultParams().SigVerifyCostSecp256k1
|
||||
default:
|
||||
panic("unexpected key type")
|
||||
}
|
||||
}
|
||||
return cost
|
||||
}
|
||||
|
@ -3,15 +3,17 @@ package ante
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
ibcante "github.com/cosmos/ibc-go/v5/modules/core/ante"
|
||||
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cerc-io/laconicd/ethereum/eip712"
|
||||
@ -19,6 +21,7 @@ import (
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
)
|
||||
|
||||
var ethermintCodec codec.ProtoCodecMarshaler
|
||||
@ -29,19 +32,47 @@ func init() {
|
||||
ethermintCodec = codec.NewProtoCodec(registry)
|
||||
}
|
||||
|
||||
// Eip712SigVerificationDecorator Verify all signatures for a tx and return an error if any are invalid. Note,
|
||||
// the Eip712SigVerificationDecorator decorator will not get executed on ReCheck.
|
||||
// Deprecated: NewLegacyCosmosAnteHandlerEip712 creates an AnteHandler to process legacy EIP-712
|
||||
// transactions, as defined by the presence of an ExtensionOptionsWeb3Tx extension.
|
||||
func NewLegacyCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
|
||||
return sdk.ChainAnteDecorators(
|
||||
RejectMessagesDecorator{}, // reject MsgEthereumTxs
|
||||
authante.NewSetUpContextDecorator(),
|
||||
authante.NewValidateBasicDecorator(),
|
||||
authante.NewTxTimeoutHeightDecorator(),
|
||||
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
|
||||
authante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
authante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
authante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
// SetPubKeyDecorator must be called before all signature verification decorators
|
||||
authante.NewSetPubKeyDecorator(options.AccountKeeper),
|
||||
authante.NewValidateSigCountDecorator(options.AccountKeeper),
|
||||
authante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
||||
// Note: signature verification uses EIP instead of the cosmos signature validator
|
||||
NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||
authante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
|
||||
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
||||
)
|
||||
}
|
||||
|
||||
// Deprecated: LegacyEip712SigVerificationDecorator Verify all signatures for a tx and return an error if any are invalid. Note,
|
||||
// the LegacyEip712SigVerificationDecorator decorator will not get executed on ReCheck.
|
||||
// NOTE: As of v0.20.0, EIP-712 signature verification is handled by the ethsecp256k1 public key (see ethsecp256k1.go)
|
||||
//
|
||||
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
|
||||
// CONTRACT: Tx must implement SigVerifiableTx interface
|
||||
type Eip712SigVerificationDecorator struct {
|
||||
type LegacyEip712SigVerificationDecorator struct {
|
||||
ak evmtypes.AccountKeeper
|
||||
signModeHandler authsigning.SignModeHandler
|
||||
}
|
||||
|
||||
// NewEip712SigVerificationDecorator creates a new Eip712SigVerificationDecorator
|
||||
func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandler authsigning.SignModeHandler) Eip712SigVerificationDecorator {
|
||||
return Eip712SigVerificationDecorator{
|
||||
// Deprecated: NewLegacyEip712SigVerificationDecorator creates a new LegacyEip712SigVerificationDecorator
|
||||
func NewLegacyEip712SigVerificationDecorator(
|
||||
ak evmtypes.AccountKeeper,
|
||||
signModeHandler authsigning.SignModeHandler,
|
||||
) LegacyEip712SigVerificationDecorator {
|
||||
return LegacyEip712SigVerificationDecorator{
|
||||
ak: ak,
|
||||
signModeHandler: signModeHandler,
|
||||
}
|
||||
@ -49,7 +80,7 @@ func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandle
|
||||
|
||||
// AnteHandle handles validation of EIP712 signed cosmos txs.
|
||||
// it is not run on RecheckTx
|
||||
func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
func (svd LegacyEip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
tx sdk.Tx,
|
||||
simulate bool,
|
||||
next sdk.AnteHandler,
|
||||
@ -61,12 +92,12 @@ func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
|
||||
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement authsigning.SigVerifiableTx", tx)
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "tx %T doesn't implement authsigning.SigVerifiableTx", tx)
|
||||
}
|
||||
|
||||
authSignTx, ok := tx.(authsigning.Tx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement the authsigning.Tx interface", tx)
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "tx %T doesn't implement the authsigning.Tx interface", tx)
|
||||
}
|
||||
|
||||
// stdSigs contains the sequence number, account number, and signatures.
|
||||
@ -80,12 +111,16 @@ func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
|
||||
// EIP712 allows just one signature
|
||||
if len(sigs) != 1 {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signers (%d); EIP712 signatures allows just one signature", len(sigs))
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrTooManySignatures,
|
||||
"invalid number of signers (%d); EIP712 signatures allows just one signature",
|
||||
len(sigs),
|
||||
)
|
||||
}
|
||||
|
||||
// check that signer length and signature length are the same
|
||||
if len(sigs) != len(signerAddrs) {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs))
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrorInvalidSigner, "invalid number of signers; expected: %d, got %d", len(signerAddrs), len(sigs))
|
||||
}
|
||||
|
||||
// EIP712 has just one signature, avoid looping here and only read index 0
|
||||
@ -100,13 +135,13 @@ func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
// retrieve pubkey
|
||||
pubKey := acc.GetPubKey()
|
||||
if !simulate && pubKey == nil {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidPubKey, "pubkey on account is not set")
|
||||
}
|
||||
|
||||
// Check account sequence number.
|
||||
if sig.Sequence != acc.GetSequence() {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrWrongSequence,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrWrongSequence,
|
||||
"account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence,
|
||||
)
|
||||
}
|
||||
@ -132,7 +167,7 @@ func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
|
||||
|
||||
if err := VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, authSignTx); err != nil {
|
||||
errMsg := fmt.Errorf("signature verification failed; please verify account number (%d) and chain-id (%s): %w", accNum, chainID, err)
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg.Error())
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrUnauthorized, errMsg.Error())
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
@ -150,12 +185,12 @@ func VerifySignature(
|
||||
switch data := sigData.(type) {
|
||||
case *signing.SingleSignatureData:
|
||||
if data.SignMode != signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrNotSupported, "unexpected SignatureData %T: wrong SignMode", sigData)
|
||||
return errorsmod.Wrapf(errortypes.ErrNotSupported, "unexpected SignatureData %T: wrong SignMode", sigData)
|
||||
}
|
||||
|
||||
// Note: this prevents the user from sending thrash data in the signature field
|
||||
// Note: this prevents the user from sending trash data in the signature field
|
||||
if len(data.Signature) != 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrTooManySignatures, "invalid signature value; EIP712 must have the cosmos transaction signature empty")
|
||||
return errorsmod.Wrap(errortypes.ErrTooManySignatures, "invalid signature value; EIP712 must have the cosmos transaction signature empty")
|
||||
}
|
||||
|
||||
// @contract: this code is reached only when Msg has Web3Tx extension (so this custom Ante handler flow),
|
||||
@ -163,7 +198,7 @@ func VerifySignature(
|
||||
|
||||
msgs := tx.GetMsgs()
|
||||
if len(msgs) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrNoSignatures, "tx doesn't contain any msgs to verify signature")
|
||||
return errorsmod.Wrap(errortypes.ErrNoSignatures, "tx doesn't contain any msgs to verify signature")
|
||||
}
|
||||
|
||||
txBytes := legacytx.StdSignBytes(
|
||||
@ -180,33 +215,33 @@ func VerifySignature(
|
||||
|
||||
signerChainID, err := ethermint.ParseChainID(signerData.ChainID)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrapf(err, "failed to parse chainID: %s", signerData.ChainID)
|
||||
return errorsmod.Wrapf(err, "failed to parse chain-id: %s", signerData.ChainID)
|
||||
}
|
||||
|
||||
txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx)
|
||||
if !ok {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "tx doesnt contain any extensions")
|
||||
return errorsmod.Wrap(errortypes.ErrUnknownExtensionOptions, "tx doesnt contain any extensions")
|
||||
}
|
||||
opts := txWithExtensions.GetExtensionOptions()
|
||||
if len(opts) != 1 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "tx doesnt contain expected amount of extension options")
|
||||
return errorsmod.Wrap(errortypes.ErrUnknownExtensionOptions, "tx doesnt contain expected amount of extension options")
|
||||
}
|
||||
|
||||
extOpt, ok := opts[0].GetCachedValue().(*ethermint.ExtensionOptionsWeb3Tx)
|
||||
if !ok {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "unknown extension option")
|
||||
return errorsmod.Wrap(errortypes.ErrUnknownExtensionOptions, "unknown extension option")
|
||||
}
|
||||
|
||||
if extOpt.TypedDataChainID != signerChainID.Uint64() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "invalid chainID")
|
||||
return errorsmod.Wrap(errortypes.ErrInvalidChainID, "invalid chain-id")
|
||||
}
|
||||
|
||||
if len(extOpt.FeePayer) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "no feePayer on ExtensionOptionsWeb3Tx")
|
||||
return errorsmod.Wrap(errortypes.ErrUnknownExtensionOptions, "no feePayer on ExtensionOptionsWeb3Tx")
|
||||
}
|
||||
feePayer, err := sdk.AccAddressFromBech32(extOpt.FeePayer)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to parse feePayer from ExtensionOptionsWeb3Tx")
|
||||
return errorsmod.Wrap(err, "failed to parse feePayer from ExtensionOptionsWeb3Tx")
|
||||
}
|
||||
|
||||
feeDelegation := &eip712.FeeDelegationOptions{
|
||||
@ -215,17 +250,17 @@ func VerifySignature(
|
||||
|
||||
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to pack tx data in EIP712 object")
|
||||
return errorsmod.Wrap(err, "failed to create EIP-712 typed data from tx")
|
||||
}
|
||||
|
||||
sigHash, err := eip712.ComputeTypedDataHash(typedData)
|
||||
sigHash, _, err := apitypes.TypedDataAndHash(typedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
feePayerSig := extOpt.FeePayerSig
|
||||
if len(feePayerSig) != ethcrypto.SignatureLength {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "signature length doesn't match typical [R||S||V] signature 65 bytes")
|
||||
return errorsmod.Wrap(errortypes.ErrorInvalidSigner, "signature length doesn't match typical [R||S||V] signature 65 bytes")
|
||||
}
|
||||
|
||||
// Remove the recovery offset if needed (ie. Metamask eip712 signature)
|
||||
@ -235,12 +270,12 @@ func VerifySignature(
|
||||
|
||||
feePayerPubkey, err := secp256k1.RecoverPubkey(sigHash, feePayerSig)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to recover delegated fee payer from sig")
|
||||
return errorsmod.Wrap(err, "failed to recover delegated fee payer from sig")
|
||||
}
|
||||
|
||||
ecPubKey, err := ethcrypto.UnmarshalPubkey(feePayerPubkey)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to unmarshal recovered fee payer pubkey")
|
||||
return errorsmod.Wrap(err, "failed to unmarshal recovered fee payer pubkey")
|
||||
}
|
||||
|
||||
pk := ðsecp256k1.PubKey{
|
||||
@ -248,23 +283,23 @@ func VerifySignature(
|
||||
}
|
||||
|
||||
if !pubKey.Equals(pk) {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "feePayer pubkey %s is different from transaction pubkey %s", pubKey, pk)
|
||||
return errorsmod.Wrapf(errortypes.ErrInvalidPubKey, "feePayer pubkey %s is different from transaction pubkey %s", pubKey, pk)
|
||||
}
|
||||
|
||||
recoveredFeePayerAcc := sdk.AccAddress(pk.Address().Bytes())
|
||||
|
||||
if !recoveredFeePayerAcc.Equals(feePayer) {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrorInvalidSigner, "failed to verify delegated fee payer %s signature", recoveredFeePayerAcc)
|
||||
return errorsmod.Wrapf(errortypes.ErrorInvalidSigner, "failed to verify delegated fee payer %s signature", recoveredFeePayerAcc)
|
||||
}
|
||||
|
||||
// VerifySignature of ethsecp256k1 accepts 64 byte signature [R||S]
|
||||
// WARNING! Under NO CIRCUMSTANCES try to use pubKey.VerifySignature there
|
||||
if !secp256k1.VerifySignature(pubKey.Bytes(), sigHash, feePayerSig[:len(feePayerSig)-1]) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "unable to verify signer signature of EIP712 typed data")
|
||||
return errorsmod.Wrap(errortypes.ErrorInvalidSigner, "unable to verify signer signature of EIP712 typed data")
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrTooManySignatures, "unexpected SignatureData %T", sigData)
|
||||
return errorsmod.Wrapf(errortypes.ErrTooManySignatures, "unexpected SignatureData %T", sigData)
|
||||
}
|
||||
}
|
||||
|
450
app/ante/eth.go
450
app/ante/eth.go
@ -1,19 +1,32 @@
|
||||
// Copyright 2021 Evmos Foundation
|
||||
// This file is part of Evmos' Ethermint library.
|
||||
//
|
||||
// The Ethermint library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The Ethermint library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE
|
||||
package ante
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
evmkeeper "github.com/cerc-io/laconicd/x/evm/keeper"
|
||||
"github.com/cerc-io/laconicd/x/evm/keeper"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
|
||||
@ -21,61 +34,6 @@ import (
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// EthSigVerificationDecorator validates an ethereum signatures
|
||||
type EthSigVerificationDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthSigVerificationDecorator creates a new EthSigVerificationDecorator
|
||||
func NewEthSigVerificationDecorator(ek EVMKeeper) EthSigVerificationDecorator {
|
||||
return EthSigVerificationDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle validates checks that the registered chain id is the same as the one on the message, and
|
||||
// that the signer address matches the one defined on the message.
|
||||
// It's not skipped for RecheckTx, because it set `From` address which is critical from other ante handler to work.
|
||||
// Failure in RecheckTx will prevent tx to be included into block, especially when CheckTx succeed, in which case user
|
||||
// won't see the error message.
|
||||
func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
chainID := esvd.evmKeeper.ChainID()
|
||||
|
||||
params := esvd.evmKeeper.GetParams(ctx)
|
||||
|
||||
ethCfg := params.ChainConfig.EthereumConfig(chainID)
|
||||
blockNum := big.NewInt(ctx.BlockHeight())
|
||||
signer := ethtypes.MakeSigner(ethCfg, blockNum)
|
||||
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
ethTx := msgEthTx.AsTransaction()
|
||||
if !params.AllowUnprotectedTxs && !ethTx.Protected() {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrNotSupported,
|
||||
"rejected unprotected Ethereum txs. Please EIP155 sign your transaction to protect it against replay-attacks")
|
||||
}
|
||||
|
||||
sender, err := signer.Sender(ethTx)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrorInvalidSigner,
|
||||
"couldn't retrieve sender address from the ethereum transaction: %s",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
// set up the sender to the transaction field if not already
|
||||
msgEthTx.From = sender.Hex()
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthAccountVerificationDecorator validates an account balance checks
|
||||
type EthAccountVerificationDecorator struct {
|
||||
ak evmtypes.AccountKeeper
|
||||
@ -109,35 +67,35 @@ func (avd EthAccountVerificationDecorator) AnteHandle(
|
||||
for i, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "failed to unpack tx data any for tx %d", i)
|
||||
return ctx, errorsmod.Wrapf(err, "failed to unpack tx data any for tx %d", i)
|
||||
}
|
||||
|
||||
// sender address should be in the tx cache from the previous AnteHandle call
|
||||
from := msgEthTx.GetFrom()
|
||||
if from.Empty() {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty")
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidAddress, "from address cannot be empty")
|
||||
}
|
||||
|
||||
// check whether the sender address is EOA
|
||||
fromAddr := common.BytesToAddress(from)
|
||||
acct := avd.evmKeeper.GetAccount(ctx, fromAddr) //nolint: all
|
||||
acct := avd.evmKeeper.GetAccount(ctx, fromAddr)
|
||||
|
||||
if acct == nil {
|
||||
acc := avd.ak.NewAccountWithAddress(ctx, from)
|
||||
avd.ak.SetAccount(ctx, acc)
|
||||
acct = statedb.NewEmptyAccount()
|
||||
} else if acct.IsContract() {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType,
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType,
|
||||
"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
|
||||
}
|
||||
|
||||
if err := evmkeeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil {
|
||||
return ctx, sdkerrors.Wrap(err, "failed to check sender balance")
|
||||
if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil {
|
||||
return ctx, errorsmod.Wrap(err, "failed to check sender balance")
|
||||
}
|
||||
}
|
||||
return next(ctx, tx, simulate)
|
||||
@ -165,7 +123,7 @@ func NewEthGasConsumeDecorator(
|
||||
// (during CheckTx only) and that the sender has enough balance to pay for the gas cost.
|
||||
//
|
||||
// Intrinsic gas for a transaction is the amount of gas that the transaction uses before the
|
||||
// transaction is executed. The gas is a constant value plus any cost inccured by additional bytes
|
||||
// transaction is executed. The gas is a constant value plus any cost incurred by additional bytes
|
||||
// of data supplied with the transaction.
|
||||
//
|
||||
// This AnteHandler decorator will fail if:
|
||||
@ -175,31 +133,43 @@ func NewEthGasConsumeDecorator(
|
||||
// - user doesn't have enough balance to deduct the transaction fees (gas_limit * gas_price)
|
||||
// - transaction or block gas meter runs out of gas
|
||||
// - sets the gas meter limit
|
||||
// - gas limit is greater than the block gas meter limit
|
||||
func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
params := egcd.evmKeeper.GetParams(ctx)
|
||||
gasWanted := uint64(0)
|
||||
// gas consumption limit already checked during CheckTx so there's no need to
|
||||
// verify it again during ReCheckTx
|
||||
if ctx.IsReCheckTx() {
|
||||
// Use new context with gasWanted = 0
|
||||
// Otherwise, there's an error on txmempool.postCheck (tendermint)
|
||||
// that is not bubbled up. Thus, the Tx never runs on DeliverMode
|
||||
// Error: "gas wanted -1 is negative"
|
||||
// For more information, see issue #1554
|
||||
// https://github.com/cerc-io/laconicd/issues/1554
|
||||
newCtx := ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted))
|
||||
return next(newCtx, tx, simulate)
|
||||
}
|
||||
|
||||
ethCfg := params.ChainConfig.EthereumConfig(egcd.evmKeeper.ChainID())
|
||||
chainCfg := egcd.evmKeeper.GetChainConfig(ctx)
|
||||
ethCfg := chainCfg.EthereumConfig(egcd.evmKeeper.ChainID())
|
||||
|
||||
blockHeight := big.NewInt(ctx.BlockHeight())
|
||||
homestead := ethCfg.IsHomestead(blockHeight)
|
||||
istanbul := ethCfg.IsIstanbul(blockHeight)
|
||||
london := ethCfg.IsLondon(blockHeight)
|
||||
evmDenom := params.EvmDenom
|
||||
gasWanted := uint64(0)
|
||||
var events sdk.Events
|
||||
|
||||
// Use the lowest priority of all the messages as the final one.
|
||||
minPriority := int64(math.MaxInt64)
|
||||
baseFee := egcd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrap(err, "failed to unpack tx data")
|
||||
return ctx, errorsmod.Wrap(err, "failed to unpack tx data")
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() && egcd.maxGasWanted != 0 {
|
||||
@ -213,45 +183,59 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
|
||||
gasWanted += txData.GetGas()
|
||||
}
|
||||
|
||||
fees, priority, err := egcd.evmKeeper.DeductTxCostsFromUserBalance(
|
||||
ctx,
|
||||
*msgEthTx,
|
||||
txData,
|
||||
evmDenom,
|
||||
homestead,
|
||||
istanbul,
|
||||
london,
|
||||
)
|
||||
evmDenom := egcd.evmKeeper.GetEVMDenom(ctx)
|
||||
|
||||
fees, err := keeper.VerifyFee(txData, evmDenom, baseFee, homestead, istanbul, ctx.IsCheckTx())
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "failed to deduct transaction costs from user balance")
|
||||
return ctx, errorsmod.Wrapf(err, "failed to verify the fees")
|
||||
}
|
||||
|
||||
events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyFee, fees.String())))
|
||||
err = egcd.evmKeeper.DeductTxCostsFromUserBalance(ctx, fees, common.HexToAddress(msgEthTx.From))
|
||||
if err != nil {
|
||||
return ctx, errorsmod.Wrapf(err, "failed to deduct transaction costs from user balance")
|
||||
}
|
||||
|
||||
events = append(events,
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeTx,
|
||||
sdk.NewAttribute(sdk.AttributeKeyFee, fees.String()),
|
||||
),
|
||||
)
|
||||
|
||||
priority := evmtypes.GetTxPriority(txData, baseFee)
|
||||
|
||||
if priority < minPriority {
|
||||
minPriority = priority
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change to typed events
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
// TODO: deprecate after https://github.com/cosmos/cosmos-sdk/issues/9514 is fixed on SDK
|
||||
blockGasLimit := ethermint.BlockGasLimit(ctx)
|
||||
|
||||
// NOTE: safety check
|
||||
if blockGasLimit > 0 {
|
||||
// generate a copy of the gas pool (i.e block gas meter) to see if we've run out of gas for this block
|
||||
// if current gas consumed is greater than the limit, this funcion panics and the error is recovered on the Baseapp
|
||||
gasPool := sdk.NewGasMeter(blockGasLimit)
|
||||
gasPool.ConsumeGas(ctx.GasMeter().GasConsumedToLimit(), "gas pool check")
|
||||
// return error if the tx gas is greater than the block limit (max gas)
|
||||
|
||||
// NOTE: it's important here to use the gas wanted instead of the gas consumed
|
||||
// from the tx gas pool. The later only has the value so far since the
|
||||
// EthSetupContextDecorator so it will never exceed the block gas limit.
|
||||
if gasWanted > blockGasLimit {
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrOutOfGas,
|
||||
"tx gas (%d) exceeds block gas limit (%d)",
|
||||
gasWanted,
|
||||
blockGasLimit,
|
||||
)
|
||||
}
|
||||
|
||||
// Set ctx.GasMeter with a limit of GasWanted (gasLimit)
|
||||
gasConsumed := ctx.GasMeter().GasConsumed()
|
||||
ctx = ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted))
|
||||
ctx.GasMeter().ConsumeGas(gasConsumed, "copy gas consumed")
|
||||
// Set tx GasMeter with a limit of GasWanted (i.e gas limit from the Ethereum tx).
|
||||
// The gas consumed will be then reset to the gas used by the state transition
|
||||
// in the EVM.
|
||||
|
||||
newCtx := ctx.WithPriority(minPriority)
|
||||
// FIXME: use a custom gas configuration that doesn't add any additional gas and only
|
||||
// takes into account the gas consumed at the end of the EVM transaction.
|
||||
newCtx := ctx.
|
||||
WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted)).
|
||||
WithPriority(minPriority)
|
||||
|
||||
// we know that we have enough gas on the pool to cover the intrinsic gas
|
||||
return next(newCtx, tx, simulate)
|
||||
@ -280,19 +264,35 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
baseFee := ctd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
|
||||
coreMsg, err := msgEthTx.AsMessage(signer, baseFee)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
return ctx, errorsmod.Wrapf(
|
||||
err,
|
||||
"failed to create an ethereum core.Message from signer %T", signer,
|
||||
)
|
||||
}
|
||||
|
||||
if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) {
|
||||
if baseFee == nil {
|
||||
return ctx, errorsmod.Wrap(
|
||||
evmtypes.ErrInvalidBaseFee,
|
||||
"base fee is supported but evm block context value is nil",
|
||||
)
|
||||
}
|
||||
if coreMsg.GasFeeCap().Cmp(baseFee) < 0 {
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInsufficientFee,
|
||||
"max fee per gas less than block base fee (%s < %s)",
|
||||
coreMsg.GasFeeCap(), baseFee,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: pass in an empty coinbase address and nil tracer as we don't need them for the check below
|
||||
cfg := &evmtypes.EVMConfig{
|
||||
ChainConfig: ethCfg,
|
||||
@ -300,35 +300,20 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
|
||||
CoinBase: common.Address{},
|
||||
BaseFee: baseFee,
|
||||
}
|
||||
|
||||
stateDB := statedb.New(ctx, ctd.evmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash().Bytes())))
|
||||
evm := ctd.evmKeeper.NewEVM(ctx, coreMsg, cfg, evmtypes.NewNoOpTracer(), stateDB)
|
||||
|
||||
// check that caller has enough balance to cover asset transfer for **topmost** call
|
||||
// NOTE: here the gas consumed is from the context with the infinite gas meter
|
||||
if coreMsg.Value().Sign() > 0 && !evm.Context().CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInsufficientFunds,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInsufficientFunds,
|
||||
"failed to transfer %s from address %s using the EVM block context transfer function",
|
||||
coreMsg.Value(),
|
||||
coreMsg.From(),
|
||||
)
|
||||
}
|
||||
|
||||
if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) {
|
||||
if baseFee == nil {
|
||||
return ctx, sdkerrors.Wrap(
|
||||
evmtypes.ErrInvalidBaseFee,
|
||||
"base fee is supported but evm block context value is nil",
|
||||
)
|
||||
}
|
||||
if coreMsg.GasFeeCap().Cmp(baseFee) < 0 {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInsufficientFee,
|
||||
"max fee per gas less than block base fee (%s < %s)",
|
||||
coreMsg.GasFeeCap(), baseFee,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
@ -353,19 +338,19 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrap(err, "failed to unpack tx data")
|
||||
return ctx, errorsmod.Wrap(err, "failed to unpack tx data")
|
||||
}
|
||||
|
||||
// increase sequence of sender
|
||||
acc := issd.ak.GetAccount(ctx, msgEthTx.GetFrom())
|
||||
if acc == nil {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrUnknownAddress,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrUnknownAddress,
|
||||
"account %s is nil", common.BytesToAddress(msgEthTx.GetFrom().Bytes()),
|
||||
)
|
||||
}
|
||||
@ -374,14 +359,14 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s
|
||||
// we merged the nonce verification to nonce increment, so when tx includes multiple messages
|
||||
// with same sender, they'll be accepted.
|
||||
if txData.GetNonce() != nonce {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInvalidSequence,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInvalidSequence,
|
||||
"invalid nonce; got %d, expected %d", txData.GetNonce(), nonce,
|
||||
)
|
||||
}
|
||||
|
||||
if err := acc.SetSequence(nonce + 1); err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1)
|
||||
return ctx, errorsmod.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1)
|
||||
}
|
||||
|
||||
issd.ak.SetAccount(ctx, acc)
|
||||
@ -389,216 +374,3 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthValidateBasicDecorator is adapted from ValidateBasicDecorator from cosmos-sdk, it ignores ErrNoSignatures
|
||||
type EthValidateBasicDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthValidateBasicDecorator creates a new EthValidateBasicDecorator
|
||||
func NewEthValidateBasicDecorator(ek EVMKeeper) EthValidateBasicDecorator {
|
||||
return EthValidateBasicDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle handles basic validation of tx
|
||||
func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
// no need to validate basic on recheck tx, call next antehandler
|
||||
if ctx.IsReCheckTx() {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
err := tx.ValidateBasic()
|
||||
// ErrNoSignatures is fine with eth tx
|
||||
if err != nil && !errors.Is(err, sdkerrors.ErrNoSignatures) {
|
||||
return ctx, sdkerrors.Wrap(err, "tx basic validation failed")
|
||||
}
|
||||
|
||||
// For eth type cosmos tx, some fields should be veified as zero values,
|
||||
// since we will only verify the signature against the hash of the MsgEthereumTx.Data
|
||||
wrapperTx, ok := tx.(protoTxProvider)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid tx type %T, didn't implement interface protoTxProvider", tx)
|
||||
}
|
||||
|
||||
protoTx := wrapperTx.GetProtoTx()
|
||||
body := protoTx.Body
|
||||
if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
|
||||
"for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty")
|
||||
}
|
||||
|
||||
if len(body.ExtensionOptions) != 1 {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx length of ExtensionOptions should be 1")
|
||||
}
|
||||
|
||||
txFee := sdk.Coins{}
|
||||
txGasLimit := uint64(0)
|
||||
|
||||
params := vbd.evmKeeper.GetParams(ctx)
|
||||
chainID := vbd.evmKeeper.ChainID()
|
||||
ethCfg := params.ChainConfig.EthereumConfig(chainID)
|
||||
baseFee := vbd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
|
||||
for _, msg := range protoTx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
// Validate `From` field
|
||||
if msgEthTx.From != "" {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid From %s, expect empty string", msgEthTx.From)
|
||||
}
|
||||
|
||||
txGasLimit += msgEthTx.GetGas()
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrap(err, "failed to unpack MsgEthereumTx Data")
|
||||
}
|
||||
|
||||
// return error if contract creation or call are disabled through governance
|
||||
if !params.EnableCreate && txData.GetTo() == nil {
|
||||
return ctx, sdkerrors.Wrap(evmtypes.ErrCreateDisabled, "failed to create new contract")
|
||||
} else if !params.EnableCall && txData.GetTo() != nil {
|
||||
return ctx, sdkerrors.Wrap(evmtypes.ErrCallDisabled, "failed to call contract")
|
||||
}
|
||||
|
||||
if baseFee == nil && txData.TxType() == ethtypes.DynamicFeeTxType {
|
||||
return ctx, sdkerrors.Wrap(ethtypes.ErrTxTypeNotSupported, "dynamic fee tx not supported")
|
||||
}
|
||||
|
||||
txFee = txFee.Add(sdk.NewCoin(params.EvmDenom, sdkmath.NewIntFromBigInt(txData.Fee())))
|
||||
}
|
||||
|
||||
authInfo := protoTx.AuthInfo
|
||||
if len(authInfo.SignerInfos) > 0 {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo SignerInfos should be empty")
|
||||
}
|
||||
|
||||
if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo Fee payer and granter should be empty")
|
||||
}
|
||||
|
||||
if !authInfo.Fee.Amount.IsEqual(txFee) {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee Amount (%s != %s)", authInfo.Fee.Amount, txFee)
|
||||
}
|
||||
|
||||
if authInfo.Fee.GasLimit != txGasLimit {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee GasLimit (%d != %d)", authInfo.Fee.GasLimit, txGasLimit)
|
||||
}
|
||||
|
||||
sigs := protoTx.Signatures
|
||||
if len(sigs) > 0 {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx Signatures should be empty")
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthSetupContextDecorator is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption
|
||||
// by setting the gas meter to infinite
|
||||
type EthSetupContextDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
func NewEthSetUpContextDecorator(evmKeeper EVMKeeper) EthSetupContextDecorator {
|
||||
return EthSetupContextDecorator{
|
||||
evmKeeper: evmKeeper,
|
||||
}
|
||||
}
|
||||
|
||||
func (esc EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// all transactions must implement GasTx
|
||||
_, ok := tx.(authante.GasTx)
|
||||
if !ok {
|
||||
return newCtx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be GasTx")
|
||||
}
|
||||
|
||||
newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
// Reset transient gas used to prepare the execution of current cosmos tx.
|
||||
// Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs.
|
||||
esc.evmKeeper.ResetTransientGasUsed(ctx)
|
||||
return next(newCtx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthMempoolFeeDecorator will check if the transaction's effective fee is at least as large
|
||||
// as the local validator's minimum gasFee (defined in validator config).
|
||||
// If fee is too low, decorator returns error and tx is rejected from mempool.
|
||||
// Note this only applies when ctx.CheckTx = true
|
||||
// If fee is high enough or not CheckTx, then call next AnteHandler
|
||||
// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
|
||||
type EthMempoolFeeDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
func NewEthMempoolFeeDecorator(ek EVMKeeper) EthMempoolFeeDecorator {
|
||||
return EthMempoolFeeDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle ensures that the provided fees meet a minimum threshold for the validator,
|
||||
// if this is a CheckTx. This is only for local mempool purposes, and thus
|
||||
// is only ran on check tx.
|
||||
// It only do the check if london hardfork not enabled or feemarket not enabled, because in that case feemarket will take over the task.
|
||||
func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
if ctx.IsCheckTx() && !simulate {
|
||||
params := mfd.evmKeeper.GetParams(ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(mfd.evmKeeper.ChainID())
|
||||
baseFee := mfd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
if baseFee == nil {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
evmDenom := params.EvmDenom
|
||||
feeAmt := ethMsg.GetFee()
|
||||
glDec := sdk.NewDec(int64(ethMsg.GetGas()))
|
||||
requiredFee := ctx.MinGasPrices().AmountOf(evmDenom).Mul(glDec)
|
||||
if sdk.NewDecFromBigInt(feeAmt).LT(requiredFee) {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeAmt, requiredFee)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthEmitEventDecorator emit events in ante handler in case of tx execution failed (out of block gas limit).
|
||||
type EthEmitEventDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthEmitEventDecorator creates a new EthEmitEventDecorator
|
||||
func NewEthEmitEventDecorator(evmKeeper EVMKeeper) EthEmitEventDecorator {
|
||||
return EthEmitEventDecorator{evmKeeper}
|
||||
}
|
||||
|
||||
// AnteHandle emits some basic events for the eth messages
|
||||
func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// After eth tx passed ante handler, the fee is deducted and nonce increased, it shouldn't be ignored by json-rpc,
|
||||
// we need to emit some basic events at the very end of ante handler to be indexed by tendermint.
|
||||
txIndex := eeed.evmKeeper.GetTxIndexTransient(ctx)
|
||||
for i, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
// emit ethereum tx hash as event, should be indexed by tm tx indexer for query purpose.
|
||||
// it's emitted in ante handler so we can query failed transaction (out of block gas limit).
|
||||
ctx.EventManager().EmitEvent(sdk.NewEvent(
|
||||
evmtypes.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyEthereumTxHash, msgEthTx.Hash),
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i), 10)),
|
||||
))
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
@ -9,65 +9,13 @@ import (
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
"github.com/cerc-io/laconicd/server/config"
|
||||
"github.com/cerc-io/laconicd/tests"
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
|
||||
addr, privKey := tests.NewAddrKey()
|
||||
|
||||
signedTx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
signedTx.From = addr.Hex()
|
||||
err := signedTx.Sign(suite.ethSigner, tests.NewSigner(privKey))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
unprotectedTx := evmtypes.NewTxContract(nil, 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
unprotectedTx.From = addr.Hex()
|
||||
err = unprotectedTx.Sign(ethtypes.HomesteadSigner{}, tests.NewSigner(privKey))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tx sdk.Tx
|
||||
allowUnprotectedTxs bool
|
||||
reCheckTx bool
|
||||
expPass bool
|
||||
}{
|
||||
{"ReCheckTx", &invalidTx{}, false, true, false},
|
||||
{"invalid transaction type", &invalidTx{}, false, false, false},
|
||||
{
|
||||
"invalid sender",
|
||||
evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &addr, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"successful signature verification", signedTx, false, false, true},
|
||||
{"invalid, reject unprotected txs", unprotectedTx, false, false, false},
|
||||
{"successful, allow unprotected txs", unprotectedTx, true, false, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.evmParamsOption = func(params *evmtypes.Params) {
|
||||
params.AllowUnprotectedTxs = tc.allowUnprotectedTxs
|
||||
}
|
||||
suite.SetupTest()
|
||||
dec := ante.NewEthSigVerificationDecorator(suite.app.EvmKeeper)
|
||||
_, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, NextFn)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
suite.evmParamsOption = nil
|
||||
}
|
||||
|
||||
func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
|
||||
dec := ante.NewEthAccountVerificationDecorator(
|
||||
suite.app.AccountKeeper, suite.app.EvmKeeper,
|
||||
@ -233,6 +181,9 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
||||
tx2.From = addr.Hex()
|
||||
tx2Priority := int64(1)
|
||||
|
||||
tx3GasLimit := ethermint.BlockGasLimit(suite.ctx) + uint64(1)
|
||||
tx3 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx3GasLimit, gasPrice, nil, nil, nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}})
|
||||
|
||||
dynamicFeeTx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit,
|
||||
nil, // gasPrice
|
||||
new(big.Int).Add(baseFee, big.NewInt(evmtypes.DefaultPriorityReduction.Int64()*2)), // gasFeeCap
|
||||
@ -269,6 +220,14 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
||||
false, false,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"gas limit above block gas limit",
|
||||
tx3,
|
||||
math.MaxUint64,
|
||||
func() {},
|
||||
false, false,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"not enough balance for fees",
|
||||
tx2,
|
||||
@ -320,6 +279,17 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
||||
true, false,
|
||||
dynamicFeeTxPriority,
|
||||
},
|
||||
{
|
||||
"success - gas limit on gasMeter is set on ReCheckTx mode",
|
||||
dynamicFeeTx,
|
||||
0, // for reCheckTX mode, gas limit should be set to 0
|
||||
func() {
|
||||
vmdb.AddBalance(addr, big.NewInt(1001000000000000))
|
||||
suite.ctx = suite.ctx.WithIsReCheckTx(true)
|
||||
},
|
||||
true, false,
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -521,33 +491,3 @@ func (suite AnteTestSuite) TestEthIncrementSenderSequenceDecorator() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite AnteTestSuite) TestEthSetupContextDecorator() {
|
||||
dec := ante.NewEthSetUpContextDecorator(suite.app.EvmKeeper)
|
||||
tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tx sdk.Tx
|
||||
expPass bool
|
||||
}{
|
||||
{"invalid transaction type - does not implement GasTx", &invalidTx{}, false},
|
||||
{
|
||||
"success - transaction implement GasTx",
|
||||
tx,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
_, err := dec.AnteHandle(suite.ctx, tc.tx, false, NextFn)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
)
|
||||
|
||||
@ -64,7 +65,7 @@ func NewDynamicFeeChecker(k DynamicFeeEVMKeeper) authante.TxFeeChecker {
|
||||
baseFeeInt := sdkmath.NewIntFromBigInt(baseFee)
|
||||
|
||||
if feeCap.LT(baseFeeInt) {
|
||||
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient gas prices; got: %s required: %s", feeCap, baseFeeInt)
|
||||
return nil, 0, errorsmod.Wrapf(errortypes.ErrInsufficientFee, "insufficient gas prices; got: %s required: %s", feeCap, baseFeeInt)
|
||||
}
|
||||
|
||||
// calculate the effective gas price using the EIP-1559 logic.
|
||||
@ -112,7 +113,7 @@ func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.FeeTx) (sdk.Coi
|
||||
}
|
||||
|
||||
if !feeCoins.IsAnyGTE(requiredFees) {
|
||||
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
|
||||
return nil, 0, errorsmod.Wrapf(errortypes.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@ package ante
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// GasWantedDecorator keeps track of the gasWanted amount on the current block in transient store
|
||||
@ -27,8 +27,8 @@ func NewGasWantedDecorator(
|
||||
}
|
||||
|
||||
func (gwd GasWantedDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
params := gwd.evmKeeper.GetParams(ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(gwd.evmKeeper.ChainID())
|
||||
chainCfg := gwd.evmKeeper.GetChainConfig(ctx)
|
||||
ethCfg := chainCfg.EthereumConfig(gwd.evmKeeper.ChainID())
|
||||
|
||||
blockHeight := big.NewInt(ctx.BlockHeight())
|
||||
isLondon := ethCfg.IsLondon(blockHeight)
|
||||
@ -39,12 +39,12 @@ func (gwd GasWantedDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
|
||||
}
|
||||
|
||||
gasWanted := feeTx.GetGas()
|
||||
feeMktParams := gwd.feeMarketKeeper.GetParams(ctx)
|
||||
isBaseFeeEnabled := gwd.feeMarketKeeper.GetBaseFeeEnabled(ctx)
|
||||
|
||||
// Add total gasWanted to cumulative in block transientStore in FeeMarket module
|
||||
if feeMktParams.IsBaseFeeEnabled(ctx.BlockHeight()) {
|
||||
if isBaseFeeEnabled {
|
||||
if _, err := gwd.feeMarketKeeper.AddTransientGasWanted(ctx, gasWanted); err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "failed to add gas wanted to transient store")
|
||||
return ctx, errorsmod.Wrapf(err, "failed to add gas wanted to transient store")
|
||||
}
|
||||
}
|
||||
|
||||
|
118
app/ante/fees.go
118
app/ante/fees.go
@ -3,8 +3,9 @@ package ante
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
@ -20,14 +21,50 @@ type MinGasPriceDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// EthMinGasPriceDecorator will check if the transaction's fee is at least as large
|
||||
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
|
||||
// is rejected. This applies to both CheckTx and DeliverTx and regardless
|
||||
// if London hard fork or fee market params (EIP-1559) are enabled.
|
||||
// If fee is high enough, then call next AnteHandler
|
||||
type EthMinGasPriceDecorator struct {
|
||||
feesKeeper FeeMarketKeeper
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// EthMempoolFeeDecorator will check if the transaction's effective fee is at least as large
|
||||
// as the local validator's minimum gasFee (defined in validator config).
|
||||
// If fee is too low, decorator returns error and tx is rejected from mempool.
|
||||
// Note this only applies when ctx.CheckTx = true
|
||||
// If fee is high enough or not CheckTx, then call next AnteHandler
|
||||
// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
|
||||
type EthMempoolFeeDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for
|
||||
// Cosmos transactions.
|
||||
func NewMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) MinGasPriceDecorator {
|
||||
return MinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek}
|
||||
}
|
||||
|
||||
// NewEthMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for
|
||||
// Ethereum transactions.
|
||||
func NewEthMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) EthMinGasPriceDecorator {
|
||||
return EthMinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek}
|
||||
}
|
||||
|
||||
// NewEthMempoolFeeDecorator creates a new NewEthMempoolFeeDecorator instance used only for
|
||||
// Ethereum transactions.
|
||||
func NewEthMempoolFeeDecorator(ek EVMKeeper) EthMempoolFeeDecorator {
|
||||
return EthMempoolFeeDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
feeTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "invalid transaction type %T, expected sdk.FeeTx", tx)
|
||||
}
|
||||
|
||||
minGasPrice := mpd.feesKeeper.GetParams(ctx).MinGasPrice
|
||||
@ -37,10 +74,10 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
evmParams := mpd.evmKeeper.GetParams(ctx)
|
||||
evmDenom := mpd.evmKeeper.GetEVMDenom(ctx)
|
||||
minGasPrices := sdk.DecCoins{
|
||||
{
|
||||
Denom: evmParams.EvmDenom,
|
||||
Denom: evmDenom,
|
||||
Amount: minGasPrice,
|
||||
},
|
||||
}
|
||||
@ -62,7 +99,7 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
|
||||
}
|
||||
|
||||
if !feeCoins.IsAnyGTE(requiredFees) {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee,
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInsufficientFee,
|
||||
"provided fee < minimum global fee (%s < %s). Please increase the gas price.",
|
||||
feeCoins,
|
||||
requiredFees)
|
||||
@ -71,20 +108,8 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthMinGasPriceDecorator will check if the transaction's fee is at least as large
|
||||
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
|
||||
// is rejected. This applies to both CheckTx and DeliverTx and regardless
|
||||
// if London hard fork or fee market params (EIP-1559) are enabled.
|
||||
// If fee is high enough, then call next AnteHandler
|
||||
type EthMinGasPriceDecorator struct {
|
||||
feesKeeper FeeMarketKeeper
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
func NewEthMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) EthMinGasPriceDecorator {
|
||||
return EthMinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek}
|
||||
}
|
||||
|
||||
// AnteHandle ensures that the that the effective fee from the transaction is greater than the
|
||||
// minimum global fee, which is defined by the MinGasPrice (parameter) * GasLimit (tx argument).
|
||||
func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
minGasPrice := empd.feesKeeper.GetParams(ctx).MinGasPrice
|
||||
|
||||
@ -93,15 +118,15 @@ func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
paramsEvm := empd.evmKeeper.GetParams(ctx)
|
||||
ethCfg := paramsEvm.ChainConfig.EthereumConfig(empd.evmKeeper.ChainID())
|
||||
chainCfg := empd.evmKeeper.GetChainConfig(ctx)
|
||||
ethCfg := chainCfg.EthereumConfig(empd.evmKeeper.ChainID())
|
||||
baseFee := empd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrUnknownRequest,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrUnknownRequest,
|
||||
"invalid message type %T, expected %T",
|
||||
msg, (*evmtypes.MsgEthereumTx)(nil),
|
||||
)
|
||||
@ -120,7 +145,7 @@ func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(ethMsg.Data)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "failed to unpack tx data %s", ethMsg.Hash)
|
||||
return ctx, errorsmod.Wrapf(err, "failed to unpack tx data %s", ethMsg.Hash)
|
||||
}
|
||||
|
||||
if txData.TxType() != ethtypes.LegacyTxType {
|
||||
@ -133,8 +158,8 @@ func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
|
||||
fee := sdk.NewDecFromBigInt(feeAmt)
|
||||
|
||||
if fee.LT(requiredFee) {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInsufficientFee,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInsufficientFee,
|
||||
"provided fee < minimum global fee (%d < %d). Please increase the priority tip (for EIP-1559 txs) or the gas prices (for access list or legacy txs)", //nolint:lll
|
||||
fee.TruncateInt().Int64(), requiredFee.TruncateInt().Int64(),
|
||||
)
|
||||
@ -143,3 +168,44 @@ func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// AnteHandle ensures that the provided fees meet a minimum threshold for the validator.
|
||||
// This check only for local mempool purposes, and thus it is only run on (Re)CheckTx.
|
||||
// The logic is also skipped if the London hard fork and EIP-1559 are enabled.
|
||||
func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
if !ctx.IsCheckTx() || simulate {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
chainCfg := mfd.evmKeeper.GetChainConfig(ctx)
|
||||
ethCfg := chainCfg.EthereumConfig(mfd.evmKeeper.ChainID())
|
||||
|
||||
baseFee := mfd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
// skip check as the London hard fork and EIP-1559 are enabled
|
||||
if baseFee != nil {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
evmDenom := mfd.evmKeeper.GetEVMDenom(ctx)
|
||||
minGasPrice := ctx.MinGasPrices().AmountOf(evmDenom)
|
||||
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
fee := sdk.NewDecFromBigInt(ethMsg.GetFee())
|
||||
gasLimit := sdk.NewDecFromBigInt(new(big.Int).SetUint64(ethMsg.GetGas()))
|
||||
requiredFee := minGasPrice.Mul(gasLimit)
|
||||
|
||||
if fee.LT(requiredFee) {
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInsufficientFee,
|
||||
"insufficient fee; got: %s required: %s",
|
||||
fee, requiredFee,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func (s AnteTestSuite) TestMinGasPriceDecorator() {
|
||||
return &invalidTx{}
|
||||
},
|
||||
false,
|
||||
"must be a FeeTx",
|
||||
"invalid transaction type",
|
||||
false,
|
||||
},
|
||||
{
|
||||
@ -345,3 +345,7 @@ func (s AnteTestSuite) TestEthMinGasPriceDecorator() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite AnteTestSuite) TestEthMempoolFeeDecorator() {
|
||||
// TODO: add test
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
@ -20,7 +21,7 @@ type HandlerOptions struct {
|
||||
AccountKeeper evmtypes.AccountKeeper
|
||||
BankKeeper evmtypes.BankKeeper
|
||||
IBCKeeper *ibckeeper.Keeper
|
||||
FeeMarketKeeper evmtypes.FeeMarketKeeper
|
||||
FeeMarketKeeper FeeMarketKeeper
|
||||
EvmKeeper EVMKeeper
|
||||
FeegrantKeeper ante.FeegrantKeeper
|
||||
SignModeHandler authsigning.SignModeHandler
|
||||
@ -31,20 +32,20 @@ type HandlerOptions struct {
|
||||
}
|
||||
|
||||
func (options HandlerOptions) validate() error {
|
||||
if options.AccountKeeper == evmtypes.AccountKeeper(nil) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler")
|
||||
if options.AccountKeeper == nil {
|
||||
return errorsmod.Wrap(errortypes.ErrLogic, "account keeper is required for AnteHandler")
|
||||
}
|
||||
if options.BankKeeper == evmtypes.BankKeeper(nil) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler")
|
||||
if options.BankKeeper == nil {
|
||||
return errorsmod.Wrap(errortypes.ErrLogic, "bank keeper is required for AnteHandler")
|
||||
}
|
||||
if options.SignModeHandler == authsigning.SignModeHandler(nil) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
|
||||
if options.SignModeHandler == nil {
|
||||
return errorsmod.Wrap(errortypes.ErrLogic, "sign mode handler is required for ante builder")
|
||||
}
|
||||
if options.FeeMarketKeeper == evmtypes.FeeMarketKeeper(nil) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "fee market keeper is required for AnteHandler")
|
||||
if options.FeeMarketKeeper == nil {
|
||||
return errorsmod.Wrap(errortypes.ErrLogic, "fee market keeper is required for AnteHandler")
|
||||
}
|
||||
if options.EvmKeeper == EVMKeeper(nil) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "evm keeper is required for AnteHandler")
|
||||
if options.EvmKeeper == nil {
|
||||
return errorsmod.Wrap(errortypes.ErrLogic, "evm keeper is required for AnteHandler")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -70,9 +71,9 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
||||
RejectMessagesDecorator{}, // reject MsgEthereumTxs
|
||||
ante.NewSetUpContextDecorator(),
|
||||
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
|
||||
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
|
||||
ante.NewValidateBasicDecorator(),
|
||||
ante.NewTxTimeoutHeightDecorator(),
|
||||
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
|
||||
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
@ -86,27 +87,3 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
||||
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
||||
)
|
||||
}
|
||||
|
||||
func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
|
||||
return sdk.ChainAnteDecorators(
|
||||
RejectMessagesDecorator{}, // reject MsgEthereumTxs
|
||||
ante.NewSetUpContextDecorator(),
|
||||
// NOTE: extensions option decorator removed
|
||||
// ante.NewRejectExtensionOptionsDecorator(),
|
||||
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
|
||||
ante.NewValidateBasicDecorator(),
|
||||
ante.NewTxTimeoutHeightDecorator(),
|
||||
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
// SetPubKeyDecorator must be called before all signature verification decorators
|
||||
ante.NewSetPubKeyDecorator(options.AccountKeeper),
|
||||
ante.NewValidateSigCountDecorator(options.AccountKeeper),
|
||||
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
||||
// Note: signature verification uses EIP instead of the cosmos signature validator
|
||||
NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
|
||||
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
||||
)
|
||||
}
|
||||
|
@ -28,12 +28,15 @@ type EVMKeeper interface {
|
||||
DynamicFeeEVMKeeper
|
||||
|
||||
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) evm.EVM
|
||||
DeductTxCostsFromUserBalance(
|
||||
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
|
||||
) (fees sdk.Coins, priority int64, err error)
|
||||
DeductTxCostsFromUserBalance(ctx sdk.Context, fees sdk.Coins, from common.Address) error
|
||||
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
|
||||
ResetTransientGasUsed(ctx sdk.Context)
|
||||
GetTxIndexTransient(ctx sdk.Context) uint64
|
||||
GetChainConfig(ctx sdk.Context) evmtypes.ChainConfig
|
||||
GetEVMDenom(ctx sdk.Context) string
|
||||
GetEnableCreate(ctx sdk.Context) bool
|
||||
GetEnableCall(ctx sdk.Context) bool
|
||||
GetAllowUnprotectedTxs(ctx sdk.Context) bool
|
||||
}
|
||||
|
||||
type protoTxProvider interface {
|
||||
@ -44,4 +47,5 @@ type protoTxProvider interface {
|
||||
type FeeMarketKeeper interface {
|
||||
GetParams(ctx sdk.Context) (params feemarkettypes.Params)
|
||||
AddTransientGasWanted(ctx sdk.Context, gasWanted uint64) (uint64, error)
|
||||
GetBaseFeeEnabled(ctx sdk.Context) bool
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// RejectMessagesDecorator prevents invalid msg types from being executed
|
||||
@ -15,8 +16,8 @@ type RejectMessagesDecorator struct{}
|
||||
func (rmd RejectMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
if _, ok := msg.(*evmtypes.MsgEthereumTx); ok {
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInvalidType,
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrInvalidType,
|
||||
"MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option",
|
||||
)
|
||||
}
|
||||
|
189
app/ante/setup.go
Normal file
189
app/ante/setup.go
Normal file
@ -0,0 +1,189 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// EthSetupContextDecorator is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption
|
||||
// by setting the gas meter to infinite
|
||||
type EthSetupContextDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
func NewEthSetUpContextDecorator(evmKeeper EVMKeeper) EthSetupContextDecorator {
|
||||
return EthSetupContextDecorator{
|
||||
evmKeeper: evmKeeper,
|
||||
}
|
||||
}
|
||||
|
||||
func (esc EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// all transactions must implement GasTx
|
||||
_, ok := tx.(authante.GasTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "invalid transaction type %T, expected GasTx", tx)
|
||||
}
|
||||
|
||||
// We need to setup an empty gas config so that the gas is consistent with Ethereum.
|
||||
newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()).
|
||||
WithKVGasConfig(storetypes.GasConfig{}).
|
||||
WithTransientKVGasConfig(storetypes.GasConfig{})
|
||||
|
||||
// Reset transient gas used to prepare the execution of current cosmos tx.
|
||||
// Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs.
|
||||
esc.evmKeeper.ResetTransientGasUsed(ctx)
|
||||
return next(newCtx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthEmitEventDecorator emit events in ante handler in case of tx execution failed (out of block gas limit).
|
||||
type EthEmitEventDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthEmitEventDecorator creates a new EthEmitEventDecorator
|
||||
func NewEthEmitEventDecorator(evmKeeper EVMKeeper) EthEmitEventDecorator {
|
||||
return EthEmitEventDecorator{evmKeeper}
|
||||
}
|
||||
|
||||
// AnteHandle emits some basic events for the eth messages
|
||||
func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// After eth tx passed ante handler, the fee is deducted and nonce increased, it shouldn't be ignored by json-rpc,
|
||||
// we need to emit some basic events at the very end of ante handler to be indexed by tendermint.
|
||||
txIndex := eeed.evmKeeper.GetTxIndexTransient(ctx)
|
||||
for i, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
// emit ethereum tx hash as an event so that it can be indexed by Tendermint for query purposes
|
||||
// it's emitted in ante handler, so we can query failed transaction (out of block gas limit).
|
||||
ctx.EventManager().EmitEvent(sdk.NewEvent(
|
||||
evmtypes.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyEthereumTxHash, msgEthTx.Hash),
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i), 10)),
|
||||
))
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthValidateBasicDecorator is adapted from ValidateBasicDecorator from cosmos-sdk, it ignores ErrNoSignatures
|
||||
type EthValidateBasicDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthValidateBasicDecorator creates a new EthValidateBasicDecorator
|
||||
func NewEthValidateBasicDecorator(ek EVMKeeper) EthValidateBasicDecorator {
|
||||
return EthValidateBasicDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle handles basic validation of tx
|
||||
func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
// no need to validate basic on recheck tx, call next antehandler
|
||||
if ctx.IsReCheckTx() {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
err := tx.ValidateBasic()
|
||||
// ErrNoSignatures is fine with eth tx
|
||||
if err != nil && !errors.Is(err, errortypes.ErrNoSignatures) {
|
||||
return ctx, errorsmod.Wrap(err, "tx basic validation failed")
|
||||
}
|
||||
|
||||
// For eth type cosmos tx, some fields should be verified as zero values,
|
||||
// since we will only verify the signature against the hash of the MsgEthereumTx.Data
|
||||
wrapperTx, ok := tx.(protoTxProvider)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid tx type %T, didn't implement interface protoTxProvider", tx)
|
||||
}
|
||||
|
||||
protoTx := wrapperTx.GetProtoTx()
|
||||
body := protoTx.Body
|
||||
if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 {
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidRequest,
|
||||
"for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty")
|
||||
}
|
||||
|
||||
if len(body.ExtensionOptions) != 1 {
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidRequest, "for eth tx length of ExtensionOptions should be 1")
|
||||
}
|
||||
|
||||
authInfo := protoTx.AuthInfo
|
||||
if len(authInfo.SignerInfos) > 0 {
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidRequest, "for eth tx AuthInfo SignerInfos should be empty")
|
||||
}
|
||||
|
||||
if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" {
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidRequest, "for eth tx AuthInfo Fee payer and granter should be empty")
|
||||
}
|
||||
|
||||
sigs := protoTx.Signatures
|
||||
if len(sigs) > 0 {
|
||||
return ctx, errorsmod.Wrap(errortypes.ErrInvalidRequest, "for eth tx Signatures should be empty")
|
||||
}
|
||||
|
||||
txFee := sdk.Coins{}
|
||||
txGasLimit := uint64(0)
|
||||
|
||||
chainCfg := vbd.evmKeeper.GetChainConfig(ctx)
|
||||
chainID := vbd.evmKeeper.ChainID()
|
||||
ethCfg := chainCfg.EthereumConfig(chainID)
|
||||
baseFee := vbd.evmKeeper.GetBaseFee(ctx, ethCfg)
|
||||
enableCreate := vbd.evmKeeper.GetEnableCreate(ctx)
|
||||
enableCall := vbd.evmKeeper.GetEnableCall(ctx)
|
||||
evmDenom := vbd.evmKeeper.GetEVMDenom(ctx)
|
||||
|
||||
for _, msg := range protoTx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
// Validate `From` field
|
||||
if msgEthTx.From != "" {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidRequest, "invalid From %s, expect empty string", msgEthTx.From)
|
||||
}
|
||||
|
||||
txGasLimit += msgEthTx.GetGas()
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
|
||||
if err != nil {
|
||||
return ctx, errorsmod.Wrap(err, "failed to unpack MsgEthereumTx Data")
|
||||
}
|
||||
|
||||
// return error if contract creation or call are disabled through governance
|
||||
if !enableCreate && txData.GetTo() == nil {
|
||||
return ctx, errorsmod.Wrap(evmtypes.ErrCreateDisabled, "failed to create new contract")
|
||||
} else if !enableCall && txData.GetTo() != nil {
|
||||
return ctx, errorsmod.Wrap(evmtypes.ErrCallDisabled, "failed to call contract")
|
||||
}
|
||||
|
||||
if baseFee == nil && txData.TxType() == ethtypes.DynamicFeeTxType {
|
||||
return ctx, errorsmod.Wrap(ethtypes.ErrTxTypeNotSupported, "dynamic fee tx not supported")
|
||||
}
|
||||
|
||||
txFee = txFee.Add(sdk.Coin{Denom: evmDenom, Amount: sdkmath.NewIntFromBigInt(txData.Fee())})
|
||||
}
|
||||
|
||||
if !authInfo.Fee.Amount.IsEqual(txFee) {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidRequest, "invalid AuthInfo Fee Amount (%s != %s)", authInfo.Fee.Amount, txFee)
|
||||
}
|
||||
|
||||
if authInfo.Fee.GasLimit != txGasLimit {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidRequest, "invalid AuthInfo Fee GasLimit (%d != %d)", authInfo.Fee.GasLimit, txGasLimit)
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
42
app/ante/setup_test.go
Normal file
42
app/ante/setup_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func (suite AnteTestSuite) TestEthSetupContextDecorator() {
|
||||
dec := ante.NewEthSetUpContextDecorator(suite.app.EvmKeeper)
|
||||
tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tx sdk.Tx
|
||||
expPass bool
|
||||
}{
|
||||
{"invalid transaction type - does not implement GasTx", &invalidTx{}, false},
|
||||
{
|
||||
"success - transaction implement GasTx",
|
||||
tx,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
ctx, err := dec.AnteHandle(suite.ctx, tc.tx, false, NextFn)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Equal(storetypes.GasConfig{}, ctx.KVGasConfig())
|
||||
suite.Equal(storetypes.GasConfig{}, ctx.TransientKVGasConfig())
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
64
app/ante/signverify_test.go
Normal file
64
app/ante/signverify_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
"github.com/cerc-io/laconicd/tests"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
|
||||
addr, privKey := tests.NewAddrKey()
|
||||
|
||||
signedTx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
signedTx.From = addr.Hex()
|
||||
err := signedTx.Sign(suite.ethSigner, tests.NewSigner(privKey))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
unprotectedTx := evmtypes.NewTxContract(nil, 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
|
||||
unprotectedTx.From = addr.Hex()
|
||||
err = unprotectedTx.Sign(ethtypes.HomesteadSigner{}, tests.NewSigner(privKey))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tx sdk.Tx
|
||||
allowUnprotectedTxs bool
|
||||
reCheckTx bool
|
||||
expPass bool
|
||||
}{
|
||||
{"ReCheckTx", &invalidTx{}, false, true, false},
|
||||
{"invalid transaction type", &invalidTx{}, false, false, false},
|
||||
{
|
||||
"invalid sender",
|
||||
evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &addr, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"successful signature verification", signedTx, false, false, true},
|
||||
{"invalid, reject unprotected txs", unprotectedTx, false, false, false},
|
||||
{"successful, allow unprotected txs", unprotectedTx, true, false, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.evmParamsOption = func(params *evmtypes.Params) {
|
||||
params.AllowUnprotectedTxs = tc.allowUnprotectedTxs
|
||||
}
|
||||
suite.SetupTest()
|
||||
dec := ante.NewEthSigVerificationDecorator(suite.app.EvmKeeper)
|
||||
_, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, NextFn)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
suite.evmParamsOption = nil
|
||||
}
|
65
app/ante/sigverify.go
Normal file
65
app/ante/sigverify.go
Normal file
@ -0,0 +1,65 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// EthSigVerificationDecorator validates an ethereum signatures
|
||||
type EthSigVerificationDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthSigVerificationDecorator creates a new EthSigVerificationDecorator
|
||||
func NewEthSigVerificationDecorator(ek EVMKeeper) EthSigVerificationDecorator {
|
||||
return EthSigVerificationDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle validates checks that the registered chain id is the same as the one on the message, and
|
||||
// that the signer address matches the one defined on the message.
|
||||
// It's not skipped for RecheckTx, because it set `From` address which is critical from other ante handler to work.
|
||||
// Failure in RecheckTx will prevent tx to be included into block, especially when CheckTx succeed, in which case user
|
||||
// won't see the error message.
|
||||
func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
chainID := esvd.evmKeeper.ChainID()
|
||||
chainCfg := esvd.evmKeeper.GetChainConfig(ctx)
|
||||
ethCfg := chainCfg.EthereumConfig(chainID)
|
||||
blockNum := big.NewInt(ctx.BlockHeight())
|
||||
signer := ethtypes.MakeSigner(ethCfg, blockNum)
|
||||
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
allowUnprotectedTxs := esvd.evmKeeper.GetAllowUnprotectedTxs(ctx)
|
||||
ethTx := msgEthTx.AsTransaction()
|
||||
if !allowUnprotectedTxs && !ethTx.Protected() {
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrNotSupported,
|
||||
"rejected unprotected Ethereum transaction. Please EIP155 sign your transaction to protect it against replay-attacks")
|
||||
}
|
||||
|
||||
sender, err := signer.Sender(ethTx)
|
||||
if err != nil {
|
||||
return ctx, errorsmod.Wrapf(
|
||||
errortypes.ErrorInvalidSigner,
|
||||
"couldn't retrieve sender address from the ethereum transaction: %s",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
// set up the sender to the transaction field if not already
|
||||
msgEthTx.From = sender.Hex()
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
@ -15,26 +15,32 @@ import (
|
||||
"github.com/cerc-io/laconicd/types"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||
types2 "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
types3 "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
cryptocodec "github.com/cerc-io/laconicd/crypto/codec"
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
sdkante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
authz "github.com/cosmos/cosmos-sdk/x/authz"
|
||||
|
||||
"github.com/cerc-io/laconicd/app"
|
||||
ante "github.com/cerc-io/laconicd/app/ante"
|
||||
@ -46,7 +52,8 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
evtypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant"
|
||||
types5 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
@ -92,6 +99,8 @@ func (suite *AnteTestSuite) SetupTest() {
|
||||
evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.GrayGlacierBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.MergeNetsplitBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.ShanghaiBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.CancunBlock = &maxInt
|
||||
}
|
||||
if suite.evmParamsOption != nil {
|
||||
suite.evmParamsOption(&evmGenesis.Params)
|
||||
@ -111,6 +120,7 @@ func (suite *AnteTestSuite) SetupTest() {
|
||||
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
|
||||
// We're using TestMsg amino encoding in some tests, so register it here.
|
||||
encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
|
||||
eip712.SetEncodingConfig(encodingConfig)
|
||||
|
||||
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
|
||||
|
||||
@ -263,41 +273,57 @@ func (suite *AnteTestSuite) CreateTestCosmosTxBuilder(gasPrice sdkmath.Int, deno
|
||||
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgSend
|
||||
recipient := sdk.AccAddress(common.Address{}.Bytes())
|
||||
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgSend
|
||||
valEthAddr := tests.GenerateAddress()
|
||||
valAddr := sdk.ValAddress(valEthAddr.Bytes())
|
||||
msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
msgSend := stakingtypes.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgCreateValidator
|
||||
valAddr := sdk.ValAddress(from.Bytes())
|
||||
privEd := ed25519.GenPrivKey()
|
||||
msgCreate, err := types3.NewMsgCreateValidator(
|
||||
msgCreate, err := stakingtypes.NewMsgCreateValidator(
|
||||
valAddr,
|
||||
privEd.PubKey(),
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)),
|
||||
// TODO: can this values be empty strings?
|
||||
types3.NewDescription("moniker", "indentity", "website", "security_contract", "details"),
|
||||
types3.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()),
|
||||
stakingtypes.NewDescription("moniker", "indentity", "website", "security_contract", "details"),
|
||||
stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()),
|
||||
sdk.OneInt(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgCreateValidator
|
||||
valAddr := sdk.ValAddress(from.Bytes())
|
||||
privEd := ed25519.GenPrivKey()
|
||||
msgCreate, err := stakingtypes.NewMsgCreateValidator(
|
||||
valAddr,
|
||||
privEd.PubKey(),
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)),
|
||||
// Ensure optional fields can be left blank
|
||||
stakingtypes.NewDescription("moniker", "indentity", "", "", ""),
|
||||
stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()),
|
||||
sdk.OneInt(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, deposit sdk.Coins) client.TxBuilder {
|
||||
proposal, ok := types5.ContentFromProposalType("My proposal", "My description", types5.ProposalTypeText)
|
||||
proposal, ok := govtypes.ContentFromProposalType("My proposal", "My description", govtypes.ProposalTypeText)
|
||||
suite.Require().True(ok)
|
||||
msgSubmit, err := types5.NewMsgSubmitProposal(proposal, deposit, from)
|
||||
msgSubmit, err := govtypes.NewMsgSubmitProposal(proposal, deposit, from)
|
||||
suite.Require().NoError(err)
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
@ -311,18 +337,18 @@ func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress,
|
||||
grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes())
|
||||
msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
valAddr := sdk.ValAddress(from.Bytes())
|
||||
msgEdit := types3.NewMsgEditValidator(
|
||||
msgEdit := stakingtypes.NewMsgEditValidator(
|
||||
valAddr,
|
||||
types3.NewDescription("moniker", "identity", "website", "security_contract", "details"),
|
||||
stakingtypes.NewDescription("moniker", "identity", "website", "security_contract", "details"),
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
@ -335,7 +361,72 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddres
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence)
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgVoteV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
msgVote := govtypesv1.NewMsgVote(from, 1, govtypesv1.VoteOption_VOTE_OPTION_YES, "")
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgVote)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build V1 proposal messages. Must all be same-type, since EIP-712
|
||||
// does not support arrays of variable type.
|
||||
authAcc := suite.app.GovKeeper.GetGovernanceAccount(suite.ctx)
|
||||
|
||||
proposal1, ok := govtypes.ContentFromProposalType("My proposal 1", "My description 1", govtypes.ProposalTypeText)
|
||||
suite.Require().True(ok)
|
||||
content1, err := govtypesv1.NewLegacyContent(
|
||||
proposal1,
|
||||
sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), authAcc.GetAddress().Bytes()),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
proposal2, ok := govtypes.ContentFromProposalType("My proposal 2", "My description 2", govtypes.ProposalTypeText)
|
||||
suite.Require().True(ok)
|
||||
content2, err := govtypesv1.NewLegacyContent(
|
||||
proposal2,
|
||||
sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), authAcc.GetAddress().Bytes()),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
proposalMsgs := []sdk.Msg{
|
||||
content1,
|
||||
content2,
|
||||
}
|
||||
|
||||
// Build V1 proposal
|
||||
msgProposal, err := govtypesv1.NewMsgSubmitProposal(
|
||||
proposalMsgs,
|
||||
sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100))),
|
||||
sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), from.Bytes()),
|
||||
"Metadata",
|
||||
)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgProposal)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MsgExec(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
recipient := sdk.AccAddress(common.Address{}.Bytes())
|
||||
msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
msgExec := authz.NewMsgExec(from, []sdk.Msg{msgSend})
|
||||
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, &msgExec)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MultipleMsgSend(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
recipient := sdk.AccAddress(common.Address{}.Bytes())
|
||||
msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msgSend, msgSend, msgSend})
|
||||
}
|
||||
|
||||
// Fails
|
||||
func (suite *AnteTestSuite) CreateTestEIP712MultipleSignerMsgs(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
recipient := sdk.AccAddress(common.Address{}.Bytes())
|
||||
msgSend1 := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
msgSend2 := banktypes.NewMsgSend(recipient, from, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msgSend1, msgSend2})
|
||||
}
|
||||
|
||||
// StdSignBytes returns the bytes to sign for a transaction.
|
||||
@ -376,8 +467,14 @@ func StdSignBytes(cdc *codec.LegacyAmino, chainID string, accnum uint64, sequenc
|
||||
return sdk.MustSortJSON(bz)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
|
||||
func (suite *AnteTestSuite) CreateTestEIP712SingleMessageTxBuilder(
|
||||
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg,
|
||||
) client.TxBuilder {
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msg})
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
|
||||
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msgs []sdk.Msg,
|
||||
) client.TxBuilder {
|
||||
var err error
|
||||
|
||||
@ -398,13 +495,13 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
|
||||
fee := legacytx.NewStdFee(gas, gasAmount)
|
||||
accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()
|
||||
|
||||
data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, []sdk.Msg{msg}, "", nil)
|
||||
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msg, data, &eip712.FeeDelegationOptions{
|
||||
data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, msgs, "", nil)
|
||||
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msgs[0], data, &eip712.FeeDelegationOptions{
|
||||
FeePayer: from,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sigHash, err := eip712.ComputeTypedDataHash(typedData)
|
||||
sigHash, _, err := apitypes.TypedDataAndHash(typedData)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Sign typedData
|
||||
@ -442,12 +539,189 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
|
||||
err = builder.SetSignatures(sigsV2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = builder.SetMsgs(msg)
|
||||
err = builder.SetMsgs(msgs...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
// Generate a set of pub/priv keys to be used in creating multi-keys
|
||||
func (suite *AnteTestSuite) GenerateMultipleKeys(n int) ([]cryptotypes.PrivKey, []cryptotypes.PubKey) {
|
||||
privKeys := make([]cryptotypes.PrivKey, n)
|
||||
pubKeys := make([]cryptotypes.PubKey, n)
|
||||
for i := 0; i < n; i++ {
|
||||
privKey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
privKeys[i] = privKey
|
||||
pubKeys[i] = privKey.PubKey()
|
||||
}
|
||||
return privKeys, pubKeys
|
||||
}
|
||||
|
||||
// generateSingleSignature signs the given sign doc bytes using the given signType (EIP-712 or Standard)
|
||||
func (suite *AnteTestSuite) generateSingleSignature(signMode signing.SignMode, privKey cryptotypes.PrivKey, signDocBytes []byte, signType string) (signature signing.SignatureV2) {
|
||||
var (
|
||||
msg []byte
|
||||
err error
|
||||
)
|
||||
|
||||
msg = signDocBytes
|
||||
|
||||
if signType == "EIP-712" {
|
||||
msg, err = eip712.GetEIP712BytesForMsg(signDocBytes)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
sigBytes, _ := privKey.Sign(msg)
|
||||
sigData := &signing.SingleSignatureData{
|
||||
SignMode: signMode,
|
||||
Signature: sigBytes,
|
||||
}
|
||||
|
||||
return signing.SignatureV2{
|
||||
PubKey: privKey.PubKey(),
|
||||
Data: sigData,
|
||||
}
|
||||
}
|
||||
|
||||
// generateMultikeySignatures signs a set of messages using each private key within a given multi-key
|
||||
func (suite *AnteTestSuite) generateMultikeySignatures(signMode signing.SignMode, privKeys []cryptotypes.PrivKey, signDocBytes []byte, signType string) (signatures []signing.SignatureV2) {
|
||||
n := len(privKeys)
|
||||
signatures = make([]signing.SignatureV2, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
privKey := privKeys[i]
|
||||
currentType := signType
|
||||
|
||||
// If mixed type, alternate signing type on each iteration
|
||||
if signType == "mixed" {
|
||||
if i%2 == 0 {
|
||||
currentType = "EIP-712"
|
||||
} else {
|
||||
currentType = "Standard"
|
||||
}
|
||||
}
|
||||
|
||||
signatures[i] = suite.generateSingleSignature(
|
||||
signMode,
|
||||
privKey,
|
||||
signDocBytes,
|
||||
currentType,
|
||||
)
|
||||
}
|
||||
|
||||
return signatures
|
||||
}
|
||||
|
||||
// RegisterAccount creates an account with the keeper and populates the initial balance
|
||||
func (suite *AnteTestSuite) RegisterAccount(pubKey cryptotypes.PubKey, balance *big.Int) {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, sdk.AccAddress(pubKey.Address()))
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, common.BytesToAddress(pubKey.Address()), balance)
|
||||
}
|
||||
|
||||
// createSignerBytes generates sign doc bytes using the given parameters
|
||||
func (suite *AnteTestSuite) createSignerBytes(chainId string, signMode signing.SignMode, pubKey cryptotypes.PubKey, txBuilder client.TxBuilder) []byte {
|
||||
acc, err := sdkante.GetSignerAcc(suite.ctx, suite.app.AccountKeeper, sdk.AccAddress(pubKey.Address()))
|
||||
suite.Require().NoError(err)
|
||||
signerInfo := authsigning.SignerData{
|
||||
Address: sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), acc.GetAddress().Bytes()),
|
||||
ChainID: chainId,
|
||||
AccountNumber: acc.GetAccountNumber(),
|
||||
Sequence: acc.GetSequence(),
|
||||
PubKey: pubKey,
|
||||
}
|
||||
|
||||
signerBytes, err := suite.clientCtx.TxConfig.SignModeHandler().GetSignBytes(
|
||||
signMode,
|
||||
signerInfo,
|
||||
txBuilder.GetTx(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return signerBytes
|
||||
}
|
||||
|
||||
// createBaseTxBuilder creates a TxBuilder to be used for Single- or Multi-signing
|
||||
func (suite *AnteTestSuite) createBaseTxBuilder(msg sdk.Msg, gas uint64) client.TxBuilder {
|
||||
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
|
||||
|
||||
txBuilder.SetGasLimit(gas)
|
||||
txBuilder.SetFeeAmount(sdk.NewCoins(
|
||||
sdk.NewCoin("aphoton", sdk.NewInt(10000)),
|
||||
))
|
||||
|
||||
err := txBuilder.SetMsgs(msg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
txBuilder.SetMemo("")
|
||||
|
||||
return txBuilder
|
||||
}
|
||||
|
||||
// CreateTestSignedMultisigTx creates and sign a multi-signed tx for the given message. `signType` indicates whether to use standard signing ("Standard"),
|
||||
// EIP-712 signing ("EIP-712"), or a mix of the two ("mixed").
|
||||
func (suite *AnteTestSuite) CreateTestSignedMultisigTx(privKeys []cryptotypes.PrivKey, signMode signing.SignMode, msg sdk.Msg, chainId string, gas uint64, signType string) client.TxBuilder {
|
||||
pubKeys := make([]cryptotypes.PubKey, len(privKeys))
|
||||
for i, privKey := range privKeys {
|
||||
pubKeys[i] = privKey.PubKey()
|
||||
}
|
||||
|
||||
// Re-derive multikey
|
||||
numKeys := len(privKeys)
|
||||
multiKey := kmultisig.NewLegacyAminoPubKey(numKeys, pubKeys)
|
||||
|
||||
suite.RegisterAccount(multiKey, big.NewInt(10000000000))
|
||||
|
||||
txBuilder := suite.createBaseTxBuilder(msg, gas)
|
||||
|
||||
// Prepare signature field
|
||||
sig := multisig.NewMultisig(len(pubKeys))
|
||||
txBuilder.SetSignatures(signing.SignatureV2{
|
||||
PubKey: multiKey,
|
||||
Data: sig,
|
||||
})
|
||||
|
||||
signerBytes := suite.createSignerBytes(chainId, signMode, multiKey, txBuilder)
|
||||
|
||||
// Sign for each key and update signature field
|
||||
sigs := suite.generateMultikeySignatures(signMode, privKeys, signerBytes, signType)
|
||||
for _, pkSig := range sigs {
|
||||
err := multisig.AddSignatureV2(sig, pkSig, pubKeys)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
txBuilder.SetSignatures(signing.SignatureV2{
|
||||
PubKey: multiKey,
|
||||
Data: sig,
|
||||
})
|
||||
|
||||
return txBuilder
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestSingleSignedTx(privKey cryptotypes.PrivKey, signMode signing.SignMode, msg sdk.Msg, chainId string, gas uint64, signType string) client.TxBuilder {
|
||||
pubKey := privKey.PubKey()
|
||||
|
||||
suite.RegisterAccount(pubKey, big.NewInt(10000000000))
|
||||
|
||||
txBuilder := suite.createBaseTxBuilder(msg, gas)
|
||||
|
||||
// Prepare signature field
|
||||
sig := signing.SingleSignatureData{}
|
||||
txBuilder.SetSignatures(signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &sig,
|
||||
})
|
||||
|
||||
signerBytes := suite.createSignerBytes(chainId, signMode, pubKey, txBuilder)
|
||||
|
||||
sigData := suite.generateSingleSignature(signMode, privKey, signerBytes, signType)
|
||||
txBuilder.SetSignatures(sigData)
|
||||
|
||||
return txBuilder
|
||||
}
|
||||
|
||||
func NextFn(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
45
app/app.go
45
app/app.go
@ -2,6 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/node"
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
@ -26,6 +28,7 @@ import (
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
"github.com/cosmos/cosmos-sdk/store/streaming"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
@ -101,6 +104,7 @@ import (
|
||||
_ "github.com/cerc-io/laconicd/client/docs/statik"
|
||||
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
"github.com/cerc-io/laconicd/ethereum/eip712"
|
||||
srvflags "github.com/cerc-io/laconicd/server/flags"
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm"
|
||||
@ -184,7 +188,7 @@ var (
|
||||
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
|
||||
govtypes.ModuleName: {authtypes.Burner},
|
||||
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
|
||||
evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, // used for secure addition and subtraction of balance using module account
|
||||
evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, //nolint:lll // used for secure addition and subtraction of balance using module account
|
||||
auctiontypes.ModuleName: nil,
|
||||
auctiontypes.AuctionBurnModuleAccountName: nil,
|
||||
registrytypes.ModuleName: nil,
|
||||
@ -199,8 +203,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var _ simapp.App = (*EthermintApp)(nil)
|
||||
|
||||
// var _ server.Application (*EthermintApp)(nil)
|
||||
|
||||
// EthermintApp implements an extended ABCI application. It is an application
|
||||
@ -256,9 +258,6 @@ type EthermintApp struct {
|
||||
// the module manager
|
||||
mm *module.Manager
|
||||
|
||||
// simulation manager
|
||||
sm *module.SimulationManager
|
||||
|
||||
// the configurator
|
||||
configurator module.Configurator
|
||||
}
|
||||
@ -280,6 +279,8 @@ func NewEthermintApp(
|
||||
cdc := encodingConfig.Amino
|
||||
interfaceRegistry := encodingConfig.InterfaceRegistry
|
||||
|
||||
eip712.SetEncodingConfig(encodingConfig)
|
||||
|
||||
// NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx
|
||||
bApp := baseapp.NewBaseApp(
|
||||
appName,
|
||||
@ -313,6 +314,12 @@ func NewEthermintApp(
|
||||
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey)
|
||||
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
|
||||
|
||||
// load state streaming if enabled
|
||||
if _, _, err := streaming.LoadStreamingServices(bApp, appOpts, appCodec, keys); err != nil {
|
||||
fmt.Printf("failed to load state streaming: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
app := &EthermintApp{
|
||||
BaseApp: bApp,
|
||||
cdc: cdc,
|
||||
@ -658,17 +665,6 @@ func NewEthermintApp(
|
||||
// add test gRPC service for testing gRPC queries in isolation
|
||||
// testdata.RegisterTestServiceServer(app.GRPCQueryRouter(), testdata.TestServiceImpl{})
|
||||
|
||||
// create the simulation manager and define the order of the modules for deterministic simulations
|
||||
//
|
||||
// NOTE: this is not required apps that don't use the simulator for fuzz testing
|
||||
// transactions
|
||||
overrideModules := map[string]module.AppModuleSimulation{
|
||||
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
|
||||
}
|
||||
app.sm = module.NewSimulationManagerFromAppModules(app.mm.Modules, overrideModules)
|
||||
|
||||
app.sm.RegisterStoreDecoders()
|
||||
|
||||
// initialize stores
|
||||
app.MountKVStores(keys)
|
||||
app.MountTransientStores(tkeys)
|
||||
@ -718,6 +714,8 @@ func (app *EthermintApp) setAnteHandler(txConfig client.TxConfig, maxGasWanted u
|
||||
EvmKeeper: app.EvmKeeper,
|
||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
||||
MaxTxGasWanted: maxGasWanted,
|
||||
ExtensionOptionChecker: ethermint.HasDynamicFeeExtensionOption,
|
||||
// TxFeeChecker: ante.NewDynamicFeeChecker(app.EvmKeeper),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -838,11 +836,6 @@ func (app *EthermintApp) GetSubspace(moduleName string) paramstypes.Subspace {
|
||||
return subspace
|
||||
}
|
||||
|
||||
// SimulationManager implements the SimulationApp interface
|
||||
func (app *EthermintApp) SimulationManager() *module.SimulationManager {
|
||||
return app.sm
|
||||
}
|
||||
|
||||
// RegisterAPIRoutes registers all application module routes with the provided
|
||||
// API server.
|
||||
func (app *EthermintApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
|
||||
@ -851,6 +844,8 @@ func (app *EthermintApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.
|
||||
authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
|
||||
// Register new tendermint queries routes from grpc-gateway.
|
||||
tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
|
||||
// Register node gRPC service for grpc-gateway.
|
||||
node.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
|
||||
|
||||
// Register grpc-gateway routes for all modules.
|
||||
ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
|
||||
@ -876,6 +871,12 @@ func (app *EthermintApp) RegisterTendermintService(clientCtx client.Context) {
|
||||
)
|
||||
}
|
||||
|
||||
// RegisterNodeService registers the node gRPC service on the provided
|
||||
// application gRPC query router.
|
||||
func (app *EthermintApp) RegisterNodeService(clientCtx client.Context) {
|
||||
node.RegisterNodeService(clientCtx, app.GRPCQueryRouter())
|
||||
}
|
||||
|
||||
// RegisterSwaggerAPI registers swagger route with API Server
|
||||
func RegisterSwaggerAPI(_ client.Context, rtr *mux.Router) {
|
||||
statikFS, err := fs.New()
|
||||
|
@ -1,414 +0,0 @@
|
||||
package app
|
||||
|
||||
// TODO: COsmos SDK fix for the simulator issue for custom keys
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cerc-io/laconicd/app/ante"
|
||||
evmenc "github.com/cerc-io/laconicd/encoding"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
||||
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"
|
||||
ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
// MakeEncodingConfig creates the EncodingConfig
|
||||
func MakeEncodingConfig() params.EncodingConfig {
|
||||
return evmenc.MakeConfig(ModuleBasics)
|
||||
}
|
||||
|
||||
func init() {
|
||||
simapp.GetSimulatorFlags()
|
||||
}
|
||||
|
||||
const SimAppChainID = "simulation_777-1"
|
||||
|
||||
type storeKeysPrefixes struct {
|
||||
A storetypes.StoreKey
|
||||
B storetypes.StoreKey
|
||||
Prefixes [][]byte
|
||||
}
|
||||
|
||||
// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
|
||||
// an IAVLStore for faster simulation speed.
|
||||
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
||||
bapp.SetFauxMerkleMode()
|
||||
}
|
||||
|
||||
// NewSimApp disable feemarket on native tx, otherwise the cosmos-sdk simulation tests will fail.
|
||||
func NewSimApp(logger log.Logger, db dbm.DB) (*EthermintApp, error) {
|
||||
encodingConfig := MakeEncodingConfig()
|
||||
app := NewEthermintApp(logger, db, nil, false, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, encodingConfig, simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
// disable feemarket on native tx
|
||||
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
|
||||
AccountKeeper: app.AccountKeeper,
|
||||
BankKeeper: app.BankKeeper,
|
||||
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
|
||||
FeegrantKeeper: app.FeeGrantKeeper,
|
||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
||||
IBCKeeper: app.IBCKeeper,
|
||||
EvmKeeper: app.EvmKeeper,
|
||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
||||
MaxTxGasWanted: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.SetAnteHandler(anteHandler)
|
||||
if err := app.LoadLatestVersion(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// interBlockCacheOpt returns a BaseApp option function that sets the persistent
|
||||
// inter-block write-through cache.
|
||||
func interBlockCacheOpt() func(*baseapp.BaseApp) {
|
||||
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
|
||||
}
|
||||
|
||||
func TestFullAppSimulation(t *testing.T) {
|
||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
||||
if skip {
|
||||
t.Skip("skipping application simulation")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.Equal(t, appName, app.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||
app.ModuleAccountAddrs(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simapp.PrintStats(db)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppImportExport(t *testing.T) {
|
||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
||||
if skip {
|
||||
t.Skip("skipping application import/export simulation")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, appName, app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||
app.ModuleAccountAddrs(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simapp.PrintStats(db)
|
||||
}
|
||||
|
||||
fmt.Printf("exporting genesis...\n")
|
||||
|
||||
exported, err := app.ExportAppStateAndValidators(false, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf("importing genesis...\n")
|
||||
|
||||
//nolint: dogsled
|
||||
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, newDB.Close())
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
||||
require.Equal(t, appName, newApp.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
var genesisState simapp.GenesisState
|
||||
err = json.Unmarshal(exported.AppState, &genesisState)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Sprintf("%v", r)
|
||||
if !strings.Contains(err, "validator set is empty after InitGenesis") {
|
||||
panic(r)
|
||||
}
|
||||
logger.Info("Skipping simulation as all validators have been unbonded")
|
||||
logger.Info("err", err, "stacktrace", string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
||||
ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
||||
newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
||||
newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
||||
|
||||
fmt.Printf("comparing stores...\n")
|
||||
|
||||
storeKeysPrefixes := []storeKeysPrefixes{
|
||||
{app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}},
|
||||
{
|
||||
app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey],
|
||||
[][]byte{
|
||||
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
|
||||
stakingtypes.HistoricalInfoKey,
|
||||
},
|
||||
}, // ordering may change but it doesn't matter
|
||||
{app.keys[slashingtypes.StoreKey], newApp.keys[slashingtypes.StoreKey], [][]byte{}},
|
||||
{app.keys[minttypes.StoreKey], newApp.keys[minttypes.StoreKey], [][]byte{}},
|
||||
{app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}},
|
||||
{app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}},
|
||||
{app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}},
|
||||
{app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}},
|
||||
{app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}},
|
||||
{app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}},
|
||||
{app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}},
|
||||
{app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}},
|
||||
{app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}},
|
||||
}
|
||||
|
||||
for _, skp := range storeKeysPrefixes {
|
||||
storeA := ctxA.KVStore(skp.A)
|
||||
storeB := ctxB.KVStore(skp.B)
|
||||
|
||||
failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes)
|
||||
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare")
|
||||
|
||||
fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B)
|
||||
require.Equal(t, len(failedKVAs), 0, simapp.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppSimulationAfterImport(t *testing.T) {
|
||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
||||
if skip {
|
||||
t.Skip("skipping application simulation after import")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.Equal(t, appName, app.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Run randomized simulation
|
||||
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||
app.ModuleAccountAddrs(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simapp.PrintStats(db)
|
||||
}
|
||||
|
||||
if stopEarly {
|
||||
fmt.Println("can't export or import a zero-validator genesis, exiting test...")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("exporting genesis...\n")
|
||||
|
||||
exported, err := app.ExportAppStateAndValidators(true, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Sprintf("%v", r)
|
||||
if !strings.Contains(err, "validator set is empty after InitGenesis") {
|
||||
panic(r)
|
||||
}
|
||||
logger.Info("Skipping simulation as all validators have been unbonded")
|
||||
logger.Info("err", err, "stacktrace", string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Printf("importing genesis...\n")
|
||||
|
||||
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, newDB.Close())
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
||||
require.Equal(t, appName, newApp.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
newApp.InitChain(abci.RequestInitChain{
|
||||
ChainId: SimAppChainID,
|
||||
AppStateBytes: exported.AppState,
|
||||
})
|
||||
|
||||
_, _, err = simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
newApp.BaseApp,
|
||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simapp.SimulationOperations(newApp, newApp.AppCodec(), config),
|
||||
app.ModuleAccountAddrs(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
||||
// and doesn't depend on the application.
|
||||
func TestAppStateDeterminism(t *testing.T) {
|
||||
if !simapp.FlagEnabledValue {
|
||||
t.Skip("skipping application simulation")
|
||||
}
|
||||
|
||||
config := simapp.NewConfigFromFlags()
|
||||
config.InitialBlockHeight = 1
|
||||
config.ExportParamsPath = ""
|
||||
config.OnOperation = false
|
||||
config.AllInvariants = false
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
numSeeds := 3
|
||||
numTimesToRunPerSeed := 5
|
||||
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
||||
|
||||
for i := 0; i < numSeeds; i++ {
|
||||
config.Seed = rand.Int63()
|
||||
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ {
|
||||
var logger log.Logger
|
||||
if simapp.FlagVerboseValue {
|
||||
logger = log.TestingLogger()
|
||||
} else {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf(
|
||||
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
||||
config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
||||
)
|
||||
|
||||
_, _, err = simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||
app.ModuleAccountAddrs(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if config.Commit {
|
||||
simapp.PrintStats(db)
|
||||
}
|
||||
|
||||
appHash := app.LastCommitID().Hash
|
||||
appHashList[j] = appHash
|
||||
|
||||
if j != 0 {
|
||||
require.Equal(
|
||||
t, string(appHashList[0]), string(appHashList[j]),
|
||||
"non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
127
app/utils.go
127
app/utils.go
@ -5,27 +5,21 @@ import (
|
||||
"math/rand" // #nosec G702
|
||||
"time"
|
||||
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/encoding"
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@ -83,7 +77,7 @@ func SetupWithDB(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.Genesis
|
||||
// Initialize the chain
|
||||
app.InitChain(
|
||||
abci.RequestInitChain{
|
||||
ChainId: "ethermint_9000-1",
|
||||
ChainId: "laconic_9000-1",
|
||||
Validators: []abci.ValidatorUpdate{},
|
||||
ConsensusParams: DefaultConsensusParams,
|
||||
AppStateBytes: stateBytes,
|
||||
@ -94,96 +88,6 @@ func SetupWithDB(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.Genesis
|
||||
return app
|
||||
}
|
||||
|
||||
// RandomGenesisAccounts is used by the auth module to create random genesis accounts in simulation when a genesis.json is not specified.
|
||||
// In contrast, the default auth module's RandomGenesisAccounts implementation creates only base accounts and vestings accounts.
|
||||
func RandomGenesisAccounts(simState *module.SimulationState) authtypes.GenesisAccounts {
|
||||
emptyCodeHash := crypto.Keccak256(nil)
|
||||
genesisAccs := make(authtypes.GenesisAccounts, len(simState.Accounts))
|
||||
for i, acc := range simState.Accounts {
|
||||
bacc := authtypes.NewBaseAccountWithAddress(acc.Address)
|
||||
|
||||
ethacc := ðermint.EthAccount{
|
||||
BaseAccount: bacc,
|
||||
CodeHash: common.BytesToHash(emptyCodeHash).String(),
|
||||
}
|
||||
genesisAccs[i] = ethacc
|
||||
}
|
||||
|
||||
return genesisAccs
|
||||
}
|
||||
|
||||
// RandomAccounts creates random accounts with an ethsecp256k1 private key
|
||||
// TODO: replace secp256k1.GenPrivKeyFromSecret() with similar function in go-ethereum
|
||||
func RandomAccounts(r *rand.Rand, n int) []simtypes.Account {
|
||||
accs := make([]simtypes.Account, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
// don't need that much entropy for simulation
|
||||
privkeySeed := make([]byte, 15)
|
||||
_, _ = r.Read(privkeySeed)
|
||||
|
||||
prv := secp256k1.GenPrivKeyFromSecret(privkeySeed)
|
||||
ethPrv := ðsecp256k1.PrivKey{}
|
||||
_ = ethPrv.UnmarshalAmino(prv.Bytes()) // UnmarshalAmino simply copies the bytes and assigns them to ethPrv.Key
|
||||
accs[i].PrivKey = ethPrv
|
||||
accs[i].PubKey = accs[i].PrivKey.PubKey()
|
||||
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())
|
||||
|
||||
accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed)
|
||||
}
|
||||
|
||||
return accs
|
||||
}
|
||||
|
||||
// StateFn returns the initial application state using a genesis or the simulation parameters.
|
||||
// It is a wrapper of simapp.AppStateFn to replace evm param EvmDenom with staking param BondDenom.
|
||||
func StateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simtypes.AppStateFn {
|
||||
return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config,
|
||||
) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) {
|
||||
appStateFn := simapp.AppStateFn(cdc, simManager)
|
||||
appState, simAccs, chainID, genesisTimestamp = appStateFn(r, accs, config)
|
||||
|
||||
rawState := make(map[string]json.RawMessage)
|
||||
err := json.Unmarshal(appState, &rawState)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stakingStateBz, ok := rawState[stakingtypes.ModuleName]
|
||||
if !ok {
|
||||
panic("staking genesis state is missing")
|
||||
}
|
||||
|
||||
stakingState := new(stakingtypes.GenesisState)
|
||||
cdc.MustUnmarshalJSON(stakingStateBz, stakingState)
|
||||
|
||||
// we should get the BondDenom and make it the evmdenom.
|
||||
// thus simulation accounts could have positive amount of gas token.
|
||||
bondDenom := stakingState.Params.BondDenom
|
||||
|
||||
evmStateBz, ok := rawState[evmtypes.ModuleName]
|
||||
if !ok {
|
||||
panic("evm genesis state is missing")
|
||||
}
|
||||
|
||||
evmState := new(evmtypes.GenesisState)
|
||||
cdc.MustUnmarshalJSON(evmStateBz, evmState)
|
||||
|
||||
// we should replace the EvmDenom with BondDenom
|
||||
evmState.Params.EvmDenom = bondDenom
|
||||
|
||||
// change appState back
|
||||
rawState[evmtypes.ModuleName] = cdc.MustMarshalJSON(evmState)
|
||||
|
||||
// replace appstate
|
||||
appState, err = json.Marshal(rawState)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return appState, simAccs, chainID, genesisTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestGenesisState generate genesis state with single validator
|
||||
func NewTestGenesisState(codec codec.Codec) simapp.GenesisState {
|
||||
privVal := mock.NewPV()
|
||||
@ -207,6 +111,29 @@ func NewTestGenesisState(codec codec.Codec) simapp.GenesisState {
|
||||
return genesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance)
|
||||
}
|
||||
|
||||
// RandomAccounts creates random accounts with an ethsecp256k1 private key
|
||||
// TODO: replace secp256k1.GenPrivKeyFromSecret() with similar function in go-ethereum
|
||||
func RandomAccounts(r *rand.Rand, n int) []simtypes.Account {
|
||||
accs := make([]simtypes.Account, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
// don't need that much entropy for simulation
|
||||
privkeySeed := make([]byte, 15)
|
||||
_, _ = r.Read(privkeySeed)
|
||||
|
||||
prv := secp256k1.GenPrivKeyFromSecret(privkeySeed)
|
||||
ethPrv := ðsecp256k1.PrivKey{}
|
||||
_ = ethPrv.UnmarshalAmino(prv.Bytes()) // UnmarshalAmino simply copies the bytes and assigns them to ethPrv.Key
|
||||
accs[i].PrivKey = ethPrv
|
||||
accs[i].PubKey = accs[i].PrivKey.PubKey()
|
||||
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())
|
||||
|
||||
accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed)
|
||||
}
|
||||
|
||||
return accs
|
||||
}
|
||||
|
||||
func genesisStateWithValSet(codec codec.Codec, genesisState simapp.GenesisState,
|
||||
valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount,
|
||||
balances ...banktypes.Balance,
|
||||
|
@ -1,113 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var (
|
||||
maxTestingAccounts = 100
|
||||
seed = int64(233)
|
||||
)
|
||||
|
||||
func TestRandomGenesisAccounts(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
accs := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
||||
|
||||
encodingConfig := MakeEncodingConfig()
|
||||
appCodec := encodingConfig.Codec
|
||||
cdc := encodingConfig.Amino
|
||||
|
||||
paramsKeeper := initParamsKeeper(appCodec, cdc, sdk.NewKVStoreKey(paramstypes.StoreKey), sdk.NewTransientStoreKey(paramstypes.StoreKey))
|
||||
subSpace, find := paramsKeeper.GetSubspace(authtypes.ModuleName)
|
||||
require.True(t, find)
|
||||
accountKeeper := authkeeper.NewAccountKeeper(
|
||||
appCodec, sdk.NewKVStoreKey(authtypes.StoreKey), subSpace, ethermint.ProtoAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(),
|
||||
)
|
||||
authModule := auth.NewAppModule(appCodec, accountKeeper, RandomGenesisAccounts)
|
||||
|
||||
genesisState := simapp.NewDefaultGenesisState(appCodec)
|
||||
simState := &module.SimulationState{Accounts: accs, Cdc: appCodec, Rand: r, GenState: genesisState}
|
||||
authModule.GenerateGenesisState(simState)
|
||||
|
||||
authStateBz, find := genesisState[authtypes.ModuleName]
|
||||
require.True(t, find)
|
||||
|
||||
authState := new(authtypes.GenesisState)
|
||||
appCodec.MustUnmarshalJSON(authStateBz, authState)
|
||||
accounts, err := authtypes.UnpackAccounts(authState.Accounts)
|
||||
require.NoError(t, err)
|
||||
for _, acc := range accounts {
|
||||
_, ok := acc.(ethermint.EthAccountI)
|
||||
require.True(t, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateFn(t *testing.T) {
|
||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
||||
if skip {
|
||||
t.Skip("skipping AppStateFn testing")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
config.ChainID = SimAppChainID
|
||||
config.Commit = true
|
||||
|
||||
defer func() {
|
||||
db.Close()
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
require.Equal(t, appName, app.Name())
|
||||
|
||||
appStateFn := StateFn(app.AppCodec(), app.SimulationManager())
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
accounts := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
||||
appState, _, _, _ := appStateFn(r, accounts, config)
|
||||
|
||||
rawState := make(map[string]json.RawMessage)
|
||||
err = json.Unmarshal(appState, &rawState)
|
||||
require.NoError(t, err)
|
||||
|
||||
stakingStateBz, ok := rawState[stakingtypes.ModuleName]
|
||||
require.True(t, ok)
|
||||
|
||||
stakingState := new(stakingtypes.GenesisState)
|
||||
app.AppCodec().MustUnmarshalJSON(stakingStateBz, stakingState)
|
||||
bondDenom := stakingState.Params.BondDenom
|
||||
|
||||
evmStateBz, ok := rawState[evmtypes.ModuleName]
|
||||
require.True(t, ok)
|
||||
|
||||
evmState := new(evmtypes.GenesisState)
|
||||
app.AppCodec().MustUnmarshalJSON(evmStateBz, evmState)
|
||||
require.Equal(t, bondDenom, evmState.Params.EvmDenom)
|
||||
}
|
||||
|
||||
func TestRandomAccounts(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
accounts := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
||||
for _, acc := range accounts {
|
||||
_, ok := acc.PrivKey.(*ethsecp256k1.PrivKey)
|
||||
require.True(t, ok)
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
version: v1
|
||||
directories:
|
||||
- proto
|
||||
- third_party/proto
|
||||
|
@ -13,57 +13,6 @@
|
||||
"Params": "EvmParams"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/auth/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Account": "AuthAccount",
|
||||
"Params": "AuthParams"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/bank/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Balance": "BankBalance",
|
||||
"Params": "BankParams"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/distribution/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "DistributionParams",
|
||||
"DelegatorValidators": "DistDelegatorValidators"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/mint/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "MintParams"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/gov/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "GovParams"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/staking/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "StakingParams"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
4
client/docs/statik/statik.go
vendored
4
client/docs/statik/statik.go
vendored
File diff suppressed because one or more lines are too long
272
client/docs/swagger-ui/swagger.yaml
vendored
272
client/docs/swagger-ui/swagger.yaml
vendored
@ -154,10 +154,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -369,10 +372,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -583,10 +589,20 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
<<<<<<< HEAD
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
=======
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
>>>>>>> v0.20.0
|
||||
...
|
||||
}
|
||||
|
||||
@ -791,10 +807,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -1014,10 +1033,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -1226,10 +1248,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -1301,6 +1326,18 @@ paths:
|
||||
required: false
|
||||
type: string
|
||||
format: uint64
|
||||
- name: proposer_address
|
||||
description: the proposer of the requested block.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: byte
|
||||
- name: chain_id
|
||||
description: the eip155 chain id parsed from the requested block header.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: int64
|
||||
tags:
|
||||
- Query
|
||||
/ethermint/evm/v1/eth_call:
|
||||
@ -1524,10 +1561,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -1599,6 +1639,18 @@ paths:
|
||||
required: false
|
||||
type: string
|
||||
format: uint64
|
||||
- name: proposer_address
|
||||
description: the proposer of the requested block.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: byte
|
||||
- name: chain_id
|
||||
description: the eip155 chain id parsed from the requested block header.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: int64
|
||||
tags:
|
||||
- Query
|
||||
/ethermint/evm/v1/params:
|
||||
@ -1713,11 +1765,24 @@ paths:
|
||||
title: >-
|
||||
Eip-4345 (bomb delay) switch block (nil = no fork, 0 =
|
||||
already activated)
|
||||
<<<<<<< HEAD
|
||||
merge_fork_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 =
|
||||
already in merge proceedings)
|
||||
=======
|
||||
gray_glacier_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 =
|
||||
already activated)
|
||||
merge_netsplit_block:
|
||||
type: string
|
||||
title: >-
|
||||
Virtual fork after The Merge to use as a network
|
||||
splitter
|
||||
>>>>>>> v0.20.0
|
||||
description: >-
|
||||
ChainConfig defines the Ethereum ChainConfig parameters
|
||||
using *sdk.Int values
|
||||
@ -1727,7 +1792,11 @@ paths:
|
||||
type: boolean
|
||||
description: >-
|
||||
Allow unprotected transactions defines if replay-protected
|
||||
<<<<<<< HEAD
|
||||
(i.e non EIP155
|
||||
=======
|
||||
(i.e non EIP155
|
||||
>>>>>>> v0.20.0
|
||||
|
||||
signed) transactions can be executed on the state machine.
|
||||
title: Params defines the EVM module parameters
|
||||
@ -1861,10 +1930,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -2072,10 +2144,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -2292,10 +2367,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -2481,10 +2559,22 @@ paths:
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
<<<<<<< HEAD
|
||||
- name: trace_config.overrides.merge_fork_block
|
||||
description: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in
|
||||
merge proceedings).
|
||||
=======
|
||||
- name: trace_config.overrides.gray_glacier_block
|
||||
description: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated).
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: trace_config.overrides.merge_netsplit_block
|
||||
description: Virtual fork after The Merge to use as a network splitter.
|
||||
>>>>>>> v0.20.0
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
@ -2498,6 +2588,11 @@ paths:
|
||||
in: query
|
||||
required: false
|
||||
type: boolean
|
||||
- name: trace_config.tracer_json_config
|
||||
description: tracer config.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: block_number
|
||||
description: block number.
|
||||
in: query
|
||||
@ -2515,6 +2610,18 @@ paths:
|
||||
required: false
|
||||
type: string
|
||||
format: date-time
|
||||
- name: proposer_address
|
||||
description: the proposer of the requested block.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: byte
|
||||
- name: chain_id
|
||||
description: the eip155 chain id parsed from the requested block header.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: int64
|
||||
tags:
|
||||
- Query
|
||||
/ethermint/evm/v1/trace_tx:
|
||||
@ -2659,10 +2766,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -2783,7 +2893,7 @@ paths:
|
||||
type: string
|
||||
format: byte
|
||||
- name: msg.size
|
||||
description: encoded storage size of the transaction.
|
||||
description: 'DEPRECATED: encoded storage size of the transaction.'
|
||||
in: query
|
||||
required: false
|
||||
type: number
|
||||
@ -2927,10 +3037,22 @@ paths:
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
<<<<<<< HEAD
|
||||
- name: trace_config.overrides.merge_fork_block
|
||||
description: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in
|
||||
merge proceedings).
|
||||
=======
|
||||
- name: trace_config.overrides.gray_glacier_block
|
||||
description: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated).
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: trace_config.overrides.merge_netsplit_block
|
||||
description: Virtual fork after The Merge to use as a network splitter.
|
||||
>>>>>>> v0.20.0
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
@ -2944,6 +3066,11 @@ paths:
|
||||
in: query
|
||||
required: false
|
||||
type: boolean
|
||||
- name: trace_config.tracer_json_config
|
||||
description: tracer config.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: block_number
|
||||
description: block number of requested transaction.
|
||||
in: query
|
||||
@ -2961,6 +3088,18 @@ paths:
|
||||
required: false
|
||||
type: string
|
||||
format: date-time
|
||||
- name: proposer_address
|
||||
description: the proposer of the requested block.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: byte
|
||||
- name: chain_id
|
||||
description: the eip155 chain id parsed from the requested block header.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
format: int64
|
||||
tags:
|
||||
- Query
|
||||
/ethermint/evm/v1/validator_account/{cons_address}:
|
||||
@ -3120,10 +3259,13 @@ paths:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -3190,6 +3332,7 @@ paths:
|
||||
type: string
|
||||
tags:
|
||||
- Query
|
||||
<<<<<<< HEAD
|
||||
/cosmos/auth/v1beta1/accounts:
|
||||
get:
|
||||
summary: Accounts returns all the existing accounts
|
||||
@ -14052,6 +14195,8 @@ paths:
|
||||
type: boolean
|
||||
tags:
|
||||
- Query
|
||||
=======
|
||||
>>>>>>> v0.20.0
|
||||
definitions:
|
||||
ethermint.evm.v1.ChainConfig:
|
||||
type: object
|
||||
@ -14111,11 +14256,22 @@ definitions:
|
||||
title: >-
|
||||
Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
<<<<<<< HEAD
|
||||
merge_fork_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in merge
|
||||
proceedings)
|
||||
=======
|
||||
gray_glacier_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
merge_netsplit_block:
|
||||
type: string
|
||||
title: Virtual fork after The Merge to use as a network splitter
|
||||
>>>>>>> v0.20.0
|
||||
description: >-
|
||||
ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int
|
||||
values
|
||||
@ -14287,10 +14443,13 @@ definitions:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -14344,7 +14503,7 @@ definitions:
|
||||
size:
|
||||
type: number
|
||||
format: double
|
||||
title: encoded storage size of the transaction
|
||||
title: 'DEPRECATED: encoded storage size of the transaction'
|
||||
hash:
|
||||
type: string
|
||||
title: transaction hash in hex format
|
||||
@ -14515,11 +14674,22 @@ definitions:
|
||||
title: >-
|
||||
Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
<<<<<<< HEAD
|
||||
merge_fork_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in
|
||||
merge proceedings)
|
||||
=======
|
||||
gray_glacier_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
merge_netsplit_block:
|
||||
type: string
|
||||
title: Virtual fork after The Merge to use as a network splitter
|
||||
>>>>>>> v0.20.0
|
||||
description: >-
|
||||
ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int
|
||||
values
|
||||
@ -14529,7 +14699,11 @@ definitions:
|
||||
type: boolean
|
||||
description: >-
|
||||
Allow unprotected transactions defines if replay-protected (i.e non
|
||||
<<<<<<< HEAD
|
||||
EIP155
|
||||
=======
|
||||
EIP155
|
||||
>>>>>>> v0.20.0
|
||||
|
||||
signed) transactions can be executed on the state machine.
|
||||
title: Params defines the EVM module parameters
|
||||
@ -14684,11 +14858,22 @@ definitions:
|
||||
title: >-
|
||||
Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
<<<<<<< HEAD
|
||||
merge_fork_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already
|
||||
in merge proceedings)
|
||||
=======
|
||||
gray_glacier_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
merge_netsplit_block:
|
||||
type: string
|
||||
title: Virtual fork after The Merge to use as a network splitter
|
||||
>>>>>>> v0.20.0
|
||||
description: >-
|
||||
ChainConfig defines the Ethereum ChainConfig parameters using
|
||||
*sdk.Int values
|
||||
@ -14698,7 +14883,11 @@ definitions:
|
||||
type: boolean
|
||||
description: >-
|
||||
Allow unprotected transactions defines if replay-protected (i.e
|
||||
<<<<<<< HEAD
|
||||
non EIP155
|
||||
=======
|
||||
non EIP155
|
||||
>>>>>>> v0.20.0
|
||||
|
||||
signed) transactions can be executed on the state machine.
|
||||
title: Params defines the EVM module parameters
|
||||
@ -14839,11 +15028,22 @@ definitions:
|
||||
title: >-
|
||||
Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
<<<<<<< HEAD
|
||||
merge_fork_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in
|
||||
merge proceedings)
|
||||
=======
|
||||
gray_glacier_block:
|
||||
type: string
|
||||
title: >-
|
||||
EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already
|
||||
activated)
|
||||
merge_netsplit_block:
|
||||
type: string
|
||||
title: Virtual fork after The Merge to use as a network splitter
|
||||
>>>>>>> v0.20.0
|
||||
description: >-
|
||||
ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int
|
||||
values
|
||||
@ -14855,6 +15055,9 @@ definitions:
|
||||
enable_return_data:
|
||||
type: boolean
|
||||
title: enable return data capture
|
||||
tracer_json_config:
|
||||
type: string
|
||||
title: tracer config
|
||||
description: TraceConfig holds extra parameters to trace functions.
|
||||
google.protobuf.Any:
|
||||
type: object
|
||||
@ -14958,10 +15161,13 @@ definitions:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -15132,10 +15338,13 @@ definitions:
|
||||
Example 4: Pack and unpack a message in Go
|
||||
|
||||
foo := &pb.Foo{...}
|
||||
any, err := ptypes.MarshalAny(foo)
|
||||
any, err := anypb.New(foo)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
...
|
||||
foo := &pb.Foo{}
|
||||
if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
if err := any.UnmarshalTo(foo); err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
@ -15185,6 +15394,7 @@ definitions:
|
||||
"@type": "type.googleapis.com/google.protobuf.Duration",
|
||||
"value": "1.212s"
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
cosmos.auth.v1beta1.Params:
|
||||
type: object
|
||||
properties:
|
||||
@ -21145,3 +21355,5 @@ definitions:
|
||||
application's
|
||||
|
||||
state transition machine.
|
||||
=======
|
||||
>>>>>>> v0.20.0
|
||||
|
@ -50,18 +50,32 @@ output
|
||||
- armor encrypted private key (saved to file)
|
||||
*/
|
||||
func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error {
|
||||
var err error
|
||||
var (
|
||||
algo keyring.SignatureAlgo
|
||||
err error
|
||||
)
|
||||
|
||||
name := args[0]
|
||||
|
||||
interactive, _ := cmd.Flags().GetBool(flagInteractive)
|
||||
noBackup, _ := cmd.Flags().GetBool(flagNoBackup)
|
||||
useLedger, _ := cmd.Flags().GetBool(flags.FlagUseLedger)
|
||||
algoStr, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm)
|
||||
|
||||
showMnemonic := !noBackup
|
||||
kb := ctx.Keyring
|
||||
outputFormat := ctx.OutputFormat
|
||||
|
||||
keyringAlgos, _ := kb.SupportedAlgorithms()
|
||||
algoStr, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm)
|
||||
algo, err := keyring.NewSigningAlgoFromString(algoStr, keyringAlgos)
|
||||
keyringAlgos, ledgerAlgos := kb.SupportedAlgorithms()
|
||||
|
||||
// check if the provided signing algorithm is supported by the keyring or
|
||||
// ledger
|
||||
if useLedger {
|
||||
algo, err = keyring.NewSigningAlgoFromString(algoStr, ledgerAlgos)
|
||||
} else {
|
||||
algo, err = keyring.NewSigningAlgoFromString(algoStr, keyringAlgos)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -144,7 +158,6 @@ func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *buf
|
||||
account, _ := cmd.Flags().GetUint32(flagAccount)
|
||||
index, _ := cmd.Flags().GetUint32(flagIndex)
|
||||
hdPath, _ := cmd.Flags().GetString(flagHDPath)
|
||||
useLedger, _ := cmd.Flags().GetBool(flags.FlagUseLedger)
|
||||
|
||||
if len(hdPath) == 0 {
|
||||
hdPath = hd.CreateHDPath(coinType, account, index).String()
|
||||
@ -155,7 +168,9 @@ func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *buf
|
||||
// If we're using ledger, only thing we need is the path and the bech32 prefix.
|
||||
if useLedger {
|
||||
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
|
||||
k, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index)
|
||||
|
||||
// use the provided algo to save the ledger key
|
||||
k, err := kb.SaveLedgerKey(name, algo, bech32PrefixAccAddr, coinType, account, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import (
|
||||
"github.com/cerc-io/laconicd/version"
|
||||
)
|
||||
|
||||
const (
|
||||
flagLong = "long"
|
||||
)
|
||||
const flagLong = "long"
|
||||
|
||||
func init() {
|
||||
infoCmd.Flags().Bool(flagLong, false, "Print full information")
|
||||
|
@ -32,7 +32,7 @@ const (
|
||||
// AddGenesisAccountCmd returns add-genesis-account cobra Command.
|
||||
func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
|
||||
Use: "add-genesis-account ADDRESS_OR_KEY_NAME COIN...",
|
||||
Short: "Add a genesis account to genesis.json",
|
||||
Long: `Add a genesis account to genesis.json. The provided account must specify
|
||||
the account address or key name and a list of initial coins. If a key name is given,
|
||||
|
@ -37,13 +37,14 @@ import (
|
||||
"github.com/cerc-io/laconicd/client/debug"
|
||||
"github.com/cerc-io/laconicd/crypto/hd"
|
||||
"github.com/cerc-io/laconicd/encoding"
|
||||
"github.com/cerc-io/laconicd/ethereum/eip712"
|
||||
"github.com/cerc-io/laconicd/server"
|
||||
servercfg "github.com/cerc-io/laconicd/server/config"
|
||||
srvflags "github.com/cerc-io/laconicd/server/flags"
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
)
|
||||
|
||||
const EnvPrefix = "ETHERMINT"
|
||||
const EnvPrefix = "LACONIC"
|
||||
|
||||
// NewRootCmd creates a new root command for simd. It is called once in the
|
||||
// main function.
|
||||
@ -61,9 +62,11 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
|
||||
WithKeyringOptions(hd.EthSecp256k1Option()).
|
||||
WithViper(EnvPrefix)
|
||||
|
||||
eip712.SetEncodingConfig(encodingConfig)
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "laconicd",
|
||||
Short: "Ethermint Daemon",
|
||||
Short: "Laconic Daemon",
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
// set the default command outputs
|
||||
cmd.SetOut(cmd.OutOrStdout())
|
||||
@ -182,6 +185,7 @@ func txCommand() *cobra.Command {
|
||||
authcmd.GetBroadcastCommand(),
|
||||
authcmd.GetEncodeCommand(),
|
||||
authcmd.GetDecodeCommand(),
|
||||
authcmd.GetAuxToFeeCommand(),
|
||||
)
|
||||
|
||||
app.ModuleBasics.AddTxCommands(cmd)
|
||||
@ -246,6 +250,8 @@ func (a appCreator) newApp(logger tmlog.Logger, db dbm.DB, traceStore io.Writer,
|
||||
baseapp.SetTrace(cast.ToBool(appOpts.Get(sdkserver.FlagTrace))),
|
||||
baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(sdkserver.FlagIndexEvents))),
|
||||
baseapp.SetSnapshot(snapshotStore, snapshotOptions),
|
||||
baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(sdkserver.FlagIAVLCacheSize))),
|
||||
baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(sdkserver.FlagDisableIAVLFastNode))),
|
||||
)
|
||||
|
||||
return ethermintApp
|
||||
|
@ -6,11 +6,12 @@ import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"github.com/cerc-io/laconicd/ethereum/eip712"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
@ -182,7 +183,7 @@ func (pubKey PubKey) MarshalAmino() ([]byte, error) {
|
||||
// UnmarshalAmino overrides Amino binary marshaling.
|
||||
func (pubKey *PubKey) UnmarshalAmino(bz []byte) error {
|
||||
if len(bz) != PubKeySize {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "invalid pubkey size, expected %d, got %d", PubKeySize, len(bz))
|
||||
return errorsmod.Wrapf(errortypes.ErrInvalidPubKey, "invalid pubkey size, expected %d, got %d", PubKeySize, len(bz))
|
||||
}
|
||||
pubKey.Key = bz
|
||||
|
||||
@ -203,10 +204,28 @@ func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
|
||||
|
||||
// VerifySignature verifies that the ECDSA public key created a given signature over
|
||||
// the provided message. It will calculate the Keccak256 hash of the message
|
||||
// prior to verification.
|
||||
// prior to verification and approve verification if the signature can be verified
|
||||
// from either the original message or its EIP-712 representation.
|
||||
//
|
||||
// CONTRACT: The signature should be in [R || S] format.
|
||||
func (pubKey PubKey) VerifySignature(msg, sig []byte) bool {
|
||||
return pubKey.verifySignatureECDSA(msg, sig) || pubKey.verifySignatureAsEIP712(msg, sig)
|
||||
}
|
||||
|
||||
// Verifies the signature as an EIP-712 signature by first converting the message payload
|
||||
// to EIP-712 object bytes, then performing ECDSA verification on the hash. This is to support
|
||||
// signing a Cosmos payload using EIP-712.
|
||||
func (pubKey PubKey) verifySignatureAsEIP712(msg, sig []byte) bool {
|
||||
eip712Bytes, err := eip712.GetEIP712BytesForMsg(msg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return pubKey.verifySignatureECDSA(eip712Bytes, sig)
|
||||
}
|
||||
|
||||
// Perform standard ECDSA signature verification for the given raw bytes and signature.
|
||||
func (pubKey PubKey) verifySignatureECDSA(msg, sig []byte) bool {
|
||||
if len(sig) == crypto.SignatureLength {
|
||||
// remove recovery ID (V) if contained in the signature
|
||||
sig = sig[:len(sig)-1]
|
||||
|
4
crypto/ethsecp256k1/keys.pb.go
generated
4
crypto/ethsecp256k1/keys.pb.go
generated
@ -5,7 +5,7 @@ package ethsecp256k1
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
_ "github.com/cosmos/gogoproto/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
@ -27,6 +27,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
// Tendermint's PubKey interface. It represents the 33-byte compressed public
|
||||
// key format.
|
||||
type PubKey struct {
|
||||
// key is the public key in byte form
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
@ -72,6 +73,7 @@ func (m *PubKey) GetKey() []byte {
|
||||
// PrivKey defines a type alias for an ecdsa.PrivateKey that implements
|
||||
// Tendermint's PrivateKey interface.
|
||||
type PrivKey struct {
|
||||
// key is the private key in byte form
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -10,7 +11,6 @@ import (
|
||||
|
||||
hdwallet "github.com/miguelmota/go-ethereum-hdwallet"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
amino "github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
@ -20,10 +20,10 @@ import (
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
)
|
||||
|
||||
var TestCodec codec.Codec
|
||||
var TestCodec amino.Codec
|
||||
|
||||
func init() {
|
||||
cdc := codec.NewLegacyAmino()
|
||||
cdc := amino.NewLegacyAmino()
|
||||
cryptocodec.RegisterCrypto(cdc)
|
||||
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
@ -31,12 +31,18 @@ func init() {
|
||||
enccodec.RegisterInterfaces(interfaceRegistry)
|
||||
}
|
||||
|
||||
const mnemonic = "picnic rent average infant boat squirrel federal assault mercy purity very motor fossil wheel verify upset box fresh horse vivid copy predict square regret"
|
||||
const (
|
||||
mnemonic = "picnic rent average infant boat squirrel federal assault mercy purity very motor fossil wheel verify upset box fresh horse vivid copy predict square regret"
|
||||
|
||||
// hdWalletFixEnv defines whether the standard (correct) bip39
|
||||
// derivation path was used, or if derivation was affected by
|
||||
// https://github.com/btcsuite/btcutil/issues/172
|
||||
hdWalletFixEnv = "GO_ETHEREUM_HDWALLET_FIX_ISSUE_179"
|
||||
)
|
||||
|
||||
func TestKeyring(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
mockIn := strings.NewReader("")
|
||||
|
||||
kr, err := keyring.New("ethermint", keyring.BackendTest, dir, mockIn, TestCodec, EthSecp256k1Option())
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -68,7 +74,9 @@ func TestKeyring(t *testing.T) {
|
||||
privkey := EthSecp256k1.Generate()(bz)
|
||||
addr := common.BytesToAddress(privkey.PubKey().Address().Bytes())
|
||||
|
||||
os.Setenv(hdWalletFixEnv, "true")
|
||||
wallet, err := hdwallet.NewFromMnemonic(mnemonic)
|
||||
os.Setenv(hdWalletFixEnv, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
path := hdwallet.MustParseDerivationPath(hdPath)
|
||||
|
@ -423,6 +423,7 @@ TraceConfig holds extra parameters to trace functions.
|
||||
| `overrides` | [ChainConfig](#ethermint.evm.v1.ChainConfig) | | Chain overrides, can be used to execute a trace using future fork rules |
|
||||
| `enable_memory` | [bool](#bool) | | enable memory capture |
|
||||
| `enable_return_data` | [bool](#bool) | | enable return data capture |
|
||||
| `tracer_json_config` | [string](#string) | | tracer config |
|
||||
|
||||
|
||||
|
||||
@ -710,6 +711,8 @@ EthCallRequest defines EthCall request
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `args` | [bytes](#bytes) | | same json format as the json rpc api. |
|
||||
| `gas_cap` | [uint64](#uint64) | | the default gas cap to be used |
|
||||
| `proposer_address` | [bytes](#bytes) | | the proposer of the requested block |
|
||||
| `chain_id` | [int64](#int64) | | the eip155 chain id parsed from the requested block header |
|
||||
|
||||
|
||||
|
||||
@ -939,6 +942,8 @@ QueryTraceBlockRequest defines TraceTx request
|
||||
| `block_number` | [int64](#int64) | | block number |
|
||||
| `block_hash` | [string](#string) | | block hex hash |
|
||||
| `block_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | block time |
|
||||
| `proposer_address` | [bytes](#bytes) | | the proposer of the requested block |
|
||||
| `chain_id` | [int64](#int64) | | the eip155 chain id parsed from the requested block header |
|
||||
|
||||
|
||||
|
||||
@ -974,6 +979,8 @@ QueryTraceTxRequest defines TraceTx request
|
||||
| `block_number` | [int64](#int64) | | block number of requested transaction |
|
||||
| `block_hash` | [string](#string) | | block hex hash of requested transaction |
|
||||
| `block_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | block time of requested transaction |
|
||||
| `proposer_address` | [bytes](#bytes) | | the proposer of the requested block |
|
||||
| `chain_id` | [int64](#int64) | | the eip155 chain id parsed from the requested block header |
|
||||
|
||||
|
||||
|
||||
@ -1914,9 +1921,9 @@ Tx defines the gRPC tx interface
|
||||
|
||||
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
|
||||
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
|
||||
| `CreateAuction` | [MsgCreateAuction](#vulcanize.auction.v1beta1.MsgCreateAuction) | [MsgCreateAuctionResponse](#vulcanize.auction.v1beta1.MsgCreateAuctionResponse) | CreateAuction is the command for creating an auction | |
|
||||
| `CommitBid` | [MsgCommitBid](#vulcanize.auction.v1beta1.MsgCommitBid) | [MsgCommitBidResponse](#vulcanize.auction.v1beta1.MsgCommitBidResponse) | CommitBid is the command for committing a bid | |
|
||||
| `RevealBid` | [MsgRevealBid](#vulcanize.auction.v1beta1.MsgRevealBid) | [MsgRevealBidResponse](#vulcanize.auction.v1beta1.MsgRevealBidResponse) | RevealBid is the command for revealing a bid | |
|
||||
| `CreateAuction` | [MsgCreateAuction](#vulcanize.auction.v1beta1.MsgCreateAuction) | [MsgCreateAuctionResponse](#vulcanize.auction.v1beta1.MsgCreateAuctionResponse) | CreateAuction is the command for creating an auction | POST|/vulcanize/auction/v1beta1/create_auction|
|
||||
| `CommitBid` | [MsgCommitBid](#vulcanize.auction.v1beta1.MsgCommitBid) | [MsgCommitBidResponse](#vulcanize.auction.v1beta1.MsgCommitBidResponse) | CommitBid is the command for committing a bid | POST|/vulcanize/auction/v1beta1/commit_bid|
|
||||
| `RevealBid` | [MsgRevealBid](#vulcanize.auction.v1beta1.MsgRevealBid) | [MsgRevealBidResponse](#vulcanize.auction.v1beta1.MsgRevealBidResponse) | RevealBid is the command for revealing a bid | POST|/vulcanize/auction/v1beta1/reveal_bid|
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
@ -2306,10 +2313,10 @@ Msg defines the bond Msg service.
|
||||
|
||||
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
|
||||
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
|
||||
| `CreateBond` | [MsgCreateBond](#vulcanize.bond.v1beta1.MsgCreateBond) | [MsgCreateBondResponse](#vulcanize.bond.v1beta1.MsgCreateBondResponse) | CreateBond defines a method for creating a new bond. | |
|
||||
| `RefillBond` | [MsgRefillBond](#vulcanize.bond.v1beta1.MsgRefillBond) | [MsgRefillBondResponse](#vulcanize.bond.v1beta1.MsgRefillBondResponse) | RefillBond defines a method for refilling amount for bond. | |
|
||||
| `WithdrawBond` | [MsgWithdrawBond](#vulcanize.bond.v1beta1.MsgWithdrawBond) | [MsgWithdrawBondResponse](#vulcanize.bond.v1beta1.MsgWithdrawBondResponse) | WithdrawBond defines a method for withdrawing amount from bond. | |
|
||||
| `CancelBond` | [MsgCancelBond](#vulcanize.bond.v1beta1.MsgCancelBond) | [MsgCancelBondResponse](#vulcanize.bond.v1beta1.MsgCancelBondResponse) | CancelBond defines a method for cancelling a bond. | |
|
||||
| `CreateBond` | [MsgCreateBond](#vulcanize.bond.v1beta1.MsgCreateBond) | [MsgCreateBondResponse](#vulcanize.bond.v1beta1.MsgCreateBondResponse) | CreateBond defines a method for creating a new bond. | POST|/vulcanize/bond/v1beta1/create_bond|
|
||||
| `RefillBond` | [MsgRefillBond](#vulcanize.bond.v1beta1.MsgRefillBond) | [MsgRefillBondResponse](#vulcanize.bond.v1beta1.MsgRefillBondResponse) | RefillBond defines a method for refilling amount for bond. | POST|/vulcanize/bond/v1beta1/refill_bond|
|
||||
| `WithdrawBond` | [MsgWithdrawBond](#vulcanize.bond.v1beta1.MsgWithdrawBond) | [MsgWithdrawBondResponse](#vulcanize.bond.v1beta1.MsgWithdrawBondResponse) | WithdrawBond defines a method for withdrawing amount from bond. | POST|/vulcanize/bond/v1beta1/withdraw_bond|
|
||||
| `CancelBond` | [MsgCancelBond](#vulcanize.bond.v1beta1.MsgCancelBond) | [MsgCancelBondResponse](#vulcanize.bond.v1beta1.MsgCancelBondResponse) | CancelBond defines a method for cancelling a bond. | POST|/vulcanize/bond/v1beta1/cancel_bond|
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
@ -2334,6 +2341,7 @@ Msg defines the bond Msg service.
|
||||
| `laconic_id` | [string](#string) | | |
|
||||
| `x500` | [X500](#vulcanize.registry.v1beta1.X500) | | |
|
||||
| `type` | [string](#string) | | |
|
||||
| `version` | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
@ -2353,6 +2361,7 @@ Msg defines the bond Msg service.
|
||||
| `build_artifact_cid` | [string](#string) | | |
|
||||
| `tls_cert_cid` | [string](#string) | | |
|
||||
| `type` | [string](#string) | | |
|
||||
| `version` | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
@ -3378,16 +3387,16 @@ Msg
|
||||
|
||||
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
|
||||
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
|
||||
| `SetRecord` | [MsgSetRecord](#vulcanize.registry.v1beta1.MsgSetRecord) | [MsgSetRecordResponse](#vulcanize.registry.v1beta1.MsgSetRecordResponse) | SetRecord will records a new record with given payload and bond id | |
|
||||
| `RenewRecord` | [MsgRenewRecord](#vulcanize.registry.v1beta1.MsgRenewRecord) | [MsgRenewRecordResponse](#vulcanize.registry.v1beta1.MsgRenewRecordResponse) | Renew Record will renew the expire record | |
|
||||
| `AssociateBond` | [MsgAssociateBond](#vulcanize.registry.v1beta1.MsgAssociateBond) | [MsgAssociateBondResponse](#vulcanize.registry.v1beta1.MsgAssociateBondResponse) | AssociateBond | |
|
||||
| `DissociateBond` | [MsgDissociateBond](#vulcanize.registry.v1beta1.MsgDissociateBond) | [MsgDissociateBondResponse](#vulcanize.registry.v1beta1.MsgDissociateBondResponse) | DissociateBond | |
|
||||
| `DissociateRecords` | [MsgDissociateRecords](#vulcanize.registry.v1beta1.MsgDissociateRecords) | [MsgDissociateRecordsResponse](#vulcanize.registry.v1beta1.MsgDissociateRecordsResponse) | DissociateRecords | |
|
||||
| `ReAssociateRecords` | [MsgReAssociateRecords](#vulcanize.registry.v1beta1.MsgReAssociateRecords) | [MsgReAssociateRecordsResponse](#vulcanize.registry.v1beta1.MsgReAssociateRecordsResponse) | ReAssociateRecords | |
|
||||
| `SetName` | [MsgSetName](#vulcanize.registry.v1beta1.MsgSetName) | [MsgSetNameResponse](#vulcanize.registry.v1beta1.MsgSetNameResponse) | SetName will store the name with given crn and name | |
|
||||
| `ReserveName` | [MsgReserveAuthority](#vulcanize.registry.v1beta1.MsgReserveAuthority) | [MsgReserveAuthorityResponse](#vulcanize.registry.v1beta1.MsgReserveAuthorityResponse) | Reserve name | |
|
||||
| `DeleteName` | [MsgDeleteNameAuthority](#vulcanize.registry.v1beta1.MsgDeleteNameAuthority) | [MsgDeleteNameAuthorityResponse](#vulcanize.registry.v1beta1.MsgDeleteNameAuthorityResponse) | Delete Name method will remove authority name | |
|
||||
| `SetAuthorityBond` | [MsgSetAuthorityBond](#vulcanize.registry.v1beta1.MsgSetAuthorityBond) | [MsgSetAuthorityBondResponse](#vulcanize.registry.v1beta1.MsgSetAuthorityBondResponse) | SetAuthorityBond | |
|
||||
| `SetRecord` | [MsgSetRecord](#vulcanize.registry.v1beta1.MsgSetRecord) | [MsgSetRecordResponse](#vulcanize.registry.v1beta1.MsgSetRecordResponse) | SetRecord will records a new record with given payload and bond id | POST|/vulcanize/registry/v1beta1/set_record|
|
||||
| `RenewRecord` | [MsgRenewRecord](#vulcanize.registry.v1beta1.MsgRenewRecord) | [MsgRenewRecordResponse](#vulcanize.registry.v1beta1.MsgRenewRecordResponse) | Renew Record will renew the expire record | POST|/vulcanize/registry/v1beta1/renew_record|
|
||||
| `AssociateBond` | [MsgAssociateBond](#vulcanize.registry.v1beta1.MsgAssociateBond) | [MsgAssociateBondResponse](#vulcanize.registry.v1beta1.MsgAssociateBondResponse) | AssociateBond | POST|/vulcanize/registry/v1beta1/associate_bond|
|
||||
| `DissociateBond` | [MsgDissociateBond](#vulcanize.registry.v1beta1.MsgDissociateBond) | [MsgDissociateBondResponse](#vulcanize.registry.v1beta1.MsgDissociateBondResponse) | DissociateBond | POST|/vulcanize/registry/v1beta1/dissociate_bond|
|
||||
| `DissociateRecords` | [MsgDissociateRecords](#vulcanize.registry.v1beta1.MsgDissociateRecords) | [MsgDissociateRecordsResponse](#vulcanize.registry.v1beta1.MsgDissociateRecordsResponse) | DissociateRecords | POST|/vulcanize/registry/v1beta1/dissociate_records|
|
||||
| `ReAssociateRecords` | [MsgReAssociateRecords](#vulcanize.registry.v1beta1.MsgReAssociateRecords) | [MsgReAssociateRecordsResponse](#vulcanize.registry.v1beta1.MsgReAssociateRecordsResponse) | ReAssociateRecords | POST|/vulcanize/registry/v1beta1/reassociate_records|
|
||||
| `SetName` | [MsgSetName](#vulcanize.registry.v1beta1.MsgSetName) | [MsgSetNameResponse](#vulcanize.registry.v1beta1.MsgSetNameResponse) | SetName will store the name with given crn and name | POST|/vulcanize/registry/v1beta1/set_name|
|
||||
| `ReserveName` | [MsgReserveAuthority](#vulcanize.registry.v1beta1.MsgReserveAuthority) | [MsgReserveAuthorityResponse](#vulcanize.registry.v1beta1.MsgReserveAuthorityResponse) | Reserve name | POST|/vulcanize/registry/v1beta1/reserve_name|
|
||||
| `DeleteName` | [MsgDeleteNameAuthority](#vulcanize.registry.v1beta1.MsgDeleteNameAuthority) | [MsgDeleteNameAuthorityResponse](#vulcanize.registry.v1beta1.MsgDeleteNameAuthorityResponse) | Delete Name method will remove authority name | POST|/vulcanize/registry/v1beta1/delete_name|
|
||||
| `SetAuthorityBond` | [MsgSetAuthorityBond](#vulcanize.registry.v1beta1.MsgSetAuthorityBond) | [MsgSetAuthorityBondResponse](#vulcanize.registry.v1beta1.MsgSetAuthorityBondResponse) | SetAuthorityBond | POST|/vulcanize/registry/v1beta1/set_authority_bond|
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
@ -2,6 +2,7 @@ package eip712
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@ -9,38 +10,21 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
)
|
||||
|
||||
// ComputeTypedDataHash computes keccak hash of typed data for signing.
|
||||
func ComputeTypedDataHash(typedData apitypes.TypedData) ([]byte, error) {
|
||||
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
||||
if err != nil {
|
||||
err = sdkerrors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
|
||||
if err != nil {
|
||||
err = sdkerrors.Wrap(err, "failed to pack and hash typedData primary type")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
|
||||
return crypto.Keccak256(rawData), nil
|
||||
}
|
||||
const bytesStr = "bytes"
|
||||
|
||||
// WrapTxToTypedData is an ultimate method that wraps Amino-encoded Cosmos Tx JSON data
|
||||
// into an EIP712-compatible TypedData request.
|
||||
@ -51,12 +35,6 @@ func WrapTxToTypedData(
|
||||
data []byte,
|
||||
feeDelegation *FeeDelegationOptions,
|
||||
) (apitypes.TypedData, error) {
|
||||
txData := make(map[string]interface{})
|
||||
|
||||
if err := json.Unmarshal(data, &txData); err != nil {
|
||||
return apitypes.TypedData{}, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, "failed to JSON unmarshal data")
|
||||
}
|
||||
|
||||
domain := apitypes.TypedDataDomain{
|
||||
Name: "Cosmos Web3",
|
||||
Version: "1.0.0",
|
||||
@ -70,10 +48,19 @@ func WrapTxToTypedData(
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
txData := make(map[string]interface{})
|
||||
if err := json.Unmarshal(data, &txData); err != nil {
|
||||
return apitypes.TypedData{}, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, "failed to JSON unmarshal data")
|
||||
}
|
||||
|
||||
if err := patchTxData(txData, msgTypes, "Tx"); err != nil {
|
||||
return apitypes.TypedData{}, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, "failed to patch JSON data")
|
||||
}
|
||||
|
||||
if feeDelegation != nil {
|
||||
feeInfo, ok := txData["fee"].(map[string]interface{})
|
||||
if !ok {
|
||||
return apitypes.TypedData{}, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "cannot parse fee from tx data")
|
||||
return apitypes.TypedData{}, errorsmod.Wrap(errortypes.ErrInvalidType, "cannot parse fee from tx data")
|
||||
}
|
||||
|
||||
feeInfo["feePayer"] = feeDelegation.FeePayer.String()
|
||||
@ -206,7 +193,11 @@ func traverseFields(
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
var field reflect.Value
|
||||
var (
|
||||
field reflect.Value
|
||||
err error
|
||||
)
|
||||
|
||||
if v.IsValid() {
|
||||
field = v.Field(i)
|
||||
}
|
||||
@ -215,23 +206,15 @@ func traverseFields(
|
||||
fieldName := jsonNameFromTag(t.Field(i).Tag)
|
||||
|
||||
if fieldType == cosmosAnyType {
|
||||
any, ok := field.Interface().(*codectypes.Any)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "%T", field.Interface())
|
||||
// Unpack field, value as Any
|
||||
if fieldType, field, err = unpackAny(cdc, field); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
anyWrapper := &cosmosAnyWrapper{
|
||||
Type: any.TypeUrl,
|
||||
}
|
||||
|
||||
if err := cdc.UnpackAny(any, &anyWrapper.Value); err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to unpack Any in msg struct")
|
||||
}
|
||||
|
||||
fieldType = reflect.TypeOf(anyWrapper)
|
||||
field = reflect.ValueOf(anyWrapper)
|
||||
|
||||
// then continue as normal
|
||||
// If field is an empty value, do not include in types, since it will not be present in the object
|
||||
if field.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
// If its a nil pointer, do not include in types
|
||||
@ -273,6 +256,12 @@ func traverseFields(
|
||||
fieldType = fieldType.Elem()
|
||||
field = field.Index(0)
|
||||
isCollection = true
|
||||
|
||||
if fieldType == cosmosAnyType {
|
||||
if fieldType, field, err = unpackAny(cdc, field); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
@ -304,10 +293,15 @@ func traverseFields(
|
||||
ethTyp := typToEth(fieldType)
|
||||
if len(ethTyp) > 0 {
|
||||
// Support array of uint64
|
||||
if isCollection && fieldType.Kind() != reflect.Slice && fieldType.Kind() != reflect.Array {
|
||||
if isCollection {
|
||||
if fieldType.Kind() != reflect.Slice && fieldType.Kind() != reflect.Array {
|
||||
ethTyp += "[]"
|
||||
}
|
||||
|
||||
// convert uint8[] to bytes
|
||||
if fieldType.Kind() == reflect.Uint8 {
|
||||
ethTyp = bytesStr
|
||||
}
|
||||
}
|
||||
if prefix == typeDefPrefix {
|
||||
typeMap[rootType] = append(typeMap[rootType], apitypes.Type{
|
||||
Name: fieldName,
|
||||
@ -363,6 +357,27 @@ func jsonNameFromTag(tag reflect.StructTag) string {
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
// Unpack the given Any value with Type/Value deconstruction
|
||||
func unpackAny(cdc codectypes.AnyUnpacker, field reflect.Value) (reflect.Type, reflect.Value, error) {
|
||||
any, ok := field.Interface().(*codectypes.Any)
|
||||
if !ok {
|
||||
return nil, reflect.Value{}, errorsmod.Wrapf(errortypes.ErrPackAny, "%T", field.Interface())
|
||||
}
|
||||
|
||||
anyWrapper := &cosmosAnyWrapper{
|
||||
Type: any.TypeUrl,
|
||||
}
|
||||
|
||||
if err := cdc.UnpackAny(any, &anyWrapper.Value); err != nil {
|
||||
return nil, reflect.Value{}, errorsmod.Wrap(err, "failed to unpack Any in msg struct")
|
||||
}
|
||||
|
||||
fieldType := reflect.TypeOf(anyWrapper)
|
||||
field = reflect.ValueOf(anyWrapper)
|
||||
|
||||
return fieldType, field, nil
|
||||
}
|
||||
|
||||
// _.foo_bar.baz -> TypeFooBarBaz
|
||||
//
|
||||
// this is needed for Geth's own signing code which doesn't
|
||||
@ -429,14 +444,13 @@ func typToEth(typ reflect.Type) string {
|
||||
return "uint32"
|
||||
case reflect.Uint64:
|
||||
return "uint64"
|
||||
case reflect.Slice:
|
||||
case reflect.Slice | reflect.Array:
|
||||
// Note: this case may never be reached due to previous handling in traverseFields
|
||||
ethName := typToEth(typ.Elem())
|
||||
if len(ethName) > 0 {
|
||||
return ethName + "[]"
|
||||
if ethName == "uint8" {
|
||||
return bytesStr
|
||||
}
|
||||
case reflect.Array:
|
||||
ethName := typToEth(typ.Elem())
|
||||
if len(ethName) > 0 {
|
||||
return ethName + "[]"
|
||||
}
|
||||
case reflect.Ptr:
|
||||
@ -465,7 +479,7 @@ func typToEth(typ reflect.Type) string {
|
||||
func doRecover(err *error) {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(error); ok {
|
||||
e = sdkerrors.Wrap(e, "panicked with error")
|
||||
e = errorsmod.Wrap(e, "panicked with error")
|
||||
*err = e
|
||||
return
|
||||
}
|
||||
@ -473,3 +487,77 @@ func doRecover(err *error) {
|
||||
*err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Performs extra type conversions on JSON-decoded data accoding to the provided type definitions
|
||||
// for compatibility with Geth's encoding
|
||||
func patchTxData(data map[string]any, schema apitypes.Types, rootType string) error {
|
||||
// Scan the data for any types that need to be converted.
|
||||
// This is adapted from TypedData.EncodeData
|
||||
for _, field := range schema[rootType] {
|
||||
encType := field.Type
|
||||
encValue := data[field.Name]
|
||||
|
||||
switch {
|
||||
case encType[len(encType)-1:] == "]":
|
||||
arrayValue, ok := encValue.([]interface{})
|
||||
if !ok {
|
||||
return dataMismatchError(encType, encValue)
|
||||
}
|
||||
|
||||
parsedType := strings.Split(encType, "[")[0]
|
||||
if schema[parsedType] != nil {
|
||||
for _, item := range arrayValue {
|
||||
mapValue, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return dataMismatchError(parsedType, item)
|
||||
}
|
||||
|
||||
err := patchTxData(mapValue, schema, parsedType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, item := range arrayValue {
|
||||
converted, err := handleConversion(parsedType, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arrayValue[i] = converted
|
||||
}
|
||||
}
|
||||
case schema[encType] != nil:
|
||||
mapValue, ok := encValue.(map[string]interface{})
|
||||
if !ok {
|
||||
return dataMismatchError(encType, encValue)
|
||||
}
|
||||
err := patchTxData(mapValue, schema, encType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
converted, err := handleConversion(encType, encValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[field.Name] = converted
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleConversion(encType string, encValue any) (any, error) {
|
||||
if encType == bytesStr {
|
||||
// Protobuf encodes byte strings in base64
|
||||
if v, ok := encValue.(string); ok {
|
||||
return base64.StdEncoding.DecodeString(v)
|
||||
}
|
||||
}
|
||||
return encValue, nil
|
||||
}
|
||||
|
||||
// dataMismatchError generates an error for a mismatch between
|
||||
// the provided type and data
|
||||
func dataMismatchError(encType string, encValue any) error {
|
||||
return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType)
|
||||
}
|
||||
|
477
ethereum/eip712/eip712_test.go
Normal file
477
ethereum/eip712/eip712_test.go
Normal file
@ -0,0 +1,477 @@
|
||||
package eip712_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
registrytypes "github.com/cerc-io/laconicd/x/registry/types"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cerc-io/laconicd/app"
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cerc-io/laconicd/encoding"
|
||||
"github.com/cerc-io/laconicd/ethereum/eip712"
|
||||
)
|
||||
|
||||
// Unit tests for single-signer EIP-712 signature verification. Multi-signer verification tests are included
|
||||
// in ante_test.go.
|
||||
|
||||
type EIP712TestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
config params.EncodingConfig
|
||||
clientCtx client.Context
|
||||
}
|
||||
|
||||
func TestEIP712TestSuite(t *testing.T) {
|
||||
suite.Run(t, &EIP712TestSuite{})
|
||||
}
|
||||
|
||||
// Set up test env to replicate prod. environment
|
||||
func (suite *EIP712TestSuite) SetupTest() {
|
||||
suite.config = encoding.MakeConfig(app.ModuleBasics)
|
||||
suite.clientCtx = client.Context{}.WithTxConfig(suite.config.TxConfig)
|
||||
|
||||
sdk.GetConfig().SetBech32PrefixForAccount("ethm", "")
|
||||
eip712.SetEncodingConfig(suite.config)
|
||||
}
|
||||
|
||||
// Helper to create random test addresses for messages
|
||||
func (suite *EIP712TestSuite) createTestAddress() sdk.AccAddress {
|
||||
privkey, _ := ethsecp256k1.GenerateKey()
|
||||
key, err := privkey.ToECDSA()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
return addr.Bytes()
|
||||
}
|
||||
|
||||
// Helper to create random keypair for signing + verification
|
||||
func (suite *EIP712TestSuite) createTestKeyPair() (*ethsecp256k1.PrivKey, *ethsecp256k1.PubKey) {
|
||||
privKey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
pubKey := ðsecp256k1.PubKey{
|
||||
Key: privKey.PubKey().Bytes(),
|
||||
}
|
||||
suite.Require().Implements((*cryptotypes.PubKey)(nil), pubKey)
|
||||
|
||||
return privKey, pubKey
|
||||
}
|
||||
|
||||
// Helper to create instance of sdk.Coins[] with single coin
|
||||
func (suite *EIP712TestSuite) makeCoins(denom string, amount math.Int) sdk.Coins {
|
||||
return sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
denom,
|
||||
amount,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) TestEIP712SignatureVerification() {
|
||||
suite.SetupTest()
|
||||
|
||||
signModes := []signing.SignMode{
|
||||
signing.SignMode_SIGN_MODE_DIRECT,
|
||||
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
}
|
||||
|
||||
// Fixed test address
|
||||
testAddress := suite.createTestAddress()
|
||||
|
||||
testCases := []struct {
|
||||
title string
|
||||
chainId string
|
||||
fee txtypes.Fee
|
||||
memo string
|
||||
msgs []sdk.Msg
|
||||
accountNumber uint64
|
||||
sequence uint64
|
||||
timeoutHeight uint64
|
||||
expectSuccess bool
|
||||
}{
|
||||
{
|
||||
title: "Succeeds - Standard MsgSend",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
banktypes.NewMsgSend(
|
||||
suite.createTestAddress(),
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(1)),
|
||||
),
|
||||
},
|
||||
accountNumber: 8,
|
||||
sequence: 5,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Succeeds - Standard MsgVote",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
govtypes.NewMsgVote(
|
||||
suite.createTestAddress(),
|
||||
5,
|
||||
govtypes.OptionNo,
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Succeeds - Standard MsgDelegate",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
stakingtypes.NewMsgDelegate(
|
||||
suite.createTestAddress(),
|
||||
sdk.ValAddress(suite.createTestAddress()),
|
||||
suite.makeCoins("photon", math.NewInt(1))[0],
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Succeeds - Standard MsgWithdrawDelegationReward",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
distributiontypes.NewMsgWithdrawDelegatorReward(
|
||||
suite.createTestAddress(),
|
||||
sdk.ValAddress(suite.createTestAddress()),
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Succeeds - Two Single-Signer MsgDelegate",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
stakingtypes.NewMsgDelegate(
|
||||
testAddress,
|
||||
sdk.ValAddress(suite.createTestAddress()),
|
||||
suite.makeCoins("photon", math.NewInt(1))[0],
|
||||
),
|
||||
stakingtypes.NewMsgDelegate(
|
||||
testAddress,
|
||||
sdk.ValAddress(suite.createTestAddress()),
|
||||
suite.makeCoins("photon", math.NewInt(5))[0],
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Fails - Two MsgVotes with Different Signers",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
govtypes.NewMsgVote(
|
||||
suite.createTestAddress(),
|
||||
5,
|
||||
govtypes.OptionNo,
|
||||
),
|
||||
govtypes.NewMsgVote(
|
||||
suite.createTestAddress(),
|
||||
25,
|
||||
govtypes.OptionAbstain,
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: false,
|
||||
},
|
||||
{
|
||||
title: "Fails - Empty transaction",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: false,
|
||||
},
|
||||
{
|
||||
title: "Fails - Single-Signer MsgSend + MsgVote",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
govtypes.NewMsgVote(
|
||||
testAddress,
|
||||
5,
|
||||
govtypes.OptionNo,
|
||||
),
|
||||
banktypes.NewMsgSend(
|
||||
testAddress,
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(50)),
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: false,
|
||||
},
|
||||
{
|
||||
title: "Fails - Invalid ChainID",
|
||||
chainId: "invalidchainid",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
govtypes.NewMsgVote(
|
||||
suite.createTestAddress(),
|
||||
5,
|
||||
govtypes.OptionNo,
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: false,
|
||||
},
|
||||
{
|
||||
title: "Fails - Includes TimeoutHeight",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
govtypes.NewMsgVote(
|
||||
suite.createTestAddress(),
|
||||
5,
|
||||
govtypes.OptionNo,
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
timeoutHeight: 1000,
|
||||
expectSuccess: false,
|
||||
},
|
||||
{
|
||||
title: "Fails - Single Message / Multi-Signer",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 20000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
banktypes.NewMsgMultiSend(
|
||||
[]banktypes.Input{
|
||||
banktypes.NewInput(
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(50)),
|
||||
),
|
||||
banktypes.NewInput(
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(50)),
|
||||
),
|
||||
},
|
||||
[]banktypes.Output{
|
||||
banktypes.NewOutput(
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(50)),
|
||||
),
|
||||
banktypes.NewOutput(
|
||||
suite.createTestAddress(),
|
||||
suite.makeCoins("photon", math.NewInt(50)),
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: false,
|
||||
},
|
||||
|
||||
// test laconic registry messages
|
||||
{
|
||||
title: "Succeeds - Standard MsgSetName",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 100000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
registrytypes.NewMsgSetName(
|
||||
"testcrn",
|
||||
"testcid",
|
||||
suite.createTestAddress(),
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
{
|
||||
title: "Succeeds - Standard MsgSetRecord",
|
||||
fee: txtypes.Fee{
|
||||
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
|
||||
GasLimit: 100000,
|
||||
},
|
||||
memo: "",
|
||||
msgs: []sdk.Msg{
|
||||
registrytypes.NewMsgSetRecord(
|
||||
registrytypes.Payload{
|
||||
Record: ®istrytypes.Record{
|
||||
Attributes: []byte("test attributes"),
|
||||
},
|
||||
Signatures: []registrytypes.Signature{
|
||||
{
|
||||
Sig: "fake sig",
|
||||
PubKey: "fake pubkey",
|
||||
},
|
||||
},
|
||||
},
|
||||
"testbondid",
|
||||
suite.createTestAddress(),
|
||||
),
|
||||
},
|
||||
accountNumber: 25,
|
||||
sequence: 78,
|
||||
expectSuccess: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, signMode := range signModes {
|
||||
suite.Run(tc.title, func() {
|
||||
privKey, pubKey := suite.createTestKeyPair()
|
||||
|
||||
// Init tx builder
|
||||
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
|
||||
|
||||
// Set gas and fees
|
||||
txBuilder.SetGasLimit(tc.fee.GasLimit)
|
||||
txBuilder.SetFeeAmount(tc.fee.Amount)
|
||||
|
||||
// Set messages
|
||||
err := txBuilder.SetMsgs(tc.msgs...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Set memo
|
||||
txBuilder.SetMemo(tc.memo)
|
||||
|
||||
// Prepare signature field
|
||||
txSigData := signing.SingleSignatureData{
|
||||
SignMode: signMode,
|
||||
Signature: nil,
|
||||
}
|
||||
txSig := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &txSigData,
|
||||
Sequence: tc.sequence,
|
||||
}
|
||||
|
||||
err = txBuilder.SetSignatures([]signing.SignatureV2{txSig}...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
chainId := "ethermint_9000-1"
|
||||
if tc.chainId != "" {
|
||||
chainId = tc.chainId
|
||||
}
|
||||
|
||||
if tc.timeoutHeight != 0 {
|
||||
txBuilder.SetTimeoutHeight(tc.timeoutHeight)
|
||||
}
|
||||
|
||||
// Declare signerData
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: chainId,
|
||||
AccountNumber: tc.accountNumber,
|
||||
Sequence: tc.sequence,
|
||||
PubKey: pubKey,
|
||||
Address: sdk.MustBech32ifyAddressBytes("ethm", pubKey.Bytes()),
|
||||
}
|
||||
|
||||
bz, err := suite.clientCtx.TxConfig.SignModeHandler().GetSignBytes(
|
||||
signMode,
|
||||
signerData,
|
||||
txBuilder.GetTx(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.verifyEIP712SignatureVerification(tc.expectSuccess, *privKey, *pubKey, bz)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the payload passes signature verification if signed as its EIP-712 representation.
|
||||
func (suite *EIP712TestSuite) verifyEIP712SignatureVerification(expectedSuccess bool, privKey ethsecp256k1.PrivKey, pubKey ethsecp256k1.PubKey, signBytes []byte) {
|
||||
// Convert to EIP712 bytes and sign
|
||||
eip712Bytes, err := eip712.GetEIP712BytesForMsg(signBytes)
|
||||
if !expectedSuccess {
|
||||
// Expect failure generating EIP-712 bytes
|
||||
suite.Require().Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err := privKey.Sign(eip712Bytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify against original payload bytes. This should pass, even though it is not
|
||||
// the original message that was signed.
|
||||
res := pubKey.VerifySignature(signBytes, sig)
|
||||
suite.Require().True(res)
|
||||
|
||||
// Verify against the signed EIP-712 bytes. This should pass, since it is the message signed.
|
||||
res = pubKey.VerifySignature(eip712Bytes, sig)
|
||||
suite.Require().True(res)
|
||||
|
||||
// Verify against random bytes to ensure it does not pass unexpectedly (sanity check).
|
||||
randBytes := make([]byte, len(signBytes))
|
||||
copy(randBytes, signBytes)
|
||||
// Change the first element of signBytes to a different value
|
||||
randBytes[0] = (signBytes[0] + 10) % 128
|
||||
res = pubKey.VerifySignature(randBytes, sig)
|
||||
suite.Require().False(res)
|
||||
}
|
299
ethereum/eip712/encoding.go
Normal file
299
ethereum/eip712/encoding.go
Normal file
@ -0,0 +1,299 @@
|
||||
package eip712
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txTypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
type aminoMessage struct {
|
||||
Type string `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
var (
|
||||
protoCodec codec.ProtoCodecMarshaler
|
||||
aminoCodec *codec.LegacyAmino
|
||||
)
|
||||
|
||||
// SetEncodingConfig set the encoding config to the singleton codecs (Amino and Protobuf).
|
||||
// The process of unmarshaling SignDoc bytes into a SignDoc object requires having a codec
|
||||
// populated with all relevant message types. As a result, we must call this method on app
|
||||
// initialization with the app's encoding config.
|
||||
func SetEncodingConfig(cfg params.EncodingConfig) {
|
||||
aminoCodec = cfg.Amino
|
||||
protoCodec = codec.NewProtoCodec(cfg.InterfaceRegistry)
|
||||
}
|
||||
|
||||
// Get the EIP-712 object bytes for the given SignDoc bytes by first decoding the bytes into
|
||||
// an EIP-712 object, then hashing the EIP-712 object to create the bytes to be signed.
|
||||
// See https://eips.ethereum.org/EIPS/eip-712 for more.
|
||||
func GetEIP712BytesForMsg(signDocBytes []byte) ([]byte, error) {
|
||||
typedData, err := GetEIP712TypedDataForMsg(signDocBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, rawData, err := apitypes.TypedDataAndHash(typedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get EIP-712 object bytes: %w", err)
|
||||
}
|
||||
|
||||
return []byte(rawData), nil
|
||||
}
|
||||
|
||||
// GetEIP712TypedDataForMsg returns the EIP-712 TypedData representation for either
|
||||
// Amino or Protobuf encoded signature doc bytes.
|
||||
func GetEIP712TypedDataForMsg(signDocBytes []byte) (apitypes.TypedData, error) {
|
||||
// Attempt to decode as both Amino and Protobuf since the message format is unknown.
|
||||
// If either decode works, we can move forward with the corresponding typed data.
|
||||
typedDataAmino, errAmino := decodeAminoSignDoc(signDocBytes)
|
||||
if errAmino == nil && isValidEIP712Payload(typedDataAmino) {
|
||||
return typedDataAmino, nil
|
||||
}
|
||||
typedDataProtobuf, errProtobuf := decodeProtobufSignDoc(signDocBytes)
|
||||
if errProtobuf == nil && isValidEIP712Payload(typedDataProtobuf) {
|
||||
return typedDataProtobuf, nil
|
||||
}
|
||||
|
||||
return apitypes.TypedData{}, fmt.Errorf("could not decode sign doc as either Amino or Protobuf.\n amino: %v\n protobuf: %v", errAmino, errProtobuf)
|
||||
}
|
||||
|
||||
// isValidEIP712Payload ensures that the given TypedData does not contain empty fields from
|
||||
// an improper initialization.
|
||||
func isValidEIP712Payload(typedData apitypes.TypedData) bool {
|
||||
return len(typedData.Message) != 0 && len(typedData.Types) != 0 && typedData.PrimaryType != "" && typedData.Domain != apitypes.TypedDataDomain{}
|
||||
}
|
||||
|
||||
// decodeAminoSignDoc attempts to decode the provided sign doc (bytes) as an Amino payload
|
||||
// and returns a signable EIP-712 TypedData object.
|
||||
func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
|
||||
// Ensure codecs have been initialized
|
||||
if err := validateCodecInit(); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
var aminoDoc legacytx.StdSignDoc
|
||||
if err := aminoCodec.UnmarshalJSON(signDocBytes, &aminoDoc); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
var fees legacytx.StdFee
|
||||
if err := aminoCodec.UnmarshalJSON(aminoDoc.Fee, &fees); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
// Validate payload messages
|
||||
msgs := make([]sdk.Msg, len(aminoDoc.Msgs))
|
||||
for i, jsonMsg := range aminoDoc.Msgs {
|
||||
var m sdk.Msg
|
||||
if err := aminoCodec.UnmarshalJSON(jsonMsg, &m); err != nil {
|
||||
return apitypes.TypedData{}, fmt.Errorf("failed to unmarshal sign doc message: %w", err)
|
||||
}
|
||||
msgs[i] = m
|
||||
}
|
||||
|
||||
if err := validatePayloadMessages(msgs); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
// Use first message for fee payer and type inference
|
||||
msg := msgs[0]
|
||||
|
||||
// By convention, the fee payer is the first address in the list of signers.
|
||||
feePayer := msg.GetSigners()[0]
|
||||
feeDelegation := &FeeDelegationOptions{
|
||||
FeePayer: feePayer,
|
||||
}
|
||||
|
||||
chainID, err := ethermint.ParseChainID(aminoDoc.ChainID)
|
||||
if err != nil {
|
||||
return apitypes.TypedData{}, errors.New("invalid chain ID passed as argument")
|
||||
}
|
||||
|
||||
typedData, err := WrapTxToTypedData(
|
||||
protoCodec,
|
||||
chainID.Uint64(),
|
||||
msg,
|
||||
signDocBytes,
|
||||
feeDelegation,
|
||||
)
|
||||
if err != nil {
|
||||
return apitypes.TypedData{}, fmt.Errorf("could not convert to EIP712 representation: %w", err)
|
||||
}
|
||||
|
||||
return typedData, nil
|
||||
}
|
||||
|
||||
// decodeProtobufSignDoc attempts to decode the provided sign doc (bytes) as a Protobuf payload
|
||||
// and returns a signable EIP-712 TypedData object.
|
||||
func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
|
||||
// Ensure codecs have been initialized
|
||||
if err := validateCodecInit(); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
signDoc := &txTypes.SignDoc{}
|
||||
if err := signDoc.Unmarshal(signDocBytes); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
authInfo := &txTypes.AuthInfo{}
|
||||
if err := authInfo.Unmarshal(signDoc.AuthInfoBytes); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
body := &txTypes.TxBody{}
|
||||
if err := body.Unmarshal(signDoc.BodyBytes); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
// Until support for these fields is added, throw an error at their presence
|
||||
if body.TimeoutHeight != 0 || len(body.ExtensionOptions) != 0 || len(body.NonCriticalExtensionOptions) != 0 {
|
||||
return apitypes.TypedData{}, errors.New("body contains unsupported fields: TimeoutHeight, ExtensionOptions, or NonCriticalExtensionOptions")
|
||||
}
|
||||
|
||||
if len(authInfo.SignerInfos) != 1 {
|
||||
return apitypes.TypedData{}, fmt.Errorf("invalid number of signer infos provided, expected 1 got %v", len(authInfo.SignerInfos))
|
||||
}
|
||||
|
||||
// Validate payload messages
|
||||
msgs := make([]sdk.Msg, len(body.Messages))
|
||||
for i, protoMsg := range body.Messages {
|
||||
var m sdk.Msg
|
||||
if err := protoCodec.UnpackAny(protoMsg, &m); err != nil {
|
||||
return apitypes.TypedData{}, fmt.Errorf("could not unpack message object with error %w", err)
|
||||
}
|
||||
msgs[i] = m
|
||||
}
|
||||
|
||||
if err := validatePayloadMessages(msgs); err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
// Use first message for fee payer and type inference
|
||||
msg := msgs[0]
|
||||
|
||||
signerInfo := authInfo.SignerInfos[0]
|
||||
|
||||
chainID, err := ethermint.ParseChainID(signDoc.ChainId)
|
||||
if err != nil {
|
||||
return apitypes.TypedData{}, fmt.Errorf("invalid chain ID passed as argument: %w", err)
|
||||
}
|
||||
|
||||
stdFee := &legacytx.StdFee{
|
||||
Amount: authInfo.Fee.Amount,
|
||||
Gas: authInfo.Fee.GasLimit,
|
||||
}
|
||||
|
||||
feePayer := msg.GetSigners()[0]
|
||||
feeDelegation := &FeeDelegationOptions{
|
||||
FeePayer: feePayer,
|
||||
}
|
||||
|
||||
tip := authInfo.Tip
|
||||
|
||||
// WrapTxToTypedData expects the payload as an Amino Sign Doc
|
||||
signBytes := legacytx.StdSignBytes(
|
||||
signDoc.ChainId,
|
||||
signDoc.AccountNumber,
|
||||
signerInfo.Sequence,
|
||||
body.TimeoutHeight,
|
||||
*stdFee,
|
||||
msgs,
|
||||
body.Memo,
|
||||
tip,
|
||||
)
|
||||
|
||||
typedData, err := WrapTxToTypedData(
|
||||
protoCodec,
|
||||
chainID.Uint64(),
|
||||
msg,
|
||||
signBytes,
|
||||
feeDelegation,
|
||||
)
|
||||
if err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
return typedData, nil
|
||||
}
|
||||
|
||||
// validateCodecInit ensures that both Amino and Protobuf encoding codecs have been set on app init,
|
||||
// so the module does not panic if either codec is not found.
|
||||
func validateCodecInit() error {
|
||||
if aminoCodec == nil || protoCodec == nil {
|
||||
return errors.New("missing codec: codecs have not been properly initialized using SetEncodingConfig")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validatePayloadMessages ensures that the transaction messages can be represented in an EIP-712
|
||||
// encoding by checking that messages exist, are of the same type, and share a single signer.
|
||||
func validatePayloadMessages(msgs []sdk.Msg) error {
|
||||
if len(msgs) == 0 {
|
||||
return errors.New("unable to build EIP-712 payload: transaction does contain any messages")
|
||||
}
|
||||
|
||||
var msgType string
|
||||
var msgSigner sdk.AccAddress
|
||||
|
||||
for i, m := range msgs {
|
||||
t, err := getMsgType(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(m.GetSigners()) != 1 {
|
||||
return errors.New("unable to build EIP-712 payload: expect exactly 1 signer")
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
msgType = t
|
||||
msgSigner = m.GetSigners()[0]
|
||||
continue
|
||||
}
|
||||
|
||||
if t != msgType {
|
||||
return errors.New("unable to build EIP-712 payload: different types of messages detected")
|
||||
}
|
||||
|
||||
if !msgSigner.Equals(m.GetSigners()[0]) {
|
||||
return errors.New("unable to build EIP-712 payload: multiple signers detected")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMsgType returns the message type prefix for the given Cosmos SDK Msg
|
||||
func getMsgType(msg sdk.Msg) (string, error) {
|
||||
jsonBytes, err := aminoCodec.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var jsonMsg aminoMessage
|
||||
if err := json.Unmarshal(jsonBytes, &jsonMsg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Verify Type was successfully filled in
|
||||
if jsonMsg.Type == "" {
|
||||
return "", errors.New("could not decode message: type is missing")
|
||||
}
|
||||
|
||||
return jsonMsg.Type, nil
|
||||
}
|
106
go.mod
106
go.mod
@ -1,20 +1,24 @@
|
||||
module github.com/cerc-io/laconicd
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
cosmossdk.io/math v1.0.0-beta.3
|
||||
github.com/99designs/gqlgen v0.17.20
|
||||
cosmossdk.io/errors v1.0.0-beta.7
|
||||
cosmossdk.io/math v1.0.0-beta.4
|
||||
github.com/99designs/gqlgen v0.17.22
|
||||
github.com/armon/go-metrics v0.4.1
|
||||
github.com/btcsuite/btcd v0.22.1
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||
github.com/cosmos/cosmos-sdk v0.46.3
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.1
|
||||
github.com/cosmos/cosmos-sdk v0.46.7
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/cosmos/ibc-go/v5 v5.0.1
|
||||
github.com/cosmos/gogoproto v1.4.3
|
||||
github.com/cosmos/ibc-go/v5 v5.2.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/deckarep/golang-set v1.8.0
|
||||
github.com/ethereum/go-ethereum v1.10.19
|
||||
github.com/ethereum/go-ethereum v1.10.26
|
||||
github.com/gibson042/canonicaljson-go v1.0.3
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/gogo/protobuf v1.3.3
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/gorilla/mux v1.8.0
|
||||
@ -23,41 +27,37 @@ require (
|
||||
github.com/holiman/uint256 v1.2.1
|
||||
github.com/improbable-eng/grpc-web v0.15.0
|
||||
github.com/ipfs/go-cid v0.3.2
|
||||
github.com/ipfs/go-ipld-cbor v0.0.6
|
||||
github.com/ipld/go-ipld-prime v0.18.0
|
||||
github.com/ipld/go-ipld-prime v0.19.0
|
||||
github.com/miguelmota/go-ethereum-hdwallet v0.1.1
|
||||
github.com/multiformats/go-multihash v0.2.1
|
||||
github.com/onsi/ginkgo/v2 v2.2.0
|
||||
github.com/onsi/gomega v1.20.2
|
||||
github.com/onsi/ginkgo/v2 v2.5.1
|
||||
github.com/onsi/gomega v1.24.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/regen-network/cosmos-proto v0.3.1
|
||||
github.com/rs/cors v1.8.2
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/viper v1.13.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/viper v1.14.0
|
||||
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tendermint/tendermint v0.34.22
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tendermint/tendermint v0.34.24
|
||||
github.com/tendermint/tm-db v0.6.7
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/vektah/gqlparser/v2 v2.5.1
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/text v0.4.0
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6
|
||||
google.golang.org/grpc v1.50.1
|
||||
google.golang.org/protobuf v1.28.1
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/text v0.7.0
|
||||
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c
|
||||
google.golang.org/grpc v1.51.0
|
||||
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.105.0 // indirect
|
||||
cloud.google.com/go/compute v1.12.1 // indirect
|
||||
cloud.google.com/go/compute v1.13.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.1 // indirect
|
||||
cloud.google.com/go/iam v0.7.0 // indirect
|
||||
cloud.google.com/go/iam v0.8.0 // indirect
|
||||
cloud.google.com/go/storage v1.27.0 // indirect
|
||||
cosmossdk.io/errors v1.0.0-beta.7 // indirect
|
||||
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
|
||||
@ -65,11 +65,11 @@ require (
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/Workiva/go-datastructures v1.0.53 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.40.45 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.122 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
@ -77,13 +77,11 @@ require (
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
|
||||
github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect
|
||||
github.com/confio/ics23/go v0.7.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.4 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-alpha7 // indirect
|
||||
github.com/confio/ics23/go v0.9.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/gorocksdb v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v0.19.3 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
|
||||
github.com/cosmos/ledger-go v0.9.2 // indirect
|
||||
github.com/cosmos/iavl v0.19.4 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect
|
||||
github.com/creachadair/taskgroup v0.3.2 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
@ -97,11 +95,12 @@ require (
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||
github.com/go-kit/kit v0.12.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
@ -115,14 +114,14 @@ require (
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.6.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/gtank/ristretto255 v0.1.2 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-getter v1.6.1 // indirect
|
||||
github.com/hashicorp/go-getter v1.7.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
@ -132,14 +131,12 @@ require (
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/huin/goupnp v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/ipfs/go-block-format v0.0.2 // indirect
|
||||
github.com/ipfs/go-ipfs-util v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipld-format v0.0.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/kevinburke/go-bindata v3.24.0+incompatible // indirect
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/lib/pq v1.10.6 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
@ -153,13 +150,14 @@ require (
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.0.3 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
||||
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
@ -170,9 +168,10 @@ require (
|
||||
github.com/prometheus/client_golang v1.12.2 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.34.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/regen-network/cosmos-proto v0.3.1 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/zerolog v1.27.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
@ -181,7 +180,7 @@ require (
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.4.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/tendermint/btcd v0.1.1 // indirect
|
||||
@ -189,19 +188,19 @@ require (
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.8 // indirect
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 // indirect
|
||||
github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/zondax/hid v0.9.1 // indirect
|
||||
github.com/zondax/ledger-go v0.14.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/oauth2 v0.1.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/term v0.2.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.102.0 // indirect
|
||||
google.golang.org/api v0.103.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
@ -211,10 +210,13 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
// use cosmos keyring
|
||||
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
|
||||
|
||||
// ics23 patch for dragonberry
|
||||
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
|
||||
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
||||
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
|
||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0
|
||||
// use cosmos flavored protobufs
|
||||
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
|
||||
)
|
||||
|
16
gometalinter.json
Normal file
16
gometalinter.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"Linters": {
|
||||
"vet": "go tool vet -composites=false :PATH:LINE:MESSAGE"
|
||||
},
|
||||
"Enable": [
|
||||
"golint",
|
||||
"vet",
|
||||
"ineffassign",
|
||||
"unparam",
|
||||
"unconvert",
|
||||
"misspell"
|
||||
],
|
||||
"Deadline": "500s",
|
||||
"Vendor": true,
|
||||
"Cyclo": 11
|
||||
}
|
269
gomod2nix.toml
269
gomod2nix.toml
@ -2,26 +2,32 @@ schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."cloud.google.com/go"]
|
||||
version = "v0.102.1"
|
||||
hash = "sha256-rv3MHFvIrN6AcrmWcWGSQwgaZLbjJMmJFi3bsfHoiO4="
|
||||
version = "v0.105.0"
|
||||
hash = "sha256-2nYtHjuN9ghGcM6aPlOxyNNarHebHtj0Xec48sWwdaI="
|
||||
[mod."cloud.google.com/go/compute"]
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-g+n7L36LC+NP4KaiEu9fCMn6S9fkxjp4PCLOp/oXV38="
|
||||
version = "v1.12.1"
|
||||
hash = "sha256-mMsdL+6R1KuFeAFqEJv8oq/cmRc9hSluEisCpGP0GTc="
|
||||
[mod."cloud.google.com/go/compute/metadata"]
|
||||
version = "v0.2.1"
|
||||
hash = "sha256-gqjkb6y5POkTWXGQtCvgygvXqoLkESB+cykKp7Mp3jI="
|
||||
[mod."cloud.google.com/go/iam"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-sPtj6QPPC/QrkDXJW4Y7rMIHk5l1y9XV/DgM47eEtvg="
|
||||
version = "v0.7.0"
|
||||
hash = "sha256-2UTJ/BbZ+YMGJhyo3HRPUYVDXW6+B5znFdqiutboYtA="
|
||||
[mod."cloud.google.com/go/storage"]
|
||||
version = "v1.22.1"
|
||||
hash = "sha256-YZt05wru0kacyi6moTgvocavActKpXx7FlLh+o0HGDs="
|
||||
version = "v1.27.0"
|
||||
hash = "sha256-V4B6A1Ms8cemB5Cs6nAtUe1N1ldaI9oqTdzGU2FUhrc="
|
||||
[mod."cosmossdk.io/errors"]
|
||||
version = "v1.0.0-beta.7"
|
||||
hash = "sha256-XblGvIx6Wvvq6wggXjp+KbeJGXoe7AZH7hXEdauCezU="
|
||||
[mod."cosmossdk.io/math"]
|
||||
version = "v1.0.0-beta.3"
|
||||
hash = "sha256-lTQ27ZlL+kWlc+S//sJmyiOwaf9qS+YLv61I4OXi9XE="
|
||||
version = "v1.0.0-beta.4"
|
||||
hash = "sha256-UYdq/46EubyjxkldGike8FlwJLWGCB576VB7th285ao="
|
||||
[mod."filippo.io/edwards25519"]
|
||||
version = "v1.0.0-rc.1"
|
||||
hash = "sha256-3DboBqby2ejRU33FG96Z8JF5AJ8HP2rC/v++VyoQ2LQ="
|
||||
[mod."github.com/99designs/gqlgen"]
|
||||
version = "v0.17.22"
|
||||
hash = "sha256-Cu3nAqFbsV7DH+4UZvX83ScxJtPlvOyD7ptUPXnwGng="
|
||||
[mod."github.com/99designs/keyring"]
|
||||
version = "v1.1.7-0.20210622111912-ef00f8ac3d76"
|
||||
hash = "sha256-oCalyOZWegRgKHZ1GvYHNkrMKh51j8cOE/K4yBPM5Oc="
|
||||
@ -38,6 +44,9 @@ schema = 3
|
||||
[mod."github.com/Workiva/go-datastructures"]
|
||||
version = "v1.0.53"
|
||||
hash = "sha256-W6qOvqu8sokMlZrpOF1SWG138H0/BotywKNLlDF8Zug="
|
||||
[mod."github.com/agnivade/levenshtein"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-tgv2s8XIcWjscv1pP87TOIUV4vADT2px1Lo/MQCTBjM="
|
||||
[mod."github.com/armon/go-metrics"]
|
||||
version = "v0.4.1"
|
||||
hash = "sha256-usxTUHA0QQMdM6sHi2z51nmnEKMbA0qUilxJFpWHlYE="
|
||||
@ -57,8 +66,8 @@ schema = 3
|
||||
version = "v0.22.1"
|
||||
hash = "sha256-hBU+roIELcmbW2Gz7eGZzL9qNA1bakq5wNxqCgs4TKc="
|
||||
[mod."github.com/btcsuite/btcd/btcec/v2"]
|
||||
version = "v2.2.0"
|
||||
hash = "sha256-cwoIqi0SiAaGEBtuQKip9OVirKIeJw5fUoWo7x/xfOc="
|
||||
version = "v2.3.2"
|
||||
hash = "sha256-natWs+yIAuD1UI07iZtjPilroQLfXizFn3lNOiOT83U="
|
||||
[mod."github.com/btcsuite/btcd/chaincfg/chainhash"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-vix0j/KGNvoKjhlKgVeSLY6un2FHeIEoZWMC4z3yvZ4="
|
||||
@ -84,35 +93,36 @@ schema = 3
|
||||
version = "v0.7.9"
|
||||
hash = "sha256-ZWIXIXcHGjeCNgMrpXymry8/8esDDauGFfF/+gEoO1Y="
|
||||
[mod."github.com/confio/ics23/go"]
|
||||
version = "v0.7.0"
|
||||
hash = "sha256-FjUQfDKtX+/13+UjJzbYPAkgM4GPZ0vk+dKz7/iRetw="
|
||||
version = "v0.8.0"
|
||||
hash = "sha256-mFKxFHp7RaE1L5NdtH9H9px2Cy6I5uOZVnFOgE8Nrew="
|
||||
replaced = "github.com/cosmos/cosmos-sdk/ics23/go"
|
||||
[mod."github.com/cosmos/btcutil"]
|
||||
version = "v1.0.4"
|
||||
hash = "sha256-JvcBXBdjdmnaW/nyf/tw/uaOAGn1b78yxrtl2/Rs3kA="
|
||||
version = "v1.0.5"
|
||||
hash = "sha256-t572Sr5iiHcuMKLMWa2i+LBAt192oa+G1oA371tG/eI="
|
||||
[mod."github.com/cosmos/cosmos-proto"]
|
||||
version = "v1.0.0-alpha7"
|
||||
hash = "sha256-2wCH+toTF2A6MfFjOa13muEH5oBCcxAhZEqirNOrBA0="
|
||||
version = "v1.0.0-beta.1"
|
||||
hash = "sha256-oATkuj+fM5eBn+ywO+w/tL0AFSIEkx0J3Yz+VhVe0QA="
|
||||
[mod."github.com/cosmos/cosmos-sdk"]
|
||||
version = "v0.46.2-0.20220920160224-9454b97ccb77"
|
||||
hash = "sha256-zwOsdcoPbEzn+q/UmW6/CauW65V6a4z/7k8DXPCvKb0="
|
||||
version = "v0.46.7"
|
||||
hash = "sha256-54DCF8lrnA1oUmBJlbUlWXOP5UbenRInUROn5P5I9qI="
|
||||
[mod."github.com/cosmos/go-bip39"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-Qm2aC2vaS8tjtMUbHmlBSagOSqbduEEDwc51qvQaBmA="
|
||||
[mod."github.com/cosmos/gogoproto"]
|
||||
version = "v1.4.3"
|
||||
hash = "sha256-Y/NL76ay/oAl8mS3skkK5ula0/xudqbwW1o22lZjKRg="
|
||||
[mod."github.com/cosmos/gorocksdb"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-209TcVuXc5s/TcOvNlaQ1HEJAUDTEK3nxPhs+d8TEcY="
|
||||
[mod."github.com/cosmos/iavl"]
|
||||
version = "v0.19.2-0.20220916140702-9b6be3095313"
|
||||
hash = "sha256-49xr/7/4L1wZ45eW5lE7C4SlcbGf4gBY+662SfJiWPQ="
|
||||
version = "v0.19.4"
|
||||
hash = "sha256-EmpRZ48pjPFq/fIHneut9Vyo5QJATfb3ZO7KzWnqs9g="
|
||||
[mod."github.com/cosmos/ibc-go/v5"]
|
||||
version = "v5.0.0"
|
||||
hash = "sha256-sDZdmuGohaaBF7bxrjo9PWJnmoF+VOkjySYhsFixPz4="
|
||||
version = "v5.2.0"
|
||||
hash = "sha256-Gfqhdz9ZKEgb7LCkHiXCwYZYUYluQ+vMew/GkssfVj8="
|
||||
[mod."github.com/cosmos/ledger-cosmos-go"]
|
||||
version = "v0.11.1"
|
||||
hash = "sha256-yli+VvVtZmHo2LPvCY6lYVUfcCDn3sBLDL+a8KIlqDA="
|
||||
[mod."github.com/cosmos/ledger-go"]
|
||||
version = "v0.9.2"
|
||||
hash = "sha256-0YI+0A6wFBjiebBwzWOQjkgXkTakCrf7Gjg2xEaQdoM="
|
||||
version = "v0.12.1"
|
||||
hash = "sha256-9+nr+/r4MyiogddS0JcXOuriPqXP4nxln8ts+mYQRcg="
|
||||
[mod."github.com/creachadair/taskgroup"]
|
||||
version = "v0.3.2"
|
||||
hash = "sha256-Y261IO/d9xjV0UScqHvo31broxvnKn4IQQC9Mu6jNkE="
|
||||
@ -156,17 +166,20 @@ schema = 3
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-k1DYvCqO3BKNcGEve/nMW0RxzMkK2tGfXbUbycqcVSo="
|
||||
[mod."github.com/ethereum/go-ethereum"]
|
||||
version = "v1.10.19"
|
||||
hash = "sha256-7FPnTGcCb8Xd1QVR+6PmGTaHdTY1mm/8osFTW1JLuG8="
|
||||
version = "v1.10.26"
|
||||
hash = "sha256-gkMEwJ4rOgn12amD4QpZ4th/10uyTTeoFmpseuKDQPs="
|
||||
[mod."github.com/felixge/httpsnoop"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-TNXnnC/ZGNY9lInAcES1cBGqIdEljKuh5LH/khVFjVk="
|
||||
[mod."github.com/fsnotify/fsnotify"]
|
||||
version = "v1.5.4"
|
||||
hash = "sha256-iPuInpDAJvDeSTr6to713J/fTOWdQIWlLXWAuWIs2Bw="
|
||||
version = "v1.6.0"
|
||||
hash = "sha256-DQesOCweQPEwmAn6s7DCP/Dwy8IypC+osbpfsvpkdP0="
|
||||
[mod."github.com/gballet/go-libpcsclite"]
|
||||
version = "v0.0.0-20190607065134-2772fd86a8ff"
|
||||
hash = "sha256-Nr5ocU9s1F2Lhx/Zq6/nIo+KkKEqMjDYOEs3yWRC48g="
|
||||
[mod."github.com/gibson042/canonicaljson-go"]
|
||||
version = "v1.0.3"
|
||||
hash = "sha256-CZc2lBZ5ivP0o7K+Cz3ermWOahY6C27rVrI+UI+XCS4="
|
||||
[mod."github.com/go-kit/kit"]
|
||||
version = "v0.12.0"
|
||||
hash = "sha256-5RkXo6s0oye8etgD5qy+AvkkkNsQ6jc0kWJj6flA4GM="
|
||||
@ -176,6 +189,9 @@ schema = 3
|
||||
[mod."github.com/go-logfmt/logfmt"]
|
||||
version = "v0.5.1"
|
||||
hash = "sha256-t50m9ffvW8PiGvO+2svnLI+N/XaWaBS+ZlhwrEQn2gU="
|
||||
[mod."github.com/go-logr/logr"]
|
||||
version = "v1.2.3"
|
||||
hash = "sha256-2L7k6GfrcW3GXXYr1FYIu20aZBjIF0cTKdte6D4riH8="
|
||||
[mod."github.com/go-ole/go-ole"]
|
||||
version = "v1.2.6"
|
||||
hash = "sha256-+oxitLeJxYF19Z6g+6CgmCHJ1Y5D8raMi2Cb3M6nXCs="
|
||||
@ -211,8 +227,8 @@ schema = 3
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-1PIeFGgUL4BK/StL/D12pg9bEQ5HfMT/fMLdus4pZTs="
|
||||
[mod."github.com/google/go-cmp"]
|
||||
version = "v0.5.8"
|
||||
hash = "sha256-8zkIo+Sr1NXMnj3PNmvjX2sZKnAKWXOFvmnX7D9bwxQ="
|
||||
version = "v0.5.9"
|
||||
hash = "sha256-lQc4O00R3QSMGs9LP8Sy7A9kj0cqV5rrUdpnGeipIyg="
|
||||
[mod."github.com/google/orderedcode"]
|
||||
version = "v0.0.1"
|
||||
hash = "sha256-KrExYovtUQrHGI1mPQf57jGw8soz7eWOC2xqEaV0uGk="
|
||||
@ -220,14 +236,11 @@ schema = 3
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-QoR55eBtA94T2tBszyxfDtO7/pjZZSGb5vm7U0Xhs0Y="
|
||||
[mod."github.com/googleapis/enterprise-certificate-proxy"]
|
||||
version = "v0.1.0"
|
||||
hash = "sha256-fxaElfiGGh1mLmltkFpVFdiuaagrLZLTW9btVpK13wg="
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-5gxsS5kDLqNFBkQ4eK6GS/sNn+aFknm6jjfteO+6jl4="
|
||||
[mod."github.com/googleapis/gax-go/v2"]
|
||||
version = "v2.4.0"
|
||||
hash = "sha256-zzat4+3iF2XBTQ6RZAUgsCbfK0HgO0nYhM4utA5dqz0="
|
||||
[mod."github.com/googleapis/go-type-adapters"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-u3ajruRV/EN2E1WKet/zoe3zmRrAy4C5F2Dx8bpQwoc="
|
||||
version = "v2.6.0"
|
||||
hash = "sha256-sSsncbJmv0n5jB3G7tHY1Zr2ucCusIeTrXZH4A8QqYI="
|
||||
[mod."github.com/gorilla/handlers"]
|
||||
version = "v1.5.1"
|
||||
hash = "sha256-GnBAARgOx1E+hDMQ63SI17hdhGtLQxb31lZOmn5j/pU="
|
||||
@ -289,8 +302,26 @@ schema = 3
|
||||
version = "v0.15.0"
|
||||
hash = "sha256-9oqKb5Y3hjleOFE2BczbEzLH6q2Jg7kUTP/M8Yk4Ne4="
|
||||
[mod."github.com/inconshreveable/mousetrap"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-ogTuLrV40FwS4ueo4hh6hi1wPywOI+LyIqfNjsibwNY="
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-ZTP9pLgwAAvHYK5A4PqwWCHGt00x5zMSOpCPoomQ3Sg="
|
||||
[mod."github.com/ipfs/go-block-format"]
|
||||
version = "v0.0.2"
|
||||
hash = "sha256-bgYso07OxWcHQx0ZxjE/G80798DnhywUSvqBb0jK48g="
|
||||
[mod."github.com/ipfs/go-cid"]
|
||||
version = "v0.3.2"
|
||||
hash = "sha256-S7iT1bGU84EgKvScz4uJL+3o1hJ17nP5ANha1B6MVfg="
|
||||
[mod."github.com/ipfs/go-ipfs-util"]
|
||||
version = "v0.0.1"
|
||||
hash = "sha256-Wht/WTcDwoPEwzyZZm1XoFhn3h8fWmg3xuUqBDvmB8k="
|
||||
[mod."github.com/ipfs/go-ipld-cbor"]
|
||||
version = "v0.0.6"
|
||||
hash = "sha256-bb7jO2s1Wy0V9Uu9U3jON9E1BJh6IKYbEEfB/oglvpU="
|
||||
[mod."github.com/ipfs/go-ipld-format"]
|
||||
version = "v0.0.1"
|
||||
hash = "sha256-LZjVFuwSwHzwxl7ZTskSFQr3kXD/qUWAmVVRCcblZFE="
|
||||
[mod."github.com/ipld/go-ipld-prime"]
|
||||
version = "v0.18.0"
|
||||
hash = "sha256-wHkeWxIwyaKku0J8bzQ2gggWJls1ORHhHewY092BA3c="
|
||||
[mod."github.com/jackpal/go-nat-pmp"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-L1D4Yoxnzihs795GZ+Q3AZsFP5c4iqyjTeyrudzPXtw="
|
||||
@ -304,8 +335,11 @@ schema = 3
|
||||
version = "v0.0.0-20190712205309-48d3d31d256d"
|
||||
hash = "sha256-bn04wkDnhQ0tb/YzmPf7MNJlApOl+z6+EAbUqH7Ti5Q="
|
||||
[mod."github.com/klauspost/compress"]
|
||||
version = "v1.15.9"
|
||||
hash = "sha256-ctPxlVq0c/SoNPVsP66RjxUjTHys3diW2Apxjyc9WdE="
|
||||
version = "v1.15.11"
|
||||
hash = "sha256-9MXm0TObg6DyqnYMIw3IChrorHc2ILf5djZYoM0e1J0="
|
||||
[mod."github.com/klauspost/cpuid/v2"]
|
||||
version = "v2.0.9"
|
||||
hash = "sha256-0rHlHzEVaDdkVbRJDhnAJjfJUmAHOYjXN5u7VoCi5uk="
|
||||
[mod."github.com/lib/pq"]
|
||||
version = "v1.10.6"
|
||||
hash = "sha256-8EhFwY/9YH5L/fd6l2beOnC3VvpegRAmCCsnDVJBqBM="
|
||||
@ -319,8 +353,8 @@ schema = 3
|
||||
version = "v0.9.0"
|
||||
hash = "sha256-Fe2OPoyRExZejwtUBivKhfJAJW7o9b1eyYpgDlWQ1No="
|
||||
[mod."github.com/mattn/go-colorable"]
|
||||
version = "v0.1.12"
|
||||
hash = "sha256-Y1vCt0ShrCz4wSmwsppCfeLPLKrWusc2zM2lUFwDMyI="
|
||||
version = "v0.1.13"
|
||||
hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.16"
|
||||
hash = "sha256-YMaPZvShDfA98vqw1+zWWl7M1IT4nHPGBrAt7kHo8Iw="
|
||||
@ -334,11 +368,14 @@ schema = 3
|
||||
version = "v0.1.1"
|
||||
hash = "sha256-SoKy4Mwkbm0J2yQLDcIcBbo/J6auTNdlZslTvJrSknM="
|
||||
[mod."github.com/mimoo/StrobeGo"]
|
||||
version = "v0.0.0-20181016162300-f8f6d4d2b643"
|
||||
hash = "sha256-ih5WfC48IniPuAHUpC6upOabZA1/DssTAcxhZe6A/dE="
|
||||
version = "v0.0.0-20210601165009-122bf33a46e0"
|
||||
hash = "sha256-rmw70RHsbeOnema++aFCPdswADMVKtb7KGF3msOI7ak="
|
||||
[mod."github.com/minio/highwayhash"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-UeHeepKtToyA5e/w3KdmpbCn+4medesZG0cAcU6P2cY="
|
||||
[mod."github.com/minio/sha256-simd"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-oEo/BoMqSLdwSjrhHTiFjl5Om4MVLNQXDJINk6Z110Y="
|
||||
[mod."github.com/mitchellh/go-homedir"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-oduBKXHAQG8X6aqLEpqZHs5DOKe84u6WkBwi4W6cv3k="
|
||||
@ -348,18 +385,36 @@ schema = 3
|
||||
[mod."github.com/mitchellh/mapstructure"]
|
||||
version = "v1.5.0"
|
||||
hash = "sha256-ztVhGQXs67MF8UadVvG72G3ly0ypQW0IRDdOOkjYwoE="
|
||||
[mod."github.com/mr-tron/base58"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-8FzMu3kHUbBX10pUdtGf59Ag7BNupx8ZHeUaodR1/Vk="
|
||||
[mod."github.com/mtibben/percent"]
|
||||
version = "v0.2.1"
|
||||
hash = "sha256-Zj1lpCP6mKQ0UUTMs2By4LC414ou+iJzKkK+eBHfEcc="
|
||||
[mod."github.com/multiformats/go-base32"]
|
||||
version = "v0.0.3"
|
||||
hash = "sha256-L18a2v2c5XenmJcwB1BmN0XzDFY45H0ougcZwrGVl7g="
|
||||
[mod."github.com/multiformats/go-base36"]
|
||||
version = "v0.1.0"
|
||||
hash = "sha256-QGoZm4HrdKWwmbJNbaTW0PvDdvPYrhx10znwhIFnFNM="
|
||||
[mod."github.com/multiformats/go-multibase"]
|
||||
version = "v0.0.3"
|
||||
hash = "sha256-yH1bl+jSJlAvPSqg9Z7xF4JS2fpoNUqH8wCDbU87ZDA="
|
||||
[mod."github.com/multiformats/go-multihash"]
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-4pBq4HNCgos2Ic0FDw2WhoPOL5VL1FHLbiHvnO8oZSc="
|
||||
[mod."github.com/multiformats/go-varint"]
|
||||
version = "v0.0.6"
|
||||
hash = "sha256-QlY6AzrSB/8IrlINPi8J8uw1J0SOa2UMTtlo79fg9gM="
|
||||
[mod."github.com/olekukonko/tablewriter"]
|
||||
version = "v0.0.5"
|
||||
hash = "sha256-/5i70IkH/qSW5KjGzv8aQNKh9tHoz98tqtL0K2DMFn4="
|
||||
[mod."github.com/onsi/ginkgo/v2"]
|
||||
version = "v2.2.0"
|
||||
hash = "sha256-+VzAN6pB5DwEqT/KHdhgfPnWBtyIIcHi1J4HQkqCvvU="
|
||||
version = "v2.5.1"
|
||||
hash = "sha256-VB29+H9k7l6il63oXJvsjamSUhsw/e99iI/BeTCderA="
|
||||
[mod."github.com/onsi/gomega"]
|
||||
version = "v1.20.2"
|
||||
hash = "sha256-3SGyvhWVlR3tYojnRSDr2SRToontAtw4hbZX+agn/Hs="
|
||||
version = "v1.24.1"
|
||||
hash = "sha256-REfxQTDRcO23GnmJfOW8/MmPJf9oE2grVvvGiC1eSbo="
|
||||
[mod."github.com/pelletier/go-toml"]
|
||||
version = "v1.9.5"
|
||||
hash = "sha256-RJ9K1BTId0Mled7S66iGgxHkZ5JKEIsrrNaEfM8aImc="
|
||||
@ -375,6 +430,9 @@ schema = 3
|
||||
[mod."github.com/pmezard/go-difflib"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
|
||||
[mod."github.com/polydawn/refmt"]
|
||||
version = "v0.0.0-20201211092308-30ac6d18308e"
|
||||
hash = "sha256-oicfIWyK51U0LaNW0qgg5enH866TM6vsJExw98whjio="
|
||||
[mod."github.com/prometheus/client_golang"]
|
||||
version = "v1.12.2"
|
||||
hash = "sha256-d8xHyML/FImuPmFOy7UGW/y85NPLzJYL6IFhR3s7XzE="
|
||||
@ -385,8 +443,8 @@ schema = 3
|
||||
version = "v0.34.0"
|
||||
hash = "sha256-M+v+7DntUBmiQNzfNmG3aLLufbl0XBQOubtYoNTzJDA="
|
||||
[mod."github.com/prometheus/procfs"]
|
||||
version = "v0.7.3"
|
||||
hash = "sha256-ik0WpnpSjMwifPYfQTfu/eb5ilNj+eLJF0d5Dftp8A8="
|
||||
version = "v0.8.0"
|
||||
hash = "sha256-hgrilokQsXCOCCvwgOSfuErxoFAQpXM/+zNJKcMVHyM="
|
||||
[mod."github.com/prometheus/tsdb"]
|
||||
version = "v0.7.1"
|
||||
hash = "sha256-BPz7YJbfMZgeR+u9YaeWeipVzHIS73EdgXD7VSJSLbA="
|
||||
@ -394,8 +452,8 @@ schema = 3
|
||||
version = "v0.1.7"
|
||||
hash = "sha256-/bfnXHBmN8vviPL7D85IzcEVXCaWyjbPPNyauzEcQ8Q="
|
||||
[mod."github.com/rcrowley/go-metrics"]
|
||||
version = "v0.0.0-20200313005456-10cdbea86bc0"
|
||||
hash = "sha256-xHqWWd4/RyAxqb80zjnrqHnHtWPfVTyByE+vDfbT52E="
|
||||
version = "v0.0.0-20201227073835-cf1acfcdf475"
|
||||
hash = "sha256-10ytHQ1SpMKYTiKuOPdEMuOVa8HVvv9ryYSIF9BHEBI="
|
||||
[mod."github.com/regen-network/cosmos-proto"]
|
||||
version = "v0.3.1"
|
||||
hash = "sha256-Bchbq/Hg72EA7Hevs8+PNuENuQaZAzk3qeVjMqFMUxc="
|
||||
@ -409,20 +467,23 @@ schema = 3
|
||||
version = "v1.27.0"
|
||||
hash = "sha256-BxQtP2TROeSSpj9l1irocuSfxn55UL4ugzB/og7r8eE="
|
||||
[mod."github.com/sasha-s/go-deadlock"]
|
||||
version = "v0.2.1-0.20190427202633-1595213edefa"
|
||||
hash = "sha256-BBOohTR+ktgJHAoYWh39/ui8MczcJECloc7tvxVBE50="
|
||||
version = "v0.3.1"
|
||||
hash = "sha256-2CBEi9/iN/OMt7wEIG+hRjgDH6CRWIgibGGGy1dQ78I="
|
||||
[mod."github.com/shirou/gopsutil"]
|
||||
version = "v3.21.4-0.20210419000835-c7a38de76ee5+incompatible"
|
||||
hash = "sha256-oqIqyFquWabIE6DID6uTEc8oFEmM1rVu2ATn3toiCEg="
|
||||
[mod."github.com/spaolacci/murmur3"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-RWD4PPrlAsZZ8Xy356MBxpj+/NZI7w2XOU14Ob7/Y9M="
|
||||
[mod."github.com/spf13/afero"]
|
||||
version = "v1.8.2"
|
||||
hash = "sha256-ERuG4GK1LOn72DRbT9URpo1QoVIm2A3mPtlf+xSPgx8="
|
||||
version = "v1.9.2"
|
||||
hash = "sha256-R1mir7Fu95QK+YL99U14RGbLJzxqWRH5rSFpssgJvzA="
|
||||
[mod."github.com/spf13/cast"]
|
||||
version = "v1.5.0"
|
||||
hash = "sha256-Pdp+wC5FWqyJKzyYHb7JCcV9BoJk/sxQw6nLyuLJvuQ="
|
||||
[mod."github.com/spf13/cobra"]
|
||||
version = "v1.5.0"
|
||||
hash = "sha256-rcyHWrxshA5DVpxrSba5X4NjppqOGrJ64QkUKKnfW2E="
|
||||
version = "v1.6.1"
|
||||
hash = "sha256-80B5HcYdFisz6QLYkTyka7f9Dr6AfcVyPwp3QChoXwU="
|
||||
[mod."github.com/spf13/jwalterweatherman"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-62BQtqTLF/eVrTOr7pUXE7AiHRjOVC8jQs3/Ehmflfs="
|
||||
@ -430,17 +491,17 @@ schema = 3
|
||||
version = "v1.0.5"
|
||||
hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw="
|
||||
[mod."github.com/spf13/viper"]
|
||||
version = "v1.13.0"
|
||||
hash = "sha256-yHviS3lMo1hB6jwyiU3zpyGtxP6v9AsyZUKiLeCVz+k="
|
||||
version = "v1.14.0"
|
||||
hash = "sha256-qn3VzD339HZIub5QP4aWVciSufAfVb9llbmQZ8pfDEs="
|
||||
[mod."github.com/status-im/keycard-go"]
|
||||
version = "v0.0.0-20200402102358-957c09536969"
|
||||
hash = "sha256-yddXXuu6mEFEO2/K6c1tWymeBKzOcvLQnNsFGRjtfXk="
|
||||
[mod."github.com/stretchr/objx"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-W6ycaeD/okr4udpCAGDsByr3OmiR0fuBzJUXHaHEFC0="
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-nY4mvP0f0Ry1IKMKQAYNuioA5h4red4mmQqeGZw6EF0="
|
||||
[mod."github.com/stretchr/testify"]
|
||||
version = "v1.8.0"
|
||||
hash = "sha256-LDxBAebK+A06y4vbH7cd1sVBOameIY81Xm8/9OPZh7o="
|
||||
version = "v1.8.1"
|
||||
hash = "sha256-3e0vOJLgCMAan+GfaGN8RGZdarh5iCavM6flf6YMNPk="
|
||||
[mod."github.com/subosito/gotenv"]
|
||||
version = "v1.4.1"
|
||||
hash = "sha256-aHAUs8Gh+BREUulljWr7vBZQg1BLu/M/8UfNK8CWSn8="
|
||||
@ -457,8 +518,8 @@ schema = 3
|
||||
version = "v0.16.0"
|
||||
hash = "sha256-JW4zO/0vMzf1dXLePOqaMtiLUZgNbuIseh9GV+jQlf0="
|
||||
[mod."github.com/tendermint/tendermint"]
|
||||
version = "v0.34.21"
|
||||
hash = "sha256-C1KW6Wd4TwsFC09IHt8Jo3dozEk/Q2ZVdTapM8btEJ0="
|
||||
version = "v0.34.24"
|
||||
hash = "sha256-3HFTv4XgN535RDaJ5OwUS+fnJHgkmLTwU7CNU2ilxEQ="
|
||||
[mod."github.com/tendermint/tm-db"]
|
||||
version = "v0.6.7"
|
||||
hash = "sha256-hl/3RrBrpkk2zA6dmrNlIYKs1/GfqegSscDSkA5Pjlo="
|
||||
@ -474,9 +535,18 @@ schema = 3
|
||||
[mod."github.com/ulikunitz/xz"]
|
||||
version = "v0.5.8"
|
||||
hash = "sha256-bfG3dssBUn+mSOAuKL+a/DTGGLUA+eASgLoGv/Gkqs0="
|
||||
[mod."github.com/vektah/gqlparser/v2"]
|
||||
version = "v2.5.1"
|
||||
hash = "sha256-sfwSSafdEXEv/ca/EOjgaPEdezewuqdE+5AuwhsHxYU="
|
||||
[mod."github.com/whyrusleeping/cbor-gen"]
|
||||
version = "v0.0.0-20200123233031-1cdf64d27158"
|
||||
hash = "sha256-E+9b4eXoLe95J6/n2Y12z15tMnxPoxdJtHe9B4mLFDA="
|
||||
[mod."github.com/zondax/hid"]
|
||||
version = "v0.9.1-0.20220302062450-5552068d2266"
|
||||
hash = "sha256-IZea8SHuLQxlltm1avieRVI054TWZg2jEoplodvNtwk="
|
||||
version = "v0.9.1"
|
||||
hash = "sha256-hSVmN/f/lQHFhF60o6ej78ELC0MMoqQgqIX2hHjdTXg="
|
||||
[mod."github.com/zondax/ledger-go"]
|
||||
version = "v0.14.0"
|
||||
hash = "sha256-RozTPSNs4RerZ4DQMBcGmvREjoRtH1G69xjhccYjIOk="
|
||||
[mod."go.etcd.io/bbolt"]
|
||||
version = "v1.3.6"
|
||||
hash = "sha256-DenVAmyN22xUiivk6fdJp4C9ZnUJXCMDUf8E0goRRV4="
|
||||
@ -484,47 +554,47 @@ schema = 3
|
||||
version = "v0.23.0"
|
||||
hash = "sha256-R3O9GyNtv6j0ic7s+2xkLLaLzbJEop0Otj1nJDFBjsg="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.0.0-20220622213112-05595931fe9d"
|
||||
hash = "sha256-2c4wvwiQ0DJSJsApfmNoxCxx1siRCiJvW9hhrGlcvds="
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-Un9wPqz8u/xpV98T4IqE6RMXIPhGCIm2prsNkHP3cjg="
|
||||
[mod."golang.org/x/exp"]
|
||||
version = "v0.0.0-20220722155223-a9213eeb770e"
|
||||
hash = "sha256-kNgzydWRpjm0sZl4uXEs3LX5L0xjJtJRAFf/CTlYUN4="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.0.0-20220726230323-06994584191e"
|
||||
hash = "sha256-VFJB2BjvFZnp0zoJLX4iinV/P6Cn7XUlsKwBGCdVjlU="
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-7IwGZh/xg4mQz88cJio2Ov5d3jGRXKj1itlAja/EAbQ="
|
||||
[mod."golang.org/x/oauth2"]
|
||||
version = "v0.0.0-20220622183110-fd043fe589d2"
|
||||
hash = "sha256-VLffpTpx3DlUzXB8mKiJfFzm4ZmgnLSUuLB5Ir0WQUg="
|
||||
version = "v0.0.0-20221014153046-6fdb5e3db783"
|
||||
hash = "sha256-IoygidVNqyAZmN+3macDeIefK8hhJToygpcqlwehdYQ="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.0.0-20220722155255-886fb9371eb4"
|
||||
hash = "sha256-ZZyIlxh+nqsOiWHstW7eHXN7RhHnbSL2eDIzcve07Q0="
|
||||
version = "v0.1.0"
|
||||
hash = "sha256-Hygjq9euZ0qz6TvHYQwOZEjNiTbTh1nSLRAWZ6KFGR8="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.0.0-20220811171246-fbc7d0a398ab"
|
||||
hash = "sha256-acnc9aKY/SyebObLasV+gowfB0S+6ehz3hnUgAmQmSU="
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-TIHhfYbZ99sCU1ZMikxwomXH5AEtD/lA1VMMW+UAhbU="
|
||||
[mod."golang.org/x/term"]
|
||||
version = "v0.0.0-20220722155259-a9ba230a4035"
|
||||
hash = "sha256-9uM1OONzbsa6bz2iKk767hAaCuafi58bdTF7at03fWY="
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-NKv2o8wz8DB/2W2h/muGEIHb+S06mBXZxhG254RpQ5s="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.3.7"
|
||||
hash = "sha256-XH2pUzzQx95O0rak00grQvfACfL+EmZiV7ZzJBkX+XY="
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-ztH+xQyM/clOcQl+y/UEPcfNKbc3xApMbEPDDZ9up0o="
|
||||
[mod."golang.org/x/xerrors"]
|
||||
version = "v0.0.0-20220609144429-65e65417b02f"
|
||||
hash = "sha256-tl8pv3oddbz2+KoIp7PFDKsxjQF8ocjPF8XPsY3sw38="
|
||||
version = "v0.0.0-20220907171357-04be3eba64a2"
|
||||
hash = "sha256-6+zueutgefIYmgXinOflz8qGDDDj0Zhv+2OkGhBTKno="
|
||||
[mod."google.golang.org/api"]
|
||||
version = "v0.93.0"
|
||||
hash = "sha256-W17B79osAhObMbrCHlgywEPg9yIPx0ZISDhp9JwFE5A="
|
||||
version = "v0.102.0"
|
||||
hash = "sha256-q8t08Wu0qzOQOmhY4GSoUrhqLTW/TeXiKhio+5ONfe4="
|
||||
[mod."google.golang.org/appengine"]
|
||||
version = "v1.6.7"
|
||||
hash = "sha256-zIxGRHiq4QBvRqkrhMGMGCaVL4iM4TtlYpAi/hrivS4="
|
||||
[mod."google.golang.org/genproto"]
|
||||
version = "v0.0.0-20220815135757-37a418bb8959"
|
||||
hash = "sha256-uS60VVZDdn6kNV24bmOmSTuwz4lBqVM+9XK176GGL2s="
|
||||
version = "v0.0.0-20221116193143-41c2ba794472"
|
||||
hash = "sha256-uQuxuOvWRsdMii5M5QresisVd1E+Ss8s2WfR2n7QSXk="
|
||||
[mod."google.golang.org/grpc"]
|
||||
version = "v1.49.0"
|
||||
hash = "sha256-x4+/XjxI2HY8fZYMiEV7Kv+2SrnlmaTJX3tBl+dPUoI="
|
||||
version = "v1.51.0"
|
||||
hash = "sha256-RzH5DU13D/ulxxOouIKpdNt8eHdff7mrEnB+JUupbLU="
|
||||
[mod."google.golang.org/protobuf"]
|
||||
version = "v1.28.1"
|
||||
hash = "sha256-sTJYgvlv5is7vHNxcuigF2lNASp0QonhUgnrguhfHSU="
|
||||
version = "v1.28.2-0.20220831092852-f930b1dc76e8"
|
||||
hash = "sha256-li5hXlXwTJ5LIZ8bVki1AZ6UFI2gXHl33JwdX1dOrtM="
|
||||
[mod."gopkg.in/ini.v1"]
|
||||
version = "v1.67.0"
|
||||
hash = "sha256-V10ahGNGT+NLRdKUyRg1dos5RxLBXBk1xutcnquc/+4="
|
||||
@ -537,6 +607,9 @@ schema = 3
|
||||
[mod."gopkg.in/yaml.v3"]
|
||||
version = "v3.0.1"
|
||||
hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU="
|
||||
[mod."lukechampine.com/blake3"]
|
||||
version = "v1.1.6"
|
||||
hash = "sha256-pKVTpuCcqGSn8s11Jq33yrXewQLvycuAoxKJEdbZ7rA="
|
||||
[mod."nhooyr.io/websocket"]
|
||||
version = "v1.8.6"
|
||||
hash = "sha256-DyaiCc/1iELrl6JSpz6WYMtFwUiSCOSoNF8IhSyP1ag="
|
||||
|
@ -1,12 +1,5 @@
|
||||
# Reference to another record.
|
||||
type Reference {
|
||||
id: String! # ID of linked record.
|
||||
}
|
||||
|
||||
# Reference to another record.
|
||||
input ReferenceInput {
|
||||
id: String!
|
||||
}
|
||||
scalar Link
|
||||
|
||||
# Bonds contain funds that are used to pay rent on record registration and renewal.
|
||||
type Bond {
|
||||
@ -37,44 +30,71 @@ type Account {
|
||||
balance: [Coin!] # Current balance for each coin type.
|
||||
}
|
||||
|
||||
# Value of a given type.
|
||||
type Value {
|
||||
null: Boolean
|
||||
# Value describes a DAG-JSON compatible value.
|
||||
union Value =
|
||||
BooleanValue
|
||||
| IntValue
|
||||
| FloatValue
|
||||
| StringValue
|
||||
| BytesValue
|
||||
| LinkValue
|
||||
| ArrayValue
|
||||
| MapValue
|
||||
|
||||
int: Int
|
||||
float: Float
|
||||
string: String
|
||||
boolean: Boolean
|
||||
json: String
|
||||
|
||||
reference: Reference
|
||||
|
||||
values: [Value]
|
||||
type BooleanValue {
|
||||
value: Boolean!
|
||||
}
|
||||
# Value of a given type used as input to queries.
|
||||
input ValueInput {
|
||||
null: Boolean
|
||||
|
||||
int: Int
|
||||
float: Float
|
||||
string: String
|
||||
boolean: Boolean
|
||||
type IntValue {
|
||||
value: Int!
|
||||
}
|
||||
|
||||
reference: ReferenceInput
|
||||
type FloatValue {
|
||||
value: Float!
|
||||
}
|
||||
|
||||
values: [ValueInput]
|
||||
type StringValue {
|
||||
value: String!
|
||||
}
|
||||
|
||||
type BytesValue {
|
||||
value: String!
|
||||
}
|
||||
|
||||
type ArrayValue {
|
||||
value: [Value]!
|
||||
}
|
||||
|
||||
type LinkValue {
|
||||
value: Link!
|
||||
}
|
||||
|
||||
type MapValue {
|
||||
value: [Attribute!]!
|
||||
}
|
||||
|
||||
# Key/value pair.
|
||||
type KeyValue {
|
||||
type Attribute {
|
||||
key: String!
|
||||
value: Value!
|
||||
value: Value
|
||||
}
|
||||
|
||||
# Value of a given type used as input to queries.
|
||||
# Note: GQL doesn't allow union input types.
|
||||
input ValueInput {
|
||||
int: Int
|
||||
float: Float
|
||||
string: String
|
||||
boolean: Boolean
|
||||
link: Link
|
||||
array: [ValueInput]
|
||||
map: [KeyValueInput!]
|
||||
}
|
||||
|
||||
# Key/value pair for inputs.
|
||||
input KeyValueInput {
|
||||
key: String!
|
||||
value: ValueInput!
|
||||
value: ValueInput
|
||||
}
|
||||
|
||||
# Status information about a node (https://docs.tendermint.com/master/rpc/#/Info/status).
|
||||
@ -155,7 +175,7 @@ type Record {
|
||||
createTime: String! # Record create time.
|
||||
expiryTime: String! # Record expiry time.
|
||||
owners: [String!] # Addresses of record owners.
|
||||
attributes: [KeyValue] # Record attributes.
|
||||
attributes: [Attribute!] # Record attributes.
|
||||
references: [Record] # Record references.
|
||||
}
|
||||
|
||||
@ -195,7 +215,7 @@ type Query {
|
||||
getBondsByIds(ids: [String!]): [Bond]
|
||||
|
||||
# Query bonds.
|
||||
queryBonds(attributes: [KeyValueInput]): [Bond]
|
||||
queryBonds(attributes: [KeyValueInput!]): [Bond]
|
||||
|
||||
# Query bonds by owner.
|
||||
queryBondsByOwner(ownerAddresses: [String!]): [OwnerBonds]
|
||||
@ -210,7 +230,7 @@ type Query {
|
||||
# Query records.
|
||||
queryRecords(
|
||||
# Multiple attribute conditions are in a logical AND.
|
||||
attributes: [KeyValueInput]
|
||||
attributes: [KeyValueInput!]
|
||||
|
||||
# Whether to query all records, not just named ones (false by default).
|
||||
all: Boolean
|
9860
gql/generated.go
9860
gql/generated.go
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,3 @@
|
||||
# .gqlgen.yml example
|
||||
#
|
||||
# Refer to https://gqlgen.com/config/
|
||||
# for detailed .gqlgen.yml documentation.
|
||||
|
||||
@ -12,3 +10,8 @@ model:
|
||||
resolver:
|
||||
filename: resolver.go
|
||||
type: Resolver
|
||||
|
||||
models:
|
||||
Link:
|
||||
model:
|
||||
- github.com/cerc-io/laconicd/gql.Link
|
||||
|
165
gql/graphiql.go
Normal file
165
gql/graphiql.go
Normal file
@ -0,0 +1,165 @@
|
||||
package gql
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GraphiQL is an in-browser IDE for exploring GraphiQL APIs.
|
||||
// This handler returns GraphiQL when requested.
|
||||
//
|
||||
// For more information, see https://github.com/graphql/graphiql.
|
||||
|
||||
func PlaygroundHandler(apiURL string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
http.Error(w, "only GET requests are supported", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := page.Execute(w, map[string]interface{}{
|
||||
"apiURL": apiURL,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/graphql/graphiql/blob/main/examples/graphiql-cdn/index.html
|
||||
var page = template.Must(template.New("graphiql").Parse(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>GraphiQL</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#graphiql {
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
This GraphiQL example depends on Promise and fetch, which are available in
|
||||
modern browsers, but can be "polyfilled" for older browsers.
|
||||
GraphiQL itself depends on React DOM.
|
||||
If you do not want to rely on a CDN, you can host these files locally or
|
||||
include them directly in your favored resource bundler.
|
||||
-->
|
||||
<script
|
||||
src="https://unpkg.com/react@17/umd/react.development.js"
|
||||
integrity="sha512-Vf2xGDzpqUOEIKO+X2rgTLWPY+65++WPwCHkX2nFMu9IcstumPsf/uKKRd5prX3wOu8Q0GBylRpsDB26R6ExOg=="
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
|
||||
integrity="sha512-Wr9OKCTtq1anK0hq5bY3X/AvDI5EflDSAh0mE9gma+4hl+kXdTJPKZ3TwLMBcrgUeoY0s3dq9JjhCQc7vddtFg=="
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
<!--
|
||||
These two files can be found in the npm module, however you may wish to
|
||||
copy them directly into your environment, or perhaps include them in your
|
||||
favored resource bundler.
|
||||
-->
|
||||
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="graphiql">Loading...</div>
|
||||
<script
|
||||
src="https://unpkg.com/graphiql/graphiql.min.js"
|
||||
type="application/javascript"
|
||||
></script>
|
||||
<script>
|
||||
// https://github.com/graphql/graphiql/blob/main/packages/graphiql/resources/renderExample.js
|
||||
|
||||
// Parse the search string to get url parameters.
|
||||
var search = window.location.search;
|
||||
var parameters = {};
|
||||
search
|
||||
.substr(1)
|
||||
.split('&')
|
||||
.forEach(function (entry) {
|
||||
var eq = entry.indexOf('=');
|
||||
if (eq >= 0) {
|
||||
parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(
|
||||
entry.slice(eq + 1),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// When the query and variables string is edited, update the URL bar so
|
||||
// that it can be easily shared.
|
||||
function onEditQuery(newQuery) {
|
||||
parameters.query = newQuery;
|
||||
updateURL();
|
||||
}
|
||||
|
||||
function onEditVariables(newVariables) {
|
||||
parameters.variables = newVariables;
|
||||
updateURL();
|
||||
}
|
||||
|
||||
function onEditHeaders(newHeaders) {
|
||||
parameters.headers = newHeaders;
|
||||
updateURL();
|
||||
}
|
||||
|
||||
function onTabChange(tabsState) {
|
||||
const activeTab = tabsState.tabs[tabsState.activeTabIndex];
|
||||
parameters.query = activeTab.query;
|
||||
parameters.variables = activeTab.variables;
|
||||
parameters.headers = activeTab.headers;
|
||||
updateURL();
|
||||
}
|
||||
|
||||
function updateURL() {
|
||||
var newSearch =
|
||||
'?' +
|
||||
Object.keys(parameters)
|
||||
.filter(function (key) {
|
||||
return Boolean(parameters[key]);
|
||||
})
|
||||
.map(function (key) {
|
||||
return (
|
||||
encodeURIComponent(key) + '=' + encodeURIComponent(parameters[key])
|
||||
);
|
||||
})
|
||||
.join('&');
|
||||
history.replaceState(null, null, newSearch);
|
||||
}
|
||||
|
||||
// Render <GraphiQL /> into the body.
|
||||
// See the README in the top level of this module to learn more about
|
||||
// how you can customize GraphiQL by providing different values or
|
||||
// additional child elements.
|
||||
ReactDOM.render(
|
||||
React.createElement(GraphiQL, {
|
||||
fetcher: GraphiQL.createFetcher({
|
||||
url: {{.apiURL}}
|
||||
}),
|
||||
query: parameters.query,
|
||||
variables: parameters.variables,
|
||||
headers: parameters.headers,
|
||||
defaultHeaders: parameters.defaultHeaders,
|
||||
onEditQuery: onEditQuery,
|
||||
onEditVariables: onEditVariables,
|
||||
onEditHeaders: onEditHeaders,
|
||||
defaultEditorToolsVisibility: true,
|
||||
isHeadersEditorEnabled: true,
|
||||
shouldPersistHeaders: true,
|
||||
onTabChange,
|
||||
}),
|
||||
document.getElementById('graphiql'),
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
@ -2,6 +2,10 @@
|
||||
|
||||
package gql
|
||||
|
||||
type Value interface {
|
||||
IsValue()
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
Address string `json:"address"`
|
||||
PubKey *string `json:"pubKey"`
|
||||
@ -10,6 +14,17 @@ type Account struct {
|
||||
Balance []*Coin `json:"balance"`
|
||||
}
|
||||
|
||||
type ArrayValue struct {
|
||||
Value []Value `json:"value"`
|
||||
}
|
||||
|
||||
func (ArrayValue) IsValue() {}
|
||||
|
||||
type Attribute struct {
|
||||
Key string `json:"key"`
|
||||
Value Value `json:"value"`
|
||||
}
|
||||
|
||||
type Auction struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
@ -53,21 +68,52 @@ type Bond struct {
|
||||
Balance []*Coin `json:"balance"`
|
||||
}
|
||||
|
||||
type BooleanValue struct {
|
||||
Value bool `json:"value"`
|
||||
}
|
||||
|
||||
func (BooleanValue) IsValue() {}
|
||||
|
||||
type BytesValue struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (BytesValue) IsValue() {}
|
||||
|
||||
type Coin struct {
|
||||
Type string `json:"type"`
|
||||
Quantity string `json:"quantity"`
|
||||
}
|
||||
|
||||
type KeyValue struct {
|
||||
Key string `json:"key"`
|
||||
Value *Value `json:"value"`
|
||||
type FloatValue struct {
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
func (FloatValue) IsValue() {}
|
||||
|
||||
type IntValue struct {
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
func (IntValue) IsValue() {}
|
||||
|
||||
type KeyValueInput struct {
|
||||
Key string `json:"key"`
|
||||
Value *ValueInput `json:"value"`
|
||||
}
|
||||
|
||||
type LinkValue struct {
|
||||
Value Link `json:"value"`
|
||||
}
|
||||
|
||||
func (LinkValue) IsValue() {}
|
||||
|
||||
type MapValue struct {
|
||||
Value []*Attribute `json:"value"`
|
||||
}
|
||||
|
||||
func (MapValue) IsValue() {}
|
||||
|
||||
type NameRecord struct {
|
||||
Latest *NameRecordEntry `json:"latest"`
|
||||
History []*NameRecordEntry `json:"history"`
|
||||
@ -102,18 +148,10 @@ type Record struct {
|
||||
CreateTime string `json:"createTime"`
|
||||
ExpiryTime string `json:"expiryTime"`
|
||||
Owners []string `json:"owners"`
|
||||
Attributes []*KeyValue `json:"attributes"`
|
||||
Attributes []*Attribute `json:"attributes"`
|
||||
References []*Record `json:"references"`
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type ReferenceInput struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Version string `json:"version"`
|
||||
Node *NodeInfo `json:"node"`
|
||||
@ -125,6 +163,12 @@ type Status struct {
|
||||
DiskUsage string `json:"disk_usage"`
|
||||
}
|
||||
|
||||
type StringValue struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (StringValue) IsValue() {}
|
||||
|
||||
type SyncInfo struct {
|
||||
LatestBlockHash string `json:"latest_block_hash"`
|
||||
LatestBlockHeight string `json:"latest_block_height"`
|
||||
@ -138,23 +182,12 @@ type ValidatorInfo struct {
|
||||
ProposerPriority *string `json:"proposer_priority"`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Null *bool `json:"null"`
|
||||
Int *int `json:"int"`
|
||||
Float *float64 `json:"float"`
|
||||
String *string `json:"string"`
|
||||
Boolean *bool `json:"boolean"`
|
||||
JSON *string `json:"json"`
|
||||
Reference *Reference `json:"reference"`
|
||||
Values []*Value `json:"values"`
|
||||
}
|
||||
|
||||
type ValueInput struct {
|
||||
Null *bool `json:"null"`
|
||||
Int *int `json:"int"`
|
||||
Float *float64 `json:"float"`
|
||||
String *string `json:"string"`
|
||||
Boolean *bool `json:"boolean"`
|
||||
Reference *ReferenceInput `json:"reference"`
|
||||
Values []*ValueInput `json:"values"`
|
||||
Link *Link `json:"link"`
|
||||
Array []*ValueInput `json:"array"`
|
||||
Map []*KeyValueInput `json:"map"`
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func (q queryResolver) QueryRecords(ctx context.Context, attributes []*KeyValueI
|
||||
res, err := nsQueryClient.ListRecords(
|
||||
context.Background(),
|
||||
®istrytypes.QueryListRecordsRequest{
|
||||
Attributes: parseRequestAttributes(attributes),
|
||||
Attributes: toRPCAttributes(attributes),
|
||||
All: (all != nil && *all),
|
||||
},
|
||||
)
|
||||
|
33
gql/scalar.go
Normal file
33
gql/scalar.go
Normal file
@ -0,0 +1,33 @@
|
||||
package gql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Represents an IPLD link. Links are generally but not necessarily implemented as CIDs
|
||||
type Link string
|
||||
|
||||
func (l Link) String() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
// UnmarshalGQLContext implements the graphql.ContextUnmarshaler interface
|
||||
func (l *Link) UnmarshalGQLContext(_ context.Context, v interface{}) error {
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("Link must be a string")
|
||||
}
|
||||
*l = Link(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalGQLContext implements the graphql.ContextMarshaler interface
|
||||
func (l Link) MarshalGQLContext(_ context.Context, w io.Writer) error {
|
||||
encodable := map[string]string{
|
||||
"/": l.String(),
|
||||
}
|
||||
return json.NewEncoder(w).Encode(encodable)
|
||||
}
|
@ -5,9 +5,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rs/cors"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
@ -16,6 +17,16 @@ func Server(ctx client.Context) {
|
||||
if !viper.GetBool("gql-server") {
|
||||
return
|
||||
}
|
||||
|
||||
router := chi.NewRouter()
|
||||
|
||||
// Add CORS middleware around every request
|
||||
// See https://github.com/rs/cors for full option listing
|
||||
router.Use(cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
Debug: false,
|
||||
}).Handler)
|
||||
|
||||
logFile := viper.GetString("log-file")
|
||||
|
||||
port := viper.GetString("gql-port")
|
||||
@ -25,20 +36,20 @@ func Server(ctx client.Context) {
|
||||
logFile: logFile,
|
||||
}}))
|
||||
|
||||
http.Handle("/", playground.Handler("GraphQL playground", "/api"))
|
||||
router.Handle("/", PlaygroundHandler("/api"))
|
||||
|
||||
if viper.GetBool("gql-playground") {
|
||||
apiBase := viper.GetString("gql-playground-api-base")
|
||||
|
||||
http.Handle("/webui", playground.Handler("GraphQL playground", apiBase+"/api"))
|
||||
http.Handle("/console", playground.Handler("GraphQL playground", apiBase+"/graphql"))
|
||||
router.Handle("/webui", PlaygroundHandler(apiBase+"/api"))
|
||||
router.Handle("/console", PlaygroundHandler(apiBase+"/graphql"))
|
||||
}
|
||||
|
||||
http.Handle("/api", srv)
|
||||
http.Handle("/graphql", srv)
|
||||
router.Handle("/api", srv)
|
||||
router.Handle("/graphql", srv)
|
||||
|
||||
log.Info("Connect to GraphQL playground", "url", fmt.Sprintf("http://localhost:%s", port))
|
||||
err := http.ListenAndServe(":"+port, nil) //nolint: all
|
||||
err := http.ListenAndServe(":"+port, router) //nolint: all
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
254
gql/util.go
254
gql/util.go
@ -2,15 +2,15 @@ package gql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect" // #nosec G702
|
||||
"fmt" // #nosec G702
|
||||
"strconv"
|
||||
|
||||
auctiontypes "github.com/cerc-io/laconicd/x/auction/types"
|
||||
bondtypes "github.com/cerc-io/laconicd/x/bond/types"
|
||||
registrytypes "github.com/cerc-io/laconicd/x/registry/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
)
|
||||
|
||||
// OwnerAttributeName denotes the owner attribute name for a bond.
|
||||
@ -61,13 +61,21 @@ func getGQLRecord(ctx context.Context, resolver QueryResolver, record registryty
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
recordType := record.ToRecordType()
|
||||
attributes, err := getAttributes(&recordType)
|
||||
node, err := ipld.Decode(record.Attributes, dagjson.Decode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.Kind() != ipld.Kind_Map {
|
||||
return nil, fmt.Errorf("invalid record attributes")
|
||||
}
|
||||
|
||||
var links []string
|
||||
attributes, err := resolveIPLDNode(node, &links)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
references, err := getReferences(ctx, resolver, &recordType)
|
||||
references, err := resolver.GetRecordsByIds(ctx, links)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -79,11 +87,96 @@ func getGQLRecord(ctx context.Context, resolver QueryResolver, record registryty
|
||||
ExpiryTime: record.GetExpiryTime(),
|
||||
Owners: record.GetOwners(),
|
||||
Names: record.GetNames(),
|
||||
Attributes: attributes,
|
||||
Attributes: attributes.(MapValue).Value,
|
||||
References: references,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveIPLDNode(node ipld.Node, links *[]string) (Value, error) {
|
||||
switch node.Kind() {
|
||||
case ipld.Kind_Map:
|
||||
var entries []*Attribute
|
||||
for itr := node.MapIterator(); !itr.Done(); {
|
||||
k, v, err := itr.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k.Kind() != ipld.Kind_String {
|
||||
return nil, fmt.Errorf("invalid record attribute key type: %s", k.Kind())
|
||||
}
|
||||
s, err := k.AsString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := resolveIPLDNode(v, links)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &Attribute{
|
||||
Key: s,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
return MapValue{entries}, nil
|
||||
case ipld.Kind_List:
|
||||
var values []Value
|
||||
for itr := node.ListIterator(); !itr.Done(); {
|
||||
_, v, err := itr.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := resolveIPLDNode(v, links)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, val)
|
||||
}
|
||||
return ArrayValue{values}, nil
|
||||
case ipld.Kind_Null:
|
||||
return nil, nil
|
||||
case ipld.Kind_Bool:
|
||||
val, err := node.AsBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return BooleanValue{val}, nil
|
||||
case ipld.Kind_Int:
|
||||
val, err := node.AsInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: handle bigger ints
|
||||
return IntValue{int(val)}, nil
|
||||
case ipld.Kind_Float:
|
||||
val, err := node.AsFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FloatValue{val}, nil
|
||||
case ipld.Kind_String:
|
||||
val, err := node.AsString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return StringValue{val}, nil
|
||||
case ipld.Kind_Bytes:
|
||||
val, err := node.AsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return BytesValue{string(val)}, nil
|
||||
case ipld.Kind_Link:
|
||||
val, err := node.AsLink()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*links = append(*links, val.String())
|
||||
return LinkValue{Link(val.String())}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid node kind")
|
||||
}
|
||||
}
|
||||
|
||||
func getGQLNameRecord(record *registrytypes.NameRecord) (*NameRecord, error) {
|
||||
if record == nil {
|
||||
return nil, fmt.Errorf("got nil record")
|
||||
@ -163,136 +256,47 @@ func GetGQLAuction(auction *auctiontypes.Auction, bids []*auctiontypes.Bid) (*Au
|
||||
return &gqlAuction, nil
|
||||
}
|
||||
|
||||
func getReferences(ctx context.Context, resolver QueryResolver, r *registrytypes.RecordType) ([]*Record, error) {
|
||||
var ids []string
|
||||
func toRPCValue(value *ValueInput) *registrytypes.QueryListRecordsRequest_ValueInput {
|
||||
var rpcval registrytypes.QueryListRecordsRequest_ValueInput
|
||||
|
||||
// #nosec G705
|
||||
for key := range r.Attributes {
|
||||
//nolint: all
|
||||
switch r.Attributes[key].(type) {
|
||||
case interface{}:
|
||||
if obj, ok := r.Attributes[key].(map[string]interface{}); ok {
|
||||
if _, ok := obj["/"]; ok && len(obj) == 1 {
|
||||
if _, ok := obj["/"].(string); ok {
|
||||
ids = append(ids, obj["/"].(string))
|
||||
switch {
|
||||
case value == nil:
|
||||
return nil
|
||||
case value.Int != nil:
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Int{Int: int64(*value.Int)}
|
||||
case value.Float != nil:
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Float{Float: *value.Float}
|
||||
case value.String != nil:
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_String_{String_: *value.String}
|
||||
case value.Boolean != nil:
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Boolean{Boolean: *value.Boolean}
|
||||
case value.Link != nil:
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Link{Link: value.Link.String()}
|
||||
case value.Array != nil:
|
||||
var contents registrytypes.QueryListRecordsRequest_ArrayInput
|
||||
for _, val := range value.Array {
|
||||
contents.Values = append(contents.Values, toRPCValue(val))
|
||||
}
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Array{Array: &contents}
|
||||
case value.Map != nil:
|
||||
var contents registrytypes.QueryListRecordsRequest_MapInput
|
||||
for _, kv := range value.Map {
|
||||
contents.Values[kv.Key] = toRPCValue(kv.Value)
|
||||
}
|
||||
rpcval.Value = ®istrytypes.QueryListRecordsRequest_ValueInput_Map{Map: &contents}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolver.GetRecordsByIds(ctx, ids)
|
||||
return &rpcval
|
||||
}
|
||||
|
||||
func getAttributes(r *registrytypes.RecordType) ([]*KeyValue, error) {
|
||||
return mapToKeyValuePairs(r.Attributes)
|
||||
}
|
||||
|
||||
func mapToKeyValuePairs(attrs map[string]interface{}) ([]*KeyValue, error) {
|
||||
kvPairs := []*KeyValue{}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
|
||||
// #nosec G705
|
||||
for key, value := range attrs {
|
||||
kvPair := &KeyValue{
|
||||
Key: key,
|
||||
Value: &Value{},
|
||||
}
|
||||
|
||||
switch val := value.(type) {
|
||||
case nil:
|
||||
kvPair.Value.Null = &trueVal
|
||||
case int:
|
||||
kvPair.Value.Int = &val
|
||||
case float64:
|
||||
kvPair.Value.Float = &val
|
||||
case string:
|
||||
kvPair.Value.String = &val
|
||||
case bool:
|
||||
kvPair.Value.Boolean = &val
|
||||
case interface{}:
|
||||
if obj, ok := value.(map[string]interface{}); ok {
|
||||
if _, ok := obj["/"]; ok && len(obj) == 1 {
|
||||
if _, ok := obj["/"].(string); ok {
|
||||
kvPair.Value.Reference = &Reference{
|
||||
ID: obj["/"].(string),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonStr := string(bytes)
|
||||
kvPair.Value.JSON = &jsonStr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if kvPair.Value.Null == nil {
|
||||
kvPair.Value.Null = &falseVal
|
||||
}
|
||||
|
||||
valueType := reflect.ValueOf(value)
|
||||
if valueType.Kind() == reflect.Slice {
|
||||
bytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonStr := string(bytes)
|
||||
kvPair.Value.JSON = &jsonStr
|
||||
}
|
||||
|
||||
kvPairs = append(kvPairs, kvPair)
|
||||
}
|
||||
|
||||
return kvPairs, nil
|
||||
}
|
||||
|
||||
func parseRequestAttributes(attrs []*KeyValueInput) []*registrytypes.QueryListRecordsRequest_KeyValueInput {
|
||||
func toRPCAttributes(attrs []*KeyValueInput) []*registrytypes.QueryListRecordsRequest_KeyValueInput {
|
||||
kvPairs := []*registrytypes.QueryListRecordsRequest_KeyValueInput{}
|
||||
|
||||
for _, value := range attrs {
|
||||
parsedValue := toRPCValue(value.Value)
|
||||
kvPair := ®istrytypes.QueryListRecordsRequest_KeyValueInput{
|
||||
Key: value.Key,
|
||||
Value: ®istrytypes.QueryListRecordsRequest_ValueInput{},
|
||||
Value: parsedValue,
|
||||
}
|
||||
|
||||
if value.Value.String != nil {
|
||||
kvPair.Value.String_ = *value.Value.String
|
||||
kvPair.Value.Type = "string"
|
||||
}
|
||||
|
||||
if value.Value.Int != nil {
|
||||
kvPair.Value.Int = int64(*value.Value.Int)
|
||||
kvPair.Value.Type = "int"
|
||||
}
|
||||
|
||||
if value.Value.Float != nil {
|
||||
kvPair.Value.Float = *value.Value.Float
|
||||
kvPair.Value.Type = "float"
|
||||
}
|
||||
|
||||
if value.Value.Boolean != nil {
|
||||
kvPair.Value.Boolean = *value.Value.Boolean
|
||||
kvPair.Value.Type = "boolean"
|
||||
}
|
||||
|
||||
if value.Value.Reference != nil {
|
||||
reference := ®istrytypes.QueryListRecordsRequest_ReferenceInput{
|
||||
Id: value.Value.Reference.ID,
|
||||
}
|
||||
|
||||
kvPair.Value.Reference = reference
|
||||
kvPair.Value.Type = "reference"
|
||||
}
|
||||
|
||||
// TODO: Handle arrays.
|
||||
|
||||
kvPairs = append(kvPairs, kvPair)
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,11 @@ package indexer
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
rpctypes "github.com/cerc-io/laconicd/rpc/types"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -110,12 +110,12 @@ func (kv *KVIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.Response
|
||||
ethTxIndex++
|
||||
|
||||
if err := saveTxResult(kv.clientCtx.Codec, batch, txHash, &txResult); err != nil {
|
||||
return sdkerrors.Wrapf(err, "IndexBlock %d", height)
|
||||
return errorsmod.Wrapf(err, "IndexBlock %d", height)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
return sdkerrors.Wrapf(err, "IndexBlock %d, write batch", block.Height)
|
||||
return errorsmod.Wrapf(err, "IndexBlock %d, write batch", block.Height)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -134,14 +134,14 @@ func (kv *KVIndexer) FirstIndexedBlock() (int64, error) {
|
||||
func (kv *KVIndexer) GetByTxHash(hash common.Hash) (*ethermint.TxResult, error) {
|
||||
bz, err := kv.db.Get(TxHashKey(hash))
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "GetByTxHash %s", hash.Hex())
|
||||
return nil, errorsmod.Wrapf(err, "GetByTxHash %s", hash.Hex())
|
||||
}
|
||||
if len(bz) == 0 {
|
||||
return nil, fmt.Errorf("tx not found, hash: %s", hash.Hex())
|
||||
}
|
||||
var txKey ethermint.TxResult
|
||||
if err := kv.clientCtx.Codec.Unmarshal(bz, &txKey); err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "GetByTxHash %s", hash.Hex())
|
||||
return nil, errorsmod.Wrapf(err, "GetByTxHash %s", hash.Hex())
|
||||
}
|
||||
return &txKey, nil
|
||||
}
|
||||
@ -150,7 +150,7 @@ func (kv *KVIndexer) GetByTxHash(hash common.Hash) (*ethermint.TxResult, error)
|
||||
func (kv *KVIndexer) GetByBlockAndIndex(blockNumber int64, txIndex int32) (*ethermint.TxResult, error) {
|
||||
bz, err := kv.db.Get(TxIndexKey(blockNumber, txIndex))
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "GetByBlockAndIndex %d %d", blockNumber, txIndex)
|
||||
return nil, errorsmod.Wrapf(err, "GetByBlockAndIndex %d %d", blockNumber, txIndex)
|
||||
}
|
||||
if len(bz) == 0 {
|
||||
return nil, fmt.Errorf("tx not found, block: %d, eth-index: %d", blockNumber, txIndex)
|
||||
@ -174,7 +174,7 @@ func TxIndexKey(blockNumber int64, txIndex int32) []byte {
|
||||
func LoadLastBlock(db dbm.DB) (int64, error) {
|
||||
it, err := db.ReverseIterator([]byte{KeyPrefixTxIndex}, []byte{KeyPrefixTxIndex + 1})
|
||||
if err != nil {
|
||||
return 0, sdkerrors.Wrap(err, "LoadLastBlock")
|
||||
return 0, errorsmod.Wrap(err, "LoadLastBlock")
|
||||
}
|
||||
defer it.Close()
|
||||
if !it.Valid() {
|
||||
@ -187,7 +187,7 @@ func LoadLastBlock(db dbm.DB) (int64, error) {
|
||||
func LoadFirstBlock(db dbm.DB) (int64, error) {
|
||||
it, err := db.Iterator([]byte{KeyPrefixTxIndex}, []byte{KeyPrefixTxIndex + 1})
|
||||
if err != nil {
|
||||
return 0, sdkerrors.Wrap(err, "LoadFirstBlock")
|
||||
return 0, errorsmod.Wrap(err, "LoadFirstBlock")
|
||||
}
|
||||
defer it.Close()
|
||||
if !it.Valid() {
|
||||
@ -213,10 +213,10 @@ func isEthTx(tx sdk.Tx) bool {
|
||||
func saveTxResult(codec codec.Codec, batch dbm.Batch, txHash common.Hash, txResult *ethermint.TxResult) error {
|
||||
bz := codec.MustMarshal(txResult)
|
||||
if err := batch.Set(TxHashKey(txHash), bz); err != nil {
|
||||
return sdkerrors.Wrap(err, "set tx-hash key")
|
||||
return errorsmod.Wrap(err, "set tx-hash key")
|
||||
}
|
||||
if err := batch.Set(TxIndexKey(txResult.Height, txResult.EthTxIndex), txHash.Bytes()); err != nil {
|
||||
return sdkerrors.Wrap(err, "set tx-index key")
|
||||
return errorsmod.Wrap(err, "set tx-index key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
2
init.bat
2
init.bat
@ -9,7 +9,7 @@ rem 3. add path C:\msys64\mingw64\bin
|
||||
rem C:\msys64\usr\bin
|
||||
|
||||
set KEY="mykey"
|
||||
set CHAINID="ethermint_9000-1"
|
||||
set CHAINID="laconic_9000-1"
|
||||
set MONIKER="localtestnet"
|
||||
set KEYRING="test"
|
||||
set KEYALGO="eth_secp256k1"
|
||||
|
181
init.sh
181
init.sh
@ -1,114 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
KEY="mykey"
|
||||
CHAINID="ethermint_9000-1"
|
||||
CHAINID="laconic_9000-1"
|
||||
MONIKER="localtestnet"
|
||||
KEYRING="test"
|
||||
KEYALGO="eth_secp256k1"
|
||||
LOGLEVEL="info"
|
||||
# to trace evm
|
||||
LOGLEVEL="${LOGLEVEL:-info}"
|
||||
# trace evm
|
||||
TRACE="--trace"
|
||||
# TRACE=""
|
||||
|
||||
# validate dependencies are installed
|
||||
command -v jq > /dev/null 2>&1 || { echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"; exit 1; }
|
||||
if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
|
||||
# validate dependencies are installed
|
||||
command -v jq > /dev/null 2>&1 || {
|
||||
echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# remove existing daemon and client
|
||||
rm -rf ~/.laconic*
|
||||
# remove existing daemon and client
|
||||
rm -rf $HOME/.laconicd/*
|
||||
rm -rf $HOME/.laconic/*
|
||||
|
||||
make install
|
||||
if [ -n "`which make`" ]; then
|
||||
make install
|
||||
fi
|
||||
|
||||
laconicd config keyring-backend $KEYRING
|
||||
laconicd config chain-id $CHAINID
|
||||
laconicd config keyring-backend $KEYRING
|
||||
laconicd config chain-id $CHAINID
|
||||
|
||||
# if $KEY exists it should be deleted
|
||||
laconicd keys add $KEY --keyring-backend $KEYRING --algo $KEYALGO
|
||||
# if $KEY exists it should be deleted
|
||||
laconicd keys add $KEY --keyring-backend $KEYRING --algo $KEYALGO
|
||||
|
||||
# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer)
|
||||
laconicd init $MONIKER --chain-id $CHAINID
|
||||
# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer)
|
||||
laconicd init $MONIKER --chain-id $CHAINID
|
||||
|
||||
# Change parameter token denominations to aphoton
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["staking"]["params"]["bond_denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
# Custom modules
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_commit_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_reveal_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_minimum_bid"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
update_genesis() {
|
||||
jq "$1" $HOME/.laconicd/config/genesis.json > $HOME/.laconicd/config/tmp_genesis.json &&
|
||||
mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
}
|
||||
|
||||
if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then
|
||||
# Change parameter token denominations to aphoton
|
||||
update_genesis '.app_state["staking"]["params"]["bond_denom"]="aphoton"'
|
||||
update_genesis '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["mint"]["params"]["mint_denom"]="aphoton"'
|
||||
# Custom modules
|
||||
update_genesis '.app_state["registry"]["params"]["record_rent"]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_rent"]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_commit_fee"]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_reveal_fee"]["denom"]="aphoton"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_minimum_bid"]["denom"]="aphoton"'
|
||||
|
||||
if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then
|
||||
echo "Setting timers for expiry tests."
|
||||
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_grace_period"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
fi
|
||||
update_genesis '.app_state["registry"]["params"]["record_rent_duration"]="60s"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="60s"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"'
|
||||
fi
|
||||
|
||||
if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then
|
||||
if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then
|
||||
echo "Enabling auction and setting timers."
|
||||
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_enabled"]=true' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_grace_period"]="300s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_commits_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
fi
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_enabled"]=true'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="300s"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_commits_duration"]="60s"'
|
||||
update_genesis '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"'
|
||||
fi
|
||||
|
||||
# increase block time (?)
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["time_iota_ms"]="1000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
# increase block time (?)
|
||||
update_genesis '.consensus_params["block"]["time_iota_ms"]="1000"'
|
||||
|
||||
# Set gas limit in genesis
|
||||
cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="10000000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||
# Set gas limit in genesis
|
||||
update_genesis '.consensus_params["block"]["max_gas"]="10000000"'
|
||||
|
||||
# disable produce empty block
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# disable produce empty block
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
|
||||
else
|
||||
sed -i 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
|
||||
fi
|
||||
|
||||
if [[ $1 == "pending" ]]; then
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_propose = "3s"/timeout_propose = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_commit = "5s"/timeout_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i '' 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||
else
|
||||
sed -i 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_propose = "3s"/timeout_propose = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_commit = "5s"/timeout_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||
sed -i 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||
fi
|
||||
fi
|
||||
|
||||
# Allocate genesis accounts (cosmos formatted addresses)
|
||||
laconicd add-genesis-account $KEY 100000000000000000000000000aphoton --keyring-backend $KEYRING
|
||||
if [[ "$1" == "pending" ]]; then
|
||||
alias sed-i="sed -i"
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
alias sed-i="sed -i ''"
|
||||
fi
|
||||
sed-i \
|
||||
-e 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' \
|
||||
-e 's/timeout_propose = "3s"/timeout_propose = "30s"/g' \
|
||||
-e 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' \
|
||||
-e 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' \
|
||||
-e 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' \
|
||||
-e 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' \
|
||||
-e 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' \
|
||||
-e 's/timeout_commit = "5s"/timeout_commit = "150s"/g' \
|
||||
-e 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' \
|
||||
$HOME/.laconicd/config/config.toml
|
||||
fi
|
||||
|
||||
# Sign genesis transaction
|
||||
laconicd gentx $KEY 1000000000000000000000aphoton --keyring-backend $KEYRING --chain-id $CHAINID
|
||||
# Enable telemetry (prometheus metrics: http://localhost:1317/metrics?format=prometheus)
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' 's/enabled = false/enabled = true/g' $HOME/.laconicd/config/app.toml
|
||||
sed -i '' 's/prometheus-retention-time = 0/prometheus-retention-time = 60/g' $HOME/.laconicd/config/app.toml
|
||||
sed -i '' 's/prometheus = false/prometheus = true/g' $HOME/.laconicd/config/config.toml
|
||||
else
|
||||
sed -i 's/enabled = false/enabled = true/g' $HOME/.laconicd/config/app.toml
|
||||
sed -i 's/prometheus-retention-time = 0/prometheus-retention-time = 60/g' $HOME/.laconicd/config/app.toml
|
||||
sed -i 's/prometheus = false/prometheus = true/g' $HOME/.laconicd/config/config.toml
|
||||
fi
|
||||
|
||||
# Collect genesis tx
|
||||
laconicd collect-gentxs
|
||||
# Allocate genesis accounts (cosmos formatted addresses)
|
||||
laconicd add-genesis-account $KEY 100000000000000000000000000aphoton --keyring-backend $KEYRING
|
||||
|
||||
# Run this to ensure everything worked and that the genesis file is setup correctly
|
||||
laconicd validate-genesis
|
||||
# Sign genesis transaction
|
||||
laconicd gentx $KEY 1000000000000000000000aphoton --keyring-backend $KEYRING --chain-id $CHAINID
|
||||
|
||||
if [[ $1 == "pending" ]]; then
|
||||
# Collect genesis tx
|
||||
laconicd collect-gentxs
|
||||
|
||||
# Run this to ensure everything worked and that the genesis file is setup correctly
|
||||
laconicd validate-genesis
|
||||
|
||||
if [[ "$1" == "pending" ]]; then
|
||||
echo "pending mode is on, please wait for the first block committed."
|
||||
fi
|
||||
else
|
||||
echo "Using existing database at $HOME/.laconicd. To replace, run '`basename $0` clean'"
|
||||
fi
|
||||
|
||||
# Start the node (remove the --pruning=nothing flag if historical queries are not needed)
|
||||
laconicd start --pruning=nothing --evm.tracer=json $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --json-rpc.api eth,txpool,personal,net,debug,web3,miner --api.enable --gql-server --gql-playground
|
||||
laconicd start \
|
||||
--pruning=nothing \
|
||||
--evm.tracer=json $TRACE \
|
||||
--log_level $LOGLEVEL \
|
||||
--minimum-gas-prices=0.0001aphoton \
|
||||
--json-rpc.api eth,txpool,personal,net,debug,web3,miner \
|
||||
--api.enable \
|
||||
--gql-server --gql-playground
|
||||
|
5
mlc_config.json
Normal file
5
mlc_config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"retryOn429": true,
|
||||
"retryCount": 3,
|
||||
"fallbackRetryDelay": "20s"
|
||||
}
|
@ -15,7 +15,7 @@ COPY . .
|
||||
RUN make build-linux
|
||||
|
||||
# Final image
|
||||
FROM golang:1.18 as final
|
||||
FROM golang:1.19 as final
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
@ -27,7 +27,7 @@ import sources.nixpkgs {
|
||||
dotenv = builtins.path { name = "dotenv"; path = ../scripts/.env; };
|
||||
};
|
||||
})
|
||||
(_: pkgs: { test-env = import ./testenv.nix { inherit pkgs; }; })
|
||||
(_: pkgs: { test-env = pkgs.callPackage ./testenv.nix { }; })
|
||||
(_: pkgs: {
|
||||
cosmovisor = pkgs.buildGo118Module rec {
|
||||
name = "cosmovisor";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user