Compare commits

..

3 Commits

Author SHA1 Message Date
aleem1314
34812d5e27 wip: update to v0.46 2023-03-02 21:54:24 +05:30
aleem1314
290c9fbf0c wip: port wasm module 2023-02-28 15:05:49 +05:30
aleem1314
1cb6e7acbc feat: add proto files 2023-02-28 14:30:54 +05:30
204 changed files with 64487 additions and 7719 deletions

View File

@ -23,7 +23,6 @@ jobs:
- name: "Dependency Review"
uses: actions/dependency-review-action@v3
if: env.GIT_DIFF
# TODO: https://git.vdb.to/cerc-io/laconicd/issues/115
# - name: "Go vulnerability check"
# run: make vulncheck
# if: env.GIT_DIFF
- name: "Go vulnerability check"
run: make vulncheck
if: env.GIT_DIFF

View File

@ -39,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v20
- uses: cachix/install-nix-action@v18
- uses: cachix/cachix-action@v12
with:
name: ethermint

21
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: "Close stale issues & pull requests"
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: "This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed in 7 days-before-close if no further activity occurs."
stale-issue-message: "This issue is stale because it has been open 45 days with no activity. Remove `Status: Stale` label or comment or this will be closed in 7 days."
days-before-stale: 45
days-before-close: 7
exempt-issue-labels: "Status: Blocked, Type: Bug, pinned, automerge"
exempt-pr-labels: "Status: Blocked, Type: Bug, pinned, automerge"
stale-pr-label: "Status: Stale"
stale-issue-label: "Status: Stale"

View File

@ -6,14 +6,14 @@ run:
linters:
enable:
- bodyclose
# - depguard # 20231120 disable until https://github.com/golangci/golangci-lint/issues/3906 is released
- depguard
- dogsled
- dupl
- errcheck
- goconst
- gocritic
- gofumpt
# - revive # 20231120 overly sensitive unused detection
- revive
- gosec
- gosimple
- govet

View File

@ -19,9 +19,6 @@ test/
tests/
*_test.go
# false positive; TODO: https://github.com/cerc-io/laconicd/issues/104
testutil/network/network.go
# Semgrep rules folder
.semgrep

View File

@ -237,7 +237,7 @@ endif
ifeq (, $(shell which go-bindata))
@echo "Installing go-bindata..."
@go get github.com/kevinburke/go-bindata/go-bindata@v3
@go get github.com/kevinburke/go-bindata/go-bindata
else
@echo "go-bindata already installed; skipping..."
endif

28
go.mod
View File

@ -27,8 +27,10 @@ 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/ipld/go-ipld-prime v0.19.0
github.com/ipfs/go-ipld-cbor v0.0.6
github.com/ipld/go-ipld-prime v0.18.0
github.com/miguelmota/go-ethereum-hdwallet v0.1.1
github.com/multiformats/go-multihash v0.2.0
github.com/onsi/ginkgo/v2 v2.5.1
github.com/onsi/gomega v1.24.1
github.com/pkg/errors v0.9.1
@ -43,8 +45,8 @@ require (
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.7.0
golang.org/x/text v0.7.0
golang.org/x/net v0.4.0
golang.org/x/text v0.5.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
@ -65,7 +67,7 @@ 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.44.122 // indirect
github.com/aws/aws-sdk-go v1.40.45 // 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
@ -121,7 +123,7 @@ require (
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.7.0 // indirect
github.com/hashicorp/go-getter v1.6.1 // 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
@ -131,10 +133,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.11 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
@ -150,14 +154,13 @@ 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.14.1 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // 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
@ -188,17 +191,18 @@ 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.10 // 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 // indirect
github.com/zondax/ledger-go v0.14.0 // indirect
go.etcd.io/bbolt v1.3.6 // 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.1.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/term v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.103.0 // indirect
google.golang.org/appengine v1.6.7 // indirect

433
go.sum
View File

@ -19,177 +19,36 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=
cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
cloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU=
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=
cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=
cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ=
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w=
cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE=
@ -274,9 +133,10 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
@ -353,12 +213,6 @@ github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3h
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E=
github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@ -476,10 +330,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg=
github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0=
@ -590,9 +440,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -609,7 +457,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -634,9 +481,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@ -648,7 +492,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -661,11 +504,6 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -674,22 +512,12 @@ github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -723,6 +551,8 @@ github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -732,8 +562,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.7.0 h1:bzrYP+qu/gMrL1au7/aDvkoOVGUJpeKBgbqRHACAFDY=
github.com/hashicorp/go-getter v1.7.0/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-getter v1.6.1 h1:NASsgP4q6tL94WH6nJxKWj8As2H/2kop/bB1d8JMyRY=
github.com/hashicorp/go-getter v1.6.1/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@ -748,6 +578,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
@ -796,10 +627,20 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE=
github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04=
github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4=
github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50=
github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0=
github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA=
github.com/ipfs/go-ipld-format v0.0.1 h1:HCu4eB/Gh+KD/Q0M8u888RFkorTWNIL3da4oc5dwc80=
github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
github.com/ipld/go-ipld-prime v0.18.0 h1:xUk7NUBSWHEXdjiOu2sLXouFJOMs0yoYzeI5RAqhYQo=
github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
@ -807,6 +648,7 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1C
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b h1:izTof8BKh/nE1wrKOrloNA5q4odOarjf+Xpe+4qow98=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@ -828,6 +670,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@ -836,12 +679,6 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kevinburke/go-bindata v1.1.0 h1:JZ8XIdxtkAszuYkgf17qwQzcU/RgsGSVLSULJdN+5bU=
github.com/kevinburke/go-bindata v1.1.0/go.mod h1:UJ72WTOoRKzbz+vwtKgARJwsy+1ZGUukVBUCc197TJo=
github.com/kevinburke/go-bindata v3.22.0+incompatible h1:/JmqEhIWQ7GRScV0WjX/0tqBrC5D21ALg0H0U/KZ/ts=
github.com/kevinburke/go-bindata v3.22.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY=
github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
@ -850,6 +687,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
@ -932,17 +770,19 @@ github.com/miguelmota/go-ethereum-hdwallet v0.1.1/go.mod h1:f9m9uXokAHA6WNoYOPjj
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94=
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -962,6 +802,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
@ -971,11 +812,14 @@ github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE=
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
github.com/multiformats/go-multicodec v0.5.0 h1:EgU6cBe/D7WRwQb1KmnBvU7lrcFGMggZVTPtOW9dDHs=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multihash v0.2.0 h1:oytJb9ZA1OUW0r0f9ea18GiaPOo4SXyc7p2movyUuo4=
github.com/multiformats/go-multihash v0.2.0/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -1057,6 +901,7 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls=
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@ -1149,6 +994,7 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -1239,8 +1085,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@ -1254,7 +1100,10 @@ github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUO
github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w=
github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 h1:WXhVOwj2USAXB5oMDwRl3piOux2XMV9TANaYxXHdkoE=
github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@ -1285,10 +1134,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -1302,10 +1149,12 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -1353,7 +1202,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@ -1416,27 +1264,17 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1446,23 +1284,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1474,9 +1299,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1490,6 +1313,7 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1542,60 +1366,37 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1606,9 +1407,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1674,10 +1474,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@ -1686,9 +1482,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
@ -1717,35 +1510,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@ -1800,73 +1564,7 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c h1:S34D59DS2GWOEwWNt4fYmTcFrtlOgukG2k9WsomZ7tg=
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@ -1893,27 +1591,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1926,9 +1605,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk=
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=

163
init.sh
View File

@ -10,111 +10,104 @@ LOGLEVEL="info"
TRACE="--trace"
# TRACE=""
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; }
# 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 $HOME/.laconicd/*
rm -rf $HOME/.laconic/*
# remove existing daemon and client
rm -rf ~/.laconic*
if [ -n "`which make`" ]; then
make install
fi
make install
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
# 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
if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then
echo "Setting timers for expiry tests."
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
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
if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then
echo "Enabling auction and setting timers."
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
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
# 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 (?)
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
# 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
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
# disable produce empty block
# 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 = 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
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
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
# Allocate genesis accounts (cosmos formatted addresses)
laconicd add-genesis-account $KEY 100000000000000000000000000aphoton --keyring-backend $KEYRING
# Sign genesis transaction
laconicd gentx $KEY 1000000000000000000000aphoton --keyring-backend $KEYRING --chain-id $CHAINID
# Sign genesis transaction
laconicd gentx $KEY 1000000000000000000000aphoton --keyring-backend $KEYRING --chain-id $CHAINID
# Collect genesis tx
laconicd collect-gentxs
# Collect genesis tx
laconicd collect-gentxs
# Run this to ensure everything worked and that the genesis file is setup correctly
laconicd validate-genesis
# 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'"
if [[ $1 == "pending" ]]; then
echo "pending mode is on, please wait for the first block committed."
fi
# Start the node (remove the --pruning=nothing flag if historical queries are not needed)

View File

@ -30,102 +30,4 @@ message WebsiteRegistrationRecord {
string tls_cert_cid = 4 [(gogoproto.moretags) = "json:\"TLSCertCID\" yaml:\"TLSCertCID\""];
string type = 5 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string version = 6 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
}
message ApplicationRecord {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string description = 3 [(gogoproto.moretags) = "json:\"description\" yaml:\"description\""];
string version = 4 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string homepage = 5 [(gogoproto.moretags) = "json:\"homepage\" yaml:\"homepage\""];
string license = 6 [(gogoproto.moretags) = "json:\"license\" yaml:\"license\""];
string author = 7 [(gogoproto.moretags) = "json:\"author\" yaml:\"author\""];
repeated string repository = 8 [(gogoproto.moretags) = "json:\"repository\" yaml:\"repository\""];
string repository_ref = 9 [(gogoproto.moretags) = "json:\"repositoryRef\" yaml:\"repositoryRef\""];
string app_version = 10 [(gogoproto.moretags) = "json:\"appVersion\" yaml:\"appVersion\""];
string app_type = 11 [(gogoproto.moretags) = "json:\"appType\" yaml:\"appType\""];
string engines = 12 [(gogoproto.moretags) = "json:\"engines\" yaml:\"engines\""];
repeated string os = 13 [(gogoproto.moretags) = "json:\"os\" yaml:\"os\""];
repeated string cpu = 14 [(gogoproto.moretags) = "json:\"cpu\" yaml:\"cpu\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message ApplicationArtifact {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string description = 4 [(gogoproto.moretags) = "json:\"description\" yaml:\"description\""];
string version = 5 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string application = 6 [(gogoproto.moretags) = "json:\"application\" yaml:\"application\""];
string content_type = 7 [(gogoproto.moretags) = "json:\"contentType\" yaml:\"contentType\""];
string os = 8 [(gogoproto.moretags) = "json:\"os\" yaml:\"os\""];
string cpu = 9 [(gogoproto.moretags) = "json:\"cpu\" yaml:\"cpu\""];
repeated string uri = 10 [(gogoproto.moretags) = "json:\"uri\" yaml:\"uri\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message DnsRecord {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string version = 3 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string resource_type = 4 [(gogoproto.moretags) = "json:\"resourceType\" yaml:\"resourceType\""];
string value = 5 [(gogoproto.moretags) = "json:\"value\" yaml:\"value\""];
string request = 6 [(gogoproto.moretags) = "json:\"request\" yaml:\"request\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message ApplicationDeploymentRequest {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string version = 3 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string application = 4 [(gogoproto.moretags) = "json:\"application\" yaml:\"application\""];
string dns = 5 [(gogoproto.moretags) = "json:\"dns\" yaml:\"dns\""];
string config = 6 [(gogoproto.moretags) = "json:\"config\" yaml:\"config\""];
string deployment = 7 [(gogoproto.moretags) = "json:\"deployment\" yaml:\"deployment\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message ApplicationDeploymentRecord {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string description = 3 [(gogoproto.moretags) = "json:\"description\" yaml:\"description\""];
string version = 4 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string application = 5 [(gogoproto.moretags) = "json:\"application\" yaml:\"application\""];
string url = 6 [(gogoproto.moretags) = "json:\"url\" yaml:\"url\""];
string dns = 7 [(gogoproto.moretags) = "json:\"dns\" yaml:\"dns\""];
string request = 8 [(gogoproto.moretags) = "json:\"request\" yaml:\"request\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message ApplicationDeploymentRemovalRequest {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string version = 2 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string deployment = 3 [(gogoproto.moretags) = "json:\"deployment\" yaml:\"deployment\""];
string request = 4 [(gogoproto.moretags) = "json:\"request\" yaml:\"request\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message ApplicationDeploymentRemovalRecord {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string version = 2 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string deployment = 3 [(gogoproto.moretags) = "json:\"deployment\" yaml:\"deployment\""];
string request = 4 [(gogoproto.moretags) = "json:\"request\" yaml:\"request\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
message GeneralRecord {
string type = 1 [(gogoproto.moretags) = "json:\"type\" yaml:\"type\""];
string name = 2 [(gogoproto.moretags) = "json:\"name\" yaml:\"name\""];
string description = 3 [(gogoproto.moretags) = "json:\"description\" yaml:\"description\""];
string version = 4 [(gogoproto.moretags) = "json:\"version\" yaml:\"version\""];
string category = 5 [(gogoproto.moretags) = "json:\"category\" yaml:\"category\""];
string value = 6 [(gogoproto.moretags) = "json:\"value\" yaml:\"value\""];
string meta = 20 [(gogoproto.moretags) = "json:\"meta\" yaml:\"meta\""];
repeated string tags = 21 [(gogoproto.moretags) = "json:\"tags\" yaml:\"tags\""];
}
}

109
proto/wasm/v1/authz.proto Normal file
View File

@ -0,0 +1,109 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/base/v1beta1/coin.proto";
import "google/protobuf/any.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_getters_all) = false;
// ContractExecutionAuthorization defines authorization for wasm execute.
// Since: wasmd 0.30
message ContractExecutionAuthorization {
option (cosmos_proto.implements_interface) = "Authorization";
// Grants for contract executions
repeated ContractGrant grants = 1 [ (gogoproto.nullable) = false ];
}
// ContractMigrationAuthorization defines authorization for wasm contract
// migration. Since: wasmd 0.30
message ContractMigrationAuthorization {
option (cosmos_proto.implements_interface) = "Authorization";
// Grants for contract migrations
repeated ContractGrant grants = 1 [ (gogoproto.nullable) = false ];
}
// ContractGrant a granted permission for a single contract
// Since: wasmd 0.30
message ContractGrant {
// Contract is the bech32 address of the smart contract
string contract = 1;
// Limit defines execution limits that are enforced and updated when the grant
// is applied. When the limit lapsed the grant is removed.
google.protobuf.Any limit = 2
[ (cosmos_proto.accepts_interface) = "ContractAuthzLimitX" ];
// Filter define more fine-grained control on the message payload passed
// to the contract in the operation. When no filter applies on execution, the
// operation is prohibited.
google.protobuf.Any filter = 3
[ (cosmos_proto.accepts_interface) = "ContractAuthzFilterX" ];
}
// MaxCallsLimit limited number of calls to the contract. No funds transferable.
// Since: wasmd 0.30
message MaxCallsLimit {
option (cosmos_proto.implements_interface) = "ContractAuthzLimitX";
// Remaining number that is decremented on each execution
uint64 remaining = 1;
}
// MaxFundsLimit defines the maximal amounts that can be sent to the contract.
// Since: wasmd 0.30
message MaxFundsLimit {
option (cosmos_proto.implements_interface) = "ContractAuthzLimitX";
// Amounts is the maximal amount of tokens transferable to the contract.
repeated cosmos.base.v1beta1.Coin amounts = 1 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// CombinedLimit defines the maximal amounts that can be sent to a contract and
// the maximal number of calls executable. Both need to remain >0 to be valid.
// Since: wasmd 0.30
message CombinedLimit {
option (cosmos_proto.implements_interface) = "ContractAuthzLimitX";
// Remaining number that is decremented on each execution
uint64 calls_remaining = 1;
// Amounts is the maximal amount of tokens transferable to the contract.
repeated cosmos.base.v1beta1.Coin amounts = 2 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// AllowAllMessagesFilter is a wildcard to allow any type of contract payload
// message.
// Since: wasmd 0.30
message AllowAllMessagesFilter {
option (cosmos_proto.implements_interface) = "ContractAuthzFilterX";
}
// AcceptedMessageKeysFilter accept only the specific contract message keys in
// the json object to be executed.
// Since: wasmd 0.30
message AcceptedMessageKeysFilter {
option (cosmos_proto.implements_interface) = "ContractAuthzFilterX";
// Messages is the list of unique keys
repeated string keys = 1;
}
// AcceptedMessagesFilter accept only the specific raw contract messages to be
// executed.
// Since: wasmd 0.30
message AcceptedMessagesFilter {
option (cosmos_proto.implements_interface) = "ContractAuthzFilterX";
// Messages is the list of raw contract messages
repeated bytes messages = 1 [ (gogoproto.casttype) = "RawContractMessage" ];
}

View File

@ -0,0 +1,46 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "gogoproto/gogo.proto";
import "wasm/v1/types.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
// GenesisState - genesis state of x/wasm
message GenesisState {
Params params = 1 [ (gogoproto.nullable) = false ];
repeated Code codes = 2
[ (gogoproto.nullable) = false, (gogoproto.jsontag) = "codes,omitempty" ];
repeated Contract contracts = 3 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "contracts,omitempty"
];
repeated Sequence sequences = 4 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "sequences,omitempty"
];
}
// Code struct encompasses CodeInfo and CodeBytes
message Code {
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
CodeInfo code_info = 2 [ (gogoproto.nullable) = false ];
bytes code_bytes = 3;
// Pinned to wasmvm cache
bool pinned = 4;
}
// Contract struct encompasses ContractAddress, ContractInfo, and ContractState
message Contract {
string contract_address = 1;
ContractInfo contract_info = 2 [ (gogoproto.nullable) = false ];
repeated Model contract_state = 3 [ (gogoproto.nullable) = false ];
repeated ContractCodeHistoryEntry contract_code_history = 4
[ (gogoproto.nullable) = false ];
}
// Sequence key and value of an id generation counter
message Sequence {
bytes id_key = 1 [ (gogoproto.customname) = "IDKey" ];
uint64 value = 2;
}

31
proto/wasm/v1/ibc.proto Normal file
View File

@ -0,0 +1,31 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "gogoproto/gogo.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_getters_all) = false;
// MsgIBCSend
message MsgIBCSend {
// the channel by which the packet will be sent
string channel = 2 [ (gogoproto.moretags) = "yaml:\"source_channel\"" ];
// Timeout height relative to the current block height.
// The timeout is disabled when set to 0.
uint64 timeout_height = 4
[ (gogoproto.moretags) = "yaml:\"timeout_height\"" ];
// Timeout timestamp (in nanoseconds) relative to the current block timestamp.
// The timeout is disabled when set to 0.
uint64 timeout_timestamp = 5
[ (gogoproto.moretags) = "yaml:\"timeout_timestamp\"" ];
// Data is the payload to transfer. We must not make assumption what format or
// content is in here.
bytes data = 6;
}
// MsgIBCCloseChannel port and channel need to be owned by the contract
message MsgIBCCloseChannel {
string channel = 2 [ (gogoproto.moretags) = "yaml:\"source_channel\"" ];
}

View File

@ -0,0 +1,272 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/base/v1beta1/coin.proto";
import "wasm/v1/types.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.equal_all) = true;
// StoreCodeProposal gov proposal content type to submit WASM code to the system
message StoreCodeProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// RunAs is the address that is passed to the contract's environment as sender
string run_as = 3;
// WASMByteCode can be raw or gzip compressed
bytes wasm_byte_code = 4 [ (gogoproto.customname) = "WASMByteCode" ];
// Used in v1beta1
reserved 5, 6;
// InstantiatePermission to apply on contract creation, optional
AccessConfig instantiate_permission = 7;
// UnpinCode code on upload, optional
bool unpin_code = 8;
// Source is the URL where the code is hosted
string source = 9;
// Builder is the docker image used to build the code deterministically, used
// for smart contract verification
string builder = 10;
// CodeHash is the SHA256 sum of the code outputted by builder, used for smart
// contract verification
bytes code_hash = 11;
}
// InstantiateContractProposal gov proposal content type to instantiate a
// contract.
message InstantiateContractProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// RunAs is the address that is passed to the contract's environment as sender
string run_as = 3;
// Admin is an optional address that can execute migrations
string admin = 4;
// CodeID is the reference to the stored WASM code
uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ];
// Label is optional metadata to be stored with a constract instance.
string label = 6;
// Msg json encoded message to be passed to the contract on instantiation
bytes msg = 7 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 8 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// InstantiateContract2Proposal gov proposal content type to instantiate
// contract 2
message InstantiateContract2Proposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// RunAs is the address that is passed to the contract's enviroment as sender
string run_as = 3;
// Admin is an optional address that can execute migrations
string admin = 4;
// CodeID is the reference to the stored WASM code
uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ];
// Label is optional metadata to be stored with a constract instance.
string label = 6;
// Msg json encode message to be passed to the contract on instantiation
bytes msg = 7 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 8 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Salt is an arbitrary value provided by the sender. Size can be 1 to 64.
bytes salt = 9;
// FixMsg include the msg value into the hash for the predictable address.
// Default is false
bool fix_msg = 10;
}
// MigrateContractProposal gov proposal content type to migrate a contract.
message MigrateContractProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// Note: skipping 3 as this was previously used for unneeded run_as
// Contract is the address of the smart contract
string contract = 4;
// CodeID references the new WASM code
uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ];
// Msg json encoded message to be passed to the contract on migration
bytes msg = 6 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// SudoContractProposal gov proposal content type to call sudo on a contract.
message SudoContractProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// Contract is the address of the smart contract
string contract = 3;
// Msg json encoded message to be passed to the contract as sudo
bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// ExecuteContractProposal gov proposal content type to call execute on a
// contract.
message ExecuteContractProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// RunAs is the address that is passed to the contract's environment as sender
string run_as = 3;
// Contract is the address of the smart contract
string contract = 4;
// Msg json encoded message to be passed to the contract as execute
bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 6 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// UpdateAdminProposal gov proposal content type to set an admin for a contract.
message UpdateAdminProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// NewAdmin address to be set
string new_admin = 3 [ (gogoproto.moretags) = "yaml:\"new_admin\"" ];
// Contract is the address of the smart contract
string contract = 4;
}
// ClearAdminProposal gov proposal content type to clear the admin of a
// contract.
message ClearAdminProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// Contract is the address of the smart contract
string contract = 3;
}
// PinCodesProposal gov proposal content type to pin a set of code ids in the
// wasmvm cache.
message PinCodesProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ];
// Description is a human readable text
string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ];
// CodeIDs references the new WASM codes
repeated uint64 code_ids = 3 [
(gogoproto.customname) = "CodeIDs",
(gogoproto.moretags) = "yaml:\"code_ids\""
];
}
// UnpinCodesProposal gov proposal content type to unpin a set of code ids in
// the wasmvm cache.
message UnpinCodesProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ];
// Description is a human readable text
string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ];
// CodeIDs references the WASM codes
repeated uint64 code_ids = 3 [
(gogoproto.customname) = "CodeIDs",
(gogoproto.moretags) = "yaml:\"code_ids\""
];
}
// AccessConfigUpdate contains the code id and the access config to be
// applied.
message AccessConfigUpdate {
// CodeID is the reference to the stored WASM code to be updated
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
// InstantiatePermission to apply to the set of code ids
AccessConfig instantiate_permission = 2 [ (gogoproto.nullable) = false ];
}
// UpdateInstantiateConfigProposal gov proposal content type to update
// instantiate config to a set of code ids.
message UpdateInstantiateConfigProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ];
// Description is a human readable text
string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ];
// AccessConfigUpdate contains the list of code ids and the access config
// to be applied.
repeated AccessConfigUpdate access_config_updates = 3
[ (gogoproto.nullable) = false ];
}
// StoreAndInstantiateContractProposal gov proposal content type to store
// and instantiate the contract.
message StoreAndInstantiateContractProposal {
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
// Title is a short summary
string title = 1;
// Description is a human readable text
string description = 2;
// RunAs is the address that is passed to the contract's environment as sender
string run_as = 3;
// WASMByteCode can be raw or gzip compressed
bytes wasm_byte_code = 4 [ (gogoproto.customname) = "WASMByteCode" ];
// InstantiatePermission to apply on contract creation, optional
AccessConfig instantiate_permission = 5;
// UnpinCode code on upload, optional
bool unpin_code = 6;
// Admin is an optional address that can execute migrations
string admin = 7;
// Label is optional metadata to be stored with a constract instance.
string label = 8;
// Msg json encoded message to be passed to the contract on instantiation
bytes msg = 9 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 10 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Source is the URL where the code is hosted
string source = 11;
// Builder is the docker image used to build the code deterministically, used
// for smart contract verification
string builder = 12;
// CodeHash is the SHA256 sum of the code outputted by builder, used for smart
// contract verification
bytes code_hash = 13;
}

263
proto/wasm/v1/query.proto Normal file
View File

@ -0,0 +1,263 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "gogoproto/gogo.proto";
import "wasm/v1/types.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.equal_all) = false;
// Query provides defines the gRPC querier service
service Query {
// ContractInfo gets the contract meta data
rpc ContractInfo(QueryContractInfoRequest)
returns (QueryContractInfoResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/contract/{address}";
}
// ContractHistory gets the contract code history
rpc ContractHistory(QueryContractHistoryRequest)
returns (QueryContractHistoryResponse) {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contract/{address}/history";
}
// ContractsByCode lists all smart contracts for a code id
rpc ContractsByCode(QueryContractsByCodeRequest)
returns (QueryContractsByCodeResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/code/{code_id}/contracts";
}
// AllContractState gets all raw store data for a single contract
rpc AllContractState(QueryAllContractStateRequest)
returns (QueryAllContractStateResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/contract/{address}/state";
}
// RawContractState gets single key from the raw store data of a contract
rpc RawContractState(QueryRawContractStateRequest)
returns (QueryRawContractStateResponse) {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}";
}
// SmartContractState get smart query result from the contract
rpc SmartContractState(QuerySmartContractStateRequest)
returns (QuerySmartContractStateResponse) {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}";
}
// Code gets the binary code and metadata for a singe wasm code
rpc Code(QueryCodeRequest) returns (QueryCodeResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/code/{code_id}";
}
// Codes gets the metadata for all stored wasm codes
rpc Codes(QueryCodesRequest) returns (QueryCodesResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/code";
}
// PinnedCodes gets the pinned code ids
rpc PinnedCodes(QueryPinnedCodesRequest) returns (QueryPinnedCodesResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/codes/pinned";
}
// Params gets the module params
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/codes/params";
}
// ContractsByCreator gets the contracts by creator
rpc ContractsByCreator(QueryContractsByCreatorRequest)
returns (QueryContractsByCreatorResponse) {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contracts/creator/{creator_address}";
}
}
// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC
// method
message QueryContractInfoRequest {
// address is the address of the contract to query
string address = 1;
}
// QueryContractInfoResponse is the response type for the Query/ContractInfo RPC
// method
message QueryContractInfoResponse {
option (gogoproto.equal) = true;
// address is the address of the contract
string address = 1;
ContractInfo contract_info = 2 [
(gogoproto.embed) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = ""
];
}
// QueryContractHistoryRequest is the request type for the Query/ContractHistory
// RPC method
message QueryContractHistoryRequest {
// address is the address of the contract to query
string address = 1;
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryContractHistoryResponse is the response type for the
// Query/ContractHistory RPC method
message QueryContractHistoryResponse {
repeated ContractCodeHistoryEntry entries = 1
[ (gogoproto.nullable) = false ];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// QueryContractsByCodeRequest is the request type for the Query/ContractsByCode
// RPC method
message QueryContractsByCodeRequest {
uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryContractsByCodeResponse is the response type for the
// Query/ContractsByCode RPC method
message QueryContractsByCodeResponse {
// contracts are a set of contract addresses
repeated string contracts = 1;
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// QueryAllContractStateRequest is the request type for the
// Query/AllContractState RPC method
message QueryAllContractStateRequest {
// address is the address of the contract
string address = 1;
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryAllContractStateResponse is the response type for the
// Query/AllContractState RPC method
message QueryAllContractStateResponse {
repeated Model models = 1 [ (gogoproto.nullable) = false ];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// QueryRawContractStateRequest is the request type for the
// Query/RawContractState RPC method
message QueryRawContractStateRequest {
// address is the address of the contract
string address = 1;
bytes query_data = 2;
}
// QueryRawContractStateResponse is the response type for the
// Query/RawContractState RPC method
message QueryRawContractStateResponse {
// Data contains the raw store data
bytes data = 1;
}
// QuerySmartContractStateRequest is the request type for the
// Query/SmartContractState RPC method
message QuerySmartContractStateRequest {
// address is the address of the contract
string address = 1;
// QueryData contains the query data passed to the contract
bytes query_data = 2 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// QuerySmartContractStateResponse is the response type for the
// Query/SmartContractState RPC method
message QuerySmartContractStateResponse {
// Data contains the json data returned from the smart contract
bytes data = 1 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// QueryCodeRequest is the request type for the Query/Code RPC method
message QueryCodeRequest {
uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID
}
// CodeInfoResponse contains code meta data from CodeInfo
message CodeInfoResponse {
option (gogoproto.equal) = true;
uint64 code_id = 1 [
(gogoproto.customname) = "CodeID",
(gogoproto.jsontag) = "id"
]; // id for legacy support
string creator = 2;
bytes data_hash = 3
[ (gogoproto.casttype) =
"github.com/tendermint/tendermint/libs/bytes.HexBytes" ];
// Used in v1beta1
reserved 4, 5;
AccessConfig instantiate_permission = 6 [ (gogoproto.nullable) = false ];
}
// QueryCodeResponse is the response type for the Query/Code RPC method
message QueryCodeResponse {
option (gogoproto.equal) = true;
CodeInfoResponse code_info = 1
[ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ];
bytes data = 2 [ (gogoproto.jsontag) = "data" ];
}
// QueryCodesRequest is the request type for the Query/Codes RPC method
message QueryCodesRequest {
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}
// QueryCodesResponse is the response type for the Query/Codes RPC method
message QueryCodesResponse {
repeated CodeInfoResponse code_infos = 1 [ (gogoproto.nullable) = false ];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// QueryPinnedCodesRequest is the request type for the Query/PinnedCodes
// RPC method
message QueryPinnedCodesRequest {
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryPinnedCodesResponse is the response type for the
// Query/PinnedCodes RPC method
message QueryPinnedCodesResponse {
repeated uint64 code_ids = 1
[ (gogoproto.nullable) = false, (gogoproto.customname) = "CodeIDs" ];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// QueryParamsRequest is the request type for the Query/Params RPC method.
message QueryParamsRequest {}
// QueryParamsResponse is the response type for the Query/Params RPC method.
message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];
}
// QueryContractsByCreatorRequest is the request type for the
// Query/ContractsByCreator RPC method.
message QueryContractsByCreatorRequest {
// CreatorAddress is the address of contract creator
string creator_address = 1;
// Pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryContractsByCreatorResponse is the response type for the
// Query/ContractsByCreator RPC method.
message QueryContractsByCreatorResponse {
// ContractAddresses result set
repeated string contract_addresses = 1;
// Pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

192
proto/wasm/v1/tx.proto Normal file
View File

@ -0,0 +1,192 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";
import "wasm/v1/types.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_getters_all) = false;
// Msg defines the wasm Msg service.
service Msg {
// StoreCode to submit Wasm code to the system
rpc StoreCode(MsgStoreCode) returns (MsgStoreCodeResponse);
// InstantiateContract creates a new smart contract instance for the given
// code id.
rpc InstantiateContract(MsgInstantiateContract)
returns (MsgInstantiateContractResponse);
// InstantiateContract2 creates a new smart contract instance for the given
// code id with a predictable address
rpc InstantiateContract2(MsgInstantiateContract2)
returns (MsgInstantiateContract2Response);
// Execute submits the given message data to a smart contract
rpc ExecuteContract(MsgExecuteContract) returns (MsgExecuteContractResponse);
// Migrate runs a code upgrade/ downgrade for a smart contract
rpc MigrateContract(MsgMigrateContract) returns (MsgMigrateContractResponse);
// UpdateAdmin sets a new admin for a smart contract
rpc UpdateAdmin(MsgUpdateAdmin) returns (MsgUpdateAdminResponse);
// ClearAdmin removes any admin stored for a smart contract
rpc ClearAdmin(MsgClearAdmin) returns (MsgClearAdminResponse);
// UpdateInstantiateConfig updates instantiate config for a smart contract
rpc UpdateInstantiateConfig(MsgUpdateInstantiateConfig)
returns (MsgUpdateInstantiateConfigResponse);
}
// MsgStoreCode submit Wasm code to the system
message MsgStoreCode {
// Sender is the that actor that signed the messages
string sender = 1;
// WASMByteCode can be raw or gzip compressed
bytes wasm_byte_code = 2 [ (gogoproto.customname) = "WASMByteCode" ];
// Used in v1beta1
reserved 3, 4;
// InstantiatePermission access control to apply on contract creation,
// optional
AccessConfig instantiate_permission = 5;
}
// MsgStoreCodeResponse returns store result data.
message MsgStoreCodeResponse {
// CodeID is the reference to the stored WASM code
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
// Checksum is the sha256 hash of the stored code
bytes checksum = 2;
}
// MsgInstantiateContract create a new smart contract instance for the given
// code id.
message MsgInstantiateContract {
// Sender is the that actor that signed the messages
string sender = 1;
// Admin is an optional address that can execute migrations
string admin = 2;
// CodeID is the reference to the stored WASM code
uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ];
// Label is optional metadata to be stored with a contract instance.
string label = 4;
// Msg json encoded message to be passed to the contract on instantiation
bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 6 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// MsgInstantiateContract2 create a new smart contract instance for the given
// code id with a predicable address.
message MsgInstantiateContract2 {
// Sender is the that actor that signed the messages
string sender = 1;
// Admin is an optional address that can execute migrations
string admin = 2;
// CodeID is the reference to the stored WASM code
uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ];
// Label is optional metadata to be stored with a contract instance.
string label = 4;
// Msg json encoded message to be passed to the contract on instantiation
bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 6 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Salt is an arbitrary value provided by the sender. Size can be 1 to 64.
bytes salt = 7;
// FixMsg include the msg value into the hash for the predictable address.
// Default is false
bool fix_msg = 8;
}
// MsgInstantiateContractResponse return instantiation result data
message MsgInstantiateContractResponse {
// Address is the bech32 address of the new contract instance.
string address = 1;
// Data contains bytes to returned from the contract
bytes data = 2;
}
// MsgInstantiateContract2Response return instantiation result data
message MsgInstantiateContract2Response {
// Address is the bech32 address of the new contract instance.
string address = 1;
// Data contains bytes to returned from the contract
bytes data = 2;
}
// MsgExecuteContract submits the given message data to a smart contract
message MsgExecuteContract {
// Sender is the that actor that signed the messages
string sender = 1;
// Contract is the address of the smart contract
string contract = 2;
// Msg json encoded message to be passed to the contract
bytes msg = 3 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on execution
repeated cosmos.base.v1beta1.Coin funds = 5 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// MsgExecuteContractResponse returns execution result data.
message MsgExecuteContractResponse {
// Data contains bytes to returned from the contract
bytes data = 1;
}
// MsgMigrateContract runs a code upgrade/ downgrade for a smart contract
message MsgMigrateContract {
// Sender is the that actor that signed the messages
string sender = 1;
// Contract is the address of the smart contract
string contract = 2;
// CodeID references the new WASM code
uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ];
// Msg json encoded message to be passed to the contract on migration
bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// MsgMigrateContractResponse returns contract migration result data.
message MsgMigrateContractResponse {
// Data contains same raw bytes returned as data from the wasm contract.
// (May be empty)
bytes data = 1;
}
// MsgUpdateAdmin sets a new admin for a smart contract
message MsgUpdateAdmin {
// Sender is the that actor that signed the messages
string sender = 1;
// NewAdmin address to be set
string new_admin = 2;
// Contract is the address of the smart contract
string contract = 3;
}
// MsgUpdateAdminResponse returns empty data
message MsgUpdateAdminResponse {}
// MsgClearAdmin removes any admin stored for a smart contract
message MsgClearAdmin {
// Sender is the actor that signed the messages
string sender = 1;
// Contract is the address of the smart contract
string contract = 3;
}
// MsgClearAdminResponse returns empty data
message MsgClearAdminResponse {}
// MsgUpdateInstantiateConfig updates instantiate config for a smart contract
message MsgUpdateInstantiateConfig {
// Sender is the that actor that signed the messages
string sender = 1;
// CodeID references the stored WASM code
uint64 code_id = 2 [ (gogoproto.customname) = "CodeID" ];
// NewInstantiatePermission is the new access control
AccessConfig new_instantiate_permission = 3;
}
// MsgUpdateInstantiateConfigResponse returns empty data
message MsgUpdateInstantiateConfigResponse {}

144
proto/wasm/v1/types.proto Normal file
View File

@ -0,0 +1,144 @@
syntax = "proto3";
package cosmwasm.wasm.v1;
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
option go_package = "github.com/cerc-io/laconicd/x/wasm/types";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.equal_all) = true;
// AccessType permission types
enum AccessType {
option (gogoproto.goproto_enum_prefix) = false;
option (gogoproto.goproto_enum_stringer) = false;
// AccessTypeUnspecified placeholder for empty value
ACCESS_TYPE_UNSPECIFIED = 0
[ (gogoproto.enumvalue_customname) = "AccessTypeUnspecified" ];
// AccessTypeNobody forbidden
ACCESS_TYPE_NOBODY = 1
[ (gogoproto.enumvalue_customname) = "AccessTypeNobody" ];
// AccessTypeOnlyAddress restricted to a single address
// Deprecated: use AccessTypeAnyOfAddresses instead
ACCESS_TYPE_ONLY_ADDRESS = 2
[ (gogoproto.enumvalue_customname) = "AccessTypeOnlyAddress" ];
// AccessTypeEverybody unrestricted
ACCESS_TYPE_EVERYBODY = 3
[ (gogoproto.enumvalue_customname) = "AccessTypeEverybody" ];
// AccessTypeAnyOfAddresses allow any of the addresses
ACCESS_TYPE_ANY_OF_ADDRESSES = 4
[ (gogoproto.enumvalue_customname) = "AccessTypeAnyOfAddresses" ];
}
// AccessTypeParam
message AccessTypeParam {
option (gogoproto.goproto_stringer) = true;
AccessType value = 1 [ (gogoproto.moretags) = "yaml:\"value\"" ];
}
// AccessConfig access control type.
message AccessConfig {
option (gogoproto.goproto_stringer) = true;
AccessType permission = 1 [ (gogoproto.moretags) = "yaml:\"permission\"" ];
// Address
// Deprecated: replaced by addresses
string address = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ];
repeated string addresses = 3 [ (gogoproto.moretags) = "yaml:\"addresses\"" ];
}
// Params defines the set of wasm parameters.
message Params {
option (gogoproto.goproto_stringer) = false;
AccessConfig code_upload_access = 1 [
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"code_upload_access\""
];
AccessType instantiate_default_permission = 2
[ (gogoproto.moretags) = "yaml:\"instantiate_default_permission\"" ];
}
// CodeInfo is data for the uploaded contract WASM code
message CodeInfo {
// CodeHash is the unique identifier created by wasmvm
bytes code_hash = 1;
// Creator address who initially stored the code
string creator = 2;
// Used in v1beta1
reserved 3, 4;
// InstantiateConfig access control to apply on contract creation, optional
AccessConfig instantiate_config = 5 [ (gogoproto.nullable) = false ];
}
// ContractInfo stores a WASM contract instance
message ContractInfo {
option (gogoproto.equal) = true;
// CodeID is the reference to the stored Wasm code
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
// Creator address who initially instantiated the contract
string creator = 2;
// Admin is an optional address that can execute migrations
string admin = 3;
// Label is optional metadata to be stored with a contract instance.
string label = 4;
// Created Tx position when the contract was instantiated.
AbsoluteTxPosition created = 5;
string ibc_port_id = 6 [ (gogoproto.customname) = "IBCPortID" ];
// Extension is an extension point to store custom metadata within the
// persistence model.
google.protobuf.Any extension = 7
[ (cosmos_proto.accepts_interface) = "ContractInfoExtension" ];
}
// ContractCodeHistoryOperationType actions that caused a code change
enum ContractCodeHistoryOperationType {
option (gogoproto.goproto_enum_prefix) = false;
// ContractCodeHistoryOperationTypeUnspecified placeholder for empty value
CONTRACT_CODE_HISTORY_OPERATION_TYPE_UNSPECIFIED = 0
[ (gogoproto.enumvalue_customname) =
"ContractCodeHistoryOperationTypeUnspecified" ];
// ContractCodeHistoryOperationTypeInit on chain contract instantiation
CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT = 1
[ (gogoproto.enumvalue_customname) =
"ContractCodeHistoryOperationTypeInit" ];
// ContractCodeHistoryOperationTypeMigrate code migration
CONTRACT_CODE_HISTORY_OPERATION_TYPE_MIGRATE = 2
[ (gogoproto.enumvalue_customname) =
"ContractCodeHistoryOperationTypeMigrate" ];
// ContractCodeHistoryOperationTypeGenesis based on genesis data
CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS = 3
[ (gogoproto.enumvalue_customname) =
"ContractCodeHistoryOperationTypeGenesis" ];
}
// ContractCodeHistoryEntry metadata to a contract.
message ContractCodeHistoryEntry {
ContractCodeHistoryOperationType operation = 1;
// CodeID is the reference to the stored WASM code
uint64 code_id = 2 [ (gogoproto.customname) = "CodeID" ];
// Updated Tx position when the operation was executed.
AbsoluteTxPosition updated = 3;
bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ];
}
// AbsoluteTxPosition is a unique transaction position that allows for global
// ordering of transactions.
message AbsoluteTxPosition {
// BlockHeight is the block the contract was created at
uint64 block_height = 1;
// TxIndex is a monotonic counter within the block (actual transaction index,
// or gas consumed)
uint64 tx_index = 2;
}
// Model is a struct that holds a KV pair
message Model {
// hex-encode key to read it better (this is often ascii)
bytes key = 1 [ (gogoproto.casttype) =
"github.com/tendermint/tendermint/libs/bytes.HexBytes" ];
// base64-encode raw value
bytes value = 2;
}

View File

@ -1,4 +1,4 @@
# Validator Guide for laconic_81337-6 Testnet
# Validator Guide for laconic_81337-5 Testnet
## Hardware Prerequisites
@ -48,8 +48,8 @@ sudo apt install git curl build-essential make jq -y
# Remove any existing installation of `go`
sudo rm -rf /usr/local/go
# Install Go version 1.19.7
curl https://dl.google.com/go/go1.19.7.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf -
# Install Go version 1.18.8
curl https://dl.google.com/go/go1.18.8.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf -
# Update env variables to include go
cat <<'EOF' >>$HOME/.profile
@ -67,7 +67,7 @@ Check the version of go installed
```sh
go version
# Should return something like: go version go1.19.7 linux/amd64
# Should return something like: go version go1.17.2 linux/amd64
```
---
@ -80,17 +80,16 @@ cd laconicd
# Checkout main branch
git fetch --all
git checkout v0.8.0
git checkout v0.6.0
# Build and install laconic
make VERSION=v0.8.0 install
make install
```
Verify your installation
```sh
laconicd version --long
```
On running the above command, you should see a similar response like this. Make sure that the _version_ and _commit
@ -123,13 +122,13 @@ Make sure the directory `~/.laconicd` does not exist or is empty
>In order to run the below commands in a docker container:
>```sh
>docker run -ti -v ~/.laconicd:/root/.laconicd \
>git.vdb.to/cerc-io/laconicd/laconicd:v0.8.0 /bin/sh
>git.vdb.to/cerc-io/laconicd/laconicd:v0.6.0 /bin/sh
>```
---
```sh
# Initialize the validator node
laconicd init <your-node-moniker> --chain-id laconic_81337-6
laconicd init <your-node-moniker> --chain-id laconic_81337-5
```
Running the above commands will initialize the validator node with default configuration. The config files will be saved in the default location (`~/.laconicd/config`).
@ -169,7 +168,7 @@ Create Your `gentx` transaction file
```sh
laconicd gentx <key-name> 12900000000000000000000achk \
--pubkey=$(laconicd tendermint show-validator) \
--chain-id="laconic_81337-6" \
--chain-id="laconic_81337-5" \
--moniker="<your-moniker-name>" \
--website="<your-validator-website>" \
--details="<your-validator-description>" \
@ -199,10 +198,10 @@ Submit your `gentx` file to the [https://github.com/cerc-io/laconic-testnet](htt
To submit the gentx file, follow the below process:
- Fork the [https://github.com/cerc-io/laconic-testnet](https://github.com/cerc-io/laconic-testnet) repository
- Upload your gentx file in the `laconic_81337-6/config/gentxs` folder
- Upload your gentx file in the `laconic_81337-5/config/gentxs` folder
- Submit Pull Request to [https://github.com/cerc-io/laconic-testnet](https://github.com/cerc-io/laconic-testnet) with name `ADD <your-moniker> gentx`
The genesis file will be published in the `laconic_81337-6/config/` folder within the [https://github.com/cerc-io/laconic-testnet](https://github.com/cerc-io/laconic-testnet) repository.
The genesis file will be published in the `laconic_81337-5/config/` folder within the [https://github.com/cerc-io/laconic-testnet](https://github.com/cerc-io/laconic-testnet) repository.
# CONTINUE WITH BELOW STEPS ONLY AFTER GENESIS FILE HAS BEEN PUBLISHED
@ -272,33 +271,25 @@ journalctl -f -u laconicd
In this example the Tendermint RPC and Prometheus metrics ports are exposed only to localhost. You may want to change 127.0.0.1 to private or public network interface of your host if you need to access these ports remotely.
```sh
docker create \
--name laconic-testnet-6 \
--restart always \
-v ~/.laconicd:/root/.laconicd \
-p 26656:26656 \
-p 127.0.0.1:26657:26657 \
-p 127.0.0.1:26660:26660 \
git.vdb.to/cerc-io/laconicd/laconicd:v0.8.0 \
laconicd start --gql-playground --gql-server --log_level=warn
docker create --name laconic-testnet-5 -v ~/.laconicd:/root/.laconicd -p 26656:26656 -p 127.0.0.1:26657:26657 -p 127.0.0.1:26660:26660 git.vdb.to/cerc-io/laconicd/laconicd:v0.6.0 laconicd start --gql-playground --gql-server --log_level=warn
```
### Run validator node
```sh
docker start laconic-testnet-6
docker start laconic-testnet-5
```
### Check validator node logs
```sh
docker logs laconic-testnet-6
docker logs laconic-testnet-5
```
### Run shell inside docker container
```sh
docker exec -ti laconic-testnet-6 /bin/sh
docker exec -ti laconic-testnet-5 /bin/sh
```
---
## Helpful commands

View File

@ -10,8 +10,9 @@ laconicd_rest_endpoint=http://laconicd:1317
laconicd_gql_endpoint=http://laconicd:9473/api
# Run tests
docker network inspect sdk_tests_default
docker compose logs laconicd
docker compose exec laconicd sh -c "curl --retry 10 --retry-delay 3 --retry-connrefused http://127.0.0.1:9473/api"
docker compose exec laconicd sh -c "curl --retry 10 --retry-delay 3 --retry-connrefused http://localhost:9473/api"
sleep 30s
docker logs laconicd
docker compose exec laconicd sh -c "curl http://127.0.0.1:9473/api"
docker compose exec laconicd sh -c "curl http://localhost:9473/api"
docker compose exec sdk-test-runner sh -c "COSMOS_CHAIN_ID=${cosmos_chain_id} LACONICD_REST_ENDPOINT=${laconicd_rest_endpoint} LACONICD_GQL_ENDPOINT=${laconicd_gql_endpoint} PRIVATE_KEY=${laconicd_key} yarn test"

View File

@ -10,6 +10,7 @@ import (
canonicalJson "github.com/gibson042/canonicaljson-go"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/codec/dagjson"
"github.com/ipld/go-ipld-prime/linking"
@ -17,6 +18,7 @@ import (
"github.com/ipld/go-ipld-prime/multicodec"
basicnode "github.com/ipld/go-ipld-prime/node/basic"
"github.com/ipld/go-ipld-prime/storage/memstore"
mh "github.com/multiformats/go-multihash"
)
var store = memstore.Store{}
@ -33,7 +35,7 @@ func GenerateHash(json map[string]interface{}) (string, []byte, error) {
return "", nil, err
}
cidString, err := CIDFromJSONBytes(content)
cidString, err := CIDFromJSONBytesUsingIpldPrime(content)
if err != nil {
return "", nil, err
}
@ -41,6 +43,16 @@ func GenerateHash(json map[string]interface{}) (string, []byte, error) {
return cidString, content, nil
}
// CIDFromJSONBytes returns CID (cbor) for json (as bytes).
func CIDFromJSONBytes(content []byte) (string, error) {
cid, err := cbor.FromJSON(bytes.NewReader(content), mh.SHA2_256, -1)
if err != nil {
return "", err
}
return cid.String(), nil
}
// GetAttributeAsString returns a map attribute as string, if possible.
func GetAttributeAsString(obj map[string]interface{}, attr string) (string, error) {
if value, ok := obj[attr]; ok {
@ -54,14 +66,10 @@ func GetAttributeAsString(obj map[string]interface{}, attr string) (string, erro
return "", errors.New("attribute not found")
}
// CIDFromJSONBytes returns CID (dagcbor) for json (as bytes).
// CIDFromJSONBytesUsingIpldPrime returns CID (dagcbor) for json (as bytes).
// This is combination of samples for unmarshalling and linking
// see: https://pkg.go.dev/github.com/ipld/go-ipld-prime
func CIDFromJSONBytes(content []byte) (string, error) {
if len(content) == 0 {
return "", nil
}
func CIDFromJSONBytesUsingIpldPrime(content []byte) (string, error) {
np := basicnode.Prototype.Any // Pick a stle for the in-memory data.
nb := np.NewBuilder() // Create a builder.
err := dagjson.Decode(nb, bytes.NewReader(content)) // Hand the builder to decoding -- decoding will fill it in!

View File

@ -1,9 +1,8 @@
package utils
import (
"testing"
"github.com/stretchr/testify/require"
"testing"
)
func TestAndValidateCIDGeneration(t *testing.T) {
@ -12,12 +11,13 @@ func TestAndValidateCIDGeneration(t *testing.T) {
content string
expected string
}{
{
"empty string", "", "",
},
{
"empty json", "{}", "bafyreigbtj4x7ip5legnfznufuopl4sg4knzc2cof6duas4b3q2fy6swua",
},
// empty string and empty json blows up
// {
// "empty string", "", "bafyreiengp2sbi6ez34a2jctv34bwyjl7yoliteleaswgcwtqzrhmpyt2m",
// },
// {
// "empty json", "{}", "bafyreihpfkdvib5muloxlj5b3tgdwibjdcu3zdsuhyft33z7gtgnlzlkpm",
// },
{
"test record", "{\"build_artifact_cid\":\"QmP8jTG1m9GSDJLCbeWhVSVgEzCPPwXRdCRuJtQ5Tz9Kc9\",\"repo_registration_record_cid\":\"QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D\",\"tls_cert_cid\":\"QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\",\"type\":\"WebsiteRegistrationRecord\",\"url\":\"https://cerc.io\",\"version\":\"0.0.1\"}",
@ -26,8 +26,10 @@ func TestAndValidateCIDGeneration(t *testing.T) {
}
for _, tc := range testCases {
newImpl, err := CIDFromJSONBytes([]byte(tc.content))
deprecatedAndCorrect, _ := CIDFromJSONBytes([]byte(tc.content))
newImpl, err := CIDFromJSONBytesUsingIpldPrime([]byte(tc.content))
require.NoError(t, err)
require.Equal(t, deprecatedAndCorrect, newImpl, tc.name)
require.Equal(t, tc.expected, newImpl)
}
}

View File

@ -3,7 +3,6 @@ package keeper
import (
"context"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -27,7 +26,7 @@ func (q Querier) Auctions(c context.Context, req *types.AuctionsRequest) (*types
func (q Querier) GetAuction(c context.Context, req *types.AuctionRequest) (*types.AuctionResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
if req.Id == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
}
resp := q.Keeper.GetAuction(ctx, req.Id)
@ -38,10 +37,10 @@ func (q Querier) GetAuction(c context.Context, req *types.AuctionRequest) (*type
func (q Querier) GetBid(c context.Context, req *types.BidRequest) (*types.BidResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
if req.AuctionId == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
}
if req.Bidder == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "bidder address is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "bidder address is required")
}
resp := q.Keeper.GetBid(ctx, req.AuctionId, req.Bidder)
return &types.BidResponse{Bid: &resp}, nil
@ -51,7 +50,7 @@ func (q Querier) GetBid(c context.Context, req *types.BidRequest) (*types.BidRes
func (q Querier) GetBids(c context.Context, req *types.BidsRequest) (*types.BidsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
if req.AuctionId == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "auction ID is required")
}
resp := q.Keeper.GetBids(ctx, req.AuctionId)
return &types.BidsResponse{Bids: resp}, nil
@ -61,7 +60,7 @@ func (q Querier) GetBids(c context.Context, req *types.BidsRequest) (*types.Bids
func (q Querier) AuctionsByBidder(c context.Context, req *types.AuctionsByBidderRequest) (*types.AuctionsByBidderResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
if req.BidderAddress == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "bidder address is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "bidder address is required")
}
resp := q.Keeper.QueryAuctionsByBidder(ctx, req.BidderAddress)
return &types.AuctionsByBidderResponse{Auctions: &types.Auctions{Auctions: resp}}, nil
@ -71,7 +70,7 @@ func (q Querier) AuctionsByBidder(c context.Context, req *types.AuctionsByBidder
func (q Querier) AuctionsByOwner(c context.Context, req *types.AuctionsByOwnerRequest) (*types.AuctionsByOwnerResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
if req.OwnerAddress == "" {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "owner address is required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "owner address is required")
}
resp := q.Keeper.QueryAuctionsByOwner(ctx, req.OwnerAddress)
return &types.AuctionsByOwnerResponse{Auctions: &types.Auctions{Auctions: resp}}, nil

View File

@ -6,7 +6,6 @@ import (
"fmt"
"time"
errorsmod "cosmossdk.io/errors"
"github.com/cerc-io/laconicd/x/auction/types"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
@ -307,7 +306,7 @@ func (k Keeper) CreateAuction(ctx sdk.Context, msg types.MsgCreateAuction) (*typ
// Generate auction Id.
account := k.accountKeeper.GetAccount(ctx, signerAddress)
if account == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "Account not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "Account not found.")
}
auctionID := types.AuctionID{
@ -341,12 +340,12 @@ func (k Keeper) CreateAuction(ctx sdk.Context, msg types.MsgCreateAuction) (*typ
func (k Keeper) CommitBid(ctx sdk.Context, msg types.MsgCommitBid) (*types.Bid, error) {
if !k.HasAuction(ctx, msg.AuctionId) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Auction not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Auction not found.")
}
auction := k.GetAuction(ctx, msg.AuctionId)
if auction.Status != types.AuctionStatusCommitPhase {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Auction is not in commit phase.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Auction is not in commit phase.")
}
signerAddress, err := sdk.AccAddressFromBech32(msg.Signer)
@ -391,12 +390,12 @@ func (k Keeper) CommitBid(ctx sdk.Context, msg types.MsgCommitBid) (*types.Bid,
// RevealBid reeals a bid committed earlier.
func (k Keeper) RevealBid(ctx sdk.Context, msg types.MsgRevealBid) (*types.Auction, error) {
if !k.HasAuction(ctx, msg.AuctionId) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Auction not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Auction not found.")
}
auction := k.GetAuction(ctx, msg.AuctionId)
if auction.Status != types.AuctionStatusRevealPhase {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Auction is not in reveal phase.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Auction is not in reveal phase.")
}
signerAddress, err := sdk.AccAddressFromBech32(msg.Signer)
@ -405,65 +404,65 @@ func (k Keeper) RevealBid(ctx sdk.Context, msg types.MsgRevealBid) (*types.Aucti
}
if !k.HasBid(ctx, msg.AuctionId, signerAddress.String()) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bid not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bid not found.")
}
bid := k.GetBid(ctx, auction.Id, signerAddress.String())
if bid.Status != types.BidStatusCommitted {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bid not in committed state.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bid not in committed state.")
}
revealBytes, err := hex.DecodeString(msg.Reveal)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal string.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal string.")
}
cid, err := wnsUtils.CIDFromJSONBytes(revealBytes)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal JSON.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal JSON.")
}
if bid.CommitHash != cid {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Commit hash mismatch.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Commit hash mismatch.")
}
var reveal map[string]interface{}
err = json.Unmarshal(revealBytes, &reveal)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Reveal JSON unmarshal error.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Reveal JSON unmarshal error.")
}
chainID, err := wnsUtils.GetAttributeAsString(reveal, "chainId")
if err != nil || chainID != ctx.ChainID() {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal chainID.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal chainID.")
}
auctionID, err := wnsUtils.GetAttributeAsString(reveal, "auctionId")
if err != nil || auctionID != msg.AuctionId {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal auction Id.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal auction Id.")
}
bidderAddress, err := wnsUtils.GetAttributeAsString(reveal, "bidderAddress")
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid address.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid address.")
}
if bidderAddress != signerAddress.String() {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Reveal bid address mismatch.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Reveal bid address mismatch.")
}
bidAmountStr, err := wnsUtils.GetAttributeAsString(reveal, "bidAmount")
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid amount.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid amount.")
}
bidAmount, err := sdk.ParseCoinNormalized(bidAmountStr)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid amount.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid reveal bid amount.")
}
if bidAmount.IsLT(auction.MinimumBid) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bid is lower than minimum bid.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bid is lower than minimum bid.")
}
// Lock bid amount.

View File

@ -1,7 +1,6 @@
package types
import (
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -33,19 +32,19 @@ func (msg MsgCreateAuction) Type() string { return "create" }
// ValidateBasic Implements Msg.
func (msg MsgCreateAuction) ValidateBasic() error {
if msg.Signer == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
if msg.CommitsDuration <= 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "commit phase duration invalid.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "commit phase duration invalid.")
}
if msg.RevealsDuration <= 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "reveal phase duration invalid.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "reveal phase duration invalid.")
}
if !msg.MinimumBid.IsPositive() {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum bid should be greater than zero.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "minimum bid should be greater than zero.")
}
return nil
@ -80,15 +79,15 @@ func (msg MsgCommitBid) Type() string { return "commit" }
// ValidateBasic Implements Msg.
func (msg MsgCommitBid) ValidateBasic() error {
if msg.Signer == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer address.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer address.")
}
if msg.AuctionId == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid auction ID.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid auction ID.")
}
if msg.CommitHash == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid commit hash.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid commit hash.")
}
return nil
@ -123,15 +122,15 @@ func (msg MsgRevealBid) Type() string { return "reveal" }
// ValidateBasic Implements Msg.
func (msg MsgRevealBid) ValidateBasic() error {
if msg.Signer == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer address.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer address.")
}
if msg.AuctionId == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid auction ID.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid auction ID.")
}
if msg.Reveal == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid reveal data.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid reveal data.")
}
return nil

View File

@ -1,337 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: vulcanize/auction/v1beta1/tx.proto
/*
Package types is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package types
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
var (
filter_Msg_CreateAuction_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_CreateAuction_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCreateAuction
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CreateAuction_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CreateAuction(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_CreateAuction_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCreateAuction
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CreateAuction_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CreateAuction(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_CommitBid_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_CommitBid_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCommitBid
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CommitBid_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CommitBid(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_CommitBid_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCommitBid
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CommitBid_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CommitBid(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_RevealBid_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_RevealBid_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRevealBid
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RevealBid_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.RevealBid(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_RevealBid_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRevealBid
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RevealBid_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RevealBid(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux".
// UnaryRPC :call MsgServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead.
func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error {
mux.Handle("POST", pattern_Msg_CreateAuction_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_CreateAuction_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CreateAuction_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_CommitBid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_CommitBid_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CommitBid_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RevealBid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_RevealBid_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RevealBid_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMsgHandler(ctx, mux, conn)
}
// RegisterMsgHandler registers the http handlers for service Msg to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn))
}
// RegisterMsgHandlerClient registers the http handlers for service Msg
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MsgClient" to call the correct interceptors.
func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error {
mux.Handle("POST", pattern_Msg_CreateAuction_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_CreateAuction_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CreateAuction_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_CommitBid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_CommitBid_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CommitBid_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RevealBid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_RevealBid_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RevealBid_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Msg_CreateAuction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "auction", "v1beta1", "create_auction"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_CommitBid_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "auction", "v1beta1", "commit_bid"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_RevealBid_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "auction", "v1beta1", "reveal_bid"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Msg_CreateAuction_0 = runtime.ForwardResponseMessage
forward_Msg_CommitBid_0 = runtime.ForwardResponseMessage
forward_Msg_RevealBid_0 = runtime.ForwardResponseMessage
)

View File

@ -3,7 +3,6 @@ package keeper
import (
"context"
errorsmod "cosmossdk.io/errors"
"github.com/cerc-io/laconicd/x/bond/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -31,7 +30,7 @@ func (q Querier) GetBondByID(c context.Context, req *types.QueryGetBondByIDReque
ctx := sdk.UnwrapSDKContext(c)
bondID := req.GetId()
if len(bondID) == 0 {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "bond id required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "bond id required")
}
bond := q.Keeper.GetBond(ctx, req.GetId())
return &types.QueryGetBondByIDResponse{Bond: &bond}, nil
@ -41,7 +40,7 @@ func (q Querier) GetBondsByOwner(c context.Context, req *types.QueryGetBondsByOw
ctx := sdk.UnwrapSDKContext(c)
owner := req.GetOwner()
if len(owner) == 0 {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "owner id required")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "owner id required")
}
bonds := q.Keeper.QueryBondsByOwner(ctx, owner)
return &types.QueryGetBondsByOwnerResponse{Bonds: bonds}, nil

View File

@ -5,7 +5,6 @@ import (
"encoding/hex"
"fmt"
errorsmod "cosmossdk.io/errors"
"github.com/cerc-io/laconicd/x/bond/types"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
@ -114,7 +113,7 @@ func (k Keeper) CreateBond(ctx sdk.Context, ownerAddress sdk.AccAddress, coins s
for _, coin := range coins {
balance := k.bankKeeper.HasBalance(ctx, ownerAddress, coin)
if !balance {
return nil, errorsmod.Wrap(sdkerrors.ErrInsufficientFunds, "failed to create bond; Insufficient funds")
return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "failed to create bond; Insufficient funds")
}
}
@ -130,7 +129,7 @@ func (k Keeper) CreateBond(ctx sdk.Context, ownerAddress sdk.AccAddress, coins s
bond := types.Bond{Id: bondID, Owner: ownerAddress.String(), Balance: coins}
if bond.Balance.IsAnyGT(maxBondAmount) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Max bond amount exceeded.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Max bond amount exceeded.")
}
// Move funds into the bond account module.
@ -222,18 +221,18 @@ func (k Keeper) QueryBondsByOwner(ctx sdk.Context, ownerAddress string) []types.
// RefillBond refills an existing bond.
func (k Keeper) RefillBond(ctx sdk.Context, id string, ownerAddress sdk.AccAddress, coins sdk.Coins) (*types.Bond, error) {
if !k.HasBond(ctx, id) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
bond := k.GetBond(ctx, id)
if bond.Owner != ownerAddress.String() {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
// Check if account has funds.
for _, coin := range coins {
if !k.bankKeeper.HasBalance(ctx, ownerAddress, coin) {
return nil, errorsmod.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient funds.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient funds.")
}
}
@ -241,7 +240,7 @@ func (k Keeper) RefillBond(ctx sdk.Context, id string, ownerAddress sdk.AccAddre
updatedBalance := bond.Balance.Add(coins...)
if updatedBalance.IsAnyGT(maxBondAmount) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Max bond amount exceeded.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Max bond amount exceeded.")
}
// Move funds into the bond account module.
@ -260,17 +259,17 @@ func (k Keeper) RefillBond(ctx sdk.Context, id string, ownerAddress sdk.AccAddre
// WithdrawBond withdraws funds from a bond.
func (k Keeper) WithdrawBond(ctx sdk.Context, id string, ownerAddress sdk.AccAddress, coins sdk.Coins) (*types.Bond, error) {
if !k.HasBond(ctx, id) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
bond := k.GetBond(ctx, id)
if bond.Owner != ownerAddress.String() {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
updatedBalance, isNeg := bond.Balance.SafeSub(coins...)
if isNeg {
return nil, errorsmod.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient bond balance.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient bond balance.")
}
// Move funds from the bond into the account.
@ -289,18 +288,18 @@ func (k Keeper) WithdrawBond(ctx sdk.Context, id string, ownerAddress sdk.AccAdd
// CancelBond cancels a bond, returning funds to the owner.
func (k Keeper) CancelBond(ctx sdk.Context, id string, ownerAddress sdk.AccAddress) (*types.Bond, error) {
if !k.HasBond(ctx, id) {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
bond := k.GetBond(ctx, id)
if bond.Owner != ownerAddress.String() {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
// Check if bond is used in other modules.
for _, usageKeeper := range k.usageKeepers {
if usageKeeper.UsesBond(ctx, id) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprintf("Bond in use by the '%s' module.", usageKeeper.ModuleName()))
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprintf("Bond in use by the '%s' module.", usageKeeper.ModuleName()))
}
}
@ -331,7 +330,7 @@ func (k Keeper) GetBondModuleBalances(ctx sdk.Context) sdk.Coins {
// TransferCoinsToModuleAccount moves funds from the bonds module account to another module account.
func (k Keeper) TransferCoinsToModuleAccount(ctx sdk.Context, id, moduleAccount string, coins sdk.Coins) error {
if !k.HasBond(ctx, id) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond not found.")
}
bondObj := k.GetBond(ctx, id)
@ -341,13 +340,13 @@ func (k Keeper) TransferCoinsToModuleAccount(ctx sdk.Context, id, moduleAccount
if isNeg {
// Check if bond has sufficient funds.
return errorsmod.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient funds.")
return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient funds.")
}
// Move funds from bond module to record rent module.
err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, moduleAccount, coins)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Error transferring funds.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Error transferring funds.")
}
// Update bond balance.

View File

@ -1,7 +1,6 @@
package types
import (
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -29,10 +28,10 @@ func (msg MsgCreateBond) Type() string { return "create" }
func (msg MsgCreateBond) ValidateBasic() error {
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
if len(msg.Coins) == 0 || !msg.Coins.IsValid() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
}
return nil
}
@ -65,13 +64,13 @@ func (msg MsgRefillBond) Type() string { return "refill" }
func (msg MsgRefillBond) ValidateBasic() error {
if len(msg.Id) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
if len(msg.Coins) == 0 || !msg.Coins.IsValid() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
}
return nil
}
@ -104,13 +103,13 @@ func (msg MsgWithdrawBond) Type() string { return "withdraw" }
func (msg MsgWithdrawBond) ValidateBasic() error {
if len(msg.Id) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
if len(msg.Coins) == 0 || !msg.Coins.IsValid() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Invalid amount.")
}
return nil
}
@ -142,10 +141,10 @@ func (msg MsgCancelBond) Type() string { return "cancel" }
func (msg MsgCancelBond) ValidateBasic() error {
if len(msg.Id) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, msg.Id)
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
return nil
}

420
x/bond/types/tx.pb.gw.go generated
View File

@ -1,420 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: vulcanize/bond/v1beta1/tx.proto
/*
Package types is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package types
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
var (
filter_Msg_CreateBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_CreateBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCreateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CreateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CreateBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_CreateBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCreateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CreateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CreateBond(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_RefillBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_RefillBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRefillBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RefillBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.RefillBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_RefillBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRefillBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RefillBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RefillBond(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_WithdrawBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_WithdrawBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgWithdrawBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_WithdrawBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.WithdrawBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_WithdrawBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgWithdrawBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_WithdrawBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.WithdrawBond(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_CancelBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_CancelBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCancelBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CancelBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CancelBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_CancelBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgCancelBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_CancelBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CancelBond(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux".
// UnaryRPC :call MsgServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead.
func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error {
mux.Handle("POST", pattern_Msg_CreateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_CreateBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CreateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RefillBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_RefillBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RefillBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_WithdrawBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_WithdrawBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_WithdrawBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_CancelBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_CancelBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CancelBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMsgHandler(ctx, mux, conn)
}
// RegisterMsgHandler registers the http handlers for service Msg to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn))
}
// RegisterMsgHandlerClient registers the http handlers for service Msg
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MsgClient" to call the correct interceptors.
func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error {
mux.Handle("POST", pattern_Msg_CreateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_CreateBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CreateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RefillBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_RefillBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RefillBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_WithdrawBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_WithdrawBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_WithdrawBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_CancelBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_CancelBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_CancelBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Msg_CreateBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "bond", "v1beta1", "create_bond"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_RefillBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "bond", "v1beta1", "refill_bond"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_WithdrawBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "bond", "v1beta1", "withdraw_bond"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_CancelBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "bond", "v1beta1", "cancel_bond"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Msg_CreateBond_0 = runtime.ForwardResponseMessage
forward_Msg_RefillBond_0 = runtime.ForwardResponseMessage
forward_Msg_WithdrawBond_0 = runtime.ForwardResponseMessage
forward_Msg_CancelBond_0 = runtime.ForwardResponseMessage
)

View File

@ -6,8 +6,8 @@ import (
sdkmath "cosmossdk.io/math"
errorsmod "cosmossdk.io/errors"
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/params"
@ -91,61 +91,61 @@ func getBlockValue(block *sdkmath.Int) *big.Int {
// if any of the block values is uninitialized (i.e nil) or if the EIP150Hash is an invalid hash.
func (cc ChainConfig) Validate() error {
if err := validateBlock(cc.HomesteadBlock); err != nil {
return errorsmod.Wrap(err, "homesteadBlock")
return sdkerrors.Wrap(err, "homesteadBlock")
}
if err := validateBlock(cc.DAOForkBlock); err != nil {
return errorsmod.Wrap(err, "daoForkBlock")
return sdkerrors.Wrap(err, "daoForkBlock")
}
if err := validateBlock(cc.EIP150Block); err != nil {
return errorsmod.Wrap(err, "eip150Block")
return sdkerrors.Wrap(err, "eip150Block")
}
if err := validateHash(cc.EIP150Hash); err != nil {
return err
}
if err := validateBlock(cc.EIP155Block); err != nil {
return errorsmod.Wrap(err, "eip155Block")
return sdkerrors.Wrap(err, "eip155Block")
}
if err := validateBlock(cc.EIP158Block); err != nil {
return errorsmod.Wrap(err, "eip158Block")
return sdkerrors.Wrap(err, "eip158Block")
}
if err := validateBlock(cc.ByzantiumBlock); err != nil {
return errorsmod.Wrap(err, "byzantiumBlock")
return sdkerrors.Wrap(err, "byzantiumBlock")
}
if err := validateBlock(cc.ConstantinopleBlock); err != nil {
return errorsmod.Wrap(err, "constantinopleBlock")
return sdkerrors.Wrap(err, "constantinopleBlock")
}
if err := validateBlock(cc.PetersburgBlock); err != nil {
return errorsmod.Wrap(err, "petersburgBlock")
return sdkerrors.Wrap(err, "petersburgBlock")
}
if err := validateBlock(cc.IstanbulBlock); err != nil {
return errorsmod.Wrap(err, "istanbulBlock")
return sdkerrors.Wrap(err, "istanbulBlock")
}
if err := validateBlock(cc.MuirGlacierBlock); err != nil {
return errorsmod.Wrap(err, "muirGlacierBlock")
return sdkerrors.Wrap(err, "muirGlacierBlock")
}
if err := validateBlock(cc.BerlinBlock); err != nil {
return errorsmod.Wrap(err, "berlinBlock")
return sdkerrors.Wrap(err, "berlinBlock")
}
if err := validateBlock(cc.LondonBlock); err != nil {
return errorsmod.Wrap(err, "londonBlock")
return sdkerrors.Wrap(err, "londonBlock")
}
if err := validateBlock(cc.ArrowGlacierBlock); err != nil {
return errorsmod.Wrap(err, "arrowGlacierBlock")
return sdkerrors.Wrap(err, "arrowGlacierBlock")
}
if err := validateBlock(cc.MergeForkBlock); err != nil {
return errorsmod.Wrap(err, "mergeForkBlock")
return sdkerrors.Wrap(err, "mergeForkBlock")
}
// NOTE: chain ID is not needed to check config order
if err := cc.EthereumConfig(nil).CheckConfigForkOrder(); err != nil {
return errorsmod.Wrap(err, "invalid config fork order")
return sdkerrors.Wrap(err, "invalid config fork order")
}
return nil
}
func validateHash(hex string) error {
if hex != "" && strings.TrimSpace(hex) == "" {
return errorsmod.Wrap(types.ErrInvalidChainConfig, "hash cannot be blank")
return sdkerrors.Wrap(types.ErrInvalidChainConfig, "hash cannot be blank")
}
return nil
@ -158,7 +158,7 @@ func validateBlock(block *sdkmath.Int) error {
}
if block.IsNegative() {
return errorsmod.Wrapf(
return sdkerrors.Wrapf(
types.ErrInvalidChainConfig, "block value cannot be negative: %s", block,
)
}

View File

@ -8,8 +8,8 @@ import (
"github.com/cerc-io/laconicd/x/evm/types"
errorsmod "cosmossdk.io/errors"
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/params"
@ -90,61 +90,61 @@ func getBlockValue(block *sdkmath.Int) *big.Int {
// if any of the block values is uninitialized (i.e nil) or if the EIP150Hash is an invalid hash.
func (cc ChainConfig) Validate() error {
if err := validateBlock(cc.HomesteadBlock); err != nil {
return errorsmod.Wrap(err, "homesteadBlock")
return sdkerrors.Wrap(err, "homesteadBlock")
}
if err := validateBlock(cc.DAOForkBlock); err != nil {
return errorsmod.Wrap(err, "daoForkBlock")
return sdkerrors.Wrap(err, "daoForkBlock")
}
if err := validateBlock(cc.EIP150Block); err != nil {
return errorsmod.Wrap(err, "eip150Block")
return sdkerrors.Wrap(err, "eip150Block")
}
if err := validateHash(cc.EIP150Hash); err != nil {
return err
}
if err := validateBlock(cc.EIP155Block); err != nil {
return errorsmod.Wrap(err, "eip155Block")
return sdkerrors.Wrap(err, "eip155Block")
}
if err := validateBlock(cc.EIP158Block); err != nil {
return errorsmod.Wrap(err, "eip158Block")
return sdkerrors.Wrap(err, "eip158Block")
}
if err := validateBlock(cc.ByzantiumBlock); err != nil {
return errorsmod.Wrap(err, "byzantiumBlock")
return sdkerrors.Wrap(err, "byzantiumBlock")
}
if err := validateBlock(cc.ConstantinopleBlock); err != nil {
return errorsmod.Wrap(err, "constantinopleBlock")
return sdkerrors.Wrap(err, "constantinopleBlock")
}
if err := validateBlock(cc.PetersburgBlock); err != nil {
return errorsmod.Wrap(err, "petersburgBlock")
return sdkerrors.Wrap(err, "petersburgBlock")
}
if err := validateBlock(cc.IstanbulBlock); err != nil {
return errorsmod.Wrap(err, "istanbulBlock")
return sdkerrors.Wrap(err, "istanbulBlock")
}
if err := validateBlock(cc.MuirGlacierBlock); err != nil {
return errorsmod.Wrap(err, "muirGlacierBlock")
return sdkerrors.Wrap(err, "muirGlacierBlock")
}
if err := validateBlock(cc.BerlinBlock); err != nil {
return errorsmod.Wrap(err, "berlinBlock")
return sdkerrors.Wrap(err, "berlinBlock")
}
if err := validateBlock(cc.LondonBlock); err != nil {
return errorsmod.Wrap(err, "londonBlock")
return sdkerrors.Wrap(err, "londonBlock")
}
if err := validateBlock(cc.ArrowGlacierBlock); err != nil {
return errorsmod.Wrap(err, "arrowGlacierBlock")
return sdkerrors.Wrap(err, "arrowGlacierBlock")
}
if err := validateBlock(cc.MergeForkBlock); err != nil {
return errorsmod.Wrap(err, "mergeForkBlock")
return sdkerrors.Wrap(err, "mergeForkBlock")
}
// NOTE: chain ID is not needed to check config order
if err := cc.EthereumConfig(nil).CheckConfigForkOrder(); err != nil {
return errorsmod.Wrap(err, "invalid config fork order")
return sdkerrors.Wrap(err, "invalid config fork order")
}
return nil
}
func validateHash(hex string) error {
if hex != "" && strings.TrimSpace(hex) == "" {
return errorsmod.Wrap(types.ErrInvalidChainConfig, "hash cannot be blank")
return sdkerrors.Wrap(types.ErrInvalidChainConfig, "hash cannot be blank")
}
return nil
@ -157,7 +157,7 @@ func validateBlock(block *sdkmath.Int) error {
}
if block.IsNegative() {
return errorsmod.Wrapf(
return sdkerrors.Wrapf(
types.ErrInvalidChainConfig, "block value cannot be negative: %s", block,
)
}

View File

@ -15,8 +15,6 @@ import (
tmcli "github.com/tendermint/tendermint/libs/cli"
)
const badPath = "/asdasd"
func (s *IntegrationTestSuite) TestGRPCQueryParams() {
val := s.network.Validators[0]
sr := s.Require()
@ -30,7 +28,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryParams() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
},
@ -77,7 +75,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryWhoIs() {
}{
{
"invalid url",
reqUrl + badPath,
reqUrl + "/asdasd",
true,
"",
func(authorityName string) {
@ -145,7 +143,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryLookup() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
func(authorityName string) {
@ -197,7 +195,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryRecordExpiryQueue() {
}{
{
"invalid url",
reqUrl + badPath,
reqUrl + "/asdasd",
true,
"",
func(bondId string) {
@ -269,7 +267,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryAuthorityExpiryQueue() {
}{
{
"invalid url",
reqUrl + badPath,
reqUrl + "/asdasd",
true,
"",
func(authorityName string) {
@ -341,7 +339,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryListRecords() {
}{
{
"invalid url",
reqUrl + badPath,
reqUrl + "/asdasd",
true,
"",
func(bondId string) {
@ -411,7 +409,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRecordByID() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
func(bondId string) string {
@ -480,7 +478,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRecordByBondID() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
func(bondId string) {
@ -534,7 +532,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRegistryModuleBalance() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
func(bondId string) {
@ -585,7 +583,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryNamesList() {
}{
{
"invalid url",
reqURL + badPath,
reqURL + "/asdasd",
true,
"",
func(authorityName string) {

View File

@ -1,7 +0,0 @@
record:
type: GeneralRecord
name: foo
version: 1.0.0
tags:
- tagA
- tagB

View File

@ -70,7 +70,7 @@ func UnMarshalMapFromJSONBytes(bytes []byte) map[string]interface{} {
// GetCid gets the content ID.
func GetCid(content []byte) (string, error) {
return wnsUtils.CIDFromJSONBytes(content)
return wnsUtils.CIDFromJSONBytesUsingIpldPrime(content)
}
// BytesToBase64 encodes a byte array as a base64 string.

View File

@ -3,7 +3,6 @@ package keeper
import (
"context"
errorsmod "cosmossdk.io/errors"
"github.com/cerc-io/laconicd/x/registry/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -49,7 +48,7 @@ func (q Querier) GetRecord(c context.Context, req *types.QueryRecordByIDRequest)
ctx := sdk.UnwrapSDKContext(c)
id := req.GetId()
if !q.Keeper.HasRecord(ctx, id) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "Record not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "Record not found.")
}
record := q.Keeper.GetRecord(ctx, id)
return &types.QueryRecordByIDResponse{Record: record}, nil
@ -87,11 +86,11 @@ func (q Querier) LookupCrn(c context.Context, req *types.QueryLookupCrn) (*types
ctx := sdk.UnwrapSDKContext(c)
crn := req.GetCrn()
if !q.Keeper.HasNameRecord(ctx, crn) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "CRN not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "CRN not found.")
}
nameRecord := q.Keeper.GetNameRecord(ctx, crn)
if nameRecord == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "name record not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "name record not found.")
}
return &types.QueryLookupCrnResponse{Name: nameRecord}, nil
}
@ -101,7 +100,7 @@ func (q Querier) ResolveCrn(c context.Context, req *types.QueryResolveCrn) (*typ
crn := req.GetCrn()
record := q.Keeper.ResolveCRN(ctx, crn)
if record == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "record not found.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "record not found.")
}
return &types.QueryResolveCrnResponse{Record: record}, nil
}

View File

@ -3,12 +3,12 @@ package keeper_test
import (
"context"
"fmt"
"os"
"github.com/cerc-io/laconicd/x/registry/client/cli"
"github.com/cerc-io/laconicd/x/registry/helpers"
"github.com/cerc-io/laconicd/x/registry/keeper"
registrytypes "github.com/cerc-io/laconicd/x/registry/types"
"os"
"reflect"
)
func (suite *KeeperTestSuite) TestGrpcQueryParams() {
@ -39,7 +39,6 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
examples := []string{
"/../helpers/examples/service_provider_example.yml",
"/../helpers/examples/website_registration_example.yml",
"/../helpers/examples/general_record_example.yml",
}
testCases := []struct {
msg string
@ -60,7 +59,7 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
&registrytypes.QueryListRecordsRequest{},
true,
false,
3,
2,
},
{
"Filter with type",
@ -80,60 +79,6 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
false,
1,
},
{
"Filter with tag (extant) (https://git.vdb.to/cerc-io/laconicd/issues/129)",
&registrytypes.QueryListRecordsRequest{
Attributes: []*registrytypes.QueryListRecordsRequest_KeyValueInput{
{
Key: "tags",
Value: &registrytypes.QueryListRecordsRequest_ValueInput{
Type: "string",
String_: "tagA",
},
},
},
All: true,
},
true,
false,
1,
},
{
"Filter with tag (non-existent) (https://git.vdb.to/cerc-io/laconicd/issues/129)",
&registrytypes.QueryListRecordsRequest{
Attributes: []*registrytypes.QueryListRecordsRequest_KeyValueInput{
{
Key: "tags",
Value: &registrytypes.QueryListRecordsRequest_ValueInput{
Type: "string",
String_: "NOEXIST",
},
},
},
All: true,
},
true,
false,
0,
},
{
"Filter test for key collision (https://git.vdb.to/cerc-io/laconicd/issues/122)",
&registrytypes.QueryListRecordsRequest{
Attributes: []*registrytypes.QueryListRecordsRequest_KeyValueInput{
{
Key: "typ",
Value: &registrytypes.QueryListRecordsRequest_ValueInput{
Type: "string",
String_: "eWebsiteRegistrationRecord",
},
},
},
All: true,
},
true,
false,
0,
},
{
"Filter with attributes ServiceProviderRegistration",
&registrytypes.QueryListRecordsRequest{
@ -178,7 +123,7 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
} else {
sr.NoError(err)
sr.Equal(test.noOfRecords, len(resp.GetRecords()))
if test.createRecords && test.noOfRecords > 0 {
if test.createRecords {
recordId = resp.GetRecords()[0].GetId()
sr.NotZero(resp.GetRecords())
sr.Equal(resp.GetRecords()[0].GetBondId(), suite.bond.GetId())
@ -188,25 +133,10 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
sr.NoError(err)
recAttr := helpers.UnMarshalMapFromJSONBytes(bz)
for _, attr := range test.req.GetAttributes() {
av := keeper.GetAttributeValue(attr.Value)
if nil != av && nil != recAttr[attr.Key] &&
reflect.Slice == reflect.TypeOf(recAttr[attr.Key]).Kind() &&
reflect.Slice != reflect.TypeOf(av).Kind() {
found := false
allValues := recAttr[attr.Key].([]interface{})
for i := range allValues {
if av == allValues[i] {
fmt.Printf("Found %s in %s", allValues[i], recAttr[attr.Key])
found = true
}
}
sr.Equal(true, found, fmt.Sprintf("Unable to find %s in %s", av, recAttr[attr.Key]))
if attr.Key[:4] == "x500" {
sr.Equal(keeper.GetAttributeValue(attr.Value), recAttr["x500"].(map[string]interface{})[attr.Key[4:]])
} else {
if attr.Key[:4] == "x500" {
sr.Equal(av, recAttr["x500"].(map[string]interface{})[attr.Key[4:]])
} else {
sr.Equal(av, recAttr[attr.Key])
}
sr.Equal(keeper.GetAttributeValue(attr.Value), recAttr[attr.Key])
}
}
}

View File

@ -4,11 +4,9 @@ import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sort"
"time"
errorsmod "cosmossdk.io/errors"
auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper"
bondkeeper "github.com/cerc-io/laconicd/x/bond/keeper"
"github.com/cerc-io/laconicd/x/registry/helpers"
@ -21,7 +19,6 @@ import (
auth "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bank "github.com/cosmos/cosmos-sdk/x/bank/keeper"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/tendermint/tendermint/libs/log"
)
var (
@ -98,11 +95,6 @@ func NewKeeper(cdc codec.BinaryCodec, accountKeeper auth.AccountKeeper, bankKeep
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", types.ModuleName)
}
// GetRecordIndexKey Generates Bond ID -> Bond index key.
func GetRecordIndexKey(id string) []byte {
return append(PrefixCIDToRecordIndex, []byte(id)...)
@ -248,7 +240,7 @@ func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*type
resourceSignBytes, _ := record.GetSignBytes()
cid, err := record.GetCID()
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid record JSON")
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid record JSON")
}
record.ID = cid
@ -263,13 +255,13 @@ func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*type
pubKey, err := legacy.PubKeyFromBytes(helpers.BytesFromBase64(sig.PubKey))
if err != nil {
fmt.Println("Error decoding pubKey from bytes: ", err)
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid public key.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Invalid public key.")
}
sigOK := pubKey.VerifySignature(resourceSignBytes, helpers.BytesFromBase64(sig.Sig))
if !sigOK {
fmt.Println("Signature mismatch: ", sig.PubKey)
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid signature.")
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Invalid signature.")
}
record.Owners = append(record.Owners, pubKey.Address().String())
}
@ -345,26 +337,13 @@ func (k Keeper) ProcessAttributes(ctx sdk.Context, record types.RecordType) erro
}
}
}
case "WebsiteRegistrationRecord", "ApplicationRecord", "ApplicationDeploymentRequest",
"ApplicationDeploymentRecord", "ApplicationArtifact", "ApplicationDeploymentRemovalRequest",
"ApplicationDeploymentRemovalRecord", "DnsRecord", "GeneralRecord":
case "WebsiteRegistrationRecord":
{
// #nosec G705
for key := range record.Attributes {
attr := record.Attributes[key]
if reflect.Slice == reflect.TypeOf(attr).Kind() {
av := attr.([]interface{})
for i := range av {
indexKey := GetAttributesIndexKey(key, av[i])
if err := k.SetAttributeMapping(ctx, indexKey, record.ID); err != nil {
return err
}
}
} else {
indexKey := GetAttributesIndexKey(key, attr)
if err := k.SetAttributeMapping(ctx, indexKey, record.ID); err != nil {
return err
}
indexKey := GetAttributesIndexKey(key, record.Attributes[key])
if err := k.SetAttributeMapping(ctx, indexKey, record.ID); err != nil {
return err
}
}
}
@ -381,7 +360,7 @@ func (k Keeper) ProcessAttributes(ctx sdk.Context, record types.RecordType) erro
}
func GetAttributesIndexKey(key string, value interface{}) []byte {
keyString := fmt.Sprintf("%s=%s", key, value)
keyString := fmt.Sprintf("%s%s", key, value)
return append(PrefixAttributesIndex, []byte(keyString)...)
}
@ -409,8 +388,7 @@ func (k Keeper) GetAttributeMapping(ctx sdk.Context, key []byte) ([]string, erro
store := ctx.KVStore(k.storeKey)
if !store.Has(key) {
k.Logger(ctx).Debug(fmt.Sprintf("store doesn't have key: %q", key))
return []string{}, nil
return nil, fmt.Errorf("store doesn't have key")
}
var recordIds []string

View File

@ -7,7 +7,6 @@ import (
"strings"
"time"
errorsmod "cosmossdk.io/errors"
auctiontypes "github.com/cerc-io/laconicd/x/auction/types"
"github.com/cerc-io/laconicd/x/registry/helpers"
"github.com/cerc-io/laconicd/x/registry/types"
@ -113,12 +112,12 @@ func (k Keeper) updateBlockChangeSetForName(ctx sdk.Context, crn string) {
func (k Keeper) getAuthority(ctx sdk.Context, crn string) (string, *url.URL, *types.NameAuthority, error) {
parsedCRN, err := url.Parse(crn)
if err != nil {
return "", nil, nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.")
return "", nil, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.")
}
name := parsedCRN.Host
if !k.HasNameAuthority(ctx, name) {
return name, nil, nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.")
return name, nil, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.")
}
authority := k.GetNameAuthority(ctx, name)
return name, parsedCRN, &authority, nil
@ -132,19 +131,19 @@ func (k Keeper) checkCRNAccess(ctx sdk.Context, signer sdk.AccAddress, crn strin
formattedCRN := fmt.Sprintf("crn://%s%s", name, parsedCRN.RequestURI())
if formattedCRN != crn {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.")
}
if authority.OwnerAddress != signer.String() {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Access denied.")
}
if authority.Status != types.AuthorityActive {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Authority is not active.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Authority is not active.")
}
if authority.BondId == "" || len(authority.BondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Authority bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Authority bond not found.")
}
if authority.OwnerPublicKey == "" {
@ -324,13 +323,13 @@ func (k Keeper) ProcessReserveSubAuthority(ctx sdk.Context, name string, msg typ
// Check if parent authority exists.
if !k.HasNameAuthority(ctx, parent) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Parent authority not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Parent authority not found.")
}
parentAuthority := k.GetNameAuthority(ctx, parent)
// Sub-authority creator needs to be the owner of the parent authority.
if parentAuthority.OwnerAddress != msg.Signer {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Access denied.")
}
// Sub-authority owner defaults to parent authority owner.
@ -363,17 +362,17 @@ func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRo
if k.HasNameAuthority(ctx, name) {
authority := k.GetNameAuthority(ctx, name)
if authority.Status != types.AuthorityExpired {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name already reserved.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name already reserved.")
}
}
ownerAddress, err := sdk.AccAddressFromBech32(owner)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid owner address.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid owner address.")
}
ownerAccount := k.accountKeeper.GetAccount(ctx, ownerAddress)
if ownerAccount == nil {
return errorsmod.Wrap(sdkerrors.ErrUnknownAddress, "Account not found.")
return sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, "Account not found.")
}
authority := types.NameAuthority{
@ -431,11 +430,11 @@ func (k Keeper) ProcessReserveAuthority(ctx sdk.Context, msg types.MsgReserveAut
crn := fmt.Sprintf("crn://%s", msg.GetName())
parsedCrn, err := url.Parse(crn)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name")
}
name := parsedCrn.Host
if fmt.Sprintf("crn://%s", name) != crn {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name")
}
if strings.Contains(name, ".") {
return k.ProcessReserveSubAuthority(ctx, name, msg)
@ -451,20 +450,20 @@ func (k Keeper) ProcessSetAuthorityBond(ctx sdk.Context, msg types.MsgSetAuthori
name := msg.GetName()
signer := msg.GetSigner()
if !k.HasNameAuthority(ctx, name) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.")
}
authority := k.GetNameAuthority(ctx, name)
if authority.OwnerAddress != signer {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Access denied")
}
if !k.bondKeeper.HasBond(ctx, msg.BondId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
//
bond := k.bondKeeper.GetBond(ctx, msg.BondId)
if bond.Owner != signer {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
// No-op if bond hasn't changed.
@ -497,7 +496,7 @@ func (k Keeper) ProcessDeleteName(ctx sdk.Context, msg types.MsgDeleteNameAuthor
}
if !k.HasNameRecord(ctx, msg.Crn) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name not found.")
}
// Set CID to empty string.

View File

@ -4,7 +4,6 @@ import (
"fmt"
"time"
errorsmod "cosmossdk.io/errors"
auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper"
auctiontypes "github.com/cerc-io/laconicd/x/auction/types"
bondtypes "github.com/cerc-io/laconicd/x/bond/types"
@ -159,7 +158,7 @@ func (k RecordKeeper) QueryRecordsByBond(ctx sdk.Context, bondID string) []types
// ProcessRenewRecord renews a record.
func (k Keeper) ProcessRenewRecord(ctx sdk.Context, msg types.MsgRenewRecord) error {
if !k.HasRecord(ctx, msg.RecordId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
}
// Check if renewal is required (i.e. expired record marked as deleted).
@ -170,7 +169,7 @@ func (k Keeper) ProcessRenewRecord(ctx sdk.Context, msg types.MsgRenewRecord) er
}
if !record.Deleted || expiryTime.After(ctx.BlockTime()) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Renewal not required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Renewal not required.")
}
recordType := record.ToRecordType()
@ -185,23 +184,23 @@ func (k Keeper) ProcessRenewRecord(ctx sdk.Context, msg types.MsgRenewRecord) er
// ProcessAssociateBond associates a record with a bond.
func (k Keeper) ProcessAssociateBond(ctx sdk.Context, msg types.MsgAssociateBond) error {
if !k.HasRecord(ctx, msg.RecordId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
}
if !k.bondKeeper.HasBond(ctx, msg.BondId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
// Check if already associated with a bond.
record := k.GetRecord(ctx, msg.RecordId)
if record.BondId != "" || len(record.BondId) != 0 {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond already exists.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond already exists.")
}
// Only the bond owner can associate a record with the bond.
bond := k.bondKeeper.GetBond(ctx, msg.BondId)
if msg.Signer != bond.Owner {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
record.BondId = msg.BondId
@ -219,20 +218,20 @@ func (k Keeper) ProcessAssociateBond(ctx sdk.Context, msg types.MsgAssociateBond
// ProcessDissociateBond dissociates a record from its bond.
func (k Keeper) ProcessDissociateBond(ctx sdk.Context, msg types.MsgDissociateBond) error {
if !k.HasRecord(ctx, msg.RecordId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Record not found.")
}
// Check if associated with a bond.
record := k.GetRecord(ctx, msg.RecordId)
bondID := record.BondId
if bondID == "" || len(bondID) == 0 {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond not found.")
}
// Only the bond owner can dissociate a record from the bond.
bond := k.bondKeeper.GetBond(ctx, bondID)
if msg.Signer != bond.Owner {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
// Clear bond ID.
@ -246,13 +245,13 @@ func (k Keeper) ProcessDissociateBond(ctx sdk.Context, msg types.MsgDissociateBo
// ProcessDissociateRecords dissociates all records associated with a given bond.
func (k Keeper) ProcessDissociateRecords(ctx sdk.Context, msg types.MsgDissociateRecords) error {
if !k.bondKeeper.HasBond(ctx, msg.BondId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
}
// Only the bond owner can dissociate all records from the bond.
bond := k.bondKeeper.GetBond(ctx, msg.BondId)
if msg.Signer != bond.Owner {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.")
}
// Dissociate all records from the bond.
@ -270,22 +269,22 @@ func (k Keeper) ProcessDissociateRecords(ctx sdk.Context, msg types.MsgDissociat
// ProcessReAssociateRecords switches records from and old to new bond.
func (k Keeper) ProcessReAssociateRecords(ctx sdk.Context, msg types.MsgReAssociateRecords) error {
if !k.bondKeeper.HasBond(ctx, msg.OldBondId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Old bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Old bond not found.")
}
if !k.bondKeeper.HasBond(ctx, msg.NewBondId) {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "New bond not found.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "New bond not found.")
}
// Only the bond owner can re-associate all records.
oldBond := k.bondKeeper.GetBond(ctx, msg.OldBondId)
if msg.Signer != oldBond.Owner {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Old bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Old bond owner mismatch.")
}
newBond := k.bondKeeper.GetBond(ctx, msg.NewBondId)
if msg.Signer != newBond.Owner {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "New bond owner mismatch.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "New bond owner mismatch.")
}
// Re-associate all records.

File diff suppressed because it is too large Load Diff

View File

@ -50,54 +50,6 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
(*Attributes)(nil),
&WebsiteRegistrationRecord{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationRecord",
(*Attributes)(nil),
&ApplicationRecord{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationDeploymentRequest",
(*Attributes)(nil),
&ApplicationDeploymentRequest{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationDeploymentRecord",
(*Attributes)(nil),
&ApplicationDeploymentRecord{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationArtifact",
(*Attributes)(nil),
&ApplicationArtifact{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationDeploymentRemovalRequest",
(*Attributes)(nil),
&ApplicationDeploymentRemovalRequest{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.ApplicationDeploymentRemovalRecord",
(*Attributes)(nil),
&ApplicationDeploymentRemovalRecord{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.DnsRecord",
(*Attributes)(nil),
&DnsRecord{},
)
registry.RegisterInterface(
"vulcanize.registry.v1beta1.GeneralRecord",
(*Attributes)(nil),
&GeneralRecord{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

View File

@ -3,7 +3,6 @@ package types
import (
"net/url"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -33,15 +32,15 @@ func (msg MsgSetName) Type() string { return "set-name" }
// ValidateBasic Implements Msg.
func (msg MsgSetName) ValidateBasic() error {
if msg.Crn == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "CRN is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "CRN is required.")
}
if msg.Cid == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "CID is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "CID is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer")
}
return nil
@ -77,11 +76,11 @@ func (msg MsgReserveAuthority) Type() string { return "reserve-authority" }
// ValidateBasic Implements Msg.
func (msg MsgReserveAuthority) ValidateBasic() error {
if len(msg.Name) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "name is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer")
}
return nil
@ -117,15 +116,15 @@ func (msg MsgSetAuthorityBond) Type() string { return "authority-bond" }
// ValidateBasic Implements Msg.
func (msg MsgSetAuthorityBond) ValidateBasic() error {
if len(msg.Name) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "name is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
if len(msg.BondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "bond id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "bond id is required.")
}
return nil
@ -160,16 +159,16 @@ func (msg MsgDeleteNameAuthority) Type() string { return "delete-name" }
// ValidateBasic Implements Msg.
func (msg MsgDeleteNameAuthority) ValidateBasic() error {
if len(msg.Crn) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "crn is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "crn is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
_, err := url.Parse(msg.Crn)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid crn.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid crn.")
}
return nil

View File

@ -1,7 +1,6 @@
package types
import (
errorsmod "cosmossdk.io/errors"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -35,17 +34,17 @@ func (msg MsgSetRecord) Type() string { return "set-record" }
func (msg MsgSetRecord) ValidateBasic() error {
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
}
owners := msg.Payload.Record.Owners
for _, owner := range owners {
if owner == "" {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Record owner not set.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Record owner not set.")
}
}
if len(msg.BondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond ID is required.")
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Bond ID is required.")
}
return nil
}
@ -84,11 +83,11 @@ func (msg MsgRenewRecord) Type() string { return "renew-record" }
// ValidateBasic Implements Msg.
func (msg MsgRenewRecord) ValidateBasic() error {
if len(msg.RecordId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
return nil
@ -124,13 +123,13 @@ func (msg MsgAssociateBond) Type() string { return "associate-bond" }
// ValidateBasic Implements Msg.
func (msg MsgAssociateBond) ValidateBasic() error {
if len(msg.RecordId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
}
if len(msg.BondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "bond id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "bond id is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
return nil
@ -165,10 +164,10 @@ func (msg MsgDissociateBond) Type() string { return "dissociate-bond" }
// ValidateBasic Implements Msg.
func (msg MsgDissociateBond) ValidateBasic() error {
if len(msg.RecordId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "record id is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
return nil
@ -203,10 +202,10 @@ func (msg MsgDissociateRecords) Type() string { return "dissociate-records" }
// ValidateBasic Implements Msg.
func (msg MsgDissociateRecords) ValidateBasic() error {
if len(msg.BondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "bond id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "bond id is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
return nil
@ -242,13 +241,13 @@ func (msg MsgReAssociateRecords) Type() string { return "reassociate-records" }
// ValidateBasic Implements Msg.
func (msg MsgReAssociateRecords) ValidateBasic() error {
if len(msg.OldBondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "old-bond-id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "old-bond-id is required.")
}
if len(msg.NewBondId) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "new-bond-id is required.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "new-bond-id is required.")
}
if len(msg.Signer) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer.")
}
return nil

View File

@ -1,918 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: vulcanize/registry/v1beta1/tx.proto
/*
Package types is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package types
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
var (
filter_Msg_SetRecord_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_SetRecord_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetRecord
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetRecord_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.SetRecord(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_SetRecord_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetRecord
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetRecord_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SetRecord(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_RenewRecord_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_RenewRecord_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRenewRecord
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RenewRecord_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.RenewRecord(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_RenewRecord_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgRenewRecord
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_RenewRecord_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RenewRecord(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_AssociateBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_AssociateBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgAssociateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_AssociateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.AssociateBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_AssociateBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgAssociateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_AssociateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AssociateBond(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_DissociateBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_DissociateBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDissociateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DissociateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.DissociateBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_DissociateBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDissociateBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DissociateBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DissociateBond(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_DissociateRecords_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_DissociateRecords_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDissociateRecords
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DissociateRecords_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.DissociateRecords(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_DissociateRecords_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDissociateRecords
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DissociateRecords_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DissociateRecords(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_ReAssociateRecords_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_ReAssociateRecords_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgReAssociateRecords
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ReAssociateRecords_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ReAssociateRecords(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_ReAssociateRecords_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgReAssociateRecords
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ReAssociateRecords_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ReAssociateRecords(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_SetName_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_SetName_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetName
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.SetName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_SetName_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetName
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SetName(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_ReserveName_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_ReserveName_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgReserveAuthority
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ReserveName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ReserveName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_ReserveName_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgReserveAuthority
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ReserveName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ReserveName(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_DeleteName_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_DeleteName_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDeleteNameAuthority
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DeleteName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.DeleteName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_DeleteName_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgDeleteNameAuthority
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DeleteName_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DeleteName(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Msg_SetAuthorityBond_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Msg_SetAuthorityBond_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetAuthorityBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetAuthorityBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.SetAuthorityBond(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Msg_SetAuthorityBond_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq MsgSetAuthorityBond
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetAuthorityBond_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SetAuthorityBond(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux".
// UnaryRPC :call MsgServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead.
func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error {
mux.Handle("POST", pattern_Msg_SetRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_SetRecord_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetRecord_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RenewRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_RenewRecord_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RenewRecord_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_AssociateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_AssociateBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_AssociateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DissociateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_DissociateBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DissociateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DissociateRecords_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_DissociateRecords_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DissociateRecords_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_ReAssociateRecords_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_ReAssociateRecords_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_ReAssociateRecords_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_SetName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_SetName_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_ReserveName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_ReserveName_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_ReserveName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DeleteName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_DeleteName_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DeleteName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_SetAuthorityBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Msg_SetAuthorityBond_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetAuthorityBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMsgHandler(ctx, mux, conn)
}
// RegisterMsgHandler registers the http handlers for service Msg to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn))
}
// RegisterMsgHandlerClient registers the http handlers for service Msg
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MsgClient" to call the correct interceptors.
func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error {
mux.Handle("POST", pattern_Msg_SetRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_SetRecord_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetRecord_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_RenewRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_RenewRecord_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_RenewRecord_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_AssociateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_AssociateBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_AssociateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DissociateBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_DissociateBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DissociateBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DissociateRecords_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_DissociateRecords_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DissociateRecords_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_ReAssociateRecords_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_ReAssociateRecords_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_ReAssociateRecords_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_SetName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_SetName_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_ReserveName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_ReserveName_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_ReserveName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_DeleteName_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_DeleteName_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_DeleteName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Msg_SetAuthorityBond_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Msg_SetAuthorityBond_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Msg_SetAuthorityBond_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Msg_SetRecord_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "set_record"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_RenewRecord_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "renew_record"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_AssociateBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "associate_bond"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_DissociateBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "dissociate_bond"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_DissociateRecords_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "dissociate_records"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_ReAssociateRecords_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "reassociate_records"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_SetName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "set_name"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_ReserveName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "reserve_name"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_DeleteName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "delete_name"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Msg_SetAuthorityBond_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"vulcanize", "registry", "v1beta1", "set_authority_bond"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Msg_SetRecord_0 = runtime.ForwardResponseMessage
forward_Msg_RenewRecord_0 = runtime.ForwardResponseMessage
forward_Msg_AssociateBond_0 = runtime.ForwardResponseMessage
forward_Msg_DissociateBond_0 = runtime.ForwardResponseMessage
forward_Msg_DissociateRecords_0 = runtime.ForwardResponseMessage
forward_Msg_ReAssociateRecords_0 = runtime.ForwardResponseMessage
forward_Msg_SetName_0 = runtime.ForwardResponseMessage
forward_Msg_ReserveName_0 = runtime.ForwardResponseMessage
forward_Msg_DeleteName_0 = runtime.ForwardResponseMessage
forward_Msg_SetAuthorityBond_0 = runtime.ForwardResponseMessage
)

View File

@ -68,78 +68,6 @@ func payLoadAttributes(recordPayLoad map[string]interface{}) (*codectypes.Any, e
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationRecord":
{
var attributes ApplicationRecord
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationDeploymentRequest":
{
var attributes ApplicationDeploymentRequest
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationDeploymentRecord":
{
var attributes ApplicationDeploymentRecord
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationDeploymentRemovalRequest":
{
var attributes ApplicationDeploymentRemovalRequest
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationDeploymentRemovalRecord":
{
var attributes ApplicationDeploymentRemovalRecord
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "ApplicationArtifact":
{
var attributes ApplicationArtifact
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "DnsRecord":
{
var attributes DnsRecord
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
case "GeneralRecord":
{
var attributes GeneralRecord
err := json.Unmarshal(bz, &attributes)
if err != nil {
return &codectypes.Any{}, err
}
return codectypes.NewAnyWithValue(&attributes)
}
default:
return &codectypes.Any{}, fmt.Errorf("unsupported record type %s", recordType.(string))
}
@ -207,110 +135,6 @@ func GetJSONBytesFromAny(any codectypes.Any) ([]byte, error) {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationRecord":
{
var attributes ApplicationRecord
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationDeploymentRequest":
{
var attributes ApplicationDeploymentRequest
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationDeploymentRecord":
{
var attributes ApplicationDeploymentRecord
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationDeploymentRemovalRequest":
{
var attributes ApplicationDeploymentRemovalRequest
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationDeploymentRemovalRecord":
{
var attributes ApplicationDeploymentRemovalRecord
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "ApplicationArtifact":
{
var attributes ApplicationArtifact
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "DnsRecord":
{
var attributes DnsRecord
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")
}
}
case "GeneralRecord":
{
var attributes GeneralRecord
err := proto.Unmarshal(any.Value, &attributes)
if err != nil {
panic("Proto unmarshal error")
}
bz, err = json.Marshal(attributes)
if err != nil {
panic("JSON marshal error")

205
x/wasm/Governance.md Normal file
View File

@ -0,0 +1,205 @@
# Governance
This document gives an overview of how the various governance
proposals interact with the CosmWasm contract lifecycle. It is
a high-level, technical introduction meant to provide context before
looking into the code, or constructing proposals.
## Proposal Types
We have added 9 new wasm specific proposal types that cover the contract's live cycle and authorization:
* `StoreCodeProposal` - upload a wasm binary
* `InstantiateContractProposal` - instantiate a wasm contract
* `MigrateContractProposal` - migrate a wasm contract to a new code version
* `SudoContractProposal` - call into the protected `sudo` entry point of a contract
* `ExecuteContractProposal` - execute a wasm contract as an arbitrary user
* `UpdateAdminProposal` - set a new admin for a contract
* `ClearAdminProposal` - clear admin for a contract to prevent further migrations
* `PinCodes` - pin the given code ids in cache. This trades memory for reduced startup time and lowers gas cost
* `UnpinCodes` - unpin the given code ids from the cache. This frees up memory and returns to standard speed and gas cost
* `UpdateInstantiateConfigProposal` - update instantiate permissions to a list of given code ids.
* `StoreAndInstantiateContractProposal` - upload and instantiate a wasm contract.
For details see the proposal type [implementation](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/proposal.go)
### Unit tests
[Proposal type validations](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/proposal_test.go)
## Proposal Handler
The [wasmd proposal_handler](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/proposal_handler.go) implements the `gov.Handler` function
and executes the wasmd proposal types after a successful tally.
The proposal handler uses a [`GovAuthorizationPolicy`](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/authz_policy.go#L29) to bypass the existing contract's authorization policy.
### Tests
* [Integration: Submit and execute proposal](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/proposal_integration_test.go)
## Gov Integration
The wasmd proposal handler can be added to the gov router in the [abci app](https://github.com/CosmWasm/wasmd/blob/master/app/app.go#L306)
to receive proposal execution calls.
```go
govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.wasmKeeper, enabledProposals))
```
## Wasmd Authorization Settings
Settings via sdk `params` module:
- `code_upload_access` - who can upload a wasm binary: `Nobody`, `Everybody`, `OnlyAddress`
- `instantiate_default_permission` - platform default, who can instantiate a wasm binary when the code owner has not set it
See [params.go](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/params.go)
### Init Params Via Genesis
```json
"wasm": {
"params": {
"code_upload_access": {
"permission": "Everybody"
},
"instantiate_default_permission": "Everybody"
}
},
```
The values can be updated via gov proposal implemented in the `params` module.
### Update Params Via [ParamChangeProposal](https://github.com/cosmos/cosmos-sdk/blob/v0.45.3/proto/cosmos/params/v1beta1/params.proto#L10)
Example to submit a parameter change gov proposal:
```sh
wasmd tx gov submit-proposal param-change <proposal-json-file> --from validator --chain-id=testing -b block
```
#### Content examples
* Disable wasm code uploads
```json
{
"title": "Foo",
"description": "Bar",
"changes": [
{
"subspace": "wasm",
"key": "uploadAccess",
"value": {
"permission": "Nobody"
}
}
],
"deposit": ""
}
```
* Allow wasm code uploads for everybody
```json
{
"title": "Foo",
"description": "Bar",
"changes": [
{
"subspace": "wasm",
"key": "uploadAccess",
"value": {
"permission": "Everybody"
}
}
],
"deposit": ""
}
```
* Restrict code uploads to a single address
```json
{
"title": "Foo",
"description": "Bar",
"changes": [
{
"subspace": "wasm",
"key": "uploadAccess",
"value": {
"permission": "OnlyAddress",
"address": "cosmos1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0fr2sh"
}
}
],
"deposit": ""
}
```
* Set chain **default** instantiation settings to nobody
```json
{
"title": "Foo",
"description": "Bar",
"changes": [
{
"subspace": "wasm",
"key": "instantiateAccess",
"value": "Nobody"
}
],
"deposit": ""
}
```
* Set chain **default** instantiation settings to everybody
```json
{
"title": "Foo",
"description": "Bar",
"changes": [
{
"subspace": "wasm",
"key": "instantiateAccess",
"value": "Everybody"
}
],
"deposit": ""
}
```
### Enable gov proposals at **compile time**.
As gov proposals bypass the existing authorization policy they are disabled and require to be enabled at compile time.
```
-X github.com/CosmWasm/wasmd/app.ProposalsEnabled=true - enable all x/wasm governance proposals (default false)
-X github.com/CosmWasm/wasmd/app.EnableSpecificProposals=MigrateContract,UpdateAdmin,ClearAdmin - enable a subset of the x/wasm governance proposal types (overrides ProposalsEnabled)
```
The `ParamChangeProposal` is always enabled.
### Tests
* [params validation unit tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/params_test.go)
* [genesis validation tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/genesis_test.go)
* [policy integration tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/keeper_test.go)
## CLI
```shell script
wasmd tx gov submit-proposal [command]
Available Commands:
wasm-store Submit a wasm binary proposal
instantiate-contract Submit an instantiate wasm contract proposal
migrate-contract Submit a migrate wasm contract to a new code version proposal
set-contract-admin Submit a new admin for a contract proposal
clear-contract-admin Submit a clear admin for a contract to prevent further migrations proposal
...
```
## Rest
New [`ProposalHandlers`](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/client/proposal_handler.go)
* Integration
```shell script
gov.NewAppModuleBasic(append(wasmclient.ProposalHandlers, paramsclient.ProposalHandler, distr.ProposalHandler, upgradeclient.ProposalHandler)...),
```
In [abci app](https://github.com/CosmWasm/wasmd/blob/master/app/app.go#L109)
### Tests
* [Rest Unit tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/client/proposal_handler_test.go)
* [Rest smoke LCD test](https://github.com/CosmWasm/wasmd/blob/master/lcd_test/wasm_test.go)
## Pull requests
* https://github.com/CosmWasm/wasmd/pull/190
* https://github.com/CosmWasm/wasmd/pull/186
* https://github.com/CosmWasm/wasmd/pull/183
* https://github.com/CosmWasm/wasmd/pull/180
* https://github.com/CosmWasm/wasmd/pull/179
* https://github.com/CosmWasm/wasmd/pull/173

137
x/wasm/IBC.md Normal file
View File

@ -0,0 +1,137 @@
# IBC specification
This documents how CosmWasm contracts are expected to interact with IBC.
## General Concepts
**IBC Enabled** - when instantiating a contract, we detect if it supports IBC messages.
We require "feature flags" in the contract/vm handshake to ensure compatibility
for features like staking or chain-specific extensions. IBC functionality will require
another "feature flag", and the list of "enabled features" can be returned to the `x/wasm`
module to control conditional IBC behavior.
If this feature is enabled, it is considered "IBC Enabled", and that info will
be stored in the ContractInfo. (For mock, we assume all contracts are IBC enabled)
Also, please read the [IBC Docs](https://docs.cosmos.network/master/ibc/overview.html)
for detailed descriptions of the terms *Port*, *Client*, *Connection*,
and *Channel*
## Overview
We use "One Port per Contract", which is the most straight-forward mapping, treating each contract
like a module. It does lead to very long portIDs however. Pay special attention to both the Channel establishment
(which should be compatible with standard ICS20 modules without changes on their part), as well
as how contracts can properly identify their counterparty.
(We considered on port for the `x/wasm` module and multiplexing on it, but [dismissed that idea](#rejected-ideas))
* Upon `Instantiate`, if a contract is *IBC Enabled*, we dynamically
bind a port for this contract. The port name is `wasm.<contract address>`,
eg. `wasm.cosmos1hmdudppzceg27qsuq707tjg8rkgj7g5hnvnw29`
* If a *Channel* is being established with a registered `wasm.xyz` port,
the `x/wasm.Keeper` will handle this and call into the appropriate
contract to determine supported protocol versions during the
[`ChanOpenTry` and `ChanOpenAck` phases](https://docs.cosmos.network/master/ibc/overview.html#channels).
(See [Channel Handshake Version Negotiation](https://docs.cosmos.network/master/ibc/custom.html#channel-handshake-version-negotiation))
* Both the *Port* and the *Channel* are fully owned by one contract.
* `x/wasm` will allow both *ORDERED* and *UNORDERED* channels and pass that mode
down to the contract in `OnChanOpenTry`, so the contract can decide if it accepts
the mode. We will recommend the contract developers stick with *ORDERED* channels
for custom protocols unless they can reason about async packet timing.
* When sending a packet, the CosmWasm contract must specify the local *ChannelID*.
As there is a unique *PortID* per contract, that is filled in by `x/wasm`
to produce the globally unique `(PortID, ChannelID)`
* When receiving a Packet (or Ack or Timeout), the contracts receives the local
*ChannelID* it came from, as well as the packet that was sent by the counterparty.
* When receiving an Ack or Timeout packet, the contract also receives the
original packet that it sent earlier.
* We do not support multihop packets in this model (they are rejected by `x/wasm`).
They are currently not fully specified nor implemented in IBC 1.0, so let us
simplify our model until this is well established
## Workflow
Establishing *Clients* and *Connections* is out of the scope of this
module and must be created by the same means as for `ibc-transfer`
(via the [go cli](https://github.com/cosmos/relayer) or better [ts-relayer](https://github.com/confio/ts-relayer)).
`x/wasm` will bind a unique *Port* for each "IBC Enabled" contract.
For mocks, all the Packet Handling and Channel Lifecycle Hooks are routed
to some Golang stub handler, but containing the contract address, so we
can perform contract-specific actions for each packet. In a real setting,
we route to the contract that owns the port/channel and call one of it's various
entry points.
Please refer to the CosmWasm repo for all
[details on the IBC API from the point of view of a CosmWasm contract](https://github.com/CosmWasm/cosmwasm/blob/main/IBC.md).
## Future Ideas
Here are some ideas we may add in the future
### Dynamic Ports and Channels
* multiple ports per contract
* elastic ports that can be assigned to different contracts
* transfer of channels to another contract
This is inspired by the Agoric design, but also adds considerable complexity to both the `x/wasm`
implementation as well as the correctness reasoning of any given contract. This will not be
available in the first version of our "IBC Enabled contracts", but we can consider it for later,
if there are concrete user cases that would significantly benefit from this added complexity.
### Add multihop support
Once the ICS and IBC specs fully establish how multihop packets work, we should add support for that.
Both on setting up the routes with OpenChannel, as well as acting as an intermediate relayer (if that is possible)
## Rejected Ideas
### One Port per Module
We decided on "one port per contract", especially after the IBC team raised
the max length on port names to allow `wasm-<bech32 address>` to be a valid port.
Here are the arguments for "one port for x/wasm" vs "one port per contract". Here
was an alternate proposal:
In this approach, the `x/wasm` module just binds one port to handle all
modules. This can be well defined name like `wasm`. Since we always
have `(ChannelID, PortID)` for routing messages, we can reuse one port
for all contracts as long as we have a clear way to map the `ChannelID`
to a specific contract when it is being established.
* On genesis we bind the port `wasm` for all communication with the `x/wasm`
module.
* The *Port* is fully owned by `x/wasm`
* Each *Channel* is fully owned by one contract.
* `x/wasm` only accepts *ORDERED Channels* for simplicity of contract
correctness.
To clarify:
* When a *Channel* is being established with port `wasm`, the
`x/wasm.Keeper` must be able to identify for which contract this
is destined. **how to do so**??
* One idea: the channel name must be the contract address. This means
(`wasm`, `cosmos13d...`) will map to the given contract in the wasm module.
The problem with this is that if two contracts from chainA want to
connect to the same contracts on chainB, they will want to claim the
same *ChannelID* and *PortID*. Not sure how to differentiate multiple
parties in this way.
* Other ideas: have a special field we send on `OnChanOpenInit` that
specifies the destination contract, and allow any *ChannelID*.
However, looking at [`OnChanOpenInit` function signature](https://docs.cosmos.network/master/ibc/custom.html#implement-ibcmodule-interface-and-callbacks),
I don't see a place to put this extra info, without abusing the version field,
which is a [specified field](https://docs.cosmos.network/master/ibc/custom.html#channel-handshake-version-negotiation):
```
Versions must be strings but can implement any versioning structure.
If your application plans to have linear releases then semantic versioning is recommended.
...
Valid version selection includes selecting a compatible version identifier with a subset
of features supported by your application for that version.
...
ICS20 currently implements basic string matching with a
single supported version.
```

219
x/wasm/README.md Normal file
View File

@ -0,0 +1,219 @@
# Wasm Module
This should be a brief overview of the functionality
## Configuration
You can add the following section to `config/app.toml`:
```toml
[wasm]
# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
query_gas_limit = 300000
# This defines the memory size for Wasm modules that we can keep cached to speed-up instantiation
# The value is in MiB not bytes
memory_cache_size = 300
```
The values can also be set via CLI flags on with the `start` command:
```shell script
--wasm.memory_cache_size uint32 Sets the size in MiB (NOT bytes) of an in-memory cache for wasm modules. Set to 0 to disable. (default 100)
--wasm.query_gas_limit uint Set the max gas that can be spent on executing a query with a Wasm contract (default 3000000)
```
## Events
A number of events are returned to allow good indexing of the transactions from smart contracts.
Every call to Instantiate or Execute will be tagged with the info on the contract that was executed and who executed it.
It should look something like this (with different addresses). The module is always `wasm`, and `code_id` is only present
when Instantiating a contract, so you can subscribe to new instances, it is omitted on Execute. There is also an `action` tag
which is auto-added by the Cosmos SDK and has a value of either `store-code`, `instantiate` or `execute` depending on which message
was sent:
```json
{
"Type": "message",
"Attr": [
{
"key": "module",
"value": "wasm"
},
{
"key": "action",
"value": "instantiate"
},
{
"key": "signer",
"value": "cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"
},
{
"key": "code_id",
"value": "1"
},
{
"key": "_contract_address",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
}
]
}
```
If any funds were transferred to the contract as part of the message, or if the contract released funds as part of it's executions,
it will receive the typical events associated with sending tokens from bank. In this case, we instantiate the contract and
provide a initial balance in the same `MsgInstantiateContract`. We see the following events in addition to the above one:
```json
[
{
"Type": "transfer",
"Attr": [
{
"key": "recipient",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
},
{
"key": "sender",
"value": "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv"
},
{
"key": "amount",
"value": "100000denom"
}
]
}
]
```
Finally, the contract itself can emit a "custom event" on Execute only (not on Init).
There is one event per contract, so if one contract calls a second contract, you may receive
one event for the original contract and one for the re-invoked contract. All attributes from the contract are passed through verbatim,
and we add a `_contract_address` attribute that contains the actual contract that emitted that event.
Here is an example from the escrow contract successfully releasing funds to the destination address:
```json
{
"Type": "wasm",
"Attr": [
{
"key": "_contract_address",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
},
{
"key": "action",
"value": "release"
},
{
"key": "destination",
"value": "cosmos14k7v7ms4jxkk2etmg9gljxjm4ru3qjdugfsflq"
}
]
}
```
### Pulling this all together
We will invoke an escrow contract to release to the designated beneficiary.
The escrow was previously loaded with `100000denom` (from the above example).
In this transaction, we send `5000denom` along with the `MsgExecuteContract`
and the contract releases the entire funds (`105000denom`) to the beneficiary.
We will see all the following events, where you should be able to reconstruct the actions
(remember there are two events for each transfer). We see (1) the initial transfer of funds
to the contract, (2) the contract custom event that it released funds (3) the transfer of funds
from the contract to the beneficiary and (4) the generic x/wasm event stating that the contract
was executed (which always appears, while 2 is optional and has information as reliable as the contract):
```json
[
{
"Type": "transfer",
"Attr": [
{
"key": "recipient",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
},
{
"key": "sender",
"value": "cosmos1zm074khx32hqy20hlshlsd423n07pwlu9cpt37"
},
{
"key": "amount",
"value": "5000denom"
}
]
},
{
"Type": "wasm",
"Attr": [
{
"key": "_contract_address",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
},
{
"key": "action",
"value": "release"
},
{
"key": "destination",
"value": "cosmos14k7v7ms4jxkk2etmg9gljxjm4ru3qjdugfsflq"
}
]
},
{
"Type": "transfer",
"Attr": [
{
"key": "recipient",
"value": "cosmos14k7v7ms4jxkk2etmg9gljxjm4ru3qjdugfsflq"
},
{
"key": "sender",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
},
{
"key": "amount",
"value": "105000denom"
}
]
},
{
"Type": "message",
"Attr": [
{
"key": "module",
"value": "wasm"
},
{
"key": "action",
"value": "execute"
},
{
"key": "signer",
"value": "cosmos1zm074khx32hqy20hlshlsd423n07pwlu9cpt37"
},
{
"key": "_contract_address",
"value": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"
}
]
}
]
```
A note on this format. This is what we return from our module. However, it seems to me that many events with the same `Type`
get merged together somewhere along the stack, so in this case, you *may* end up with one "transfer" event with the info for
both transfers. Double check when evaluating the event logs, I will document better with more experience, especially when I
find out the entire path for the events.
## Messages
TODO
## CLI
TODO - working, but not the nicest interface (json + bash = bleh). Use to upload, but I suggest to focus on frontend / js tooling
## Rest
TODO - main supported interface, under rapid change

133
x/wasm/alias.go Normal file
View File

@ -0,0 +1,133 @@
// nolint
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/cerc-io/laconicd/x/wasm/types
// ALIASGEN: github.com/cerc-io/laconicd/x/wasm/keeper
package wasm
import (
"github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cerc-io/laconicd/x/wasm/types"
)
const (
firstCodeID = 1
ModuleName = types.ModuleName
StoreKey = types.StoreKey
TStoreKey = types.TStoreKey
QuerierRoute = types.QuerierRoute
RouterKey = types.RouterKey
WasmModuleEventType = types.WasmModuleEventType
AttributeKeyContractAddr = types.AttributeKeyContractAddr
ProposalTypeStoreCode = types.ProposalTypeStoreCode
ProposalTypeInstantiateContract = types.ProposalTypeInstantiateContract
ProposalTypeMigrateContract = types.ProposalTypeMigrateContract
ProposalTypeUpdateAdmin = types.ProposalTypeUpdateAdmin
ProposalTypeClearAdmin = types.ProposalTypeClearAdmin
QueryListContractByCode = keeper.QueryListContractByCode
QueryGetContract = keeper.QueryGetContract
QueryGetContractState = keeper.QueryGetContractState
QueryGetCode = keeper.QueryGetCode
QueryListCode = keeper.QueryListCode
QueryMethodContractStateSmart = keeper.QueryMethodContractStateSmart
QueryMethodContractStateAll = keeper.QueryMethodContractStateAll
QueryMethodContractStateRaw = keeper.QueryMethodContractStateRaw
)
var (
// functions aliases
RegisterCodec = types.RegisterLegacyAminoCodec
RegisterInterfaces = types.RegisterInterfaces
ValidateGenesis = types.ValidateGenesis
ConvertToProposals = types.ConvertToProposals
GetCodeKey = types.GetCodeKey
GetContractAddressKey = types.GetContractAddressKey
GetContractStorePrefixKey = types.GetContractStorePrefix
NewCodeInfo = types.NewCodeInfo
NewAbsoluteTxPosition = types.NewAbsoluteTxPosition
NewContractInfo = types.NewContractInfo
NewEnv = types.NewEnv
NewWasmCoins = types.NewWasmCoins
DefaultWasmConfig = types.DefaultWasmConfig
DefaultParams = types.DefaultParams
InitGenesis = keeper.InitGenesis
ExportGenesis = keeper.ExportGenesis
NewMessageHandler = keeper.NewDefaultMessageHandler
DefaultEncoders = keeper.DefaultEncoders
EncodeBankMsg = keeper.EncodeBankMsg
NoCustomMsg = keeper.NoCustomMsg
EncodeStakingMsg = keeper.EncodeStakingMsg
EncodeWasmMsg = keeper.EncodeWasmMsg
NewKeeper = keeper.NewKeeper
DefaultQueryPlugins = keeper.DefaultQueryPlugins
BankQuerier = keeper.BankQuerier
NoCustomQuerier = keeper.NoCustomQuerier
StakingQuerier = keeper.StakingQuerier
WasmQuerier = keeper.WasmQuerier
CreateTestInput = keeper.CreateTestInput
TestHandler = keeper.TestHandler
NewWasmProposalHandler = keeper.NewWasmProposalHandler
NewQuerier = keeper.Querier
ContractFromPortID = keeper.ContractFromPortID
WithWasmEngine = keeper.WithWasmEngine
NewCountTXDecorator = keeper.NewCountTXDecorator
// variable aliases
ModuleCdc = types.ModuleCdc
DefaultCodespace = types.DefaultCodespace
ErrCreateFailed = types.ErrCreateFailed
ErrAccountExists = types.ErrAccountExists
ErrInstantiateFailed = types.ErrInstantiateFailed
ErrExecuteFailed = types.ErrExecuteFailed
ErrGasLimit = types.ErrGasLimit
ErrInvalidGenesis = types.ErrInvalidGenesis
ErrNotFound = types.ErrNotFound
ErrQueryFailed = types.ErrQueryFailed
ErrInvalidMsg = types.ErrInvalidMsg
KeyLastCodeID = types.KeyLastCodeID
KeyLastInstanceID = types.KeyLastInstanceID
CodeKeyPrefix = types.CodeKeyPrefix
ContractKeyPrefix = types.ContractKeyPrefix
ContractStorePrefix = types.ContractStorePrefix
EnableAllProposals = types.EnableAllProposals
DisableAllProposals = types.DisableAllProposals
)
type (
ProposalType = types.ProposalType
GenesisState = types.GenesisState
Code = types.Code
Contract = types.Contract
MsgStoreCode = types.MsgStoreCode
MsgStoreCodeResponse = types.MsgStoreCodeResponse
MsgInstantiateContract = types.MsgInstantiateContract
MsgInstantiateContract2 = types.MsgInstantiateContract2
MsgInstantiateContractResponse = types.MsgInstantiateContractResponse
MsgExecuteContract = types.MsgExecuteContract
MsgExecuteContractResponse = types.MsgExecuteContractResponse
MsgMigrateContract = types.MsgMigrateContract
MsgMigrateContractResponse = types.MsgMigrateContractResponse
MsgUpdateAdmin = types.MsgUpdateAdmin
MsgUpdateAdminResponse = types.MsgUpdateAdminResponse
MsgClearAdmin = types.MsgClearAdmin
MsgWasmIBCCall = types.MsgIBCSend
MsgClearAdminResponse = types.MsgClearAdminResponse
MsgServer = types.MsgServer
Model = types.Model
CodeInfo = types.CodeInfo
ContractInfo = types.ContractInfo
CreatedAt = types.AbsoluteTxPosition
Config = types.WasmConfig
CodeInfoResponse = types.CodeInfoResponse
MessageHandler = keeper.SDKMessageHandler
BankEncoder = keeper.BankEncoder
CustomEncoder = keeper.CustomEncoder
StakingEncoder = keeper.StakingEncoder
WasmEncoder = keeper.WasmEncoder
MessageEncoders = keeper.MessageEncoders
Keeper = keeper.Keeper
QueryHandler = keeper.QueryHandler
CustomQuerier = keeper.CustomQuerier
QueryPlugins = keeper.QueryPlugins
Option = keeper.Option
)

834
x/wasm/client/cli/gov_tx.go Normal file
View File

@ -0,0 +1,834 @@
package cli
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"strconv"
"strings"
"github.com/docker/distribution/reference"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func ProposalStoreCodeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "wasm-store [wasm file] --title [text] --description [text] --run-as [address] --unpin-code [unpin_code] --source [source] --builder [builder] --code-hash [code_hash]",
Short: "Submit a wasm binary proposal",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseStoreCodeArgs(args[0], clientCtx.FromAddress, cmd.Flags())
if err != nil {
return err
}
runAs, err := cmd.Flags().GetString(flagRunAs)
if err != nil {
return fmt.Errorf("run-as: %s", err)
}
if len(runAs) == 0 {
return errors.New("run-as address is required")
}
unpinCode, err := cmd.Flags().GetBool(flagUnpinCode)
if err != nil {
return err
}
source, builder, codeHash, err := parseVerificationFlags(src.WASMByteCode, cmd.Flags())
if err != nil {
return err
}
content := types.StoreCodeProposal{
Title: proposalTitle,
Description: proposalDescr,
RunAs: runAs,
WASMByteCode: src.WASMByteCode,
InstantiatePermission: src.InstantiatePermission,
UnpinCode: unpinCode,
Source: source,
Builder: builder,
CodeHash: codeHash,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagRunAs, "", "The address that is stored as code creator")
cmd.Flags().Bool(flagUnpinCode, false, "Unpin code on upload, optional")
cmd.Flags().String(flagSource, "", "Code Source URL is a valid absolute HTTPS URI to the contract's source code,")
cmd.Flags().String(flagBuilder, "", "Builder is a valid docker image name with tag, such as \"cosmwasm/workspace-optimizer:0.12.9\"")
cmd.Flags().BytesHex(flagCodeHash, nil, "CodeHash is the sha256 hash of the wasm code")
addInstantiatePermissionFlags(cmd)
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func parseVerificationFlags(wasm []byte, flags *flag.FlagSet) (string, string, []byte, error) {
source, err := flags.GetString(flagSource)
if err != nil {
return "", "", nil, fmt.Errorf("source: %s", err)
}
builder, err := flags.GetString(flagBuilder)
if err != nil {
return "", "", nil, fmt.Errorf("builder: %s", err)
}
codeHash, err := flags.GetBytesHex(flagCodeHash)
if err != nil {
return "", "", nil, fmt.Errorf("codeHash: %s", err)
}
// if any set require others to be set
if len(source) != 0 || len(builder) != 0 || len(codeHash) != 0 {
if source == "" {
return "", "", nil, fmt.Errorf("source is required")
}
if _, err = url.ParseRequestURI(source); err != nil {
return "", "", nil, fmt.Errorf("source: %s", err)
}
if builder == "" {
return "", "", nil, fmt.Errorf("builder is required")
}
if _, err := reference.ParseDockerRef(builder); err != nil {
return "", "", nil, fmt.Errorf("builder: %s", err)
}
if len(codeHash) == 0 {
return "", "", nil, fmt.Errorf("code hash is required")
}
// wasm is unzipped in parseStoreCodeArgs
// checksum generation will be decoupled here
// reference https://github.com/CosmWasm/wasmvm/issues/359
checksum := sha256.Sum256(wasm)
if !bytes.Equal(checksum[:], codeHash) {
return "", "", nil, fmt.Errorf("code-hash mismatch: %X, checksum: %X", codeHash, checksum)
}
}
return source, builder, codeHash, nil
}
func ProposalInstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional]",
Short: "Submit an instantiate wasm contract proposal",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseInstantiateArgs(args[0], args[1], clientCtx.Keyring, clientCtx.FromAddress, cmd.Flags())
if err != nil {
return err
}
runAs, err := cmd.Flags().GetString(flagRunAs)
if err != nil {
return fmt.Errorf("run-as: %s", err)
}
if len(runAs) == 0 {
return errors.New("run-as address is required")
}
content := types.InstantiateContractProposal{
Title: proposalTitle,
Description: proposalDescr,
RunAs: runAs,
Admin: src.Admin,
CodeID: src.CodeID,
Label: src.Label,
Msg: src.Msg,
Funds: src.Funds,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
cmd.Flags().String(flagAdmin, "", "Address or key name of an admin")
cmd.Flags().String(flagRunAs, "", "The address that pays the init funds. It is the creator of the contract and passed to the contract as sender on proposal execution")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalInstantiateContract2Cmd() *cobra.Command {
decoder := newArgDecoder(hex.DecodeString)
cmd := &cobra.Command{
Use: "instantiate-contract-2 [code_id_int64] [json_encoded_init_args] [salt] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional] --fix-msg [bool,optional]",
Short: "Submit an instantiate wasm contract proposal with predictable address",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseInstantiateArgs(args[0], args[1], clientCtx.Keyring, clientCtx.FromAddress, cmd.Flags())
if err != nil {
return err
}
runAs, err := cmd.Flags().GetString(flagRunAs)
if err != nil {
return fmt.Errorf("run-as: %s", err)
}
if len(runAs) == 0 {
return errors.New("run-as address is required")
}
salt, err := decoder.DecodeString(args[2])
if err != nil {
return fmt.Errorf("salt: %w", err)
}
fixMsg, err := cmd.Flags().GetBool(flagFixMsg)
if err != nil {
return fmt.Errorf("fix msg: %w", err)
}
content := types.NewInstantiateContract2Proposal(proposalTitle, proposalDescr, runAs, src.Admin, src.CodeID, src.Label, src.Msg, src.Funds, salt, fixMsg)
msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
cmd.Flags().String(flagAdmin, "", "Address of an admin")
cmd.Flags().String(flagRunAs, "", "The address that pays the init funds. It is the creator of the contract and passed to the contract as sender on proposal execution")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
cmd.Flags().Bool(flagFixMsg, false, "An optional flag to include the json_encoded_init_args for the predictable address generation mode")
decoder.RegisterFlags(cmd.PersistentFlags(), "salt")
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "store-instantiate [wasm file] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address]" +
"--unpin-code [unpin_code,optional] --source [source,optional] --builder [builder,optional] --code-hash [code_hash,optional] --admin [address,optional] --amount [coins,optional]",
Short: "Submit and instantiate a wasm contract proposal",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseStoreCodeArgs(args[0], clientCtx.FromAddress, cmd.Flags())
if err != nil {
return err
}
runAs, err := cmd.Flags().GetString(flagRunAs)
if err != nil {
return fmt.Errorf("run-as: %s", err)
}
if len(runAs) == 0 {
return errors.New("run-as address is required")
}
unpinCode, err := cmd.Flags().GetBool(flagUnpinCode)
if err != nil {
return err
}
source, builder, codeHash, err := parseVerificationFlags(src.WASMByteCode, cmd.Flags())
if err != nil {
return err
}
amountStr, err := cmd.Flags().GetString(flagAmount)
if err != nil {
return fmt.Errorf("amount: %s", err)
}
amount, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return fmt.Errorf("amount: %s", err)
}
label, err := cmd.Flags().GetString(flagLabel)
if err != nil {
return fmt.Errorf("label: %s", err)
}
if label == "" {
return errors.New("label is required on all contracts")
}
adminStr, err := cmd.Flags().GetString(flagAdmin)
if err != nil {
return fmt.Errorf("admin: %s", err)
}
noAdmin, err := cmd.Flags().GetBool(flagNoAdmin)
if err != nil {
return fmt.Errorf("no-admin: %s", err)
}
// ensure sensible admin is set (or explicitly immutable)
if adminStr == "" && !noAdmin {
return fmt.Errorf("you must set an admin or explicitly pass --no-admin to make it immutible (wasmd issue #719)")
}
if adminStr != "" && noAdmin {
return fmt.Errorf("you set an admin and passed --no-admin, those cannot both be true")
}
if adminStr != "" {
addr, err := sdk.AccAddressFromBech32(adminStr)
if err != nil {
info, err := clientCtx.Keyring.Key(adminStr)
if err != nil {
return fmt.Errorf("admin %s", err)
}
adminStr = info.GetAddress().String()
} else {
adminStr = addr.String()
}
}
content := types.StoreAndInstantiateContractProposal{
Title: proposalTitle,
Description: proposalDescr,
RunAs: runAs,
WASMByteCode: src.WASMByteCode,
InstantiatePermission: src.InstantiatePermission,
UnpinCode: unpinCode,
Source: source,
Builder: builder,
CodeHash: codeHash,
Admin: adminStr,
Label: label,
Msg: []byte(args[1]),
Funds: amount,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagRunAs, "", "The address that is stored as code creator. It is the creator of the contract and passed to the contract as sender on proposal execution")
cmd.Flags().Bool(flagUnpinCode, false, "Unpin code on upload, optional")
cmd.Flags().String(flagSource, "", "Code Source URL is a valid absolute HTTPS URI to the contract's source code,")
cmd.Flags().String(flagBuilder, "", "Builder is a valid docker image name with tag, such as \"cosmwasm/workspace-optimizer:0.12.9\"")
cmd.Flags().BytesHex(flagCodeHash, nil, "CodeHash is the sha256 hash of the wasm code")
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
cmd.Flags().String(flagAdmin, "", "Address or key name of an admin")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
addInstantiatePermissionFlags(cmd)
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalMigrateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "migrate-contract [contract_addr_bech32] [new_code_id_int64] [json_encoded_migration_args]",
Short: "Submit a migrate wasm contract to a new code version proposal",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseMigrateContractArgs(args, clientCtx)
if err != nil {
return err
}
content := types.MigrateContractProposal{
Title: proposalTitle,
Description: proposalDescr,
Contract: src.Contract,
CodeID: src.CodeID,
Msg: src.Msg,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalExecuteContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "execute-contract [contract_addr_bech32] [json_encoded_migration_args]",
Short: "Submit a execute wasm contract proposal (run by any address)",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
contract := args[0]
execMsg := []byte(args[1])
amountStr, err := cmd.Flags().GetString(flagAmount)
if err != nil {
return fmt.Errorf("amount: %s", err)
}
funds, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return fmt.Errorf("amount: %s", err)
}
runAs, err := cmd.Flags().GetString(flagRunAs)
if err != nil {
return fmt.Errorf("run-as: %s", err)
}
if len(runAs) == 0 {
return errors.New("run-as address is required")
}
content := types.ExecuteContractProposal{
Title: proposalTitle,
Description: proposalDescr,
Contract: contract,
Msg: execMsg,
RunAs: runAs,
Funds: funds,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagRunAs, "", "The address that is passed as sender to the contract on proposal execution")
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalSudoContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sudo-contract [contract_addr_bech32] [json_encoded_migration_args]",
Short: "Submit a sudo wasm contract proposal (to call privileged commands)",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
contract := args[0]
sudoMsg := []byte(args[1])
content := types.SudoContractProposal{
Title: proposalTitle,
Description: proposalDescr,
Contract: contract,
Msg: sudoMsg,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flagsExecute
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalUpdateContractAdminCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "set-contract-admin [contract_addr_bech32] [new_admin_addr_bech32]",
Short: "Submit a new admin for a contract proposal",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
src, err := parseUpdateContractAdminArgs(args, clientCtx)
if err != nil {
return err
}
content := types.UpdateAdminProposal{
Title: proposalTitle,
Description: proposalDescr,
Contract: src.Contract,
NewAdmin: src.NewAdmin,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalClearContractAdminCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "clear-contract-admin [contract_addr_bech32]",
Short: "Submit a clear admin for a contract to prevent further migrations proposal",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
content := types.ClearAdminProposal{
Title: proposalTitle,
Description: proposalDescr,
Contract: args[0],
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func ProposalPinCodesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "pin-codes [code-ids]",
Short: "Submit a pin code proposal for pinning a code to cache",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
codeIds, err := parsePinCodesArgs(args)
if err != nil {
return err
}
content := types.PinCodesProposal{
Title: proposalTitle,
Description: proposalDescr,
CodeIDs: codeIds,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func parsePinCodesArgs(args []string) ([]uint64, error) {
codeIDs := make([]uint64, len(args))
for i, c := range args {
codeID, err := strconv.ParseUint(c, 10, 64)
if err != nil {
return codeIDs, fmt.Errorf("code IDs: %s", err)
}
codeIDs[i] = codeID
}
return codeIDs, nil
}
func ProposalUnpinCodesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "unpin-codes [code-ids]",
Short: "Submit a unpin code proposal for unpinning a code to cache",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
codeIds, err := parsePinCodesArgs(args)
if err != nil {
return err
}
content := types.UnpinCodesProposal{
Title: proposalTitle,
Description: proposalDescr,
CodeIDs: codeIds,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func parseAccessConfig(raw string) (c types.AccessConfig, err error) {
switch raw {
case "nobody":
return types.AllowNobody, nil
case "everybody":
return types.AllowEverybody, nil
default:
parts := strings.Split(raw, ",")
addrs := make([]sdk.AccAddress, len(parts))
for i, v := range parts {
addr, err := sdk.AccAddressFromBech32(v)
if err != nil {
return types.AccessConfig{}, fmt.Errorf("unable to parse address %q: %s", v, err)
}
addrs[i] = addr
}
defer func() { // convert panic in ".With" to error for better output
if r := recover(); r != nil {
err = r.(error)
}
}()
cfg := types.AccessTypeAnyOfAddresses.With(addrs...)
return cfg, cfg.ValidateBasic()
}
}
func parseAccessConfigUpdates(args []string) ([]types.AccessConfigUpdate, error) {
updates := make([]types.AccessConfigUpdate, len(args))
for i, c := range args {
// format: code_id:access_config
// access_config: nobody|everybody|address(es)
parts := strings.Split(c, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid format")
}
codeID, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid code ID: %s", err)
}
accessConfig, err := parseAccessConfig(parts[1])
if err != nil {
return nil, err
}
updates[i] = types.AccessConfigUpdate{
CodeID: codeID,
InstantiatePermission: accessConfig,
}
}
return updates, nil
}
func ProposalUpdateInstantiateConfigCmd() *cobra.Command {
bech32Prefix := sdk.GetConfig().GetBech32AccountAddrPrefix()
cmd := &cobra.Command{
Use: "update-instantiate-config [code-id:permission]...",
Short: "Submit an update instantiate config proposal.",
Args: cobra.MinimumNArgs(1),
Long: strings.TrimSpace(
fmt.Sprintf(`Submit an update instantiate config proposal for multiple code ids.
Example:
$ %s tx gov submit-proposal update-instantiate-config 1:nobody 2:everybody 3:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x
`, version.AppName, bech32Prefix, bech32Prefix)),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, proposalTitle, proposalDescr, deposit, err := getProposalInfo(cmd)
if err != nil {
return err
}
updates, err := parseAccessConfigUpdates(args)
if err != nil {
return err
}
content := types.UpdateInstantiateConfigProposal{
Title: proposalTitle,
Description: proposalDescr,
AccessConfigUpdates: updates,
}
msg, err := govv1beta1.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "Description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal")
return cmd
}
func getProposalInfo(cmd *cobra.Command) (client.Context, string, string, sdk.Coins, error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return client.Context{}, "", "", nil, err
}
proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle)
if err != nil {
return clientCtx, proposalTitle, "", nil, err
}
proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription)
if err != nil {
return client.Context{}, proposalTitle, proposalDescr, nil, err
}
depositArg, err := cmd.Flags().GetString(cli.FlagDeposit)
if err != nil {
return client.Context{}, proposalTitle, proposalDescr, nil, err
}
deposit, err := sdk.ParseCoinsNormalized(depositArg)
if err != nil {
return client.Context{}, proposalTitle, proposalDescr, deposit, err
}
return clientCtx, proposalTitle, proposalDescr, deposit, nil
}

View File

@ -0,0 +1,158 @@
package cli
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestParseAccessConfigUpdates(t *testing.T) {
specs := map[string]struct {
src []string
exp []types.AccessConfigUpdate
expErr bool
}{
"nobody": {
src: []string{"1:nobody"},
exp: []types.AccessConfigUpdate{{
CodeID: 1,
InstantiatePermission: types.AccessConfig{Permission: types.AccessTypeNobody},
}},
},
"everybody": {
src: []string{"1:everybody"},
exp: []types.AccessConfigUpdate{{
CodeID: 1,
InstantiatePermission: types.AccessConfig{Permission: types.AccessTypeEverybody},
}},
},
"any of addresses - single": {
src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"},
exp: []types.AccessConfigUpdate{
{
CodeID: 1,
InstantiatePermission: types.AccessConfig{
Permission: types.AccessTypeAnyOfAddresses,
Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"},
},
},
},
},
"any of addresses - multiple": {
src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"},
exp: []types.AccessConfigUpdate{
{
CodeID: 1,
InstantiatePermission: types.AccessConfig{
Permission: types.AccessTypeAnyOfAddresses,
Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"},
},
},
},
},
"multiple code ids with different permissions": {
src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", "2:nobody"},
exp: []types.AccessConfigUpdate{
{
CodeID: 1,
InstantiatePermission: types.AccessConfig{
Permission: types.AccessTypeAnyOfAddresses,
Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"},
},
}, {
CodeID: 2,
InstantiatePermission: types.AccessConfig{
Permission: types.AccessTypeNobody,
},
},
},
},
"any of addresses - empty list": {
src: []string{"1:"},
expErr: true,
},
"any of addresses - invalid address": {
src: []string{"1:foo"},
expErr: true,
},
"any of addresses - duplicate address": {
src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"},
expErr: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
got, gotErr := parseAccessConfigUpdates(spec.src)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
assert.Equal(t, spec.exp, got)
})
}
}
func TestParseCodeInfoFlags(t *testing.T) {
correctSource := "https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/testdata/hackatom.wasm"
correctBuilderRef := "cosmwasm/workspace-optimizer:0.12.9"
wasmBin, err := os.ReadFile("../../keeper/testdata/hackatom.wasm")
require.NoError(t, err)
checksumStr := "beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b"
specs := map[string]struct {
args []string
expErr bool
}{
"source missing": {
args: []string{"--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: true,
},
"builder missing": {
args: []string{"--code-source-url=" + correctSource, "--code-hash=" + checksumStr},
expErr: true,
},
"code hash missing": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef},
expErr: true,
},
"source format wrong": {
args: []string{"--code-source-url=" + "format_wrong", "--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: true,
},
"builder format wrong": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + "format//", "--code-hash=" + checksumStr},
expErr: true,
},
"code hash wrong": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef, "--code-hash=" + "AA"},
expErr: true,
},
"happy path, none set": {
args: []string{},
expErr: false,
},
"happy path all set": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: false,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
flags := ProposalStoreCodeCmd().Flags()
require.NoError(t, flags.Parse(spec.args))
_, _, _, gotErr := parseVerificationFlags(wasmBin, flags)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
})
}
}

163
x/wasm/client/cli/new_tx.go Normal file
View File

@ -0,0 +1,163 @@
package cli
import (
"strconv"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/spf13/cobra"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// MigrateContractCmd will migrate a contract to a new code version
func MigrateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "migrate [contract_addr_bech32] [new_code_id_int64] [json_encoded_migration_args]",
Short: "Migrate a wasm contract to a new code version",
Aliases: []string{"update", "mig", "m"},
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseMigrateContractArgs(args, clientCtx)
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return nil
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func parseMigrateContractArgs(args []string, cliCtx client.Context) (types.MsgMigrateContract, error) {
// get the id of the code to instantiate
codeID, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return types.MsgMigrateContract{}, sdkerrors.Wrap(err, "code id")
}
migrateMsg := args[2]
msg := types.MsgMigrateContract{
Sender: cliCtx.GetFromAddress().String(),
Contract: args[0],
CodeID: codeID,
Msg: []byte(migrateMsg),
}
return msg, nil
}
// UpdateContractAdminCmd sets an new admin for a contract
func UpdateContractAdminCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "set-contract-admin [contract_addr_bech32] [new_admin_addr_bech32]",
Short: "Set new admin for a contract",
Aliases: []string{"new-admin", "admin", "set-adm", "sa"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseUpdateContractAdminArgs(args, clientCtx)
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func parseUpdateContractAdminArgs(args []string, cliCtx client.Context) (types.MsgUpdateAdmin, error) {
msg := types.MsgUpdateAdmin{
Sender: cliCtx.GetFromAddress().String(),
Contract: args[0],
NewAdmin: args[1],
}
return msg, nil
}
// ClearContractAdminCmd clears an admin for a contract
func ClearContractAdminCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "clear-contract-admin [contract_addr_bech32]",
Short: "Clears admin for a contract to prevent further migrations",
Aliases: []string{"clear-admin", "clr-adm"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg := types.MsgClearAdmin{
Sender: clientCtx.GetFromAddress().String(),
Contract: args[0],
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
// UpdateInstantiateConfigCmd updates instantiate config for a smart contract.
func UpdateInstantiateConfigCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update-instantiate-config [code_id_int64]",
Short: "Update instantiate config for a codeID",
Aliases: []string{"update-instantiate-config"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
codeID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
perm, err := parseAccessConfigFlags(cmd.Flags())
if err != nil {
return err
}
msg := types.MsgUpdateInstantiateConfig{
Sender: string(clientCtx.GetFromAddress()),
CodeID: codeID,
NewInstantiatePermission: perm,
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
addInstantiatePermissionFlags(cmd)
flags.AddTxFlagsToCmd(cmd)
return cmd
}

674
x/wasm/client/cli/query.go Normal file
View File

@ -0,0 +1,674 @@
package cli
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"strconv"
wasmvm "github.com/CosmWasm/wasmvm"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func GetQueryCmd() *cobra.Command {
queryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the wasm module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
SilenceUsage: true,
}
queryCmd.AddCommand(
GetCmdListCode(),
GetCmdListContractByCode(),
GetCmdQueryCode(),
GetCmdQueryCodeInfo(),
GetCmdGetContractInfo(),
GetCmdGetContractHistory(),
GetCmdGetContractState(),
GetCmdListPinnedCode(),
GetCmdLibVersion(),
GetCmdQueryParams(),
GetCmdBuildAddress(),
GetCmdListContractsByCreator(),
)
return queryCmd
}
// GetCmdLibVersion gets current libwasmvm version.
func GetCmdLibVersion() *cobra.Command {
cmd := &cobra.Command{
Use: "libwasmvm-version",
Short: "Get libwasmvm version",
Long: "Get libwasmvm version",
Aliases: []string{"lib-version"},
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
version, err := wasmvm.LibwasmvmVersion()
if err != nil {
return fmt.Errorf("error retrieving libwasmvm version: %w", err)
}
fmt.Println(version)
return nil
},
SilenceUsage: true,
}
return cmd
}
// GetCmdBuildAddress build a contract address
func GetCmdBuildAddress() *cobra.Command {
decoder := newArgDecoder(hex.DecodeString)
cmd := &cobra.Command{
Use: "build-address [code-hash] [creator-address] [salt-hex-encoded] [json_encoded_init_args (required when set as fixed)]",
Short: "build contract address",
Aliases: []string{"address"},
Args: cobra.RangeArgs(3, 4),
RunE: func(cmd *cobra.Command, args []string) error {
codeHash, err := hex.DecodeString(args[0])
if err != nil {
return fmt.Errorf("code-hash: %s", err)
}
creator, err := sdk.AccAddressFromBech32(args[1])
if err != nil {
return fmt.Errorf("creator: %s", err)
}
salt, err := hex.DecodeString(args[2])
switch {
case err != nil:
return fmt.Errorf("salt: %s", err)
case len(salt) == 0:
return errors.New("empty salt")
}
if len(args) == 3 {
cmd.Println(keeper.BuildContractAddressPredictable(codeHash, creator, salt, []byte{}).String())
return nil
}
msg := types.RawContractMessage(args[3])
if err := msg.ValidateBasic(); err != nil {
return fmt.Errorf("init message: %s", err)
}
cmd.Println(keeper.BuildContractAddressPredictable(codeHash, creator, salt, msg).String())
return nil
},
SilenceUsage: true,
}
decoder.RegisterFlags(cmd.PersistentFlags(), "salt")
return cmd
}
// GetCmdListCode lists all wasm code uploaded
func GetCmdListCode() *cobra.Command {
cmd := &cobra.Command{
Use: "list-code",
Short: "List all wasm bytecode on the chain",
Long: "List all wasm bytecode on the chain",
Aliases: []string{"list-codes", "codes", "lco"},
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Codes(
context.Background(),
&types.QueryCodesRequest{
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "list codes")
return cmd
}
// GetCmdListContractByCode lists all wasm code uploaded for given code id
func GetCmdListContractByCode() *cobra.Command {
cmd := &cobra.Command{
Use: "list-contract-by-code [code_id]",
Short: "List wasm all bytecode on the chain for given code id",
Long: "List wasm all bytecode on the chain for given code id",
Aliases: []string{"list-contracts-by-code", "list-contracts", "contracts", "lca"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
codeID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
if codeID == 0 {
return errors.New("empty code id")
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.ContractsByCode(
context.Background(),
&types.QueryContractsByCodeRequest{
CodeId: codeID,
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "list contracts by code")
return cmd
}
// GetCmdQueryCode returns the bytecode for a given contract
func GetCmdQueryCode() *cobra.Command {
cmd := &cobra.Command{
Use: "code [code_id] [output filename]",
Short: "Downloads wasm bytecode for given code id",
Long: "Downloads wasm bytecode for given code id",
Aliases: []string{"source-code", "source"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
codeID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Code(
context.Background(),
&types.QueryCodeRequest{
CodeId: codeID,
},
)
if err != nil {
return err
}
if len(res.Data) == 0 {
return fmt.Errorf("contract not found")
}
fmt.Printf("Downloading wasm code to %s\n", args[1])
return os.WriteFile(args[1], res.Data, 0o600)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
// GetCmdQueryCodeInfo returns the code info for a given code id
func GetCmdQueryCodeInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "code-info [code_id]",
Short: "Prints out metadata of a code id",
Long: "Prints out metadata of a code id",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
codeID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Code(
context.Background(),
&types.QueryCodeRequest{
CodeId: codeID,
},
)
if err != nil {
return err
}
if res.CodeInfoResponse == nil {
return fmt.Errorf("contract not found")
}
return clientCtx.PrintProto(res.CodeInfoResponse)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
// GetCmdGetContractInfo gets details about a given contract
func GetCmdGetContractInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "contract [bech32_address]",
Short: "Prints out metadata of a contract given its address",
Long: "Prints out metadata of a contract given its address",
Aliases: []string{"meta", "c"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.ContractInfo(
context.Background(),
&types.QueryContractInfoRequest{
Address: args[0],
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
// GetCmdGetContractState dumps full internal state of a given contract
func GetCmdGetContractState() *cobra.Command {
cmd := &cobra.Command{
Use: "contract-state",
Short: "Querying commands for the wasm module",
Aliases: []string{"state", "cs", "s"},
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
SilenceUsage: true,
}
cmd.AddCommand(
GetCmdGetContractStateAll(),
GetCmdGetContractStateRaw(),
GetCmdGetContractStateSmart(),
)
return cmd
}
func GetCmdGetContractStateAll() *cobra.Command {
cmd := &cobra.Command{
Use: "all [bech32_address]",
Short: "Prints out all internal state of a contract given its address",
Long: "Prints out all internal state of a contract given its address",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.AllContractState(
context.Background(),
&types.QueryAllContractStateRequest{
Address: args[0],
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "contract state")
return cmd
}
func GetCmdGetContractStateRaw() *cobra.Command {
decoder := newArgDecoder(hex.DecodeString)
cmd := &cobra.Command{
Use: "raw [bech32_address] [key]",
Short: "Prints out internal state for key of a contract given its address",
Long: "Prints out internal state for of a contract given its address",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
queryData, err := decoder.DecodeString(args[1])
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.RawContractState(
context.Background(),
&types.QueryRawContractStateRequest{
Address: args[0],
QueryData: queryData,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
decoder.RegisterFlags(cmd.PersistentFlags(), "key argument")
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
func GetCmdGetContractStateSmart() *cobra.Command {
decoder := newArgDecoder(asciiDecodeString)
cmd := &cobra.Command{
Use: "smart [bech32_address] [query]",
Short: "Calls contract with given address with query data and prints the returned result",
Long: "Calls contract with given address with query data and prints the returned result",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
if args[1] == "" {
return errors.New("query data must not be empty")
}
queryData, err := decoder.DecodeString(args[1])
if err != nil {
return fmt.Errorf("decode query: %s", err)
}
if !json.Valid(queryData) {
return errors.New("query data must be json")
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.SmartContractState(
context.Background(),
&types.QuerySmartContractStateRequest{
Address: args[0],
QueryData: queryData,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
decoder.RegisterFlags(cmd.PersistentFlags(), "query argument")
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
// GetCmdGetContractHistory prints the code history for a given contract
func GetCmdGetContractHistory() *cobra.Command {
cmd := &cobra.Command{
Use: "contract-history [bech32_address]",
Short: "Prints out the code history for a contract given its address",
Long: "Prints out the code history for a contract given its address",
Aliases: []string{"history", "hist", "ch"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.ContractHistory(
context.Background(),
&types.QueryContractHistoryRequest{
Address: args[0],
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "contract history")
return cmd
}
// GetCmdListPinnedCode lists all wasm code ids that are pinned
func GetCmdListPinnedCode() *cobra.Command {
cmd := &cobra.Command{
Use: "pinned",
Short: "List all pinned code ids",
Long: "List all pinned code ids",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.PinnedCodes(
context.Background(),
&types.QueryPinnedCodesRequest{
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "list codes")
return cmd
}
// GetCmdListContractsByCreator lists all contracts by creator
func GetCmdListContractsByCreator() *cobra.Command {
cmd := &cobra.Command{
Use: "list-contracts-by-creator [creator]",
Short: "List all contracts by creator",
Long: "List all contracts by creator",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.ContractsByCreator(
context.Background(),
&types.QueryContractsByCreatorRequest{
CreatorAddress: args[0],
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
type argumentDecoder struct {
// dec is the default decoder
dec func(string) ([]byte, error)
asciiF, hexF, b64F bool
}
func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
return &argumentDecoder{dec: def}
}
func (a *argumentDecoder) RegisterFlags(f *flag.FlagSet, argName string) {
f.BoolVar(&a.asciiF, "ascii", false, "ascii encoded "+argName)
f.BoolVar(&a.hexF, "hex", false, "hex encoded "+argName)
f.BoolVar(&a.b64F, "b64", false, "base64 encoded "+argName)
}
func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
found := -1
for i, v := range []*bool{&a.asciiF, &a.hexF, &a.b64F} {
if !*v {
continue
}
if found != -1 {
return nil, errors.New("multiple decoding flags used")
}
found = i
}
switch found {
case 0:
return asciiDecodeString(s)
case 1:
return hex.DecodeString(s)
case 2:
return base64.StdEncoding.DecodeString(s)
default:
return a.dec(s)
}
}
func asciiDecodeString(s string) ([]byte, error) {
return []byte(s), nil
}
// sdk ReadPageRequest expects binary but we encoded to base64 in our marshaller
func withPageKeyDecoded(flagSet *flag.FlagSet) *flag.FlagSet {
encoded, err := flagSet.GetString(flags.FlagPageKey)
if err != nil {
panic(err.Error())
}
raw, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
panic(err.Error())
}
err = flagSet.Set(flags.FlagPageKey, string(raw))
if err != nil {
panic(err.Error())
}
return flagSet
}
// GetCmdQueryParams implements a command to return the current wasm
// parameters.
func GetCmdQueryParams() *cobra.Command {
cmd := &cobra.Command{
Use: "params",
Short: "Query the current wasm parameters",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
params := &types.QueryParamsRequest{}
res, err := queryClient.Params(cmd.Context(), params)
if err != nil {
return err
}
return clientCtx.PrintProto(&res.Params)
},
SilenceUsage: true,
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

544
x/wasm/client/cli/tx.go Normal file
View File

@ -0,0 +1,544 @@
package cli
import (
"encoding/hex"
"errors"
"fmt"
"os"
"strconv"
"time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/cerc-io/laconicd/x/wasm/ioutils"
"github.com/cerc-io/laconicd/x/wasm/types"
)
const (
flagAmount = "amount"
flagLabel = "label"
flagSource = "code-source-url"
flagBuilder = "builder"
flagCodeHash = "code-hash"
flagAdmin = "admin"
flagNoAdmin = "no-admin"
flagFixMsg = "fix-msg"
flagRunAs = "run-as"
flagInstantiateByEverybody = "instantiate-everybody"
flagInstantiateNobody = "instantiate-nobody"
flagInstantiateByAddress = "instantiate-only-address"
flagInstantiateByAnyOfAddress = "instantiate-anyof-addresses"
flagUnpinCode = "unpin-code"
flagAllowedMsgKeys = "allow-msg-keys"
flagAllowedRawMsgs = "allow-raw-msgs"
flagExpiration = "expiration"
flagMaxCalls = "max-calls"
flagMaxFunds = "max-funds"
flagAllowAllMsgs = "allow-all-messages"
flagNoTokenTransfer = "no-token-transfer" //nolint:gosec
)
// GetTxCmd returns the transaction commands for this module
func GetTxCmd() *cobra.Command {
txCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Wasm transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
SilenceUsage: true,
}
txCmd.AddCommand(
StoreCodeCmd(),
InstantiateContractCmd(),
InstantiateContract2Cmd(),
ExecuteContractCmd(),
MigrateContractCmd(),
UpdateContractAdminCmd(),
ClearContractAdminCmd(),
GrantAuthorizationCmd(),
UpdateInstantiateConfigCmd(),
)
return txCmd
}
// StoreCodeCmd will upload code to be reused.
func StoreCodeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "store [wasm file]",
Short: "Upload a wasm binary",
Aliases: []string{"upload", "st", "s"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseStoreCodeArgs(args[0], clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
if err = msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
addInstantiatePermissionFlags(cmd)
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgStoreCode, error) {
wasm, err := os.ReadFile(file)
if err != nil {
return types.MsgStoreCode{}, err
}
// gzip the wasm file
if ioutils.IsWasm(wasm) {
wasm, err = ioutils.GzipIt(wasm)
if err != nil {
return types.MsgStoreCode{}, err
}
} else if !ioutils.IsGzip(wasm) {
return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip")
}
perm, err := parseAccessConfigFlags(flags)
if err != nil {
return types.MsgStoreCode{}, err
}
msg := types.MsgStoreCode{
Sender: sender.String(),
WASMByteCode: wasm,
InstantiatePermission: perm,
}
return msg, nil
}
func parseAccessConfigFlags(flags *flag.FlagSet) (*types.AccessConfig, error) {
addrs, err := flags.GetStringSlice(flagInstantiateByAnyOfAddress)
if err != nil {
return nil, fmt.Errorf("flag any of: %s", err)
}
if len(addrs) != 0 {
acceptedAddrs := make([]sdk.AccAddress, len(addrs))
for i, v := range addrs {
acceptedAddrs[i], err = sdk.AccAddressFromBech32(v)
if err != nil {
return nil, fmt.Errorf("parse %q: %w", v, err)
}
}
x := types.AccessTypeAnyOfAddresses.With(acceptedAddrs...)
return &x, nil
}
onlyAddrStr, err := flags.GetString(flagInstantiateByAddress)
if err != nil {
return nil, fmt.Errorf("instantiate by address: %s", err)
}
if onlyAddrStr != "" {
return nil, fmt.Errorf("not supported anymore. Use: %s", flagInstantiateByAnyOfAddress)
}
everybodyStr, err := flags.GetString(flagInstantiateByEverybody)
if err != nil {
return nil, fmt.Errorf("instantiate by everybody: %s", err)
}
if everybodyStr != "" {
ok, err := strconv.ParseBool(everybodyStr)
if err != nil {
return nil, fmt.Errorf("boolean value expected for instantiate by everybody: %s", err)
}
if ok {
return &types.AllowEverybody, nil
}
}
nobodyStr, err := flags.GetString(flagInstantiateNobody)
if err != nil {
return nil, fmt.Errorf("instantiate by nobody: %s", err)
}
if nobodyStr != "" {
ok, err := strconv.ParseBool(nobodyStr)
if err != nil {
return nil, fmt.Errorf("boolean value expected for instantiate by nobody: %s", err)
}
if ok {
return &types.AllowNobody, nil
}
}
return nil, nil
}
func addInstantiatePermissionFlags(cmd *cobra.Command) {
cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional")
cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional")
cmd.Flags().String(flagInstantiateByAddress, "", fmt.Sprintf("Removed: use %s instead", flagInstantiateByAnyOfAddress))
cmd.Flags().StringSlice(flagInstantiateByAnyOfAddress, []string{}, "Any of the addresses can instantiate a contract from the code, optional")
}
// InstantiateContractCmd will instantiate a contract from previously uploaded code.
func InstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "instantiate [code_id_int64] [json_encoded_init_args] --label [text] --admin [address,optional] --amount [coins,optional] ",
Short: "Instantiate a wasm contract",
Long: fmt.Sprintf(`Creates a new instance of an uploaded wasm code with the given 'constructor' message.
Each contract instance has a unique address assigned.
Example:
$ %s tx wasm instantiate 1 '{"foo":"bar"}' --admin="$(%s keys show mykey -a)" \
--from mykey --amount="100ustake" --label "local0.1.0"
`, version.AppName, version.AppName),
Aliases: []string{"start", "init", "inst", "i"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseInstantiateArgs(args[0], args[1], clientCtx.Keyring, clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
cmd.Flags().String(flagAdmin, "", "Address or key name of an admin")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
flags.AddTxFlagsToCmd(cmd)
return cmd
}
// InstantiateContract2Cmd will instantiate a contract from previously uploaded code with predicable address generated
func InstantiateContract2Cmd() *cobra.Command {
decoder := newArgDecoder(hex.DecodeString)
cmd := &cobra.Command{
Use: "instantiate2 [code_id_int64] [json_encoded_init_args] [salt] --label [text] --admin [address,optional] --amount [coins,optional] " +
"--fix-msg [bool,optional]",
Short: "Instantiate a wasm contract with predictable address",
Long: fmt.Sprintf(`Creates a new instance of an uploaded wasm code with the given 'constructor' message.
Each contract instance has a unique address assigned. They are assigned automatically but in order to have predictable addresses
for special use cases, the given 'salt' argument and '--fix-msg' parameters can be used to generate a custom address.
Predictable address example (also see '%s query wasm build-address -h'):
$ %s tx wasm instantiate2 1 '{"foo":"bar"}' $(echo -n "testing" | xxd -ps) --admin="$(%s keys show mykey -a)" \
--from mykey --amount="100ustake" --label "local0.1.0" \
--fix-msg
`, version.AppName, version.AppName, version.AppName),
Aliases: []string{"start", "init", "inst", "i"},
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
salt, err := decoder.DecodeString(args[2])
if err != nil {
return fmt.Errorf("salt: %w", err)
}
fixMsg, err := cmd.Flags().GetBool(flagFixMsg)
if err != nil {
return fmt.Errorf("fix msg: %w", err)
}
data, err := parseInstantiateArgs(args[0], args[1], clientCtx.Keyring, clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
msg := &types.MsgInstantiateContract2{
Sender: data.Sender,
Admin: data.Admin,
CodeID: data.CodeID,
Label: data.Label,
Msg: data.Msg,
Funds: data.Funds,
Salt: salt,
FixMsg: fixMsg,
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
cmd.Flags().String(flagAdmin, "", "Address or key name of an admin")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
cmd.Flags().Bool(flagFixMsg, false, "An optional flag to include the json_encoded_init_args for the predictable address generation mode")
decoder.RegisterFlags(cmd.PersistentFlags(), "salt")
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func parseInstantiateArgs(rawCodeID, initMsg string, kr keyring.Keyring, sender sdk.AccAddress, flags *flag.FlagSet) (*types.MsgInstantiateContract, error) {
// get the id of the code to instantiate
codeID, err := strconv.ParseUint(rawCodeID, 10, 64)
if err != nil {
return nil, err
}
amountStr, err := flags.GetString(flagAmount)
if err != nil {
return nil, fmt.Errorf("amount: %s", err)
}
amount, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return nil, fmt.Errorf("amount: %s", err)
}
label, err := flags.GetString(flagLabel)
if err != nil {
return nil, fmt.Errorf("label: %s", err)
}
if label == "" {
return nil, errors.New("label is required on all contracts")
}
adminStr, err := flags.GetString(flagAdmin)
if err != nil {
return nil, fmt.Errorf("admin: %s", err)
}
noAdmin, err := flags.GetBool(flagNoAdmin)
if err != nil {
return nil, fmt.Errorf("no-admin: %s", err)
}
// ensure sensible admin is set (or explicitly immutable)
if adminStr == "" && !noAdmin {
return nil, fmt.Errorf("you must set an admin or explicitly pass --no-admin to make it immutible (wasmd issue #719)")
}
if adminStr != "" && noAdmin {
return nil, fmt.Errorf("you set an admin and passed --no-admin, those cannot both be true")
}
if adminStr != "" {
addr, err := sdk.AccAddressFromBech32(adminStr)
if err != nil {
info, err := kr.Key(adminStr)
if err != nil {
return nil, fmt.Errorf("admin %s", err)
}
adminStr = info.GetAddress().String()
} else {
adminStr = addr.String()
}
}
// build and sign the transaction, then broadcast to Tendermint
msg := types.MsgInstantiateContract{
Sender: sender.String(),
CodeID: codeID,
Label: label,
Funds: amount,
Msg: []byte(initMsg),
Admin: adminStr,
}
return &msg, nil
}
// ExecuteContractCmd will instantiate a contract from previously uploaded code.
func ExecuteContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "execute [contract_addr_bech32] [json_encoded_send_args] --amount [coins,optional]",
Short: "Execute a command on a wasm contract",
Aliases: []string{"run", "call", "exec", "ex", "e"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseExecuteArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
SilenceUsage: true,
}
cmd.Flags().String(flagAmount, "", "Coins to send to the contract along with command")
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func parseExecuteArgs(contractAddr string, execMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) {
amountStr, err := flags.GetString(flagAmount)
if err != nil {
return types.MsgExecuteContract{}, fmt.Errorf("amount: %s", err)
}
amount, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return types.MsgExecuteContract{}, err
}
return types.MsgExecuteContract{
Sender: sender.String(),
Contract: contractAddr,
Funds: amount,
Msg: []byte(execMsg),
}, nil
}
func GrantAuthorizationCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "grant [grantee] [message_type=\"execution\"|\"migration\"] [contract_addr_bech32] --allow-raw-msgs [msg1,msg2,...] --allow-msg-keys [key1,key2,...] --allow-all-messages",
Short: "Grant authorization to an address",
Long: fmt.Sprintf(`Grant authorization to an address.
Examples:
$ %s tx grant <grantee_addr> execution <contract_addr> --allow-all-messages --max-calls 1 --no-token-transfer --expiration 1667979596
$ %s tx grant <grantee_addr> execution <contract_addr> --allow-all-messages --max-funds 100000uwasm --expiration 1667979596
$ %s tx grant <grantee_addr> execution <contract_addr> --allow-all-messages --max-calls 5 --max-funds 100000uwasm --expiration 1667979596
`, version.AppName, version.AppName, version.AppName),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
grantee, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
contract, err := sdk.AccAddressFromBech32(args[2])
if err != nil {
return err
}
msgKeys, err := cmd.Flags().GetStringSlice(flagAllowedMsgKeys)
if err != nil {
return err
}
rawMsgs, err := cmd.Flags().GetStringSlice(flagAllowedRawMsgs)
if err != nil {
return err
}
maxFundsStr, err := cmd.Flags().GetString(flagMaxFunds)
if err != nil {
return fmt.Errorf("max funds: %s", err)
}
maxCalls, err := cmd.Flags().GetUint64(flagMaxCalls)
if err != nil {
return err
}
exp, err := cmd.Flags().GetInt64(flagExpiration)
if err != nil {
return err
}
if exp == 0 {
return errors.New("expiration must be set")
}
allowAllMsgs, err := cmd.Flags().GetBool(flagAllowAllMsgs)
if err != nil {
return err
}
noTokenTransfer, err := cmd.Flags().GetBool(flagNoTokenTransfer)
if err != nil {
return err
}
var limit types.ContractAuthzLimitX
switch {
case maxFundsStr != "" && maxCalls != 0 && !noTokenTransfer:
maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr)
if err != nil {
return fmt.Errorf("max funds: %s", err)
}
limit = types.NewCombinedLimit(maxCalls, maxFunds...)
case maxFundsStr != "" && maxCalls == 0 && !noTokenTransfer:
maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr)
if err != nil {
return fmt.Errorf("max funds: %s", err)
}
limit = types.NewMaxFundsLimit(maxFunds...)
case maxCalls != 0 && noTokenTransfer && maxFundsStr == "":
limit = types.NewMaxCallsLimit(maxCalls)
default:
return errors.New("invalid limit setup")
}
var filter types.ContractAuthzFilterX
switch {
case allowAllMsgs && len(msgKeys) != 0 || allowAllMsgs && len(rawMsgs) != 0 || len(msgKeys) != 0 && len(rawMsgs) != 0:
return errors.New("cannot set more than one filter within one grant")
case allowAllMsgs:
filter = types.NewAllowAllMessagesFilter()
case len(msgKeys) != 0:
filter = types.NewAcceptedMessageKeysFilter(msgKeys...)
case len(rawMsgs) != 0:
msgs := make([]types.RawContractMessage, len(rawMsgs))
for i, msg := range rawMsgs {
msgs[i] = types.RawContractMessage(msg)
}
filter = types.NewAcceptedMessagesFilter(msgs...)
default:
return errors.New("invalid filter setup")
}
grant, err := types.NewContractGrant(contract, limit, filter)
if err != nil {
return err
}
var authorization authz.Authorization
switch args[1] {
case "execution":
authorization = types.NewContractExecutionAuthorization(*grant)
case "migration":
authorization = types.NewContractMigrationAuthorization(*grant)
default:
return fmt.Errorf("%s authorization type not supported", args[1])
}
grantMsg, err := authz.NewMsgGrant(clientCtx.GetFromAddress(), grantee, authorization, time.Unix(0, exp))
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), grantMsg)
},
}
flags.AddTxFlagsToCmd(cmd)
cmd.Flags().StringSlice(flagAllowedMsgKeys, []string{}, "Allowed msg keys")
cmd.Flags().StringSlice(flagAllowedRawMsgs, []string{}, "Allowed raw msgs")
cmd.Flags().Uint64(flagMaxCalls, 0, "Maximal number of calls to the contract")
cmd.Flags().String(flagMaxFunds, "", "Maximal amount of tokens transferable to the contract.")
cmd.Flags().Int64(flagExpiration, 0, "The Unix timestamp.")
cmd.Flags().Bool(flagAllowAllMsgs, false, "Allow all messages")
cmd.Flags().Bool(flagNoTokenTransfer, false, "Don't allow token transfer")
return cmd
}

View File

@ -0,0 +1,59 @@
package cli
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestParseAccessConfigFlags(t *testing.T) {
specs := map[string]struct {
args []string
expCfg *types.AccessConfig
expErr bool
}{
"nobody": {
args: []string{"--instantiate-nobody=true"},
expCfg: &types.AccessConfig{Permission: types.AccessTypeNobody},
},
"everybody": {
args: []string{"--instantiate-everybody=true"},
expCfg: &types.AccessConfig{Permission: types.AccessTypeEverybody},
},
"only address": {
args: []string{"--instantiate-only-address=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"},
expErr: true,
},
"only address - invalid": {
args: []string{"--instantiate-only-address=foo"},
expErr: true,
},
"any of address": {
args: []string{"--instantiate-anyof-addresses=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"},
expCfg: &types.AccessConfig{Permission: types.AccessTypeAnyOfAddresses, Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}},
},
"any of address - invalid": {
args: []string{"--instantiate-anyof-addresses=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,foo"},
expErr: true,
},
"not set": {
args: []string{},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
flags := StoreCodeCmd().Flags()
require.NoError(t, flags.Parse(spec.args))
gotCfg, gotErr := parseAccessConfigFlags(flags)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
assert.Equal(t, spec.expCfg, gotCfg)
})
}
}

View File

@ -0,0 +1,25 @@
package client
import (
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
"github.com/cerc-io/laconicd/x/wasm/client/cli"
"github.com/cerc-io/laconicd/x/wasm/client/rest" //nolint:staticcheck
)
// ProposalHandlers define the wasm cli proposal types and rest handler.
// Deprecated: the rest package will be removed. You can use the GRPC gateway instead
var ProposalHandlers = []govclient.ProposalHandler{
govclient.NewProposalHandler(cli.ProposalStoreCodeCmd, rest.StoreCodeProposalHandler),
govclient.NewProposalHandler(cli.ProposalInstantiateContractCmd, rest.InstantiateProposalHandler),
govclient.NewProposalHandler(cli.ProposalMigrateContractCmd, rest.MigrateProposalHandler),
govclient.NewProposalHandler(cli.ProposalExecuteContractCmd, rest.ExecuteProposalHandler),
govclient.NewProposalHandler(cli.ProposalSudoContractCmd, rest.SudoProposalHandler),
govclient.NewProposalHandler(cli.ProposalUpdateContractAdminCmd, rest.UpdateContractAdminProposalHandler),
govclient.NewProposalHandler(cli.ProposalClearContractAdminCmd, rest.ClearContractAdminProposalHandler),
govclient.NewProposalHandler(cli.ProposalPinCodesCmd, rest.PinCodeProposalHandler),
govclient.NewProposalHandler(cli.ProposalUnpinCodesCmd, rest.UnpinCodeProposalHandler),
govclient.NewProposalHandler(cli.ProposalUpdateInstantiateConfigCmd, rest.UpdateInstantiateConfigProposalHandler),
govclient.NewProposalHandler(cli.ProposalStoreAndInstantiateContractCmd, rest.EmptyRestHandler),
govclient.NewProposalHandler(cli.ProposalInstantiateContract2Cmd, rest.EmptyRestHandler),
}

View File

@ -0,0 +1,381 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/keeper"
)
func TestGovRestHandlers(t *testing.T) {
type dict map[string]interface{}
var (
anyAddress = "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz"
aBaseReq = dict{
"from": anyAddress,
"memo": "rest test",
"chain_id": "testing",
"account_number": "1",
"sequence": "1",
"fees": []dict{{"denom": "ustake", "amount": "1000000"}},
}
)
encodingConfig := keeper.MakeEncodingConfig(t)
clientCtx := client.Context{}.
WithCodec(encodingConfig.Marshaler).
WithTxConfig(encodingConfig.TxConfig).
WithLegacyAmino(encodingConfig.Amino).
WithInput(os.Stdin).
WithAccountRetriever(authtypes.AccountRetriever{}).
WithBroadcastMode(flags.BroadcastBlock).
WithChainID("testing")
// router setup as in gov/client/rest/tx.go
propSubRtr := mux.NewRouter().PathPrefix("/gov/proposals").Subrouter()
for _, ph := range ProposalHandlers {
r := ph.RESTHandler(clientCtx)
propSubRtr.HandleFunc(fmt.Sprintf("/%s", r.SubRoute), r.Handler).Methods("POST")
}
specs := map[string]struct {
srcBody dict
srcPath string
expCode int
}{
"store-code": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": []byte("valid wasm byte code"),
"source": "https://example.com/",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"store-code without verification info": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": []byte("valid wasm byte code"),
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"store-code without permission": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": []byte("valid wasm byte code"),
"source": "https://example.com/",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"store-code invalid permission": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": []byte("valid wasm byte code"),
"source": "https://example.com/",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"instantiate_permission": dict{
"permission": "Nobody",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"store-code with incomplete proposal data: blank title": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": []byte("valid wasm byte code"),
"source": "https://example.com/",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"store-code with incomplete content data: no wasm_byte_code": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": "",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"source": "https://example.com/",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"store-code with incomplete content data: no builder": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": "",
"source": "https://example.com/",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"store-code with incomplete content data: no code hash": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": "",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"source": "https://example.com/",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"store-code with incomplete content data: no source": {
srcPath: "/gov/proposals/wasm_store_code",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "store-code",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"wasm_byte_code": "",
"builder": "cosmwasm/workspace-optimizer:v0.12.9",
"code_hash": "79F174F09BFE3F83398BF7C147929D5F735161BD46D645E85216BB13BF91D42D",
"instantiate_permission": dict{
"permission": "OnlyAddress",
"address": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"instantiate contract": {
srcPath: "/gov/proposals/wasm_instantiate",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "instantiate",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"admin": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"code_id": "1",
"label": "https://example.com/",
"msg": dict{"recipient": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz"},
"funds": []dict{{"denom": "ustake", "amount": "100"}},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"migrate contract": {
srcPath: "/gov/proposals/wasm_migrate",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"code_id": "1",
"msg": dict{"foo": "bar"},
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"execute contract": {
srcPath: "/gov/proposals/wasm_execute",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"msg": dict{"foo": "bar"},
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"execute contract fails with no run_as": {
srcPath: "/gov/proposals/wasm_execute",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"msg": dict{"foo": "bar"},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"execute contract fails with no message": {
srcPath: "/gov/proposals/wasm_execute",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"run_as": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"sudo contract": {
srcPath: "/gov/proposals/wasm_sudo",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"msg": dict{"foo": "bar"},
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"sudo contract fails with no message": {
srcPath: "/gov/proposals/wasm_sudo",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusBadRequest,
},
"update contract admin": {
srcPath: "/gov/proposals/wasm_update_admin",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"new_admin": "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
"clear contract admin": {
srcPath: "/gov/proposals/wasm_clear_admin",
srcBody: dict{
"title": "Test Proposal",
"description": "My proposal",
"type": "migrate",
"contract": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"deposit": []dict{{"denom": "ustake", "amount": "10"}},
"proposer": "cosmos1ve557a5g9yw2g2z57js3pdmcvd5my6g8ze20np",
"base_req": aBaseReq,
},
expCode: http.StatusOK,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
src, err := json.Marshal(spec.srcBody)
require.NoError(t, err)
// when
r := httptest.NewRequest("POST", spec.srcPath, bytes.NewReader(src))
w := httptest.NewRecorder()
propSubRtr.ServeHTTP(w, r)
// then
require.Equal(t, spec.expCode, w.Code, w.Body.String())
})
}
}

547
x/wasm/client/rest/gov.go Normal file
View File

@ -0,0 +1,547 @@
package rest
import (
"encoding/json"
"net/http"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
type StoreCodeProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
RunAs string `json:"run_as" yaml:"run_as"`
// WASMByteCode can be raw or gzip compressed
WASMByteCode []byte `json:"wasm_byte_code" yaml:"wasm_byte_code"`
// InstantiatePermission to apply on contract creation, optional
InstantiatePermission *types.AccessConfig `json:"instantiate_permission" yaml:"instantiate_permission"`
// UnpinCode indicates if the code should not be pinned as part of the proposal.
UnpinCode bool `json:"unpin_code" yaml:"unpin_code"`
// Source is the URL where the code is hosted
Source string `json:"source" yaml:"source"`
// Builder is the docker image used to build the code deterministically, used for smart
// contract verification
Builder string `json:"builder" yaml:"builder"`
// CodeHash is the SHA256 sum of the code outputted by optimizer, used for smart contract verification
CodeHash []byte `json:"code_hash" yaml:"code_hash"`
}
func (s StoreCodeProposalJSONReq) Content() govtypes.Content {
return &types.StoreCodeProposal{
Title: s.Title,
Description: s.Description,
RunAs: s.RunAs,
WASMByteCode: s.WASMByteCode,
InstantiatePermission: s.InstantiatePermission,
UnpinCode: s.UnpinCode,
Source: s.Source,
Builder: s.Builder,
CodeHash: s.CodeHash,
}
}
func (s StoreCodeProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s StoreCodeProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s StoreCodeProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func StoreCodeProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_store_code",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req StoreCodeProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type InstantiateProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
RunAs string `json:"run_as" yaml:"run_as"`
// Admin is an optional address that can execute migrations
Admin string `json:"admin,omitempty" yaml:"admin"`
Code uint64 `json:"code_id" yaml:"code_id"`
Label string `json:"label" yaml:"label"`
Msg json.RawMessage `json:"msg" yaml:"msg"`
Funds sdk.Coins `json:"funds" yaml:"funds"`
}
func (s InstantiateProposalJSONReq) Content() govtypes.Content {
return &types.InstantiateContractProposal{
Title: s.Title,
Description: s.Description,
RunAs: s.RunAs,
Admin: s.Admin,
CodeID: s.Code,
Label: s.Label,
Msg: types.RawContractMessage(s.Msg),
Funds: s.Funds,
}
}
func (s InstantiateProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s InstantiateProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s InstantiateProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func InstantiateProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_instantiate",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req InstantiateProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type MigrateProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
Contract string `json:"contract" yaml:"contract"`
Code uint64 `json:"code_id" yaml:"code_id"`
Msg json.RawMessage `json:"msg" yaml:"msg"`
}
func (s MigrateProposalJSONReq) Content() govtypes.Content {
return &types.MigrateContractProposal{
Title: s.Title,
Description: s.Description,
Contract: s.Contract,
CodeID: s.Code,
Msg: types.RawContractMessage(s.Msg),
}
}
func (s MigrateProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s MigrateProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s MigrateProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func MigrateProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_migrate",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req MigrateProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type ExecuteProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
Contract string `json:"contract" yaml:"contract"`
Msg json.RawMessage `json:"msg" yaml:"msg"`
// RunAs is the role that is passed to the contract's environment
RunAs string `json:"run_as" yaml:"run_as"`
Funds sdk.Coins `json:"funds" yaml:"funds"`
}
func (s ExecuteProposalJSONReq) Content() govtypes.Content {
return &types.ExecuteContractProposal{
Title: s.Title,
Description: s.Description,
Contract: s.Contract,
Msg: types.RawContractMessage(s.Msg),
RunAs: s.RunAs,
Funds: s.Funds,
}
}
func (s ExecuteProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s ExecuteProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s ExecuteProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func ExecuteProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_execute",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req ExecuteProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type SudoProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
Contract string `json:"contract" yaml:"contract"`
Msg json.RawMessage `json:"msg" yaml:"msg"`
}
func (s SudoProposalJSONReq) Content() govtypes.Content {
return &types.SudoContractProposal{
Title: s.Title,
Description: s.Description,
Contract: s.Contract,
Msg: types.RawContractMessage(s.Msg),
}
}
func (s SudoProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s SudoProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s SudoProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func SudoProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_sudo",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req SudoProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type UpdateAdminJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
NewAdmin string `json:"new_admin" yaml:"new_admin"`
Contract string `json:"contract" yaml:"contract"`
}
func (s UpdateAdminJSONReq) Content() govtypes.Content {
return &types.UpdateAdminProposal{
Title: s.Title,
Description: s.Description,
Contract: s.Contract,
NewAdmin: s.NewAdmin,
}
}
func (s UpdateAdminJSONReq) GetProposer() string {
return s.Proposer
}
func (s UpdateAdminJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s UpdateAdminJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func UpdateContractAdminProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_update_admin",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req UpdateAdminJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type ClearAdminJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
Contract string `json:"contract" yaml:"contract"`
}
func (s ClearAdminJSONReq) Content() govtypes.Content {
return &types.ClearAdminProposal{
Title: s.Title,
Description: s.Description,
Contract: s.Contract,
}
}
func (s ClearAdminJSONReq) GetProposer() string {
return s.Proposer
}
func (s ClearAdminJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s ClearAdminJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func ClearContractAdminProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "wasm_clear_admin",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req ClearAdminJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type PinCodeJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
CodeIDs []uint64 `json:"code_ids" yaml:"code_ids"`
}
func (s PinCodeJSONReq) Content() govtypes.Content {
return &types.PinCodesProposal{
Title: s.Title,
Description: s.Description,
CodeIDs: s.CodeIDs,
}
}
func (s PinCodeJSONReq) GetProposer() string {
return s.Proposer
}
func (s PinCodeJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s PinCodeJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func PinCodeProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "pin_code",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req PinCodeJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type UnpinCodeJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
CodeIDs []uint64 `json:"code_ids" yaml:"code_ids"`
}
func (s UnpinCodeJSONReq) Content() govtypes.Content {
return &types.UnpinCodesProposal{
Title: s.Title,
Description: s.Description,
CodeIDs: s.CodeIDs,
}
}
func (s UnpinCodeJSONReq) GetProposer() string {
return s.Proposer
}
func (s UnpinCodeJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s UnpinCodeJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func UnpinCodeProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "unpin_code",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req UnpinCodeJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type UpdateInstantiateConfigProposalJSONReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Proposer string `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
AccessConfigUpdates []types.AccessConfigUpdate `json:"access_config_updates" yaml:"access_config_updates"`
}
func (s UpdateInstantiateConfigProposalJSONReq) Content() govtypes.Content {
return &types.UpdateInstantiateConfigProposal{
Title: s.Title,
Description: s.Description,
AccessConfigUpdates: s.AccessConfigUpdates,
}
}
func (s UpdateInstantiateConfigProposalJSONReq) GetProposer() string {
return s.Proposer
}
func (s UpdateInstantiateConfigProposalJSONReq) GetDeposit() sdk.Coins {
return s.Deposit
}
func (s UpdateInstantiateConfigProposalJSONReq) GetBaseReq() rest.BaseReq {
return s.BaseReq
}
func UpdateInstantiateConfigProposalHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "update_instantiate_config",
Handler: func(w http.ResponseWriter, r *http.Request) {
var req UpdateInstantiateConfigProposalJSONReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
toStdTxResponse(cliCtx, w, req)
},
}
}
type wasmProposalData interface {
Content() govtypes.Content
GetProposer() string
GetDeposit() sdk.Coins
GetBaseReq() rest.BaseReq
}
func toStdTxResponse(cliCtx client.Context, w http.ResponseWriter, data wasmProposalData) {
proposerAddr, err := sdk.AccAddressFromBech32(data.GetProposer())
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
msg, err := govtypes.NewMsgSubmitProposal(data.Content(), data.GetDeposit(), proposerAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
baseReq := data.GetBaseReq().Sanitize()
if !baseReq.ValidateBasic(w) {
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, baseReq, msg)
}
func EmptyRestHandler(cliCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "unsupported",
Handler: func(w http.ResponseWriter, r *http.Request) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "Legacy REST Routes are not supported for gov proposals")
},
}
}

View File

@ -0,0 +1,86 @@
package rest
import (
"net/http"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/gorilla/mux"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func registerNewTxRoutes(cliCtx client.Context, r *mux.Router) {
r.HandleFunc("/wasm/contract/{contractAddr}/admin", setContractAdminHandlerFn(cliCtx)).Methods("PUT")
r.HandleFunc("/wasm/contract/{contractAddr}/code", migrateContractHandlerFn(cliCtx)).Methods("PUT")
}
type migrateContractReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Admin string `json:"admin,omitempty" yaml:"admin"`
CodeID uint64 `json:"code_id" yaml:"code_id"`
Msg []byte `json:"msg,omitempty" yaml:"msg"`
}
type updateContractAdministrateReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Admin string `json:"admin,omitempty" yaml:"admin"`
}
func setContractAdminHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req updateContractAdministrateReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
vars := mux.Vars(r)
contractAddr := vars["contractAddr"]
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
msg := &types.MsgUpdateAdmin{
Sender: req.BaseReq.From,
NewAdmin: req.Admin,
Contract: contractAddr,
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, msg)
}
}
func migrateContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req migrateContractReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
vars := mux.Vars(r)
contractAddr := vars["contractAddr"]
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
msg := &types.MsgMigrateContract{
Sender: req.BaseReq.From,
Contract: contractAddr,
CodeID: req.CodeID,
Msg: req.Msg,
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, msg)
}
}

270
x/wasm/client/rest/query.go Normal file
View File

@ -0,0 +1,270 @@
package rest
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/gorilla/mux"
"github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func registerQueryRoutes(cliCtx client.Context, r *mux.Router) {
r.HandleFunc("/wasm/code", listCodesHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/code/{codeID}", queryCodeHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/code/{codeID}/contracts", listContractsByCodeHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/contract/{contractAddr}", queryContractHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/contract/{contractAddr}/state", queryContractStateAllHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/contract/{contractAddr}/history", queryContractHistoryFn(cliCtx)).Methods("GET")
r.HandleFunc("/wasm/contract/{contractAddr}/smart/{query}", queryContractStateSmartHandlerFn(cliCtx)).Queries("encoding", "{encoding}").Methods("GET")
r.HandleFunc("/wasm/contract/{contractAddr}/raw/{key}", queryContractStateRawHandlerFn(cliCtx)).Queries("encoding", "{encoding}").Methods("GET")
}
func listCodesHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryListCode)
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
}
}
func queryCodeHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
codeID, err := strconv.ParseUint(mux.Vars(r)["codeID"], 10, 64)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%d", types.QuerierRoute, keeper.QueryGetCode, codeID)
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if len(res) == 0 {
rest.WriteErrorResponse(w, http.StatusNotFound, "contract not found")
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
}
}
func listContractsByCodeHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
codeID, err := strconv.ParseUint(mux.Vars(r)["codeID"], 10, 64)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%d", types.QuerierRoute, keeper.QueryListContractByCode, codeID)
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
}
}
func queryContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContract, addr.String())
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
}
}
func queryContractStateAllHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateAll)
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// parse res
var resultData []types.Model
err = json.Unmarshal(res, &resultData)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, resultData)
}
}
func queryContractStateRawHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
decoder := newArgDecoder(hex.DecodeString)
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
decoder.encoding = mux.Vars(r)["encoding"]
queryData, err := decoder.DecodeString(mux.Vars(r)["key"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateRaw)
res, height, err := cliCtx.QueryWithData(route, queryData)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
// ensure this is base64 encoded
encoded := base64.StdEncoding.EncodeToString(res)
rest.PostProcessResponse(w, cliCtx, encoded)
}
}
type smartResponse struct {
Smart []byte `json:"smart"`
}
func queryContractStateSmartHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
decoder := newArgDecoder(hex.DecodeString)
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
decoder.encoding = mux.Vars(r)["encoding"]
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateSmart)
queryData, err := decoder.DecodeString(mux.Vars(r)["query"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, height, err := cliCtx.QueryWithData(route, queryData)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// return as raw bytes (to be base64-encoded)
responseData := smartResponse{Smart: res}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, responseData)
}
}
func queryContractHistoryFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryContractHistory, addr.String())
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
}
}
type argumentDecoder struct {
// dec is the default decoder
dec func(string) ([]byte, error)
encoding string
}
func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
return &argumentDecoder{dec: def}
}
func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
switch a.encoding {
case "hex":
return hex.DecodeString(s)
case "base64":
return base64.StdEncoding.DecodeString(s)
default:
return a.dec(s)
}
}

View File

@ -0,0 +1,15 @@
// Deprecated: the rest package will be removed. You can use the GRPC gateway instead
package rest
import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/gorilla/mux"
)
// RegisterRoutes registers staking-related REST handlers to a router
// Deprecated: the rest package will be removed. You can use the GRPC gateway instead
func RegisterRoutes(cliCtx client.Context, r *mux.Router) {
registerQueryRoutes(cliCtx, r)
registerTxRoutes(cliCtx, r)
registerNewTxRoutes(cliCtx, r)
}

149
x/wasm/client/rest/tx.go Normal file
View File

@ -0,0 +1,149 @@
package rest
import (
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/gorilla/mux"
"github.com/cerc-io/laconicd/x/wasm/ioutils"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func registerTxRoutes(cliCtx client.Context, r *mux.Router) {
r.HandleFunc("/wasm/code", storeCodeHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/wasm/code/{codeId}", instantiateContractHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/wasm/contract/{contractAddr}", executeContractHandlerFn(cliCtx)).Methods("POST")
}
type storeCodeReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
WasmBytes []byte `json:"wasm_bytes"`
}
type instantiateContractReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Label string `json:"label" yaml:"label"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
Admin string `json:"admin,omitempty" yaml:"admin"`
Msg []byte `json:"msg" yaml:"msg"`
}
type executeContractReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
ExecMsg []byte `json:"exec_msg" yaml:"exec_msg"`
Amount sdk.Coins `json:"coins" yaml:"coins"`
}
func storeCodeHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req storeCodeReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
var err error
wasm := req.WasmBytes
// gzip the wasm file
if ioutils.IsWasm(wasm) {
wasm, err = ioutils.GzipIt(wasm)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
} else if !ioutils.IsGzip(wasm) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "Invalid input file, use wasm binary or zip")
return
}
// build and sign the transaction, then broadcast to Tendermint
msg := types.MsgStoreCode{
Sender: req.BaseReq.From,
WASMByteCode: wasm,
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, &msg)
}
}
func instantiateContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req instantiateContractReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
vars := mux.Vars(r)
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
// get the id of the code to instantiate
codeID, err := strconv.ParseUint(vars["codeId"], 10, 64)
if err != nil {
return
}
msg := types.MsgInstantiateContract{
Sender: req.BaseReq.From,
CodeID: codeID,
Label: req.Label,
Funds: req.Deposit,
Msg: req.Msg,
Admin: req.Admin,
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, &msg)
}
}
func executeContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req executeContractReq
if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) {
return
}
vars := mux.Vars(r)
contractAddr := vars["contractAddr"]
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
msg := types.MsgExecuteContract{
Sender: req.BaseReq.From,
Contract: contractAddr,
Msg: req.ExecMsg,
Funds: req.Amount,
}
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, &msg)
}
}

34
x/wasm/common_test.go Normal file
View File

@ -0,0 +1,34 @@
package wasm
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
// ensure store code returns the expected response
func assertStoreCodeResponse(t *testing.T, data []byte, expected uint64) {
var pStoreResp MsgStoreCodeResponse
require.NoError(t, pStoreResp.Unmarshal(data))
require.Equal(t, pStoreResp.CodeID, expected)
}
// ensure execution returns the expected data
func assertExecuteResponse(t *testing.T, data []byte, expected []byte) {
var pExecResp MsgExecuteContractResponse
require.NoError(t, pExecResp.Unmarshal(data))
require.Equal(t, pExecResp.Data, expected)
}
// ensures this returns a valid bech32 address and returns it
func parseInitResponse(t *testing.T, data []byte) string {
var pInstResp MsgInstantiateContractResponse
require.NoError(t, pInstResp.Unmarshal(data))
require.NotEmpty(t, pInstResp.Address)
addr := pInstResp.Address
// ensure this is a valid sdk address
_, err := sdk.AccAddressFromBech32(addr)
require.NoError(t, err)
return addr
}

96
x/wasm/genesis_test.go Normal file
View File

@ -0,0 +1,96 @@
package wasm
import (
"encoding/json"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
func TestInitGenesis(t *testing.T) {
data := setupTest(t)
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
creator := data.faucet.NewFundedRandomAccount(data.ctx, deposit.Add(deposit...)...)
fred := data.faucet.NewFundedRandomAccount(data.ctx, topUp...)
h := data.module.Route().Handler()
q := data.module.LegacyQuerierHandler(nil)
msg := MsgStoreCode{
Sender: creator.String(),
WASMByteCode: testContract,
}
err := msg.ValidateBasic()
require.NoError(t, err)
res, err := h(data.ctx, &msg)
require.NoError(t, err)
assertStoreCodeResponse(t, res.Data, 1)
_, _, bob := keyPubAddr()
initMsg := initMsg{
Verifier: fred,
Beneficiary: bob,
}
initMsgBz, err := json.Marshal(initMsg)
require.NoError(t, err)
initCmd := MsgInstantiateContract{
Sender: creator.String(),
CodeID: firstCodeID,
Msg: initMsgBz,
Funds: deposit,
Label: "testing",
}
res, err = h(data.ctx, &initCmd)
require.NoError(t, err)
contractBech32Addr := parseInitResponse(t, res.Data)
execCmd := MsgExecuteContract{
Sender: fred.String(),
Contract: contractBech32Addr,
Msg: []byte(`{"release":{}}`),
Funds: topUp,
}
res, err = h(data.ctx, &execCmd)
require.NoError(t, err)
// from https://github.com/CosmWasm/cosmwasm/blob/master/contracts/hackatom/src/contract.rs#L167
assertExecuteResponse(t, res.Data, []byte{0xf0, 0x0b, 0xaa})
// ensure all contract state is as after init
assertCodeList(t, q, data.ctx, 1)
assertCodeBytes(t, q, data.ctx, 1, testContract)
assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr})
assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator)
assertContractState(t, q, data.ctx, contractBech32Addr, state{
Verifier: fred.String(),
Beneficiary: bob.String(),
Funder: creator.String(),
})
// export into genstate
genState := ExportGenesis(data.ctx, &data.keeper)
// create new app to import genstate into
newData := setupTest(t)
q2 := newData.module.LegacyQuerierHandler(nil)
// initialize new app with genstate
InitGenesis(newData.ctx, &newData.keeper, *genState)
// run same checks again on newdata, to make sure it was reinitialized correctly
assertCodeList(t, q2, newData.ctx, 1)
assertCodeBytes(t, q2, newData.ctx, 1, testContract)
assertContractList(t, q2, newData.ctx, 1, []string{contractBech32Addr})
assertContractInfo(t, q2, newData.ctx, contractBech32Addr, 1, creator)
assertContractState(t, q2, newData.ctx, contractBech32Addr, state{
Verifier: fred.String(),
Beneficiary: bob.String(),
Funder: creator.String(),
})
}

77
x/wasm/handler.go Normal file
View File

@ -0,0 +1,77 @@
package wasm
import (
"fmt"
"github.com/gogo/protobuf/proto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cerc-io/laconicd/x/wasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// NewHandler returns a handler for "wasm" type messages.
func NewHandler(k types.ContractOpsKeeper) sdk.Handler {
msgServer := keeper.NewMsgServerImpl(k)
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
var (
res proto.Message
err error
)
switch msg := msg.(type) {
case *MsgStoreCode: //nolint:typecheck
res, err = msgServer.StoreCode(sdk.WrapSDKContext(ctx), msg)
case *MsgInstantiateContract:
res, err = msgServer.InstantiateContract(sdk.WrapSDKContext(ctx), msg)
case *MsgInstantiateContract2:
res, err = msgServer.InstantiateContract2(sdk.WrapSDKContext(ctx), msg)
case *MsgExecuteContract:
res, err = msgServer.ExecuteContract(sdk.WrapSDKContext(ctx), msg)
case *MsgMigrateContract:
res, err = msgServer.MigrateContract(sdk.WrapSDKContext(ctx), msg)
case *MsgUpdateAdmin:
res, err = msgServer.UpdateAdmin(sdk.WrapSDKContext(ctx), msg)
case *MsgClearAdmin:
res, err = msgServer.ClearAdmin(sdk.WrapSDKContext(ctx), msg)
case *types.MsgUpdateInstantiateConfig:
res, err = msgServer.UpdateInstantiateConfig(sdk.WrapSDKContext(ctx), msg)
default:
errMsg := fmt.Sprintf("unrecognized wasm message type: %T", msg)
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
}
ctx = ctx.WithEventManager(filterMessageEvents(ctx))
return sdk.WrapServiceResult(ctx, res, err)
}
}
// filterMessageEvents returns the same events with all of type == EventTypeMessage removed except
// for wasm message types.
// this is so only our top-level message event comes through
func filterMessageEvents(ctx sdk.Context) *sdk.EventManager {
m := sdk.NewEventManager()
for _, e := range ctx.EventManager().Events() {
if e.Type == sdk.EventTypeMessage &&
!hasWasmModuleAttribute(e.Attributes) {
continue
}
m.EmitEvent(e)
}
return m
}
func hasWasmModuleAttribute(attrs []abci.EventAttribute) bool {
for _, a := range attrs {
if sdk.AttributeKeyModule == string(a.Key) &&
types.ModuleName == string(a.Value) {
return true
}
}
return false
}

357
x/wasm/ibc.go Normal file
View File

@ -0,0 +1,357 @@
package wasm
import (
"math"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported"
"github.com/cerc-io/laconicd/x/wasm/types"
)
var _ porttypes.IBCModule = IBCHandler{}
// internal interface that is implemented by ibc middleware
type appVersionGetter interface {
// GetAppVersion returns the application level version with all middleware data stripped out
GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool)
}
type IBCHandler struct {
keeper types.IBCContractKeeper
channelKeeper types.ChannelKeeper
appVersionGetter appVersionGetter
}
func NewIBCHandler(k types.IBCContractKeeper, ck types.ChannelKeeper, vg appVersionGetter) IBCHandler {
return IBCHandler{keeper: k, channelKeeper: ck, appVersionGetter: vg}
}
// OnChanOpenInit implements the IBCModule interface
func (i IBCHandler) OnChanOpenInit(
ctx sdk.Context,
order channeltypes.Order,
connectionHops []string,
portID string,
channelID string,
chanCap *capabilitytypes.Capability,
counterParty channeltypes.Counterparty,
version string,
) (string, error) {
// ensure port, version, capability
if err := ValidateChannelParams(channelID); err != nil {
return "", err
}
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return "", sdkerrors.Wrapf(err, "contract port id")
}
msg := wasmvmtypes.IBCChannelOpenMsg{
OpenInit: &wasmvmtypes.IBCOpenInit{
Channel: wasmvmtypes.IBCChannel{
Endpoint: wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: counterParty.PortId, ChannelID: counterParty.ChannelId},
Order: order.String(),
// DESIGN V3: this may be "" ??
Version: version,
ConnectionID: connectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
},
},
}
// Allow contracts to return a version (or default to proposed version if unset)
acceptedVersion, err := i.keeper.OnOpenChannel(ctx, contractAddr, msg)
if err != nil {
return "", err
}
if acceptedVersion == "" {
acceptedVersion = version
}
// Claim channel capability passed back by IBC module
if err := i.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return "", sdkerrors.Wrap(err, "claim capability")
}
return acceptedVersion, nil
}
// OnChanOpenTry implements the IBCModule interface
func (i IBCHandler) OnChanOpenTry(
ctx sdk.Context,
order channeltypes.Order,
connectionHops []string,
portID, channelID string,
chanCap *capabilitytypes.Capability,
counterParty channeltypes.Counterparty,
counterpartyVersion string,
) (string, error) {
// ensure port, version, capability
if err := ValidateChannelParams(channelID); err != nil {
return "", err
}
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return "", sdkerrors.Wrapf(err, "contract port id")
}
msg := wasmvmtypes.IBCChannelOpenMsg{
OpenTry: &wasmvmtypes.IBCOpenTry{
Channel: wasmvmtypes.IBCChannel{
Endpoint: wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: counterParty.PortId, ChannelID: counterParty.ChannelId},
Order: order.String(),
Version: counterpartyVersion,
ConnectionID: connectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
},
CounterpartyVersion: counterpartyVersion,
},
}
// Allow contracts to return a version (or default to counterpartyVersion if unset)
version, err := i.keeper.OnOpenChannel(ctx, contractAddr, msg)
if err != nil {
return "", err
}
if version == "" {
version = counterpartyVersion
}
// Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos
// (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry)
// If module can already authenticate the capability then module already owns it, so we don't need to claim
// Otherwise, module does not have channel capability, and we must claim it from IBC
if !i.keeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) {
// Only claim channel capability passed back by IBC module if we do not already own it
if err := i.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return "", sdkerrors.Wrap(err, "claim capability")
}
}
return version, nil
}
// OnChanOpenAck implements the IBCModule interface
func (i IBCHandler) OnChanOpenAck(
ctx sdk.Context,
portID, channelID string,
counterpartyChannelID string,
counterpartyVersion string,
) error {
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
}
channelInfo.Counterparty.ChannelId = counterpartyChannelID
appVersion, ok := i.appVersionGetter.GetAppVersion(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "port ID (%s) channel ID (%s)", portID, channelID)
}
msg := wasmvmtypes.IBCChannelConnectMsg{
OpenAck: &wasmvmtypes.IBCOpenAck{
Channel: toWasmVMChannel(portID, channelID, channelInfo, appVersion),
CounterpartyVersion: counterpartyVersion,
},
}
return i.keeper.OnConnectChannel(ctx, contractAddr, msg)
}
// OnChanOpenConfirm implements the IBCModule interface
func (i IBCHandler) OnChanOpenConfirm(ctx sdk.Context, portID, channelID string) error {
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
}
appVersion, ok := i.appVersionGetter.GetAppVersion(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "port ID (%s) channel ID (%s)", portID, channelID)
}
msg := wasmvmtypes.IBCChannelConnectMsg{
OpenConfirm: &wasmvmtypes.IBCOpenConfirm{
Channel: toWasmVMChannel(portID, channelID, channelInfo, appVersion),
},
}
return i.keeper.OnConnectChannel(ctx, contractAddr, msg)
}
// OnChanCloseInit implements the IBCModule interface
func (i IBCHandler) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
}
appVersion, ok := i.appVersionGetter.GetAppVersion(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "port ID (%s) channel ID (%s)", portID, channelID)
}
msg := wasmvmtypes.IBCChannelCloseMsg{
CloseInit: &wasmvmtypes.IBCCloseInit{Channel: toWasmVMChannel(portID, channelID, channelInfo, appVersion)},
}
err = i.keeper.OnCloseChannel(ctx, contractAddr, msg)
if err != nil {
return err
}
// emit events?
return err
}
// OnChanCloseConfirm implements the IBCModule interface
func (i IBCHandler) OnChanCloseConfirm(ctx sdk.Context, portID, channelID string) error {
// counterparty has closed the channel
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
}
appVersion, ok := i.appVersionGetter.GetAppVersion(ctx, portID, channelID)
if !ok {
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "port ID (%s) channel ID (%s)", portID, channelID)
}
msg := wasmvmtypes.IBCChannelCloseMsg{
CloseConfirm: &wasmvmtypes.IBCCloseConfirm{Channel: toWasmVMChannel(portID, channelID, channelInfo, appVersion)},
}
err = i.keeper.OnCloseChannel(ctx, contractAddr, msg)
if err != nil {
return err
}
// emit events?
return err
}
func toWasmVMChannel(portID, channelID string, channelInfo channeltypes.Channel, appVersion string) wasmvmtypes.IBCChannel {
return wasmvmtypes.IBCChannel{
Endpoint: wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: channelInfo.Counterparty.PortId, ChannelID: channelInfo.Counterparty.ChannelId},
Order: channelInfo.Ordering.String(),
Version: appVersion,
ConnectionID: channelInfo.ConnectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
}
}
// OnRecvPacket implements the IBCModule interface
func (i IBCHandler) OnRecvPacket(
ctx sdk.Context,
packet channeltypes.Packet,
relayer sdk.AccAddress,
) ibcexported.Acknowledgement {
contractAddr, err := ContractFromPortID(packet.DestinationPort)
if err != nil {
return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(err, "contract port id"))
}
msg := wasmvmtypes.IBCPacketReceiveMsg{Packet: newIBCPacket(packet), Relayer: relayer.String()}
ack, err := i.keeper.OnRecvPacket(ctx, contractAddr, msg)
if err != nil {
return channeltypes.NewErrorAcknowledgement(err)
}
return ContractConfirmStateAck(ack)
}
var _ ibcexported.Acknowledgement = ContractConfirmStateAck{}
type ContractConfirmStateAck []byte
func (w ContractConfirmStateAck) Success() bool {
return true // always commit state
}
func (w ContractConfirmStateAck) Acknowledgement() []byte {
return w
}
// OnAcknowledgementPacket implements the IBCModule interface
func (i IBCHandler) OnAcknowledgementPacket(
ctx sdk.Context,
packet channeltypes.Packet,
acknowledgement []byte,
relayer sdk.AccAddress,
) error {
contractAddr, err := ContractFromPortID(packet.SourcePort)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
err = i.keeper.OnAckPacket(ctx, contractAddr, wasmvmtypes.IBCPacketAckMsg{
Acknowledgement: wasmvmtypes.IBCAcknowledgement{Data: acknowledgement},
OriginalPacket: newIBCPacket(packet),
Relayer: relayer.String(),
})
if err != nil {
return sdkerrors.Wrap(err, "on ack")
}
return nil
}
// OnTimeoutPacket implements the IBCModule interface
func (i IBCHandler) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error {
contractAddr, err := ContractFromPortID(packet.SourcePort)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}
msg := wasmvmtypes.IBCPacketTimeoutMsg{Packet: newIBCPacket(packet), Relayer: relayer.String()}
err = i.keeper.OnTimeoutPacket(ctx, contractAddr, msg)
if err != nil {
return sdkerrors.Wrap(err, "on timeout")
}
return nil
}
func newIBCPacket(packet channeltypes.Packet) wasmvmtypes.IBCPacket {
timeout := wasmvmtypes.IBCTimeout{
Timestamp: packet.TimeoutTimestamp,
}
if !packet.TimeoutHeight.IsZero() {
timeout.Block = &wasmvmtypes.IBCTimeoutBlock{
Height: packet.TimeoutHeight.RevisionHeight,
Revision: packet.TimeoutHeight.RevisionNumber,
}
}
return wasmvmtypes.IBCPacket{
Data: packet.Data,
Src: wasmvmtypes.IBCEndpoint{ChannelID: packet.SourceChannel, PortID: packet.SourcePort},
Dest: wasmvmtypes.IBCEndpoint{ChannelID: packet.DestinationChannel, PortID: packet.DestinationPort},
Sequence: packet.Sequence,
Timeout: timeout,
}
}
func ValidateChannelParams(channelID string) error {
// NOTE: for escrow address security only 2^32 channels are allowed to be created
// Issue: https://github.com/cosmos/cosmos-sdk/issues/7737
channelSequence, err := channeltypes.ParseChannelSequence(channelID)
if err != nil {
return err
}
if channelSequence > math.MaxUint32 {
return sdkerrors.Wrapf(types.ErrMaxIBCChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, math.MaxUint32)
}
return nil
}

View File

@ -0,0 +1,126 @@
package wasm_test
import (
"testing"
wasmvm "github.com/CosmWasm/wasmvm"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wasmibctesting "github.com/cerc-io/laconicd/x/wasm/ibctesting"
wasmkeeper "github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cerc-io/laconicd/x/wasm/keeper/wasmtesting"
)
func TestOnChanOpenInitVersion(t *testing.T) {
const startVersion = "v1"
specs := map[string]struct {
contractRsp *wasmvmtypes.IBC3ChannelOpenResponse
expVersion string
}{
"different version": {
contractRsp: &wasmvmtypes.IBC3ChannelOpenResponse{Version: "v2"},
expVersion: "v2",
},
"no response": {
expVersion: startVersion,
},
"empty result": {
contractRsp: &wasmvmtypes.IBC3ChannelOpenResponse{},
expVersion: startVersion,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
myContract := &wasmtesting.MockIBCContractCallbacks{
IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) {
return spec.contractRsp, 0, nil
},
}
var (
chainAOpts = []wasmkeeper.Option{
wasmkeeper.WithWasmEngine(
wasmtesting.NewIBCContractMockWasmer(myContract)),
}
coordinator = wasmibctesting.NewCoordinator(t, 2, chainAOpts)
chainA = coordinator.GetChain(wasmibctesting.GetChainID(0))
chainB = coordinator.GetChain(wasmibctesting.GetChainID(1))
myContractAddr = chainA.SeedNewContractInstance()
contractInfo = chainA.App.WasmKeeper.GetContractInfo(chainA.GetContext(), myContractAddr)
)
path := wasmibctesting.NewPath(chainA, chainB)
coordinator.SetupConnections(path)
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
PortID: contractInfo.IBCPortID,
Version: startVersion,
Order: channeltypes.UNORDERED,
}
require.NoError(t, path.EndpointA.ChanOpenInit())
assert.Equal(t, spec.expVersion, path.EndpointA.ChannelConfig.Version)
})
}
}
func TestOnChanOpenTryVersion(t *testing.T) {
const startVersion = ibctransfertypes.Version
specs := map[string]struct {
contractRsp *wasmvmtypes.IBC3ChannelOpenResponse
expVersion string
}{
"different version": {
contractRsp: &wasmvmtypes.IBC3ChannelOpenResponse{Version: "v2"},
expVersion: "v2",
},
"no response": {
expVersion: startVersion,
},
"empty result": {
contractRsp: &wasmvmtypes.IBC3ChannelOpenResponse{},
expVersion: startVersion,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
myContract := &wasmtesting.MockIBCContractCallbacks{
IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) {
return spec.contractRsp, 0, nil
},
}
var (
chainAOpts = []wasmkeeper.Option{
wasmkeeper.WithWasmEngine(
wasmtesting.NewIBCContractMockWasmer(myContract)),
}
coordinator = wasmibctesting.NewCoordinator(t, 2, chainAOpts)
chainA = coordinator.GetChain(wasmibctesting.GetChainID(0))
chainB = coordinator.GetChain(wasmibctesting.GetChainID(1))
myContractAddr = chainA.SeedNewContractInstance()
contractInfo = chainA.ContractInfo(myContractAddr)
)
path := wasmibctesting.NewPath(chainA, chainB)
coordinator.SetupConnections(path)
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
PortID: contractInfo.IBCPortID,
Version: startVersion,
Order: channeltypes.UNORDERED,
}
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
PortID: ibctransfertypes.PortID,
Version: ibctransfertypes.Version,
Order: channeltypes.UNORDERED,
}
require.NoError(t, path.EndpointB.ChanOpenInit())
require.NoError(t, path.EndpointA.ChanOpenTry())
assert.Equal(t, spec.expVersion, path.EndpointA.ChannelConfig.Version)
})
}
}

124
x/wasm/ibc_reflect_test.go Normal file
View File

@ -0,0 +1,124 @@
package wasm_test
import (
"testing"
"github.com/stretchr/testify/assert"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/stretchr/testify/require"
wasmibctesting "github.com/cerc-io/laconicd/x/wasm/ibctesting"
wasmkeeper "github.com/cerc-io/laconicd/x/wasm/keeper"
)
func TestIBCReflectContract(t *testing.T) {
// scenario:
// chain A: ibc_reflect_send.wasm
// chain B: reflect.wasm + ibc_reflect.wasm
//
// Chain A "ibc_reflect_send" sends a IBC packet "on channel connect" event to chain B "ibc_reflect"
// "ibc_reflect" sends a submessage to "reflect" which is returned as submessage.
var (
coordinator = wasmibctesting.NewCoordinator(t, 2)
chainA = coordinator.GetChain(wasmibctesting.GetChainID(0))
chainB = coordinator.GetChain(wasmibctesting.GetChainID(1))
)
coordinator.CommitBlock(chainA, chainB)
initMsg := []byte(`{}`)
codeID := chainA.StoreCodeFile("./keeper/testdata/ibc_reflect_send.wasm").CodeID
sendContractAddr := chainA.InstantiateContract(codeID, initMsg)
reflectID := chainB.StoreCodeFile("./keeper/testdata/reflect.wasm").CodeID
initMsg = wasmkeeper.IBCReflectInitMsg{
ReflectCodeID: reflectID,
}.GetBytes(t)
codeID = chainB.StoreCodeFile("./keeper/testdata/ibc_reflect.wasm").CodeID
reflectContractAddr := chainB.InstantiateContract(codeID, initMsg)
var (
sourcePortID = chainA.ContractInfo(sendContractAddr).IBCPortID
counterpartPortID = chainB.ContractInfo(reflectContractAddr).IBCPortID
)
coordinator.CommitBlock(chainA, chainB)
coordinator.UpdateTime()
require.Equal(t, chainA.CurrentHeader.Time, chainB.CurrentHeader.Time)
path := wasmibctesting.NewPath(chainA, chainB)
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
PortID: sourcePortID,
Version: "ibc-reflect-v1",
Order: channeltypes.ORDERED,
}
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
PortID: counterpartPortID,
Version: "ibc-reflect-v1",
Order: channeltypes.ORDERED,
}
coordinator.SetupConnections(path)
coordinator.CreateChannels(path)
// TODO: query both contracts directly to ensure they have registered the proper connection
// (and the chainB has created a reflect contract)
// there should be one packet to relay back and forth (whoami)
// TODO: how do I find the packet that was previously sent by the smart contract?
// Coordinator.RecvPacket requires channeltypes.Packet as input?
// Given the source (portID, channelID), we should be able to count how many packets are pending, query the data
// and submit them to the other side (same with acks). This is what the real relayer does. I guess the test framework doesn't?
// Update: I dug through the code, especially channel.Keeper.SendPacket, and it only writes a commitment
// only writes I see: https://github.com/cosmos/cosmos-sdk/blob/31fdee0228bd6f3e787489c8e4434aabc8facb7d/x/ibc/core/04-channel/keeper/packet.go#L115-L116
// commitment is hashed packet: https://github.com/cosmos/cosmos-sdk/blob/31fdee0228bd6f3e787489c8e4434aabc8facb7d/x/ibc/core/04-channel/types/packet.go#L14-L34
// how is the relayer supposed to get the original packet data??
// eg. ibctransfer doesn't store the packet either: https://github.com/cosmos/cosmos-sdk/blob/master/x/ibc/applications/transfer/keeper/relay.go#L145-L162
// ... or I guess the original packet data is only available in the event logs????
// https://github.com/cosmos/cosmos-sdk/blob/31fdee0228bd6f3e787489c8e4434aabc8facb7d/x/ibc/core/04-channel/keeper/packet.go#L121-L132
// ensure the expected packet was prepared, and relay it
require.Equal(t, 1, len(chainA.PendingSendPackets))
require.Equal(t, 0, len(chainB.PendingSendPackets))
err := coordinator.RelayAndAckPendingPackets(path)
require.NoError(t, err)
require.Equal(t, 0, len(chainA.PendingSendPackets))
require.Equal(t, 0, len(chainB.PendingSendPackets))
// let's query the source contract and make sure it registered an address
query := ReflectSendQueryMsg{Account: &AccountQuery{ChannelID: path.EndpointA.ChannelID}}
var account AccountResponse
err = chainA.SmartQuery(sendContractAddr.String(), query, &account)
require.NoError(t, err)
require.NotEmpty(t, account.RemoteAddr)
require.Empty(t, account.RemoteBalance)
// close channel
coordinator.CloseChannel(path)
// let's query the source contract and make sure it registered an address
account = AccountResponse{}
err = chainA.SmartQuery(sendContractAddr.String(), query, &account)
require.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
type ReflectSendQueryMsg struct {
Admin *struct{} `json:"admin,omitempty"`
ListAccounts *struct{} `json:"list_accounts,omitempty"`
Account *AccountQuery `json:"account,omitempty"`
}
type AccountQuery struct {
ChannelID string `json:"channel_id"`
}
type AccountResponse struct {
LastUpdateTime uint64 `json:"last_update_time,string"`
RemoteAddr string `json:"remote_addr"`
RemoteBalance wasmvmtypes.Coins `json:"remote_balance"`
}

82
x/wasm/ibc_test.go Normal file
View File

@ -0,0 +1,82 @@
package wasm
import (
"testing"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
"github.com/stretchr/testify/assert"
)
func TestMapToWasmVMIBCPacket(t *testing.T) {
var myTimestamp uint64 = 1
specs := map[string]struct {
src channeltypes.Packet
exp wasmvmtypes.IBCPacket
}{
"with height timeout": {
src: IBCPacketFixture(),
exp: wasmvmtypes.IBCPacket{
Data: []byte("myData"),
Src: wasmvmtypes.IBCEndpoint{PortID: "srcPort", ChannelID: "channel-1"},
Dest: wasmvmtypes.IBCEndpoint{PortID: "destPort", ChannelID: "channel-2"},
Sequence: 1,
Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{Height: 1, Revision: 2}},
},
},
"with time timeout": {
src: IBCPacketFixture(func(p *channeltypes.Packet) {
p.TimeoutTimestamp = myTimestamp
p.TimeoutHeight = clienttypes.Height{}
}),
exp: wasmvmtypes.IBCPacket{
Data: []byte("myData"),
Src: wasmvmtypes.IBCEndpoint{PortID: "srcPort", ChannelID: "channel-1"},
Dest: wasmvmtypes.IBCEndpoint{PortID: "destPort", ChannelID: "channel-2"},
Sequence: 1,
Timeout: wasmvmtypes.IBCTimeout{Timestamp: myTimestamp},
},
}, "with time and height timeout": {
src: IBCPacketFixture(func(p *channeltypes.Packet) {
p.TimeoutTimestamp = myTimestamp
}),
exp: wasmvmtypes.IBCPacket{
Data: []byte("myData"),
Src: wasmvmtypes.IBCEndpoint{PortID: "srcPort", ChannelID: "channel-1"},
Dest: wasmvmtypes.IBCEndpoint{PortID: "destPort", ChannelID: "channel-2"},
Sequence: 1,
Timeout: wasmvmtypes.IBCTimeout{
Block: &wasmvmtypes.IBCTimeoutBlock{Height: 1, Revision: 2},
Timestamp: myTimestamp,
},
},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
got := newIBCPacket(spec.src)
assert.Equal(t, spec.exp, got)
})
}
}
func IBCPacketFixture(mutators ...func(p *channeltypes.Packet)) channeltypes.Packet {
r := channeltypes.Packet{
Sequence: 1,
SourcePort: "srcPort",
SourceChannel: "channel-1",
DestinationPort: "destPort",
DestinationChannel: "channel-2",
Data: []byte("myData"),
TimeoutHeight: clienttypes.Height{
RevisionHeight: 1,
RevisionNumber: 2,
},
TimeoutTimestamp: 0,
}
for _, m := range mutators {
m(&r)
}
return r
}

View File

@ -0,0 +1,2 @@
# testing package for ibc
Customized version of cosmos-sdk x/ibc/testing

594
x/wasm/ibctesting/chain.go Normal file
View File

@ -0,0 +1,594 @@
package ibctesting
import (
"fmt"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
"github.com/cosmos/ibc-go/v4/modules/core/exported"
"github.com/cosmos/ibc-go/v4/modules/core/types"
ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
"github.com/cosmos/ibc-go/v4/testing/mock"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmprotoversion "github.com/tendermint/tendermint/proto/tendermint/version"
tmtypes "github.com/tendermint/tendermint/types"
tmversion "github.com/tendermint/tendermint/version"
"github.com/cerc-io/laconicd/app"
"github.com/cerc-io/laconicd/app/params"
"github.com/cerc-io/laconicd/x/wasm"
)
var MaxAccounts = 10
type SenderAccount struct {
SenderPrivKey cryptotypes.PrivKey
SenderAccount authtypes.AccountI
}
// TestChain is a testing struct that wraps a simapp with the last TM Header, the current ABCI
// header and the validators of the TestChain. It also contains a field called ChainID. This
// is the clientID that *other* chains use to refer to this TestChain. The SenderAccount
// is used for delivering transactions through the application state.
// NOTE: the actual application uses an empty chain-id for ease of testing.
type TestChain struct {
t *testing.T
Coordinator *Coordinator
App *app.WasmApp
ChainID string
LastHeader *ibctmtypes.Header // header for last block height committed
CurrentHeader tmproto.Header // header for current block height
QueryServer types.QueryServer
TxConfig client.TxConfig
Codec codec.BinaryCodec
Vals *tmtypes.ValidatorSet
NextVals *tmtypes.ValidatorSet
// Signers is a map from validator address to the PrivValidator
// The map is converted into an array that is the same order as the validators right before signing commit
// This ensures that signers will always be in correct order even as validator powers change.
// If a test adds a new validator after chain creation, then the signer map must be updated to include
// the new PrivValidator entry.
Signers map[string]tmtypes.PrivValidator
// autogenerated sender private key
SenderPrivKey cryptotypes.PrivKey
SenderAccount authtypes.AccountI
SenderAccounts []SenderAccount
PendingSendPackets []channeltypes.Packet
}
type PacketAck struct {
Packet channeltypes.Packet
Ack []byte
}
// NewTestChain initializes a new test chain with a default of 4 validators
// Use this function if the tests do not need custom control over the validator set
func NewTestChain(t *testing.T, coord *Coordinator, chainID string, opts ...wasm.Option) *TestChain {
// generate validators private/public key
var (
validatorsPerChain = 4
validators = make([]*tmtypes.Validator, 0, validatorsPerChain)
signersByAddress = make(map[string]tmtypes.PrivValidator, validatorsPerChain)
)
for i := 0; i < validatorsPerChain; i++ {
privVal := mock.NewPV()
pubKey, err := privVal.GetPubKey()
require.NoError(t, err)
validators = append(validators, tmtypes.NewValidator(pubKey, 1))
signersByAddress[pubKey.Address().String()] = privVal
}
// construct validator set;
// Note that the validators are sorted by voting power
// or, if equal, by address lexical order
valSet := tmtypes.NewValidatorSet(validators)
return NewTestChainWithValSet(t, coord, chainID, valSet, signersByAddress, opts...)
}
// NewTestChainWithValSet initializes a new TestChain instance with the given validator set
// and signer array. It also initializes 10 Sender accounts with a balance of 10000000000000000000 coins of
// bond denom to use for tests.
//
// The first block height is committed to state in order to allow for client creations on
// counterparty chains. The TestChain will return with a block height starting at 2.
//
// Time management is handled by the Coordinator in order to ensure synchrony between chains.
// Each update of any chain increments the block header time for all chains by 5 seconds.
//
// NOTE: to use a custom sender privkey and account for testing purposes, replace and modify this
// constructor function.
//
// CONTRACT: Validator array must be provided in the order expected by Tendermint.
// i.e. sorted first by power and then lexicographically by address.
func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator, opts ...wasm.Option) *TestChain {
genAccs := []authtypes.GenesisAccount{}
genBals := []banktypes.Balance{}
senderAccs := []SenderAccount{}
// generate genesis accounts
for i := 0; i < MaxAccounts; i++ {
senderPrivKey := secp256k1.GenPrivKey()
acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), uint64(i), 0)
amount, ok := sdk.NewIntFromString("10000000000000000000")
require.True(t, ok)
// add sender account
balance := banktypes.Balance{
Address: acc.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)),
}
genAccs = append(genAccs, acc)
genBals = append(genBals, balance)
senderAcc := SenderAccount{
SenderAccount: acc,
SenderPrivKey: senderPrivKey,
}
senderAccs = append(senderAccs, senderAcc)
}
wasmApp := app.SetupWithGenesisValSet(t, valSet, genAccs, chainID, opts, genBals...)
// create current header and call begin block
header := tmproto.Header{
ChainID: chainID,
Height: 1,
Time: coord.CurrentTime.UTC(),
}
txConfig := params.MakeEncodingConfig().TxConfig
// create an account to send transactions from
chain := &TestChain{
t: t,
Coordinator: coord,
ChainID: chainID,
App: wasmApp,
CurrentHeader: header,
QueryServer: wasmApp.IBCKeeper,
TxConfig: txConfig,
Codec: wasmApp.AppCodec(),
Vals: valSet,
NextVals: valSet,
Signers: signers,
SenderPrivKey: senderAccs[0].SenderPrivKey,
SenderAccount: senderAccs[0].SenderAccount,
SenderAccounts: senderAccs,
}
coord.CommitBlock(chain)
return chain
}
// GetContext returns the current context for the application.
func (chain *TestChain) GetContext() sdk.Context {
return chain.App.BaseApp.NewContext(false, chain.CurrentHeader)
}
// QueryProof performs an abci query with the given key and returns the proto encoded merkle proof
// for the query and the height at which the proof will succeed on a tendermint verifier.
func (chain *TestChain) QueryProof(key []byte) ([]byte, clienttypes.Height) {
return chain.QueryProofAtHeight(key, chain.App.LastBlockHeight())
}
// QueryProofAtHeight performs an abci query with the given key and returns the proto encoded merkle proof
// for the query and the height at which the proof will succeed on a tendermint verifier.
func (chain *TestChain) QueryProofAtHeight(key []byte, height int64) ([]byte, clienttypes.Height) {
res := chain.App.Query(abci.RequestQuery{
Path: fmt.Sprintf("store/%s/key", host.StoreKey),
Height: height - 1,
Data: key,
Prove: true,
})
merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps)
require.NoError(chain.t, err)
proof, err := chain.App.AppCodec().Marshal(&merkleProof)
require.NoError(chain.t, err)
revision := clienttypes.ParseChainID(chain.ChainID)
// proof height + 1 is returned as the proof created corresponds to the height the proof
// was created in the IAVL tree. Tendermint and subsequently the clients that rely on it
// have heights 1 above the IAVL tree. Thus we return proof height + 1
return proof, clienttypes.NewHeight(revision, uint64(res.Height)+1)
}
// QueryUpgradeProof performs an abci query with the given key and returns the proto encoded merkle proof
// for the query and the height at which the proof will succeed on a tendermint verifier.
func (chain *TestChain) QueryUpgradeProof(key []byte, height uint64) ([]byte, clienttypes.Height) {
res := chain.App.Query(abci.RequestQuery{
Path: "store/upgrade/key",
Height: int64(height - 1),
Data: key,
Prove: true,
})
merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps)
require.NoError(chain.t, err)
proof, err := chain.App.AppCodec().Marshal(&merkleProof)
require.NoError(chain.t, err)
revision := clienttypes.ParseChainID(chain.ChainID)
// proof height + 1 is returned as the proof created corresponds to the height the proof
// was created in the IAVL tree. Tendermint and subsequently the clients that rely on it
// have heights 1 above the IAVL tree. Thus we return proof height + 1
return proof, clienttypes.NewHeight(revision, uint64(res.Height+1))
}
// QueryConsensusStateProof performs an abci query for a consensus state
// stored on the given clientID. The proof and consensusHeight are returned.
func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, clienttypes.Height) {
clientState := chain.GetClientState(clientID)
consensusHeight := clientState.GetLatestHeight().(clienttypes.Height)
consensusKey := host.FullConsensusStateKey(clientID, consensusHeight)
proofConsensus, _ := chain.QueryProof(consensusKey)
return proofConsensus, consensusHeight
}
// NextBlock sets the last header to the current header and increments the current header to be
// at the next block height. It does not update the time as that is handled by the Coordinator.
// It will call Endblock and Commit and apply the validator set changes to the next validators
// of the next block being created. This follows the Tendermint protocol of applying valset changes
// returned on block `n` to the validators of block `n+2`.
// It calls BeginBlock with the new block created before returning.
func (chain *TestChain) NextBlock() {
res := chain.App.EndBlock(abci.RequestEndBlock{Height: chain.CurrentHeader.Height})
chain.App.Commit()
// set the last header to the current header
// use nil trusted fields
chain.LastHeader = chain.CurrentTMClientHeader()
// val set changes returned from previous block get applied to the next validators
// of this block. See tendermint spec for details.
chain.Vals = chain.NextVals
chain.NextVals = ibctesting.ApplyValSetChanges(chain.t, chain.Vals, res.ValidatorUpdates)
// increment the current header
chain.CurrentHeader = tmproto.Header{
ChainID: chain.ChainID,
Height: chain.App.LastBlockHeight() + 1,
AppHash: chain.App.LastCommitID().Hash,
// NOTE: the time is increased by the coordinator to maintain time synchrony amongst
// chains.
Time: chain.CurrentHeader.Time,
ValidatorsHash: chain.Vals.Hash(),
NextValidatorsHash: chain.NextVals.Hash(),
}
chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader})
}
// sendMsgs delivers a transaction through the application without returning the result.
func (chain *TestChain) sendMsgs(msgs ...sdk.Msg) error {
_, err := chain.SendMsgs(msgs...)
return err
}
// SendMsgs delivers a transaction through the application. It updates the senders sequence
// number and updates the TestChain's headers. It returns the result and error if one
// occurred.
func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) {
// ensure the chain has the latest time
chain.Coordinator.UpdateTimeForChain(chain)
_, r, err := app.SignAndDeliver(
chain.t,
chain.TxConfig,
chain.App.BaseApp,
chain.GetContext().BlockHeader(),
msgs,
chain.ChainID,
[]uint64{chain.SenderAccount.GetAccountNumber()},
[]uint64{chain.SenderAccount.GetSequence()},
chain.SenderPrivKey,
)
// NextBlock calls app.Commit()
chain.NextBlock()
if err != nil {
return r, err
}
// increment sequence for successful transaction execution
err = chain.SenderAccount.SetSequence(chain.SenderAccount.GetSequence() + 1)
if err != nil {
return nil, err
}
chain.Coordinator.IncrementTime()
chain.captureIBCEvents(r)
return r, nil
}
func (chain *TestChain) captureIBCEvents(r *sdk.Result) {
toSend := getSendPackets(r.Events)
if len(toSend) > 0 {
// Keep a queue on the chain that we can relay in tests
chain.PendingSendPackets = append(chain.PendingSendPackets, toSend...)
}
}
// GetClientState retrieves the client state for the provided clientID. The client is
// expected to exist otherwise testing will fail.
func (chain *TestChain) GetClientState(clientID string) exported.ClientState {
clientState, found := chain.App.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID)
require.True(chain.t, found)
return clientState
}
// GetConsensusState retrieves the consensus state for the provided clientID and height.
// It will return a success boolean depending on if consensus state exists or not.
func (chain *TestChain) GetConsensusState(clientID string, height exported.Height) (exported.ConsensusState, bool) {
return chain.App.IBCKeeper.ClientKeeper.GetClientConsensusState(chain.GetContext(), clientID, height)
}
// GetValsAtHeight will return the validator set of the chain at a given height. It will return
// a success boolean depending on if the validator set exists or not at that height.
func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bool) {
histInfo, ok := chain.App.StakingKeeper.GetHistoricalInfo(chain.GetContext(), height)
if !ok {
return nil, false
}
valSet := stakingtypes.Validators(histInfo.Valset)
tmValidators, err := teststaking.ToTmValidators(valSet, sdk.DefaultPowerReduction)
if err != nil {
panic(err)
}
return tmtypes.NewValidatorSet(tmValidators), true
}
// GetAcknowledgement retrieves an acknowledgement for the provided packet. If the
// acknowledgement does not exist then testing will fail.
func (chain *TestChain) GetAcknowledgement(packet exported.PacketI) []byte {
ack, found := chain.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(chain.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
require.True(chain.t, found)
return ack
}
// GetPrefix returns the prefix for used by a chain in connection creation
func (chain *TestChain) GetPrefix() commitmenttypes.MerklePrefix {
return commitmenttypes.NewMerklePrefix(chain.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes())
}
// ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the
// light client on the source chain.
func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctmtypes.Header, error) {
return chain.ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty, clientID, clienttypes.ZeroHeight())
}
// ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the
// light client on the source chain.
func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctmtypes.Header, error) {
header := counterparty.LastHeader
// Relayer must query for LatestHeight on client to get TrustedHeight if the trusted height is not set
if trustedHeight.IsZero() {
trustedHeight = chain.GetClientState(clientID).GetLatestHeight().(clienttypes.Height)
}
var (
tmTrustedVals *tmtypes.ValidatorSet
ok bool
)
// Once we get TrustedHeight from client, we must query the validators from the counterparty chain
// If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators
// If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo
if trustedHeight == counterparty.LastHeader.GetHeight() {
tmTrustedVals = counterparty.Vals
} else {
// NOTE: We need to get validators from counterparty at height: trustedHeight+1
// since the last trusted validators for a header at height h
// is the NextValidators at h+1 committed to in header h by
// NextValidatorsHash
tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1))
if !ok {
return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight)
}
}
// inject trusted fields into last header
// for now assume revision number is 0
header.TrustedHeight = trustedHeight
trustedVals, err := tmTrustedVals.ToProto()
if err != nil {
return nil, err
}
header.TrustedValidators = trustedVals
return header, nil
}
// ExpireClient fast forwards the chain's block time by the provided amount of time which will
// expire any clients with a trusting period less than or equal to this amount of time.
func (chain *TestChain) ExpireClient(amount time.Duration) {
chain.Coordinator.IncrementTimeBy(amount)
}
// CurrentTMClientHeader creates a TM header using the current header parameters
// on the chain. The trusted fields in the header are set to nil.
func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header {
return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, chain.NextVals, nil, chain.Signers)
}
// CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow
// caller flexibility to use params that differ from the chain.
func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctmtypes.Header {
var (
valSet *tmproto.ValidatorSet
trustedVals *tmproto.ValidatorSet
)
require.NotNil(chain.t, tmValSet)
vsetHash := tmValSet.Hash()
nextValHash := nextVals.Hash()
tmHeader := tmtypes.Header{
Version: tmprotoversion.Consensus{Block: tmversion.BlockProtocol, App: 2},
ChainID: chainID,
Height: blockHeight,
Time: timestamp,
LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)),
LastCommitHash: chain.App.LastCommitID().Hash,
DataHash: tmhash.Sum([]byte("data_hash")),
ValidatorsHash: vsetHash,
NextValidatorsHash: nextValHash,
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
AppHash: chain.CurrentHeader.AppHash,
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
ProposerAddress: tmValSet.Proposer.Address, //nolint:staticcheck
}
hhash := tmHeader.Hash()
blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set")))
voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet)
// MakeCommit expects a signer array in the same order as the validator array.
// Thus we iterate over the ordered validator set and construct a signer array
// from the signer map in the same order.
signerArr := make([]tmtypes.PrivValidator, len(tmValSet.Validators))
for i, v := range tmValSet.Validators {
signerArr[i] = signers[v.Address.String()]
}
commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signerArr, timestamp)
require.NoError(chain.t, err)
signedHeader := &tmproto.SignedHeader{
Header: tmHeader.ToProto(),
Commit: commit.ToProto(),
}
valSet, err = tmValSet.ToProto()
require.NoError(chain.t, err)
if tmTrustedVals != nil {
trustedVals, err = tmTrustedVals.ToProto()
require.NoError(chain.t, err)
}
// The trusted fields may be nil. They may be filled before relaying messages to a client.
// The relayer is responsible for querying client and injecting appropriate trusted fields.
return &ibctmtypes.Header{
SignedHeader: signedHeader,
ValidatorSet: valSet,
TrustedHeight: trustedHeight,
TrustedValidators: trustedVals,
}
}
// MakeBlockID copied unimported test functions from tmtypes to use them here
func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.BlockID {
return tmtypes.BlockID{
Hash: hash,
PartSetHeader: tmtypes.PartSetHeader{
Total: partSetSize,
Hash: partSetHash,
},
}
}
// CreatePortCapability binds and claims a capability for the given portID if it does not
// already exist. This function will fail testing on any resulting error.
// NOTE: only creation of a capability for a transfer or mock port is supported
// Other applications must bind to the port in InitGenesis or modify this code.
func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.ScopedKeeper, portID string) {
// check if the portId is already binded, if not bind it
_, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID))
if !ok {
// create capability using the IBC capability keeper
cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), host.PortPath(portID))
require.NoError(chain.t, err)
// claim capability using the scopedKeeper
err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID))
require.NoError(chain.t, err)
}
chain.NextBlock()
}
// GetPortCapability returns the port capability for the given portID. The capability must
// exist, otherwise testing will fail.
func (chain *TestChain) GetPortCapability(portID string) *capabilitytypes.Capability {
cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID))
require.True(chain.t, ok)
return cap
}
// CreateChannelCapability binds and claims a capability for the given portID and channelID
// if it does not already exist. This function will fail testing on any resulting error. The
// scoped keeper passed in will claim the new capability.
func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.ScopedKeeper, portID, channelID string) {
capName := host.ChannelCapabilityPath(portID, channelID)
// check if the portId is already binded, if not bind it
_, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), capName)
if !ok {
cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), capName)
require.NoError(chain.t, err)
err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, capName)
require.NoError(chain.t, err)
}
chain.NextBlock()
}
// GetChannelCapability returns the channel capability for the given portID and channelID.
// The capability must exist, otherwise testing will fail.
func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabilitytypes.Capability {
cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.ChannelCapabilityPath(portID, channelID))
require.True(chain.t, ok)
return cap
}
func (chain *TestChain) Balance(acc sdk.AccAddress, denom string) sdk.Coin {
return chain.App.BankKeeper.GetBalance(chain.GetContext(), acc, denom)
}
func (chain *TestChain) AllBalances(acc sdk.AccAddress) sdk.Coins {
return chain.App.BankKeeper.GetAllBalances(chain.GetContext(), acc)
}

View File

@ -0,0 +1,317 @@
package ibctesting
import (
"fmt"
"strconv"
"testing"
"time"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
wasmkeeper "github.com/cerc-io/laconicd/x/wasm/keeper"
)
const ChainIDPrefix = "testchain"
var (
globalStartTime = time.Date(2020, 12, 4, 10, 30, 0, 0, time.UTC)
TimeIncrement = time.Second * 5
)
// Coordinator is a testing struct which contains N TestChain's. It handles keeping all chains
// in sync with regards to time.
type Coordinator struct {
t *testing.T
CurrentTime time.Time
Chains map[string]*TestChain
}
// NewCoordinator initializes Coordinator with N TestChain's
func NewCoordinator(t *testing.T, n int, opts ...[]wasmkeeper.Option) *Coordinator {
chains := make(map[string]*TestChain)
coord := &Coordinator{
t: t,
CurrentTime: globalStartTime,
}
for i := 0; i < n; i++ {
chainID := GetChainID(i)
var x []wasmkeeper.Option
if len(opts) > i {
x = opts[i]
}
chains[chainID] = NewTestChain(t, coord, chainID, x...)
}
coord.Chains = chains
return coord
}
// IncrementTime iterates through all the TestChain's and increments their current header time
// by 5 seconds.
//
// CONTRACT: this function must be called after every Commit on any TestChain.
func (coord *Coordinator) IncrementTime() {
coord.IncrementTimeBy(TimeIncrement)
}
// IncrementTimeBy iterates through all the TestChain's and increments their current header time
// by specified time.
func (coord *Coordinator) IncrementTimeBy(increment time.Duration) {
coord.CurrentTime = coord.CurrentTime.Add(increment).UTC()
coord.UpdateTime()
}
// UpdateTime updates all clocks for the TestChains to the current global time.
func (coord *Coordinator) UpdateTime() {
for _, chain := range coord.Chains {
coord.UpdateTimeForChain(chain)
}
}
// UpdateTimeForChain updates the clock for a specific chain.
func (coord *Coordinator) UpdateTimeForChain(chain *TestChain) {
chain.CurrentHeader.Time = coord.CurrentTime.UTC()
chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader})
}
// Setup constructs a TM client, connection, and channel on both chains provided. It will
// fail if any error occurs. The clientID's, TestConnections, and TestChannels are returned
// for both chains. The channels created are connected to the ibc-transfer application.
func (coord *Coordinator) Setup(path *Path) {
coord.SetupConnections(path)
// channels can also be referenced through the returned connections
coord.CreateChannels(path)
}
// SetupClients is a helper function to create clients on both chains. It assumes the
// caller does not anticipate any errors.
func (coord *Coordinator) SetupClients(path *Path) {
err := path.EndpointA.CreateClient()
require.NoError(coord.t, err)
err = path.EndpointB.CreateClient()
require.NoError(coord.t, err)
}
// SetupClientConnections is a helper function to create clients and the appropriate
// connections on both the source and counterparty chain. It assumes the caller does not
// anticipate any errors.
func (coord *Coordinator) SetupConnections(path *Path) {
coord.SetupClients(path)
coord.CreateConnections(path)
}
// CreateConnection constructs and executes connection handshake messages in order to create
// OPEN channels on chainA and chainB. The connection information of for chainA and chainB
// are returned within a TestConnection struct. The function expects the connections to be
// successfully opened otherwise testing will fail.
func (coord *Coordinator) CreateConnections(path *Path) {
err := path.EndpointA.ConnOpenInit()
require.NoError(coord.t, err)
err = path.EndpointB.ConnOpenTry()
require.NoError(coord.t, err)
err = path.EndpointA.ConnOpenAck()
require.NoError(coord.t, err)
err = path.EndpointB.ConnOpenConfirm()
require.NoError(coord.t, err)
// ensure counterparty is up to date
err = path.EndpointA.UpdateClient()
require.NoError(coord.t, err)
}
// CreateMockChannels constructs and executes channel handshake messages to create OPEN
// channels that use a mock application module that returns nil on all callbacks. This
// function is expects the channels to be successfully opened otherwise testing will
// fail.
func (coord *Coordinator) CreateMockChannels(path *Path) {
path.EndpointA.ChannelConfig.PortID = ibctesting.MockPort
path.EndpointB.ChannelConfig.PortID = ibctesting.MockPort
coord.CreateChannels(path)
}
// CreateTransferChannels constructs and executes channel handshake messages to create OPEN
// ibc-transfer channels on chainA and chainB. The function expects the channels to be
// successfully opened otherwise testing will fail.
func (coord *Coordinator) CreateTransferChannels(path *Path) {
path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort
path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort
coord.CreateChannels(path)
}
// CreateChannel constructs and executes channel handshake messages in order to create
// OPEN channels on chainA and chainB. The function expects the channels to be successfully
// opened otherwise testing will fail.
func (coord *Coordinator) CreateChannels(path *Path) {
err := path.EndpointA.ChanOpenInit()
require.NoError(coord.t, err)
err = path.EndpointB.ChanOpenTry()
require.NoError(coord.t, err)
err = path.EndpointA.ChanOpenAck()
require.NoError(coord.t, err)
err = path.EndpointB.ChanOpenConfirm()
require.NoError(coord.t, err)
// ensure counterparty is up to date
err = path.EndpointA.UpdateClient()
require.NoError(coord.t, err)
}
// GetChain returns the TestChain using the given chainID and returns an error if it does
// not exist.
func (coord *Coordinator) GetChain(chainID string) *TestChain {
chain, found := coord.Chains[chainID]
require.True(coord.t, found, fmt.Sprintf("%s chain does not exist", chainID))
return chain
}
// GetChainID returns the chainID used for the provided index.
func GetChainID(index int) string {
return ChainIDPrefix + strconv.Itoa(index)
}
// CommitBlock commits a block on the provided indexes and then increments the global time.
//
// CONTRACT: the passed in list of indexes must not contain duplicates
func (coord *Coordinator) CommitBlock(chains ...*TestChain) {
for _, chain := range chains {
chain.NextBlock()
}
coord.IncrementTime()
}
// CommitNBlocks commits n blocks to state and updates the block height by 1 for each commit.
func (coord *Coordinator) CommitNBlocks(chain *TestChain, n uint64) {
for i := uint64(0); i < n; i++ {
chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader})
chain.NextBlock()
coord.IncrementTime()
}
}
// ConnOpenInitOnBothChains initializes a connection on both endpoints with the state INIT
// using the OpenInit handshake call.
func (coord *Coordinator) ConnOpenInitOnBothChains(path *Path) error {
if err := path.EndpointA.ConnOpenInit(); err != nil {
return err
}
if err := path.EndpointB.ConnOpenInit(); err != nil {
return err
}
if err := path.EndpointA.UpdateClient(); err != nil {
return err
}
if err := path.EndpointB.UpdateClient(); err != nil {
return err
}
return nil
}
// ChanOpenInitOnBothChains initializes a channel on the source chain and counterparty chain
// with the state INIT using the OpenInit handshake call.
func (coord *Coordinator) ChanOpenInitOnBothChains(path *Path) error {
// NOTE: only creation of a capability for a transfer or mock port is supported
// Other applications must bind to the port in InitGenesis or modify this code.
if err := path.EndpointA.ChanOpenInit(); err != nil {
return err
}
if err := path.EndpointB.ChanOpenInit(); err != nil {
return err
}
if err := path.EndpointA.UpdateClient(); err != nil {
return err
}
if err := path.EndpointB.UpdateClient(); err != nil {
return err
}
return nil
}
// RelayAndAckPendingPackets sends pending packages from path.EndpointA to the counterparty chain and acks
func (coord *Coordinator) RelayAndAckPendingPackets(path *Path) error {
// get all the packet to relay src->dest
src := path.EndpointA
coord.t.Logf("Relay: %d Packets A->B, %d Packets B->A\n", len(src.Chain.PendingSendPackets), len(path.EndpointB.Chain.PendingSendPackets))
for i, v := range src.Chain.PendingSendPackets {
err := path.RelayPacket(v, nil)
if err != nil {
return err
}
src.Chain.PendingSendPackets = append(src.Chain.PendingSendPackets[0:i], src.Chain.PendingSendPackets[i+1:]...)
}
src = path.EndpointB
for i, v := range src.Chain.PendingSendPackets {
err := path.RelayPacket(v, nil)
if err != nil {
return err
}
src.Chain.PendingSendPackets = append(src.Chain.PendingSendPackets[0:i], src.Chain.PendingSendPackets[i+1:]...)
}
return nil
}
// TimeoutPendingPackets returns the package to source chain to let the IBC app revert any operation.
// from A to A
func (coord *Coordinator) TimeoutPendingPackets(path *Path) error {
src := path.EndpointA
dest := path.EndpointB
toSend := src.Chain.PendingSendPackets
coord.t.Logf("Timeout %d Packets A->A\n", len(toSend))
if err := src.UpdateClient(); err != nil {
return err
}
// Increment time and commit block so that 5 second delay period passes between send and receive
coord.IncrementTime()
coord.CommitBlock(src.Chain, dest.Chain)
for _, packet := range toSend {
// get proof of packet unreceived on dest
packetKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proofUnreceived, proofHeight := dest.QueryProof(packetKey)
timeoutMsg := channeltypes.NewMsgTimeout(packet, packet.Sequence, proofUnreceived, proofHeight, src.Chain.SenderAccount.GetAddress().String())
err := src.Chain.sendMsgs(timeoutMsg)
if err != nil {
return err
}
}
src.Chain.PendingSendPackets = nil
return nil
}
// CloseChannel close channel on both sides
func (coord *Coordinator) CloseChannel(path *Path) {
err := path.EndpointA.ChanCloseInit()
require.NoError(coord.t, err)
coord.IncrementTime()
err = path.EndpointB.UpdateClient()
require.NoError(coord.t, err)
err = path.EndpointB.ChanCloseConfirm()
require.NoError(coord.t, err)
}

View File

@ -0,0 +1,597 @@
package ibctesting
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
"github.com/cosmos/ibc-go/v4/modules/core/exported"
ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
"github.com/stretchr/testify/require"
)
// Endpoint is a which represents a channel endpoint and its associated
// client and connections. It contains client, connection, and channel
// configuration parameters. Endpoint functions will utilize the parameters
// set in the configuration structs when executing IBC messages.
type Endpoint struct {
Chain *TestChain
Counterparty *Endpoint
ClientID string
ConnectionID string
ChannelID string
ClientConfig ibctesting.ClientConfig
ConnectionConfig *ibctesting.ConnectionConfig
ChannelConfig *ibctesting.ChannelConfig
}
// NewEndpoint constructs a new endpoint without the counterparty.
// CONTRACT: the counterparty endpoint must be set by the caller.
func NewEndpoint(
chain *TestChain, clientConfig ibctesting.ClientConfig,
connectionConfig *ibctesting.ConnectionConfig, channelConfig *ibctesting.ChannelConfig,
) *Endpoint {
return &Endpoint{
Chain: chain,
ClientConfig: clientConfig,
ConnectionConfig: connectionConfig,
ChannelConfig: channelConfig,
}
}
// NewDefaultEndpoint constructs a new endpoint using default values.
// CONTRACT: the counterparty endpoitn must be set by the caller.
func NewDefaultEndpoint(chain *TestChain) *Endpoint {
return &Endpoint{
Chain: chain,
ClientConfig: ibctesting.NewTendermintConfig(),
ConnectionConfig: ibctesting.NewConnectionConfig(),
ChannelConfig: ibctesting.NewChannelConfig(),
}
}
// QueryProof queries proof associated with this endpoint using the lastest client state
// height on the counterparty chain.
func (endpoint *Endpoint) QueryProof(key []byte) ([]byte, clienttypes.Height) {
// obtain the counterparty client representing the chain associated with the endpoint
clientState := endpoint.Counterparty.Chain.GetClientState(endpoint.Counterparty.ClientID)
// query proof on the counterparty using the latest height of the IBC client
return endpoint.QueryProofAtHeight(key, clientState.GetLatestHeight().GetRevisionHeight())
}
// QueryProofAtHeight queries proof associated with this endpoint using the proof height
// provided
func (endpoint *Endpoint) QueryProofAtHeight(key []byte, height uint64) ([]byte, clienttypes.Height) {
// query proof on the counterparty using the latest height of the IBC client
return endpoint.Chain.QueryProofAtHeight(key, int64(height))
}
// CreateClient creates an IBC client on the endpoint. It will update the
// clientID for the endpoint if the message is successfully executed.
// NOTE: a solo machine client will be created with an empty diversifier.
func (endpoint *Endpoint) CreateClient() (err error) {
// ensure counterparty has committed state
endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain)
var (
clientState exported.ClientState
consensusState exported.ConsensusState
)
switch endpoint.ClientConfig.GetClientType() {
case exported.Tendermint:
tmConfig, ok := endpoint.ClientConfig.(*ibctesting.TendermintConfig)
require.True(endpoint.Chain.t, ok)
height := endpoint.Counterparty.Chain.LastHeader.GetHeight().(clienttypes.Height)
clientState = ibctmtypes.NewClientState(
endpoint.Counterparty.Chain.ChainID, tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift,
height, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, tmConfig.AllowUpdateAfterExpiry, tmConfig.AllowUpdateAfterMisbehaviour,
)
consensusState = endpoint.Counterparty.Chain.LastHeader.ConsensusState()
case exported.Solomachine:
// TODO
// solo := NewSolomachine(chain.t, endpoint.Chain.Codec, clientID, "", 1)
// clientState = solo.ClientState()
// consensusState = solo.ConsensusState()
default:
err = fmt.Errorf("client type %s is not supported", endpoint.ClientConfig.GetClientType())
}
if err != nil {
return err
}
msg, err := clienttypes.NewMsgCreateClient(
clientState, consensusState, endpoint.Chain.SenderAccount.GetAddress().String(),
)
require.NoError(endpoint.Chain.t, err)
res, err := endpoint.Chain.SendMsgs(msg)
if err != nil {
return err
}
endpoint.ClientID, err = ibctesting.ParseClientIDFromEvents(res.GetEvents())
require.NoError(endpoint.Chain.t, err)
return nil
}
// UpdateClient updates the IBC client associated with the endpoint.
func (endpoint *Endpoint) UpdateClient() (err error) {
// ensure counterparty has committed state
endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain)
var header exported.Header
switch endpoint.ClientConfig.GetClientType() {
case exported.Tendermint:
header, err = endpoint.Chain.ConstructUpdateTMClientHeader(endpoint.Counterparty.Chain, endpoint.ClientID)
default:
err = fmt.Errorf("client type %s is not supported", endpoint.ClientConfig.GetClientType())
}
if err != nil {
return err
}
msg, err := clienttypes.NewMsgUpdateClient(
endpoint.ClientID, header,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
require.NoError(endpoint.Chain.t, err)
return endpoint.Chain.sendMsgs(msg)
}
// ConnOpenInit will construct and execute a MsgConnectionOpenInit on the associated endpoint.
func (endpoint *Endpoint) ConnOpenInit() error {
msg := connectiontypes.NewMsgConnectionOpenInit(
endpoint.ClientID,
endpoint.Counterparty.ClientID,
endpoint.Counterparty.Chain.GetPrefix(), ibctesting.DefaultOpenInitVersion, endpoint.ConnectionConfig.DelayPeriod,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
res, err := endpoint.Chain.SendMsgs(msg)
if err != nil {
return err
}
endpoint.ConnectionID, err = ibctesting.ParseConnectionIDFromEvents(res.GetEvents())
require.NoError(endpoint.Chain.t, err)
return nil
}
// ConnOpenTry will construct and execute a MsgConnectionOpenTry on the associated endpoint.
func (endpoint *Endpoint) ConnOpenTry() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
counterpartyClient, proofClient, proofConsensus, consensusHeight, proofInit, proofHeight := endpoint.QueryConnectionHandshakeProof()
msg := connectiontypes.NewMsgConnectionOpenTry(
endpoint.ClientID, endpoint.Counterparty.ConnectionID, endpoint.Counterparty.ClientID,
counterpartyClient, endpoint.Counterparty.Chain.GetPrefix(), []*connectiontypes.Version{ibctesting.ConnectionVersion}, endpoint.ConnectionConfig.DelayPeriod,
proofInit, proofClient, proofConsensus,
proofHeight, consensusHeight,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
res, err := endpoint.Chain.SendMsgs(msg)
if err != nil {
return err
}
if endpoint.ConnectionID == "" {
endpoint.ConnectionID, err = ibctesting.ParseConnectionIDFromEvents(res.GetEvents())
require.NoError(endpoint.Chain.t, err)
}
return nil
}
// ConnOpenAck will construct and execute a MsgConnectionOpenAck on the associated endpoint.
func (endpoint *Endpoint) ConnOpenAck() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
counterpartyClient, proofClient, proofConsensus, consensusHeight, proofTry, proofHeight := endpoint.QueryConnectionHandshakeProof()
msg := connectiontypes.NewMsgConnectionOpenAck(
endpoint.ConnectionID, endpoint.Counterparty.ConnectionID, counterpartyClient, // testing doesn't use flexible selection
proofTry, proofClient, proofConsensus,
proofHeight, consensusHeight,
ibctesting.ConnectionVersion,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(msg)
}
// ConnOpenConfirm will construct and execute a MsgConnectionOpenConfirm on the associated endpoint.
func (endpoint *Endpoint) ConnOpenConfirm() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
connectionKey := host.ConnectionKey(endpoint.Counterparty.ConnectionID)
proof, height := endpoint.Counterparty.Chain.QueryProof(connectionKey)
msg := connectiontypes.NewMsgConnectionOpenConfirm(
endpoint.ConnectionID,
proof, height,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(msg)
}
// QueryConnectionHandshakeProof returns all the proofs necessary to execute OpenTry or Open Ack of
// the connection handshakes. It returns the counterparty client state, proof of the counterparty
// client state, proof of the counterparty consensus state, the consensus state height, proof of
// the counterparty connection, and the proof height for all the proofs returned.
func (endpoint *Endpoint) QueryConnectionHandshakeProof() (
clientState exported.ClientState, proofClient,
proofConsensus []byte, consensusHeight clienttypes.Height,
proofConnection []byte, proofHeight clienttypes.Height,
) {
// obtain the client state on the counterparty chain
clientState = endpoint.Counterparty.Chain.GetClientState(endpoint.Counterparty.ClientID)
// query proof for the client state on the counterparty
clientKey := host.FullClientStateKey(endpoint.Counterparty.ClientID)
proofClient, proofHeight = endpoint.Counterparty.QueryProof(clientKey)
consensusHeight = clientState.GetLatestHeight().(clienttypes.Height)
// query proof for the consensus state on the counterparty
consensusKey := host.FullConsensusStateKey(endpoint.Counterparty.ClientID, consensusHeight)
proofConsensus, _ = endpoint.Counterparty.QueryProofAtHeight(consensusKey, proofHeight.GetRevisionHeight())
// query proof for the connection on the counterparty
connectionKey := host.ConnectionKey(endpoint.Counterparty.ConnectionID)
proofConnection, _ = endpoint.Counterparty.QueryProofAtHeight(connectionKey, proofHeight.GetRevisionHeight())
return
}
// ChanOpenInit will construct and execute a MsgChannelOpenInit on the associated endpoint.
func (endpoint *Endpoint) ChanOpenInit() error {
msg := channeltypes.NewMsgChannelOpenInit(
endpoint.ChannelConfig.PortID,
endpoint.ChannelConfig.Version, endpoint.ChannelConfig.Order, []string{endpoint.ConnectionID},
endpoint.Counterparty.ChannelConfig.PortID,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
res, err := endpoint.Chain.SendMsgs(msg)
if err != nil {
return err
}
endpoint.ChannelID, err = ibctesting.ParseChannelIDFromEvents(res.GetEvents())
require.NoError(endpoint.Chain.t, err)
// update version to selected app version
// NOTE: this update must be performed after SendMsgs()
endpoint.ChannelConfig.Version = endpoint.GetChannel().Version
return nil
}
// ChanOpenTry will construct and execute a MsgChannelOpenTry on the associated endpoint.
func (endpoint *Endpoint) ChanOpenTry() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID)
proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey)
msg := channeltypes.NewMsgChannelOpenTry(
endpoint.ChannelConfig.PortID,
endpoint.ChannelConfig.Version, endpoint.ChannelConfig.Order, []string{endpoint.ConnectionID},
endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID, endpoint.Counterparty.ChannelConfig.Version,
proof, height,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
res, err := endpoint.Chain.SendMsgs(msg)
if err != nil {
return err
}
if endpoint.ChannelID == "" {
endpoint.ChannelID, err = ibctesting.ParseChannelIDFromEvents(res.GetEvents())
require.NoError(endpoint.Chain.t, err)
}
// update version to selected app version
// NOTE: this update must be performed after the endpoint channelID is set
endpoint.ChannelConfig.Version = endpoint.GetChannel().Version
return nil
}
// ChanOpenAck will construct and execute a MsgChannelOpenAck on the associated endpoint.
func (endpoint *Endpoint) ChanOpenAck() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID)
proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey)
msg := channeltypes.NewMsgChannelOpenAck(
endpoint.ChannelConfig.PortID, endpoint.ChannelID,
endpoint.Counterparty.ChannelID, endpoint.Counterparty.ChannelConfig.Version, // testing doesn't use flexible selection
proof, height,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
if err := endpoint.Chain.sendMsgs(msg); err != nil {
return err
}
endpoint.ChannelConfig.Version = endpoint.GetChannel().Version
return nil
}
// ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated endpoint.
func (endpoint *Endpoint) ChanOpenConfirm() error {
if err := endpoint.UpdateClient(); err != nil {
return err
}
channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID)
proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey)
msg := channeltypes.NewMsgChannelOpenConfirm(
endpoint.ChannelConfig.PortID, endpoint.ChannelID,
proof, height,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(msg)
}
// ChanCloseInit will construct and execute a MsgChannelCloseInit on the associated endpoint.
//
// NOTE: does not work with ibc-transfer module
func (endpoint *Endpoint) ChanCloseInit() error {
msg := channeltypes.NewMsgChannelCloseInit(
endpoint.ChannelConfig.PortID, endpoint.ChannelID,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(msg)
}
// ChanCloseConfirm will construct and execute a NewMsgChannelCloseConfirm on the associated endpoint.
func (endpoint *Endpoint) ChanCloseConfirm() error {
channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID)
proof, proofHeight := endpoint.Counterparty.QueryProof(channelKey)
msg := channeltypes.NewMsgChannelCloseConfirm(
endpoint.ChannelConfig.PortID, endpoint.ChannelID,
proof, proofHeight,
endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(msg)
}
// SendPacket sends a packet through the channel keeper using the associated endpoint
// The counterparty client is updated so proofs can be sent to the counterparty chain.
func (endpoint *Endpoint) SendPacket(packet exported.PacketI) error {
channelCap := endpoint.Chain.GetChannelCapability(packet.GetSourcePort(), packet.GetSourceChannel())
// no need to send message, acting as a module
err := endpoint.Chain.App.IBCKeeper.ChannelKeeper.SendPacket(endpoint.Chain.GetContext(), channelCap, packet)
if err != nil {
return err
}
// commit changes since no message was sent
endpoint.Chain.Coordinator.CommitBlock(endpoint.Chain)
return endpoint.Counterparty.UpdateClient()
}
// RecvPacket receives a packet on the associated endpoint.
// The counterparty client is updated.
func (endpoint *Endpoint) RecvPacket(packet channeltypes.Packet) error {
_, err := endpoint.RecvPacketWithResult(packet)
if err != nil {
return err
}
return nil
}
// RecvPacketWithResult receives a packet on the associated endpoint and the result
// of the transaction is returned. The counterparty client is updated.
func (endpoint *Endpoint) RecvPacketWithResult(packet channeltypes.Packet) (*sdk.Result, error) {
// get proof of packet commitment on source
packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
proof, proofHeight := endpoint.Counterparty.Chain.QueryProof(packetKey)
recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, endpoint.Chain.SenderAccount.GetAddress().String())
// receive on counterparty and update source client
res, err := endpoint.Chain.SendMsgs(recvMsg)
if err != nil {
return nil, err
}
if err := endpoint.Counterparty.UpdateClient(); err != nil {
return nil, err
}
return res, nil
}
// WriteAcknowledgement writes an acknowledgement on the channel associated with the endpoint.
// The counterparty client is updated.
func (endpoint *Endpoint) WriteAcknowledgement(ack exported.Acknowledgement, packet exported.PacketI) error {
channelCap := endpoint.Chain.GetChannelCapability(packet.GetDestPort(), packet.GetDestChannel())
// no need to send message, acting as a handler
err := endpoint.Chain.App.IBCKeeper.ChannelKeeper.WriteAcknowledgement(endpoint.Chain.GetContext(), channelCap, packet, ack)
if err != nil {
return err
}
// commit changes since no message was sent
endpoint.Chain.Coordinator.CommitBlock(endpoint.Chain)
return endpoint.Counterparty.UpdateClient()
}
// AcknowledgePacket sends a MsgAcknowledgement to the channel associated with the endpoint.
func (endpoint *Endpoint) AcknowledgePacket(packet channeltypes.Packet, ack []byte) error {
// get proof of acknowledgement on counterparty
packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey)
ackMsg := channeltypes.NewMsgAcknowledgement(packet, ack, proof, proofHeight, endpoint.Chain.SenderAccount.GetAddress().String())
return endpoint.Chain.sendMsgs(ackMsg)
}
// TimeoutPacket sends a MsgTimeout to the channel associated with the endpoint.
func (endpoint *Endpoint) TimeoutPacket(packet channeltypes.Packet) error {
// get proof for timeout based on channel order
var packetKey []byte
switch endpoint.ChannelConfig.Order {
case channeltypes.ORDERED:
packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel())
case channeltypes.UNORDERED:
packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
default:
return fmt.Errorf("unsupported order type %s", endpoint.ChannelConfig.Order)
}
proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey)
nextSeqRecv, found := endpoint.Counterparty.Chain.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID)
require.True(endpoint.Chain.t, found)
timeoutMsg := channeltypes.NewMsgTimeout(
packet, nextSeqRecv,
proof, proofHeight, endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(timeoutMsg)
}
// TimeoutOnClose sends a MsgTimeoutOnClose to the channel associated with the endpoint.
func (endpoint *Endpoint) TimeoutOnClose(packet channeltypes.Packet) error {
// get proof for timeout based on channel order
var packetKey []byte
switch endpoint.ChannelConfig.Order {
case channeltypes.ORDERED:
packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel())
case channeltypes.UNORDERED:
packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
default:
return fmt.Errorf("unsupported order type %s", endpoint.ChannelConfig.Order)
}
proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey)
channelKey := host.ChannelKey(packet.GetDestPort(), packet.GetDestChannel())
proofClosed, _ := endpoint.Counterparty.QueryProof(channelKey)
nextSeqRecv, found := endpoint.Counterparty.Chain.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID)
require.True(endpoint.Chain.t, found)
timeoutOnCloseMsg := channeltypes.NewMsgTimeoutOnClose(
packet, nextSeqRecv,
proof, proofClosed, proofHeight, endpoint.Chain.SenderAccount.GetAddress().String(),
)
return endpoint.Chain.sendMsgs(timeoutOnCloseMsg)
}
// SetChannelClosed sets a channel state to CLOSED.
func (endpoint *Endpoint) SetChannelClosed() error {
channel := endpoint.GetChannel()
channel.State = channeltypes.CLOSED
endpoint.Chain.App.IBCKeeper.ChannelKeeper.SetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID, channel)
endpoint.Chain.Coordinator.CommitBlock(endpoint.Chain)
return endpoint.Counterparty.UpdateClient()
}
// GetClientState retrieves the Client State for this endpoint. The
// client state is expected to exist otherwise testing will fail.
func (endpoint *Endpoint) GetClientState() exported.ClientState {
return endpoint.Chain.GetClientState(endpoint.ClientID)
}
// SetClientState sets the client state for this endpoint.
func (endpoint *Endpoint) SetClientState(clientState exported.ClientState) {
endpoint.Chain.App.IBCKeeper.ClientKeeper.SetClientState(endpoint.Chain.GetContext(), endpoint.ClientID, clientState)
}
// GetConsensusState retrieves the Consensus State for this endpoint at the provided height.
// The consensus state is expected to exist otherwise testing will fail.
func (endpoint *Endpoint) GetConsensusState(height exported.Height) exported.ConsensusState {
consensusState, found := endpoint.Chain.GetConsensusState(endpoint.ClientID, height)
require.True(endpoint.Chain.t, found)
return consensusState
}
// SetConsensusState sets the consensus state for this endpoint.
func (endpoint *Endpoint) SetConsensusState(consensusState exported.ConsensusState, height exported.Height) {
endpoint.Chain.App.IBCKeeper.ClientKeeper.SetClientConsensusState(endpoint.Chain.GetContext(), endpoint.ClientID, height, consensusState)
}
// GetConnection retrieves an IBC Connection for the endpoint. The
// connection is expected to exist otherwise testing will fail.
func (endpoint *Endpoint) GetConnection() connectiontypes.ConnectionEnd {
connection, found := endpoint.Chain.App.IBCKeeper.ConnectionKeeper.GetConnection(endpoint.Chain.GetContext(), endpoint.ConnectionID)
require.True(endpoint.Chain.t, found)
return connection
}
// SetConnection sets the connection for this endpoint.
func (endpoint *Endpoint) SetConnection(connection connectiontypes.ConnectionEnd) {
endpoint.Chain.App.IBCKeeper.ConnectionKeeper.SetConnection(endpoint.Chain.GetContext(), endpoint.ConnectionID, connection)
}
// GetChannel retrieves an IBC Channel for the endpoint. The channel
// is expected to exist otherwise testing will fail.
func (endpoint *Endpoint) GetChannel() channeltypes.Channel {
channel, found := endpoint.Chain.App.IBCKeeper.ChannelKeeper.GetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID)
require.True(endpoint.Chain.t, found)
return channel
}
// SetChannel sets the channel for this endpoint.
func (endpoint *Endpoint) SetChannel(channel channeltypes.Channel) {
endpoint.Chain.App.IBCKeeper.ChannelKeeper.SetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID, channel)
}
// QueryClientStateProof performs and abci query for a client stat associated
// with this endpoint and returns the ClientState along with the proof.
func (endpoint *Endpoint) QueryClientStateProof() (exported.ClientState, []byte) {
// retrieve client state to provide proof for
clientState := endpoint.GetClientState()
clientKey := host.FullClientStateKey(endpoint.ClientID)
proofClient, _ := endpoint.QueryProof(clientKey)
return clientState, proofClient
}

View File

@ -0,0 +1,118 @@
package ibctesting
import (
"encoding/hex"
"fmt"
"strconv"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
abci "github.com/tendermint/tendermint/abci/types"
)
func getSendPackets(evts []abci.Event) []channeltypes.Packet {
var res []channeltypes.Packet
for _, evt := range evts {
if evt.Type == channeltypes.EventTypeSendPacket {
packet := parsePacketFromEvent(evt)
res = append(res, packet)
}
}
return res
}
// Used for various debug statements above when needed... do not remove
// func showEvent(evt abci.Event) {
// fmt.Printf("evt.Type: %s\n", evt.Type)
// for _, attr := range evt.Attributes {
// fmt.Printf(" %s = %s\n", string(attr.Key), string(attr.Value))
// }
//}
func parsePacketFromEvent(evt abci.Event) channeltypes.Packet {
return channeltypes.Packet{
Sequence: getUintField(evt, channeltypes.AttributeKeySequence),
SourcePort: getField(evt, channeltypes.AttributeKeySrcPort),
SourceChannel: getField(evt, channeltypes.AttributeKeySrcChannel),
DestinationPort: getField(evt, channeltypes.AttributeKeyDstPort),
DestinationChannel: getField(evt, channeltypes.AttributeKeyDstChannel),
Data: getHexField(evt, channeltypes.AttributeKeyDataHex),
TimeoutHeight: parseTimeoutHeight(getField(evt, channeltypes.AttributeKeyTimeoutHeight)),
TimeoutTimestamp: getUintField(evt, channeltypes.AttributeKeyTimeoutTimestamp),
}
}
func getHexField(evt abci.Event, key string) []byte {
got := getField(evt, key)
if got == "" {
return nil
}
bz, err := hex.DecodeString(got)
if err != nil {
panic(err)
}
return bz
}
// return the value for the attribute with the given name
func getField(evt abci.Event, key string) string {
for _, attr := range evt.Attributes {
if string(attr.Key) == key {
return string(attr.Value)
}
}
return ""
}
func getUintField(evt abci.Event, key string) uint64 {
raw := getField(evt, key)
return toUint64(raw)
}
func toUint64(raw string) uint64 {
if raw == "" {
return 0
}
i, err := strconv.ParseUint(raw, 10, 64)
if err != nil {
panic(err)
}
return i
}
func parseTimeoutHeight(raw string) clienttypes.Height {
chunks := strings.Split(raw, "-")
return clienttypes.Height{
RevisionNumber: toUint64(chunks[0]),
RevisionHeight: toUint64(chunks[1]),
}
}
func ParsePortIDFromEvents(events sdk.Events) (string, error) {
for _, ev := range events {
if ev.Type == channeltypes.EventTypeChannelOpenInit || ev.Type == channeltypes.EventTypeChannelOpenTry {
for _, attr := range ev.Attributes {
if string(attr.Key) == channeltypes.AttributeKeyPortID {
return string(attr.Value), nil
}
}
}
}
return "", fmt.Errorf("port id event attribute not found")
}
func ParseChannelVersionFromEvents(events sdk.Events) (string, error) {
for _, ev := range events {
if ev.Type == channeltypes.EventTypeChannelOpenInit || ev.Type == channeltypes.EventTypeChannelOpenTry {
for _, attr := range ev.Attributes {
if string(attr.Key) == channeltypes.AttributeVersion {
return string(attr.Value), nil
}
}
}
}
return "", fmt.Errorf("version event attribute not found")
}

View File

@ -0,0 +1,52 @@
package ibctesting
import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/app"
)
// Fund an address with the given amount in default denom
func (chain *TestChain) Fund(addr sdk.AccAddress, amount sdk.Int) {
require.NoError(chain.t, chain.sendMsgs(&banktypes.MsgSend{
FromAddress: chain.SenderAccount.GetAddress().String(),
ToAddress: addr.String(),
Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)),
}))
}
// SendNonDefaultSenderMsgs delivers a transaction through the application. It returns the result and error if one
// occurred.
func (chain *TestChain) SendNonDefaultSenderMsgs(senderPrivKey cryptotypes.PrivKey, msgs ...sdk.Msg) (*sdk.Result, error) {
require.NotEqual(chain.t, chain.SenderPrivKey, senderPrivKey, "use SendMsgs method")
// ensure the chain has the latest time
chain.Coordinator.UpdateTimeForChain(chain)
addr := sdk.AccAddress(senderPrivKey.PubKey().Address().Bytes())
account := chain.App.AccountKeeper.GetAccount(chain.GetContext(), addr)
require.NotNil(chain.t, account)
_, r, err := app.SignAndDeliver(
chain.t,
chain.TxConfig,
chain.App.BaseApp,
chain.GetContext().BlockHeader(),
msgs,
chain.ChainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
senderPrivKey,
)
// SignAndDeliver calls app.Commit()
chain.NextBlock()
chain.Coordinator.IncrementTime()
if err != nil {
return r, err
}
chain.captureIBCEvents(r)
return r, nil
}

113
x/wasm/ibctesting/path.go Normal file
View File

@ -0,0 +1,113 @@
package ibctesting
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
)
// Path contains two endpoints representing two chains connected over IBC
type Path struct {
EndpointA *Endpoint
EndpointB *Endpoint
}
// NewPath constructs an endpoint for each chain using the default values
// for the endpoints. Each endpoint is updated to have a pointer to the
// counterparty endpoint.
func NewPath(chainA, chainB *TestChain) *Path {
endpointA := NewDefaultEndpoint(chainA)
endpointB := NewDefaultEndpoint(chainB)
endpointA.Counterparty = endpointB
endpointB.Counterparty = endpointA
return &Path{
EndpointA: endpointA,
EndpointB: endpointB,
}
}
// SetChannelOrdered sets the channel order for both endpoints to ORDERED.
func (path *Path) SetChannelOrdered() {
path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED
path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED
}
// RelayPacket attempts to relay the packet first on EndpointA and then on EndpointB
// if EndpointA does not contain a packet commitment for that packet. An error is returned
// if a relay step fails or the packet commitment does not exist on either endpoint.
func (path *Path) RelayPacket(packet channeltypes.Packet, ack []byte) error {
pc := path.EndpointA.Chain.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(path.EndpointA.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointA.Chain.App.AppCodec(), packet)) {
// packet found, relay from A to B
if err := path.EndpointB.UpdateClient(); err != nil {
return err
}
res, err := path.EndpointB.RecvPacketWithResult(packet)
if err != nil {
return err
}
ack, err := ibctesting.ParseAckFromEvents(res.GetEvents())
if err != nil {
return err
}
if err := path.EndpointA.AcknowledgePacket(packet, ack); err != nil {
return err
}
return nil
}
pc = path.EndpointB.Chain.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(path.EndpointB.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointB.Chain.App.AppCodec(), packet)) {
// packet found, relay B to A
if err := path.EndpointA.UpdateClient(); err != nil {
return err
}
res, err := path.EndpointA.RecvPacketWithResult(packet)
if err != nil {
return err
}
ack, err := ibctesting.ParseAckFromEvents(res.GetEvents())
if err != nil {
return err
}
if err := path.EndpointB.AcknowledgePacket(packet, ack); err != nil {
return err
}
return nil
}
return fmt.Errorf("packet commitment does not exist on either endpoint for provided packet")
}
// SendMsg delivers the provided messages to the chain. The counterparty
// client is updated with the new source consensus state.
func (path *Path) SendMsg(msgs ...sdk.Msg) error {
if err := path.EndpointA.Chain.sendMsgs(msgs...); err != nil {
return err
}
if err := path.EndpointA.UpdateClient(); err != nil {
return err
}
return path.EndpointB.UpdateClient()
}
func (path *Path) Invert() *Path {
return &Path{
EndpointA: path.EndpointB,
EndpointB: path.EndpointA,
}
}

138
x/wasm/ibctesting/wasm.go Normal file
View File

@ -0,0 +1,138 @@
package ibctesting
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"os"
"strings"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang/protobuf/proto" //nolint
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/rand"
"github.com/cerc-io/laconicd/x/wasm/types"
)
var wasmIdent = []byte("\x00\x61\x73\x6D")
// SeedNewContractInstance stores some wasm code and instantiates a new contract on this chain.
// This method can be called to prepare the store with some valid CodeInfo and ContractInfo. The returned
// Address is the contract address for this instance. Test should make use of this data and/or use NewIBCContractMockWasmer
// for using a contract mock in Go.
func (chain *TestChain) SeedNewContractInstance() sdk.AccAddress {
pInstResp := chain.StoreCode(append(wasmIdent, rand.Bytes(10)...))
codeID := pInstResp.CodeID
anyAddressStr := chain.SenderAccount.GetAddress().String()
initMsg := []byte(fmt.Sprintf(`{"verifier": %q, "beneficiary": %q}`, anyAddressStr, anyAddressStr))
return chain.InstantiateContract(codeID, initMsg)
}
func (chain *TestChain) StoreCodeFile(filename string) types.MsgStoreCodeResponse {
wasmCode, err := os.ReadFile(filename)
require.NoError(chain.t, err)
if strings.HasSuffix(filename, "wasm") { // compress for gas limit
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
_, err := gz.Write(wasmCode)
require.NoError(chain.t, err)
err = gz.Close()
require.NoError(chain.t, err)
wasmCode = buf.Bytes()
}
return chain.StoreCode(wasmCode)
}
func (chain *TestChain) StoreCode(byteCode []byte) types.MsgStoreCodeResponse {
storeMsg := &types.MsgStoreCode{
Sender: chain.SenderAccount.GetAddress().String(),
WASMByteCode: byteCode,
}
r, err := chain.SendMsgs(storeMsg)
require.NoError(chain.t, err)
protoResult := chain.parseSDKResultData(r)
require.Len(chain.t, protoResult.Data, 1)
// unmarshal protobuf response from data
var pInstResp types.MsgStoreCodeResponse
require.NoError(chain.t, pInstResp.Unmarshal(protoResult.Data[0].Data))
require.NotEmpty(chain.t, pInstResp.CodeID)
require.NotEmpty(chain.t, pInstResp.Checksum)
return pInstResp
}
func (chain *TestChain) InstantiateContract(codeID uint64, initMsg []byte) sdk.AccAddress {
instantiateMsg := &types.MsgInstantiateContract{
Sender: chain.SenderAccount.GetAddress().String(),
Admin: chain.SenderAccount.GetAddress().String(),
CodeID: codeID,
Label: "ibc-test",
Msg: initMsg,
Funds: sdk.Coins{ibctesting.TestCoin},
}
r, err := chain.SendMsgs(instantiateMsg)
require.NoError(chain.t, err)
protoResult := chain.parseSDKResultData(r)
require.Len(chain.t, protoResult.Data, 1)
var pExecResp types.MsgInstantiateContractResponse
require.NoError(chain.t, pExecResp.Unmarshal(protoResult.Data[0].Data))
a, err := sdk.AccAddressFromBech32(pExecResp.Address)
require.NoError(chain.t, err)
return a
}
// SmartQuery This will serialize the query message and submit it to the contract.
// The response is parsed into the provided interface.
// Usage: SmartQuery(addr, QueryMsg{Foo: 1}, &response)
func (chain *TestChain) SmartQuery(contractAddr string, queryMsg interface{}, response interface{}) error {
msg, err := json.Marshal(queryMsg)
if err != nil {
return err
}
req := types.QuerySmartContractStateRequest{
Address: contractAddr,
QueryData: msg,
}
reqBin, err := proto.Marshal(&req)
if err != nil {
return err
}
// TODO: what is the query?
res := chain.App.Query(abci.RequestQuery{
Path: "/cosmwasm.wasm.v1.Query/SmartContractState",
Data: reqBin,
})
if res.Code != 0 {
return fmt.Errorf("query failed: (%d) %s", res.Code, res.Log)
}
// unpack protobuf
var resp types.QuerySmartContractStateResponse
err = proto.Unmarshal(res.Value, &resp)
if err != nil {
return err
}
// unpack json content
return json.Unmarshal(resp.Data, response)
}
func (chain *TestChain) parseSDKResultData(r *sdk.Result) sdk.TxMsgData {
var protoResult sdk.TxMsgData
require.NoError(chain.t, proto.Unmarshal(r.Data, &protoResult))
return protoResult
}
// ContractInfo is a helper function to returns the ContractInfo for the given contract address
func (chain *TestChain) ContractInfo(contractAddr sdk.AccAddress) *types.ContractInfo {
return chain.App.WasmKeeper.GetContractInfo(chain.GetContext(), contractAddr)
}

41
x/wasm/ioutils/ioutil.go Normal file
View File

@ -0,0 +1,41 @@
package ioutils
import (
"bytes"
"compress/gzip"
"io"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// Uncompress expects a valid gzip source to unpack or fails. See IsGzip
func Uncompress(gzipSrc []byte, limit uint64) ([]byte, error) {
if uint64(len(gzipSrc)) > limit {
return nil, types.ErrLimit
}
zr, err := gzip.NewReader(bytes.NewReader(gzipSrc))
if err != nil {
return nil, err
}
zr.Multistream(false)
defer zr.Close()
return io.ReadAll(LimitReader(zr, int64(limit)))
}
// LimitReader returns a Reader that reads from r
// but stops with types.ErrLimit after n bytes.
// The underlying implementation is a *io.LimitedReader.
func LimitReader(r io.Reader, n int64) io.Reader {
return &LimitedReader{r: &io.LimitedReader{R: r, N: n}}
}
type LimitedReader struct {
r *io.LimitedReader
}
func (l *LimitedReader) Read(p []byte) (n int, err error) {
if l.r.N <= 0 {
return 0, types.ErrLimit
}
return l.r.Read(p)
}

View File

@ -0,0 +1,82 @@
package ioutils
import (
"bytes"
"compress/gzip"
"errors"
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestUncompress(t *testing.T) {
wasmRaw, err := os.ReadFile("../keeper/testdata/hackatom.wasm")
require.NoError(t, err)
wasmGzipped, err := os.ReadFile("../keeper/testdata/hackatom.wasm.gzip")
require.NoError(t, err)
const maxSize = 400_000
specs := map[string]struct {
src []byte
expError error
expResult []byte
}{
"handle wasm compressed": {
src: wasmGzipped,
expResult: wasmRaw,
},
"handle gzip identifier only": {
src: gzipIdent,
expError: io.ErrUnexpectedEOF,
},
"handle broken gzip": {
src: append(gzipIdent, byte(0x1)),
expError: io.ErrUnexpectedEOF,
},
"handle incomplete gzip": {
src: wasmGzipped[:len(wasmGzipped)-5],
expError: io.ErrUnexpectedEOF,
},
"handle limit gzip output": {
src: asGzip(bytes.Repeat([]byte{0x1}, maxSize)),
expResult: bytes.Repeat([]byte{0x1}, maxSize),
},
"handle big gzip output": {
src: asGzip(bytes.Repeat([]byte{0x1}, maxSize+1)),
expError: types.ErrLimit,
},
"handle other big gzip output": {
src: asGzip(bytes.Repeat([]byte{0x1}, 2*maxSize)),
expError: types.ErrLimit,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
r, err := Uncompress(spec.src, maxSize)
require.True(t, errors.Is(spec.expError, err), "exp %v got %+v", spec.expError, err)
if spec.expError != nil {
return
}
assert.Equal(t, spec.expResult, r)
})
}
}
func asGzip(src []byte) []byte {
var buf bytes.Buffer
zipper := gzip.NewWriter(&buf)
if _, err := io.Copy(zipper, bytes.NewReader(src)); err != nil {
panic(err)
}
if err := zipper.Close(); err != nil {
panic(err)
}
return buf.Bytes()
}

43
x/wasm/ioutils/utils.go Normal file
View File

@ -0,0 +1,43 @@
package ioutils
import (
"bytes"
"compress/gzip"
)
// Note: []byte can never be const as they are inherently mutable
var (
// magic bytes to identify gzip.
// See https://www.ietf.org/rfc/rfc1952.txt
// and https://github.com/golang/go/blob/master/src/net/http/sniff.go#L186
gzipIdent = []byte("\x1F\x8B\x08")
wasmIdent = []byte("\x00\x61\x73\x6D")
)
// IsGzip returns checks if the file contents are gzip compressed
func IsGzip(input []byte) bool {
return len(input) >= 3 && bytes.Equal(gzipIdent, input[0:3])
}
// IsWasm checks if the file contents are of wasm binary
func IsWasm(input []byte) bool {
return bytes.Equal(input[:4], wasmIdent)
}
// GzipIt compresses the input ([]byte)
func GzipIt(input []byte) ([]byte, error) {
// Create gzip writer.
var b bytes.Buffer
w := gzip.NewWriter(&b)
_, err := w.Write(input)
if err != nil {
return nil, err
}
err = w.Close() // You must close this first to flush the bytes to the buffer.
if err != nil {
return nil, err
}
return b.Bytes(), nil
}

View File

@ -0,0 +1,68 @@
package ioutils
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func GetTestData() ([]byte, []byte, []byte, error) {
wasmCode, err := os.ReadFile("../keeper/testdata/hackatom.wasm")
if err != nil {
return nil, nil, nil, err
}
gzipData, err := GzipIt(wasmCode)
if err != nil {
return nil, nil, nil, err
}
someRandomStr := []byte("hello world")
return wasmCode, someRandomStr, gzipData, nil
}
func TestIsWasm(t *testing.T) {
wasmCode, someRandomStr, gzipData, err := GetTestData()
require.NoError(t, err)
t.Log("should return false for some random string data")
require.False(t, IsWasm(someRandomStr))
t.Log("should return false for gzip data")
require.False(t, IsWasm(gzipData))
t.Log("should return true for exact wasm")
require.True(t, IsWasm(wasmCode))
}
func TestIsGzip(t *testing.T) {
wasmCode, someRandomStr, gzipData, err := GetTestData()
require.NoError(t, err)
require.False(t, IsGzip(wasmCode))
require.False(t, IsGzip(someRandomStr))
require.False(t, IsGzip(nil))
require.True(t, IsGzip(gzipData[0:3]))
require.True(t, IsGzip(gzipData))
}
func TestGzipIt(t *testing.T) {
wasmCode, someRandomStr, _, err := GetTestData()
originalGzipData := []byte{
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 202, 72, 205, 201, 201, 87, 40, 207, 47, 202, 73, 1,
4, 0, 0, 255, 255, 133, 17, 74, 13, 11, 0, 0, 0,
}
require.NoError(t, err)
t.Log("gzip wasm with no error")
_, err = GzipIt(wasmCode)
require.NoError(t, err)
t.Log("gzip of a string should return exact gzip data")
strToGzip, err := GzipIt(someRandomStr)
require.True(t, IsGzip(strToGzip))
require.NoError(t, err)
require.Equal(t, originalGzipData, strToGzip)
}

View File

@ -0,0 +1,76 @@
package keeper
import (
"encoding/binary"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// AddressGenerator abstract address generator to be used for a single contract address
type AddressGenerator func(ctx sdk.Context, codeID uint64, checksum []byte) sdk.AccAddress
// ClassicAddressGenerator generates a contract address using codeID and instanceID sequence
func (k Keeper) ClassicAddressGenerator() AddressGenerator {
return func(ctx sdk.Context, codeID uint64, _ []byte) sdk.AccAddress {
instanceID := k.autoIncrementID(ctx, types.KeyLastInstanceID)
return BuildContractAddressClassic(codeID, instanceID)
}
}
// PredicableAddressGenerator generates a predictable contract address
func PredicableAddressGenerator(creator sdk.AccAddress, salt []byte, msg []byte, fixMsg bool) AddressGenerator {
return func(ctx sdk.Context, _ uint64, checksum []byte) sdk.AccAddress {
if !fixMsg { // clear msg to not be included in the address generation
msg = []byte{}
}
return BuildContractAddressPredictable(checksum, creator, salt, msg)
}
}
// BuildContractAddressClassic builds an sdk account address for a contract.
func BuildContractAddressClassic(codeID, instanceID uint64) sdk.AccAddress {
contractID := make([]byte, 16)
binary.BigEndian.PutUint64(contractID[:8], codeID)
binary.BigEndian.PutUint64(contractID[8:], instanceID)
return address.Module(types.ModuleName, contractID)[:types.ContractAddrLen]
}
// BuildContractAddressPredictable generates a contract address for the wasm module with len = types.ContractAddrLen using the
// Cosmos SDK address.Module function.
// Internally a key is built containing:
// (len(checksum) | checksum | len(sender_address) | sender_address | len(salt) | salt| len(initMsg) | initMsg).
//
// All method parameter values must be valid and not nil.
func BuildContractAddressPredictable(checksum []byte, creator sdk.AccAddress, salt, initMsg types.RawContractMessage) sdk.AccAddress {
if len(checksum) != 32 {
panic("invalid checksum")
}
if err := sdk.VerifyAddressFormat(creator); err != nil {
panic(fmt.Sprintf("creator: %s", err))
}
if err := types.ValidateSalt(salt); err != nil {
panic(fmt.Sprintf("salt: %s", err))
}
if err := initMsg.ValidateBasic(); len(initMsg) != 0 && err != nil {
panic(fmt.Sprintf("initMsg: %s", err))
}
checksum = UInt64LengthPrefix(checksum)
creator = UInt64LengthPrefix(creator)
salt = UInt64LengthPrefix(salt)
initMsg = UInt64LengthPrefix(initMsg)
key := make([]byte, len(checksum)+len(creator)+len(salt)+len(initMsg))
copy(key[0:], checksum)
copy(key[len(checksum):], creator)
copy(key[len(checksum)+len(creator):], salt)
copy(key[len(checksum)+len(creator)+len(salt):], initMsg)
return address.Module(types.ModuleName, key)[:types.ContractAddrLen]
}
// UInt64LengthPrefix prepend big endian encoded byte length
func UInt64LengthPrefix(bz []byte) []byte {
return append(sdk.Uint64ToBigEndian(uint64(len(bz))), bz...)
}

View File

@ -0,0 +1,432 @@
package keeper
import (
"encoding/json"
"fmt"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
)
func TestBuildContractAddress(t *testing.T) {
x, y := sdk.GetConfig().GetBech32AccountAddrPrefix(), sdk.GetConfig().GetBech32AccountPubPrefix()
t.Cleanup(func() {
sdk.GetConfig().SetBech32PrefixForAccount(x, y)
})
sdk.GetConfig().SetBech32PrefixForAccount("purple", "purple")
// test vectors generated via cosmjs: https://github.com/cosmos/cosmjs/pull/1253/files
type Spec struct {
In struct {
Checksum tmbytes.HexBytes `json:"checksum"`
Creator sdk.AccAddress `json:"creator"`
Salt tmbytes.HexBytes `json:"salt"`
Msg string `json:"msg"`
} `json:"in"`
Out struct {
Address sdk.AccAddress `json:"address"`
} `json:"out"`
}
var specs []Spec
require.NoError(t, json.Unmarshal([]byte(goldenMasterPredictableContractAddr), &specs))
require.NotEmpty(t, specs)
for i, spec := range specs {
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
// when
gotAddr := BuildContractAddressPredictable(spec.In.Checksum, spec.In.Creator, spec.In.Salt.Bytes(), []byte(spec.In.Msg))
require.Equal(t, spec.Out.Address.String(), gotAddr.String())
require.NoError(t, sdk.VerifyAddressFormat(gotAddr))
})
}
}
const goldenMasterPredictableContractAddr = `[
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": null
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000001610000000000000000",
"addressData": "5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f847"
},
"out": {
"address": "purple1t6r960j945lfv8mhl4mage2rg97w63xeynwrupum2s2l7em4lprs9ce5hk"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": "{}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc00000000000000016100000000000000027b7d",
"addressData": "0995499608947a5281e2c7ebd71bdb26a1ad981946dad57f6c4d3ee35de77835"
},
"out": {
"address": "purple1px25n9sgj3a99q0zcl4awx7my6s6mxqegmdd2lmvf5lwxh080q6suttktr"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc000000000000000161000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "83326e554723b15bac664ceabc8a5887e27003abe9fbd992af8c7bcea4745167"
},
"out": {
"address": "purple1svexu428ywc4htrxfn4tezjcsl38qqata8aany4033auafr529ns4v254c"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": null
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae0000000000000000",
"addressData": "9384c6248c0bb171e306fd7da0993ec1e20eba006452a3a9e078883eb3594564"
},
"out": {
"address": "purple1jwzvvfyvpwchrccxl476pxf7c83qawsqv3f2820q0zyrav6eg4jqdcq7gc"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae00000000000000027b7d",
"addressData": "9a8d5f98fb186825401a26206158e7a1213311a9b6a87944469913655af52ffb"
},
"out": {
"address": "purple1n2x4lx8mrp5z2sq6ycsxzk885ysnxydfk658j3zxnyfk2kh49lasesxf6j"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "932f07bc53f7d0b0b43cb5a54ac3e245b205e6ae6f7c1d991dc6af4a2ff9ac18"
},
"out": {
"address": "purple1jvhs00zn7lgtpdpukkj54slzgkeqte4wda7pmxgac6h55tle4svq8cmp60"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": null
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000001610000000000000000",
"addressData": "9725e94f528d8b78d33c25f3dfcd60e6142d8be60ab36f6a5b59036fd51560db"
},
"out": {
"address": "purple1juj7jn6j3k9h35euyhealntquc2zmzlxp2ek76jmtypkl4g4vrdsfwmwxk"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": "{}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff00000000000000016100000000000000027b7d",
"addressData": "b056e539bbaf447ba18f3f13b792970111fc78933eb6700f4d227b5216d63658"
},
"out": {
"address": "purple1kptw2wdm4az8hgv08ufm0y5hqyglc7yn86m8qr6dyfa4y9kkxevqmkm9q3"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff000000000000000161000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "6c98434180f052294ff89fb6d2dae34f9f4468b0b8e6e7c002b2a44adee39abd"
},
"out": {
"address": "purple1djvyxsvq7pfzjnlcn7md9khrf705g69shrnw0sqzk2jy4hhrn27sjh2ysy"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": null
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae0000000000000000",
"addressData": "0aaf1c31c5d529d21d898775bc35b3416f47bfd99188c334c6c716102cbd3101"
},
"out": {
"address": "purple1p2h3cvw9655ay8vfsa6mcddng9h5007ejxyvxdxxcutpqt9axyqsagmmay"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae00000000000000027b7d",
"addressData": "36fe6ab732187cdfed46290b448b32eb7f4798e7a4968b0537de8a842cbf030e"
},
"out": {
"address": "purple1xmlx4dejrp7dlm2x9y95fzejadl50x885jtgkpfhm69ggt9lqv8qk3vn4f"
}
},
{
"in": {
"checksum": "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d00000000000000002013a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a500000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "a0d0c942adac6f3e5e7c23116c4c42a24e96e0ab75f53690ec2d3de16067c751"
},
"out": {
"address": "purple15rgvjs4d43hnuhnuyvgkcnzz5f8fdc9twh6ndy8v9577zcr8cags40l9dt"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": null
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000001610000000000000000",
"addressData": "b95c467218d408a0f93046f227b6ece7fe18133ff30113db4d2a7becdfeca141"
},
"out": {
"address": "purple1h9wyvusc6sy2p7fsgmez0dhvullpsyel7vq38k6d9fa7ehlv59qsvnyh36"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": "{}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc00000000000000016100000000000000027b7d",
"addressData": "23fe45dbbd45dc6cd25244a74b6e99e7a65bf0bac2f2842a05049d37555a3ae6"
},
"out": {
"address": "purple1y0lytkaaghwxe5jjgjn5km5eu7n9hu96ctegg2s9qjwnw4268tnqxhg60a"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "61",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc000000000000000161000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "6faea261ed63baa65b05726269e83b217fa6205dc7d9fb74f9667d004a69c082"
},
"out": {
"address": "purple1d7h2yc0dvwa2vkc9wf3xn6pmy9l6vgzaclvlka8eve7sqjnfczpqqsdnwu"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": null
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae0000000000000000",
"addressData": "67a3ab6384729925fdb144574628ce96836fe098d2c6be4e84ac106b2728d96c"
},
"out": {
"address": "purple1v736kcuyw2vjtld3g3t5v2xwj6pklcyc6trtun5y4sgxkfegm9kq7vgpnt"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae00000000000000027b7d",
"addressData": "23a121263bfce05c144f4af86f3d8a9f87dc56f9dc48dbcffc8c5a614da4c661"
},
"out": {
"address": "purple1ywsjzf3mlns9c9z0ftux70v2n7rac4hem3ydhnlu33dxzndycesssc7x2m"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbcccccccccc",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000149999999999aaaaaaaaaabbbbbbbbbbcccccccccc0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "dd90dba6d6fcd5fb9c9c8f536314eb1bb29cb5aa084b633c5806b926a5636b58"
},
"out": {
"address": "purple1mkgdhfkkln2lh8yu3afkx98trwefedd2pp9kx0zcq6ujdftrddvq50esay"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": null
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000001610000000000000000",
"addressData": "547a743022f4f1af05b102f57bf1c1c7d7ee81bae427dc20d36b2c4ec57612ae"
},
"out": {
"address": "purple123a8gvpz7nc67pd3qt6hhuwpclt7aqd6usnacgxndvkya3tkz2hq5hz38f"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": "{}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff00000000000000016100000000000000027b7d",
"addressData": "416e169110e4b411bc53162d7503b2bbf14d6b36b1413a4f4c9af622696e9665"
},
"out": {
"address": "purple1g9hpdygsuj6pr0znzckh2qajh0c566ekk9qn5n6vntmzy6twjejsrl9alk"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "61",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff000000000000000161000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "619a0988b92d8796cea91dea63cbb1f1aefa4a6b6ee5c5d1e937007252697220"
},
"out": {
"address": "purple1vxdqnz9e9kredn4frh4x8ja37xh05jntdmjut50fxuq8y5nfwgsquu9mxh"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": null
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae0000000000000000",
"addressData": "d8af856a6a04852d19b647ad6d4336eb26e077f740aef1a0331db34d299a885a"
},
"out": {
"address": "purple1mzhc26n2qjzj6xdkg7kk6sekavnwqalhgzh0rgpnrke562v63pdq8grp8q"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae00000000000000027b7d",
"addressData": "c7fb7bea96daab23e416c4fcf328215303005e1d0d5424257335568e5381e33c"
},
"out": {
"address": "purple1clahh65km24j8eqkcn70x2pp2vpsqhsap42zgftnx4tgu5upuv7q9ywjws"
}
},
{
"in": {
"checksum": "1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b",
"creator": "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m",
"creatorData": "9999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff",
"salt": "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae",
"msg": "{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}"
},
"intermediate": {
"key": "7761736d0000000000000000201da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b00000000000000209999999999aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffff0000000000000040aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae000000000000002f7b22736f6d65223a3132332c22737472756374757265223a7b226e6573746564223a5b226f6b222c747275655d7d7d",
"addressData": "ccdf9dea141a6c2475870529ab38fae9dec30df28e005894fe6578b66133ab4a"
},
"out": {
"address": "purple1en0em6s5rfkzgav8q556kw86a80vxr0j3cq93987v4utvcfn4d9q0tql4w"
}
}
]
`

96
x/wasm/keeper/ante.go Normal file
View File

@ -0,0 +1,96 @@
package keeper
import (
"encoding/binary"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// CountTXDecorator ante handler to count the tx position in a block.
type CountTXDecorator struct {
storeKey sdk.StoreKey
}
// NewCountTXDecorator constructor
func NewCountTXDecorator(storeKey sdk.StoreKey) *CountTXDecorator {
return &CountTXDecorator{storeKey: storeKey}
}
// AnteHandle handler stores a tx counter with current height encoded in the store to let the app handle
// global rollback behavior instead of keeping state in the handler itself.
// The ante handler passes the counter value via sdk.Context upstream. See `types.TXCounter(ctx)` to read the value.
// Simulations don't get a tx counter value assigned.
func (a CountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
if simulate {
return next(ctx, tx, simulate)
}
store := ctx.KVStore(a.storeKey)
currentHeight := ctx.BlockHeight()
var txCounter uint32 // start with 0
// load counter when exists
if bz := store.Get(types.TXCounterPrefix); bz != nil {
lastHeight, val := decodeHeightCounter(bz)
if currentHeight == lastHeight {
// then use stored counter
txCounter = val
} // else use `0` from above to start with
}
// store next counter value for current height
store.Set(types.TXCounterPrefix, encodeHeightCounter(currentHeight, txCounter+1))
return next(types.WithTXCounter(ctx, txCounter), tx, simulate)
}
func encodeHeightCounter(height int64, counter uint32) []byte {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, counter)
return append(sdk.Uint64ToBigEndian(uint64(height)), b...)
}
func decodeHeightCounter(bz []byte) (int64, uint32) {
return int64(sdk.BigEndianToUint64(bz[0:8])), binary.BigEndian.Uint32(bz[8:])
}
// LimitSimulationGasDecorator ante decorator to limit gas in simulation calls
type LimitSimulationGasDecorator struct {
gasLimit *sdk.Gas
}
// NewLimitSimulationGasDecorator constructor accepts nil value to fallback to block gas limit.
func NewLimitSimulationGasDecorator(gasLimit *sdk.Gas) *LimitSimulationGasDecorator {
if gasLimit != nil && *gasLimit == 0 {
panic("gas limit must not be zero")
}
return &LimitSimulationGasDecorator{gasLimit: gasLimit}
}
// AnteHandle that limits the maximum gas available in simulations only.
// A custom max value can be configured and will be applied when set. The value should not
// exceed the max block gas limit.
// Different values on nodes are not consensus breaking as they affect only
// simulations but may have effect on client user experience.
//
// When no custom value is set then the max block gas is used as default limit.
func (d LimitSimulationGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
if !simulate {
// Wasm code is not executed in checkTX so that we don't need to limit it further.
// Tendermint rejects the TX afterwards when the tx.gas > max block gas.
// On deliverTX we rely on the tendermint/sdk mechanics that ensure
// tx has gas set and gas < max block gas
return next(ctx, tx, simulate)
}
// apply custom node gas limit
if d.gasLimit != nil {
return next(ctx.WithGasMeter(sdk.NewGasMeter(*d.gasLimit)), tx, simulate)
}
// default to max block gas when set, to be on the safe side
if maxGas := ctx.ConsensusParams().GetBlock().MaxGas; maxGas > 0 {
return next(ctx.WithGasMeter(sdk.NewGasMeter(sdk.Gas(maxGas))), tx, simulate)
}
return next(ctx, tx, simulate)
}

189
x/wasm/keeper/ante_test.go Normal file
View File

@ -0,0 +1,189 @@
package keeper_test
import (
"testing"
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cerc-io/laconicd/x/wasm/keeper"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestCountTxDecorator(t *testing.T) {
keyWasm := sdk.NewKVStoreKey(types.StoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyWasm, sdk.StoreTypeIAVL, db)
require.NoError(t, ms.LoadLatestVersion())
const myCurrentBlockHeight = 100
specs := map[string]struct {
setupDB func(t *testing.T, ctx sdk.Context)
simulate bool
nextAssertAnte func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error)
expErr bool
}{
"no initial counter set": {
setupDB: func(t *testing.T, ctx sdk.Context) {},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(0), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz)
return ctx, nil
},
},
"persistent counter incremented - big endian": {
setupDB: func(t *testing.T, ctx sdk.Context) {
bz := []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 2}
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz)
},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(1<<24+2), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 3}, bz)
return ctx, nil
},
},
"old height counter replaced": {
setupDB: func(t *testing.T, ctx sdk.Context) {
previousHeight := byte(myCurrentBlockHeight - 1)
bz := []byte{0, 0, 0, 0, 0, 0, 0, previousHeight, 0, 0, 0, 1}
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz)
},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(0), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz)
return ctx, nil
},
},
"simulation not persisted": {
setupDB: func(t *testing.T, ctx sdk.Context) {
},
simulate: true,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
_, ok := types.TXCounter(ctx)
assert.False(t, ok)
require.True(t, simulate)
// and not stored
assert.False(t, ctx.MultiStore().GetKVStore(keyWasm).Has(types.TXCounterPrefix))
return ctx, nil
},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
ctx := sdk.NewContext(ms.CacheMultiStore(), tmproto.Header{
Height: myCurrentBlockHeight,
Time: time.Date(2021, time.September, 27, 12, 0, 0, 0, time.UTC),
}, false, log.NewNopLogger())
spec.setupDB(t, ctx)
var anyTx sdk.Tx
// when
ante := keeper.NewCountTXDecorator(keyWasm)
_, gotErr := ante.AnteHandle(ctx, anyTx, spec.simulate, spec.nextAssertAnte)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
})
}
}
func TestLimitSimulationGasDecorator(t *testing.T) {
var (
hundred sdk.Gas = 100
zero sdk.Gas = 0
)
specs := map[string]struct {
customLimit *sdk.Gas
consumeGas sdk.Gas
maxBlockGas int64
simulation bool
expErr interface{}
}{
"custom limit set": {
customLimit: &hundred,
consumeGas: hundred + 1,
maxBlockGas: -1,
simulation: true,
expErr: sdk.ErrorOutOfGas{Descriptor: "testing"},
},
"block limit set": {
maxBlockGas: 100,
consumeGas: hundred + 1,
simulation: true,
expErr: sdk.ErrorOutOfGas{Descriptor: "testing"},
},
"no limits set": {
maxBlockGas: -1,
consumeGas: hundred + 1,
simulation: true,
},
"both limits set, custom applies": {
customLimit: &hundred,
consumeGas: hundred - 1,
maxBlockGas: 10,
simulation: true,
},
"not a simulation": {
customLimit: &hundred,
consumeGas: hundred + 1,
simulation: false,
},
"zero custom limit": {
customLimit: &zero,
simulation: true,
expErr: "gas limit must not be zero",
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
nextAnte := consumeGasAnteHandler(spec.consumeGas)
ctx := sdk.Context{}.
WithGasMeter(sdk.NewInfiniteGasMeter()).
WithConsensusParams(&abci.ConsensusParams{
Block: &abci.BlockParams{MaxGas: spec.maxBlockGas},
})
// when
if spec.expErr != nil {
require.PanicsWithValue(t, spec.expErr, func() {
ante := keeper.NewLimitSimulationGasDecorator(spec.customLimit)
ante.AnteHandle(ctx, nil, spec.simulation, nextAnte)
})
return
}
ante := keeper.NewLimitSimulationGasDecorator(spec.customLimit)
ante.AnteHandle(ctx, nil, spec.simulation, nextAnte)
})
}
}
func consumeGasAnteHandler(gasToConsume sdk.Gas) sdk.AnteHandler {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
ctx.GasMeter().ConsumeGas(gasToConsume, "testing")
return ctx, nil
}
}

43
x/wasm/keeper/api.go Normal file
View File

@ -0,0 +1,43 @@
package keeper
import (
wasmvm "github.com/CosmWasm/wasmvm"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// DefaultGasCostHumanAddress is how moch SDK gas we charge to convert to a human address format
DefaultGasCostHumanAddress = 5
// DefaultGasCostCanonicalAddress is how moch SDK gas we charge to convert to a canonical address format
DefaultGasCostCanonicalAddress = 4
// DefaultDeserializationCostPerByte The formular should be `len(data) * deserializationCostPerByte`
DefaultDeserializationCostPerByte = 1
)
var (
costHumanize = DefaultGasCostHumanAddress * DefaultGasMultiplier
costCanonical = DefaultGasCostCanonicalAddress * DefaultGasMultiplier
costJSONDeserialization = wasmvmtypes.UFraction{
Numerator: DefaultDeserializationCostPerByte * DefaultGasMultiplier,
Denominator: 1,
}
)
func humanAddress(canon []byte) (string, uint64, error) {
if err := sdk.VerifyAddressFormat(canon); err != nil {
return "", costHumanize, err
}
return sdk.AccAddress(canon).String(), costHumanize, nil
}
func canonicalAddress(human string) ([]byte, uint64, error) {
bz, err := sdk.AccAddressFromBech32(human)
return bz, costCanonical, err
}
var cosmwasmAPI = wasmvm.GoAPI{
HumanAddress: humanAddress,
CanonicalAddress: canonicalAddress,
}

View File

@ -0,0 +1,63 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// ChainAccessConfigs chain settings
type ChainAccessConfigs struct {
Upload types.AccessConfig
Instantiate types.AccessConfig
}
// NewChainAccessConfigs constructor
func NewChainAccessConfigs(upload types.AccessConfig, instantiate types.AccessConfig) ChainAccessConfigs {
return ChainAccessConfigs{Upload: upload, Instantiate: instantiate}
}
type AuthorizationPolicy interface {
CanCreateCode(chainConfigs ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool
CanInstantiateContract(c types.AccessConfig, actor sdk.AccAddress) bool
CanModifyContract(admin, actor sdk.AccAddress) bool
CanModifyCodeAccessConfig(creator, actor sdk.AccAddress, isSubset bool) bool
}
type DefaultAuthorizationPolicy struct{}
func (p DefaultAuthorizationPolicy) CanCreateCode(chainConfigs ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
return chainConfigs.Upload.Allowed(actor) &&
contractConfig.IsSubset(chainConfigs.Instantiate)
}
func (p DefaultAuthorizationPolicy) CanInstantiateContract(config types.AccessConfig, actor sdk.AccAddress) bool {
return config.Allowed(actor)
}
func (p DefaultAuthorizationPolicy) CanModifyContract(admin, actor sdk.AccAddress) bool {
return admin != nil && admin.Equals(actor)
}
func (p DefaultAuthorizationPolicy) CanModifyCodeAccessConfig(creator, actor sdk.AccAddress, isSubset bool) bool {
return creator != nil && creator.Equals(actor) && isSubset
}
type GovAuthorizationPolicy struct{}
// CanCreateCode implements AuthorizationPolicy.CanCreateCode to allow gov actions. Always returns true.
func (p GovAuthorizationPolicy) CanCreateCode(ChainAccessConfigs, sdk.AccAddress, types.AccessConfig) bool {
return true
}
func (p GovAuthorizationPolicy) CanInstantiateContract(types.AccessConfig, sdk.AccAddress) bool {
return true
}
func (p GovAuthorizationPolicy) CanModifyContract(sdk.AccAddress, sdk.AccAddress) bool {
return true
}
func (p GovAuthorizationPolicy) CanModifyCodeAccessConfig(sdk.AccAddress, sdk.AccAddress, bool) bool {
return true
}

View File

@ -0,0 +1,345 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestDefaultAuthzPolicyCanCreateCode(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
chainConfigs ChainAccessConfigs
contractInstConf types.AccessConfig
actor sdk.AccAddress
exp bool
panics bool
}{
"upload nobody": {
chainConfigs: NewChainAccessConfigs(types.AllowNobody, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: false,
},
"upload everybody": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: true,
},
"upload only address - same": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeOnlyAddress.With(myActorAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: true,
},
"upload only address - different": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeOnlyAddress.With(otherAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: false,
},
"upload any address - included": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: true,
},
"upload any address - not included": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeAnyOfAddresses.With(otherAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
exp: false,
},
"contract config - subtype": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowEverybody),
contractInstConf: types.AccessTypeAnyOfAddresses.With(myActorAddress),
exp: true,
},
"contract config - not subtype": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowNobody),
contractInstConf: types.AllowEverybody,
exp: false,
},
"upload undefined config - panics": {
chainConfigs: NewChainAccessConfigs(types.AccessConfig{}, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
panics: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
if !spec.panics {
got := policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
assert.Equal(t, spec.exp, got)
return
}
assert.Panics(t, func() {
policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
})
})
}
}
func TestDefaultAuthzPolicyCanInstantiateContract(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
config types.AccessConfig
actor sdk.AccAddress
exp bool
panics bool
}{
"nobody": {
config: types.AllowNobody,
exp: false,
},
"everybody": {
config: types.AllowEverybody,
exp: true,
},
"only address - same": {
config: types.AccessTypeOnlyAddress.With(myActorAddress),
exp: true,
},
"only address - different": {
config: types.AccessTypeOnlyAddress.With(otherAddress),
exp: false,
},
"any address - included": {
config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress),
exp: true,
},
"any address - not included": {
config: types.AccessTypeAnyOfAddresses.With(otherAddress),
exp: false,
},
"undefined config - panics": {
config: types.AccessConfig{},
panics: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
if !spec.panics {
got := policy.CanInstantiateContract(spec.config, myActorAddress)
assert.Equal(t, spec.exp, got)
return
}
assert.Panics(t, func() {
policy.CanInstantiateContract(spec.config, myActorAddress)
})
})
}
}
func TestDefaultAuthzPolicyCanModifyContract(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
admin sdk.AccAddress
exp bool
}{
"same as actor": {
admin: myActorAddress,
exp: true,
},
"different admin": {
admin: otherAddress,
exp: false,
},
"no admin": {
exp: false,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
got := policy.CanModifyContract(spec.admin, myActorAddress)
assert.Equal(t, spec.exp, got)
})
}
}
func TestDefaultAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
admin sdk.AccAddress
subset bool
exp bool
}{
"same as actor - subset": {
admin: myActorAddress,
subset: true,
exp: true,
},
"same as actor - not subset": {
admin: myActorAddress,
subset: false,
exp: false,
},
"different admin": {
admin: otherAddress,
exp: false,
},
"no admin": {
exp: false,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset)
assert.Equal(t, spec.exp, got)
})
}
}
func TestGovAuthzPolicyCanCreateCode(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
chainConfigs ChainAccessConfigs
contractInstConf types.AccessConfig
actor sdk.AccAddress
}{
"upload nobody": {
chainConfigs: NewChainAccessConfigs(types.AllowNobody, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"upload everybody": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"upload only address - same": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeOnlyAddress.With(myActorAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"upload only address - different": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeOnlyAddress.With(otherAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"upload any address - included": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"upload any address - not included": {
chainConfigs: NewChainAccessConfigs(types.AccessTypeAnyOfAddresses.With(otherAddress), types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
"contract config - subtype": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowEverybody),
contractInstConf: types.AccessTypeAnyOfAddresses.With(myActorAddress),
},
"contract config - not subtype": {
chainConfigs: NewChainAccessConfigs(types.AllowEverybody, types.AllowNobody),
contractInstConf: types.AllowEverybody,
},
"upload undefined config - not panics": {
chainConfigs: NewChainAccessConfigs(types.AccessConfig{}, types.AllowEverybody),
contractInstConf: types.AllowEverybody,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
assert.True(t, got)
})
}
}
func TestGovAuthzPolicyCanInstantiateContract(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
config types.AccessConfig
actor sdk.AccAddress
}{
"nobody": {
config: types.AllowNobody,
},
"everybody": {
config: types.AllowEverybody,
},
"only address - same": {
config: types.AccessTypeOnlyAddress.With(myActorAddress),
},
"only address - different": {
config: types.AccessTypeOnlyAddress.With(otherAddress),
},
"any address - included": {
config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress),
},
"any address - not included": {
config: types.AccessTypeAnyOfAddresses.With(otherAddress),
},
"undefined config - panics": {
config: types.AccessConfig{},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanInstantiateContract(spec.config, myActorAddress)
assert.True(t, got)
})
}
}
func TestGovAuthzPolicyCanModifyContract(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
admin sdk.AccAddress
}{
"same as actor": {
admin: myActorAddress,
},
"different admin": {
admin: otherAddress,
},
"no admin": {},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanModifyContract(spec.admin, myActorAddress)
assert.True(t, got)
})
}
}
func TestGovAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) {
myActorAddress := RandomAccountAddress(t)
otherAddress := RandomAccountAddress(t)
specs := map[string]struct {
admin sdk.AccAddress
subset bool
}{
"same as actor - subset": {
admin: myActorAddress,
subset: true,
},
"same as actor - not subset": {
admin: myActorAddress,
subset: false,
},
"different admin": {
admin: otherAddress,
},
"no admin": {},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset)
assert.True(t, got)
})
}
}

102
x/wasm/keeper/bench_test.go Normal file
View File

@ -0,0 +1,102 @@
package keeper
import (
"os"
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// BenchmarkVerification benchmarks secp256k1 verification which is 1000 gas based on cpu time.
//
// Just this function is copied from
// https://github.com/cosmos/cosmos-sdk/blob/90e9370bd80d9a3d41f7203ddb71166865561569/crypto/keys/internal/benchmarking/bench.go#L48-L62
// And thus under the GO license (BSD style)
func BenchmarkGasNormalization(b *testing.B) {
priv := secp256k1.GenPrivKey()
pub := priv.PubKey()
// use a short message, so this time doesn't get dominated by hashing.
message := []byte("Hello, world!")
signature, err := priv.Sign(message)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
pub.VerifySignature(message, signature)
}
}
// By comparing the timing for queries on pinned vs unpinned, the difference gives us the overhead of
// instantiating an unpinned contract. That value can be used to determine a reasonable gas price
// for the InstantiationCost
func BenchmarkInstantiationOverhead(b *testing.B) {
specs := map[string]struct {
pinned bool
db func() dbm.DB
}{
"unpinned, memory db": {
db: func() dbm.DB { return dbm.NewMemDB() },
},
"pinned, memory db": {
db: func() dbm.DB { return dbm.NewMemDB() },
pinned: true,
},
}
for name, spec := range specs {
b.Run(name, func(b *testing.B) {
wasmConfig := types.WasmConfig{MemoryCacheSize: 0}
ctx, keepers := createTestInput(b, false, AvailableCapabilities, wasmConfig, spec.db())
example := InstantiateHackatomExampleContract(b, ctx, keepers)
if spec.pinned {
require.NoError(b, keepers.ContractKeeper.PinCode(ctx, example.CodeID))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := keepers.WasmKeeper.QuerySmart(ctx, example.Contract, []byte(`{"verifier":{}}`))
require.NoError(b, err)
}
})
}
}
// Calculate the time it takes to compile some wasm code the first time.
// This will help us adjust pricing for UploadCode
func BenchmarkCompilation(b *testing.B) {
specs := map[string]struct {
wasmFile string
}{
"hackatom": {
wasmFile: "./testdata/hackatom.wasm",
},
"burner": {
wasmFile: "./testdata/burner.wasm",
},
"ibc_reflect": {
wasmFile: "./testdata/ibc_reflect.wasm",
},
}
for name, spec := range specs {
b.Run(name, func(b *testing.B) {
wasmConfig := types.WasmConfig{MemoryCacheSize: 0}
db := dbm.NewMemDB()
ctx, keepers := createTestInput(b, false, AvailableCapabilities, wasmConfig, db)
// print out code size for comparisons
code, err := os.ReadFile(spec.wasmFile)
require.NoError(b, err)
b.Logf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b(size: %d) ", len(code))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = StoreExampleContract(b, ctx, keepers, spec.wasmFile)
}
})
}
}

View File

@ -0,0 +1,130 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
var _ types.ContractOpsKeeper = PermissionedKeeper{}
// decoratedKeeper contains a subset of the wasm keeper that are already or can be guarded by an authorization policy in the future
type decoratedKeeper interface {
create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, checksum []byte, err error)
instantiate(
ctx sdk.Context,
codeID uint64,
creator, admin sdk.AccAddress,
initMsg []byte,
label string,
deposit sdk.Coins,
addressGenerator AddressGenerator,
authZ AuthorizationPolicy,
) (sdk.AccAddress, []byte, error)
migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte, authZ AuthorizationPolicy) ([]byte, error)
setContractAdmin(ctx sdk.Context, contractAddress, caller, newAdmin sdk.AccAddress, authZ AuthorizationPolicy) error
pinCode(ctx sdk.Context, codeID uint64) error
unpinCode(ctx sdk.Context, codeID uint64) error
execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins) ([]byte, error)
Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error)
setContractInfoExtension(ctx sdk.Context, contract sdk.AccAddress, extra types.ContractInfoExtension) error
setAccessConfig(ctx sdk.Context, codeID uint64, caller sdk.AccAddress, newConfig types.AccessConfig, autz AuthorizationPolicy) error
ClassicAddressGenerator() AddressGenerator
}
type PermissionedKeeper struct {
authZPolicy AuthorizationPolicy
nested decoratedKeeper
}
func NewPermissionedKeeper(nested decoratedKeeper, authZPolicy AuthorizationPolicy) *PermissionedKeeper {
return &PermissionedKeeper{authZPolicy: authZPolicy, nested: nested}
}
func NewGovPermissionKeeper(nested decoratedKeeper) *PermissionedKeeper {
return NewPermissionedKeeper(nested, GovAuthorizationPolicy{})
}
func NewDefaultPermissionKeeper(nested decoratedKeeper) *PermissionedKeeper {
return NewPermissionedKeeper(nested, DefaultAuthorizationPolicy{})
}
func (p PermissionedKeeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig) (codeID uint64, checksum []byte, err error) {
return p.nested.create(ctx, creator, wasmCode, instantiateAccess, p.authZPolicy)
}
// Instantiate creates an instance of a WASM contract using the classic sequence based address generator
func (p PermissionedKeeper) Instantiate(
ctx sdk.Context,
codeID uint64,
creator, admin sdk.AccAddress,
initMsg []byte,
label string,
deposit sdk.Coins,
) (sdk.AccAddress, []byte, error) {
return p.nested.instantiate(ctx, codeID, creator, admin, initMsg, label, deposit, p.nested.ClassicAddressGenerator(), p.authZPolicy)
}
// Instantiate2 creates an instance of a WASM contract using the predictable address generator
func (p PermissionedKeeper) Instantiate2(
ctx sdk.Context,
codeID uint64,
creator, admin sdk.AccAddress,
initMsg []byte,
label string,
deposit sdk.Coins,
salt []byte,
fixMsg bool,
) (sdk.AccAddress, []byte, error) {
return p.nested.instantiate(
ctx,
codeID,
creator,
admin,
initMsg,
label,
deposit,
PredicableAddressGenerator(creator, salt, initMsg, fixMsg),
p.authZPolicy,
)
}
func (p PermissionedKeeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins) ([]byte, error) {
return p.nested.execute(ctx, contractAddress, caller, msg, coins)
}
func (p PermissionedKeeper) Migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte) ([]byte, error) {
return p.nested.migrate(ctx, contractAddress, caller, newCodeID, msg, p.authZPolicy)
}
func (p PermissionedKeeper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) {
return p.nested.Sudo(ctx, contractAddress, msg)
}
func (p PermissionedKeeper) UpdateContractAdmin(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newAdmin sdk.AccAddress) error {
return p.nested.setContractAdmin(ctx, contractAddress, caller, newAdmin, p.authZPolicy)
}
func (p PermissionedKeeper) ClearContractAdmin(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress) error {
return p.nested.setContractAdmin(ctx, contractAddress, caller, nil, p.authZPolicy)
}
func (p PermissionedKeeper) PinCode(ctx sdk.Context, codeID uint64) error {
return p.nested.pinCode(ctx, codeID)
}
func (p PermissionedKeeper) UnpinCode(ctx sdk.Context, codeID uint64) error {
return p.nested.unpinCode(ctx, codeID)
}
// SetContractInfoExtension updates the extra attributes that can be stored with the contract info
func (p PermissionedKeeper) SetContractInfoExtension(ctx sdk.Context, contract sdk.AccAddress, extra types.ContractInfoExtension) error {
return p.nested.setContractInfoExtension(ctx, contract, extra)
}
// SetAccessConfig updates the access config of a code id.
func (p PermissionedKeeper) SetAccessConfig(ctx sdk.Context, codeID uint64, caller sdk.AccAddress, newConfig types.AccessConfig) error {
return p.nested.setAccessConfig(ctx, codeID, caller, newConfig, p.authZPolicy)
}

View File

@ -0,0 +1,168 @@
package keeper
import (
"encoding/json"
"fmt"
"math"
"strings"
"testing"
"github.com/cerc-io/laconicd/x/wasm/keeper/wasmtesting"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestInstantiate2(t *testing.T) {
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities)
example := StoreHackatomExampleContract(t, parentCtx, keepers)
otherExample := StoreReflectContract(t, parentCtx, keepers)
mock := &wasmtesting.MockWasmer{}
wasmtesting.MakeInstantiable(mock)
keepers.WasmKeeper.wasmVM = mock // set mock to not fail on contract init message
verifierAddr := RandomAccountAddress(t)
beneficiaryAddr := RandomAccountAddress(t)
initMsg := mustMarshal(t, HackatomExampleInitMsg{Verifier: verifierAddr, Beneficiary: beneficiaryAddr})
otherAddr := keepers.Faucet.NewFundedRandomAccount(parentCtx, sdk.NewInt64Coin("denom", 1_000_000_000))
const (
mySalt = "my salt"
myLabel = "my label"
)
// create instances for duplicate checks
exampleContract := func(t *testing.T, ctx sdk.Context, fixMsg bool) {
_, _, err := keepers.ContractKeeper.Instantiate2(
ctx,
example.CodeID,
example.CreatorAddr,
nil,
initMsg,
myLabel,
sdk.NewCoins(sdk.NewInt64Coin("denom", 1)),
[]byte(mySalt),
fixMsg,
)
require.NoError(t, err)
}
exampleWithFixMsg := func(t *testing.T, ctx sdk.Context) {
exampleContract(t, ctx, true)
}
exampleWithoutFixMsg := func(t *testing.T, ctx sdk.Context) {
exampleContract(t, ctx, false)
}
specs := map[string]struct {
setup func(t *testing.T, ctx sdk.Context)
codeID uint64
sender sdk.AccAddress
salt []byte
initMsg json.RawMessage
fixMsg bool
expErr error
}{
"fix msg - generates different address than without fixMsg": {
setup: exampleWithoutFixMsg,
codeID: example.CodeID,
sender: example.CreatorAddr,
salt: []byte(mySalt),
initMsg: initMsg,
fixMsg: true,
},
"fix msg - different sender": {
setup: exampleWithFixMsg,
codeID: example.CodeID,
sender: otherAddr,
salt: []byte(mySalt),
initMsg: initMsg,
fixMsg: true,
},
"fix msg - different code": {
setup: exampleWithFixMsg,
codeID: otherExample.CodeID,
sender: example.CreatorAddr,
salt: []byte(mySalt),
initMsg: []byte(`{}`),
fixMsg: true,
},
"fix msg - different salt": {
setup: exampleWithFixMsg,
codeID: example.CodeID,
sender: example.CreatorAddr,
salt: []byte("other salt"),
initMsg: initMsg,
fixMsg: true,
},
"fix msg - different init msg": {
setup: exampleWithFixMsg,
codeID: example.CodeID,
sender: example.CreatorAddr,
salt: []byte(mySalt),
initMsg: mustMarshal(t, HackatomExampleInitMsg{Verifier: otherAddr, Beneficiary: beneficiaryAddr}),
fixMsg: true,
},
"different sender": {
setup: exampleWithoutFixMsg,
codeID: example.CodeID,
sender: otherAddr,
salt: []byte(mySalt),
initMsg: initMsg,
},
"different code": {
setup: exampleWithoutFixMsg,
codeID: otherExample.CodeID,
sender: example.CreatorAddr,
salt: []byte(mySalt),
initMsg: []byte(`{}`),
},
"different salt": {
setup: exampleWithoutFixMsg,
codeID: example.CodeID,
sender: example.CreatorAddr,
salt: []byte(`other salt`),
initMsg: initMsg,
},
"different init msg - reject same address": {
setup: exampleWithoutFixMsg,
codeID: example.CodeID,
sender: example.CreatorAddr,
salt: []byte(mySalt),
initMsg: mustMarshal(t, HackatomExampleInitMsg{Verifier: otherAddr, Beneficiary: beneficiaryAddr}),
expErr: types.ErrDuplicate,
},
"fix msg - long msg": {
setup: exampleWithFixMsg,
codeID: example.CodeID,
sender: otherAddr,
salt: []byte(mySalt),
initMsg: []byte(fmt.Sprintf(`{"foo":%q}`, strings.Repeat("b", math.MaxInt16+1))), // too long kills CI
fixMsg: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
ctx, _ := parentCtx.CacheContext()
spec.setup(t, ctx)
gotAddr, _, gotErr := keepers.ContractKeeper.Instantiate2(
ctx,
spec.codeID,
spec.sender,
nil,
spec.initMsg,
myLabel,
sdk.NewCoins(sdk.NewInt64Coin("denom", 2)),
spec.salt,
spec.fixMsg,
)
if spec.expErr != nil {
assert.ErrorIs(t, gotErr, spec.expErr)
return
}
require.NoError(t, gotErr)
assert.NotEmpty(t, gotAddr)
})
}
}

67
x/wasm/keeper/events.go Normal file
View File

@ -0,0 +1,67 @@
package keeper
import (
"fmt"
"strings"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// newWasmModuleEvent creates with wasm module event for interacting with the given contract. Adds custom attributes
// to this event.
func newWasmModuleEvent(customAttributes []wasmvmtypes.EventAttribute, contractAddr sdk.AccAddress) (sdk.Events, error) {
attrs, err := contractSDKEventAttributes(customAttributes, contractAddr)
if err != nil {
return nil, err
}
// each wasm invocation always returns one sdk.Event
return sdk.Events{sdk.NewEvent(types.WasmModuleEventType, attrs...)}, nil
}
const eventTypeMinLength = 2
// newCustomEvents converts wasmvm events from a contract response to sdk type events
func newCustomEvents(evts wasmvmtypes.Events, contractAddr sdk.AccAddress) (sdk.Events, error) {
events := make(sdk.Events, 0, len(evts))
for _, e := range evts {
typ := strings.TrimSpace(e.Type)
if len(typ) <= eventTypeMinLength {
return nil, sdkerrors.Wrap(types.ErrInvalidEvent, fmt.Sprintf("Event type too short: '%s'", typ))
}
attributes, err := contractSDKEventAttributes(e.Attributes, contractAddr)
if err != nil {
return nil, err
}
events = append(events, sdk.NewEvent(fmt.Sprintf("%s%s", types.CustomContractEventPrefix, typ), attributes...))
}
return events, nil
}
// convert and add contract address issuing this event
func contractSDKEventAttributes(customAttributes []wasmvmtypes.EventAttribute, contractAddr sdk.AccAddress) ([]sdk.Attribute, error) {
attrs := []sdk.Attribute{sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddr.String())}
// append attributes from wasm to the sdk.Event
for _, l := range customAttributes {
// ensure key and value are non-empty (and trim what is there)
key := strings.TrimSpace(l.Key)
if len(key) == 0 {
return nil, sdkerrors.Wrap(types.ErrInvalidEvent, fmt.Sprintf("Empty attribute key. Value: %s", l.Value))
}
value := strings.TrimSpace(l.Value)
// TODO: check if this is legal in the SDK - if it is, we can remove this check
if len(value) == 0 {
return nil, sdkerrors.Wrap(types.ErrInvalidEvent, fmt.Sprintf("Empty attribute value. Key: %s", key))
}
// and reserve all _* keys for our use (not contract)
if strings.HasPrefix(key, types.AttributeReservedPrefix) {
return nil, sdkerrors.Wrap(types.ErrInvalidEvent, fmt.Sprintf("Attribute key starts with reserved prefix %s: '%s'", types.AttributeReservedPrefix, key))
}
attrs = append(attrs, sdk.NewAttribute(key, value))
}
return attrs, nil
}

View File

@ -0,0 +1,290 @@
package keeper
import (
"context"
"testing"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestHasWasmModuleEvent(t *testing.T) {
myContractAddr := RandomAccountAddress(t)
specs := map[string]struct {
srcEvents []sdk.Event
exp bool
}{
"event found": {
srcEvents: []sdk.Event{
sdk.NewEvent(types.WasmModuleEventType, sdk.NewAttribute("_contract_address", myContractAddr.String())),
},
exp: true,
},
"different event: not found": {
srcEvents: []sdk.Event{
sdk.NewEvent(types.CustomContractEventPrefix, sdk.NewAttribute("_contract_address", myContractAddr.String())),
},
exp: false,
},
"event with different address: not found": {
srcEvents: []sdk.Event{
sdk.NewEvent(types.WasmModuleEventType, sdk.NewAttribute("_contract_address", RandomBech32AccountAddress(t))),
},
exp: false,
},
"no event": {
srcEvents: []sdk.Event{},
exp: false,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
em := sdk.NewEventManager()
em.EmitEvents(spec.srcEvents)
ctx := sdk.Context{}.WithContext(context.Background()).WithEventManager(em)
got := hasWasmModuleEvent(ctx, myContractAddr)
assert.Equal(t, spec.exp, got)
})
}
}
func TestNewCustomEvents(t *testing.T) {
myContract := RandomAccountAddress(t)
specs := map[string]struct {
src wasmvmtypes.Events
exp sdk.Events
isError bool
}{
"all good": {
src: wasmvmtypes.Events{{
Type: "foo",
Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myVal"}},
}},
exp: sdk.Events{sdk.NewEvent("wasm-foo",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("myKey", "myVal"))},
},
"multiple attributes": {
src: wasmvmtypes.Events{{
Type: "foo",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "myKey", Value: "myVal"},
{Key: "myOtherKey", Value: "myOtherVal"},
},
}},
exp: sdk.Events{sdk.NewEvent("wasm-foo",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("myKey", "myVal"),
sdk.NewAttribute("myOtherKey", "myOtherVal"))},
},
"multiple events": {
src: wasmvmtypes.Events{{
Type: "foo",
Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myVal"}},
}, {
Type: "bar",
Attributes: []wasmvmtypes.EventAttribute{{Key: "otherKey", Value: "otherVal"}},
}},
exp: sdk.Events{
sdk.NewEvent("wasm-foo",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("myKey", "myVal")),
sdk.NewEvent("wasm-bar",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("otherKey", "otherVal")),
},
},
"without attributes": {
src: wasmvmtypes.Events{{
Type: "foo",
}},
exp: sdk.Events{sdk.NewEvent("wasm-foo",
sdk.NewAttribute("_contract_address", myContract.String()))},
},
"error on short event type": {
src: wasmvmtypes.Events{{
Type: "f",
}},
isError: true,
},
"error on _contract_address": {
src: wasmvmtypes.Events{{
Type: "foo",
Attributes: []wasmvmtypes.EventAttribute{{Key: "_contract_address", Value: RandomBech32AccountAddress(t)}},
}},
isError: true,
},
"error on reserved prefix": {
src: wasmvmtypes.Events{{
Type: "wasm",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "_reserved", Value: "is skipped"},
{Key: "normal", Value: "is used"},
},
}},
isError: true,
},
"error on empty value": {
src: wasmvmtypes.Events{{
Type: "boom",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "some", Value: "data"},
{Key: "key", Value: ""},
},
}},
isError: true,
},
"error on empty key": {
src: wasmvmtypes.Events{{
Type: "boom",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "some", Value: "data"},
{Key: "", Value: "value"},
},
}},
isError: true,
},
"error on whitespace type": {
src: wasmvmtypes.Events{{
Type: " f ",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "some", Value: "data"},
},
}},
isError: true,
},
"error on only whitespace key": {
src: wasmvmtypes.Events{{
Type: "boom",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "some", Value: "data"},
{Key: "\n\n\n\n", Value: "value"},
},
}},
isError: true,
},
"error on only whitespace value": {
src: wasmvmtypes.Events{{
Type: "boom",
Attributes: []wasmvmtypes.EventAttribute{
{Key: "some", Value: "data"},
{Key: "myKey", Value: " \t\r\n"},
},
}},
isError: true,
},
"strip out whitespace": {
src: wasmvmtypes.Events{{
Type: " food\n",
Attributes: []wasmvmtypes.EventAttribute{{Key: "my Key", Value: "\tmyVal"}},
}},
exp: sdk.Events{sdk.NewEvent("wasm-food",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("my Key", "myVal"))},
},
"empty event elements": {
src: make(wasmvmtypes.Events, 10),
isError: true,
},
"nil": {
exp: sdk.Events{},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
gotEvent, err := newCustomEvents(spec.src, myContract)
if spec.isError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, spec.exp, gotEvent)
}
})
}
}
func TestNewWasmModuleEvent(t *testing.T) {
myContract := RandomAccountAddress(t)
specs := map[string]struct {
src []wasmvmtypes.EventAttribute
exp sdk.Events
isError bool
}{
"all good": {
src: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myVal"}},
exp: sdk.Events{sdk.NewEvent("wasm",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("myKey", "myVal"))},
},
"multiple attributes": {
src: []wasmvmtypes.EventAttribute{
{Key: "myKey", Value: "myVal"},
{Key: "myOtherKey", Value: "myOtherVal"},
},
exp: sdk.Events{sdk.NewEvent("wasm",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("myKey", "myVal"),
sdk.NewAttribute("myOtherKey", "myOtherVal"))},
},
"without attributes": {
exp: sdk.Events{sdk.NewEvent("wasm",
sdk.NewAttribute("_contract_address", myContract.String()))},
},
"error on _contract_address": {
src: []wasmvmtypes.EventAttribute{{Key: "_contract_address", Value: RandomBech32AccountAddress(t)}},
isError: true,
},
"error on whitespace key": {
src: []wasmvmtypes.EventAttribute{{Key: " ", Value: "value"}},
isError: true,
},
"error on whitespace value": {
src: []wasmvmtypes.EventAttribute{{Key: "key", Value: "\n\n\n"}},
isError: true,
},
"strip whitespace": {
src: []wasmvmtypes.EventAttribute{{Key: " my-real-key ", Value: "\n\n\nsome-val\t\t\t"}},
exp: sdk.Events{sdk.NewEvent("wasm",
sdk.NewAttribute("_contract_address", myContract.String()),
sdk.NewAttribute("my-real-key", "some-val"))},
},
"empty elements": {
src: make([]wasmvmtypes.EventAttribute, 10),
isError: true,
},
"nil": {
exp: sdk.Events{sdk.NewEvent("wasm",
sdk.NewAttribute("_contract_address", myContract.String()),
)},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
gotEvent, err := newWasmModuleEvent(spec.src, myContract)
if spec.isError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, spec.exp, gotEvent)
}
})
}
}
// returns true when a wasm module event was emitted for this contract already
func hasWasmModuleEvent(ctx sdk.Context, contractAddr sdk.AccAddress) bool {
for _, e := range ctx.EventManager().Events() {
if e.Type == types.WasmModuleEventType {
for _, a := range e.Attributes {
if string(a.Key) == types.AttributeKeyContractAddr && string(a.Value) == contractAddr.String() {
return true
}
}
}
}
return false
}

View File

@ -0,0 +1,252 @@
package keeper
import (
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cerc-io/laconicd/x/wasm/types"
)
const (
// DefaultGasMultiplier is how many CosmWasm gas points = 1 Cosmos SDK gas point.
//
// CosmWasm gas strategy is documented in https://github.com/CosmWasm/cosmwasm/blob/v1.0.0-beta/docs/GAS.md.
// Cosmos SDK reference costs can be found here: https://github.com/cosmos/cosmos-sdk/blob/v0.42.10/store/types/gas.go#L198-L209.
//
// The original multiplier of 100 up to CosmWasm 0.16 was based on
// "A write at ~3000 gas and ~200us = 10 gas per us (microsecond) cpu/io
// Rough timing have 88k gas at 90us, which is equal to 1k sdk gas... (one read)"
// as well as manual Wasmer benchmarks from 2019. This was then multiplied by 150_000
// in the 0.16 -> 1.0 upgrade (https://github.com/CosmWasm/cosmwasm/pull/1120).
//
// The multiplier deserves more reproducible benchmarking and a strategy that allows easy adjustments.
// This is tracked in https://github.com/CosmWasm/wasmd/issues/566 and https://github.com/CosmWasm/wasmd/issues/631.
// Gas adjustments are consensus breaking but may happen in any release marked as consensus breaking.
// Do not make assumptions on how much gas an operation will consume in places that are hard to adjust,
// such as hardcoding them in contracts.
//
// Please note that all gas prices returned to wasmvm should have this multiplied.
// Benchmarks and numbers were discussed in: https://github.com/CosmWasm/wasmd/pull/634#issuecomment-938055852
DefaultGasMultiplier uint64 = 140_000_000
// DefaultInstanceCost is how much SDK gas we charge each time we load a WASM instance.
// Creating a new instance is costly, and this helps put a recursion limit to contracts calling contracts.
// Benchmarks and numbers were discussed in: https://github.com/CosmWasm/wasmd/pull/634#issuecomment-938056803
DefaultInstanceCost uint64 = 60_000
// DefaultCompileCost is how much SDK gas is charged *per byte* for compiling WASM code.
// Benchmarks and numbers were discussed in: https://github.com/CosmWasm/wasmd/pull/634#issuecomment-938056803
DefaultCompileCost uint64 = 3
// DefaultEventAttributeDataCost is how much SDK gas is charged *per byte* for attribute data in events.
// This is used with len(key) + len(value)
DefaultEventAttributeDataCost uint64 = 1
// DefaultContractMessageDataCost is how much SDK gas is charged *per byte* of the message that goes to the contract
// This is used with len(msg). Note that the message is deserialized in the receiving contract and this is charged
// with wasm gas already. The derserialization of results is also charged in wasmvm. I am unsure if we need to add
// additional costs here.
// Note: also used for error fields on reply, and data on reply. Maybe these should be pulled out to a different (non-zero) field
DefaultContractMessageDataCost uint64 = 0
// DefaultPerAttributeCost is how much SDK gas we charge per attribute count.
DefaultPerAttributeCost uint64 = 10
// DefaultPerCustomEventCost is how much SDK gas we charge per event count.
DefaultPerCustomEventCost uint64 = 20
// DefaultEventAttributeDataFreeTier number of bytes of total attribute data we do not charge.
DefaultEventAttributeDataFreeTier = 100
)
// default: 0.15 gas.
// see https://github.com/CosmWasm/wasmd/pull/898#discussion_r937727200
var defaultPerByteUncompressCost = wasmvmtypes.UFraction{
Numerator: 15,
Denominator: 100,
}
// DefaultPerByteUncompressCost is how much SDK gas we charge per source byte to unpack
func DefaultPerByteUncompressCost() wasmvmtypes.UFraction {
return defaultPerByteUncompressCost
}
// GasRegister abstract source for gas costs
type GasRegister interface {
// NewContractInstanceCosts costs to crate a new contract instance from code
NewContractInstanceCosts(pinned bool, msgLen int) sdk.Gas
// CompileCosts costs to persist and "compile" a new wasm contract
CompileCosts(byteLength int) sdk.Gas
// UncompressCosts costs to unpack a new wasm contract
UncompressCosts(byteLength int) sdk.Gas
// InstantiateContractCosts costs when interacting with a wasm contract
InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas
// ReplyCosts costs to to handle a message reply
ReplyCosts(pinned bool, reply wasmvmtypes.Reply) sdk.Gas
// EventCosts costs to persist an event
EventCosts(attrs []wasmvmtypes.EventAttribute, events wasmvmtypes.Events) sdk.Gas
// ToWasmVMGas converts from sdk gas to wasmvm gas
ToWasmVMGas(source sdk.Gas) uint64
// FromWasmVMGas converts from wasmvm gas to sdk gas
FromWasmVMGas(source uint64) sdk.Gas
}
// WasmGasRegisterConfig config type
type WasmGasRegisterConfig struct {
// InstanceCost costs when interacting with a wasm contract
InstanceCost sdk.Gas
// CompileCosts costs to persist and "compile" a new wasm contract
CompileCost sdk.Gas
// UncompressCost costs per byte to unpack a contract
UncompressCost wasmvmtypes.UFraction
// GasMultiplier is how many cosmwasm gas points = 1 sdk gas point
// SDK reference costs can be found here: https://github.com/cosmos/cosmos-sdk/blob/02c6c9fafd58da88550ab4d7d494724a477c8a68/store/types/gas.go#L153-L164
GasMultiplier sdk.Gas
// EventPerAttributeCost is how much SDK gas is charged *per byte* for attribute data in events.
// This is used with len(key) + len(value)
EventPerAttributeCost sdk.Gas
// EventAttributeDataCost is how much SDK gas is charged *per byte* for attribute data in events.
// This is used with len(key) + len(value)
EventAttributeDataCost sdk.Gas
// EventAttributeDataFreeTier number of bytes of total attribute data that is free of charge
EventAttributeDataFreeTier uint64
// ContractMessageDataCost SDK gas charged *per byte* of the message that goes to the contract
// This is used with len(msg)
ContractMessageDataCost sdk.Gas
// CustomEventCost cost per custom event
CustomEventCost uint64
}
// DefaultGasRegisterConfig default values
func DefaultGasRegisterConfig() WasmGasRegisterConfig {
return WasmGasRegisterConfig{
InstanceCost: DefaultInstanceCost,
CompileCost: DefaultCompileCost,
GasMultiplier: DefaultGasMultiplier,
EventPerAttributeCost: DefaultPerAttributeCost,
CustomEventCost: DefaultPerCustomEventCost,
EventAttributeDataCost: DefaultEventAttributeDataCost,
EventAttributeDataFreeTier: DefaultEventAttributeDataFreeTier,
ContractMessageDataCost: DefaultContractMessageDataCost,
UncompressCost: DefaultPerByteUncompressCost(),
}
}
// WasmGasRegister implements GasRegister interface
type WasmGasRegister struct {
c WasmGasRegisterConfig
}
// NewDefaultWasmGasRegister creates instance with default values
func NewDefaultWasmGasRegister() WasmGasRegister {
return NewWasmGasRegister(DefaultGasRegisterConfig())
}
// NewWasmGasRegister constructor
func NewWasmGasRegister(c WasmGasRegisterConfig) WasmGasRegister {
if c.GasMultiplier == 0 {
panic(sdkerrors.Wrap(sdkerrors.ErrLogic, "GasMultiplier can not be 0"))
}
return WasmGasRegister{
c: c,
}
}
// NewContractInstanceCosts costs to crate a new contract instance from code
func (g WasmGasRegister) NewContractInstanceCosts(pinned bool, msgLen int) storetypes.Gas {
return g.InstantiateContractCosts(pinned, msgLen)
}
// CompileCosts costs to persist and "compile" a new wasm contract
func (g WasmGasRegister) CompileCosts(byteLength int) storetypes.Gas {
if byteLength < 0 {
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
}
return g.c.CompileCost * uint64(byteLength)
}
// UncompressCosts costs to unpack a new wasm contract
func (g WasmGasRegister) UncompressCosts(byteLength int) sdk.Gas {
if byteLength < 0 {
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
}
return g.c.UncompressCost.Mul(uint64(byteLength)).Floor()
}
// InstantiateContractCosts costs when interacting with a wasm contract
func (g WasmGasRegister) InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas {
if msgLen < 0 {
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
}
dataCosts := sdk.Gas(msgLen) * g.c.ContractMessageDataCost
if pinned {
return dataCosts
}
return g.c.InstanceCost + dataCosts
}
// ReplyCosts costs to to handle a message reply
func (g WasmGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) sdk.Gas {
var eventGas sdk.Gas
msgLen := len(reply.Result.Err)
if reply.Result.Ok != nil {
msgLen += len(reply.Result.Ok.Data)
var attrs []wasmvmtypes.EventAttribute
for _, e := range reply.Result.Ok.Events {
eventGas += sdk.Gas(len(e.Type)) * g.c.EventAttributeDataCost
attrs = append(attrs, e.Attributes...)
}
// apply free tier on the whole set not per event
eventGas += g.EventCosts(attrs, nil)
}
return eventGas + g.InstantiateContractCosts(pinned, msgLen)
}
// EventCosts costs to persist an event
func (g WasmGasRegister) EventCosts(attrs []wasmvmtypes.EventAttribute, events wasmvmtypes.Events) sdk.Gas {
gas, remainingFreeTier := g.eventAttributeCosts(attrs, g.c.EventAttributeDataFreeTier)
for _, e := range events {
gas += g.c.CustomEventCost
gas += sdk.Gas(len(e.Type)) * g.c.EventAttributeDataCost // no free tier with event type
var attrCost sdk.Gas
attrCost, remainingFreeTier = g.eventAttributeCosts(e.Attributes, remainingFreeTier)
gas += attrCost
}
return gas
}
func (g WasmGasRegister) eventAttributeCosts(attrs []wasmvmtypes.EventAttribute, freeTier uint64) (sdk.Gas, uint64) {
if len(attrs) == 0 {
return 0, freeTier
}
var storedBytes uint64
for _, l := range attrs {
storedBytes += uint64(len(l.Key)) + uint64(len(l.Value))
}
storedBytes, freeTier = calcWithFreeTier(storedBytes, freeTier)
// total Length * costs + attribute count * costs
r := sdk.NewIntFromUint64(g.c.EventAttributeDataCost).Mul(sdk.NewIntFromUint64(storedBytes)).
Add(sdk.NewIntFromUint64(g.c.EventPerAttributeCost).Mul(sdk.NewIntFromUint64(uint64(len(attrs)))))
if !r.IsUint64() {
panic(sdk.ErrorOutOfGas{Descriptor: "overflow"})
}
return r.Uint64(), freeTier
}
// apply free tier
func calcWithFreeTier(storedBytes uint64, freeTier uint64) (uint64, uint64) {
if storedBytes <= freeTier {
return 0, freeTier - storedBytes
}
storedBytes -= freeTier
return storedBytes, 0
}
// ToWasmVMGas convert to wasmVM contract runtime gas unit
func (g WasmGasRegister) ToWasmVMGas(source storetypes.Gas) uint64 {
x := source * g.c.GasMultiplier
if x < source {
panic(sdk.ErrorOutOfGas{Descriptor: "overflow"})
}
return x
}
// FromWasmVMGas converts to SDK gas unit
func (g WasmGasRegister) FromWasmVMGas(source uint64) sdk.Gas {
return source / g.c.GasMultiplier
}

View File

@ -0,0 +1,472 @@
package keeper
import (
"math"
"strings"
"testing"
"github.com/cerc-io/laconicd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
)
func TestCompileCosts(t *testing.T) {
specs := map[string]struct {
srcLen int
srcConfig WasmGasRegisterConfig
exp sdk.Gas
expPanic bool
}{
"one byte": {
srcLen: 1,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(3), // DefaultCompileCost
},
"zero byte": {
srcLen: 0,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(0),
},
"negative len": {
srcLen: -1,
srcConfig: DefaultGasRegisterConfig(),
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
NewWasmGasRegister(spec.srcConfig).CompileCosts(spec.srcLen)
})
return
}
gotGas := NewWasmGasRegister(spec.srcConfig).CompileCosts(spec.srcLen)
assert.Equal(t, spec.exp, gotGas)
})
}
}
func TestNewContractInstanceCosts(t *testing.T) {
specs := map[string]struct {
srcLen int
srcConfig WasmGasRegisterConfig
pinned bool
exp sdk.Gas
expPanic bool
}{
"small msg - pinned": {
srcLen: 1,
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: DefaultContractMessageDataCost,
},
"big msg - pinned": {
srcLen: math.MaxUint32,
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: DefaultContractMessageDataCost * sdk.Gas(math.MaxUint32),
},
"empty msg - pinned": {
srcLen: 0,
pinned: true,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(0),
},
"small msg - unpinned": {
srcLen: 1,
srcConfig: DefaultGasRegisterConfig(),
exp: DefaultContractMessageDataCost + DefaultInstanceCost,
},
"big msg - unpinned": {
srcLen: math.MaxUint32,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultContractMessageDataCost*math.MaxUint32 + DefaultInstanceCost),
},
"empty msg - unpinned": {
srcLen: 0,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost),
},
"negative len": {
srcLen: -1,
srcConfig: DefaultGasRegisterConfig(),
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
NewWasmGasRegister(spec.srcConfig).NewContractInstanceCosts(spec.pinned, spec.srcLen)
})
return
}
gotGas := NewWasmGasRegister(spec.srcConfig).NewContractInstanceCosts(spec.pinned, spec.srcLen)
assert.Equal(t, spec.exp, gotGas)
})
}
}
func TestContractInstanceCosts(t *testing.T) {
// same as TestNewContractInstanceCosts currently
specs := map[string]struct {
srcLen int
srcConfig WasmGasRegisterConfig
pinned bool
exp sdk.Gas
expPanic bool
}{
"small msg - pinned": {
srcLen: 1,
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: DefaultContractMessageDataCost,
},
"big msg - pinned": {
srcLen: math.MaxUint32,
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: sdk.Gas(DefaultContractMessageDataCost * math.MaxUint32),
},
"empty msg - pinned": {
srcLen: 0,
pinned: true,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(0),
},
"small msg - unpinned": {
srcLen: 1,
srcConfig: DefaultGasRegisterConfig(),
exp: DefaultContractMessageDataCost + DefaultInstanceCost,
},
"big msg - unpinned": {
srcLen: math.MaxUint32,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultContractMessageDataCost*math.MaxUint32 + DefaultInstanceCost),
},
"empty msg - unpinned": {
srcLen: 0,
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost),
},
"negative len": {
srcLen: -1,
srcConfig: DefaultGasRegisterConfig(),
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
NewWasmGasRegister(spec.srcConfig).InstantiateContractCosts(spec.pinned, spec.srcLen)
})
return
}
gotGas := NewWasmGasRegister(spec.srcConfig).InstantiateContractCosts(spec.pinned, spec.srcLen)
assert.Equal(t, spec.exp, gotGas)
})
}
}
func TestReplyCost(t *testing.T) {
specs := map[string]struct {
src wasmvmtypes.Reply
srcConfig WasmGasRegisterConfig
pinned bool
exp sdk.Gas
expPanic bool
}{
"subcall response with events and data - pinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
},
Data: []byte{0x1},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: sdk.Gas(3*DefaultEventAttributeDataCost + DefaultPerAttributeCost + DefaultContractMessageDataCost), // 3 == len("foo")
},
"subcall response with events - pinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: sdk.Gas(3*DefaultEventAttributeDataCost + DefaultPerAttributeCost), // 3 == len("foo")
},
"subcall response with events exceeds free tier- pinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: strings.Repeat("x", DefaultEventAttributeDataFreeTier), Value: "myData"}}},
},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: sdk.Gas((3+6)*DefaultEventAttributeDataCost + DefaultPerAttributeCost), // 3 == len("foo"), 6 == len("myData")
},
"subcall response error - pinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Err: "foo",
},
},
srcConfig: DefaultGasRegisterConfig(),
pinned: true,
exp: 3 * DefaultContractMessageDataCost,
},
"subcall response with events and data - unpinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
},
Data: []byte{0x1},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost + 3*DefaultEventAttributeDataCost + DefaultPerAttributeCost + DefaultContractMessageDataCost),
},
"subcall response with events - unpinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost + 3*DefaultEventAttributeDataCost + DefaultPerAttributeCost),
},
"subcall response with events exceeds free tier- unpinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: []wasmvmtypes.Event{
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: strings.Repeat("x", DefaultEventAttributeDataFreeTier), Value: "myData"}}},
},
},
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost + (3+6)*DefaultEventAttributeDataCost + DefaultPerAttributeCost), // 3 == len("foo"), 6 == len("myData")
},
"subcall response error - unpinned": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Err: "foo",
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: sdk.Gas(DefaultInstanceCost + 3*DefaultContractMessageDataCost),
},
"subcall response with empty events": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{
Events: make([]wasmvmtypes.Event, 10),
},
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: DefaultInstanceCost,
},
"subcall response with events unset": {
src: wasmvmtypes.Reply{
Result: wasmvmtypes.SubMsgResult{
Ok: &wasmvmtypes.SubMsgResponse{},
},
},
srcConfig: DefaultGasRegisterConfig(),
exp: DefaultInstanceCost,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
NewWasmGasRegister(spec.srcConfig).ReplyCosts(spec.pinned, spec.src)
})
return
}
gotGas := NewWasmGasRegister(spec.srcConfig).ReplyCosts(spec.pinned, spec.src)
assert.Equal(t, spec.exp, gotGas)
})
}
}
func TestEventCosts(t *testing.T) {
// most cases are covered in TestReplyCost already. This ensures some edge cases
specs := map[string]struct {
srcAttrs []wasmvmtypes.EventAttribute
srcEvents wasmvmtypes.Events
expGas sdk.Gas
}{
"empty events": {
srcEvents: make([]wasmvmtypes.Event, 1),
expGas: DefaultPerCustomEventCost,
},
"empty attributes": {
srcAttrs: make([]wasmvmtypes.EventAttribute, 1),
expGas: DefaultPerAttributeCost,
},
"both nil": {
expGas: 0,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
gotGas := NewDefaultWasmGasRegister().EventCosts(spec.srcAttrs, spec.srcEvents)
assert.Equal(t, spec.expGas, gotGas)
})
}
}
func TestToWasmVMGasConversion(t *testing.T) {
specs := map[string]struct {
src storetypes.Gas
srcConfig WasmGasRegisterConfig
exp uint64
expPanic bool
}{
"0": {
src: 0,
exp: 0,
srcConfig: DefaultGasRegisterConfig(),
},
"max": {
srcConfig: WasmGasRegisterConfig{
GasMultiplier: 1,
},
src: math.MaxUint64,
exp: math.MaxUint64,
},
"overflow": {
srcConfig: WasmGasRegisterConfig{
GasMultiplier: 2,
},
src: math.MaxUint64,
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
r := NewWasmGasRegister(spec.srcConfig)
_ = r.ToWasmVMGas(spec.src)
})
return
}
r := NewWasmGasRegister(spec.srcConfig)
got := r.ToWasmVMGas(spec.src)
assert.Equal(t, spec.exp, got)
})
}
}
func TestFromWasmVMGasConversion(t *testing.T) {
specs := map[string]struct {
src uint64
exp storetypes.Gas
srcConfig WasmGasRegisterConfig
expPanic bool
}{
"0": {
src: 0,
exp: 0,
srcConfig: DefaultGasRegisterConfig(),
},
"max": {
srcConfig: WasmGasRegisterConfig{
GasMultiplier: 1,
},
src: math.MaxUint64,
exp: math.MaxUint64,
},
"missconfigured": {
srcConfig: WasmGasRegisterConfig{
GasMultiplier: 0,
},
src: 1,
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() {
r := NewWasmGasRegister(spec.srcConfig)
_ = r.FromWasmVMGas(spec.src)
})
return
}
r := NewWasmGasRegister(spec.srcConfig)
got := r.FromWasmVMGas(spec.src)
assert.Equal(t, spec.exp, got)
})
}
}
func TestUncompressCosts(t *testing.T) {
specs := map[string]struct {
lenIn int
exp sdk.Gas
expPanic bool
}{
"0": {
exp: 0,
},
"even": {
lenIn: 100,
exp: 15,
},
"round down when uneven": {
lenIn: 19,
exp: 2,
},
"max len": {
lenIn: types.MaxWasmSize,
exp: 122880,
},
"invalid len": {
lenIn: -1,
expPanic: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
if spec.expPanic {
assert.Panics(t, func() { NewDefaultWasmGasRegister().UncompressCosts(spec.lenIn) })
return
}
got := NewDefaultWasmGasRegister().UncompressCosts(spec.lenIn)
assert.Equal(t, spec.exp, got)
})
}
}

116
x/wasm/keeper/genesis.go Normal file
View File

@ -0,0 +1,116 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// ValidatorSetSource is a subset of the staking keeper
type ValidatorSetSource interface {
ApplyAndReturnValidatorSetUpdates(sdk.Context) (updates []abci.ValidatorUpdate, err error)
}
// InitGenesis sets supply information for genesis.
//
// CONTRACT: all types of accounts must have been already initialized/created
func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState) ([]abci.ValidatorUpdate, error) {
contractKeeper := NewGovPermissionKeeper(keeper)
keeper.SetParams(ctx, data.Params)
var maxCodeID uint64
for i, code := range data.Codes {
err := keeper.importCode(ctx, code.CodeID, code.CodeInfo, code.CodeBytes)
if err != nil {
return nil, sdkerrors.Wrapf(err, "code %d with id: %d", i, code.CodeID)
}
if code.CodeID > maxCodeID {
maxCodeID = code.CodeID
}
if code.Pinned {
if err := contractKeeper.PinCode(ctx, code.CodeID); err != nil {
return nil, sdkerrors.Wrapf(err, "contract number %d", i)
}
}
}
var maxContractID int
for i, contract := range data.Contracts {
contractAddr, err := sdk.AccAddressFromBech32(contract.ContractAddress)
if err != nil {
return nil, sdkerrors.Wrapf(err, "address in contract number %d", i)
}
err = keeper.importContract(ctx, contractAddr, &contract.ContractInfo, contract.ContractState, contract.ContractCodeHistory)
if err != nil {
return nil, sdkerrors.Wrapf(err, "contract number %d", i)
}
maxContractID = i + 1 // not ideal but max(contractID) is not persisted otherwise
}
for i, seq := range data.Sequences {
err := keeper.importAutoIncrementID(ctx, seq.IDKey, seq.Value)
if err != nil {
return nil, sdkerrors.Wrapf(err, "sequence number %d", i)
}
}
// sanity check seq values
seqVal := keeper.PeekAutoIncrementID(ctx, types.KeyLastCodeID)
if seqVal <= maxCodeID {
return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s with value: %d must be greater than: %d ", string(types.KeyLastCodeID), seqVal, maxCodeID)
}
seqVal = keeper.PeekAutoIncrementID(ctx, types.KeyLastInstanceID)
if seqVal <= uint64(maxContractID) {
return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s with value: %d must be greater than: %d ", string(types.KeyLastInstanceID), seqVal, maxContractID)
}
return nil, nil
}
// ExportGenesis returns a GenesisState for a given context and keeper.
func ExportGenesis(ctx sdk.Context, keeper *Keeper) *types.GenesisState {
var genState types.GenesisState
genState.Params = keeper.GetParams(ctx)
keeper.IterateCodeInfos(ctx, func(codeID uint64, info types.CodeInfo) bool {
bytecode, err := keeper.GetByteCode(ctx, codeID)
if err != nil {
panic(err)
}
genState.Codes = append(genState.Codes, types.Code{
CodeID: codeID,
CodeInfo: info,
CodeBytes: bytecode,
Pinned: keeper.IsPinnedCode(ctx, codeID),
})
return false
})
keeper.IterateContractInfo(ctx, func(addr sdk.AccAddress, contract types.ContractInfo) bool {
var state []types.Model
keeper.IterateContractState(ctx, addr, func(key, value []byte) bool {
state = append(state, types.Model{Key: key, Value: value})
return false
})
contractCodeHistory := keeper.GetContractHistory(ctx, addr)
genState.Contracts = append(genState.Contracts, types.Contract{
ContractAddress: addr.String(),
ContractInfo: contract,
ContractState: state,
ContractCodeHistory: contractCodeHistory,
})
return false
})
for _, k := range [][]byte{types.KeyLastCodeID, types.KeyLastInstanceID} {
genState.Sequences = append(genState.Sequences, types.Sequence{
IDKey: k,
Value: keeper.PeekAutoIncrementID(ctx, k),
})
}
return &genState
}

View File

@ -0,0 +1,671 @@
package keeper
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"math/rand"
"os"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
"github.com/cerc-io/laconicd/x/wasm/types"
wasmTypes "github.com/cerc-io/laconicd/x/wasm/types"
)
const firstCodeID = 1
func TestGenesisExportImport(t *testing.T) {
wasmKeeper, srcCtx, srcStoreKeys := setupKeeper(t)
contractKeeper := NewGovPermissionKeeper(wasmKeeper)
wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
// store some test data
f := fuzz.New().Funcs(ModelFuzzers...)
wasmKeeper.SetParams(srcCtx, types.DefaultParams())
for i := 0; i < 25; i++ {
var (
codeInfo types.CodeInfo
contract types.ContractInfo
stateModels []types.Model
history []types.ContractCodeHistoryEntry
pinned bool
contractExtension bool
)
f.Fuzz(&codeInfo)
f.Fuzz(&contract)
f.Fuzz(&stateModels)
f.NilChance(0).Fuzz(&history)
f.Fuzz(&pinned)
f.Fuzz(&contractExtension)
creatorAddr, err := sdk.AccAddressFromBech32(codeInfo.Creator)
require.NoError(t, err)
codeID, _, err := contractKeeper.Create(srcCtx, creatorAddr, wasmCode, &codeInfo.InstantiateConfig)
require.NoError(t, err)
if pinned {
contractKeeper.PinCode(srcCtx, codeID)
}
if contractExtension {
anyTime := time.Now().UTC()
var nestedType govtypes.TextProposal
f.NilChance(0).Fuzz(&nestedType)
myExtension, err := govtypes.NewProposal(&nestedType, 1, anyTime, anyTime)
require.NoError(t, err)
contract.SetExtension(&myExtension)
}
contract.CodeID = codeID
contractAddr := wasmKeeper.ClassicAddressGenerator()(srcCtx, codeID, nil)
wasmKeeper.storeContractInfo(srcCtx, contractAddr, &contract)
wasmKeeper.appendToContractHistory(srcCtx, contractAddr, history...)
wasmKeeper.importContractState(srcCtx, contractAddr, stateModels)
}
var wasmParams types.Params
f.NilChance(0).Fuzz(&wasmParams)
wasmKeeper.SetParams(srcCtx, wasmParams)
// export
exportedState := ExportGenesis(srcCtx, wasmKeeper)
// order should not matter
rand.Shuffle(len(exportedState.Codes), func(i, j int) {
exportedState.Codes[i], exportedState.Codes[j] = exportedState.Codes[j], exportedState.Codes[i]
})
rand.Shuffle(len(exportedState.Contracts), func(i, j int) {
exportedState.Contracts[i], exportedState.Contracts[j] = exportedState.Contracts[j], exportedState.Contracts[i]
})
rand.Shuffle(len(exportedState.Sequences), func(i, j int) {
exportedState.Sequences[i], exportedState.Sequences[j] = exportedState.Sequences[j], exportedState.Sequences[i]
})
exportedGenesis, err := wasmKeeper.cdc.MarshalJSON(exportedState)
require.NoError(t, err)
// setup new instances
dstKeeper, dstCtx, dstStoreKeys := setupKeeper(t)
// reset contract code index in source DB for comparison with dest DB
wasmKeeper.IterateContractInfo(srcCtx, func(address sdk.AccAddress, info wasmTypes.ContractInfo) bool {
creatorAddress := sdk.MustAccAddressFromBech32(info.Creator)
history := wasmKeeper.GetContractHistory(srcCtx, address)
wasmKeeper.addToContractCodeSecondaryIndex(srcCtx, address, history[len(history)-1])
wasmKeeper.addToContractCreatorSecondaryIndex(srcCtx, creatorAddress, history[0].Updated, address)
return false
})
// re-import
var importState wasmTypes.GenesisState
err = dstKeeper.cdc.UnmarshalJSON(exportedGenesis, &importState)
require.NoError(t, err)
InitGenesis(dstCtx, dstKeeper, importState)
// compare whole DB
for j := range srcStoreKeys {
srcIT := srcCtx.KVStore(srcStoreKeys[j]).Iterator(nil, nil)
dstIT := dstCtx.KVStore(dstStoreKeys[j]).Iterator(nil, nil)
for i := 0; srcIT.Valid(); i++ {
require.True(t, dstIT.Valid(), "[%s] destination DB has less elements than source. Missing: %x", srcStoreKeys[j].Name(), srcIT.Key())
require.Equal(t, srcIT.Key(), dstIT.Key(), i)
require.Equal(t, srcIT.Value(), dstIT.Value(), "[%s] element (%d): %X", srcStoreKeys[j].Name(), i, srcIT.Key())
dstIT.Next()
srcIT.Next()
}
if !assert.False(t, dstIT.Valid()) {
t.Fatalf("dest Iterator still has key :%X", dstIT.Key())
}
srcIT.Close()
dstIT.Close()
}
}
func TestGenesisInit(t *testing.T) {
wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
myCodeInfo := wasmTypes.CodeInfoFixture(wasmTypes.WithSHA256CodeHash(wasmCode))
specs := map[string]struct {
src types.GenesisState
expSuccess bool
}{
"happy path: code info correct": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
"happy path: code ids can contain gaps": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}, {
CodeID: 3,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 10},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
"happy path: code order does not matter": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: 2,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}, {
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: nil,
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 3},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
"prevent code hash mismatch": {src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: wasmTypes.CodeInfoFixture(func(i *wasmTypes.CodeInfo) { i.CodeHash = make([]byte, sha256.Size) }),
CodeBytes: wasmCode,
}},
Params: types.DefaultParams(),
}},
"prevent duplicate codeIDs": {src: types.GenesisState{
Codes: []types.Code{
{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
},
{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
},
},
Params: types.DefaultParams(),
}},
"codes with same checksum can be pinned": {
src: types.GenesisState{
Codes: []types.Code{
{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
Pinned: true,
},
{
CodeID: 2,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
Pinned: true,
},
},
Params: types.DefaultParams(),
},
},
"happy path: code id in info and contract do match": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{}`),
},
},
},
},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 2},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
"happy path: code info with two contracts": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{}`),
},
},
}, {
ContractAddress: BuildContractAddressClassic(1, 2).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{"foo":"bar"}`),
},
},
},
},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 3},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
"prevent contracts that points to non existing codeID": {
src: types.GenesisState{
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{"foo":"bar"}`),
},
},
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate contract address": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{"foo":"bar"}`),
},
},
}, {
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{"other":"value"}`),
},
},
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate contract model keys": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractState: []types.Model{
{
Key: []byte{0x1},
Value: []byte("foo"),
},
{
Key: []byte{0x1},
Value: []byte("bar"),
},
},
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{"foo":"bar"}`),
},
},
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate sequences": {
src: types.GenesisState{
Sequences: []types.Sequence{
{IDKey: []byte("foo"), Value: 1},
{IDKey: []byte("foo"), Value: 9999},
},
Params: types.DefaultParams(),
},
},
"prevent code id seq init value == max codeID used": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: 2,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 1},
},
Params: types.DefaultParams(),
},
},
"prevent contract id seq init value == count contracts": {
src: types.GenesisState{
Codes: []types.Code{{
CodeID: firstCodeID,
CodeInfo: myCodeInfo,
CodeBytes: wasmCode,
}},
Contracts: []types.Contract{
{
ContractAddress: BuildContractAddressClassic(1, 1).String(),
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.RandCreatedFields),
ContractCodeHistory: []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: 1,
Updated: &types.AbsoluteTxPosition{BlockHeight: rand.Uint64(), TxIndex: rand.Uint64()},
Msg: []byte(`{}`),
},
},
},
},
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
keeper, ctx, _ := setupKeeper(t)
require.NoError(t, types.ValidateGenesis(spec.src))
_, gotErr := InitGenesis(ctx, keeper, spec.src)
if !spec.expSuccess {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
for _, c := range spec.src.Codes {
assert.Equal(t, c.Pinned, keeper.IsPinnedCode(ctx, c.CodeID))
}
})
}
}
func TestImportContractWithCodeHistoryPreserved(t *testing.T) {
genesisTemplate := `
{
"params":{
"code_upload_access": {
"permission": "Everybody"
},
"instantiate_default_permission": "Everybody"
},
"codes": [
{
"code_id": "1",
"code_info": {
"code_hash": %q,
"creator": "cosmos1qtu5n0cnhfkjj6l2rq97hmky9fd89gwca9yarx",
"instantiate_config": {
"permission": "OnlyAddress",
"address": "cosmos1qtu5n0cnhfkjj6l2rq97hmky9fd89gwca9yarx"
}
},
"code_bytes": %q
}
],
"contracts": [
{
"contract_address": "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr",
"contract_info": {
"code_id": "1",
"creator": "cosmos13x849jzd03vne42ynpj25hn8npjecxqrjghd8x",
"admin": "cosmos1h5t8zxmjr30e9dqghtlpl40f2zz5cgey6esxtn",
"label": "ȀĴnZV芢毤",
"created": {
"block_height" : "100",
"tx_index" : "10"
}
},
"contract_code_history": [
{
"operation": "CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT",
"code_id": "1",
"updated": {
"block_height" : "100",
"tx_index" : "10"
},
"msg": {"foo": "bar"}
},
{
"operation": "CONTRACT_CODE_HISTORY_OPERATION_TYPE_MIGRATE",
"code_id": "1",
"updated": {
"block_height" : "200",
"tx_index" : "10"
},
"msg": {"other": "msg"}
}
]
}
],
"sequences": [
{"id_key": "BGxhc3RDb2RlSWQ=", "value": "2"},
{"id_key": "BGxhc3RDb250cmFjdElk", "value": "3"}
]
}`
keeper, ctx, _ := setupKeeper(t)
wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
wasmCodeHash := sha256.Sum256(wasmCode)
enc64 := base64.StdEncoding.EncodeToString
genesisStr := fmt.Sprintf(genesisTemplate, enc64(wasmCodeHash[:]), enc64(wasmCode))
var importState wasmTypes.GenesisState
err = keeper.cdc.UnmarshalJSON([]byte(genesisStr), &importState)
require.NoError(t, err)
require.NoError(t, importState.ValidateBasic(), genesisStr)
ctx = ctx.WithBlockHeight(0).WithGasMeter(sdk.NewInfiniteGasMeter())
// when
_, err = InitGenesis(ctx, keeper, importState)
require.NoError(t, err)
// verify wasm code
gotWasmCode, err := keeper.GetByteCode(ctx, 1)
require.NoError(t, err)
assert.Equal(t, wasmCode, gotWasmCode, "byte code does not match")
// verify code info
gotCodeInfo := keeper.GetCodeInfo(ctx, 1)
require.NotNil(t, gotCodeInfo)
codeCreatorAddr := "cosmos1qtu5n0cnhfkjj6l2rq97hmky9fd89gwca9yarx"
expCodeInfo := types.CodeInfo{
CodeHash: wasmCodeHash[:],
Creator: codeCreatorAddr,
InstantiateConfig: wasmTypes.AccessConfig{
Permission: types.AccessTypeOnlyAddress,
Address: codeCreatorAddr,
},
}
assert.Equal(t, expCodeInfo, *gotCodeInfo)
// verify contract
contractAddr, _ := sdk.AccAddressFromBech32("cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr")
gotContractInfo := keeper.GetContractInfo(ctx, contractAddr)
require.NotNil(t, gotContractInfo)
contractCreatorAddr := "cosmos13x849jzd03vne42ynpj25hn8npjecxqrjghd8x"
adminAddr := "cosmos1h5t8zxmjr30e9dqghtlpl40f2zz5cgey6esxtn"
expContractInfo := types.ContractInfo{
CodeID: firstCodeID,
Creator: contractCreatorAddr,
Admin: adminAddr,
Label: "ȀĴnZV芢毤",
Created: &types.AbsoluteTxPosition{BlockHeight: 100, TxIndex: 10},
}
assert.Equal(t, expContractInfo, *gotContractInfo)
expHistory := []types.ContractCodeHistoryEntry{
{
Operation: types.ContractCodeHistoryOperationTypeInit,
CodeID: firstCodeID,
Updated: &types.AbsoluteTxPosition{
BlockHeight: 100,
TxIndex: 10,
},
Msg: []byte(`{"foo": "bar"}`),
},
{
Operation: types.ContractCodeHistoryOperationTypeMigrate,
CodeID: firstCodeID,
Updated: &types.AbsoluteTxPosition{
BlockHeight: 200,
TxIndex: 10,
},
Msg: []byte(`{"other": "msg"}`),
},
}
assert.Equal(t, expHistory, keeper.GetContractHistory(ctx, contractAddr))
assert.Equal(t, uint64(2), keeper.PeekAutoIncrementID(ctx, types.KeyLastCodeID))
assert.Equal(t, uint64(3), keeper.PeekAutoIncrementID(ctx, types.KeyLastInstanceID))
}
func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) {
t.Helper()
tempDir, err := os.MkdirTemp("", "wasm")
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(tempDir) })
var (
keyParams = sdk.NewKVStoreKey(paramtypes.StoreKey)
tkeyParams = sdk.NewTransientStoreKey(paramtypes.TStoreKey)
keyWasm = sdk.NewKVStoreKey(wasmTypes.StoreKey)
)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyWasm, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
require.NoError(t, ms.LoadLatestVersion())
ctx := sdk.NewContext(ms, tmproto.Header{
Height: 1234567,
Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC),
}, false, log.NewNopLogger())
encodingConfig := MakeEncodingConfig(t)
// register an example extension. must be protobuf
encodingConfig.InterfaceRegistry.RegisterImplementations(
(*types.ContractInfoExtension)(nil),
&govtypes.Proposal{},
)
// also registering gov interfaces for nested Any type
govtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
wasmConfig := wasmTypes.DefaultWasmConfig()
pk := paramskeeper.NewKeeper(encodingConfig.Marshaler, encodingConfig.Amino, keyParams, tkeyParams)
srcKeeper := NewKeeper(
encodingConfig.Marshaler,
keyWasm,
pk.Subspace(wasmTypes.ModuleName),
authkeeper.AccountKeeper{},
&bankkeeper.BaseKeeper{},
stakingkeeper.Keeper{},
distributionkeeper.Keeper{},
nil,
nil,
nil,
nil,
nil,
nil,
tempDir,
wasmConfig,
AvailableCapabilities,
)
return &srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}
}

View File

@ -0,0 +1,226 @@
package keeper
import (
"errors"
"fmt"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/cosmos/cosmos-sdk/baseapp"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
"github.com/cerc-io/laconicd/x/wasm/types"
)
// msgEncoder is an extension point to customize encodings
type msgEncoder interface {
// Encode converts wasmvm message to n cosmos message types
Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error)
}
// MessageRouter ADR 031 request type routing
type MessageRouter interface {
Handler(msg sdk.Msg) baseapp.MsgServiceHandler
}
// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed.
type SDKMessageHandler struct {
router MessageRouter
encoders msgEncoder
}
func NewDefaultMessageHandler(
router MessageRouter,
channelKeeper types.ChannelKeeper,
capabilityKeeper types.CapabilityKeeper,
bankKeeper types.Burner,
unpacker codectypes.AnyUnpacker,
portSource types.ICS20TransferPortSource,
customEncoders ...*MessageEncoders,
) Messenger {
encoders := DefaultEncoders(unpacker, portSource)
for _, e := range customEncoders {
encoders = encoders.Merge(e)
}
return NewMessageHandlerChain(
NewSDKMessageHandler(router, encoders),
NewIBCRawPacketHandler(channelKeeper, capabilityKeeper),
NewBurnCoinMessageHandler(bankKeeper),
)
}
func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder) SDKMessageHandler {
return SDKMessageHandler{
router: router,
encoders: encoders,
}
}
func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg)
if err != nil {
return nil, nil, err
}
for _, sdkMsg := range sdkMsgs {
res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg)
if err != nil {
return nil, nil, err
}
// append data
data = append(data, res.Data)
// append events
sdkEvents := make([]sdk.Event, len(res.Events))
for i := range res.Events {
sdkEvents[i] = sdk.Event(res.Events[i])
}
events = append(events, sdkEvents...)
}
return
}
func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) {
if err := msg.ValidateBasic(); err != nil {
return nil, err
}
// make sure this account can send it
for _, acct := range msg.GetSigners() {
if !acct.Equals(contractAddr) {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
}
}
// find the handler and execute it
if handler := h.router.Handler(msg); handler != nil {
// ADR 031 request type routing
msgResult, err := handler(ctx, msg)
return msgResult, err
}
// legacy sdk.Msg routing
// Assuming that the app developer has migrated all their Msgs to
// proto messages and has registered all `Msg services`, then this
// path should never be called, because all those Msgs should be
// registered within the `msgServiceRouter` already.
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg)
}
// MessageHandlerChain defines a chain of handlers that are called one by one until it can be handled.
type MessageHandlerChain struct {
handlers []Messenger
}
func NewMessageHandlerChain(first Messenger, others ...Messenger) *MessageHandlerChain {
r := &MessageHandlerChain{handlers: append([]Messenger{first}, others...)}
for i := range r.handlers {
if r.handlers[i] == nil {
panic(fmt.Sprintf("handler must not be nil at position : %d", i))
}
}
return r
}
// DispatchMsg dispatch message and calls chained handlers one after another in
// order to find the right one to process given message. If a handler cannot
// process given message (returns ErrUnknownMsg), its result is ignored and the
// next handler is executed.
func (m MessageHandlerChain) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) {
for _, h := range m.handlers {
events, data, err := h.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg)
switch {
case err == nil:
return events, data, nil
case errors.Is(err, types.ErrUnknownMsg):
continue
default:
return events, data, err
}
}
return nil, nil, sdkerrors.Wrap(types.ErrUnknownMsg, "no handler found")
}
// IBCRawPacketHandler handels IBC.SendPacket messages which are published to an IBC channel.
type IBCRawPacketHandler struct {
channelKeeper types.ChannelKeeper
capabilityKeeper types.CapabilityKeeper
}
func NewIBCRawPacketHandler(chk types.ChannelKeeper, cak types.CapabilityKeeper) IBCRawPacketHandler {
return IBCRawPacketHandler{channelKeeper: chk, capabilityKeeper: cak}
}
// DispatchMsg publishes a raw IBC packet onto the channel.
func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
if msg.IBC == nil || msg.IBC.SendPacket == nil {
return nil, nil, types.ErrUnknownMsg
}
if contractIBCPortID == "" {
return nil, nil, sdkerrors.Wrapf(types.ErrUnsupportedForContract, "ibc not supported")
}
contractIBCChannelID := msg.IBC.SendPacket.ChannelID
if contractIBCChannelID == "" {
return nil, nil, sdkerrors.Wrapf(types.ErrEmpty, "ibc channel")
}
sequence, found := h.channelKeeper.GetNextSequenceSend(ctx, contractIBCPortID, contractIBCChannelID)
if !found {
return nil, nil, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound,
"source port: %s, source channel: %s", contractIBCPortID, contractIBCChannelID,
)
}
channelInfo, ok := h.channelKeeper.GetChannel(ctx, contractIBCPortID, contractIBCChannelID)
if !ok {
return nil, nil, sdkerrors.Wrap(channeltypes.ErrInvalidChannel, "not found")
}
channelCap, ok := h.capabilityKeeper.GetCapability(ctx, host.ChannelCapabilityPath(contractIBCPortID, contractIBCChannelID))
if !ok {
return nil, nil, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
}
packet := channeltypes.NewPacket(
msg.IBC.SendPacket.Data,
sequence,
contractIBCPortID,
contractIBCChannelID,
channelInfo.Counterparty.PortId,
channelInfo.Counterparty.ChannelId,
ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.IBC.SendPacket.Timeout.Block),
msg.IBC.SendPacket.Timeout.Timestamp,
)
return nil, nil, h.channelKeeper.SendPacket(ctx, channelCap, packet)
}
var _ Messenger = MessageHandlerFunc(nil)
// MessageHandlerFunc is a helper to construct a function based message handler.
type MessageHandlerFunc func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error)
// DispatchMsg delegates dispatching of provided message into the MessageHandlerFunc.
func (m MessageHandlerFunc) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
return m(ctx, contractAddr, contractIBCPortID, msg)
}
// NewBurnCoinMessageHandler handles wasmvm.BurnMsg messages
func NewBurnCoinMessageHandler(burner types.Burner) MessageHandlerFunc {
return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
if msg.Bank != nil && msg.Bank.Burn != nil {
coins, err := ConvertWasmCoinsToSdkCoins(msg.Bank.Burn.Amount)
if err != nil {
return nil, nil, err
}
if coins.IsZero() {
return nil, nil, types.ErrEmpty.Wrap("amount")
}
if err := burner.SendCoinsFromAccountToModule(ctx, contractAddr, types.ModuleName, coins); err != nil {
return nil, nil, sdkerrors.Wrap(err, "transfer to module")
}
if err := burner.BurnCoins(ctx, types.ModuleName, coins); err != nil {
return nil, nil, sdkerrors.Wrap(err, "burn coins")
}
moduleLogger(ctx).Info("Burned", "amount", coins)
return nil, nil, nil
}
return nil, nil, types.ErrUnknownMsg
}
}

View File

@ -0,0 +1,393 @@
package keeper
import (
"encoding/json"
"fmt"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
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"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
ibcclienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
"github.com/cerc-io/laconicd/x/wasm/types"
)
type (
BankEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.BankMsg) ([]sdk.Msg, error)
CustomEncoder func(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error)
DistributionEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.DistributionMsg) ([]sdk.Msg, error)
StakingEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.StakingMsg) ([]sdk.Msg, error)
StargateEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.StargateMsg) ([]sdk.Msg, error)
WasmEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg, error)
IBCEncoder func(ctx sdk.Context, sender sdk.AccAddress, contractIBCPortID string, msg *wasmvmtypes.IBCMsg) ([]sdk.Msg, error)
)
type MessageEncoders struct {
Bank func(sender sdk.AccAddress, msg *wasmvmtypes.BankMsg) ([]sdk.Msg, error)
Custom func(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error)
Distribution func(sender sdk.AccAddress, msg *wasmvmtypes.DistributionMsg) ([]sdk.Msg, error)
IBC func(ctx sdk.Context, sender sdk.AccAddress, contractIBCPortID string, msg *wasmvmtypes.IBCMsg) ([]sdk.Msg, error)
Staking func(sender sdk.AccAddress, msg *wasmvmtypes.StakingMsg) ([]sdk.Msg, error)
Stargate func(sender sdk.AccAddress, msg *wasmvmtypes.StargateMsg) ([]sdk.Msg, error)
Wasm func(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg, error)
Gov func(sender sdk.AccAddress, msg *wasmvmtypes.GovMsg) ([]sdk.Msg, error)
}
func DefaultEncoders(unpacker codectypes.AnyUnpacker, portSource types.ICS20TransferPortSource) MessageEncoders {
return MessageEncoders{
Bank: EncodeBankMsg,
Custom: NoCustomMsg,
Distribution: EncodeDistributionMsg,
IBC: EncodeIBCMsg(portSource),
Staking: EncodeStakingMsg,
Stargate: EncodeStargateMsg(unpacker),
Wasm: EncodeWasmMsg,
Gov: EncodeGovMsg,
}
}
func (e MessageEncoders) Merge(o *MessageEncoders) MessageEncoders {
if o == nil {
return e
}
if o.Bank != nil {
e.Bank = o.Bank
}
if o.Custom != nil {
e.Custom = o.Custom
}
if o.Distribution != nil {
e.Distribution = o.Distribution
}
if o.IBC != nil {
e.IBC = o.IBC
}
if o.Staking != nil {
e.Staking = o.Staking
}
if o.Stargate != nil {
e.Stargate = o.Stargate
}
if o.Wasm != nil {
e.Wasm = o.Wasm
}
if o.Gov != nil {
e.Gov = o.Gov
}
return e
}
func (e MessageEncoders) Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error) {
switch {
case msg.Bank != nil:
return e.Bank(contractAddr, msg.Bank)
case msg.Custom != nil:
return e.Custom(contractAddr, msg.Custom)
case msg.Distribution != nil:
return e.Distribution(contractAddr, msg.Distribution)
case msg.IBC != nil:
return e.IBC(ctx, contractAddr, contractIBCPortID, msg.IBC)
case msg.Staking != nil:
return e.Staking(contractAddr, msg.Staking)
case msg.Stargate != nil:
return e.Stargate(contractAddr, msg.Stargate)
case msg.Wasm != nil:
return e.Wasm(contractAddr, msg.Wasm)
case msg.Gov != nil:
return EncodeGovMsg(contractAddr, msg.Gov)
}
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of Wasm")
}
func EncodeBankMsg(sender sdk.AccAddress, msg *wasmvmtypes.BankMsg) ([]sdk.Msg, error) {
if msg.Send == nil {
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of Bank")
}
if len(msg.Send.Amount) == 0 {
return nil, nil
}
toSend, err := ConvertWasmCoinsToSdkCoins(msg.Send.Amount)
if err != nil {
return nil, err
}
sdkMsg := banktypes.MsgSend{
FromAddress: sender.String(),
ToAddress: msg.Send.ToAddress,
Amount: toSend,
}
return []sdk.Msg{&sdkMsg}, nil
}
func NoCustomMsg(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error) {
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "custom variant not supported")
}
func EncodeDistributionMsg(sender sdk.AccAddress, msg *wasmvmtypes.DistributionMsg) ([]sdk.Msg, error) {
switch {
case msg.SetWithdrawAddress != nil:
setMsg := distributiontypes.MsgSetWithdrawAddress{
DelegatorAddress: sender.String(),
WithdrawAddress: msg.SetWithdrawAddress.Address,
}
return []sdk.Msg{&setMsg}, nil
case msg.WithdrawDelegatorReward != nil:
withdrawMsg := distributiontypes.MsgWithdrawDelegatorReward{
DelegatorAddress: sender.String(),
ValidatorAddress: msg.WithdrawDelegatorReward.Validator,
}
return []sdk.Msg{&withdrawMsg}, nil
default:
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of Distribution")
}
}
func EncodeStakingMsg(sender sdk.AccAddress, msg *wasmvmtypes.StakingMsg) ([]sdk.Msg, error) {
switch {
case msg.Delegate != nil:
coin, err := ConvertWasmCoinToSdkCoin(msg.Delegate.Amount)
if err != nil {
return nil, err
}
sdkMsg := stakingtypes.MsgDelegate{
DelegatorAddress: sender.String(),
ValidatorAddress: msg.Delegate.Validator,
Amount: coin,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Redelegate != nil:
coin, err := ConvertWasmCoinToSdkCoin(msg.Redelegate.Amount)
if err != nil {
return nil, err
}
sdkMsg := stakingtypes.MsgBeginRedelegate{
DelegatorAddress: sender.String(),
ValidatorSrcAddress: msg.Redelegate.SrcValidator,
ValidatorDstAddress: msg.Redelegate.DstValidator,
Amount: coin,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Undelegate != nil:
coin, err := ConvertWasmCoinToSdkCoin(msg.Undelegate.Amount)
if err != nil {
return nil, err
}
sdkMsg := stakingtypes.MsgUndelegate{
DelegatorAddress: sender.String(),
ValidatorAddress: msg.Undelegate.Validator,
Amount: coin,
}
return []sdk.Msg{&sdkMsg}, nil
default:
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of Staking")
}
}
func EncodeStargateMsg(unpacker codectypes.AnyUnpacker) StargateEncoder {
return func(sender sdk.AccAddress, msg *wasmvmtypes.StargateMsg) ([]sdk.Msg, error) {
any := codectypes.Any{
TypeUrl: msg.TypeURL,
Value: msg.Value,
}
var sdkMsg sdk.Msg
if err := unpacker.UnpackAny(&any, &sdkMsg); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, fmt.Sprintf("Cannot unpack proto message with type URL: %s", msg.TypeURL))
}
if err := codectypes.UnpackInterfaces(sdkMsg, unpacker); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, fmt.Sprintf("UnpackInterfaces inside msg: %s", err))
}
return []sdk.Msg{sdkMsg}, nil
}
}
func EncodeWasmMsg(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg, error) {
switch {
case msg.Execute != nil:
coins, err := ConvertWasmCoinsToSdkCoins(msg.Execute.Funds)
if err != nil {
return nil, err
}
sdkMsg := types.MsgExecuteContract{
Sender: sender.String(),
Contract: msg.Execute.ContractAddr,
Msg: msg.Execute.Msg,
Funds: coins,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Instantiate != nil:
coins, err := ConvertWasmCoinsToSdkCoins(msg.Instantiate.Funds)
if err != nil {
return nil, err
}
sdkMsg := types.MsgInstantiateContract{
Sender: sender.String(),
CodeID: msg.Instantiate.CodeID,
Label: msg.Instantiate.Label,
Msg: msg.Instantiate.Msg,
Admin: msg.Instantiate.Admin,
Funds: coins,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Instantiate2 != nil:
coins, err := ConvertWasmCoinsToSdkCoins(msg.Instantiate2.Funds)
if err != nil {
return nil, err
}
sdkMsg := types.MsgInstantiateContract2{
Sender: sender.String(),
Admin: msg.Instantiate2.Admin,
CodeID: msg.Instantiate2.CodeID,
Label: msg.Instantiate2.Label,
Msg: msg.Instantiate2.Msg,
Funds: coins,
Salt: msg.Instantiate2.Salt,
// FixMsg is discouraged, see: https://medium.com/cosmwasm/dev-note-3-limitations-of-instantiate2-and-how-to-deal-with-them-a3f946874230
FixMsg: false,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Migrate != nil:
sdkMsg := types.MsgMigrateContract{
Sender: sender.String(),
Contract: msg.Migrate.ContractAddr,
CodeID: msg.Migrate.NewCodeID,
Msg: msg.Migrate.Msg,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.ClearAdmin != nil:
sdkMsg := types.MsgClearAdmin{
Sender: sender.String(),
Contract: msg.ClearAdmin.ContractAddr,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.UpdateAdmin != nil:
sdkMsg := types.MsgUpdateAdmin{
Sender: sender.String(),
Contract: msg.UpdateAdmin.ContractAddr,
NewAdmin: msg.UpdateAdmin.Admin,
}
return []sdk.Msg{&sdkMsg}, nil
default:
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of Wasm")
}
}
func EncodeIBCMsg(portSource types.ICS20TransferPortSource) func(ctx sdk.Context, sender sdk.AccAddress, contractIBCPortID string, msg *wasmvmtypes.IBCMsg) ([]sdk.Msg, error) {
return func(ctx sdk.Context, sender sdk.AccAddress, contractIBCPortID string, msg *wasmvmtypes.IBCMsg) ([]sdk.Msg, error) {
switch {
case msg.CloseChannel != nil:
return []sdk.Msg{&channeltypes.MsgChannelCloseInit{
PortId: PortIDForContract(sender),
ChannelId: msg.CloseChannel.ChannelID,
Signer: sender.String(),
}}, nil
case msg.Transfer != nil:
amount, err := ConvertWasmCoinToSdkCoin(msg.Transfer.Amount)
if err != nil {
return nil, sdkerrors.Wrap(err, "amount")
}
msg := &ibctransfertypes.MsgTransfer{
SourcePort: portSource.GetPort(ctx),
SourceChannel: msg.Transfer.ChannelID,
Token: amount,
Sender: sender.String(),
Receiver: msg.Transfer.ToAddress,
TimeoutHeight: ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.Transfer.Timeout.Block),
TimeoutTimestamp: msg.Transfer.Timeout.Timestamp,
}
return []sdk.Msg{msg}, nil
default:
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of IBC")
}
}
}
func EncodeGovMsg(sender sdk.AccAddress, msg *wasmvmtypes.GovMsg) ([]sdk.Msg, error) {
switch {
case msg.Vote != nil:
voteOption, err := convertVoteOption(msg.Vote.Vote)
if err != nil {
return nil, sdkerrors.Wrap(err, "vote option")
}
m := govtypes.NewMsgVote(sender, msg.Vote.ProposalId, voteOption)
return []sdk.Msg{m}, nil
case msg.VoteWeighted != nil:
opts := make([]govtypes.WeightedVoteOption, len(msg.VoteWeighted.Options))
for i, v := range msg.VoteWeighted.Options {
weight, err := sdk.NewDecFromStr(v.Weight)
if err != nil {
return nil, sdkerrors.Wrapf(err, "weight for vote %d", i+1)
}
voteOption, err := convertVoteOption(v.Option)
if err != nil {
return nil, sdkerrors.Wrap(err, "vote option")
}
opts[i] = govtypes.WeightedVoteOption{Option: voteOption, Weight: weight}
}
m := govtypes.NewMsgVoteWeighted(sender, msg.VoteWeighted.ProposalId, opts)
return []sdk.Msg{m}, nil
default:
return nil, types.ErrUnknownMsg.Wrap("unknown variant of gov")
}
}
func convertVoteOption(s interface{}) (govtypes.VoteOption, error) {
var option govtypes.VoteOption
switch s {
case wasmvmtypes.Yes:
option = govtypes.OptionYes
case wasmvmtypes.No:
option = govtypes.OptionNo
case wasmvmtypes.NoWithVeto:
option = govtypes.OptionNoWithVeto
case wasmvmtypes.Abstain:
option = govtypes.OptionAbstain
default:
return govtypes.OptionEmpty, types.ErrInvalid
}
return option, nil
}
// ConvertWasmIBCTimeoutHeightToCosmosHeight converts a wasmvm type ibc timeout height to ibc module type height
func ConvertWasmIBCTimeoutHeightToCosmosHeight(ibcTimeoutBlock *wasmvmtypes.IBCTimeoutBlock) ibcclienttypes.Height {
if ibcTimeoutBlock == nil {
return ibcclienttypes.NewHeight(0, 0)
}
return ibcclienttypes.NewHeight(ibcTimeoutBlock.Revision, ibcTimeoutBlock.Height)
}
// ConvertWasmCoinsToSdkCoins converts the wasm vm type coins to sdk type coins
func ConvertWasmCoinsToSdkCoins(coins []wasmvmtypes.Coin) (sdk.Coins, error) {
var toSend sdk.Coins
for _, coin := range coins {
c, err := ConvertWasmCoinToSdkCoin(coin)
if err != nil {
return nil, err
}
toSend = toSend.Add(c)
}
return toSend.Sort(), nil
}
// ConvertWasmCoinToSdkCoin converts a wasm vm type coin to sdk type coin
func ConvertWasmCoinToSdkCoin(coin wasmvmtypes.Coin) (sdk.Coin, error) {
amount, ok := sdk.NewIntFromString(coin.Amount)
if !ok {
return sdk.Coin{}, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, coin.Amount+coin.Denom)
}
r := sdk.Coin{
Denom: coin.Denom,
Amount: amount,
}
return r, r.Validate()
}

View File

@ -0,0 +1,932 @@
package keeper
import (
"testing"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/x/wasm/keeper/wasmtesting"
"github.com/cerc-io/laconicd/x/wasm/types"
)
func TestEncoding(t *testing.T) {
var (
addr1 = RandomAccountAddress(t)
addr2 = RandomAccountAddress(t)
addr3 = RandomAccountAddress(t)
invalidAddr = "xrnd1d02kd90n38qvr3qb9qof83fn2d2"
)
valAddr := make(sdk.ValAddress, types.SDKAddrLen)
valAddr[0] = 12
valAddr2 := make(sdk.ValAddress, types.SDKAddrLen)
valAddr2[1] = 123
jsonMsg := types.RawContractMessage(`{"foo": 123}`)
bankMsg := &banktypes.MsgSend{
FromAddress: addr2.String(),
ToAddress: addr1.String(),
Amount: sdk.Coins{
sdk.NewInt64Coin("uatom", 12345),
sdk.NewInt64Coin("utgd", 54321),
},
}
bankMsgBin, err := proto.Marshal(bankMsg)
require.NoError(t, err)
content, err := codectypes.NewAnyWithValue(types.StoreCodeProposalFixture())
require.NoError(t, err)
proposalMsg := &govtypes.MsgSubmitProposal{
Proposer: addr1.String(),
InitialDeposit: sdk.NewCoins(sdk.NewInt64Coin("uatom", 12345)),
Content: content,
}
proposalMsgBin, err := proto.Marshal(proposalMsg)
require.NoError(t, err)
cases := map[string]struct {
sender sdk.AccAddress
srcMsg wasmvmtypes.CosmosMsg
srcContractIBCPort string
transferPortSource types.ICS20TransferPortSource
// set if valid
output []sdk.Msg
// set if expect mapping fails
expError bool
// set if sdk validate basic should fail
expInvalid bool
}{
"simple send": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: addr2.String(),
Amount: []wasmvmtypes.Coin{
{
Denom: "uatom",
Amount: "12345",
},
{
Denom: "usdt",
Amount: "54321",
},
},
},
},
},
output: []sdk.Msg{
&banktypes.MsgSend{
FromAddress: addr1.String(),
ToAddress: addr2.String(),
Amount: sdk.Coins{
sdk.NewInt64Coin("uatom", 12345),
sdk.NewInt64Coin("usdt", 54321),
},
},
},
},
"invalid send amount": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: addr2.String(),
Amount: []wasmvmtypes.Coin{
{
Denom: "uatom",
Amount: "123.456",
},
},
},
},
},
expError: true,
},
"invalid address": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: invalidAddr,
Amount: []wasmvmtypes.Coin{
{
Denom: "uatom",
Amount: "7890",
},
},
},
},
},
expError: false, // addresses are checked in the handler
expInvalid: true,
output: []sdk.Msg{
&banktypes.MsgSend{
FromAddress: addr1.String(),
ToAddress: invalidAddr,
Amount: sdk.Coins{
sdk.NewInt64Coin("uatom", 7890),
},
},
},
},
"wasm execute": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Execute: &wasmvmtypes.ExecuteMsg{
ContractAddr: addr2.String(),
Msg: jsonMsg,
Funds: []wasmvmtypes.Coin{
wasmvmtypes.NewCoin(12, "eth"),
},
},
},
},
output: []sdk.Msg{
&types.MsgExecuteContract{
Sender: addr1.String(),
Contract: addr2.String(),
Msg: jsonMsg,
Funds: sdk.NewCoins(sdk.NewInt64Coin("eth", 12)),
},
},
},
"wasm instantiate": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Instantiate: &wasmvmtypes.InstantiateMsg{
CodeID: 7,
Msg: jsonMsg,
Funds: []wasmvmtypes.Coin{
wasmvmtypes.NewCoin(123, "eth"),
},
Label: "myLabel",
Admin: addr2.String(),
},
},
},
output: []sdk.Msg{
&types.MsgInstantiateContract{
Sender: addr1.String(),
CodeID: 7,
Label: "myLabel",
Msg: jsonMsg,
Funds: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)),
Admin: addr2.String(),
},
},
},
"wasm instantiate2": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Instantiate2: &wasmvmtypes.Instantiate2Msg{
CodeID: 7,
Msg: jsonMsg,
Funds: []wasmvmtypes.Coin{
wasmvmtypes.NewCoin(123, "eth"),
},
Label: "myLabel",
Admin: addr2.String(),
Salt: []byte("mySalt"),
},
},
},
output: []sdk.Msg{
&types.MsgInstantiateContract2{
Sender: addr1.String(),
Admin: addr2.String(),
CodeID: 7,
Label: "myLabel",
Msg: jsonMsg,
Funds: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)),
Salt: []byte("mySalt"),
FixMsg: false,
},
},
},
"wasm migrate": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Migrate: &wasmvmtypes.MigrateMsg{
ContractAddr: addr1.String(),
NewCodeID: 12,
Msg: jsonMsg,
},
},
},
output: []sdk.Msg{
&types.MsgMigrateContract{
Sender: addr2.String(),
Contract: addr1.String(),
CodeID: 12,
Msg: jsonMsg,
},
},
},
"wasm update admin": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
UpdateAdmin: &wasmvmtypes.UpdateAdminMsg{
ContractAddr: addr1.String(),
Admin: addr3.String(),
},
},
},
output: []sdk.Msg{
&types.MsgUpdateAdmin{
Sender: addr2.String(),
Contract: addr1.String(),
NewAdmin: addr3.String(),
},
},
},
"wasm clear admin": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
ClearAdmin: &wasmvmtypes.ClearAdminMsg{
ContractAddr: addr1.String(),
},
},
},
output: []sdk.Msg{
&types.MsgClearAdmin{
Sender: addr2.String(),
Contract: addr1.String(),
},
},
},
"staking delegate": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Staking: &wasmvmtypes.StakingMsg{
Delegate: &wasmvmtypes.DelegateMsg{
Validator: valAddr.String(),
Amount: wasmvmtypes.NewCoin(777, "stake"),
},
},
},
output: []sdk.Msg{
&stakingtypes.MsgDelegate{
DelegatorAddress: addr1.String(),
ValidatorAddress: valAddr.String(),
Amount: sdk.NewInt64Coin("stake", 777),
},
},
},
"staking delegate to non-validator": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Staking: &wasmvmtypes.StakingMsg{
Delegate: &wasmvmtypes.DelegateMsg{
Validator: addr2.String(),
Amount: wasmvmtypes.NewCoin(777, "stake"),
},
},
},
expError: false, // fails in the handler
output: []sdk.Msg{
&stakingtypes.MsgDelegate{
DelegatorAddress: addr1.String(),
ValidatorAddress: addr2.String(),
Amount: sdk.NewInt64Coin("stake", 777),
},
},
},
"staking undelegate": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Staking: &wasmvmtypes.StakingMsg{
Undelegate: &wasmvmtypes.UndelegateMsg{
Validator: valAddr.String(),
Amount: wasmvmtypes.NewCoin(555, "stake"),
},
},
},
output: []sdk.Msg{
&stakingtypes.MsgUndelegate{
DelegatorAddress: addr1.String(),
ValidatorAddress: valAddr.String(),
Amount: sdk.NewInt64Coin("stake", 555),
},
},
},
"staking redelegate": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Staking: &wasmvmtypes.StakingMsg{
Redelegate: &wasmvmtypes.RedelegateMsg{
SrcValidator: valAddr.String(),
DstValidator: valAddr2.String(),
Amount: wasmvmtypes.NewCoin(222, "stake"),
},
},
},
output: []sdk.Msg{
&stakingtypes.MsgBeginRedelegate{
DelegatorAddress: addr1.String(),
ValidatorSrcAddress: valAddr.String(),
ValidatorDstAddress: valAddr2.String(),
Amount: sdk.NewInt64Coin("stake", 222),
},
},
},
"staking withdraw (explicit recipient)": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Distribution: &wasmvmtypes.DistributionMsg{
WithdrawDelegatorReward: &wasmvmtypes.WithdrawDelegatorRewardMsg{
Validator: valAddr2.String(),
},
},
},
output: []sdk.Msg{
&distributiontypes.MsgWithdrawDelegatorReward{
DelegatorAddress: addr1.String(),
ValidatorAddress: valAddr2.String(),
},
},
},
"staking set withdraw address": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Distribution: &wasmvmtypes.DistributionMsg{
SetWithdrawAddress: &wasmvmtypes.SetWithdrawAddressMsg{
Address: addr2.String(),
},
},
},
output: []sdk.Msg{
&distributiontypes.MsgSetWithdrawAddress{
DelegatorAddress: addr1.String(),
WithdrawAddress: addr2.String(),
},
},
},
"stargate encoded bank msg": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Stargate: &wasmvmtypes.StargateMsg{
TypeURL: "/cosmos.bank.v1beta1.MsgSend",
Value: bankMsgBin,
},
},
output: []sdk.Msg{bankMsg},
},
"stargate encoded msg with any type": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Stargate: &wasmvmtypes.StargateMsg{
TypeURL: "/cosmos.gov.v1beta1.MsgSubmitProposal",
Value: proposalMsgBin,
},
},
output: []sdk.Msg{proposalMsg},
},
"stargate encoded invalid typeUrl": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Stargate: &wasmvmtypes.StargateMsg{
TypeURL: "/cosmos.bank.v2.MsgSend",
Value: bankMsgBin,
},
},
expError: true,
},
"IBC transfer with block timeout": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
srcMsg: wasmvmtypes.CosmosMsg{
IBC: &wasmvmtypes.IBCMsg{
Transfer: &wasmvmtypes.TransferMsg{
ChannelID: "myChanID",
ToAddress: addr2.String(),
Amount: wasmvmtypes.Coin{
Denom: "ALX",
Amount: "1",
},
Timeout: wasmvmtypes.IBCTimeout{
Block: &wasmvmtypes.IBCTimeoutBlock{Revision: 1, Height: 2},
},
},
},
},
transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string {
return "myTransferPort"
}},
output: []sdk.Msg{
&ibctransfertypes.MsgTransfer{
SourcePort: "myTransferPort",
SourceChannel: "myChanID",
Token: sdk.Coin{
Denom: "ALX",
Amount: sdk.NewInt(1),
},
Sender: addr1.String(),
Receiver: addr2.String(),
TimeoutHeight: clienttypes.Height{RevisionNumber: 1, RevisionHeight: 2},
},
},
},
"IBC transfer with time timeout": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
srcMsg: wasmvmtypes.CosmosMsg{
IBC: &wasmvmtypes.IBCMsg{
Transfer: &wasmvmtypes.TransferMsg{
ChannelID: "myChanID",
ToAddress: addr2.String(),
Amount: wasmvmtypes.Coin{
Denom: "ALX",
Amount: "1",
},
Timeout: wasmvmtypes.IBCTimeout{Timestamp: 100},
},
},
},
transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string {
return "transfer"
}},
output: []sdk.Msg{
&ibctransfertypes.MsgTransfer{
SourcePort: "transfer",
SourceChannel: "myChanID",
Token: sdk.Coin{
Denom: "ALX",
Amount: sdk.NewInt(1),
},
Sender: addr1.String(),
Receiver: addr2.String(),
TimeoutTimestamp: 100,
},
},
},
"IBC transfer with time and height timeout": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
srcMsg: wasmvmtypes.CosmosMsg{
IBC: &wasmvmtypes.IBCMsg{
Transfer: &wasmvmtypes.TransferMsg{
ChannelID: "myChanID",
ToAddress: addr2.String(),
Amount: wasmvmtypes.Coin{
Denom: "ALX",
Amount: "1",
},
Timeout: wasmvmtypes.IBCTimeout{Timestamp: 100, Block: &wasmvmtypes.IBCTimeoutBlock{Height: 1, Revision: 2}},
},
},
},
transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string {
return "transfer"
}},
output: []sdk.Msg{
&ibctransfertypes.MsgTransfer{
SourcePort: "transfer",
SourceChannel: "myChanID",
Token: sdk.Coin{
Denom: "ALX",
Amount: sdk.NewInt(1),
},
Sender: addr1.String(),
Receiver: addr2.String(),
TimeoutTimestamp: 100,
TimeoutHeight: clienttypes.NewHeight(2, 1),
},
},
},
"IBC close channel": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
srcMsg: wasmvmtypes.CosmosMsg{
IBC: &wasmvmtypes.IBCMsg{
CloseChannel: &wasmvmtypes.CloseChannelMsg{
ChannelID: "channel-1",
},
},
},
output: []sdk.Msg{
&channeltypes.MsgChannelCloseInit{
PortId: "wasm." + addr1.String(),
ChannelId: "channel-1",
Signer: addr1.String(),
},
},
},
}
encodingConfig := MakeEncodingConfig(t)
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
var ctx sdk.Context
encoder := DefaultEncoders(encodingConfig.Marshaler, tc.transferPortSource)
res, err := encoder.Encode(ctx, tc.sender, tc.srcContractIBCPort, tc.srcMsg)
if tc.expError {
assert.Error(t, err)
return
} else {
require.NoError(t, err)
assert.Equal(t, tc.output, res)
}
// and valid sdk message
for _, v := range res {
gotErr := v.ValidateBasic()
if tc.expInvalid {
assert.Error(t, gotErr)
} else {
assert.NoError(t, gotErr)
}
}
})
}
}
func TestEncodeGovMsg(t *testing.T) {
myAddr := RandomAccountAddress(t)
cases := map[string]struct {
sender sdk.AccAddress
srcMsg wasmvmtypes.CosmosMsg
transferPortSource types.ICS20TransferPortSource
// set if valid
output []sdk.Msg
// set if expect mapping fails
expError bool
// set if sdk validate basic should fail
expInvalid bool
}{
"Gov vote: yes": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.Yes},
},
},
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: myAddr.String(),
Option: govtypes.OptionYes,
},
},
},
"Gov vote: No": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.No},
},
},
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: myAddr.String(),
Option: govtypes.OptionNo,
},
},
},
"Gov vote: Abstain": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 10, Vote: wasmvmtypes.Abstain},
},
},
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 10,
Voter: myAddr.String(),
Option: govtypes.OptionAbstain,
},
},
},
"Gov vote: No with veto": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.NoWithVeto},
},
},
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: myAddr.String(),
Option: govtypes.OptionNoWithVeto,
},
},
},
"Gov vote: unset option": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1},
},
},
expError: true,
},
"Gov weighted vote: single vote": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "1"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDec(1)},
},
},
},
},
"Gov weighted vote: splitted": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.23"},
{Option: wasmvmtypes.No, Weight: "0.24"},
{Option: wasmvmtypes.Abstain, Weight: "0.26"},
{Option: wasmvmtypes.NoWithVeto, Weight: "0.27"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(23, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(24, 2)},
{Option: govtypes.OptionAbstain, Weight: sdk.NewDecWithPrec(26, 2)},
{Option: govtypes.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(27, 2)},
},
},
},
},
"Gov weighted vote: duplicate option": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.5"},
{Option: wasmvmtypes.Yes, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
"Gov weighted vote: weight sum exceeds 1": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.51"},
{Option: wasmvmtypes.No, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(51, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
"Gov weighted vote: weight sum less than 1": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.49"},
{Option: wasmvmtypes.No, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(49, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
}
encodingConfig := MakeEncodingConfig(t)
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
var ctx sdk.Context
encoder := DefaultEncoders(encodingConfig.Marshaler, tc.transferPortSource)
res, gotEncErr := encoder.Encode(ctx, tc.sender, "myIBCPort", tc.srcMsg)
if tc.expError {
assert.Error(t, gotEncErr)
return
} else {
require.NoError(t, gotEncErr)
assert.Equal(t, tc.output, res)
}
// and valid sdk message
for _, v := range res {
gotErr := v.ValidateBasic()
if tc.expInvalid {
assert.Error(t, gotErr)
} else {
assert.NoError(t, gotErr)
}
}
})
}
}
func TestConvertWasmCoinToSdkCoin(t *testing.T) {
specs := map[string]struct {
src wasmvmtypes.Coin
expErr bool
expVal sdk.Coin
}{
"all good": {
src: wasmvmtypes.Coin{
Denom: "foo",
Amount: "1",
},
expVal: sdk.NewCoin("foo", sdk.NewIntFromUint64(1)),
},
"negative amount": {
src: wasmvmtypes.Coin{
Denom: "foo",
Amount: "-1",
},
expErr: true,
},
"denom to short": {
src: wasmvmtypes.Coin{
Denom: "f",
Amount: "1",
},
expErr: true,
},
"invalid demum char": {
src: wasmvmtypes.Coin{
Denom: "&fff",
Amount: "1",
},
expErr: true,
},
"not a number amount": {
src: wasmvmtypes.Coin{
Denom: "foo",
Amount: "bar",
},
expErr: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
gotVal, gotErr := ConvertWasmCoinToSdkCoin(spec.src)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
assert.Equal(t, spec.expVal, gotVal)
})
}
}
func TestConvertWasmCoinsToSdkCoins(t *testing.T) {
specs := map[string]struct {
src []wasmvmtypes.Coin
exp sdk.Coins
expErr bool
}{
"empty": {
src: []wasmvmtypes.Coin{},
exp: nil,
},
"single coin": {
src: []wasmvmtypes.Coin{{Denom: "foo", Amount: "1"}},
exp: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1))),
},
"multiple coins": {
src: []wasmvmtypes.Coin{
{Denom: "foo", Amount: "1"},
{Denom: "bar", Amount: "2"},
},
exp: sdk.NewCoins(
sdk.NewCoin("bar", sdk.NewInt(2)),
sdk.NewCoin("foo", sdk.NewInt(1)),
),
},
"sorted": {
src: []wasmvmtypes.Coin{
{Denom: "foo", Amount: "1"},
{Denom: "other", Amount: "1"},
{Denom: "bar", Amount: "1"},
},
exp: []sdk.Coin{
sdk.NewCoin("bar", sdk.NewInt(1)),
sdk.NewCoin("foo", sdk.NewInt(1)),
sdk.NewCoin("other", sdk.NewInt(1)),
},
},
"zero amounts dropped": {
src: []wasmvmtypes.Coin{
{Denom: "foo", Amount: "1"},
{Denom: "bar", Amount: "0"},
},
exp: sdk.NewCoins(
sdk.NewCoin("foo", sdk.NewInt(1)),
),
},
"duplicate denoms merged": {
src: []wasmvmtypes.Coin{
{Denom: "foo", Amount: "1"},
{Denom: "foo", Amount: "1"},
},
exp: []sdk.Coin{sdk.NewCoin("foo", sdk.NewInt(2))},
},
"duplicate denoms with one 0 amount does not fail": {
src: []wasmvmtypes.Coin{
{Denom: "foo", Amount: "0"},
{Denom: "foo", Amount: "1"},
},
exp: []sdk.Coin{sdk.NewCoin("foo", sdk.NewInt(1))},
},
"empty denom rejected": {
src: []wasmvmtypes.Coin{{Denom: "", Amount: "1"}},
expErr: true,
},
"invalid denom rejected": {
src: []wasmvmtypes.Coin{{Denom: "!%&", Amount: "1"}},
expErr: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
gotCoins, gotErr := ConvertWasmCoinsToSdkCoins(spec.src)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
assert.Equal(t, spec.exp, gotCoins)
assert.NoError(t, gotCoins.Validate())
})
}
}

Some files were not shown because too many files have changed in this diff Show More