diff --git a/.github/workflows/realease.yml b/.github/workflows/realease.yml new file mode 100644 index 00000000..9c141329 --- /dev/null +++ b/.github/workflows/realease.yml @@ -0,0 +1,46 @@ +# triggered when a semantic version tag is pushed (vX.X.X) +name: Release +on: + push: + tags: + - "*" + - "v[0-9]+\\.[0-9]+\\.[0-9]+-alpha[0-9]+" # vX.X.X-alphaX + - "v[0-9]+\\.[0-9]+\\.[0-9]+-beta[0-9]+" # vX.X.X-betaX + - "v[0-9]+\\.[0-9]+\\.[0-9]+-rc[0-9]+" # vX.X.X-rcX + - "v[0-9]+\\.[0-9]+\\.[0-9]+" # vX.X.X +concurrency: + group: ci-${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v4 + with: + go-version: "1.21" + cache: true + + - name: Set ENV + run: echo "COMET_VERSION=$(go list -m github.com/cometbft/cometbft | sed 's:.* ::')" >> $GITHUB_ENV + + - name: goreleaser test-build + uses: goreleaser/goreleaser-action@v5 + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Enable:ReleaseBuild') + with: + version: latest + args: build --clean --skip=validate + env: + COMET_VERSION: ${{ env.COMET_VERSION }} + - name: Release + uses: goreleaser/goreleaser-action@v5 + if: startsWith(github.ref, 'refs/tags/') + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMET_VERSION: ${{ env.COMET_VERSION }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..cbc6a5b2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,84 @@ +name: Tests / Code Coverage +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened, labeled] + merge_group: + types: [checks_requested] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }}-tests + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + arch: [amd64, arm64] + targetos: [darwin, linux] + name: sedad ${{ matrix.targetos }}-${{ matrix.arch }} + steps: + - uses: actions/checkout@v4 + - name: Cache binaries + id: cache-binaries + uses: actions/cache@v3 + with: + path: ./cmd/sedad/sedad + key: sedad-${{ matrix.targetos }}-${{ matrix.arch }} + - uses: technote-space/get-diff-action@v6.1.2 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - name: Setup go + if: steps.cache-binaries.outputs.cache-hit != 'true' && env.GIT_DIFF + uses: actions/setup-go@v4 + with: + go-version: "1.19" + cache: true + env: + GOOS: ${{ matrix.targetos }} + GOARCH: ${{ matrix.arch }} + - name: Compile + if: steps.cache-binaries.outputs.cache-hit != 'true' && env.GIT_DIFF + run: | + go mod download + make build + + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 + with: + go-version: "1.21" + check-latest: true + cache: true + cache-dependency-path: go.sum + - uses: technote-space/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + **/*.go + go.mod + go.sum + **/go.mod + **/go.sum + **/Makefile + Makefile + - name: test & coverage report creation + if: env.GIT_DIFF + run: make test-unit-cover + - uses: actions/upload-artifact@v3 + if: env.GIT_DIFF + with: + name: "${{ github.sha }}-${{ matrix.part }}-coverage" + path: ./${{ matrix.part }}profile.out + diff --git a/.gitignore b/.gitignore index 1badcf89..86efe83e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ build/ .idea/ .vscode/ .DS_Store +dist/ +/.act-event-file scripts/testnet/config.sh scripts/testnet/nodes diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000..a1261a8c --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,44 @@ +project_name: seda + +env: + - CGO_ENABLED=1 +# - GOOS=linux # for local m1 mac run + +before: + hooks: + - go mod download + +builds: + - main: ./cmd/seda-chaind + id: "sedad" + binary: sedad + mod_timestamp: "{{ .CommitTimestamp }}" + flags: + - -tags=badgerdb ledger netgo + - -trimpath + ldflags: + - -s -w -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X github.com/cosmos/cosmos-sdk/version.Name=seda-chain -X github.com/cosmos/cosmos-sdk/version.AppName=sedad-chain -X github.com/cosmos/cosmos-sdk/version.Version={{ .Version }} -X github.com/cosmos/cosmos-sdk/version.Commit={{ .Commit }} -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger -X github.com/cometbft/cometbft/version.TMCoreSemVer={{ .Env.COMET_VERSION }} + goos: + - linux + goarch: + - amd64 +# - arm64 # github only supports linux @ amd64 :'( + +archives: + - id: tarball + format: tar.gz + wrap_in_directory: false # must not wrap into directory to support cosmwasm + name_template: "{{ .Binary }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + files: + - LICENSE + - README.md + +snapshot: + name_template: SNAPSHOT-{{ .Commit }} + +checksum: + name_template: SHA256SUMS-v{{.Version}}.txt + algorithm: sha256 + +changelog: + skip: false \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..6fc8f963 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM golang:1.21-alpine AS build-env + +# Install minimum necessary dependencies +ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev +RUN apk add --no-cache $PACKAGES + + +WORKDIR /go/src/github.com/sedaprotocol/seda-chain + +# Optimized fetching of dependencies +COPY go.mod go.sum ./ + +RUN go mod download + +# Copy and build the project +COPY . . + +# Dockerfile Cross-Compilation Guide +# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide +ARG TARGETOS TARGETARCH + + +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} make build + + +FROM alpine:3 + +RUN apk add --no-cache curl make bash jq sed +COPY --from=build-env /go/src/github.com/cosmos/cosmos-sdk/build/seda-chaid /usr/bin/seda-chaind + +EXPOSE 26656 26657 1317 9090 + +CMD ["seda-chaind", "start"] +STOPSIGNAL SIGTERM +WORKDIR /root diff --git a/Makefile b/Makefile index a5e74a59..ced4c52d 100644 --- a/Makefile +++ b/Makefile @@ -131,3 +131,45 @@ proto-update-deps: $(DOCKER) run --rm -v $(CURDIR)/proto:/workspace --workdir /workspace $(protoImageName) buf mod update .PHONY: proto-gen proto-lint proto-update-deps + +############################################################################### +## Tests ## +############################################################################### + +PACKAGES_UNIT=$(shell go list ./...) +TEST_PACKAGES=./... +TEST_TARGETS := test-unit test-unit-cover test-race +TEST_COVERAGE_PROFILE=coverage.txt + +UNIT_TEST_TAGS = norace +TEST_RACE_TAGS = "" + +ifeq ($(EXPERIMENTAL),true) + UNIT_TEST_TAGS += experimental + TEST_RACE_TAGS += experimental +endif + +test-unit: ARGS=-timeout=10m -tags='$(UNIT_TEST_TAGS)' +test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) +test-unit-cover: ARGS=-timeout=10m -tags='$(UNIT_TEST_TAGS)' -coverprofile=$(TEST_COVERAGE_PROFILE) -covermode=atomic +test-unit-cover: TEST_PACKAGES=$(PACKAGES_UNIT) +test-race: ARGS=-timeout=10m -race -tags='$(TEST_RACE_TAGS)' +test-race: TEST_PACKAGES=$(PACKAGES_UNIT) +$(TEST_TARGETS): run-tests + +run-tests: +ifneq (,$(shell which tparse 2>/dev/null)) + @echo "--> Running tests" + @go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) | tparse +else + @echo "--> Running tests" + @go test -mod=readonly $(ARGS) $(TEST_PACKAGES) +endif + +cover-html: test-unit-cover + @echo "--> Opening in the browser" + @go tool cover -html=$(TEST_COVERAGE_PROFILE) + +.PHONY: cover-html run-tests $(TEST_TARGETS) + + diff --git a/x/wasm-storage/keeper/msg_server.go b/x/wasm-storage/keeper/msg_server.go index ee7c3623..6957fc2e 100644 --- a/x/wasm-storage/keeper/msg_server.go +++ b/x/wasm-storage/keeper/msg_server.go @@ -132,7 +132,7 @@ func unzipWasm(wasm []byte) ([]byte, error) { } unzipped, err = ioutils.Uncompress(wasm, types.MaxWasmSize) if err != nil { - return unzipped, nil + return nil, err } return unzipped, nil } diff --git a/x/wasm-storage/keeper/msg_server_test.go b/x/wasm-storage/keeper/msg_server_test.go index 721de1f8..3b0daefc 100644 --- a/x/wasm-storage/keeper/msg_server_test.go +++ b/x/wasm-storage/keeper/msg_server_test.go @@ -11,9 +11,15 @@ import ( ) func (s *KeeperTestSuite) TestStoreDataRequestWasm() { - wasm, err := os.ReadFile("test_utils/hello-world.wasm") + regWasm, err := os.ReadFile("test_utils/hello-world.wasm") + s.Require().NoError(err) + regWasmZipped, err := ioutils.GzipIt(regWasm) + s.Require().NoError(err) + + oversizedWasm, err := os.ReadFile("test_utils/oversized.wasm") + s.Require().NoError(err) + oversizedWasmZipped, err := ioutils.GzipIt(oversizedWasm) s.Require().NoError(err) - compWasm, err := ioutils.GzipIt(wasm) cases := []struct { name string @@ -28,32 +34,66 @@ func (s *KeeperTestSuite) TestStoreDataRequestWasm() { preRun: func() {}, input: types.MsgStoreDataRequestWasm{ Sender: s.authority, - Wasm: compWasm, + Wasm: regWasmZipped, WasmType: types.WasmTypeDataRequest, }, expErr: false, expOutput: types.MsgStoreDataRequestWasmResponse{ - Hash: hex.EncodeToString(crypto.Keccak256(wasm)), + Hash: hex.EncodeToString(crypto.Keccak256(regWasm)), }, }, { - name: "Overlay wasm already exist", + name: "Data Request wasm already exist", input: types.MsgStoreDataRequestWasm{ Sender: s.authority, - Wasm: compWasm, + Wasm: regWasmZipped, WasmType: types.WasmTypeDataRequest, }, preRun: func() { input := types.MsgStoreDataRequestWasm{ Sender: s.authority, - Wasm: compWasm, + Wasm: regWasmZipped, WasmType: types.WasmTypeDataRequest, } _, err := s.msgSrvr.StoreDataRequestWasm(s.ctx, &input) s.Require().Nil(err) }, expErr: true, - expErrMsg: "Data Request Wasm with given hash already exists", + expErrMsg: "data Request Wasm with given hash already exists", + }, + // TO-DO: Add after migrating ValidateBasic logic + // { + // name: "inconsistent Wasm type", + // input: types.MsgStoreDataRequestWasm{ + // Sender: s.authority, + // Wasm: regWasmZipped, + // WasmType: types.WasmTypeRelayer, + // }, + // preRun: func() {}, + // expErr: true, + // expErrMsg: "not a Data Request Wasm", + // }, + { + name: "unzipped Wasm", + input: types.MsgStoreDataRequestWasm{ + Sender: s.authority, + Wasm: regWasm, + WasmType: types.WasmTypeDataRequest, + }, + preRun: func() {}, + expErr: true, + expErrMsg: "wasm is not gzip compressed", + }, + { + name: "oversized Wasm", + input: types.MsgStoreDataRequestWasm{ + Sender: s.authority, + Wasm: oversizedWasmZipped, + WasmType: types.WasmTypeDataRequest, + }, + preRun: func() {}, + expErr: true, + expErrMsg: "", }, } for _, tc := range cases { @@ -62,7 +102,7 @@ func (s *KeeperTestSuite) TestStoreDataRequestWasm() { tc.preRun() res, err := s.msgSrvr.StoreDataRequestWasm(s.ctx, &tc.input) if tc.expErr { - s.Require().Error(err, tc.expErrMsg) + s.Require().ErrorContains(err, tc.expErrMsg) } else { s.Require().Nil(err) s.Require().Equal(tc.expOutput, *res) @@ -72,9 +112,16 @@ func (s *KeeperTestSuite) TestStoreDataRequestWasm() { } func (s *KeeperTestSuite) TestStoreOverlayWasm() { - wasm, err := os.ReadFile("test_utils/hello-world.wasm") + regWasm, err := os.ReadFile("test_utils/hello-world.wasm") + s.Require().NoError(err) + regWasmZipped, err := ioutils.GzipIt(regWasm) + s.Require().NoError(err) + + oversizedWasm, err := os.ReadFile("test_utils/oversized.wasm") + s.Require().NoError(err) + oversizedWasmZipped, err := ioutils.GzipIt(oversizedWasm) s.Require().NoError(err) - compWasm, err := ioutils.GzipIt(wasm) + cases := []struct { name string preRun func() @@ -87,45 +134,78 @@ func (s *KeeperTestSuite) TestStoreOverlayWasm() { name: "happy path", input: types.MsgStoreOverlayWasm{ Sender: s.authority, - Wasm: compWasm, - WasmType: types.WasmTypeDataRequest, + Wasm: regWasmZipped, + WasmType: types.WasmTypeRelayer, }, preRun: func() {}, expErr: false, expErrMsg: "", expOutput: types.MsgStoreOverlayWasmResponse{ - Hash: hex.EncodeToString(crypto.Keccak256(wasm)), + Hash: hex.EncodeToString(crypto.Keccak256(regWasm)), }, }, { - name: "invalid authority", + name: "invalid wasm type", input: types.MsgStoreOverlayWasm{ - Sender: "this-is-not-valid", - Wasm: compWasm, + Sender: s.authority, + Wasm: regWasm, WasmType: types.WasmTypeDataRequest, }, preRun: func() {}, expErr: true, - expErrMsg: "invalid authority this-is-not-valid", + expErrMsg: "Overlay Wasm type must be data-request-executor or relayer", + }, + { + name: "invalid authority", + input: types.MsgStoreOverlayWasm{ + Sender: "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", + Wasm: regWasmZipped, + WasmType: types.WasmTypeRelayer, + }, + preRun: func() {}, + expErr: true, + expErrMsg: "invalid authority", }, { name: "Overlay wasm already exist", input: types.MsgStoreOverlayWasm{ Sender: s.authority, - Wasm: compWasm, - WasmType: types.WasmTypeDataRequest, + Wasm: regWasmZipped, + WasmType: types.WasmTypeRelayer, }, preRun: func() { input := types.MsgStoreOverlayWasm{ Sender: s.authority, - Wasm: compWasm, - WasmType: types.WasmTypeDataRequest, + Wasm: regWasmZipped, + WasmType: types.WasmTypeRelayer, } _, err := s.msgSrvr.StoreOverlayWasm(s.ctx, &input) s.Require().Nil(err) }, expErr: true, - expErrMsg: "Overlay Wasm with given hash already exists", + expErrMsg: "overlay Wasm with given hash already exists", + }, + { + name: "unzipped Wasm", + input: types.MsgStoreOverlayWasm{ + Sender: s.authority, + Wasm: regWasm, + WasmType: types.WasmTypeRelayer, + }, + preRun: func() {}, + expErr: true, + expErrMsg: "wasm is not gzip compressed", + }, + { + name: "oversized Wasm", + input: types.MsgStoreOverlayWasm{ + Sender: s.authority, + Wasm: oversizedWasmZipped, + WasmType: types.WasmTypeRelayer, + }, + preRun: func() {}, + expErr: true, + expErrMsg: "", }, } for _, tc := range cases { @@ -134,7 +214,7 @@ func (s *KeeperTestSuite) TestStoreOverlayWasm() { tc.preRun() res, err := s.msgSrvr.StoreOverlayWasm(s.ctx, &tc.input) if tc.expErr { - s.Require().Error(err, tc.expErrMsg) + s.Require().ErrorContains(err, tc.expErrMsg) } else { s.Require().Nil(err) s.Require().Equal(tc.expOutput, *res) diff --git a/x/wasm-storage/keeper/querier_test.go b/x/wasm-storage/keeper/querier_test.go index 110c065b..2b22931f 100644 --- a/x/wasm-storage/keeper/querier_test.go +++ b/x/wasm-storage/keeper/querier_test.go @@ -38,7 +38,7 @@ func (s *KeeperTestSuite) TestOverlayWasm() { input := types.MsgStoreOverlayWasm{ Sender: s.authority, Wasm: compWasm, - WasmType: types.WasmTypeDataRequest, + WasmType: types.WasmTypeDataRequestExecutor, } storedWasm, err := s.msgSrvr.StoreOverlayWasm(s.ctx, &input) s.Require().NoError(err) @@ -91,7 +91,7 @@ func (s *KeeperTestSuite) TestOverlayWasms() { input := types.MsgStoreOverlayWasm{ Sender: s.authority, Wasm: compWasm, - WasmType: types.WasmTypeDataRequest, + WasmType: types.WasmTypeRelayer, } storedWasm, err := s.msgSrvr.StoreOverlayWasm(s.ctx, &input) s.Require().NoError(err) @@ -102,7 +102,7 @@ func (s *KeeperTestSuite) TestOverlayWasms() { input2 := types.MsgStoreOverlayWasm{ Sender: s.authority, Wasm: compWasm2, - WasmType: types.WasmTypeDataRequest, + WasmType: types.WasmTypeRelayer, } storedWasm2, err := s.msgSrvr.StoreOverlayWasm(s.ctx, &input2) s.Require().NoError(err) @@ -111,6 +111,6 @@ func (s *KeeperTestSuite) TestOverlayWasms() { res, err := s.queryClient.OverlayWasms(s.ctx, &req) s.Require().NoError(err) s.Require().NotNil(res) - s.Require().Equal(fmt.Sprintf("%s,%s", storedWasm.Hash, "WASM_TYPE_DATA_REQUEST"), res.HashTypePairs[0]) - s.Require().Equal(fmt.Sprintf("%s,%s", storedWasm2.Hash, "WASM_TYPE_DATA_REQUEST"), res.HashTypePairs[1]) + s.Require().Equal(fmt.Sprintf("%s,%s", storedWasm.Hash, "WASM_TYPE_RELAYER"), res.HashTypePairs[0]) + s.Require().Equal(fmt.Sprintf("%s,%s", storedWasm2.Hash, "WASM_TYPE_RELAYER"), res.HashTypePairs[1]) } diff --git a/x/wasm-storage/keeper/test_utils/oversized.wasm b/x/wasm-storage/keeper/test_utils/oversized.wasm new file mode 100644 index 00000000..6e315c53 Binary files /dev/null and b/x/wasm-storage/keeper/test_utils/oversized.wasm differ