diff --git a/.dockerignore b/.dockerignore index dacc6297..9bcd6634 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ -# localnet-setup -localnet-setup +Dockerfile -# build -build \ No newline at end of file +**/node_modules +init.sh + +build +localnet-setup \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index f03d6586..00000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# CODEOWNERS: https://help.github.com/articles/about-codeowners/ - -# Primary (global) repo maintainers - - * @evmos/core-engineering diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37167aa4..7dacb807 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,7 +59,7 @@ jobs: run: | make test-import if: env.GIT_DIFF - + test-rpc: runs-on: ubuntu-latest timeout-minutes: 15 @@ -91,42 +91,41 @@ jobs: path: "./laconic-sdk/" repository: cerc-io/laconic-sdk fetch-depth: 0 - ref: jest_timeout + ref: main - name: Environment run: ls -tlh && env - - name: build containers scripts + + - name: Build laconicd container working-directory: tests/sdk_tests - run: ./build-laconicd-container.sh && ./build-sdk-test-container.sh - - name: start containers + run: ./build-laconicd-container.sh + - name: Build laconic-sdk container + working-directory: laconic-sdk + run: ./scripts/build-sdk-test-container.sh + + - name: Start containers working-directory: tests/sdk_tests run: docker compose up -d - - name: run-tests.sh + - name: Run tests working-directory: tests/sdk_tests run: ./run-tests.sh - - name: reset containers for auction tests + + - name: Start containers (auctions enabled) working-directory: tests/sdk_tests - if: always() - run: docker compose down - - name: start auction containers + env: + TEST_AUCTION_ENABLED: true + run: docker compose up -d + - name: Run auction tests working-directory: tests/sdk_tests - run: docker compose -f docker-compose-auctions.yml up -d - - name: run-acution-tests.sh + run: ./run-tests.sh test:auctions + + - name: Start containers (expiry enabled) working-directory: tests/sdk_tests - run: ./run-auction-tests.sh - - name: reset containers for nameservice tests + env: + TEST_REGISTRY_EXPIRY: true + run: docker compose up -d + - name: Run nameservice expiry tests working-directory: tests/sdk_tests - if: always() - run: docker compose -f docker-compose-auctions.yml down - - name: start auction containers - working-directory: tests/sdk_tests - run: docker compose -f docker-compose-nameservice.yml up -d - - name: run-nameservice-expiry-tests.sh - working-directory: tests/sdk_tests - run: ./run-nameservice-expiry-tests.sh - - name: reset containers for nameservice tests - working-directory: tests/sdk_tests - if: always() - run: docker compose -f docker-compose-nameservice.yml down + run: ./run-tests.sh test:nameservice-expiry # integration_tests: # runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index e6ddee8c..3f9c6cd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,14 @@ FROM golang:alpine AS build-env -# Set up dependencies -ENV PACKAGES git build-base +# Install dependencies +RUN apk add --update git build-base linux-headers # Set working directory for the build WORKDIR /go/src/github.com/cerc-io/laconicd -# Install dependencies -RUN apk add --update $PACKAGES -RUN apk add linux-headers +# Cache Go modules +COPY go.mod go.sum ./ +RUN go mod download # Add source files COPY . . @@ -21,10 +21,10 @@ FROM alpine:3.17.0 # Install ca-certificates RUN apk add --update ca-certificates jq curl -WORKDIR / # Copy over binaries from the build-env COPY --from=build-env /go/src/github.com/cerc-io/laconicd/build/laconicd /usr/bin/laconicd +WORKDIR / # Run laconicd by default -CMD ["laconicd"] +ENTRYPOINT ["laconicd"] diff --git a/Makefile b/Makefile index 6c5b09b4..72d7a58b 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ ifeq ($(ENABLE_ROCKSDB),true) BUILD_TAGS += rocksdb_build test_tags += rocksdb_build else - $(warning RocksDB support is disabled; to build and test with RocksDB support, set ENABLE_ROCKSDB=true) + $(info RocksDB support is disabled; to build and test with RocksDB support, set ENABLE_ROCKSDB=true) endif # DB backend selection @@ -145,7 +145,7 @@ docker-build: # update old container docker rm laconicd || true # create a new container from the latest image - docker create --name laconic -t -i cerc-io/laconicd:latest laconicd + docker create --name laconic -t -i ${DOCKER_IMAGE}:${DOCKER_TAG} laconicd # move the binaries to the ./build directory mkdir -p ./build/ docker cp laconic:/usr/bin/laconicd ./build/ @@ -316,7 +316,7 @@ TEST_TARGETS := test-unit test-unit-cover test-race # Test runs-specific rules. To add a new test target, just add # a new rule, customise ARGS or TEST_PACKAGES ad libitum, and # append the new rule to the TEST_TARGETS list. -test-unit: ARGS=-timeout=10m -race +test-unit: ARGS=-timeout=10m -race -test.v test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) test-race: ARGS=-race diff --git a/init.sh b/init.sh index bbec499c..1044f67a 100755 --- a/init.sh +++ b/init.sh @@ -5,14 +5,17 @@ CHAINID="laconic_9000-1" MONIKER="localtestnet" KEYRING="test" KEYALGO="eth_secp256k1" -LOGLEVEL="info" +LOGLEVEL="${LOGLEVEL:-info}" # trace evm 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; } + 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/* @@ -31,41 +34,46 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then # Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer) laconicd init $MONIKER --chain-id $CHAINID + update_genesis() { + jq "$1" $HOME/.laconicd/config/genesis.json > $HOME/.laconicd/config/tmp_genesis.json && + mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json + } + # 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 + update_genesis '.app_state["staking"]["params"]["bond_denom"]="aphoton"' + update_genesis '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"' + update_genesis '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' + update_genesis '.app_state["mint"]["params"]["mint_denom"]="aphoton"' # Custom modules - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_commit_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_reveal_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_minimum_bid"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json + update_genesis '.app_state["registry"]["params"]["record_rent"]["denom"]="aphoton"' + update_genesis '.app_state["registry"]["params"]["authority_rent"]["denom"]="aphoton"' + update_genesis '.app_state["registry"]["params"]["authority_auction_commit_fee"]["denom"]="aphoton"' + update_genesis '.app_state["registry"]["params"]["authority_auction_reveal_fee"]["denom"]="aphoton"' + update_genesis '.app_state["registry"]["params"]["authority_auction_minimum_bid"]["denom"]="aphoton"' if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then echo "Setting timers for expiry tests." - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_grace_period"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json - cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json + update_genesis '.app_state["registry"]["params"]["record_rent_duration"]="60s"' + update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="60s"' + update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' fi if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then 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 + update_genesis '.app_state["registry"]["params"]["authority_auction_enabled"]=true' + update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' + update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="300s"' + update_genesis '.app_state["registry"]["params"]["authority_auction_commits_duration"]="60s"' + update_genesis '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"' fi # increase block time (?) - cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["time_iota_ms"]="1000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json + update_genesis '.consensus_params["block"]["time_iota_ms"]="1000"' # Set gas limit in genesis - cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="10000000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json + update_genesis '.consensus_params["block"]["max_gas"]="10000000"' # disable produce empty block if [[ "$OSTYPE" == "darwin"* ]]; then @@ -74,28 +82,22 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then sed -i 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml fi - if [[ $1 == "pending" ]]; then + if [[ "$1" == "pending" ]]; then + alias sed-i="sed -i" 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 + alias sed-i="sed -i ''" fi + sed-i \ + -e 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' \ + -e 's/timeout_propose = "3s"/timeout_propose = "30s"/g' \ + -e 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' \ + -e 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' \ + -e 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' \ + -e 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' \ + -e 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' \ + -e 's/timeout_commit = "5s"/timeout_commit = "150s"/g' \ + -e 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' \ + $HOME/.laconicd/config/config.toml fi # Allocate genesis accounts (cosmos formatted addresses) @@ -110,7 +112,7 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then # Run this to ensure everything worked and that the genesis file is setup correctly laconicd validate-genesis - if [[ $1 == "pending" ]]; then + if [[ "$1" == "pending" ]]; then echo "pending mode is on, please wait for the first block committed." fi else @@ -118,4 +120,11 @@ else fi # Start the node (remove the --pruning=nothing flag if historical queries are not needed) -laconicd start --pruning=nothing --evm.tracer=json $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --json-rpc.api eth,txpool,personal,net,debug,web3,miner --api.enable --gql-server --gql-playground +laconicd start \ + --pruning=nothing \ + --evm.tracer=json $TRACE \ + --log_level $LOGLEVEL \ + --minimum-gas-prices=0.0001aphoton \ + --json-rpc.api eth,txpool,personal,net,debug,web3,miner \ + --api.enable \ + --gql-server --gql-playground diff --git a/proto/vulcanize/registry/v1beta1/registry.proto b/proto/vulcanize/registry/v1beta1/registry.proto index 7d4f9824..3d2d991f 100644 --- a/proto/vulcanize/registry/v1beta1/registry.proto +++ b/proto/vulcanize/registry/v1beta1/registry.proto @@ -55,7 +55,7 @@ message Params { ]; } -// Params defines the registry module records +// Record defines a registry record message Record { string id = 1 [(gogoproto.moretags) = "json:\"id\" yaml:\"id\""]; string bond_id = 2 [(gogoproto.moretags) = "json:\"bondId\" yaml:\"bondId\""]; @@ -68,7 +68,7 @@ message Record { string type = 9 [(gogoproto.moretags) = "json:\"types\" yaml:\"types\""]; } -// AuthorityEntry defines the registry module AuthorityEntries +// AuthorityEntry defines a registry authority message AuthorityEntry { string name = 1; NameAuthority entry = 2; @@ -98,7 +98,7 @@ message NameEntry { NameRecord entry = 2; } -// NameRecord +// NameRecord defines a versioned name record message NameRecord { NameRecordEntry latest = 1; repeated NameRecordEntry history = 2; diff --git a/tests/sdk_tests/Dockerfile-sdk b/tests/sdk_tests/Dockerfile-sdk deleted file mode 100644 index fd9d6cc9..00000000 --- a/tests/sdk_tests/Dockerfile-sdk +++ /dev/null @@ -1,52 +0,0 @@ -# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile -# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster -ARG VARIANT=16-bullseye -FROM node:${VARIANT} - -ARG USERNAME=node -ARG NPM_GLOBAL=/usr/local/share/npm-global - -# Add NPM global to PATH. -ENV PATH=${NPM_GLOBAL}/bin:${PATH} - -RUN \ - # Configure global npm install location, use group to adapt to UID/GID changes - if ! cat /etc/group | grep -e "^npm:" > /dev/null 2>&1; then groupadd -r npm; fi \ - && usermod -a -G npm ${USERNAME} \ - && umask 0002 \ - && mkdir -p ${NPM_GLOBAL} \ - && touch /usr/local/etc/npmrc \ - && chown ${USERNAME}:npm ${NPM_GLOBAL} /usr/local/etc/npmrc \ - && chmod g+s ${NPM_GLOBAL} \ - && npm config -g set prefix ${NPM_GLOBAL} \ - && su ${USERNAME} -c "npm config -g set prefix ${NPM_GLOBAL}" \ - # Install eslint - && su ${USERNAME} -c "umask 0002 && npm install -g eslint" \ - && npm cache clean --force > /dev/null 2>&1 - -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends - -# [Optional] Uncomment if you want to install an additional version of node using nvm -# ARG EXTRA_NODE_VERSION=10 -# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" - -# [Optional] Uncomment if you want to install more global node modules -# RUN su node -c "npm install -g " - -WORKDIR / -COPY entrypoint.sh . -ENTRYPOINT ["/entrypoint.sh"] -# Placeholder CMD : generally this will be overridden at run time like : -# docker run -it -v /home/builder/cerc/laconic-sdk:/workspace cerc/builder-js sh -c 'cd /workspace && yarn && yarn build' -CMD node --version - -# Temp hack, clone the laconic-sdk repo here -WORKDIR /app -RUN \ - git clone https://github.com/cerc-io/laconic-sdk.git \ - && cd laconic-sdk \ - && yarn install - -WORKDIR /app/laconic-sdk diff --git a/tests/sdk_tests/build-laconicd-container.sh b/tests/sdk_tests/build-laconicd-container.sh index ed4b963d..845c3cd0 100755 --- a/tests/sdk_tests/build-laconicd-container.sh +++ b/tests/sdk_tests/build-laconicd-container.sh @@ -1,3 +1,2 @@ #!/usr/bin/env bash -docker build -t cerc-io/laconicd:local-test ../../ - +docker build -t cerc/laconicd:local ../.. --progress=plain diff --git a/tests/sdk_tests/build-sdk-test-container.sh b/tests/sdk_tests/build-sdk-test-container.sh deleted file mode 100755 index c4913889..00000000 --- a/tests/sdk_tests/build-sdk-test-container.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -docker build -t cerc-io/laconic-sdk-tester:local-test -f Dockerfile-sdk . - diff --git a/tests/sdk_tests/docker-compose-auctions.yml b/tests/sdk_tests/docker-compose-auctions.yml deleted file mode 100644 index 9b0df5fd..00000000 --- a/tests/sdk_tests/docker-compose-auctions.yml +++ /dev/null @@ -1,31 +0,0 @@ -services: - laconicd: - restart: unless-stopped - image: cerc-io/laconicd:local-test - environment: - - TEST_AUCTION_ENABLED=true - command: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"] - volumes: - - ../../init.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh - healthcheck: - test: ["CMD", "curl", "-v", "http://127.0.0.1:6060"] - interval: 1s - timeout: 5s - retries: 30 - ports: - - "6060" - - "26657" - - "26656" - - "9473" - - "8545" - - "8546" - - "9090" - - "9091" - - "1317" - - sdk-test-runner: - image: cerc-io/laconic-sdk-tester:local-test - depends_on: - laconicd: - condition: service_healthy - command: tail -F /dev/null diff --git a/tests/sdk_tests/docker-compose-nameservice.yml b/tests/sdk_tests/docker-compose-nameservice.yml deleted file mode 100644 index 17614108..00000000 --- a/tests/sdk_tests/docker-compose-nameservice.yml +++ /dev/null @@ -1,31 +0,0 @@ -services: - laconicd: - restart: unless-stopped - image: cerc-io/laconicd:local-test - environment: - - TEST_REGISTRY_EXPIRY=true - command: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"] - volumes: - - ../../init.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh - healthcheck: - test: ["CMD", "curl", "-v", "http://127.0.0.1:6060"] - interval: 1s - timeout: 5s - retries: 30 - ports: - - "6060" - - "26657" - - "26656" - - "9473" - - "8545" - - "8546" - - "9090" - - "9091" - - "1317" - - sdk-test-runner: - image: cerc-io/laconic-sdk-tester:local-test - depends_on: - laconicd: - condition: service_healthy - command: tail -F /dev/null diff --git a/tests/sdk_tests/docker-compose.yml b/tests/sdk_tests/docker-compose.yml index aa886496..ba6cda6f 100644 --- a/tests/sdk_tests/docker-compose.yml +++ b/tests/sdk_tests/docker-compose.yml @@ -1,10 +1,14 @@ services: laconicd: restart: unless-stopped - image: cerc-io/laconicd:local-test - command: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"] + image: cerc/laconicd:local + entrypoint: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"] + environment: + - TEST_AUCTION_ENABLED + - TEST_REGISTRY_EXPIRY + - LOGLEVEL volumes: - - ../../init.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh + - ../../init.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh healthcheck: test: ["CMD", "curl", "-v", "http://127.0.0.1:6060"] interval: 1s @@ -22,7 +26,7 @@ services: - "1317" sdk-test-runner: - image: cerc-io/laconic-sdk-tester:local-test + image: cerc/laconic-sdk-tester:local depends_on: laconicd: condition: service_healthy diff --git a/tests/sdk_tests/entrypoint.sh b/tests/sdk_tests/entrypoint.sh deleted file mode 100755 index 2bdf572b..00000000 --- a/tests/sdk_tests/entrypoint.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec "$@" - diff --git a/tests/sdk_tests/run-auction-tests.sh b/tests/sdk_tests/run-auction-tests.sh deleted file mode 100755 index d1a05d17..00000000 --- a/tests/sdk_tests/run-auction-tests.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$CERC_SCRIPT_DEBUG" ]; then - set -x -fi -# Get the key from laconicd -laconicd_key=$( docker compose exec laconicd echo y | docker compose exec laconicd laconicd keys export mykey --unarmored-hex --unsafe ) -# Set parameters for the test suite -cosmos_chain_id=laconic_9000-1 -laconicd_rest_endpoint=http://laconicd:1317 -laconicd_gql_endpoint=http://laconicd:9473/api -# Run tests -docker network inspect sdk_tests_default -sleep 30s -docker logs sdk_tests-laconicd-1 - -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:auctions" diff --git a/tests/sdk_tests/run-nameservice-expiry-tests.sh b/tests/sdk_tests/run-nameservice-expiry-tests.sh deleted file mode 100755 index 572eaa46..00000000 --- a/tests/sdk_tests/run-nameservice-expiry-tests.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$CERC_SCRIPT_DEBUG" ]; then - set -x -fi -# Get the key from laconicd -laconicd_key=$( docker compose exec laconicd echo y | docker compose exec laconicd laconicd keys export mykey --unarmored-hex --unsafe ) -# Set parameters for the test suite -cosmos_chain_id=laconic_9000-1 -laconicd_rest_endpoint=http://laconicd:1317 -laconicd_gql_endpoint=http://laconicd:9473/api -# Run tests -docker network inspect sdk_tests_default -sleep 30s -docker logs sdk_tests-laconicd-1 - -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:nameservice-expiry" diff --git a/tests/sdk_tests/run-tests.sh b/tests/sdk_tests/run-tests.sh index 454d1ead..8ad8bf7f 100755 --- a/tests/sdk_tests/run-tests.sh +++ b/tests/sdk_tests/run-tests.sh @@ -1,17 +1,26 @@ #!/usr/bin/env bash +# Forwards all args to yarn on the sdk-test-runner container + if [ -n "$CERC_SCRIPT_DEBUG" ]; then set -x fi + +yarn_args=("--inspect-brk=8888") +yarn_args+=("${@:-test}") + # Get the key from laconicd -laconicd_key=$( docker compose exec laconicd echo y | docker compose exec laconicd laconicd keys export mykey --unarmored-hex --unsafe ) +laconicd_key=$( + yes | docker compose exec laconicd laconicd keys export mykey --unarmored-hex --unsafe +) # Set parameters for the test suite cosmos_chain_id=laconic_9000-1 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" -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" +# Run tests +docker compose exec \ + -e COSMOS_CHAIN_ID="$cosmos_chain_id" \ + -e LACONICD_REST_ENDPOINT="$laconicd_rest_endpoint" \ + -e LACONICD_GQL_ENDPOINT="$laconicd_gql_endpoint" \ + -e PRIVATE_KEY="$laconicd_key" \ + sdk-test-runner yarn run "${yarn_args[@]}" diff --git a/testutil/network/network.go b/testutil/network/network.go index c235bc3c..0a7f30e5 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -519,6 +519,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { l.Log("starting test network...") for _, v := range network.Validators { + l.Log("starting validator:", v.Moniker) err := startInProcess(cfg, v) if err != nil { return nil, err diff --git a/x/registry/client/testutil/grpc.go b/x/registry/client/testutil/grpc.go index a180deea..5bd422ab 100644 --- a/x/registry/client/testutil/grpc.go +++ b/x/registry/client/testutil/grpc.go @@ -44,7 +44,8 @@ func (s *IntegrationTestSuite) TestGRPCQueryParams() { for _, tc := range testCases { s.Run(tc.name, func() { - resp, _ := rest.GetRequest(tc.url) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -62,7 +63,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryParams() { } } -//nolint:all +//nolint: all func (s *IntegrationTestSuite) TestGRPCQueryWhoIs() { val := s.network.Validators[0] sr := s.Require() @@ -112,11 +113,11 @@ func (s *IntegrationTestSuite) TestGRPCQueryWhoIs() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(authorityName) - tc.url = fmt.Sprintf(tc.url, authorityName) - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun(authorityName) + tc.url = fmt.Sprintf(tc.url, authorityName) + + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -133,7 +134,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryWhoIs() { func (s *IntegrationTestSuite) TestGRPCQueryLookup() { val := s.network.Validators[0] sr := s.Require() - reqURL := val.APIAddress + "/vulcanize/registry/v1beta1/lookup?crn=%s" + reqURL := val.APIAddress + "/vulcanize/registry/v1beta1/lookup" authorityName := "QueryLookUp" testCases := []struct { @@ -153,7 +154,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryLookup() { }, { "Success", - reqURL, + fmt.Sprintf(reqURL+"?crn=crn://%s/", authorityName), false, "", func(authorityName string) { @@ -165,11 +166,9 @@ func (s *IntegrationTestSuite) TestGRPCQueryLookup() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(authorityName) - tc.url = fmt.Sprintf(reqURL, fmt.Sprintf("crn://%s/", authorityName)) - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun(authorityName) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) if tc.expectErr { sr.Contains(string(resp), tc.errorMsg) } else { @@ -182,7 +181,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryLookup() { } } -//nolint:all +//nolint: all func (s *IntegrationTestSuite) TestGRPCQueryRecordExpiryQueue() { val := s.network.Validators[0] sr := s.Require() @@ -235,12 +234,11 @@ func (s *IntegrationTestSuite) TestGRPCQueryRecordExpiryQueue() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(s.bondID) - } + tc.preRun(s.bondID) // wait 12 seconds for records expires time.Sleep(time.Second * 12) - resp, _ := rest.GetRequest(tc.url) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -254,7 +252,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryRecordExpiryQueue() { } } -//nolint:all +//nolint: all func (s *IntegrationTestSuite) TestGRPCQueryAuthorityExpiryQueue() { val := s.network.Validators[0] sr := s.Require() @@ -305,13 +303,12 @@ func (s *IntegrationTestSuite) TestGRPCQueryAuthorityExpiryQueue() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun("QueryAuthorityExpiryQueue") - } + tc.preRun("QueryAuthorityExpiryQueue") // wait 12 seconds to name authorites expires time.Sleep(time.Second * 12) - resp, _ := rest.GetRequest(tc.url) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -326,7 +323,7 @@ func (s *IntegrationTestSuite) TestGRPCQueryAuthorityExpiryQueue() { } } -//nolint:all +//nolint: all func (s *IntegrationTestSuite) TestGRPCQueryListRecords() { val := s.network.Validators[0] sr := s.Require() @@ -379,10 +376,9 @@ func (s *IntegrationTestSuite) TestGRPCQueryListRecords() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(s.bondID) - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun(s.bondID) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -445,12 +441,11 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRecordByID() { for _, tc := range testCases { s.Run(tc.name, func() { - var recordID string - if !tc.expectErr { - recordID = tc.preRun(s.bondID) - tc.url = fmt.Sprintf(reqURL, recordID) - } - resp, _ := rest.GetRequest(tc.url) + recordID := tc.preRun(s.bondID) + tc.url = fmt.Sprintf(reqURL, recordID) + + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -500,11 +495,11 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRecordByBondID() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(s.bondID) - tc.url = fmt.Sprintf(reqURL, s.bondID) - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun(s.bondID) + tc.url = fmt.Sprintf(reqURL, s.bondID) + + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -554,10 +549,9 @@ func (s *IntegrationTestSuite) TestGRPCQueryGetRegistryModuleBalance() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun(s.bondID) - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun(s.bondID) + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -605,10 +599,9 @@ func (s *IntegrationTestSuite) TestGRPCQueryNamesList() { for _, tc := range testCases { s.Run(tc.name, func() { - if !tc.expectErr { - tc.preRun("ListNameRecords") - } - resp, _ := rest.GetRequest(tc.url) + tc.preRun("ListNameRecords") + resp, err := rest.GetRequest(tc.url) + s.NoError(err) require := s.Require() if tc.expectErr { require.Contains(string(resp), tc.errorMsg) @@ -645,5 +638,5 @@ func createRecord(bondID string, s *IntegrationTestSuite) { var d sdk.TxResponse err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) sr.NoError(err) - sr.Zero(d.Code) + sr.Zero(d.Code, d.RawLog) } diff --git a/x/registry/client/testutil/query.go b/x/registry/client/testutil/query.go index e0a433ff..1e37ce8e 100644 --- a/x/registry/client/testutil/query.go +++ b/x/registry/client/testutil/query.go @@ -538,9 +538,6 @@ func createNameRecord(authorityName string, s *IntegrationTestSuite) { sr.NoError(err) sr.Zero(d.Code) - // creating the bond - CreateBond(s) - // Get the bond-id bondID := GetBondID(s) diff --git a/x/registry/client/testutil/tx.go b/x/registry/client/testutil/tx.go index f7e2bea0..e7455a05 100644 --- a/x/registry/client/testutil/tx.go +++ b/x/registry/client/testutil/tx.go @@ -101,42 +101,23 @@ func CreateBond(s *IntegrationTestSuite) { val := s.network.Validators[0] sr := s.Require() - testCases := []struct { - name string - args []string - err bool - }{ - { - "create bond", - []string{ - fmt.Sprintf("100000000000%s", s.cfg.BondDenom), - fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", s.cfg.BondDenom)), - }, - false, - }, + clientCtx := val.ClientCtx + cmd := bondcli.NewCreateBondCmd() + args := []string{ + fmt.Sprintf("100000000000%s", s.cfg.BondDenom), + fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", s.cfg.BondDenom)), } + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) - for _, tc := range testCases { - s.Run(fmt.Sprintf("Case %s", tc.name), func() { - clientCtx := val.ClientCtx - cmd := bondcli.NewCreateBondCmd() - - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - if tc.err { - sr.Error(err) - } else { - sr.NoError(err) - var d sdk.TxResponse - err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) - sr.NoError(err) - sr.Zero(d.Code) - } - }) - } + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) } func GetBondID(s *IntegrationTestSuite) string { @@ -160,6 +141,11 @@ func (s *IntegrationTestSuite) TestGetCmdSetRecord() { val := s.network.Validators[0] sr := s.Require() + bondID := GetBondID(s) + dir, err := os.Getwd() + sr.NoError(err) + payloadPath := dir + "/service_provider_example.yml" + testCases := []struct { name string args []string @@ -179,6 +165,7 @@ func (s *IntegrationTestSuite) TestGetCmdSetRecord() { { "success", []string{ + payloadPath, bondID, fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=json", tmcli.OutputFlag), @@ -191,17 +178,6 @@ func (s *IntegrationTestSuite) TestGetCmdSetRecord() { for _, tc := range testCases { s.Run(fmt.Sprintf("Case %s", tc.name), func() { - if !tc.err { - // create the bond - CreateBond(s) - // get the bond id from bond list - bondID := GetBondID(s) - dir, err := os.Getwd() - sr.NoError(err) - payloadPath := dir + "/service_provider_example.yml" - - tc.args = append([]string{payloadPath, bondID}, tc.args...) - } clientCtx := val.ClientCtx cmd := cli.GetCmdSetRecord() @@ -341,9 +317,6 @@ func (s *IntegrationTestSuite) TestGetCmdSetName() { sr.NoError(err) sr.Zero(d.Code) - // creating the bond - CreateBond(s) - // Get the bond-id bondID := GetBondID(s) @@ -369,9 +342,7 @@ func (s *IntegrationTestSuite) TestGetCmdSetName() { for _, tc := range testCases { s.Run(fmt.Sprintf("Case %s", tc.name), func() { - if !tc.err { - tc.preRun(authorityName) - } + tc.preRun(authorityName) clientCtx := val.ClientCtx cmd := cli.GetCmdSetName() @@ -394,6 +365,7 @@ func (s *IntegrationTestSuite) TestGetCmdSetAuthorityBond() { val := s.network.Validators[0] sr := s.Require() authorityName := "TestGetCmdSetAuthorityBond" + bondID := GetBondID(s) testCases := []struct { name string @@ -417,6 +389,7 @@ func (s *IntegrationTestSuite) TestGetCmdSetAuthorityBond() { { "success with name and bond-id", []string{ + authorityName, bondID, fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=json", tmcli.OutputFlag), @@ -449,15 +422,7 @@ func (s *IntegrationTestSuite) TestGetCmdSetAuthorityBond() { for _, tc := range testCases { s.Run(fmt.Sprintf("Case %s", tc.name), func() { - if !tc.err { - // reserve the name - tc.preRun(authorityName) - // creating the bond - CreateBond(s) - // getting the bond-id - bondID := GetBondID(s) - tc.args = append([]string{authorityName, bondID}, tc.args...) - } + tc.preRun(authorityName) clientCtx := val.ClientCtx cmd := cli.GetCmdSetAuthorityBond() @@ -501,6 +466,7 @@ func (s *IntegrationTestSuite) TestGetCmdDeleteName() { { "successfully delete name", []string{ + fmt.Sprintf("crn://%s/", authorityName), fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=json", tmcli.OutputFlag), @@ -516,10 +482,8 @@ func (s *IntegrationTestSuite) TestGetCmdDeleteName() { for _, tc := range testCasesForDeletingName { s.Run(fmt.Sprintf("Case %s", tc.name), func() { - if !tc.err { - tc.preRun(authorityName, s) - tc.args = append([]string{fmt.Sprintf("crn://%s/", authorityName)}, tc.args...) - } + tc.preRun(authorityName, s) + clientCtx := val.ClientCtx cmd := cli.GetCmdDeleteName() @@ -574,8 +538,6 @@ func (s *IntegrationTestSuite) TestGetCmdDissociateBond() { }, false, func(s *IntegrationTestSuite) string { - // create the bond - CreateBond(s) // get the bond id from bond list bondID := GetBondID(s) dir, err := os.Getwd() @@ -816,8 +778,6 @@ func (s *IntegrationTestSuite) TestGetCmdAssociateBond() { }, false, func(s *IntegrationTestSuite) (string, string) { - // create the bond - CreateBond(s) // get the bond id from bond list bondID := GetBondID(s) dir, err := os.Getwd() diff --git a/x/registry/keeper/msg_server.go b/x/registry/keeper/msg_server.go index 775b6c6e..9a16463c 100644 --- a/x/registry/keeper/msg_server.go +++ b/x/registry/keeper/msg_server.go @@ -25,11 +25,7 @@ func (m msgServer) SetRecord(c context.Context, msg *types.MsgSetRecord) (*types return nil, err } - record, err := m.Keeper.ProcessSetRecord(ctx, types.MsgSetRecord{ - BondId: msg.GetBondId(), - Signer: msg.GetSigner(), - Payload: msg.GetPayload(), - }) + record, err := m.Keeper.ProcessSetRecord(ctx, *msg) if err != nil { return nil, err }