├── openapi ├── .openapi-generator │ ├── VERSION │ ├── go-config.yml │ └── FILES ├── .travis.yml ├── go.mod ├── .gitignore ├── go.sum ├── test │ ├── api_chainsync_test.go │ ├── api_default_test.go │ ├── api_localtxsubmission_test.go │ ├── api_localtxmonitor_test.go │ └── api_localstatequery_test.go ├── .openapi-generator-ignore ├── response.go ├── docs │ ├── ApiResponseApiError.md │ ├── ApiResponseLocalTxMonitorHasTx.md │ ├── DefaultAPI.md │ ├── LocaltxsubmissionAPI.md │ ├── ChainsyncAPI.md │ ├── ApiResponseLocalTxMonitorTxs.md │ ├── ApiResponseLocalStateQueryCurrentEra.md │ ├── ApiResponseLocalTxMonitorSizes.md │ ├── ApiResponseLocalStateQuerySystemStart.md │ ├── ApiResponseLocalStateQueryTip.md │ └── LocaltxmonitorAPI.md ├── model_api_response_api_error.go ├── model_api_response_local_tx_monitor_has_tx.go ├── model_api_response_local_tx_monitor_txs.go ├── model_api_response_local_state_query_current_era.go ├── README.md ├── api_chainsync.go ├── model_api_response_local_tx_monitor_sizes.go ├── model_api_response_local_state_query_system_start.go ├── api_default.go ├── api_localtxsubmission.go ├── configuration.go ├── model_api_response_local_state_query_tip.go └── utils.go ├── openapi-config.yml ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── conventional-commits.yml │ ├── nilaway.yml │ ├── go-test.yml │ ├── golangci-lint.yml │ ├── ci-docker.yml │ └── publish.yml ├── Dockerfile ├── openapi.sh ├── .gitignore ├── internal ├── version │ └── version.go ├── node │ ├── localtxsubmission.go │ ├── localtxmonitor.go │ ├── localstatequery.go │ ├── node.go │ └── chainsync.go ├── logging │ └── logging.go ├── api │ ├── chainsync.go │ ├── localtxsubmission.go │ ├── api.go │ └── localtxmonitor.go ├── utxorpc │ ├── api.go │ ├── watch.go │ └── sync.go └── config │ └── config.go ├── .golangci.yml ├── Makefile ├── cmd └── cardano-node-api │ └── main.go ├── config.yaml.example ├── go.mod └── README.md /openapi/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 7.14.0 2 | -------------------------------------------------------------------------------- /openapi/.openapi-generator/go-config.yml: -------------------------------------------------------------------------------- 1 | enumClassPrefix: true 2 | generateInterfaces: true 3 | structPrefix: true 4 | -------------------------------------------------------------------------------- /openapi-config.yml: -------------------------------------------------------------------------------- 1 | enumClassPrefix: true 2 | generateInterfaces: true 3 | structPrefix: true 4 | isGoSubmodule: true 5 | -------------------------------------------------------------------------------- /openapi/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | - go get -d -v . 5 | 6 | script: 7 | - go build -v ./ 8 | 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Blink Labs 2 | # 3 | * @blinklabs-io/core 4 | *.md @blinklabs-io/core @blinklabs-io/docs @blinklabs-io/pms 5 | LICENSE @blinklabs-io/core @blinklabs-io/pms 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/blinklabs-io/go:1.25.5-1 AS build 2 | 3 | WORKDIR /app 4 | COPY . . 5 | RUN make build 6 | 7 | FROM cgr.dev/chainguard/glibc-dynamic 8 | COPY --from=0 /app/cardano-node-api /bin/ 9 | USER root 10 | ENTRYPOINT ["cardano-node-api"] 11 | -------------------------------------------------------------------------------- /openapi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate -i /local/docs/swagger.yaml --git-user-id blinklabs-io --git-repo-id cardano-node-api -g go -o /local/openapi -c /local/openapi-config.yml 4 | make format golines 5 | cd openapi && go mod tidy 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | /cardano-node-api 3 | 4 | # Test binary, built with `go test -c` 5 | *.test 6 | 7 | # Output of the go coverage tool, specifically when used with LiteIDE 8 | *.out 9 | 10 | # Dependency directories (remove the comment below to include it) 11 | # vendor/ 12 | -------------------------------------------------------------------------------- /openapi/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blinklabs-io/cardano-node-api/openapi 2 | 3 | go 1.18 4 | 5 | require github.com/stretchr/testify v1.10.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /openapi/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "docker" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | - package-ecosystem: "gomod" 17 | directory: "/" 18 | schedule: 19 | interval: "weekly" 20 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | # The below is pulled from upstream and slightly modified 2 | # https://github.com/webiny/action-conventional-commits/blob/master/README.md#usage 3 | 4 | name: Conventional Commits 5 | 6 | on: 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | name: Conventional Commits 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | steps: 16 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 17 | - uses: webiny/action-conventional-commits@8bc41ff4e7d423d56fa4905f6ff79209a78776c7 # v1.3.0 https://github.com/webiny/action-conventional-commits/releases/tag/v1.3.0 18 | -------------------------------------------------------------------------------- /.github/workflows/nilaway.yml: -------------------------------------------------------------------------------- 1 | name: nilaway 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - main 8 | pull_request: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | nilaway: 15 | name: nilaway 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 19 | - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 https://github.com/actions/setup-go/releases/tag/v6.1.0 20 | with: 21 | go-version: 1.25.x 22 | - name: install nilaway 23 | run: go install go.uber.org/nilaway/cmd/nilaway@latest 24 | - name: run nilaway 25 | run: nilaway ./... 26 | -------------------------------------------------------------------------------- /.github/workflows/go-test.yml: -------------------------------------------------------------------------------- 1 | name: go-test 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | branches: 8 | - main 9 | pull_request: 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | go-test: 16 | name: go-test 17 | strategy: 18 | matrix: 19 | go-version: [1.24.x, 1.25.x] 20 | platform: [ubuntu-latest] 21 | runs-on: ${{ matrix.platform }} 22 | steps: 23 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 24 | - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 https://github.com/actions/setup-go/releases/tag/v6.1.0 25 | with: 26 | go-version: ${{ matrix.go-version }} 27 | - name: go-test 28 | run: go test ./... 29 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - main 8 | pull_request: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | golangci: 15 | name: lint 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 19 | - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 https://github.com/actions/setup-go/releases/tag/v6.1.0 20 | with: 21 | go-version: 1.25.x 22 | - name: golangci-lint 23 | uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 https://github.com/golangci/golangci-lint-action/releases/tag/v9.2.0 24 | -------------------------------------------------------------------------------- /openapi/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /openapi/test/api_chainsync_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Testing ChainsyncAPIService 5 | 6 | */ 7 | 8 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); 9 | 10 | package openapi 11 | 12 | import ( 13 | "context" 14 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | "testing" 18 | ) 19 | 20 | func Test_openapi_ChainsyncAPIService(t *testing.T) { 21 | 22 | configuration := openapiclient.NewConfiguration() 23 | apiClient := openapiclient.NewAPIClient(configuration) 24 | 25 | t.Run("Test ChainsyncAPIService ChainsyncSyncGet", func(t *testing.T) { 26 | 27 | t.Skip("skip test") // remove to run test 28 | 29 | httpRes, err := apiClient.ChainsyncAPI.ChainsyncSyncGet(context.Background()). 30 | Execute() 31 | 32 | require.Nil(t, err) 33 | assert.Equal(t, 200, httpRes.StatusCode) 34 | 35 | }) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /openapi/test/api_default_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Testing DefaultAPIService 5 | 6 | */ 7 | 8 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); 9 | 10 | package openapi 11 | 12 | import ( 13 | "context" 14 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | "testing" 18 | ) 19 | 20 | func Test_openapi_DefaultAPIService(t *testing.T) { 21 | 22 | configuration := openapiclient.NewConfiguration() 23 | apiClient := openapiclient.NewAPIClient(configuration) 24 | 25 | t.Run("Test DefaultAPIService LocaltxsubmissionTxPost", func(t *testing.T) { 26 | 27 | t.Skip("skip test") // remove to run test 28 | 29 | resp, httpRes, err := apiClient.DefaultAPI.LocaltxsubmissionTxPost(context.Background()). 30 | Execute() 31 | 32 | require.Nil(t, err) 33 | require.NotNil(t, resp) 34 | assert.Equal(t, 200, httpRes.StatusCode) 35 | 36 | }) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "fmt" 19 | ) 20 | 21 | // These are populated at build time 22 | var ( 23 | Version string 24 | CommitHash string 25 | ) 26 | 27 | func GetVersionString() string { 28 | if Version != "" { 29 | return fmt.Sprintf("%s (commit %s)", Version, CommitHash) 30 | } else { 31 | return fmt.Sprintf("devel (commit %s)", CommitHash) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /openapi/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .travis.yml 3 | README.md 4 | api/openapi.yaml 5 | api_chainsync.go 6 | api_localstatequery.go 7 | api_localtxmonitor.go 8 | api_localtxsubmission.go 9 | client.go 10 | configuration.go 11 | docs/ApiResponseApiError.md 12 | docs/ApiResponseLocalStateQueryCurrentEra.md 13 | docs/ApiResponseLocalStateQuerySystemStart.md 14 | docs/ApiResponseLocalStateQueryTip.md 15 | docs/ApiResponseLocalTxMonitorHasTx.md 16 | docs/ApiResponseLocalTxMonitorSizes.md 17 | docs/ApiResponseLocalTxMonitorTxs.md 18 | docs/ChainsyncAPI.md 19 | docs/LocalstatequeryAPI.md 20 | docs/LocaltxmonitorAPI.md 21 | docs/LocaltxsubmissionAPI.md 22 | git_push.sh 23 | go.mod 24 | go.sum 25 | model_api_response_api_error.go 26 | model_api_response_local_state_query_current_era.go 27 | model_api_response_local_state_query_system_start.go 28 | model_api_response_local_state_query_tip.go 29 | model_api_response_local_tx_monitor_has_tx.go 30 | model_api_response_local_tx_monitor_sizes.go 31 | model_api_response_local_tx_monitor_txs.go 32 | response.go 33 | utils.go 34 | -------------------------------------------------------------------------------- /openapi/test/api_localtxsubmission_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Testing LocaltxsubmissionAPIService 5 | 6 | */ 7 | 8 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); 9 | 10 | package openapi 11 | 12 | import ( 13 | "context" 14 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | "testing" 18 | ) 19 | 20 | func Test_openapi_LocaltxsubmissionAPIService(t *testing.T) { 21 | 22 | configuration := openapiclient.NewConfiguration() 23 | apiClient := openapiclient.NewAPIClient(configuration) 24 | 25 | t.Run( 26 | "Test LocaltxsubmissionAPIService LocaltxsubmissionTxPost", 27 | func(t *testing.T) { 28 | 29 | t.Skip("skip test") // remove to run test 30 | 31 | resp, httpRes, err := apiClient.LocaltxsubmissionAPI.LocaltxsubmissionTxPost(context.Background()). 32 | Execute() 33 | 34 | require.Nil(t, err) 35 | require.NotNil(t, resp) 36 | assert.Equal(t, 200, httpRes.StatusCode) 37 | 38 | }, 39 | ) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /internal/node/localtxsubmission.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package node 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/blinklabs-io/cardano-node-api/internal/config" 21 | "github.com/blinklabs-io/gouroboros/protocol/localtxsubmission" 22 | ) 23 | 24 | func buildLocalTxSubmissionConfig() localtxsubmission.Config { 25 | cfg := config.GetConfig() 26 | // #nosec G115 27 | return localtxsubmission.NewConfig( 28 | localtxsubmission.WithTimeout( 29 | time.Duration(cfg.Node.QueryTimeout) * time.Second, 30 | ), 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /openapi/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /internal/node/localtxmonitor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package node 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/blinklabs-io/cardano-node-api/internal/config" 21 | "github.com/blinklabs-io/gouroboros/protocol/localtxmonitor" 22 | ) 23 | 24 | func buildLocalTxMonitorConfig() localtxmonitor.Config { 25 | cfg := config.GetConfig() 26 | // #nosec G115 27 | return localtxmonitor.NewConfig( 28 | localtxmonitor.WithAcquireTimeout( 29 | time.Duration(cfg.Node.Timeout)*time.Second, 30 | ), 31 | localtxmonitor.WithQueryTimeout( 32 | time.Duration(cfg.Node.QueryTimeout)*time.Second, 33 | ), 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /internal/node/localstatequery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package node 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/blinklabs-io/cardano-node-api/internal/config" 21 | "github.com/blinklabs-io/gouroboros/protocol/localstatequery" 22 | ) 23 | 24 | func buildLocalStateQueryConfig() localstatequery.Config { 25 | cfg := config.GetConfig() 26 | // #nosec G115 27 | return localstatequery.NewConfig( 28 | localstatequery.WithAcquireTimeout( 29 | time.Duration(cfg.Node.Timeout)*time.Second, 30 | ), 31 | localstatequery.WithQueryTimeout( 32 | time.Duration(cfg.Node.QueryTimeout)*time.Second, 33 | ), 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | issues-exit-code: 1 4 | tests: false 5 | linters: 6 | enable: 7 | - asasalint 8 | - asciicheck 9 | - bidichk 10 | - bodyclose 11 | - contextcheck 12 | - copyloopvar 13 | - durationcheck 14 | - errchkjson 15 | - errorlint 16 | - exhaustive 17 | - fatcontext 18 | - gocheckcompilerdirectives 19 | - gochecksumtype 20 | - gomodguard 21 | - gosec 22 | - gosmopolitan 23 | - loggercheck 24 | - makezero 25 | - musttag 26 | - nilerr 27 | - nilnesserr 28 | - perfsprint 29 | - prealloc 30 | - protogetter 31 | - reassign 32 | - recvcheck 33 | - rowserrcheck 34 | - spancheck 35 | - sqlclosecheck 36 | - testifylint 37 | - unparam 38 | - usestdlibvars 39 | - whitespace 40 | - zerologlint 41 | disable: 42 | - depguard 43 | - noctx 44 | exclusions: 45 | generated: lax 46 | presets: 47 | - comments 48 | - common-false-positives 49 | - legacy 50 | - std-error-handling 51 | paths: 52 | - docs 53 | - third_party$ 54 | - builtin$ 55 | - examples$ 56 | issues: 57 | max-issues-per-linter: 0 58 | max-same-issues: 0 59 | formatters: 60 | enable: 61 | - gci 62 | - gofmt 63 | - gofumpt 64 | - goimports 65 | exclusions: 66 | generated: lax 67 | paths: 68 | - docs 69 | - third_party$ 70 | - builtin$ 71 | - examples$ 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Determine root directory 2 | ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 3 | 4 | # Gather all .go files for use in dependencies below 5 | GO_FILES=$(shell find $(ROOT_DIR) -name '*.go') 6 | 7 | # Gather list of expected binaries 8 | BINARIES=$(shell cd $(ROOT_DIR)/cmd && ls -1 | grep -v ^common) 9 | 10 | # Extract Go module name from go.mod 11 | GOMODULE=$(shell grep ^module $(ROOT_DIR)/go.mod | awk '{ print $$2 }') 12 | 13 | # Set version strings based on git tag and current ref 14 | GO_LDFLAGS=-ldflags "-s -w -X '$(GOMODULE)/internal/version.Version=$(shell git describe --tags --exact-match 2>/dev/null)' -X '$(GOMODULE)/internal/version.CommitHash=$(shell git rev-parse --short HEAD)'" 15 | 16 | .PHONY: build mod-tidy clean test swagger 17 | 18 | # Alias for building program binary 19 | build: $(BINARIES) 20 | 21 | mod-tidy: 22 | # Needed to fetch new dependencies and add them to go.mod 23 | go mod tidy 24 | 25 | clean: 26 | rm -f $(BINARIES) 27 | 28 | format: mod-tidy 29 | go fmt ./... 30 | gofmt -s -w $(GO_FILES) 31 | 32 | golines: 33 | golines -w --ignore-generated --chain-split-dots --max-len=80 --reformat-tags . 34 | 35 | test: 36 | go test -v -race ./... 37 | 38 | swagger: 39 | swag f -g api.go -d internal/api/ 40 | swag i -g api.go -d internal/api/ 41 | 42 | # Build our program binaries 43 | # Depends on GO_FILES to determine when rebuild is needed 44 | $(BINARIES): mod-tidy $(GO_FILES) 45 | CGO_ENABLED=0 go build \ 46 | $(GO_LDFLAGS) \ 47 | -o $(@) \ 48 | ./cmd/$(@) 49 | 50 | # Build docker image 51 | image: build 52 | docker build -t $(BINARIES) . 53 | -------------------------------------------------------------------------------- /openapi/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "net/http" 16 | ) 17 | 18 | // APIResponse stores the API response returned by the server. 19 | type APIResponse struct { 20 | *http.Response `json:"-"` 21 | Message string `json:"message,omitempty"` 22 | // Operation is the name of the OpenAPI operation. 23 | Operation string `json:"operation,omitempty"` 24 | // RequestURL is the request URL. This value is always available, even if the 25 | // embedded *http.Response is nil. 26 | RequestURL string `json:"url,omitempty"` 27 | // Method is the HTTP method used for the request. This value is always 28 | // available, even if the embedded *http.Response is nil. 29 | Method string `json:"method,omitempty"` 30 | // Payload holds the contents of the response body (which may be nil or empty). 31 | // This is provided here as the raw response.Body() reader will have already 32 | // been drained. 33 | Payload []byte `json:"-"` 34 | } 35 | 36 | // NewAPIResponse returns a new APIResponse object. 37 | func NewAPIResponse(r *http.Response) *APIResponse { 38 | 39 | response := &APIResponse{Response: r} 40 | return response 41 | } 42 | 43 | // NewAPIResponseWithError returns a new APIResponse object with the provided error message. 44 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 45 | 46 | response := &APIResponse{Message: errorMessage} 47 | return response 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/ci-docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker CI 2 | 3 | on: 4 | pull_request: 5 | branches: ['main'] 6 | paths: ['Dockerfile','cmd/**','docs/**','internal/**','go.*','.github/workflows/ci-docker.yml'] 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: blinklabs/cardano-node-api 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | docker: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 20 | with: 21 | fetch-depth: '0' 22 | - name: qemu 23 | uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 https://github.com/docker/setup-qemu-action/releases/tag/v3.7.0 24 | - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 https://github.com/docker/setup-buildx-action/releases/tag/v3.11.1 25 | - id: meta 26 | uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 https://github.com/docker/metadata-action/releases/tag/v5.10.0 27 | with: 28 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 29 | - name: build 30 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 https://github.com/docker/build-push-action/releases/tag/v6.18.0 31 | with: 32 | context: . 33 | push: false 34 | ### TODO: test multiple platforms 35 | # platforms: linux/amd64,linux/arm64 36 | tags: ${{ steps.meta.outputs.tags }} 37 | labels: ${{ steps.meta.outputs.labels }} 38 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseApiError.md: -------------------------------------------------------------------------------- 1 | # ApiResponseApiError 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **Msg** | Pointer to **string** | | [optional] 8 | 9 | ## Methods 10 | 11 | ### NewApiResponseApiError 12 | 13 | `func NewApiResponseApiError() *ApiResponseApiError` 14 | 15 | NewApiResponseApiError instantiates a new ApiResponseApiError object 16 | This constructor will assign default values to properties that have it defined, 17 | and makes sure properties required by API are set, but the set of arguments 18 | will change when the set of required properties is changed 19 | 20 | ### NewApiResponseApiErrorWithDefaults 21 | 22 | `func NewApiResponseApiErrorWithDefaults() *ApiResponseApiError` 23 | 24 | NewApiResponseApiErrorWithDefaults instantiates a new ApiResponseApiError object 25 | This constructor will only assign default values to properties that have it defined, 26 | but it doesn't guarantee that properties required by API are set 27 | 28 | ### GetMsg 29 | 30 | `func (o *ApiResponseApiError) GetMsg() string` 31 | 32 | GetMsg returns the Msg field if non-nil, zero value otherwise. 33 | 34 | ### GetMsgOk 35 | 36 | `func (o *ApiResponseApiError) GetMsgOk() (*string, bool)` 37 | 38 | GetMsgOk returns a tuple with the Msg field if it's non-nil, zero value otherwise 39 | and a boolean to check if the value has been set. 40 | 41 | ### SetMsg 42 | 43 | `func (o *ApiResponseApiError) SetMsg(v string)` 44 | 45 | SetMsg sets Msg field to given value. 46 | 47 | ### HasMsg 48 | 49 | `func (o *ApiResponseApiError) HasMsg() bool` 50 | 51 | HasMsg returns a boolean if a field has been set. 52 | 53 | 54 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 55 | 56 | 57 | -------------------------------------------------------------------------------- /openapi/test/api_localtxmonitor_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Testing LocaltxmonitorAPIService 5 | 6 | */ 7 | 8 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); 9 | 10 | package openapi 11 | 12 | import ( 13 | "context" 14 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | "testing" 18 | ) 19 | 20 | func Test_openapi_LocaltxmonitorAPIService(t *testing.T) { 21 | 22 | configuration := openapiclient.NewConfiguration() 23 | apiClient := openapiclient.NewAPIClient(configuration) 24 | 25 | t.Run( 26 | "Test LocaltxmonitorAPIService LocaltxmonitorHasTxTxHashGet", 27 | func(t *testing.T) { 28 | 29 | t.Skip("skip test") // remove to run test 30 | 31 | var txHash string 32 | 33 | resp, httpRes, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorHasTxTxHashGet(context.Background(), txHash). 34 | Execute() 35 | 36 | require.Nil(t, err) 37 | require.NotNil(t, resp) 38 | assert.Equal(t, 200, httpRes.StatusCode) 39 | 40 | }, 41 | ) 42 | 43 | t.Run( 44 | "Test LocaltxmonitorAPIService LocaltxmonitorSizesGet", 45 | func(t *testing.T) { 46 | 47 | t.Skip("skip test") // remove to run test 48 | 49 | resp, httpRes, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorSizesGet(context.Background()). 50 | Execute() 51 | 52 | require.Nil(t, err) 53 | require.NotNil(t, resp) 54 | assert.Equal(t, 200, httpRes.StatusCode) 55 | 56 | }, 57 | ) 58 | 59 | t.Run( 60 | "Test LocaltxmonitorAPIService LocaltxmonitorTxsGet", 61 | func(t *testing.T) { 62 | 63 | t.Skip("skip test") // remove to run test 64 | 65 | resp, httpRes, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorTxsGet(context.Background()). 66 | Execute() 67 | 68 | require.Nil(t, err) 69 | require.NotNil(t, resp) 70 | assert.Equal(t, 200, httpRes.StatusCode) 71 | 72 | }, 73 | ) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalTxMonitorHasTx.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalTxMonitorHasTx 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **HasTx** | Pointer to **bool** | | [optional] 8 | 9 | ## Methods 10 | 11 | ### NewApiResponseLocalTxMonitorHasTx 12 | 13 | `func NewApiResponseLocalTxMonitorHasTx() *ApiResponseLocalTxMonitorHasTx` 14 | 15 | NewApiResponseLocalTxMonitorHasTx instantiates a new ApiResponseLocalTxMonitorHasTx object 16 | This constructor will assign default values to properties that have it defined, 17 | and makes sure properties required by API are set, but the set of arguments 18 | will change when the set of required properties is changed 19 | 20 | ### NewApiResponseLocalTxMonitorHasTxWithDefaults 21 | 22 | `func NewApiResponseLocalTxMonitorHasTxWithDefaults() *ApiResponseLocalTxMonitorHasTx` 23 | 24 | NewApiResponseLocalTxMonitorHasTxWithDefaults instantiates a new ApiResponseLocalTxMonitorHasTx object 25 | This constructor will only assign default values to properties that have it defined, 26 | but it doesn't guarantee that properties required by API are set 27 | 28 | ### GetHasTx 29 | 30 | `func (o *ApiResponseLocalTxMonitorHasTx) GetHasTx() bool` 31 | 32 | GetHasTx returns the HasTx field if non-nil, zero value otherwise. 33 | 34 | ### GetHasTxOk 35 | 36 | `func (o *ApiResponseLocalTxMonitorHasTx) GetHasTxOk() (*bool, bool)` 37 | 38 | GetHasTxOk returns a tuple with the HasTx field if it's non-nil, zero value otherwise 39 | and a boolean to check if the value has been set. 40 | 41 | ### SetHasTx 42 | 43 | `func (o *ApiResponseLocalTxMonitorHasTx) SetHasTx(v bool)` 44 | 45 | SetHasTx sets HasTx field to given value. 46 | 47 | ### HasHasTx 48 | 49 | `func (o *ApiResponseLocalTxMonitorHasTx) HasHasTx() bool` 50 | 51 | HasHasTx returns a boolean if a field has been set. 52 | 53 | 54 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 55 | 56 | 57 | -------------------------------------------------------------------------------- /openapi/docs/DefaultAPI.md: -------------------------------------------------------------------------------- 1 | # \DefaultAPI 2 | 3 | All URIs are relative to *http://localhost/api* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**LocaltxsubmissionTxPost**](DefaultAPI.md#LocaltxsubmissionTxPost) | **Post** /localtxsubmission/tx | Submit Tx 8 | 9 | 10 | 11 | ## LocaltxsubmissionTxPost 12 | 13 | > string LocaltxsubmissionTxPost(ctx).ContentType(contentType).Execute() 14 | 15 | Submit Tx 16 | 17 | 18 | 19 | ### Example 20 | 21 | ```go 22 | package main 23 | 24 | import ( 25 | "context" 26 | "fmt" 27 | "os" 28 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 29 | ) 30 | 31 | func main() { 32 | contentType := "contentType_example" // string | Content type 33 | 34 | configuration := openapiclient.NewConfiguration() 35 | apiClient := openapiclient.NewAPIClient(configuration) 36 | resp, r, err := apiClient.DefaultAPI.LocaltxsubmissionTxPost(context.Background()).ContentType(contentType).Execute() 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Error when calling `DefaultAPI.LocaltxsubmissionTxPost``: %v\n", err) 39 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 40 | } 41 | // response from `LocaltxsubmissionTxPost`: string 42 | fmt.Fprintf(os.Stdout, "Response from `DefaultAPI.LocaltxsubmissionTxPost`: %v\n", resp) 43 | } 44 | ``` 45 | 46 | ### Path Parameters 47 | 48 | 49 | 50 | ### Other Parameters 51 | 52 | Other parameters are passed through a pointer to a apiLocaltxsubmissionTxPostRequest struct via the builder pattern 53 | 54 | 55 | Name | Type | Description | Notes 56 | ------------- | ------------- | ------------- | ------------- 57 | **contentType** | **string** | Content type | 58 | 59 | ### Return type 60 | 61 | **string** 62 | 63 | ### Authorization 64 | 65 | No authorization required 66 | 67 | ### HTTP request headers 68 | 69 | - **Content-Type**: Not defined 70 | - **Accept**: application/json 71 | 72 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 73 | [[Back to Model list]](../README.md#documentation-for-models) 74 | [[Back to README]](../README.md) 75 | 76 | -------------------------------------------------------------------------------- /openapi/docs/LocaltxsubmissionAPI.md: -------------------------------------------------------------------------------- 1 | # \LocaltxsubmissionAPI 2 | 3 | All URIs are relative to */api* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**LocaltxsubmissionTxPost**](LocaltxsubmissionAPI.md#LocaltxsubmissionTxPost) | **Post** /localtxsubmission/tx | Submit Tx 8 | 9 | 10 | 11 | ## LocaltxsubmissionTxPost 12 | 13 | > string LocaltxsubmissionTxPost(ctx).ContentType(contentType).Execute() 14 | 15 | Submit Tx 16 | 17 | 18 | 19 | ### Example 20 | 21 | ```go 22 | package main 23 | 24 | import ( 25 | "context" 26 | "fmt" 27 | "os" 28 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 29 | ) 30 | 31 | func main() { 32 | contentType := "contentType_example" // string | Content type 33 | 34 | configuration := openapiclient.NewConfiguration() 35 | apiClient := openapiclient.NewAPIClient(configuration) 36 | resp, r, err := apiClient.LocaltxsubmissionAPI.LocaltxsubmissionTxPost(context.Background()).ContentType(contentType).Execute() 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Error when calling `LocaltxsubmissionAPI.LocaltxsubmissionTxPost``: %v\n", err) 39 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 40 | } 41 | // response from `LocaltxsubmissionTxPost`: string 42 | fmt.Fprintf(os.Stdout, "Response from `LocaltxsubmissionAPI.LocaltxsubmissionTxPost`: %v\n", resp) 43 | } 44 | ``` 45 | 46 | ### Path Parameters 47 | 48 | 49 | 50 | ### Other Parameters 51 | 52 | Other parameters are passed through a pointer to a apiLocaltxsubmissionTxPostRequest struct via the builder pattern 53 | 54 | 55 | Name | Type | Description | Notes 56 | ------------- | ------------- | ------------- | ------------- 57 | **contentType** | **string** | Content type | 58 | 59 | ### Return type 60 | 61 | **string** 62 | 63 | ### Authorization 64 | 65 | No authorization required 66 | 67 | ### HTTP request headers 68 | 69 | - **Content-Type**: Not defined 70 | - **Accept**: application/json 71 | 72 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 73 | [[Back to Model list]](../README.md#documentation-for-models) 74 | [[Back to README]](../README.md) 75 | 76 | -------------------------------------------------------------------------------- /openapi/docs/ChainsyncAPI.md: -------------------------------------------------------------------------------- 1 | # \ChainsyncAPI 2 | 3 | All URIs are relative to */api* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**ChainsyncSyncGet**](ChainsyncAPI.md#ChainsyncSyncGet) | **Get** /chainsync/sync | Start a chain-sync using a websocket for events 8 | 9 | 10 | 11 | ## ChainsyncSyncGet 12 | 13 | > ChainsyncSyncGet(ctx).Tip(tip).Slot(slot).Hash(hash).Execute() 14 | 15 | Start a chain-sync using a websocket for events 16 | 17 | ### Example 18 | 19 | ```go 20 | package main 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | "os" 26 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 27 | ) 28 | 29 | func main() { 30 | tip := true // bool | whether to start from the current tip (optional) 31 | slot := int32(56) // int32 | slot to start sync at, should match hash (optional) 32 | hash := "hash_example" // string | block hash to start sync at, should match slot (optional) 33 | 34 | configuration := openapiclient.NewConfiguration() 35 | apiClient := openapiclient.NewAPIClient(configuration) 36 | r, err := apiClient.ChainsyncAPI.ChainsyncSyncGet(context.Background()).Tip(tip).Slot(slot).Hash(hash).Execute() 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Error when calling `ChainsyncAPI.ChainsyncSyncGet``: %v\n", err) 39 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 40 | } 41 | } 42 | ``` 43 | 44 | ### Path Parameters 45 | 46 | 47 | 48 | ### Other Parameters 49 | 50 | Other parameters are passed through a pointer to a apiChainsyncSyncGetRequest struct via the builder pattern 51 | 52 | 53 | Name | Type | Description | Notes 54 | ------------- | ------------- | ------------- | ------------- 55 | **tip** | **bool** | whether to start from the current tip | 56 | **slot** | **int32** | slot to start sync at, should match hash | 57 | **hash** | **string** | block hash to start sync at, should match slot | 58 | 59 | ### Return type 60 | 61 | (empty response body) 62 | 63 | ### Authorization 64 | 65 | No authorization required 66 | 67 | ### HTTP request headers 68 | 69 | - **Content-Type**: Not defined 70 | - **Accept**: */* 71 | 72 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 73 | [[Back to Model list]](../README.md#documentation-for-models) 74 | [[Back to README]](../README.md) 75 | 76 | -------------------------------------------------------------------------------- /internal/logging/logging.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logging 16 | 17 | import ( 18 | "log/slog" 19 | "os" 20 | "time" 21 | 22 | "github.com/blinklabs-io/cardano-node-api/internal/config" 23 | ) 24 | 25 | var ( 26 | globalLogger *slog.Logger 27 | accessLogger *slog.Logger 28 | ) 29 | 30 | // Configure initializes the global logger. 31 | func Configure() { 32 | cfg := config.GetConfig() 33 | var level slog.Level 34 | switch cfg.Logging.Level { 35 | case "debug": 36 | level = slog.LevelDebug 37 | case "info": 38 | level = slog.LevelInfo 39 | case "warn": 40 | level = slog.LevelWarn 41 | case "error": 42 | level = slog.LevelError 43 | default: 44 | level = slog.LevelInfo 45 | } 46 | 47 | handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ 48 | ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { 49 | if a.Key == slog.TimeKey { 50 | return slog.String( 51 | "timestamp", 52 | a.Value.Time().Format(time.RFC3339), 53 | ) 54 | } 55 | return a 56 | }, 57 | Level: level, 58 | }) 59 | globalLogger = slog.New(handler).With("component", "main") 60 | 61 | // Configure access logger 62 | accessHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ 63 | ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { 64 | if a.Key == slog.TimeKey { 65 | return slog.String( 66 | "timestamp", 67 | a.Value.Time().Format(time.RFC3339), 68 | ) 69 | } 70 | return a 71 | }, 72 | Level: slog.LevelInfo, 73 | }) 74 | accessLogger = slog.New(accessHandler).With("component", "access") 75 | } 76 | 77 | // GetLogger returns the global application logger. 78 | func GetLogger() *slog.Logger { 79 | if globalLogger == nil { 80 | Configure() 81 | } 82 | return globalLogger 83 | } 84 | 85 | // GetAccessLogger returns the access logger. 86 | func GetAccessLogger() *slog.Logger { 87 | if accessLogger == nil { 88 | Configure() 89 | } 90 | return accessLogger 91 | } 92 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalTxMonitorTxs.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalTxMonitorTxs 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **TxBytes** | Pointer to **string** | | [optional] 8 | **TxHash** | Pointer to **string** | | [optional] 9 | 10 | ## Methods 11 | 12 | ### NewApiResponseLocalTxMonitorTxs 13 | 14 | `func NewApiResponseLocalTxMonitorTxs() *ApiResponseLocalTxMonitorTxs` 15 | 16 | NewApiResponseLocalTxMonitorTxs instantiates a new ApiResponseLocalTxMonitorTxs object 17 | This constructor will assign default values to properties that have it defined, 18 | and makes sure properties required by API are set, but the set of arguments 19 | will change when the set of required properties is changed 20 | 21 | ### NewApiResponseLocalTxMonitorTxsWithDefaults 22 | 23 | `func NewApiResponseLocalTxMonitorTxsWithDefaults() *ApiResponseLocalTxMonitorTxs` 24 | 25 | NewApiResponseLocalTxMonitorTxsWithDefaults instantiates a new ApiResponseLocalTxMonitorTxs object 26 | This constructor will only assign default values to properties that have it defined, 27 | but it doesn't guarantee that properties required by API are set 28 | 29 | ### GetTxBytes 30 | 31 | `func (o *ApiResponseLocalTxMonitorTxs) GetTxBytes() string` 32 | 33 | GetTxBytes returns the TxBytes field if non-nil, zero value otherwise. 34 | 35 | ### GetTxBytesOk 36 | 37 | `func (o *ApiResponseLocalTxMonitorTxs) GetTxBytesOk() (*string, bool)` 38 | 39 | GetTxBytesOk returns a tuple with the TxBytes field if it's non-nil, zero value otherwise 40 | and a boolean to check if the value has been set. 41 | 42 | ### SetTxBytes 43 | 44 | `func (o *ApiResponseLocalTxMonitorTxs) SetTxBytes(v string)` 45 | 46 | SetTxBytes sets TxBytes field to given value. 47 | 48 | ### HasTxBytes 49 | 50 | `func (o *ApiResponseLocalTxMonitorTxs) HasTxBytes() bool` 51 | 52 | HasTxBytes returns a boolean if a field has been set. 53 | 54 | ### GetTxHash 55 | 56 | `func (o *ApiResponseLocalTxMonitorTxs) GetTxHash() string` 57 | 58 | GetTxHash returns the TxHash field if non-nil, zero value otherwise. 59 | 60 | ### GetTxHashOk 61 | 62 | `func (o *ApiResponseLocalTxMonitorTxs) GetTxHashOk() (*string, bool)` 63 | 64 | GetTxHashOk returns a tuple with the TxHash field if it's non-nil, zero value otherwise 65 | and a boolean to check if the value has been set. 66 | 67 | ### SetTxHash 68 | 69 | `func (o *ApiResponseLocalTxMonitorTxs) SetTxHash(v string)` 70 | 71 | SetTxHash sets TxHash field to given value. 72 | 73 | ### HasTxHash 74 | 75 | `func (o *ApiResponseLocalTxMonitorTxs) HasTxHash() bool` 76 | 77 | HasTxHash returns a boolean if a field has been set. 78 | 79 | 80 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 81 | 82 | 83 | -------------------------------------------------------------------------------- /internal/node/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package node 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "os" 21 | 22 | "github.com/blinklabs-io/adder/event" 23 | "github.com/blinklabs-io/cardano-node-api/internal/config" 24 | ouroboros "github.com/blinklabs-io/gouroboros" 25 | ) 26 | 27 | type ConnectionConfig struct { 28 | ChainSyncEventChan chan event.Event 29 | } 30 | 31 | func GetConnection(connCfg *ConnectionConfig) (*ouroboros.Connection, error) { 32 | // Make sure we always have a ConnectionConfig object 33 | if connCfg == nil { 34 | connCfg = &ConnectionConfig{} 35 | } 36 | cfg := config.GetConfig() 37 | // Connect to cardano-node 38 | oConn, err := ouroboros.NewConnection( 39 | ouroboros.WithNetworkMagic(uint32(cfg.Node.NetworkMagic)), 40 | ouroboros.WithNodeToNode(false), 41 | ouroboros.WithKeepAlive(true), 42 | ouroboros.WithChainSyncConfig(buildChainSyncConfig(*connCfg)), 43 | ouroboros.WithLocalTxMonitorConfig(buildLocalTxMonitorConfig()), 44 | ouroboros.WithLocalStateQueryConfig(buildLocalStateQueryConfig()), 45 | ouroboros.WithLocalTxSubmissionConfig(buildLocalTxSubmissionConfig()), 46 | ) 47 | if err != nil { 48 | return nil, fmt.Errorf("failure creating Ouroboros connection: %w", err) 49 | } 50 | 51 | if cfg.Node.Address != "" && cfg.Node.Port > 0 { 52 | // Connect to TCP port 53 | if err := oConn.Dial("tcp", fmt.Sprintf("%s:%d", cfg.Node.Address, cfg.Node.Port)); err != nil { 54 | return nil, fmt.Errorf( 55 | "failure connecting to node via TCP: %w", 56 | err, 57 | ) 58 | } 59 | } else if cfg.Node.SocketPath != "" { 60 | // Check that node socket path exists 61 | if _, err := os.Stat(cfg.Node.SocketPath); err != nil { 62 | if os.IsNotExist(err) { 63 | return nil, fmt.Errorf("node socket path does not exist: %s", cfg.Node.SocketPath) 64 | } else { 65 | return nil, fmt.Errorf("unknown error checking if node socket path exists: %w", err) 66 | } 67 | } 68 | if err := oConn.Dial("unix", cfg.Node.SocketPath); err != nil { 69 | return nil, fmt.Errorf("failure connecting to node via UNIX socket: %w", err) 70 | } 71 | } else { 72 | return nil, errors.New("you must specify either the UNIX socket path or the address/port for your cardano-node") 73 | } 74 | return oConn, nil 75 | } 76 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalStateQueryCurrentEra.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalStateQueryCurrentEra 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **Id** | Pointer to **int32** | | [optional] 8 | **Name** | Pointer to **string** | | [optional] 9 | 10 | ## Methods 11 | 12 | ### NewApiResponseLocalStateQueryCurrentEra 13 | 14 | `func NewApiResponseLocalStateQueryCurrentEra() *ApiResponseLocalStateQueryCurrentEra` 15 | 16 | NewApiResponseLocalStateQueryCurrentEra instantiates a new ApiResponseLocalStateQueryCurrentEra object 17 | This constructor will assign default values to properties that have it defined, 18 | and makes sure properties required by API are set, but the set of arguments 19 | will change when the set of required properties is changed 20 | 21 | ### NewApiResponseLocalStateQueryCurrentEraWithDefaults 22 | 23 | `func NewApiResponseLocalStateQueryCurrentEraWithDefaults() *ApiResponseLocalStateQueryCurrentEra` 24 | 25 | NewApiResponseLocalStateQueryCurrentEraWithDefaults instantiates a new ApiResponseLocalStateQueryCurrentEra object 26 | This constructor will only assign default values to properties that have it defined, 27 | but it doesn't guarantee that properties required by API are set 28 | 29 | ### GetId 30 | 31 | `func (o *ApiResponseLocalStateQueryCurrentEra) GetId() int32` 32 | 33 | GetId returns the Id field if non-nil, zero value otherwise. 34 | 35 | ### GetIdOk 36 | 37 | `func (o *ApiResponseLocalStateQueryCurrentEra) GetIdOk() (*int32, bool)` 38 | 39 | GetIdOk returns a tuple with the Id field if it's non-nil, zero value otherwise 40 | and a boolean to check if the value has been set. 41 | 42 | ### SetId 43 | 44 | `func (o *ApiResponseLocalStateQueryCurrentEra) SetId(v int32)` 45 | 46 | SetId sets Id field to given value. 47 | 48 | ### HasId 49 | 50 | `func (o *ApiResponseLocalStateQueryCurrentEra) HasId() bool` 51 | 52 | HasId returns a boolean if a field has been set. 53 | 54 | ### GetName 55 | 56 | `func (o *ApiResponseLocalStateQueryCurrentEra) GetName() string` 57 | 58 | GetName returns the Name field if non-nil, zero value otherwise. 59 | 60 | ### GetNameOk 61 | 62 | `func (o *ApiResponseLocalStateQueryCurrentEra) GetNameOk() (*string, bool)` 63 | 64 | GetNameOk returns a tuple with the Name field if it's non-nil, zero value otherwise 65 | and a boolean to check if the value has been set. 66 | 67 | ### SetName 68 | 69 | `func (o *ApiResponseLocalStateQueryCurrentEra) SetName(v string)` 70 | 71 | SetName sets Name field to given value. 72 | 73 | ### HasName 74 | 75 | `func (o *ApiResponseLocalStateQueryCurrentEra) HasName() bool` 76 | 77 | HasName returns a boolean if a field has been set. 78 | 79 | 80 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 81 | 82 | 83 | -------------------------------------------------------------------------------- /openapi/test/api_localstatequery_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Testing LocalstatequeryAPIService 5 | 6 | */ 7 | 8 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); 9 | 10 | package openapi 11 | 12 | import ( 13 | "context" 14 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | "testing" 18 | ) 19 | 20 | func Test_openapi_LocalstatequeryAPIService(t *testing.T) { 21 | 22 | configuration := openapiclient.NewConfiguration() 23 | apiClient := openapiclient.NewAPIClient(configuration) 24 | 25 | t.Run( 26 | "Test LocalstatequeryAPIService LocalstatequeryCurrentEraGet", 27 | func(t *testing.T) { 28 | 29 | t.Skip("skip test") // remove to run test 30 | 31 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequeryCurrentEraGet(context.Background()). 32 | Execute() 33 | 34 | require.Nil(t, err) 35 | require.NotNil(t, resp) 36 | assert.Equal(t, 200, httpRes.StatusCode) 37 | 38 | }, 39 | ) 40 | 41 | t.Run( 42 | "Test LocalstatequeryAPIService LocalstatequeryEraHistoryGet", 43 | func(t *testing.T) { 44 | 45 | t.Skip("skip test") // remove to run test 46 | 47 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequeryEraHistoryGet(context.Background()). 48 | Execute() 49 | 50 | require.Nil(t, err) 51 | require.NotNil(t, resp) 52 | assert.Equal(t, 200, httpRes.StatusCode) 53 | 54 | }, 55 | ) 56 | 57 | t.Run( 58 | "Test LocalstatequeryAPIService LocalstatequeryGenesisConfigGet", 59 | func(t *testing.T) { 60 | 61 | t.Skip("skip test") // remove to run test 62 | 63 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequeryGenesisConfigGet(context.Background()). 64 | Execute() 65 | 66 | require.Nil(t, err) 67 | require.NotNil(t, resp) 68 | assert.Equal(t, 200, httpRes.StatusCode) 69 | 70 | }, 71 | ) 72 | 73 | t.Run( 74 | "Test LocalstatequeryAPIService LocalstatequeryProtocolParamsGet", 75 | func(t *testing.T) { 76 | 77 | t.Skip("skip test") // remove to run test 78 | 79 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequeryProtocolParamsGet(context.Background()). 80 | Execute() 81 | 82 | require.Nil(t, err) 83 | require.NotNil(t, resp) 84 | assert.Equal(t, 200, httpRes.StatusCode) 85 | 86 | }, 87 | ) 88 | 89 | t.Run( 90 | "Test LocalstatequeryAPIService LocalstatequerySystemStartGet", 91 | func(t *testing.T) { 92 | 93 | t.Skip("skip test") // remove to run test 94 | 95 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequerySystemStartGet(context.Background()). 96 | Execute() 97 | 98 | require.Nil(t, err) 99 | require.NotNil(t, resp) 100 | assert.Equal(t, 200, httpRes.StatusCode) 101 | 102 | }, 103 | ) 104 | 105 | t.Run( 106 | "Test LocalstatequeryAPIService LocalstatequeryTipGet", 107 | func(t *testing.T) { 108 | 109 | t.Skip("skip test") // remove to run test 110 | 111 | resp, httpRes, err := apiClient.LocalstatequeryAPI.LocalstatequeryTipGet(context.Background()). 112 | Execute() 113 | 114 | require.Nil(t, err) 115 | require.NotNil(t, resp) 116 | assert.Equal(t, 200, httpRes.StatusCode) 117 | 118 | }, 119 | ) 120 | 121 | } 122 | -------------------------------------------------------------------------------- /cmd/cardano-node-api/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | "log/slog" 21 | "net/http" 22 | _ "net/http/pprof" // #nosec G108 23 | "os" 24 | "time" 25 | 26 | "github.com/blinklabs-io/cardano-node-api/internal/api" 27 | "github.com/blinklabs-io/cardano-node-api/internal/config" 28 | "github.com/blinklabs-io/cardano-node-api/internal/logging" 29 | "github.com/blinklabs-io/cardano-node-api/internal/node" 30 | "github.com/blinklabs-io/cardano-node-api/internal/utxorpc" 31 | "github.com/blinklabs-io/cardano-node-api/internal/version" 32 | "go.uber.org/automaxprocs/maxprocs" 33 | ) 34 | 35 | var cmdlineFlags struct { 36 | configFile string 37 | } 38 | 39 | func slogPrintf(format string, v ...any) { 40 | slog.Info(fmt.Sprintf(format, v...)) 41 | } 42 | 43 | func main() { 44 | flag.StringVar( 45 | &cmdlineFlags.configFile, 46 | "config", 47 | "", 48 | "path to config file to load", 49 | ) 50 | flag.Parse() 51 | 52 | // Load config 53 | cfg, err := config.Load(cmdlineFlags.configFile) 54 | if err != nil { 55 | fmt.Printf("Failed to load config: %s\n", err) 56 | os.Exit(1) 57 | } 58 | 59 | // Configure logging 60 | logging.Configure() 61 | logger := logging.GetLogger() 62 | 63 | // Test node connection 64 | if cfg.Node.SkipCheck { 65 | logger.Debug("skipping node check") 66 | } else { 67 | if oConn, err := node.GetConnection(nil); err != nil { 68 | logger.Error("failed to connect to node:", "error", err) 69 | } else { 70 | oConn.Close() 71 | } 72 | } 73 | 74 | logger.Info( 75 | "starting cardano-node-api version", 76 | "version", 77 | version.GetVersionString(), 78 | ) 79 | 80 | // Configure max processes with our logger wrapper, toss undo func 81 | _, err = maxprocs.Set(maxprocs.Logger(slogPrintf)) 82 | if err != nil { 83 | // If we hit this, something really wrong happened 84 | slog.Error(err.Error()) 85 | os.Exit(1) 86 | } 87 | 88 | // Start debug listener 89 | if cfg.Debug.ListenPort > 0 { 90 | logger.Info(fmt.Sprintf( 91 | "starting debug listener on %s:%d", 92 | cfg.Debug.ListenAddress, 93 | cfg.Debug.ListenPort, 94 | )) 95 | go func() { 96 | debugger := &http.Server{ 97 | Addr: fmt.Sprintf( 98 | "%s:%d", 99 | cfg.Debug.ListenAddress, 100 | cfg.Debug.ListenPort, 101 | ), 102 | ReadHeaderTimeout: 60 * time.Second, 103 | } 104 | err := debugger.ListenAndServe() 105 | if err != nil { 106 | logger.Error("failed to start debug listener:", "error", err) 107 | os.Exit(1) 108 | } 109 | }() 110 | } 111 | 112 | // Start API listener 113 | go func() { 114 | if err := api.Start(cfg); err != nil { 115 | logger.Error("failed to start API:", "error", err) 116 | os.Exit(1) 117 | } 118 | }() 119 | 120 | // Start UTxO RPC gRPC listener 121 | if err := utxorpc.Start(cfg); err != nil { 122 | logger.Error("failed to start gRPC:", "error", err) 123 | os.Exit(1) 124 | } 125 | 126 | // Wait forever 127 | select {} 128 | } 129 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalTxMonitorSizes.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalTxMonitorSizes 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **Capacity** | Pointer to **int32** | | [optional] 8 | **Size** | Pointer to **int32** | | [optional] 9 | **TxCount** | Pointer to **int32** | | [optional] 10 | 11 | ## Methods 12 | 13 | ### NewApiResponseLocalTxMonitorSizes 14 | 15 | `func NewApiResponseLocalTxMonitorSizes() *ApiResponseLocalTxMonitorSizes` 16 | 17 | NewApiResponseLocalTxMonitorSizes instantiates a new ApiResponseLocalTxMonitorSizes object 18 | This constructor will assign default values to properties that have it defined, 19 | and makes sure properties required by API are set, but the set of arguments 20 | will change when the set of required properties is changed 21 | 22 | ### NewApiResponseLocalTxMonitorSizesWithDefaults 23 | 24 | `func NewApiResponseLocalTxMonitorSizesWithDefaults() *ApiResponseLocalTxMonitorSizes` 25 | 26 | NewApiResponseLocalTxMonitorSizesWithDefaults instantiates a new ApiResponseLocalTxMonitorSizes object 27 | This constructor will only assign default values to properties that have it defined, 28 | but it doesn't guarantee that properties required by API are set 29 | 30 | ### GetCapacity 31 | 32 | `func (o *ApiResponseLocalTxMonitorSizes) GetCapacity() int32` 33 | 34 | GetCapacity returns the Capacity field if non-nil, zero value otherwise. 35 | 36 | ### GetCapacityOk 37 | 38 | `func (o *ApiResponseLocalTxMonitorSizes) GetCapacityOk() (*int32, bool)` 39 | 40 | GetCapacityOk returns a tuple with the Capacity field if it's non-nil, zero value otherwise 41 | and a boolean to check if the value has been set. 42 | 43 | ### SetCapacity 44 | 45 | `func (o *ApiResponseLocalTxMonitorSizes) SetCapacity(v int32)` 46 | 47 | SetCapacity sets Capacity field to given value. 48 | 49 | ### HasCapacity 50 | 51 | `func (o *ApiResponseLocalTxMonitorSizes) HasCapacity() bool` 52 | 53 | HasCapacity returns a boolean if a field has been set. 54 | 55 | ### GetSize 56 | 57 | `func (o *ApiResponseLocalTxMonitorSizes) GetSize() int32` 58 | 59 | GetSize returns the Size field if non-nil, zero value otherwise. 60 | 61 | ### GetSizeOk 62 | 63 | `func (o *ApiResponseLocalTxMonitorSizes) GetSizeOk() (*int32, bool)` 64 | 65 | GetSizeOk returns a tuple with the Size field if it's non-nil, zero value otherwise 66 | and a boolean to check if the value has been set. 67 | 68 | ### SetSize 69 | 70 | `func (o *ApiResponseLocalTxMonitorSizes) SetSize(v int32)` 71 | 72 | SetSize sets Size field to given value. 73 | 74 | ### HasSize 75 | 76 | `func (o *ApiResponseLocalTxMonitorSizes) HasSize() bool` 77 | 78 | HasSize returns a boolean if a field has been set. 79 | 80 | ### GetTxCount 81 | 82 | `func (o *ApiResponseLocalTxMonitorSizes) GetTxCount() int32` 83 | 84 | GetTxCount returns the TxCount field if non-nil, zero value otherwise. 85 | 86 | ### GetTxCountOk 87 | 88 | `func (o *ApiResponseLocalTxMonitorSizes) GetTxCountOk() (*int32, bool)` 89 | 90 | GetTxCountOk returns a tuple with the TxCount field if it's non-nil, zero value otherwise 91 | and a boolean to check if the value has been set. 92 | 93 | ### SetTxCount 94 | 95 | `func (o *ApiResponseLocalTxMonitorSizes) SetTxCount(v int32)` 96 | 97 | SetTxCount sets TxCount field to given value. 98 | 99 | ### HasTxCount 100 | 101 | `func (o *ApiResponseLocalTxMonitorSizes) HasTxCount() bool` 102 | 103 | HasTxCount returns a boolean if a field has been set. 104 | 105 | 106 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 107 | 108 | 109 | -------------------------------------------------------------------------------- /config.yaml.example: -------------------------------------------------------------------------------- 1 | --- 2 | # Example config file for cardano-node-api 3 | # The values shown below correspond to the in-code defaults 4 | 5 | logging: 6 | # Logging level 7 | # 8 | # This can also be set via the LOGGING_LEVEL environment variable 9 | level: info 10 | 11 | # Health checks 12 | # 13 | # This can also be set via the LOGGING_HEALTHCHECKS environment variable 14 | healthchecks: false 15 | 16 | api: 17 | # Listen address for the API 18 | # 19 | # This can also be set via the API_LISTEN_ADDRESS environment variable 20 | address: 21 | 22 | # Listen port for the API 23 | # 24 | # This can also be set via the API_LISTEN_PORT environment variable 25 | port: 8080 26 | 27 | metrics: 28 | # Listen address for the metrics endpoint 29 | # 30 | # This can also be set via the METRICS_LISTEN_ADDRESS environment variable 31 | address: 32 | 33 | # Listen port for the metrics endpoint 34 | # 35 | # This can also be set via the METRICS_LISTEN_PORT environment variable 36 | port: 8081 37 | 38 | # The debug endpoint provides access to pprof for debugging purposes. This is 39 | # disabled by default, but it can be enabled by setting the port to a non-zero 40 | # value 41 | debug: 42 | # Listen address for the debug endpoint 43 | # 44 | # This can also be set via the DEBUG_ADDRESS environment variable 45 | address: localhost 46 | 47 | # Listen port for the debug endpoint 48 | # 49 | # This can also be set via the DEBUG_PORT environment variable 50 | port: 0 51 | 52 | node: 53 | # Named Cardano network for cardano-node 54 | # 55 | # This is a short-cut to select the NetworkMagic and can be used to 56 | # select mainnet, preprod, or preview networks. 57 | # 58 | # This can also be set via the CARDANO_NETWORK environment variable 59 | network: mainnet 60 | 61 | # NetworkMagic for network for cardano-node 62 | # 63 | # This selects the correct network for operation and can be configured to 64 | # any network, not just the named networks. 65 | # 66 | # This can also be set via the CARDANO_NODE_NETWORK_MAGIC environment variable 67 | networkMagic: 68 | 69 | # Path to UNIX socket file for cardano-node 70 | # 71 | # This can also be set via the CARDANO_NODE_SOCKET_PATH environment variable 72 | socketPath: 73 | 74 | # Address/port for cardano-node 75 | # 76 | # This requires that you be running socat or similar to create a bridge 77 | # between TCP and the UNIX socket. 78 | # 79 | # These can also be set via the CARDANO_NODE_SOCKET_TCP_HOST and 80 | # CARDANO_NODE_SOCKET_TCP_PORT environment variables 81 | address: 82 | port: 83 | 84 | # Query Timeout 85 | # 86 | # 87 | # This can also be set via the CARDANO_NODE_SOCKET_QUERY_TIMEOUT environment variable 88 | queryTimeout: 180 89 | 90 | # Skip checking connection to cardano-node 91 | # 92 | # On startup, we connect to the configured cardano-node and exit on failure. 93 | # 94 | # Setting this to true will skip this check. 95 | skipCheck: 96 | 97 | # Timeout for connections to cardano-node 98 | # 99 | # This can also be set via the CARDANO_NODE_SOCKET_TIMEOUT environment 100 | # variable 101 | timeout: 102 | 103 | Utxorpc: 104 | # Listen address for Utxo RPC 105 | # 106 | # This can also be set via the GRPC_LISTEN_ADDRESS environment variable 107 | address: 108 | 109 | # Listen port for Utxo RPC 110 | # 111 | # This can also be set via the GRPC_LISTEN_PORT environment variable 112 | port: 9090 113 | 114 | tls: 115 | # Cert file path 116 | # 117 | # This can also be set via the TLS_CERT_FILE_PATH environment variable 118 | certFilePath: 119 | 120 | # key file path 121 | # 122 | # This can also be set via the TLS_KEY_FILE_PATH environment variable 123 | keyFilePath: 124 | -------------------------------------------------------------------------------- /openapi/model_api_response_api_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseApiError type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseApiError{} 20 | 21 | // ApiResponseApiError struct for ApiResponseApiError 22 | type ApiResponseApiError struct { 23 | Msg *string `json:"msg,omitempty"` 24 | } 25 | 26 | // NewApiResponseApiError instantiates a new ApiResponseApiError object 27 | // This constructor will assign default values to properties that have it defined, 28 | // and makes sure properties required by API are set, but the set of arguments 29 | // will change when the set of required properties is changed 30 | func NewApiResponseApiError() *ApiResponseApiError { 31 | this := ApiResponseApiError{} 32 | return &this 33 | } 34 | 35 | // NewApiResponseApiErrorWithDefaults instantiates a new ApiResponseApiError object 36 | // This constructor will only assign default values to properties that have it defined, 37 | // but it doesn't guarantee that properties required by API are set 38 | func NewApiResponseApiErrorWithDefaults() *ApiResponseApiError { 39 | this := ApiResponseApiError{} 40 | return &this 41 | } 42 | 43 | // GetMsg returns the Msg field value if set, zero value otherwise. 44 | func (o *ApiResponseApiError) GetMsg() string { 45 | if o == nil || IsNil(o.Msg) { 46 | var ret string 47 | return ret 48 | } 49 | return *o.Msg 50 | } 51 | 52 | // GetMsgOk returns a tuple with the Msg field value if set, nil otherwise 53 | // and a boolean to check if the value has been set. 54 | func (o *ApiResponseApiError) GetMsgOk() (*string, bool) { 55 | if o == nil || IsNil(o.Msg) { 56 | return nil, false 57 | } 58 | return o.Msg, true 59 | } 60 | 61 | // HasMsg returns a boolean if a field has been set. 62 | func (o *ApiResponseApiError) HasMsg() bool { 63 | if o != nil && !IsNil(o.Msg) { 64 | return true 65 | } 66 | 67 | return false 68 | } 69 | 70 | // SetMsg gets a reference to the given string and assigns it to the Msg field. 71 | func (o *ApiResponseApiError) SetMsg(v string) { 72 | o.Msg = &v 73 | } 74 | 75 | func (o ApiResponseApiError) MarshalJSON() ([]byte, error) { 76 | toSerialize, err := o.ToMap() 77 | if err != nil { 78 | return []byte{}, err 79 | } 80 | return json.Marshal(toSerialize) 81 | } 82 | 83 | func (o ApiResponseApiError) ToMap() (map[string]interface{}, error) { 84 | toSerialize := map[string]interface{}{} 85 | if !IsNil(o.Msg) { 86 | toSerialize["msg"] = o.Msg 87 | } 88 | return toSerialize, nil 89 | } 90 | 91 | type NullableApiResponseApiError struct { 92 | value *ApiResponseApiError 93 | isSet bool 94 | } 95 | 96 | func (v NullableApiResponseApiError) Get() *ApiResponseApiError { 97 | return v.value 98 | } 99 | 100 | func (v *NullableApiResponseApiError) Set(val *ApiResponseApiError) { 101 | v.value = val 102 | v.isSet = true 103 | } 104 | 105 | func (v NullableApiResponseApiError) IsSet() bool { 106 | return v.isSet 107 | } 108 | 109 | func (v *NullableApiResponseApiError) Unset() { 110 | v.value = nil 111 | v.isSet = false 112 | } 113 | 114 | func NewNullableApiResponseApiError( 115 | val *ApiResponseApiError, 116 | ) *NullableApiResponseApiError { 117 | return &NullableApiResponseApiError{value: val, isSet: true} 118 | } 119 | 120 | func (v NullableApiResponseApiError) MarshalJSON() ([]byte, error) { 121 | return json.Marshal(v.value) 122 | } 123 | 124 | func (v *NullableApiResponseApiError) UnmarshalJSON(src []byte) error { 125 | v.isSet = true 126 | return json.Unmarshal(src, &v.value) 127 | } 128 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalStateQuerySystemStart.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalStateQuerySystemStart 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **Day** | Pointer to **int32** | | [optional] 8 | **Picoseconds** | Pointer to **int32** | | [optional] 9 | **Year** | Pointer to **int32** | | [optional] 10 | 11 | ## Methods 12 | 13 | ### NewApiResponseLocalStateQuerySystemStart 14 | 15 | `func NewApiResponseLocalStateQuerySystemStart() *ApiResponseLocalStateQuerySystemStart` 16 | 17 | NewApiResponseLocalStateQuerySystemStart instantiates a new ApiResponseLocalStateQuerySystemStart object 18 | This constructor will assign default values to properties that have it defined, 19 | and makes sure properties required by API are set, but the set of arguments 20 | will change when the set of required properties is changed 21 | 22 | ### NewApiResponseLocalStateQuerySystemStartWithDefaults 23 | 24 | `func NewApiResponseLocalStateQuerySystemStartWithDefaults() *ApiResponseLocalStateQuerySystemStart` 25 | 26 | NewApiResponseLocalStateQuerySystemStartWithDefaults instantiates a new ApiResponseLocalStateQuerySystemStart object 27 | This constructor will only assign default values to properties that have it defined, 28 | but it doesn't guarantee that properties required by API are set 29 | 30 | ### GetDay 31 | 32 | `func (o *ApiResponseLocalStateQuerySystemStart) GetDay() int32` 33 | 34 | GetDay returns the Day field if non-nil, zero value otherwise. 35 | 36 | ### GetDayOk 37 | 38 | `func (o *ApiResponseLocalStateQuerySystemStart) GetDayOk() (*int32, bool)` 39 | 40 | GetDayOk returns a tuple with the Day field if it's non-nil, zero value otherwise 41 | and a boolean to check if the value has been set. 42 | 43 | ### SetDay 44 | 45 | `func (o *ApiResponseLocalStateQuerySystemStart) SetDay(v int32)` 46 | 47 | SetDay sets Day field to given value. 48 | 49 | ### HasDay 50 | 51 | `func (o *ApiResponseLocalStateQuerySystemStart) HasDay() bool` 52 | 53 | HasDay returns a boolean if a field has been set. 54 | 55 | ### GetPicoseconds 56 | 57 | `func (o *ApiResponseLocalStateQuerySystemStart) GetPicoseconds() int32` 58 | 59 | GetPicoseconds returns the Picoseconds field if non-nil, zero value otherwise. 60 | 61 | ### GetPicosecondsOk 62 | 63 | `func (o *ApiResponseLocalStateQuerySystemStart) GetPicosecondsOk() (*int32, bool)` 64 | 65 | GetPicosecondsOk returns a tuple with the Picoseconds field if it's non-nil, zero value otherwise 66 | and a boolean to check if the value has been set. 67 | 68 | ### SetPicoseconds 69 | 70 | `func (o *ApiResponseLocalStateQuerySystemStart) SetPicoseconds(v int32)` 71 | 72 | SetPicoseconds sets Picoseconds field to given value. 73 | 74 | ### HasPicoseconds 75 | 76 | `func (o *ApiResponseLocalStateQuerySystemStart) HasPicoseconds() bool` 77 | 78 | HasPicoseconds returns a boolean if a field has been set. 79 | 80 | ### GetYear 81 | 82 | `func (o *ApiResponseLocalStateQuerySystemStart) GetYear() int32` 83 | 84 | GetYear returns the Year field if non-nil, zero value otherwise. 85 | 86 | ### GetYearOk 87 | 88 | `func (o *ApiResponseLocalStateQuerySystemStart) GetYearOk() (*int32, bool)` 89 | 90 | GetYearOk returns a tuple with the Year field if it's non-nil, zero value otherwise 91 | and a boolean to check if the value has been set. 92 | 93 | ### SetYear 94 | 95 | `func (o *ApiResponseLocalStateQuerySystemStart) SetYear(v int32)` 96 | 97 | SetYear sets Year field to given value. 98 | 99 | ### HasYear 100 | 101 | `func (o *ApiResponseLocalStateQuerySystemStart) HasYear() bool` 102 | 103 | HasYear returns a boolean if a field has been set. 104 | 105 | 106 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 107 | 108 | 109 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blinklabs-io/cardano-node-api 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | connectrpc.com/connect v1.19.1 9 | connectrpc.com/grpchealth v1.4.0 10 | connectrpc.com/grpcreflect v1.3.0 11 | github.com/blinklabs-io/adder v0.35.0 12 | github.com/blinklabs-io/gouroboros v0.144.0 13 | github.com/blinklabs-io/tx-submit-api v0.20.10 14 | github.com/gin-gonic/gin v1.11.0 15 | github.com/gorilla/websocket v1.5.3 16 | github.com/kelseyhightower/envconfig v1.4.0 17 | github.com/penglongli/gin-metrics v0.1.13 18 | github.com/swaggo/files v1.0.1 19 | github.com/swaggo/gin-swagger v1.6.1 20 | github.com/swaggo/swag v1.16.6 21 | github.com/utxorpc/go-codegen v0.18.1 22 | go.uber.org/automaxprocs v1.6.0 23 | golang.org/x/net v0.47.0 24 | gopkg.in/yaml.v2 v2.4.0 25 | ) 26 | 27 | require ( 28 | filippo.io/edwards25519 v1.1.0 // indirect 29 | github.com/KyleBanks/depth v1.2.1 // indirect 30 | github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect 31 | github.com/beorn7/perks v1.0.1 // indirect 32 | github.com/bits-and-blooms/bitset v1.20.0 // indirect 33 | github.com/blinklabs-io/plutigo v0.0.16 // indirect 34 | github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect 35 | github.com/btcsuite/btcd/btcutil v1.1.6 // indirect 36 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect 37 | github.com/bytedance/sonic v1.14.0 // indirect 38 | github.com/bytedance/sonic/loader v0.3.0 // indirect 39 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 40 | github.com/cloudwego/base64x v0.1.6 // indirect 41 | github.com/consensys/gnark-crypto v0.19.2 // indirect 42 | github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect 43 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 44 | github.com/ethereum/go-ethereum v1.16.7 // indirect 45 | github.com/fxamacker/cbor/v2 v2.9.0 // indirect 46 | github.com/gabriel-vasile/mimetype v1.4.9 // indirect 47 | github.com/gin-contrib/sse v1.1.0 // indirect 48 | github.com/go-openapi/jsonpointer v0.20.0 // indirect 49 | github.com/go-openapi/jsonreference v0.20.2 // indirect 50 | github.com/go-openapi/spec v0.20.9 // indirect 51 | github.com/go-openapi/swag v0.22.4 // indirect 52 | github.com/go-playground/locales v0.14.1 // indirect 53 | github.com/go-playground/universal-translator v0.18.1 // indirect 54 | github.com/go-playground/validator/v10 v10.27.0 // indirect 55 | github.com/goccy/go-json v0.10.5 // indirect 56 | github.com/goccy/go-yaml v1.18.0 // indirect 57 | github.com/holiman/uint256 v1.3.2 // indirect 58 | github.com/jinzhu/copier v0.4.0 // indirect 59 | github.com/josharian/intern v1.0.0 // indirect 60 | github.com/json-iterator/go v1.1.12 // indirect 61 | github.com/klauspost/cpuid/v2 v2.3.0 // indirect 62 | github.com/leodido/go-urn v1.4.0 // indirect 63 | github.com/mailru/easyjson v0.7.7 // indirect 64 | github.com/mattn/go-isatty v0.0.20 // indirect 65 | github.com/minio/sha256-simd v1.0.1 // indirect 66 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 67 | github.com/modern-go/reflect2 v1.0.2 // indirect 68 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 69 | github.com/pkg/errors v0.9.1 // indirect 70 | github.com/prometheus/client_golang v1.19.1 // indirect 71 | github.com/prometheus/client_model v0.6.1 // indirect 72 | github.com/prometheus/common v0.52.2 // indirect 73 | github.com/prometheus/procfs v0.13.0 // indirect 74 | github.com/quic-go/qpack v0.6.0 // indirect 75 | github.com/quic-go/quic-go v0.57.0 // indirect 76 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 77 | github.com/ugorji/go/codec v1.3.0 // indirect 78 | github.com/x448/float16 v0.8.4 // indirect 79 | golang.org/x/arch v0.20.0 // indirect 80 | golang.org/x/crypto v0.45.0 // indirect 81 | golang.org/x/mod v0.29.0 // indirect 82 | golang.org/x/sync v0.18.0 // indirect 83 | golang.org/x/sys v0.38.0 // indirect 84 | golang.org/x/text v0.31.0 // indirect 85 | golang.org/x/tools v0.38.0 // indirect 86 | google.golang.org/protobuf v1.36.10 // indirect 87 | gopkg.in/yaml.v3 v3.0.1 // indirect 88 | ) 89 | -------------------------------------------------------------------------------- /internal/api/chainsync.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package api 16 | 17 | import ( 18 | "encoding/hex" 19 | "net/http" 20 | 21 | "github.com/blinklabs-io/adder/event" 22 | "github.com/blinklabs-io/cardano-node-api/internal/node" 23 | ocommon "github.com/blinklabs-io/gouroboros/protocol/common" 24 | "github.com/gin-gonic/gin" 25 | "github.com/gorilla/websocket" 26 | ) 27 | 28 | var upgrader = websocket.Upgrader{ 29 | ReadBufferSize: 1024, 30 | WriteBufferSize: 1024, 31 | } 32 | 33 | func configureChainSyncRoutes(apiGroup *gin.RouterGroup) { 34 | group := apiGroup.Group("/chainsync") 35 | group.GET("/sync", handleChainSyncSync) 36 | } 37 | 38 | type requestChainSyncSync struct { 39 | Hash string `form:"hash"` 40 | Slot uint64 `form:"slot"` 41 | Tip bool `form:"tip"` 42 | } 43 | 44 | // handleChainSyncSync godoc 45 | // 46 | // @Summary Start a chain-sync using a websocket for events 47 | // @Tags chainsync 48 | // @Success 101 49 | // @Failure 400 {object} responseApiError 50 | // @Failure 500 {object} responseApiError 51 | // @Param tip query bool false "whether to start from the current tip" 52 | // @Param slot query int false "slot to start sync at, should match hash" 53 | // @Param hash query string false "block hash to start sync at, should match slot" 54 | // @Router /chainsync/sync [get] 55 | func handleChainSyncSync(c *gin.Context) { 56 | // Get parameters 57 | var req requestChainSyncSync 58 | if err := c.ShouldBindQuery(&req); err != nil { 59 | c.JSON(http.StatusBadRequest, apiError(err.Error())) 60 | return 61 | } 62 | if !req.Tip && (req.Slot == 0 || req.Hash == "") { 63 | c.JSON( 64 | http.StatusBadRequest, 65 | apiError( 66 | "you must provide the 'slot' and 'hash' parameters or set 'tip' to True", 67 | ), 68 | ) 69 | return 70 | } 71 | // Setup event channel 72 | eventChan := make(chan event.Event, 10) 73 | connCfg := node.ConnectionConfig{ 74 | ChainSyncEventChan: eventChan, 75 | } 76 | // Connect to node 77 | oConn, err := node.GetConnection(&connCfg) 78 | if err != nil { 79 | c.JSON(500, apiError(err.Error())) 80 | return 81 | } 82 | // Async error handler 83 | go func() { 84 | err, ok := <-oConn.ErrorChan() 85 | if !ok { 86 | return 87 | } 88 | c.JSON(500, apiError(err.Error())) 89 | }() 90 | defer func() { 91 | // Close Ouroboros connection 92 | oConn.Close() 93 | }() 94 | var intersectPoints []ocommon.Point 95 | if req.Tip { 96 | tip, err := oConn.ChainSync().Client.GetCurrentTip() 97 | if err != nil { 98 | c.JSON(500, apiError(err.Error())) 99 | return 100 | } 101 | intersectPoints = []ocommon.Point{ 102 | tip.Point, 103 | } 104 | } else { 105 | hashBytes, err := hex.DecodeString(req.Hash) 106 | if err != nil { 107 | c.JSON(500, apiError(err.Error())) 108 | return 109 | } 110 | intersectPoints = []ocommon.Point{ 111 | ocommon.NewPoint(req.Slot, hashBytes), 112 | } 113 | } 114 | // Start the sync with the node 115 | if err := oConn.ChainSync().Client.Sync(intersectPoints); err != nil { 116 | c.JSON(500, apiError(err.Error())) 117 | return 118 | } 119 | // Upgrade the connection 120 | webConn, err := upgrader.Upgrade(c.Writer, c.Request, nil) 121 | if err != nil { 122 | return 123 | } 124 | defer webConn.Close() 125 | // Wait for events 126 | for { 127 | evt, ok := <-eventChan 128 | if !ok { 129 | return 130 | } 131 | if err := webConn.WriteJSON(evt); err != nil { 132 | c.JSON(500, apiError(err.Error())) 133 | return 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_tx_monitor_has_tx.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalTxMonitorHasTx type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalTxMonitorHasTx{} 20 | 21 | // ApiResponseLocalTxMonitorHasTx struct for ApiResponseLocalTxMonitorHasTx 22 | type ApiResponseLocalTxMonitorHasTx struct { 23 | HasTx *bool `json:"has_tx,omitempty"` 24 | } 25 | 26 | // NewApiResponseLocalTxMonitorHasTx instantiates a new ApiResponseLocalTxMonitorHasTx object 27 | // This constructor will assign default values to properties that have it defined, 28 | // and makes sure properties required by API are set, but the set of arguments 29 | // will change when the set of required properties is changed 30 | func NewApiResponseLocalTxMonitorHasTx() *ApiResponseLocalTxMonitorHasTx { 31 | this := ApiResponseLocalTxMonitorHasTx{} 32 | return &this 33 | } 34 | 35 | // NewApiResponseLocalTxMonitorHasTxWithDefaults instantiates a new ApiResponseLocalTxMonitorHasTx object 36 | // This constructor will only assign default values to properties that have it defined, 37 | // but it doesn't guarantee that properties required by API are set 38 | func NewApiResponseLocalTxMonitorHasTxWithDefaults() *ApiResponseLocalTxMonitorHasTx { 39 | this := ApiResponseLocalTxMonitorHasTx{} 40 | return &this 41 | } 42 | 43 | // GetHasTx returns the HasTx field value if set, zero value otherwise. 44 | func (o *ApiResponseLocalTxMonitorHasTx) GetHasTx() bool { 45 | if o == nil || IsNil(o.HasTx) { 46 | var ret bool 47 | return ret 48 | } 49 | return *o.HasTx 50 | } 51 | 52 | // GetHasTxOk returns a tuple with the HasTx field value if set, nil otherwise 53 | // and a boolean to check if the value has been set. 54 | func (o *ApiResponseLocalTxMonitorHasTx) GetHasTxOk() (*bool, bool) { 55 | if o == nil || IsNil(o.HasTx) { 56 | return nil, false 57 | } 58 | return o.HasTx, true 59 | } 60 | 61 | // HasHasTx returns a boolean if a field has been set. 62 | func (o *ApiResponseLocalTxMonitorHasTx) HasHasTx() bool { 63 | if o != nil && !IsNil(o.HasTx) { 64 | return true 65 | } 66 | 67 | return false 68 | } 69 | 70 | // SetHasTx gets a reference to the given bool and assigns it to the HasTx field. 71 | func (o *ApiResponseLocalTxMonitorHasTx) SetHasTx(v bool) { 72 | o.HasTx = &v 73 | } 74 | 75 | func (o ApiResponseLocalTxMonitorHasTx) MarshalJSON() ([]byte, error) { 76 | toSerialize, err := o.ToMap() 77 | if err != nil { 78 | return []byte{}, err 79 | } 80 | return json.Marshal(toSerialize) 81 | } 82 | 83 | func (o ApiResponseLocalTxMonitorHasTx) ToMap() (map[string]interface{}, error) { 84 | toSerialize := map[string]interface{}{} 85 | if !IsNil(o.HasTx) { 86 | toSerialize["has_tx"] = o.HasTx 87 | } 88 | return toSerialize, nil 89 | } 90 | 91 | type NullableApiResponseLocalTxMonitorHasTx struct { 92 | value *ApiResponseLocalTxMonitorHasTx 93 | isSet bool 94 | } 95 | 96 | func (v NullableApiResponseLocalTxMonitorHasTx) Get() *ApiResponseLocalTxMonitorHasTx { 97 | return v.value 98 | } 99 | 100 | func (v *NullableApiResponseLocalTxMonitorHasTx) Set( 101 | val *ApiResponseLocalTxMonitorHasTx, 102 | ) { 103 | v.value = val 104 | v.isSet = true 105 | } 106 | 107 | func (v NullableApiResponseLocalTxMonitorHasTx) IsSet() bool { 108 | return v.isSet 109 | } 110 | 111 | func (v *NullableApiResponseLocalTxMonitorHasTx) Unset() { 112 | v.value = nil 113 | v.isSet = false 114 | } 115 | 116 | func NewNullableApiResponseLocalTxMonitorHasTx( 117 | val *ApiResponseLocalTxMonitorHasTx, 118 | ) *NullableApiResponseLocalTxMonitorHasTx { 119 | return &NullableApiResponseLocalTxMonitorHasTx{value: val, isSet: true} 120 | } 121 | 122 | func (v NullableApiResponseLocalTxMonitorHasTx) MarshalJSON() ([]byte, error) { 123 | return json.Marshal(v.value) 124 | } 125 | 126 | func (v *NullableApiResponseLocalTxMonitorHasTx) UnmarshalJSON( 127 | src []byte, 128 | ) error { 129 | v.isSet = true 130 | return json.Unmarshal(src, &v.value) 131 | } 132 | -------------------------------------------------------------------------------- /internal/node/chainsync.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package node 16 | 17 | import ( 18 | "errors" 19 | "time" 20 | 21 | "github.com/blinklabs-io/adder/event" 22 | "github.com/blinklabs-io/cardano-node-api/internal/config" 23 | "github.com/blinklabs-io/gouroboros/ledger" 24 | "github.com/blinklabs-io/gouroboros/protocol/chainsync" 25 | "github.com/blinklabs-io/gouroboros/protocol/common" 26 | ) 27 | 28 | func buildChainSyncConfig(connCfg ConnectionConfig) chainsync.Config { 29 | cfg := config.GetConfig() 30 | // #nosec G115 31 | return chainsync.NewConfig( 32 | chainsync.WithBlockTimeout( 33 | time.Duration(cfg.Node.QueryTimeout)*time.Second, 34 | ), 35 | // We wrap the handler funcs to include our ConnectionConfig 36 | chainsync.WithRollBackwardFunc( 37 | func(connCfg ConnectionConfig) chainsync.RollBackwardFunc { 38 | return func(ctx chainsync.CallbackContext, point common.Point, tip chainsync.Tip) error { 39 | return chainSyncRollBackwardHandler( 40 | ctx, connCfg, point, tip, 41 | ) 42 | } 43 | }(connCfg), 44 | ), 45 | chainsync.WithRollForwardFunc( 46 | func(connCfg ConnectionConfig) chainsync.RollForwardFunc { 47 | return func(ctx chainsync.CallbackContext, blockType uint, blockData any, tip chainsync.Tip) error { 48 | return chainSyncRollForwardHandler( 49 | ctx, connCfg, blockType, blockData, tip, 50 | ) 51 | } 52 | }(connCfg), 53 | ), 54 | ) 55 | } 56 | 57 | func chainSyncRollBackwardHandler( 58 | _ctx chainsync.CallbackContext, 59 | connCfg ConnectionConfig, 60 | point common.Point, 61 | _tip chainsync.Tip, 62 | ) error { 63 | if connCfg.ChainSyncEventChan != nil { 64 | evt := event.New( 65 | "chainsync.rollback", 66 | time.Now(), 67 | nil, 68 | event.NewRollbackEvent(point), 69 | ) 70 | connCfg.ChainSyncEventChan <- evt 71 | } 72 | return nil 73 | } 74 | 75 | func chainSyncRollForwardHandler( 76 | _ctx chainsync.CallbackContext, 77 | connCfg ConnectionConfig, 78 | _blockType uint, 79 | blockData any, 80 | _tip chainsync.Tip, 81 | ) error { 82 | cfg := config.GetConfig() 83 | if connCfg.ChainSyncEventChan != nil { 84 | switch v := blockData.(type) { 85 | case ledger.Block: 86 | // Emit block-level event 87 | blockEvt := event.New( 88 | "chainsync.block", 89 | time.Now(), 90 | event.NewBlockContext(v, cfg.Node.NetworkMagic), 91 | event.NewBlockEvent(v, true), 92 | ) 93 | connCfg.ChainSyncEventChan <- blockEvt 94 | // Emit transaction-level events 95 | for t, transaction := range v.Transactions() { 96 | // TODO: do we need to resolve inputs? 97 | // resolvedInputs, err := resolveTransactionInputs(transaction, connCfg) 98 | // if err != nil { 99 | // return fmt.Errorf("failed to resolve inputs for transaction: %w", err) 100 | // } 101 | txEvt := event.New( 102 | "chainsync.transaction", 103 | time.Now(), 104 | // #nosec G115 105 | event.NewTransactionContext(v, transaction, uint32(t), cfg.Node.NetworkMagic), 106 | event.NewTransactionEvent(v, transaction, true, nil), 107 | ) 108 | connCfg.ChainSyncEventChan <- txEvt 109 | } 110 | /* 111 | case ledger.BlockHeader: 112 | blockSlot := v.SlotNumber() 113 | blockHash, _ := hex.DecodeString(v.Hash()) 114 | oConn, err := GetConnection() 115 | if err != nil { 116 | return err 117 | } 118 | block, err = oConn.BlockFetch().Client.GetBlock(common.NewPoint(blockSlot, blockHash)) 119 | if err != nil { 120 | return err 121 | } 122 | */ 123 | default: 124 | return errors.New("unknown block data") 125 | } 126 | } 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /internal/utxorpc/api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utxorpc 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | "time" 21 | 22 | "connectrpc.com/connect" 23 | "connectrpc.com/grpchealth" 24 | "connectrpc.com/grpcreflect" 25 | "github.com/blinklabs-io/cardano-node-api/internal/config" 26 | "github.com/blinklabs-io/cardano-node-api/internal/logging" 27 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/query/queryconnect" 28 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/submit/submitconnect" 29 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/sync/syncconnect" 30 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/watch/watchconnect" 31 | "golang.org/x/net/http2" 32 | "golang.org/x/net/http2/h2c" 33 | ) 34 | 35 | func Start(cfg *config.Config) error { 36 | // Standard logging 37 | logger := logging.GetLogger() 38 | if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" { 39 | logger.Info(fmt.Sprintf( 40 | "starting gRPC TLS listener on: %s:%d", 41 | cfg.Utxorpc.ListenAddress, 42 | cfg.Utxorpc.ListenPort, 43 | )) 44 | } else { 45 | logger.Info(fmt.Sprintf( 46 | "starting gRPC listener on %s:%d", 47 | cfg.Utxorpc.ListenAddress, 48 | cfg.Utxorpc.ListenPort, 49 | )) 50 | } 51 | mux := http.NewServeMux() 52 | compress1KB := connect.WithCompressMinBytes(1024) 53 | queryPath, queryHandler := queryconnect.NewQueryServiceHandler( 54 | &queryServiceServer{}, 55 | compress1KB, 56 | ) 57 | submitPath, submitHandler := submitconnect.NewSubmitServiceHandler( 58 | &submitServiceServer{}, 59 | compress1KB, 60 | ) 61 | syncPath, syncHandler := syncconnect.NewSyncServiceHandler( 62 | &chainSyncServiceServer{}, 63 | compress1KB, 64 | ) 65 | watchPath, watchHandler := watchconnect.NewWatchServiceHandler( 66 | &watchServiceServer{}, 67 | compress1KB, 68 | ) 69 | mux.Handle(queryPath, queryHandler) 70 | mux.Handle(submitPath, submitHandler) 71 | mux.Handle(syncPath, syncHandler) 72 | mux.Handle(watchPath, watchHandler) 73 | mux.Handle( 74 | grpchealth.NewHandler( 75 | grpchealth.NewStaticChecker( 76 | queryconnect.QueryServiceName, 77 | submitconnect.SubmitServiceName, 78 | syncconnect.SyncServiceName, 79 | watchconnect.WatchServiceName, 80 | ), 81 | compress1KB, 82 | ), 83 | ) 84 | mux.Handle( 85 | grpcreflect.NewHandlerV1( 86 | grpcreflect.NewStaticReflector( 87 | queryconnect.QueryServiceName, 88 | submitconnect.SubmitServiceName, 89 | syncconnect.SyncServiceName, 90 | watchconnect.WatchServiceName, 91 | ), 92 | compress1KB, 93 | ), 94 | ) 95 | mux.Handle( 96 | grpcreflect.NewHandlerV1Alpha( 97 | grpcreflect.NewStaticReflector( 98 | queryconnect.QueryServiceName, 99 | submitconnect.SubmitServiceName, 100 | syncconnect.SyncServiceName, 101 | watchconnect.WatchServiceName, 102 | ), 103 | compress1KB, 104 | ), 105 | ) 106 | if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" { 107 | server := &http.Server{ 108 | Addr: fmt.Sprintf( 109 | "%s:%d", 110 | cfg.Utxorpc.ListenAddress, 111 | cfg.Utxorpc.ListenPort, 112 | ), 113 | Handler: mux, 114 | ReadHeaderTimeout: 60 * time.Second, 115 | } 116 | return server.ListenAndServeTLS( 117 | cfg.Tls.CertFilePath, 118 | cfg.Tls.KeyFilePath, 119 | ) 120 | } else { 121 | server := &http.Server{ 122 | Addr: fmt.Sprintf( 123 | "%s:%d", 124 | cfg.Utxorpc.ListenAddress, 125 | cfg.Utxorpc.ListenPort, 126 | ), 127 | // Use h2c so we can serve HTTP/2 without TLS 128 | Handler: h2c.NewHandler(mux, &http2.Server{}), 129 | ReadHeaderTimeout: 60 * time.Second, 130 | } 131 | return server.ListenAndServe() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /internal/utxorpc/watch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utxorpc 16 | 17 | import ( 18 | "context" 19 | "errors" 20 | "fmt" 21 | "log" 22 | 23 | connect "connectrpc.com/connect" 24 | "github.com/blinklabs-io/adder/event" 25 | "github.com/blinklabs-io/cardano-node-api/internal/node" 26 | ocommon "github.com/blinklabs-io/gouroboros/protocol/common" 27 | watch "github.com/utxorpc/go-codegen/utxorpc/v1alpha/watch" 28 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/watch/watchconnect" 29 | ) 30 | 31 | // watchServiceServer implements the WatchService API 32 | type watchServiceServer struct { 33 | watchconnect.UnimplementedWatchServiceHandler 34 | } 35 | 36 | // WatchTx 37 | func (s *watchServiceServer) WatchTx( 38 | ctx context.Context, 39 | req *connect.Request[watch.WatchTxRequest], 40 | stream *connect.ServerStream[watch.WatchTxResponse], 41 | ) error { 42 | predicate := req.Msg.GetPredicate() // Predicate 43 | fieldMask := req.Msg.GetFieldMask() 44 | log.Printf( 45 | "Got a WatchTx request with predicate %v and fieldMask %v", 46 | predicate, 47 | fieldMask, 48 | ) 49 | 50 | // Setup event channel 51 | eventChan := make(chan event.Event, 10) 52 | connCfg := node.ConnectionConfig{ 53 | ChainSyncEventChan: eventChan, 54 | } 55 | // Connect to node 56 | oConn, err := node.GetConnection(&connCfg) 57 | if err != nil { 58 | log.Printf("ERROR: %s", err) 59 | return err 60 | } 61 | defer func() { 62 | // Close Ouroboros connection 63 | oConn.Close() 64 | }() 65 | 66 | // Get current tip 67 | tip, err := oConn.ChainSync().Client.GetCurrentTip() 68 | if err != nil { 69 | log.Printf("ERROR: %s", err) 70 | return err 71 | } 72 | // Start the sync with the node 73 | err = oConn.ChainSync().Client.Sync([]ocommon.Point{tip.Point}) 74 | if err != nil { 75 | log.Printf("ERROR: %s", err) 76 | return err 77 | } 78 | 79 | // Wait for events 80 | for { 81 | evt, ok := <-eventChan 82 | if !ok { 83 | log.Printf("ERROR: channel closed") 84 | return errors.New("ERROR: channel closed") 85 | } 86 | 87 | switch evt.Type { 88 | case "chainsync.block": 89 | // Get event context to get the block chain information 90 | context := evt.Context 91 | if context == nil { 92 | log.Printf("ERROR: empty block context") 93 | return errors.New("ERROR: empty block context") 94 | } 95 | bc := context.(event.BlockContext) 96 | // Get event payload to get the block data 97 | payload := evt.Payload 98 | if payload == nil { 99 | log.Printf( 100 | "ERROR: empty payload: block: %d, slot: %d", 101 | bc.BlockNumber, 102 | bc.SlotNumber, 103 | ) 104 | return fmt.Errorf( 105 | "ERROR: empty payload: block: %d, slot: %d", 106 | bc.BlockNumber, 107 | bc.SlotNumber, 108 | ) 109 | } 110 | be := payload.(event.BlockEvent) 111 | block := be.Block // gOuorboros Block 112 | 113 | // Loop through transactions 114 | for _, tx := range block.Transactions() { 115 | tmpTx, err := tx.Utxorpc() 116 | if err != nil { 117 | return fmt.Errorf("convert transaction: %w", err) 118 | } 119 | resp := &watch.WatchTxResponse{} 120 | var act watch.AnyChainTx 121 | actc := watch.AnyChainTx_Cardano{ 122 | Cardano: tmpTx, 123 | } 124 | act.Chain = &actc 125 | var wtra watch.WatchTxResponse_Apply 126 | wtra.Apply = &act 127 | resp.Action = &wtra 128 | if predicate == nil { 129 | err := stream.Send(resp) 130 | if err != nil { 131 | return err 132 | } 133 | } else { 134 | // TODO: filter from all Predicate types 135 | err := stream.Send(resp) 136 | if err != nil { 137 | return err 138 | } 139 | } 140 | } 141 | } 142 | // Log event 143 | log.Printf("event: %v", evt) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /internal/api/localtxsubmission.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package api 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "io" 21 | 22 | "github.com/blinklabs-io/cardano-node-api/internal/config" 23 | "github.com/blinklabs-io/cardano-node-api/internal/logging" 24 | "github.com/blinklabs-io/gouroboros/protocol/localtxsubmission" 25 | "github.com/blinklabs-io/tx-submit-api/submit" 26 | "github.com/gin-gonic/gin" 27 | ) 28 | 29 | func configureLocalTxSubmissionRoutes(apiGroup *gin.RouterGroup) { 30 | group := apiGroup.Group("/localtxsubmission") 31 | group.POST("/tx", handleLocalSubmitTx) 32 | } 33 | 34 | // handleLocalSubmitTx godoc 35 | // 36 | // @Summary Submit Tx 37 | // @Tags localtxsubmission 38 | // @Description Submit an already serialized transaction to the network. 39 | // @Produce json 40 | // @Param Content-Type header string true "Content type" Enums(application/cbor) 41 | // @Success 202 {object} string "Ok" 42 | // @Failure 400 {object} string "Bad Request" 43 | // @Failure 415 {object} string "Unsupported Media Type" 44 | // @Failure 500 {object} string "Server Error" 45 | // @Router /localtxsubmission/tx [post] 46 | func handleLocalSubmitTx(c *gin.Context) { 47 | // First, initialize our configuration and loggers 48 | cfg := config.GetConfig() 49 | logger := logging.GetLogger() 50 | // Check our headers for content-type 51 | if c.ContentType() != "application/cbor" { 52 | // Log the error, return an error to the user, and increment failed count 53 | logger.Error("invalid request body, should be application/cbor") 54 | c.JSON(415, "invalid request body, should be application/cbor") 55 | // _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil) 56 | return 57 | } 58 | // Read raw transaction bytes from the request body and store in a byte array 59 | txRawBytes, err := io.ReadAll(c.Request.Body) 60 | if err != nil { 61 | // Log the error, return an error to the user, and increment failed count 62 | logger.Error("failed to read request body:", "error", err) 63 | c.JSON(500, "failed to read request body") 64 | // _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil) 65 | return 66 | } 67 | // Close request body after read 68 | if c.Request.Body != nil { 69 | if err = c.Request.Body.Close(); err != nil { 70 | logger.Error("failed to close request body:", "error", err) 71 | } 72 | } 73 | // Send TX 74 | errorChan := make(chan error) 75 | submitConfig := &submit.Config{ 76 | ErrorChan: errorChan, 77 | NetworkMagic: cfg.Node.NetworkMagic, 78 | NodeAddress: cfg.Node.Address, 79 | NodePort: cfg.Node.Port, 80 | SocketPath: cfg.Node.SocketPath, 81 | Timeout: cfg.Node.Timeout, 82 | } 83 | txHash, err := submit.SubmitTx(submitConfig, txRawBytes) 84 | if err != nil { 85 | if c.GetHeader("Accept") == "application/cbor" { 86 | var txRejectErr *localtxsubmission.TransactionRejectedError 87 | if errors.As(err, &txRejectErr) { 88 | c.Data(400, "application/cbor", txRejectErr.ReasonCbor) 89 | } else { 90 | c.Data(500, "application/cbor", []byte{}) 91 | } 92 | } else { 93 | if err.Error() != "" { 94 | c.JSON(400, err.Error()) 95 | } else { 96 | c.JSON(400, fmt.Sprintf("%s", err)) 97 | } 98 | } 99 | // _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil) 100 | return 101 | } 102 | // Start async error handler 103 | go func() { 104 | err, ok := <-errorChan 105 | if ok { 106 | logger.Error("failure communicating with node:", "error", err) 107 | c.JSON(500, "failure communicating with node") 108 | // _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil) 109 | } 110 | }() 111 | // Return transaction ID 112 | c.JSON(202, txHash) 113 | // Increment custom metric 114 | // _ = ginmetrics.GetMonitor().GetMetric("tx_submit_count").Inc(nil) 115 | } 116 | -------------------------------------------------------------------------------- /internal/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | ouroboros "github.com/blinklabs-io/gouroboros" 22 | "github.com/kelseyhightower/envconfig" 23 | "gopkg.in/yaml.v2" 24 | ) 25 | 26 | type Config struct { 27 | Tls TlsConfig `yaml:"tls"` 28 | Logging LoggingConfig `yaml:"logging"` 29 | Api ApiConfig `yaml:"api"` 30 | Metrics MetricsConfig `yaml:"metrics"` 31 | Debug DebugConfig `yaml:"debug"` 32 | Utxorpc UtxorpcConfig `yaml:"utxorpc"` 33 | Node NodeConfig `yaml:"node"` 34 | } 35 | 36 | type LoggingConfig struct { 37 | Level string `yaml:"level" envconfig:"LOGGING_LEVEL"` 38 | Healthchecks bool `yaml:"healthchecks" envconfig:"LOGGING_HEALTHCHECKS"` 39 | } 40 | 41 | type ApiConfig struct { 42 | ListenAddress string `yaml:"address" envconfig:"API_LISTEN_ADDRESS"` 43 | ListenPort uint `yaml:"port" envconfig:"API_LISTEN_PORT"` 44 | } 45 | 46 | type DebugConfig struct { 47 | ListenAddress string `yaml:"address" envconfig:"DEBUG_ADDRESS"` 48 | ListenPort uint `yaml:"port" envconfig:"DEBUG_PORT"` 49 | } 50 | 51 | type MetricsConfig struct { 52 | ListenAddress string `yaml:"address" envconfig:"METRICS_LISTEN_ADDRESS"` 53 | ListenPort uint `yaml:"port" envconfig:"METRICS_LISTEN_PORT"` 54 | } 55 | 56 | type NodeConfig struct { 57 | Network string `yaml:"network" envconfig:"CARDANO_NETWORK"` 58 | Address string `yaml:"address" envconfig:"CARDANO_NODE_SOCKET_TCP_HOST"` 59 | SocketPath string `yaml:"socketPath" envconfig:"CARDANO_NODE_SOCKET_PATH"` 60 | Port uint `yaml:"port" envconfig:"CARDANO_NODE_SOCKET_TCP_PORT"` 61 | QueryTimeout uint `yaml:"queryTimeout" envconfig:"CARDANO_NODE_SOCKET_QUERY_TIMEOUT"` 62 | Timeout uint `yaml:"timeout" envconfig:"CARDANO_NODE_SOCKET_TIMEOUT"` 63 | NetworkMagic uint32 `yaml:"networkMagic" envconfig:"CARDANO_NODE_NETWORK_MAGIC"` 64 | SkipCheck bool `yaml:"skipCheck" envconfig:"CARDANO_NODE_SKIP_CHECK"` 65 | } 66 | 67 | type UtxorpcConfig struct { 68 | ListenAddress string `yaml:"address" envconfig:"GRPC_LISTEN_ADDRESS"` 69 | ListenPort uint `yaml:"port" envconfig:"GRPC_LISTEN_PORT"` 70 | } 71 | 72 | type TlsConfig struct { 73 | CertFilePath string `yaml:"certFilePath" envconfig:"TLS_CERT_FILE_PATH"` 74 | KeyFilePath string `yaml:"keyFilePath" envconfig:"TLS_KEY_FILE_PATH"` 75 | } 76 | 77 | // Singleton config instance with default values 78 | var globalConfig = &Config{ 79 | Logging: LoggingConfig{ 80 | Level: "info", 81 | Healthchecks: false, 82 | }, 83 | Api: ApiConfig{ 84 | ListenAddress: "", 85 | ListenPort: 8080, 86 | }, 87 | Debug: DebugConfig{ 88 | ListenAddress: "localhost", 89 | ListenPort: 0, 90 | }, 91 | Metrics: MetricsConfig{ 92 | ListenAddress: "", 93 | ListenPort: 8081, 94 | }, 95 | Node: NodeConfig{ 96 | Network: "mainnet", 97 | SocketPath: "/node-ipc/node.socket", 98 | QueryTimeout: 180, 99 | Timeout: 5, 100 | }, 101 | Utxorpc: UtxorpcConfig{ 102 | ListenAddress: "", 103 | ListenPort: 9090, 104 | }, 105 | } 106 | 107 | func Load(configFile string) (*Config, error) { 108 | // Load config file as YAML if provided 109 | if configFile != "" { 110 | buf, err := os.ReadFile(configFile) 111 | if err != nil { 112 | return nil, fmt.Errorf("error reading config file: %w", err) 113 | } 114 | err = yaml.Unmarshal(buf, globalConfig) 115 | if err != nil { 116 | return nil, fmt.Errorf("error parsing config file: %w", err) 117 | } 118 | } 119 | // Load config values from environment variables 120 | // We use "dummy" as the app name here to (mostly) prevent picking up env 121 | // vars that we hadn't explicitly specified in annotations above 122 | err := envconfig.Process("dummy", globalConfig) 123 | if err != nil { 124 | return nil, fmt.Errorf("error processing environment: %w", err) 125 | } 126 | // Populate network magic value from network name 127 | if globalConfig.Node.Network != "" { 128 | network, ok := ouroboros.NetworkByName(globalConfig.Node.Network) 129 | if !ok { 130 | return nil, fmt.Errorf( 131 | "unknown network: %s", 132 | globalConfig.Node.Network, 133 | ) 134 | } 135 | globalConfig.Node.NetworkMagic = network.NetworkMagic 136 | } 137 | return globalConfig, nil 138 | } 139 | 140 | // Config returns the global config instance 141 | func GetConfig() *Config { 142 | return globalConfig 143 | } 144 | -------------------------------------------------------------------------------- /openapi/docs/ApiResponseLocalStateQueryTip.md: -------------------------------------------------------------------------------- 1 | # ApiResponseLocalStateQueryTip 2 | 3 | ## Properties 4 | 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **BlockNo** | Pointer to **int32** | | [optional] 8 | **EpochNo** | Pointer to **int32** | | [optional] 9 | **Era** | Pointer to **string** | | [optional] 10 | **Hash** | Pointer to **string** | | [optional] 11 | **SlotNo** | Pointer to **int32** | | [optional] 12 | 13 | ## Methods 14 | 15 | ### NewApiResponseLocalStateQueryTip 16 | 17 | `func NewApiResponseLocalStateQueryTip() *ApiResponseLocalStateQueryTip` 18 | 19 | NewApiResponseLocalStateQueryTip instantiates a new ApiResponseLocalStateQueryTip object 20 | This constructor will assign default values to properties that have it defined, 21 | and makes sure properties required by API are set, but the set of arguments 22 | will change when the set of required properties is changed 23 | 24 | ### NewApiResponseLocalStateQueryTipWithDefaults 25 | 26 | `func NewApiResponseLocalStateQueryTipWithDefaults() *ApiResponseLocalStateQueryTip` 27 | 28 | NewApiResponseLocalStateQueryTipWithDefaults instantiates a new ApiResponseLocalStateQueryTip object 29 | This constructor will only assign default values to properties that have it defined, 30 | but it doesn't guarantee that properties required by API are set 31 | 32 | ### GetBlockNo 33 | 34 | `func (o *ApiResponseLocalStateQueryTip) GetBlockNo() int32` 35 | 36 | GetBlockNo returns the BlockNo field if non-nil, zero value otherwise. 37 | 38 | ### GetBlockNoOk 39 | 40 | `func (o *ApiResponseLocalStateQueryTip) GetBlockNoOk() (*int32, bool)` 41 | 42 | GetBlockNoOk returns a tuple with the BlockNo field if it's non-nil, zero value otherwise 43 | and a boolean to check if the value has been set. 44 | 45 | ### SetBlockNo 46 | 47 | `func (o *ApiResponseLocalStateQueryTip) SetBlockNo(v int32)` 48 | 49 | SetBlockNo sets BlockNo field to given value. 50 | 51 | ### HasBlockNo 52 | 53 | `func (o *ApiResponseLocalStateQueryTip) HasBlockNo() bool` 54 | 55 | HasBlockNo returns a boolean if a field has been set. 56 | 57 | ### GetEpochNo 58 | 59 | `func (o *ApiResponseLocalStateQueryTip) GetEpochNo() int32` 60 | 61 | GetEpochNo returns the EpochNo field if non-nil, zero value otherwise. 62 | 63 | ### GetEpochNoOk 64 | 65 | `func (o *ApiResponseLocalStateQueryTip) GetEpochNoOk() (*int32, bool)` 66 | 67 | GetEpochNoOk returns a tuple with the EpochNo field if it's non-nil, zero value otherwise 68 | and a boolean to check if the value has been set. 69 | 70 | ### SetEpochNo 71 | 72 | `func (o *ApiResponseLocalStateQueryTip) SetEpochNo(v int32)` 73 | 74 | SetEpochNo sets EpochNo field to given value. 75 | 76 | ### HasEpochNo 77 | 78 | `func (o *ApiResponseLocalStateQueryTip) HasEpochNo() bool` 79 | 80 | HasEpochNo returns a boolean if a field has been set. 81 | 82 | ### GetEra 83 | 84 | `func (o *ApiResponseLocalStateQueryTip) GetEra() string` 85 | 86 | GetEra returns the Era field if non-nil, zero value otherwise. 87 | 88 | ### GetEraOk 89 | 90 | `func (o *ApiResponseLocalStateQueryTip) GetEraOk() (*string, bool)` 91 | 92 | GetEraOk returns a tuple with the Era field if it's non-nil, zero value otherwise 93 | and a boolean to check if the value has been set. 94 | 95 | ### SetEra 96 | 97 | `func (o *ApiResponseLocalStateQueryTip) SetEra(v string)` 98 | 99 | SetEra sets Era field to given value. 100 | 101 | ### HasEra 102 | 103 | `func (o *ApiResponseLocalStateQueryTip) HasEra() bool` 104 | 105 | HasEra returns a boolean if a field has been set. 106 | 107 | ### GetHash 108 | 109 | `func (o *ApiResponseLocalStateQueryTip) GetHash() string` 110 | 111 | GetHash returns the Hash field if non-nil, zero value otherwise. 112 | 113 | ### GetHashOk 114 | 115 | `func (o *ApiResponseLocalStateQueryTip) GetHashOk() (*string, bool)` 116 | 117 | GetHashOk returns a tuple with the Hash field if it's non-nil, zero value otherwise 118 | and a boolean to check if the value has been set. 119 | 120 | ### SetHash 121 | 122 | `func (o *ApiResponseLocalStateQueryTip) SetHash(v string)` 123 | 124 | SetHash sets Hash field to given value. 125 | 126 | ### HasHash 127 | 128 | `func (o *ApiResponseLocalStateQueryTip) HasHash() bool` 129 | 130 | HasHash returns a boolean if a field has been set. 131 | 132 | ### GetSlotNo 133 | 134 | `func (o *ApiResponseLocalStateQueryTip) GetSlotNo() int32` 135 | 136 | GetSlotNo returns the SlotNo field if non-nil, zero value otherwise. 137 | 138 | ### GetSlotNoOk 139 | 140 | `func (o *ApiResponseLocalStateQueryTip) GetSlotNoOk() (*int32, bool)` 141 | 142 | GetSlotNoOk returns a tuple with the SlotNo field if it's non-nil, zero value otherwise 143 | and a boolean to check if the value has been set. 144 | 145 | ### SetSlotNo 146 | 147 | `func (o *ApiResponseLocalStateQueryTip) SetSlotNo(v int32)` 148 | 149 | SetSlotNo sets SlotNo field to given value. 150 | 151 | ### HasSlotNo 152 | 153 | `func (o *ApiResponseLocalStateQueryTip) HasSlotNo() bool` 154 | 155 | HasSlotNo returns a boolean if a field has been set. 156 | 157 | 158 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 159 | 160 | 161 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_tx_monitor_txs.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalTxMonitorTxs type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalTxMonitorTxs{} 20 | 21 | // ApiResponseLocalTxMonitorTxs struct for ApiResponseLocalTxMonitorTxs 22 | type ApiResponseLocalTxMonitorTxs struct { 23 | TxBytes *string `json:"tx_bytes,omitempty"` 24 | TxHash *string `json:"tx_hash,omitempty"` 25 | } 26 | 27 | // NewApiResponseLocalTxMonitorTxs instantiates a new ApiResponseLocalTxMonitorTxs object 28 | // This constructor will assign default values to properties that have it defined, 29 | // and makes sure properties required by API are set, but the set of arguments 30 | // will change when the set of required properties is changed 31 | func NewApiResponseLocalTxMonitorTxs() *ApiResponseLocalTxMonitorTxs { 32 | this := ApiResponseLocalTxMonitorTxs{} 33 | return &this 34 | } 35 | 36 | // NewApiResponseLocalTxMonitorTxsWithDefaults instantiates a new ApiResponseLocalTxMonitorTxs object 37 | // This constructor will only assign default values to properties that have it defined, 38 | // but it doesn't guarantee that properties required by API are set 39 | func NewApiResponseLocalTxMonitorTxsWithDefaults() *ApiResponseLocalTxMonitorTxs { 40 | this := ApiResponseLocalTxMonitorTxs{} 41 | return &this 42 | } 43 | 44 | // GetTxBytes returns the TxBytes field value if set, zero value otherwise. 45 | func (o *ApiResponseLocalTxMonitorTxs) GetTxBytes() string { 46 | if o == nil || IsNil(o.TxBytes) { 47 | var ret string 48 | return ret 49 | } 50 | return *o.TxBytes 51 | } 52 | 53 | // GetTxBytesOk returns a tuple with the TxBytes field value if set, nil otherwise 54 | // and a boolean to check if the value has been set. 55 | func (o *ApiResponseLocalTxMonitorTxs) GetTxBytesOk() (*string, bool) { 56 | if o == nil || IsNil(o.TxBytes) { 57 | return nil, false 58 | } 59 | return o.TxBytes, true 60 | } 61 | 62 | // HasTxBytes returns a boolean if a field has been set. 63 | func (o *ApiResponseLocalTxMonitorTxs) HasTxBytes() bool { 64 | if o != nil && !IsNil(o.TxBytes) { 65 | return true 66 | } 67 | 68 | return false 69 | } 70 | 71 | // SetTxBytes gets a reference to the given string and assigns it to the TxBytes field. 72 | func (o *ApiResponseLocalTxMonitorTxs) SetTxBytes(v string) { 73 | o.TxBytes = &v 74 | } 75 | 76 | // GetTxHash returns the TxHash field value if set, zero value otherwise. 77 | func (o *ApiResponseLocalTxMonitorTxs) GetTxHash() string { 78 | if o == nil || IsNil(o.TxHash) { 79 | var ret string 80 | return ret 81 | } 82 | return *o.TxHash 83 | } 84 | 85 | // GetTxHashOk returns a tuple with the TxHash field value if set, nil otherwise 86 | // and a boolean to check if the value has been set. 87 | func (o *ApiResponseLocalTxMonitorTxs) GetTxHashOk() (*string, bool) { 88 | if o == nil || IsNil(o.TxHash) { 89 | return nil, false 90 | } 91 | return o.TxHash, true 92 | } 93 | 94 | // HasTxHash returns a boolean if a field has been set. 95 | func (o *ApiResponseLocalTxMonitorTxs) HasTxHash() bool { 96 | if o != nil && !IsNil(o.TxHash) { 97 | return true 98 | } 99 | 100 | return false 101 | } 102 | 103 | // SetTxHash gets a reference to the given string and assigns it to the TxHash field. 104 | func (o *ApiResponseLocalTxMonitorTxs) SetTxHash(v string) { 105 | o.TxHash = &v 106 | } 107 | 108 | func (o ApiResponseLocalTxMonitorTxs) MarshalJSON() ([]byte, error) { 109 | toSerialize, err := o.ToMap() 110 | if err != nil { 111 | return []byte{}, err 112 | } 113 | return json.Marshal(toSerialize) 114 | } 115 | 116 | func (o ApiResponseLocalTxMonitorTxs) ToMap() (map[string]interface{}, error) { 117 | toSerialize := map[string]interface{}{} 118 | if !IsNil(o.TxBytes) { 119 | toSerialize["tx_bytes"] = o.TxBytes 120 | } 121 | if !IsNil(o.TxHash) { 122 | toSerialize["tx_hash"] = o.TxHash 123 | } 124 | return toSerialize, nil 125 | } 126 | 127 | type NullableApiResponseLocalTxMonitorTxs struct { 128 | value *ApiResponseLocalTxMonitorTxs 129 | isSet bool 130 | } 131 | 132 | func (v NullableApiResponseLocalTxMonitorTxs) Get() *ApiResponseLocalTxMonitorTxs { 133 | return v.value 134 | } 135 | 136 | func (v *NullableApiResponseLocalTxMonitorTxs) Set( 137 | val *ApiResponseLocalTxMonitorTxs, 138 | ) { 139 | v.value = val 140 | v.isSet = true 141 | } 142 | 143 | func (v NullableApiResponseLocalTxMonitorTxs) IsSet() bool { 144 | return v.isSet 145 | } 146 | 147 | func (v *NullableApiResponseLocalTxMonitorTxs) Unset() { 148 | v.value = nil 149 | v.isSet = false 150 | } 151 | 152 | func NewNullableApiResponseLocalTxMonitorTxs( 153 | val *ApiResponseLocalTxMonitorTxs, 154 | ) *NullableApiResponseLocalTxMonitorTxs { 155 | return &NullableApiResponseLocalTxMonitorTxs{value: val, isSet: true} 156 | } 157 | 158 | func (v NullableApiResponseLocalTxMonitorTxs) MarshalJSON() ([]byte, error) { 159 | return json.Marshal(v.value) 160 | } 161 | 162 | func (v *NullableApiResponseLocalTxMonitorTxs) UnmarshalJSON(src []byte) error { 163 | v.isSet = true 164 | return json.Unmarshal(src, &v.value) 165 | } 166 | -------------------------------------------------------------------------------- /internal/api/api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package api 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "slices" 21 | 22 | _ "github.com/blinklabs-io/cardano-node-api/docs" // docs is generated by Swag CLI 23 | "github.com/blinklabs-io/cardano-node-api/internal/config" 24 | "github.com/blinklabs-io/cardano-node-api/internal/logging" 25 | "github.com/gin-gonic/gin" 26 | "github.com/penglongli/gin-metrics/ginmetrics" 27 | swaggerFiles "github.com/swaggo/files" // swagger embed files 28 | ginSwagger "github.com/swaggo/gin-swagger" // gin-swagger middleware 29 | ) 30 | 31 | // @title cardano-node-api 32 | // @version 1.0 33 | // @description Cardano Node API 34 | // @BasePath /api 35 | // @contact.name Blink Labs 36 | // @contact.url https://blinklabs.io 37 | // @contact.email support@blinklabs.io 38 | // 39 | // @license.name Apache 2.0 40 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 41 | func Start(cfg *config.Config) error { 42 | // Standard logging 43 | logger := logging.GetLogger() 44 | if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" { 45 | logger.Info(fmt.Sprintf( 46 | "starting API TLS listener on %s:%d", 47 | cfg.Api.ListenAddress, 48 | cfg.Api.ListenPort, 49 | )) 50 | } else { 51 | logger.Info(fmt.Sprintf( 52 | "starting API listener on %s:%d", 53 | cfg.Api.ListenAddress, 54 | cfg.Api.ListenPort, 55 | )) 56 | } 57 | // Disable gin debug and color output 58 | gin.SetMode(gin.ReleaseMode) 59 | gin.DisableConsoleColor() 60 | 61 | // Configure API router 62 | router := gin.New() 63 | // Catch panics and return a 500 64 | router.Use(gin.Recovery()) 65 | // Access logging 66 | accessLogger := logging.GetAccessLogger() 67 | skipPaths := []string{} 68 | if cfg.Logging.Healthchecks { 69 | skipPaths = append(skipPaths, "/healthcheck") 70 | logger.Info("disabling access logs for /healthcheck") 71 | } 72 | accessMiddleware := func(c *gin.Context) { 73 | // Skip handling/logging for specified paths 74 | if slices.Contains(skipPaths, c.Request.URL.Path) { 75 | return 76 | } 77 | accessLogger.Info( 78 | "request received", 79 | "method", 80 | c.Request.Method, 81 | "path", 82 | c.Request.URL.Path, 83 | "remote_addr", 84 | c.ClientIP(), 85 | ) 86 | c.Next() 87 | statusCode := c.Writer.Status() 88 | accessLogger.Info( 89 | "response sent", 90 | "status", 91 | statusCode, 92 | "method", 93 | c.Request.Method, 94 | "path", 95 | c.Request.URL.Path, 96 | "remote_addr", 97 | c.ClientIP(), 98 | ) 99 | } 100 | router.Use(accessMiddleware) 101 | 102 | // Create a healthcheck 103 | router.GET("/healthcheck", handleHealthcheck) 104 | // Create a swagger endpoint 105 | router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 106 | 107 | // Configure API routes 108 | apiGroup := router.Group("/api") 109 | configureChainSyncRoutes(apiGroup) 110 | configureLocalStateQueryRoutes(apiGroup) 111 | configureLocalTxMonitorRoutes(apiGroup) 112 | configureLocalTxSubmissionRoutes(apiGroup) 113 | 114 | // Metrics 115 | metricsRouter := gin.New() 116 | metrics := ginmetrics.GetMonitor() 117 | // Set metrics path 118 | metrics.SetMetricPath("/") 119 | // Set metrics router 120 | metrics.Expose(metricsRouter) 121 | // Use metrics middleware without exposing path in main app router 122 | // We only collect metrics on the API endpoints 123 | metrics.UseWithoutExposingEndpoint(apiGroup) 124 | 125 | // Start metrics listener 126 | go func() { 127 | // TODO: return error if we cannot initialize metrics 128 | logger.Info(fmt.Sprintf("starting metrics listener on %s:%d", 129 | cfg.Metrics.ListenAddress, 130 | cfg.Metrics.ListenPort)) 131 | err := metricsRouter.Run(fmt.Sprintf("%s:%d", 132 | cfg.Metrics.ListenAddress, 133 | cfg.Metrics.ListenPort)) 134 | if err != nil { 135 | logger.Error("failed to start metrics listener:", "error", err) 136 | os.Exit(1) 137 | } 138 | }() 139 | 140 | // Start API listener 141 | if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" { 142 | err := router.RunTLS( 143 | fmt.Sprintf("%s:%d", cfg.Api.ListenAddress, cfg.Api.ListenPort), 144 | cfg.Tls.CertFilePath, 145 | cfg.Tls.KeyFilePath, 146 | ) 147 | return err 148 | } else { 149 | err := router.Run(fmt.Sprintf("%s:%d", 150 | cfg.Api.ListenAddress, 151 | cfg.Api.ListenPort)) 152 | return err 153 | } 154 | } 155 | 156 | type responseApiError struct { 157 | Msg string `json:"msg" example:"error message"` 158 | } 159 | 160 | func apiError(msg string) responseApiError { 161 | return responseApiError{ 162 | Msg: msg, 163 | } 164 | } 165 | 166 | func handleHealthcheck(c *gin.Context) { 167 | // TODO: add some actual health checking here 168 | c.JSON(200, gin.H{"failed": false}) 169 | } 170 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_state_query_current_era.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalStateQueryCurrentEra type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalStateQueryCurrentEra{} 20 | 21 | // ApiResponseLocalStateQueryCurrentEra struct for ApiResponseLocalStateQueryCurrentEra 22 | type ApiResponseLocalStateQueryCurrentEra struct { 23 | Id *int32 `json:"id,omitempty"` 24 | Name *string `json:"name,omitempty"` 25 | } 26 | 27 | // NewApiResponseLocalStateQueryCurrentEra instantiates a new ApiResponseLocalStateQueryCurrentEra object 28 | // This constructor will assign default values to properties that have it defined, 29 | // and makes sure properties required by API are set, but the set of arguments 30 | // will change when the set of required properties is changed 31 | func NewApiResponseLocalStateQueryCurrentEra() *ApiResponseLocalStateQueryCurrentEra { 32 | this := ApiResponseLocalStateQueryCurrentEra{} 33 | return &this 34 | } 35 | 36 | // NewApiResponseLocalStateQueryCurrentEraWithDefaults instantiates a new ApiResponseLocalStateQueryCurrentEra object 37 | // This constructor will only assign default values to properties that have it defined, 38 | // but it doesn't guarantee that properties required by API are set 39 | func NewApiResponseLocalStateQueryCurrentEraWithDefaults() *ApiResponseLocalStateQueryCurrentEra { 40 | this := ApiResponseLocalStateQueryCurrentEra{} 41 | return &this 42 | } 43 | 44 | // GetId returns the Id field value if set, zero value otherwise. 45 | func (o *ApiResponseLocalStateQueryCurrentEra) GetId() int32 { 46 | if o == nil || IsNil(o.Id) { 47 | var ret int32 48 | return ret 49 | } 50 | return *o.Id 51 | } 52 | 53 | // GetIdOk returns a tuple with the Id field value if set, nil otherwise 54 | // and a boolean to check if the value has been set. 55 | func (o *ApiResponseLocalStateQueryCurrentEra) GetIdOk() (*int32, bool) { 56 | if o == nil || IsNil(o.Id) { 57 | return nil, false 58 | } 59 | return o.Id, true 60 | } 61 | 62 | // HasId returns a boolean if a field has been set. 63 | func (o *ApiResponseLocalStateQueryCurrentEra) HasId() bool { 64 | if o != nil && !IsNil(o.Id) { 65 | return true 66 | } 67 | 68 | return false 69 | } 70 | 71 | // SetId gets a reference to the given int32 and assigns it to the Id field. 72 | func (o *ApiResponseLocalStateQueryCurrentEra) SetId(v int32) { 73 | o.Id = &v 74 | } 75 | 76 | // GetName returns the Name field value if set, zero value otherwise. 77 | func (o *ApiResponseLocalStateQueryCurrentEra) GetName() string { 78 | if o == nil || IsNil(o.Name) { 79 | var ret string 80 | return ret 81 | } 82 | return *o.Name 83 | } 84 | 85 | // GetNameOk returns a tuple with the Name field value if set, nil otherwise 86 | // and a boolean to check if the value has been set. 87 | func (o *ApiResponseLocalStateQueryCurrentEra) GetNameOk() (*string, bool) { 88 | if o == nil || IsNil(o.Name) { 89 | return nil, false 90 | } 91 | return o.Name, true 92 | } 93 | 94 | // HasName returns a boolean if a field has been set. 95 | func (o *ApiResponseLocalStateQueryCurrentEra) HasName() bool { 96 | if o != nil && !IsNil(o.Name) { 97 | return true 98 | } 99 | 100 | return false 101 | } 102 | 103 | // SetName gets a reference to the given string and assigns it to the Name field. 104 | func (o *ApiResponseLocalStateQueryCurrentEra) SetName(v string) { 105 | o.Name = &v 106 | } 107 | 108 | func (o ApiResponseLocalStateQueryCurrentEra) MarshalJSON() ([]byte, error) { 109 | toSerialize, err := o.ToMap() 110 | if err != nil { 111 | return []byte{}, err 112 | } 113 | return json.Marshal(toSerialize) 114 | } 115 | 116 | func (o ApiResponseLocalStateQueryCurrentEra) ToMap() (map[string]interface{}, error) { 117 | toSerialize := map[string]interface{}{} 118 | if !IsNil(o.Id) { 119 | toSerialize["id"] = o.Id 120 | } 121 | if !IsNil(o.Name) { 122 | toSerialize["name"] = o.Name 123 | } 124 | return toSerialize, nil 125 | } 126 | 127 | type NullableApiResponseLocalStateQueryCurrentEra struct { 128 | value *ApiResponseLocalStateQueryCurrentEra 129 | isSet bool 130 | } 131 | 132 | func (v NullableApiResponseLocalStateQueryCurrentEra) Get() *ApiResponseLocalStateQueryCurrentEra { 133 | return v.value 134 | } 135 | 136 | func (v *NullableApiResponseLocalStateQueryCurrentEra) Set( 137 | val *ApiResponseLocalStateQueryCurrentEra, 138 | ) { 139 | v.value = val 140 | v.isSet = true 141 | } 142 | 143 | func (v NullableApiResponseLocalStateQueryCurrentEra) IsSet() bool { 144 | return v.isSet 145 | } 146 | 147 | func (v *NullableApiResponseLocalStateQueryCurrentEra) Unset() { 148 | v.value = nil 149 | v.isSet = false 150 | } 151 | 152 | func NewNullableApiResponseLocalStateQueryCurrentEra( 153 | val *ApiResponseLocalStateQueryCurrentEra, 154 | ) *NullableApiResponseLocalStateQueryCurrentEra { 155 | return &NullableApiResponseLocalStateQueryCurrentEra{ 156 | value: val, 157 | isSet: true, 158 | } 159 | } 160 | 161 | func (v NullableApiResponseLocalStateQueryCurrentEra) MarshalJSON() ([]byte, error) { 162 | return json.Marshal(v.value) 163 | } 164 | 165 | func (v *NullableApiResponseLocalStateQueryCurrentEra) UnmarshalJSON( 166 | src []byte, 167 | ) error { 168 | v.isSet = true 169 | return json.Unmarshal(src, &v.value) 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cardano-node-api 2 | 3 | Cardano Node API 4 | 5 | An HTTP API for interfacing with a local Cardano Node and providing the node 6 | internal data for HTTP clients. This service communicates with a Cardano 7 | full node using the Ouroboros network protocol via a UNIX socket and exposes 8 | the underlying Node-to-Client (NtC) Ouroboros mini-protocols to clients via 9 | a REST API or UTxO RPC gRPC API. 10 | 11 | ## Usage 12 | 13 | The recommended method of using this application is via the published 14 | container images, coupled with Blink Labs container images for the Cardano 15 | Node. 16 | 17 | ``` 18 | docker run -d \ 19 | -p 8080:8080 \ 20 | -p 9090:9090 \ 21 | -v \ 22 | ghcr.io/blinklabs-io/cardano-node-api:main 23 | ``` 24 | 25 | 32 | ### Configuration 33 | 34 | Configuration can be done using either a `config.yaml` file or setting 35 | environment variables. Our recommendation is environment variables to adhere 36 | to the 12-factor application philisophy. 37 | 38 | #### Environment variables 39 | 40 | Configuration via environment variables can be broken into two sets of 41 | variables. The first set controls the behavior of the application, while the 42 | second set controls the connection to the Cardano node instance. 43 | 44 | Application configuration: 45 | - `API_LISTEN_ADDRESS` - Address to bind for API calls, all addresses if empty 46 | (default: empty) 47 | - `API_LISTEN_PORT` - Port to bind for API calls (default: 8080) 48 | - `DEBUG_ADDRESS` - Address to bind for pprof debugging (default: localhost) 49 | - `DEBUG_PORT` - Port to bind for pprof debugging, disabled if 0 (default: 0) 50 | - `GRPC_LISTEN_ADDRESS` - Address to bind for UTxO RPC gRPC, all addresses if empty 51 | (default: empty) 52 | - `GRPC_LISTEN_PORT` - Port to bind for gRPC calls (default: 9090) 53 | - `LOGGING_HEALTHCHECKS` - Log requests to `/healthcheck` endpoint (default: false) 54 | - `LOGGING_LEVEL` - Logging level for log output (default: info) 55 | - `METRICS_LISTEN_ADDRESS` - Address to bind for Prometheus format metrics, all 56 | addresses if empty (default: empty) 57 | - `METRICS_LISTEN_PORT` - Port to bind for metrics (default: 8081) 58 | - `TLS_CERT_FILE_PATH` - SSL certificate to use, requires `TLS_KEY_FILE_PATH` 59 | (default: empty) 60 | - `TLS_KEY_FILE_PATH` - SSL certificate key to use (default: empty) 61 | 62 | Configuring the TLS certificate and key paths will enable TLS on both the REST 63 | API and the gRPC interface. 64 | 65 | Connection to the Cardano node can be performed using specific named network 66 | shortcuts for known network magic configurations. Supported named networks are: 67 | 68 | - mainnet 69 | - preprod 70 | - preview 71 | - sanchonet 72 | 73 | You can set the network to an empty value and provide your own network magic to 74 | connect to unlisted networks. 75 | 76 | TCP connection to a Cardano Node without using an intermediary like SOCAT is 77 | possible using the node address and port. It is up to you to expose the node's 78 | NtC communication socket over TCP. TCP connections are preferred over socket 79 | within the application. 80 | 81 | Cardano node configuration: 82 | - `CARDANO_NETWORK` - Use a named Cardano network (default: mainnet) 83 | - `CARDANO_NODE_NETWORK_MAGIC` - Cardano network magic (default: automatically 84 | determined from named network) 85 | - `CARDANO_NODE_SKIP_CHECK` - Skip the connection test to Cardano Node on start 86 | (default: false) 87 | - `CARDANO_NODE_SOCKET_PATH` - Socket path to Cardano node NtC via UNIX socket 88 | (default: /node-ipc/node.socket) 89 | - `CARDANO_NODE_SOCKET_TCP_HOST` - Address to Cardano node NtC via TCP 90 | (default: unset) 91 | - `CARDANO_NODE_SOCKET_TCP_PORT` - Port to Cardano node NtC via TCP (default: 92 | unset) 93 | - `CARDANO_NODE_SOCKET_TIMEOUT` - Sets a timeout in seconds for waiting on 94 | requests to the Cardano node (default: 30) 95 | 96 | ### Connecting to a cardano-node 97 | 98 | You can connect to either a cardano-node running locally on the host or a 99 | container running either `inputoutput/cardano-node` or 100 | `ghcr.io/blinklabs-io/cardano-node` by mapping in the correct paths and setting 101 | the environment variables or configuration options to match. 102 | 103 | #### Together with ghcr.io/blinklabs-io/cardano-node in Docker 104 | 105 | Use Docker to run both cardano-node and cardano-node-api with Docker 106 | volumes for blockchain storage and node-ipc. 107 | 108 | ``` 109 | # Start mainnet node 110 | docker run --detach \ 111 | --name cardano-node \ 112 | -v node-data:/opt/cardano/data \ 113 | -v node-ipc:/opt/cardano/ipc \ 114 | -p 3001:3001 \ 115 | ghcr.io/blinklabs-io/cardano-node run 116 | 117 | # Start cardano-node-api 118 | docker run --detach \ 119 | --name cardano-node-api \ 120 | -v node-ipc:/node-ipc \ 121 | -p 8080:8080 \ 122 | -p 9090:9090 \ 123 | ghcr.io/blinklabs-io/cardano-node-api:main 124 | ``` 125 | 126 | #### Using a local cardano-node 127 | 128 | Use the local path when mapping the node-ipc volume into the container to use 129 | a local cardano-node. 130 | 131 | ``` 132 | # Start cardano-node-api 133 | docker run --detach \ 134 | --name cardano-node-api \ 135 | -v /opt/cardano/ipc:/node-ipc \ 136 | -p 8080:8080 \ 137 | -p 9090:9090 \ 138 | ghcr.io/blinklabs-io/cardano-node-api:main 139 | ``` 140 | 141 | ## Development 142 | 143 | There is a Makefile to provide some simple helpers. 144 | 145 | Run from checkout: 146 | ``` 147 | go run . 148 | ``` 149 | 150 | Create a binary: 151 | ``` 152 | make 153 | ``` 154 | 155 | Create a docker image: 156 | ``` 157 | make image 158 | ``` 159 | -------------------------------------------------------------------------------- /openapi/README.md: -------------------------------------------------------------------------------- 1 | # Go API client for openapi 2 | 3 | Cardano Node API 4 | 5 | ## Overview 6 | This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. 7 | 8 | - API version: 1.0 9 | - Package version: 1.0.0 10 | - Generator version: 7.14.0 11 | - Build package: org.openapitools.codegen.languages.GoClientCodegen 12 | For more information, please visit [https://blinklabs.io](https://blinklabs.io) 13 | 14 | ## Installation 15 | 16 | Install the following dependencies: 17 | 18 | ```sh 19 | go get github.com/stretchr/testify/assert 20 | go get golang.org/x/net/context 21 | ``` 22 | 23 | Put the package under your project folder and add the following in import: 24 | 25 | ```go 26 | import openapi "github.com/blinklabs-io/cardano-node-api/openapi" 27 | ``` 28 | 29 | To use a proxy, set the environment variable `HTTP_PROXY`: 30 | 31 | ```go 32 | os.Setenv("HTTP_PROXY", "http://proxy_name:proxy_port") 33 | ``` 34 | 35 | ## Configuration of Server URL 36 | 37 | Default configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification. 38 | 39 | ### Select Server Configuration 40 | 41 | For using other server than the one defined on index 0 set context value `openapi.ContextServerIndex` of type `int`. 42 | 43 | ```go 44 | ctx := context.WithValue(context.Background(), openapi.ContextServerIndex, 1) 45 | ``` 46 | 47 | ### Templated Server URL 48 | 49 | Templated server URL is formatted using default variables from configuration or from context value `openapi.ContextServerVariables` of type `map[string]string`. 50 | 51 | ```go 52 | ctx := context.WithValue(context.Background(), openapi.ContextServerVariables, map[string]string{ 53 | "basePath": "v2", 54 | }) 55 | ``` 56 | 57 | Note, enum values are always validated and all unused variables are silently ignored. 58 | 59 | ### URLs Configuration per Operation 60 | 61 | Each operation can use different server URL defined using `OperationServers` map in the `Configuration`. 62 | An operation is uniquely identified by `"{classname}Service.{nickname}"` string. 63 | Similar rules for overriding default operation server index and variables applies by using `openapi.ContextOperationServerIndices` and `openapi.ContextOperationServerVariables` context maps. 64 | 65 | ```go 66 | ctx := context.WithValue(context.Background(), openapi.ContextOperationServerIndices, map[string]int{ 67 | "{classname}Service.{nickname}": 2, 68 | }) 69 | ctx = context.WithValue(context.Background(), openapi.ContextOperationServerVariables, map[string]map[string]string{ 70 | "{classname}Service.{nickname}": { 71 | "port": "8443", 72 | }, 73 | }) 74 | ``` 75 | 76 | ## Documentation for API Endpoints 77 | 78 | All URIs are relative to */api* 79 | 80 | Class | Method | HTTP request | Description 81 | ------------ | ------------- | ------------- | ------------- 82 | *ChainsyncAPI* | [**ChainsyncSyncGet**](docs/ChainsyncAPI.md#chainsyncsyncget) | **Get** /chainsync/sync | Start a chain-sync using a websocket for events 83 | *LocalstatequeryAPI* | [**LocalstatequeryCurrentEraGet**](docs/LocalstatequeryAPI.md#localstatequerycurrenteraget) | **Get** /localstatequery/current-era | Query Current Era 84 | *LocalstatequeryAPI* | [**LocalstatequeryEraHistoryGet**](docs/LocalstatequeryAPI.md#localstatequeryerahistoryget) | **Get** /localstatequery/era-history | Query Era History 85 | *LocalstatequeryAPI* | [**LocalstatequeryGenesisConfigGet**](docs/LocalstatequeryAPI.md#localstatequerygenesisconfigget) | **Get** /localstatequery/genesis-config | Query Genesis Config 86 | *LocalstatequeryAPI* | [**LocalstatequeryProtocolParamsGet**](docs/LocalstatequeryAPI.md#localstatequeryprotocolparamsget) | **Get** /localstatequery/protocol-params | Query Current Protocol Parameters 87 | *LocalstatequeryAPI* | [**LocalstatequerySystemStartGet**](docs/LocalstatequeryAPI.md#localstatequerysystemstartget) | **Get** /localstatequery/system-start | Query System Start 88 | *LocalstatequeryAPI* | [**LocalstatequeryTipGet**](docs/LocalstatequeryAPI.md#localstatequerytipget) | **Get** /localstatequery/tip | Query Chain Tip 89 | *LocaltxmonitorAPI* | [**LocaltxmonitorHasTxTxHashGet**](docs/LocaltxmonitorAPI.md#localtxmonitorhastxtxhashget) | **Get** /localtxmonitor/has_tx/{tx_hash} | Check if a particular TX exists in the mempool 90 | *LocaltxmonitorAPI* | [**LocaltxmonitorSizesGet**](docs/LocaltxmonitorAPI.md#localtxmonitorsizesget) | **Get** /localtxmonitor/sizes | Get mempool capacity, size, and TX count 91 | *LocaltxmonitorAPI* | [**LocaltxmonitorTxsGet**](docs/LocaltxmonitorAPI.md#localtxmonitortxsget) | **Get** /localtxmonitor/txs | List all transactions in the mempool 92 | *LocaltxsubmissionAPI* | [**LocaltxsubmissionTxPost**](docs/LocaltxsubmissionAPI.md#localtxsubmissiontxpost) | **Post** /localtxsubmission/tx | Submit Tx 93 | 94 | 95 | ## Documentation For Models 96 | 97 | - [ApiResponseApiError](docs/ApiResponseApiError.md) 98 | - [ApiResponseLocalStateQueryCurrentEra](docs/ApiResponseLocalStateQueryCurrentEra.md) 99 | - [ApiResponseLocalStateQuerySystemStart](docs/ApiResponseLocalStateQuerySystemStart.md) 100 | - [ApiResponseLocalStateQueryTip](docs/ApiResponseLocalStateQueryTip.md) 101 | - [ApiResponseLocalTxMonitorHasTx](docs/ApiResponseLocalTxMonitorHasTx.md) 102 | - [ApiResponseLocalTxMonitorSizes](docs/ApiResponseLocalTxMonitorSizes.md) 103 | - [ApiResponseLocalTxMonitorTxs](docs/ApiResponseLocalTxMonitorTxs.md) 104 | 105 | 106 | ## Documentation For Authorization 107 | 108 | Endpoints do not require authorization. 109 | 110 | 111 | ## Documentation for Utility Methods 112 | 113 | Due to the fact that model structure members are all pointers, this package contains 114 | a number of utility functions to easily obtain pointers to values of basic types. 115 | Each of these functions takes a value of the given basic type and returns a pointer to it: 116 | 117 | * `PtrBool` 118 | * `PtrInt` 119 | * `PtrInt32` 120 | * `PtrInt64` 121 | * `PtrFloat` 122 | * `PtrFloat32` 123 | * `PtrFloat64` 124 | * `PtrString` 125 | * `PtrTime` 126 | 127 | ## Author 128 | 129 | support@blinklabs.io 130 | 131 | -------------------------------------------------------------------------------- /openapi/api_chainsync.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "bytes" 16 | "context" 17 | "io" 18 | "net/http" 19 | "net/url" 20 | ) 21 | 22 | type ChainsyncAPI interface { 23 | 24 | /* 25 | ChainsyncSyncGet Start a chain-sync using a websocket for events 26 | 27 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 28 | @return ChainsyncAPIChainsyncSyncGetRequest 29 | */ 30 | ChainsyncSyncGet(ctx context.Context) ChainsyncAPIChainsyncSyncGetRequest 31 | 32 | // ChainsyncSyncGetExecute executes the request 33 | ChainsyncSyncGetExecute( 34 | r ChainsyncAPIChainsyncSyncGetRequest, 35 | ) (*http.Response, error) 36 | } 37 | 38 | // ChainsyncAPIService ChainsyncAPI service 39 | type ChainsyncAPIService service 40 | 41 | type ChainsyncAPIChainsyncSyncGetRequest struct { 42 | ctx context.Context 43 | ApiService ChainsyncAPI 44 | tip *bool 45 | slot *int32 46 | hash *string 47 | } 48 | 49 | // whether to start from the current tip 50 | func (r ChainsyncAPIChainsyncSyncGetRequest) Tip( 51 | tip bool, 52 | ) ChainsyncAPIChainsyncSyncGetRequest { 53 | r.tip = &tip 54 | return r 55 | } 56 | 57 | // slot to start sync at, should match hash 58 | func (r ChainsyncAPIChainsyncSyncGetRequest) Slot( 59 | slot int32, 60 | ) ChainsyncAPIChainsyncSyncGetRequest { 61 | r.slot = &slot 62 | return r 63 | } 64 | 65 | // block hash to start sync at, should match slot 66 | func (r ChainsyncAPIChainsyncSyncGetRequest) Hash( 67 | hash string, 68 | ) ChainsyncAPIChainsyncSyncGetRequest { 69 | r.hash = &hash 70 | return r 71 | } 72 | 73 | func (r ChainsyncAPIChainsyncSyncGetRequest) Execute() (*http.Response, error) { 74 | return r.ApiService.ChainsyncSyncGetExecute(r) 75 | } 76 | 77 | /* 78 | ChainsyncSyncGet Start a chain-sync using a websocket for events 79 | 80 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 81 | @return ChainsyncAPIChainsyncSyncGetRequest 82 | */ 83 | func (a *ChainsyncAPIService) ChainsyncSyncGet( 84 | ctx context.Context, 85 | ) ChainsyncAPIChainsyncSyncGetRequest { 86 | return ChainsyncAPIChainsyncSyncGetRequest{ 87 | ApiService: a, 88 | ctx: ctx, 89 | } 90 | } 91 | 92 | // Execute executes the request 93 | func (a *ChainsyncAPIService) ChainsyncSyncGetExecute( 94 | r ChainsyncAPIChainsyncSyncGetRequest, 95 | ) (*http.Response, error) { 96 | var ( 97 | localVarHTTPMethod = http.MethodGet 98 | localVarPostBody interface{} 99 | formFiles []formFile 100 | ) 101 | 102 | localBasePath, err := a.client.cfg.ServerURLWithContext( 103 | r.ctx, 104 | "ChainsyncAPIService.ChainsyncSyncGet", 105 | ) 106 | if err != nil { 107 | return nil, &GenericOpenAPIError{error: err.Error()} 108 | } 109 | 110 | localVarPath := localBasePath + "/chainsync/sync" 111 | 112 | localVarHeaderParams := make(map[string]string) 113 | localVarQueryParams := url.Values{} 114 | localVarFormParams := url.Values{} 115 | 116 | if r.tip != nil { 117 | parameterAddToHeaderOrQuery(localVarQueryParams, "tip", r.tip, "", "") 118 | } 119 | if r.slot != nil { 120 | parameterAddToHeaderOrQuery(localVarQueryParams, "slot", r.slot, "", "") 121 | } 122 | if r.hash != nil { 123 | parameterAddToHeaderOrQuery(localVarQueryParams, "hash", r.hash, "", "") 124 | } 125 | // to determine the Content-Type header 126 | localVarHTTPContentTypes := []string{} 127 | 128 | // set Content-Type header 129 | localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) 130 | if localVarHTTPContentType != "" { 131 | localVarHeaderParams["Content-Type"] = localVarHTTPContentType 132 | } 133 | 134 | // to determine the Accept header 135 | localVarHTTPHeaderAccepts := []string{"*/*"} 136 | 137 | // set Accept header 138 | localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) 139 | if localVarHTTPHeaderAccept != "" { 140 | localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept 141 | } 142 | req, err := a.client.prepareRequest( 143 | r.ctx, 144 | localVarPath, 145 | localVarHTTPMethod, 146 | localVarPostBody, 147 | localVarHeaderParams, 148 | localVarQueryParams, 149 | localVarFormParams, 150 | formFiles, 151 | ) 152 | if err != nil { 153 | return nil, err 154 | } 155 | 156 | localVarHTTPResponse, err := a.client.callAPI(req) 157 | if err != nil || localVarHTTPResponse == nil { 158 | return localVarHTTPResponse, err 159 | } 160 | 161 | localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) 162 | localVarHTTPResponse.Body.Close() 163 | localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) 164 | if err != nil { 165 | return localVarHTTPResponse, err 166 | } 167 | 168 | if localVarHTTPResponse.StatusCode >= 300 { 169 | newErr := &GenericOpenAPIError{ 170 | body: localVarBody, 171 | error: localVarHTTPResponse.Status, 172 | } 173 | if localVarHTTPResponse.StatusCode == 400 { 174 | var v ApiResponseApiError 175 | err = a.client.decode( 176 | &v, 177 | localVarBody, 178 | localVarHTTPResponse.Header.Get("Content-Type"), 179 | ) 180 | if err != nil { 181 | newErr.error = err.Error() 182 | return localVarHTTPResponse, newErr 183 | } 184 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 185 | newErr.model = v 186 | return localVarHTTPResponse, newErr 187 | } 188 | if localVarHTTPResponse.StatusCode == 500 { 189 | var v ApiResponseApiError 190 | err = a.client.decode( 191 | &v, 192 | localVarBody, 193 | localVarHTTPResponse.Header.Get("Content-Type"), 194 | ) 195 | if err != nil { 196 | newErr.error = err.Error() 197 | return localVarHTTPResponse, newErr 198 | } 199 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 200 | newErr.model = v 201 | } 202 | return localVarHTTPResponse, newErr 203 | } 204 | 205 | return localVarHTTPResponse, nil 206 | } 207 | -------------------------------------------------------------------------------- /openapi/docs/LocaltxmonitorAPI.md: -------------------------------------------------------------------------------- 1 | # \LocaltxmonitorAPI 2 | 3 | All URIs are relative to */api* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**LocaltxmonitorHasTxTxHashGet**](LocaltxmonitorAPI.md#LocaltxmonitorHasTxTxHashGet) | **Get** /localtxmonitor/has_tx/{tx_hash} | Check if a particular TX exists in the mempool 8 | [**LocaltxmonitorSizesGet**](LocaltxmonitorAPI.md#LocaltxmonitorSizesGet) | **Get** /localtxmonitor/sizes | Get mempool capacity, size, and TX count 9 | [**LocaltxmonitorTxsGet**](LocaltxmonitorAPI.md#LocaltxmonitorTxsGet) | **Get** /localtxmonitor/txs | List all transactions in the mempool 10 | 11 | 12 | 13 | ## LocaltxmonitorHasTxTxHashGet 14 | 15 | > ApiResponseLocalTxMonitorHasTx LocaltxmonitorHasTxTxHashGet(ctx, txHash).Execute() 16 | 17 | Check if a particular TX exists in the mempool 18 | 19 | ### Example 20 | 21 | ```go 22 | package main 23 | 24 | import ( 25 | "context" 26 | "fmt" 27 | "os" 28 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 29 | ) 30 | 31 | func main() { 32 | txHash := "txHash_example" // string | Transaction hash (hex string) 33 | 34 | configuration := openapiclient.NewConfiguration() 35 | apiClient := openapiclient.NewAPIClient(configuration) 36 | resp, r, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorHasTxTxHashGet(context.Background(), txHash).Execute() 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Error when calling `LocaltxmonitorAPI.LocaltxmonitorHasTxTxHashGet``: %v\n", err) 39 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 40 | } 41 | // response from `LocaltxmonitorHasTxTxHashGet`: ApiResponseLocalTxMonitorHasTx 42 | fmt.Fprintf(os.Stdout, "Response from `LocaltxmonitorAPI.LocaltxmonitorHasTxTxHashGet`: %v\n", resp) 43 | } 44 | ``` 45 | 46 | ### Path Parameters 47 | 48 | 49 | Name | Type | Description | Notes 50 | ------------- | ------------- | ------------- | ------------- 51 | **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. 52 | **txHash** | **string** | Transaction hash (hex string) | 53 | 54 | ### Other Parameters 55 | 56 | Other parameters are passed through a pointer to a apiLocaltxmonitorHasTxTxHashGetRequest struct via the builder pattern 57 | 58 | 59 | Name | Type | Description | Notes 60 | ------------- | ------------- | ------------- | ------------- 61 | 62 | 63 | ### Return type 64 | 65 | [**ApiResponseLocalTxMonitorHasTx**](ApiResponseLocalTxMonitorHasTx.md) 66 | 67 | ### Authorization 68 | 69 | No authorization required 70 | 71 | ### HTTP request headers 72 | 73 | - **Content-Type**: Not defined 74 | - **Accept**: application/json 75 | 76 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 77 | [[Back to Model list]](../README.md#documentation-for-models) 78 | [[Back to README]](../README.md) 79 | 80 | 81 | ## LocaltxmonitorSizesGet 82 | 83 | > ApiResponseLocalTxMonitorSizes LocaltxmonitorSizesGet(ctx).Execute() 84 | 85 | Get mempool capacity, size, and TX count 86 | 87 | ### Example 88 | 89 | ```go 90 | package main 91 | 92 | import ( 93 | "context" 94 | "fmt" 95 | "os" 96 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 97 | ) 98 | 99 | func main() { 100 | 101 | configuration := openapiclient.NewConfiguration() 102 | apiClient := openapiclient.NewAPIClient(configuration) 103 | resp, r, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorSizesGet(context.Background()).Execute() 104 | if err != nil { 105 | fmt.Fprintf(os.Stderr, "Error when calling `LocaltxmonitorAPI.LocaltxmonitorSizesGet``: %v\n", err) 106 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 107 | } 108 | // response from `LocaltxmonitorSizesGet`: ApiResponseLocalTxMonitorSizes 109 | fmt.Fprintf(os.Stdout, "Response from `LocaltxmonitorAPI.LocaltxmonitorSizesGet`: %v\n", resp) 110 | } 111 | ``` 112 | 113 | ### Path Parameters 114 | 115 | This endpoint does not need any parameter. 116 | 117 | ### Other Parameters 118 | 119 | Other parameters are passed through a pointer to a apiLocaltxmonitorSizesGetRequest struct via the builder pattern 120 | 121 | 122 | ### Return type 123 | 124 | [**ApiResponseLocalTxMonitorSizes**](ApiResponseLocalTxMonitorSizes.md) 125 | 126 | ### Authorization 127 | 128 | No authorization required 129 | 130 | ### HTTP request headers 131 | 132 | - **Content-Type**: Not defined 133 | - **Accept**: application/json 134 | 135 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 136 | [[Back to Model list]](../README.md#documentation-for-models) 137 | [[Back to README]](../README.md) 138 | 139 | 140 | ## LocaltxmonitorTxsGet 141 | 142 | > []ApiResponseLocalTxMonitorTxs LocaltxmonitorTxsGet(ctx).Execute() 143 | 144 | List all transactions in the mempool 145 | 146 | ### Example 147 | 148 | ```go 149 | package main 150 | 151 | import ( 152 | "context" 153 | "fmt" 154 | "os" 155 | openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" 156 | ) 157 | 158 | func main() { 159 | 160 | configuration := openapiclient.NewConfiguration() 161 | apiClient := openapiclient.NewAPIClient(configuration) 162 | resp, r, err := apiClient.LocaltxmonitorAPI.LocaltxmonitorTxsGet(context.Background()).Execute() 163 | if err != nil { 164 | fmt.Fprintf(os.Stderr, "Error when calling `LocaltxmonitorAPI.LocaltxmonitorTxsGet``: %v\n", err) 165 | fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) 166 | } 167 | // response from `LocaltxmonitorTxsGet`: []ApiResponseLocalTxMonitorTxs 168 | fmt.Fprintf(os.Stdout, "Response from `LocaltxmonitorAPI.LocaltxmonitorTxsGet`: %v\n", resp) 169 | } 170 | ``` 171 | 172 | ### Path Parameters 173 | 174 | This endpoint does not need any parameter. 175 | 176 | ### Other Parameters 177 | 178 | Other parameters are passed through a pointer to a apiLocaltxmonitorTxsGetRequest struct via the builder pattern 179 | 180 | 181 | ### Return type 182 | 183 | [**[]ApiResponseLocalTxMonitorTxs**](ApiResponseLocalTxMonitorTxs.md) 184 | 185 | ### Authorization 186 | 187 | No authorization required 188 | 189 | ### HTTP request headers 190 | 191 | - **Content-Type**: Not defined 192 | - **Accept**: application/json 193 | 194 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) 195 | [[Back to Model list]](../README.md#documentation-for-models) 196 | [[Back to README]](../README.md) 197 | 198 | -------------------------------------------------------------------------------- /internal/api/localtxmonitor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package api 16 | 17 | import ( 18 | "encoding/hex" 19 | "net/http" 20 | 21 | "github.com/blinklabs-io/cardano-node-api/internal/node" 22 | "github.com/blinklabs-io/gouroboros/ledger" 23 | "github.com/gin-gonic/gin" 24 | ) 25 | 26 | func configureLocalTxMonitorRoutes(apiGroup *gin.RouterGroup) { 27 | group := apiGroup.Group("/localtxmonitor") 28 | group.GET("/sizes", handleLocalTxMonitorSizes) 29 | group.GET("/has_tx/:tx_hash", handleLocalTxMonitorHasTx) 30 | group.GET("/txs", handleLocalTxMonitorTxs) 31 | } 32 | 33 | type responseLocalTxMonitorSizes struct { 34 | Capacity uint32 `json:"capacity"` 35 | Size uint32 `json:"size"` 36 | TxCount uint32 `json:"tx_count"` 37 | } 38 | 39 | // handleLocalTxMonitorSizes godoc 40 | // 41 | // @Summary Get mempool capacity, size, and TX count 42 | // @Tags localtxmonitor 43 | // @Accept json 44 | // @Produce json 45 | // @Success 200 {object} responseLocalTxMonitorSizes 46 | // @Failure 500 {object} responseApiError 47 | // @Router /localtxmonitor/sizes [get] 48 | func handleLocalTxMonitorSizes(c *gin.Context) { 49 | // Connect to node 50 | oConn, err := node.GetConnection(nil) 51 | if err != nil { 52 | c.JSON(500, apiError(err.Error())) 53 | return 54 | } 55 | // Async error handler 56 | go func() { 57 | err, ok := <-oConn.ErrorChan() 58 | if !ok { 59 | return 60 | } 61 | c.JSON(500, apiError(err.Error())) 62 | }() 63 | defer func() { 64 | // Close Ouroboros connection 65 | oConn.Close() 66 | }() 67 | // Start client 68 | oConn.LocalTxMonitor().Client.Start() 69 | // Get sizes 70 | capacity, size, txCount, err := oConn.LocalTxMonitor().Client.GetSizes() 71 | if err != nil { 72 | c.JSON(500, apiError(err.Error())) 73 | return 74 | } 75 | // Create response 76 | resp := responseLocalTxMonitorSizes{ 77 | Capacity: capacity, 78 | Size: size, 79 | TxCount: txCount, 80 | } 81 | c.JSON(200, resp) 82 | } 83 | 84 | type requestLocalTxMonitorHasTx struct { 85 | TxHash string `uri:"tx_hash" binding:"required"` 86 | } 87 | 88 | type responseLocalTxMonitorHasTx struct { 89 | HasTx bool `json:"has_tx"` 90 | } 91 | 92 | // handleLocalTxMonitorHasTx godoc 93 | // 94 | // @Summary Check if a particular TX exists in the mempool 95 | // @Tags localtxmonitor 96 | // @Accept json 97 | // @Produce json 98 | // @Param tx_hash path string true "Transaction hash (hex string)" 99 | // @Success 200 {object} responseLocalTxMonitorHasTx 100 | // @Failure 500 {object} responseApiError 101 | // @Router /localtxmonitor/has_tx/{tx_hash} [get] 102 | func handleLocalTxMonitorHasTx(c *gin.Context) { 103 | // Get parameters 104 | var req requestLocalTxMonitorHasTx 105 | if err := c.ShouldBindUri(&req); err != nil { 106 | c.JSON(http.StatusBadRequest, apiError(err.Error())) 107 | return 108 | } 109 | // Connect to node 110 | oConn, err := node.GetConnection(nil) 111 | if err != nil { 112 | c.JSON(500, apiError(err.Error())) 113 | return 114 | } 115 | // Async error handler 116 | go func() { 117 | err, ok := <-oConn.ErrorChan() 118 | if !ok { 119 | return 120 | } 121 | c.JSON(500, apiError(err.Error())) 122 | }() 123 | defer func() { 124 | // Close Ouroboros connection 125 | oConn.Close() 126 | }() 127 | // Start client 128 | oConn.LocalTxMonitor().Client.Start() 129 | // Make the call to the node 130 | txHash, err := hex.DecodeString(req.TxHash) 131 | if err != nil { 132 | c.JSON(500, apiError(err.Error())) 133 | return 134 | } 135 | hasTx, err := oConn.LocalTxMonitor().Client.HasTx(txHash) 136 | if err != nil { 137 | c.JSON(500, apiError(err.Error())) 138 | return 139 | } 140 | // Create response 141 | resp := responseLocalTxMonitorHasTx{ 142 | HasTx: hasTx, 143 | } 144 | c.JSON(200, resp) 145 | } 146 | 147 | type responseLocalTxMonitorTxs struct { 148 | TxHash string `json:"tx_hash" swaggertype:"string" format:"base16" example:"96649a8b827a5a4d508cd4e98cd88832482f7b884d507a49466d1fb8c4b14978"` 149 | TxBytes []byte `json:"tx_bytes" swaggertype:"string" format:"base64" example:""` 150 | } 151 | 152 | // handleLocalTxMonitorTxs godoc 153 | // 154 | // @Summary List all transactions in the mempool 155 | // @Tags localtxmonitor 156 | // @Accept json 157 | // @Produce json 158 | // @Success 200 {object} []responseLocalTxMonitorTxs 159 | // @Failure 500 {object} responseApiError 160 | // @Router /localtxmonitor/txs [get] 161 | func handleLocalTxMonitorTxs(c *gin.Context) { 162 | // Connect to node 163 | oConn, err := node.GetConnection(nil) 164 | if err != nil { 165 | c.JSON(500, apiError(err.Error())) 166 | return 167 | } 168 | // Async error handler 169 | go func() { 170 | err, ok := <-oConn.ErrorChan() 171 | if !ok { 172 | return 173 | } 174 | c.JSON(500, apiError(err.Error())) 175 | }() 176 | defer func() { 177 | // Close Ouroboros connection 178 | oConn.Close() 179 | }() 180 | // Start client 181 | oConn.LocalTxMonitor().Client.Start() 182 | // Collect TX hashes 183 | resp := []responseLocalTxMonitorTxs{} 184 | for { 185 | txRawBytes, err := oConn.LocalTxMonitor().Client.NextTx() 186 | if err != nil { 187 | c.JSON(500, apiError(err.Error())) 188 | return 189 | } 190 | if txRawBytes == nil { 191 | break 192 | } 193 | // Determine transaction type (era) 194 | txType, err := ledger.DetermineTransactionType(txRawBytes) 195 | if err != nil { 196 | c.JSON(500, apiError(err.Error())) 197 | return 198 | } 199 | tx, err := ledger.NewTransactionFromCbor(txType, txRawBytes) 200 | if err != nil { 201 | c.JSON(500, apiError(err.Error())) 202 | return 203 | } 204 | // Add to response 205 | resp = append( 206 | resp, 207 | responseLocalTxMonitorTxs{ 208 | TxHash: tx.Hash().String(), 209 | TxBytes: txRawBytes, 210 | }, 211 | ) 212 | } 213 | // Send response 214 | c.JSON(200, resp) 215 | } 216 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_tx_monitor_sizes.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalTxMonitorSizes type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalTxMonitorSizes{} 20 | 21 | // ApiResponseLocalTxMonitorSizes struct for ApiResponseLocalTxMonitorSizes 22 | type ApiResponseLocalTxMonitorSizes struct { 23 | Capacity *int32 `json:"capacity,omitempty"` 24 | Size *int32 `json:"size,omitempty"` 25 | TxCount *int32 `json:"tx_count,omitempty"` 26 | } 27 | 28 | // NewApiResponseLocalTxMonitorSizes instantiates a new ApiResponseLocalTxMonitorSizes object 29 | // This constructor will assign default values to properties that have it defined, 30 | // and makes sure properties required by API are set, but the set of arguments 31 | // will change when the set of required properties is changed 32 | func NewApiResponseLocalTxMonitorSizes() *ApiResponseLocalTxMonitorSizes { 33 | this := ApiResponseLocalTxMonitorSizes{} 34 | return &this 35 | } 36 | 37 | // NewApiResponseLocalTxMonitorSizesWithDefaults instantiates a new ApiResponseLocalTxMonitorSizes object 38 | // This constructor will only assign default values to properties that have it defined, 39 | // but it doesn't guarantee that properties required by API are set 40 | func NewApiResponseLocalTxMonitorSizesWithDefaults() *ApiResponseLocalTxMonitorSizes { 41 | this := ApiResponseLocalTxMonitorSizes{} 42 | return &this 43 | } 44 | 45 | // GetCapacity returns the Capacity field value if set, zero value otherwise. 46 | func (o *ApiResponseLocalTxMonitorSizes) GetCapacity() int32 { 47 | if o == nil || IsNil(o.Capacity) { 48 | var ret int32 49 | return ret 50 | } 51 | return *o.Capacity 52 | } 53 | 54 | // GetCapacityOk returns a tuple with the Capacity field value if set, nil otherwise 55 | // and a boolean to check if the value has been set. 56 | func (o *ApiResponseLocalTxMonitorSizes) GetCapacityOk() (*int32, bool) { 57 | if o == nil || IsNil(o.Capacity) { 58 | return nil, false 59 | } 60 | return o.Capacity, true 61 | } 62 | 63 | // HasCapacity returns a boolean if a field has been set. 64 | func (o *ApiResponseLocalTxMonitorSizes) HasCapacity() bool { 65 | if o != nil && !IsNil(o.Capacity) { 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | // SetCapacity gets a reference to the given int32 and assigns it to the Capacity field. 73 | func (o *ApiResponseLocalTxMonitorSizes) SetCapacity(v int32) { 74 | o.Capacity = &v 75 | } 76 | 77 | // GetSize returns the Size field value if set, zero value otherwise. 78 | func (o *ApiResponseLocalTxMonitorSizes) GetSize() int32 { 79 | if o == nil || IsNil(o.Size) { 80 | var ret int32 81 | return ret 82 | } 83 | return *o.Size 84 | } 85 | 86 | // GetSizeOk returns a tuple with the Size field value if set, nil otherwise 87 | // and a boolean to check if the value has been set. 88 | func (o *ApiResponseLocalTxMonitorSizes) GetSizeOk() (*int32, bool) { 89 | if o == nil || IsNil(o.Size) { 90 | return nil, false 91 | } 92 | return o.Size, true 93 | } 94 | 95 | // HasSize returns a boolean if a field has been set. 96 | func (o *ApiResponseLocalTxMonitorSizes) HasSize() bool { 97 | if o != nil && !IsNil(o.Size) { 98 | return true 99 | } 100 | 101 | return false 102 | } 103 | 104 | // SetSize gets a reference to the given int32 and assigns it to the Size field. 105 | func (o *ApiResponseLocalTxMonitorSizes) SetSize(v int32) { 106 | o.Size = &v 107 | } 108 | 109 | // GetTxCount returns the TxCount field value if set, zero value otherwise. 110 | func (o *ApiResponseLocalTxMonitorSizes) GetTxCount() int32 { 111 | if o == nil || IsNil(o.TxCount) { 112 | var ret int32 113 | return ret 114 | } 115 | return *o.TxCount 116 | } 117 | 118 | // GetTxCountOk returns a tuple with the TxCount field value if set, nil otherwise 119 | // and a boolean to check if the value has been set. 120 | func (o *ApiResponseLocalTxMonitorSizes) GetTxCountOk() (*int32, bool) { 121 | if o == nil || IsNil(o.TxCount) { 122 | return nil, false 123 | } 124 | return o.TxCount, true 125 | } 126 | 127 | // HasTxCount returns a boolean if a field has been set. 128 | func (o *ApiResponseLocalTxMonitorSizes) HasTxCount() bool { 129 | if o != nil && !IsNil(o.TxCount) { 130 | return true 131 | } 132 | 133 | return false 134 | } 135 | 136 | // SetTxCount gets a reference to the given int32 and assigns it to the TxCount field. 137 | func (o *ApiResponseLocalTxMonitorSizes) SetTxCount(v int32) { 138 | o.TxCount = &v 139 | } 140 | 141 | func (o ApiResponseLocalTxMonitorSizes) MarshalJSON() ([]byte, error) { 142 | toSerialize, err := o.ToMap() 143 | if err != nil { 144 | return []byte{}, err 145 | } 146 | return json.Marshal(toSerialize) 147 | } 148 | 149 | func (o ApiResponseLocalTxMonitorSizes) ToMap() (map[string]interface{}, error) { 150 | toSerialize := map[string]interface{}{} 151 | if !IsNil(o.Capacity) { 152 | toSerialize["capacity"] = o.Capacity 153 | } 154 | if !IsNil(o.Size) { 155 | toSerialize["size"] = o.Size 156 | } 157 | if !IsNil(o.TxCount) { 158 | toSerialize["tx_count"] = o.TxCount 159 | } 160 | return toSerialize, nil 161 | } 162 | 163 | type NullableApiResponseLocalTxMonitorSizes struct { 164 | value *ApiResponseLocalTxMonitorSizes 165 | isSet bool 166 | } 167 | 168 | func (v NullableApiResponseLocalTxMonitorSizes) Get() *ApiResponseLocalTxMonitorSizes { 169 | return v.value 170 | } 171 | 172 | func (v *NullableApiResponseLocalTxMonitorSizes) Set( 173 | val *ApiResponseLocalTxMonitorSizes, 174 | ) { 175 | v.value = val 176 | v.isSet = true 177 | } 178 | 179 | func (v NullableApiResponseLocalTxMonitorSizes) IsSet() bool { 180 | return v.isSet 181 | } 182 | 183 | func (v *NullableApiResponseLocalTxMonitorSizes) Unset() { 184 | v.value = nil 185 | v.isSet = false 186 | } 187 | 188 | func NewNullableApiResponseLocalTxMonitorSizes( 189 | val *ApiResponseLocalTxMonitorSizes, 190 | ) *NullableApiResponseLocalTxMonitorSizes { 191 | return &NullableApiResponseLocalTxMonitorSizes{value: val, isSet: true} 192 | } 193 | 194 | func (v NullableApiResponseLocalTxMonitorSizes) MarshalJSON() ([]byte, error) { 195 | return json.Marshal(v.value) 196 | } 197 | 198 | func (v *NullableApiResponseLocalTxMonitorSizes) UnmarshalJSON( 199 | src []byte, 200 | ) error { 201 | v.isSet = true 202 | return json.Unmarshal(src, &v.value) 203 | } 204 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_state_query_system_start.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalStateQuerySystemStart type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalStateQuerySystemStart{} 20 | 21 | // ApiResponseLocalStateQuerySystemStart struct for ApiResponseLocalStateQuerySystemStart 22 | type ApiResponseLocalStateQuerySystemStart struct { 23 | Day *int32 `json:"day,omitempty"` 24 | Picoseconds *int32 `json:"picoseconds,omitempty"` 25 | Year *int32 `json:"year,omitempty"` 26 | } 27 | 28 | // NewApiResponseLocalStateQuerySystemStart instantiates a new ApiResponseLocalStateQuerySystemStart object 29 | // This constructor will assign default values to properties that have it defined, 30 | // and makes sure properties required by API are set, but the set of arguments 31 | // will change when the set of required properties is changed 32 | func NewApiResponseLocalStateQuerySystemStart() *ApiResponseLocalStateQuerySystemStart { 33 | this := ApiResponseLocalStateQuerySystemStart{} 34 | return &this 35 | } 36 | 37 | // NewApiResponseLocalStateQuerySystemStartWithDefaults instantiates a new ApiResponseLocalStateQuerySystemStart object 38 | // This constructor will only assign default values to properties that have it defined, 39 | // but it doesn't guarantee that properties required by API are set 40 | func NewApiResponseLocalStateQuerySystemStartWithDefaults() *ApiResponseLocalStateQuerySystemStart { 41 | this := ApiResponseLocalStateQuerySystemStart{} 42 | return &this 43 | } 44 | 45 | // GetDay returns the Day field value if set, zero value otherwise. 46 | func (o *ApiResponseLocalStateQuerySystemStart) GetDay() int32 { 47 | if o == nil || IsNil(o.Day) { 48 | var ret int32 49 | return ret 50 | } 51 | return *o.Day 52 | } 53 | 54 | // GetDayOk returns a tuple with the Day field value if set, nil otherwise 55 | // and a boolean to check if the value has been set. 56 | func (o *ApiResponseLocalStateQuerySystemStart) GetDayOk() (*int32, bool) { 57 | if o == nil || IsNil(o.Day) { 58 | return nil, false 59 | } 60 | return o.Day, true 61 | } 62 | 63 | // HasDay returns a boolean if a field has been set. 64 | func (o *ApiResponseLocalStateQuerySystemStart) HasDay() bool { 65 | if o != nil && !IsNil(o.Day) { 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | // SetDay gets a reference to the given int32 and assigns it to the Day field. 73 | func (o *ApiResponseLocalStateQuerySystemStart) SetDay(v int32) { 74 | o.Day = &v 75 | } 76 | 77 | // GetPicoseconds returns the Picoseconds field value if set, zero value otherwise. 78 | func (o *ApiResponseLocalStateQuerySystemStart) GetPicoseconds() int32 { 79 | if o == nil || IsNil(o.Picoseconds) { 80 | var ret int32 81 | return ret 82 | } 83 | return *o.Picoseconds 84 | } 85 | 86 | // GetPicosecondsOk returns a tuple with the Picoseconds field value if set, nil otherwise 87 | // and a boolean to check if the value has been set. 88 | func (o *ApiResponseLocalStateQuerySystemStart) GetPicosecondsOk() (*int32, bool) { 89 | if o == nil || IsNil(o.Picoseconds) { 90 | return nil, false 91 | } 92 | return o.Picoseconds, true 93 | } 94 | 95 | // HasPicoseconds returns a boolean if a field has been set. 96 | func (o *ApiResponseLocalStateQuerySystemStart) HasPicoseconds() bool { 97 | if o != nil && !IsNil(o.Picoseconds) { 98 | return true 99 | } 100 | 101 | return false 102 | } 103 | 104 | // SetPicoseconds gets a reference to the given int32 and assigns it to the Picoseconds field. 105 | func (o *ApiResponseLocalStateQuerySystemStart) SetPicoseconds(v int32) { 106 | o.Picoseconds = &v 107 | } 108 | 109 | // GetYear returns the Year field value if set, zero value otherwise. 110 | func (o *ApiResponseLocalStateQuerySystemStart) GetYear() int32 { 111 | if o == nil || IsNil(o.Year) { 112 | var ret int32 113 | return ret 114 | } 115 | return *o.Year 116 | } 117 | 118 | // GetYearOk returns a tuple with the Year field value if set, nil otherwise 119 | // and a boolean to check if the value has been set. 120 | func (o *ApiResponseLocalStateQuerySystemStart) GetYearOk() (*int32, bool) { 121 | if o == nil || IsNil(o.Year) { 122 | return nil, false 123 | } 124 | return o.Year, true 125 | } 126 | 127 | // HasYear returns a boolean if a field has been set. 128 | func (o *ApiResponseLocalStateQuerySystemStart) HasYear() bool { 129 | if o != nil && !IsNil(o.Year) { 130 | return true 131 | } 132 | 133 | return false 134 | } 135 | 136 | // SetYear gets a reference to the given int32 and assigns it to the Year field. 137 | func (o *ApiResponseLocalStateQuerySystemStart) SetYear(v int32) { 138 | o.Year = &v 139 | } 140 | 141 | func (o ApiResponseLocalStateQuerySystemStart) MarshalJSON() ([]byte, error) { 142 | toSerialize, err := o.ToMap() 143 | if err != nil { 144 | return []byte{}, err 145 | } 146 | return json.Marshal(toSerialize) 147 | } 148 | 149 | func (o ApiResponseLocalStateQuerySystemStart) ToMap() (map[string]interface{}, error) { 150 | toSerialize := map[string]interface{}{} 151 | if !IsNil(o.Day) { 152 | toSerialize["day"] = o.Day 153 | } 154 | if !IsNil(o.Picoseconds) { 155 | toSerialize["picoseconds"] = o.Picoseconds 156 | } 157 | if !IsNil(o.Year) { 158 | toSerialize["year"] = o.Year 159 | } 160 | return toSerialize, nil 161 | } 162 | 163 | type NullableApiResponseLocalStateQuerySystemStart struct { 164 | value *ApiResponseLocalStateQuerySystemStart 165 | isSet bool 166 | } 167 | 168 | func (v NullableApiResponseLocalStateQuerySystemStart) Get() *ApiResponseLocalStateQuerySystemStart { 169 | return v.value 170 | } 171 | 172 | func (v *NullableApiResponseLocalStateQuerySystemStart) Set( 173 | val *ApiResponseLocalStateQuerySystemStart, 174 | ) { 175 | v.value = val 176 | v.isSet = true 177 | } 178 | 179 | func (v NullableApiResponseLocalStateQuerySystemStart) IsSet() bool { 180 | return v.isSet 181 | } 182 | 183 | func (v *NullableApiResponseLocalStateQuerySystemStart) Unset() { 184 | v.value = nil 185 | v.isSet = false 186 | } 187 | 188 | func NewNullableApiResponseLocalStateQuerySystemStart( 189 | val *ApiResponseLocalStateQuerySystemStart, 190 | ) *NullableApiResponseLocalStateQuerySystemStart { 191 | return &NullableApiResponseLocalStateQuerySystemStart{ 192 | value: val, 193 | isSet: true, 194 | } 195 | } 196 | 197 | func (v NullableApiResponseLocalStateQuerySystemStart) MarshalJSON() ([]byte, error) { 198 | return json.Marshal(v.value) 199 | } 200 | 201 | func (v *NullableApiResponseLocalStateQuerySystemStart) UnmarshalJSON( 202 | src []byte, 203 | ) error { 204 | v.isSet = true 205 | return json.Unmarshal(src, &v.value) 206 | } 207 | -------------------------------------------------------------------------------- /openapi/api_default.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "bytes" 16 | "context" 17 | "io" 18 | "net/http" 19 | "net/url" 20 | ) 21 | 22 | type DefaultAPI interface { 23 | 24 | /* 25 | LocaltxsubmissionTxPost Submit Tx 26 | 27 | Submit an already serialized transaction to the network. 28 | 29 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 30 | @return DefaultAPILocaltxsubmissionTxPostRequest 31 | */ 32 | LocaltxsubmissionTxPost( 33 | ctx context.Context, 34 | ) DefaultAPILocaltxsubmissionTxPostRequest 35 | 36 | // LocaltxsubmissionTxPostExecute executes the request 37 | // @return string 38 | LocaltxsubmissionTxPostExecute( 39 | r DefaultAPILocaltxsubmissionTxPostRequest, 40 | ) (string, *http.Response, error) 41 | } 42 | 43 | // DefaultAPIService DefaultAPI service 44 | type DefaultAPIService service 45 | 46 | type DefaultAPILocaltxsubmissionTxPostRequest struct { 47 | ctx context.Context 48 | ApiService DefaultAPI 49 | contentType *string 50 | } 51 | 52 | // Content type 53 | func (r DefaultAPILocaltxsubmissionTxPostRequest) ContentType( 54 | contentType string, 55 | ) DefaultAPILocaltxsubmissionTxPostRequest { 56 | r.contentType = &contentType 57 | return r 58 | } 59 | 60 | func (r DefaultAPILocaltxsubmissionTxPostRequest) Execute() (string, *http.Response, error) { 61 | return r.ApiService.LocaltxsubmissionTxPostExecute(r) 62 | } 63 | 64 | /* 65 | LocaltxsubmissionTxPost Submit Tx 66 | 67 | Submit an already serialized transaction to the network. 68 | 69 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 70 | @return DefaultAPILocaltxsubmissionTxPostRequest 71 | */ 72 | func (a *DefaultAPIService) LocaltxsubmissionTxPost( 73 | ctx context.Context, 74 | ) DefaultAPILocaltxsubmissionTxPostRequest { 75 | return DefaultAPILocaltxsubmissionTxPostRequest{ 76 | ApiService: a, 77 | ctx: ctx, 78 | } 79 | } 80 | 81 | // Execute executes the request 82 | // 83 | // @return string 84 | func (a *DefaultAPIService) LocaltxsubmissionTxPostExecute( 85 | r DefaultAPILocaltxsubmissionTxPostRequest, 86 | ) (string, *http.Response, error) { 87 | var ( 88 | localVarHTTPMethod = http.MethodPost 89 | localVarPostBody interface{} 90 | formFiles []formFile 91 | localVarReturnValue string 92 | ) 93 | 94 | localBasePath, err := a.client.cfg.ServerURLWithContext( 95 | r.ctx, 96 | "DefaultAPIService.LocaltxsubmissionTxPost", 97 | ) 98 | if err != nil { 99 | return localVarReturnValue, nil, &GenericOpenAPIError{ 100 | error: err.Error(), 101 | } 102 | } 103 | 104 | localVarPath := localBasePath + "/localtxsubmission/tx" 105 | 106 | localVarHeaderParams := make(map[string]string) 107 | localVarQueryParams := url.Values{} 108 | localVarFormParams := url.Values{} 109 | if r.contentType == nil { 110 | return localVarReturnValue, nil, reportError( 111 | "contentType is required and must be specified", 112 | ) 113 | } 114 | 115 | // to determine the Content-Type header 116 | localVarHTTPContentTypes := []string{} 117 | 118 | // set Content-Type header 119 | localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) 120 | if localVarHTTPContentType != "" { 121 | localVarHeaderParams["Content-Type"] = localVarHTTPContentType 122 | } 123 | 124 | // to determine the Accept header 125 | localVarHTTPHeaderAccepts := []string{"application/json"} 126 | 127 | // set Accept header 128 | localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) 129 | if localVarHTTPHeaderAccept != "" { 130 | localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept 131 | } 132 | parameterAddToHeaderOrQuery( 133 | localVarHeaderParams, 134 | "Content-Type", 135 | r.contentType, 136 | "", 137 | ) 138 | req, err := a.client.prepareRequest( 139 | r.ctx, 140 | localVarPath, 141 | localVarHTTPMethod, 142 | localVarPostBody, 143 | localVarHeaderParams, 144 | localVarQueryParams, 145 | localVarFormParams, 146 | formFiles, 147 | ) 148 | if err != nil { 149 | return localVarReturnValue, nil, err 150 | } 151 | 152 | localVarHTTPResponse, err := a.client.callAPI(req) 153 | if err != nil || localVarHTTPResponse == nil { 154 | return localVarReturnValue, localVarHTTPResponse, err 155 | } 156 | 157 | localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) 158 | localVarHTTPResponse.Body.Close() 159 | localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) 160 | if err != nil { 161 | return localVarReturnValue, localVarHTTPResponse, err 162 | } 163 | 164 | if localVarHTTPResponse.StatusCode >= 300 { 165 | newErr := &GenericOpenAPIError{ 166 | body: localVarBody, 167 | error: localVarHTTPResponse.Status, 168 | } 169 | if localVarHTTPResponse.StatusCode == 400 { 170 | var v string 171 | err = a.client.decode( 172 | &v, 173 | localVarBody, 174 | localVarHTTPResponse.Header.Get("Content-Type"), 175 | ) 176 | if err != nil { 177 | newErr.error = err.Error() 178 | return localVarReturnValue, localVarHTTPResponse, newErr 179 | } 180 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 181 | newErr.model = v 182 | return localVarReturnValue, localVarHTTPResponse, newErr 183 | } 184 | if localVarHTTPResponse.StatusCode == 415 { 185 | var v string 186 | err = a.client.decode( 187 | &v, 188 | localVarBody, 189 | localVarHTTPResponse.Header.Get("Content-Type"), 190 | ) 191 | if err != nil { 192 | newErr.error = err.Error() 193 | return localVarReturnValue, localVarHTTPResponse, newErr 194 | } 195 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 196 | newErr.model = v 197 | return localVarReturnValue, localVarHTTPResponse, newErr 198 | } 199 | if localVarHTTPResponse.StatusCode == 500 { 200 | var v string 201 | err = a.client.decode( 202 | &v, 203 | localVarBody, 204 | localVarHTTPResponse.Header.Get("Content-Type"), 205 | ) 206 | if err != nil { 207 | newErr.error = err.Error() 208 | return localVarReturnValue, localVarHTTPResponse, newErr 209 | } 210 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 211 | newErr.model = v 212 | } 213 | return localVarReturnValue, localVarHTTPResponse, newErr 214 | } 215 | 216 | err = a.client.decode( 217 | &localVarReturnValue, 218 | localVarBody, 219 | localVarHTTPResponse.Header.Get("Content-Type"), 220 | ) 221 | if err != nil { 222 | newErr := &GenericOpenAPIError{ 223 | body: localVarBody, 224 | error: err.Error(), 225 | } 226 | return localVarReturnValue, localVarHTTPResponse, newErr 227 | } 228 | 229 | return localVarReturnValue, localVarHTTPResponse, nil 230 | } 231 | -------------------------------------------------------------------------------- /openapi/api_localtxsubmission.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "bytes" 16 | "context" 17 | "io" 18 | "net/http" 19 | "net/url" 20 | ) 21 | 22 | type LocaltxsubmissionAPI interface { 23 | 24 | /* 25 | LocaltxsubmissionTxPost Submit Tx 26 | 27 | Submit an already serialized transaction to the network. 28 | 29 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 30 | @return LocaltxsubmissionAPILocaltxsubmissionTxPostRequest 31 | */ 32 | LocaltxsubmissionTxPost( 33 | ctx context.Context, 34 | ) LocaltxsubmissionAPILocaltxsubmissionTxPostRequest 35 | 36 | // LocaltxsubmissionTxPostExecute executes the request 37 | // @return string 38 | LocaltxsubmissionTxPostExecute( 39 | r LocaltxsubmissionAPILocaltxsubmissionTxPostRequest, 40 | ) (string, *http.Response, error) 41 | } 42 | 43 | // LocaltxsubmissionAPIService LocaltxsubmissionAPI service 44 | type LocaltxsubmissionAPIService service 45 | 46 | type LocaltxsubmissionAPILocaltxsubmissionTxPostRequest struct { 47 | ctx context.Context 48 | ApiService LocaltxsubmissionAPI 49 | contentType *string 50 | } 51 | 52 | // Content type 53 | func (r LocaltxsubmissionAPILocaltxsubmissionTxPostRequest) ContentType( 54 | contentType string, 55 | ) LocaltxsubmissionAPILocaltxsubmissionTxPostRequest { 56 | r.contentType = &contentType 57 | return r 58 | } 59 | 60 | func (r LocaltxsubmissionAPILocaltxsubmissionTxPostRequest) Execute() (string, *http.Response, error) { 61 | return r.ApiService.LocaltxsubmissionTxPostExecute(r) 62 | } 63 | 64 | /* 65 | LocaltxsubmissionTxPost Submit Tx 66 | 67 | Submit an already serialized transaction to the network. 68 | 69 | @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 70 | @return LocaltxsubmissionAPILocaltxsubmissionTxPostRequest 71 | */ 72 | func (a *LocaltxsubmissionAPIService) LocaltxsubmissionTxPost( 73 | ctx context.Context, 74 | ) LocaltxsubmissionAPILocaltxsubmissionTxPostRequest { 75 | return LocaltxsubmissionAPILocaltxsubmissionTxPostRequest{ 76 | ApiService: a, 77 | ctx: ctx, 78 | } 79 | } 80 | 81 | // Execute executes the request 82 | // 83 | // @return string 84 | func (a *LocaltxsubmissionAPIService) LocaltxsubmissionTxPostExecute( 85 | r LocaltxsubmissionAPILocaltxsubmissionTxPostRequest, 86 | ) (string, *http.Response, error) { 87 | var ( 88 | localVarHTTPMethod = http.MethodPost 89 | localVarPostBody interface{} 90 | formFiles []formFile 91 | localVarReturnValue string 92 | ) 93 | 94 | localBasePath, err := a.client.cfg.ServerURLWithContext( 95 | r.ctx, 96 | "LocaltxsubmissionAPIService.LocaltxsubmissionTxPost", 97 | ) 98 | if err != nil { 99 | return localVarReturnValue, nil, &GenericOpenAPIError{ 100 | error: err.Error(), 101 | } 102 | } 103 | 104 | localVarPath := localBasePath + "/localtxsubmission/tx" 105 | 106 | localVarHeaderParams := make(map[string]string) 107 | localVarQueryParams := url.Values{} 108 | localVarFormParams := url.Values{} 109 | if r.contentType == nil { 110 | return localVarReturnValue, nil, reportError( 111 | "contentType is required and must be specified", 112 | ) 113 | } 114 | 115 | // to determine the Content-Type header 116 | localVarHTTPContentTypes := []string{} 117 | 118 | // set Content-Type header 119 | localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) 120 | if localVarHTTPContentType != "" { 121 | localVarHeaderParams["Content-Type"] = localVarHTTPContentType 122 | } 123 | 124 | // to determine the Accept header 125 | localVarHTTPHeaderAccepts := []string{"application/json"} 126 | 127 | // set Accept header 128 | localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) 129 | if localVarHTTPHeaderAccept != "" { 130 | localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept 131 | } 132 | parameterAddToHeaderOrQuery( 133 | localVarHeaderParams, 134 | "Content-Type", 135 | r.contentType, 136 | "", 137 | "", 138 | ) 139 | req, err := a.client.prepareRequest( 140 | r.ctx, 141 | localVarPath, 142 | localVarHTTPMethod, 143 | localVarPostBody, 144 | localVarHeaderParams, 145 | localVarQueryParams, 146 | localVarFormParams, 147 | formFiles, 148 | ) 149 | if err != nil { 150 | return localVarReturnValue, nil, err 151 | } 152 | 153 | localVarHTTPResponse, err := a.client.callAPI(req) 154 | if err != nil || localVarHTTPResponse == nil { 155 | return localVarReturnValue, localVarHTTPResponse, err 156 | } 157 | 158 | localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) 159 | localVarHTTPResponse.Body.Close() 160 | localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) 161 | if err != nil { 162 | return localVarReturnValue, localVarHTTPResponse, err 163 | } 164 | 165 | if localVarHTTPResponse.StatusCode >= 300 { 166 | newErr := &GenericOpenAPIError{ 167 | body: localVarBody, 168 | error: localVarHTTPResponse.Status, 169 | } 170 | if localVarHTTPResponse.StatusCode == 400 { 171 | var v string 172 | err = a.client.decode( 173 | &v, 174 | localVarBody, 175 | localVarHTTPResponse.Header.Get("Content-Type"), 176 | ) 177 | if err != nil { 178 | newErr.error = err.Error() 179 | return localVarReturnValue, localVarHTTPResponse, newErr 180 | } 181 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 182 | newErr.model = v 183 | return localVarReturnValue, localVarHTTPResponse, newErr 184 | } 185 | if localVarHTTPResponse.StatusCode == 415 { 186 | var v string 187 | err = a.client.decode( 188 | &v, 189 | localVarBody, 190 | localVarHTTPResponse.Header.Get("Content-Type"), 191 | ) 192 | if err != nil { 193 | newErr.error = err.Error() 194 | return localVarReturnValue, localVarHTTPResponse, newErr 195 | } 196 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 197 | newErr.model = v 198 | return localVarReturnValue, localVarHTTPResponse, newErr 199 | } 200 | if localVarHTTPResponse.StatusCode == 500 { 201 | var v string 202 | err = a.client.decode( 203 | &v, 204 | localVarBody, 205 | localVarHTTPResponse.Header.Get("Content-Type"), 206 | ) 207 | if err != nil { 208 | newErr.error = err.Error() 209 | return localVarReturnValue, localVarHTTPResponse, newErr 210 | } 211 | newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) 212 | newErr.model = v 213 | } 214 | return localVarReturnValue, localVarHTTPResponse, newErr 215 | } 216 | 217 | err = a.client.decode( 218 | &localVarReturnValue, 219 | localVarBody, 220 | localVarHTTPResponse.Header.Get("Content-Type"), 221 | ) 222 | if err != nil { 223 | newErr := &GenericOpenAPIError{ 224 | body: localVarBody, 225 | error: err.Error(), 226 | } 227 | return localVarReturnValue, localVarHTTPResponse, newErr 228 | } 229 | 230 | return localVarReturnValue, localVarHTTPResponse, nil 231 | } 232 | -------------------------------------------------------------------------------- /openapi/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "context" 16 | "fmt" 17 | "net/http" 18 | "strings" 19 | ) 20 | 21 | // contextKeys are used to identify the type of value in the context. 22 | // Since these are string, it is possible to get a short description of the 23 | // context key for logging and debugging using key.String(). 24 | 25 | type contextKey string 26 | 27 | func (c contextKey) String() string { 28 | return "auth " + string(c) 29 | } 30 | 31 | var ( 32 | // ContextServerIndex uses a server configuration from the index. 33 | ContextServerIndex = contextKey("serverIndex") 34 | 35 | // ContextOperationServerIndices uses a server configuration from the index mapping. 36 | ContextOperationServerIndices = contextKey("serverOperationIndices") 37 | 38 | // ContextServerVariables overrides a server configuration variables. 39 | ContextServerVariables = contextKey("serverVariables") 40 | 41 | // ContextOperationServerVariables overrides a server configuration variables using operation specific values. 42 | ContextOperationServerVariables = contextKey("serverOperationVariables") 43 | ) 44 | 45 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 46 | type BasicAuth struct { 47 | UserName string `json:"userName,omitempty"` 48 | Password string `json:"password,omitempty"` 49 | } 50 | 51 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 52 | type APIKey struct { 53 | Key string 54 | Prefix string 55 | } 56 | 57 | // ServerVariable stores the information about a server variable 58 | type ServerVariable struct { 59 | Description string 60 | DefaultValue string 61 | EnumValues []string 62 | } 63 | 64 | // ServerConfiguration stores the information about a server 65 | type ServerConfiguration struct { 66 | URL string 67 | Description string 68 | Variables map[string]ServerVariable 69 | } 70 | 71 | // ServerConfigurations stores multiple ServerConfiguration items 72 | type ServerConfigurations []ServerConfiguration 73 | 74 | // Configuration stores the configuration of the API client 75 | type Configuration struct { 76 | Host string `json:"host,omitempty"` 77 | Scheme string `json:"scheme,omitempty"` 78 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 79 | UserAgent string `json:"userAgent,omitempty"` 80 | Debug bool `json:"debug,omitempty"` 81 | Servers ServerConfigurations 82 | OperationServers map[string]ServerConfigurations 83 | HTTPClient *http.Client 84 | } 85 | 86 | // NewConfiguration returns a new Configuration object 87 | func NewConfiguration() *Configuration { 88 | cfg := &Configuration{ 89 | DefaultHeader: make(map[string]string), 90 | UserAgent: "OpenAPI-Generator/1.0.0/go", 91 | Debug: false, 92 | Servers: ServerConfigurations{ 93 | { 94 | URL: "/api", 95 | Description: "No description provided", 96 | }, 97 | }, 98 | OperationServers: map[string]ServerConfigurations{}, 99 | } 100 | return cfg 101 | } 102 | 103 | // AddDefaultHeader adds a new HTTP header to the default header in the request 104 | func (c *Configuration) AddDefaultHeader(key string, value string) { 105 | c.DefaultHeader[key] = value 106 | } 107 | 108 | // URL formats template on a index using given variables 109 | func (sc ServerConfigurations) URL( 110 | index int, 111 | variables map[string]string, 112 | ) (string, error) { 113 | if index < 0 || len(sc) <= index { 114 | return "", fmt.Errorf("index %v out of range %v", index, len(sc)-1) 115 | } 116 | server := sc[index] 117 | url := server.URL 118 | 119 | // go through variables and replace placeholders 120 | for name, variable := range server.Variables { 121 | if value, ok := variables[name]; ok { 122 | found := bool(len(variable.EnumValues) == 0) 123 | for _, enumValue := range variable.EnumValues { 124 | if value == enumValue { 125 | found = true 126 | } 127 | } 128 | if !found { 129 | return "", fmt.Errorf( 130 | "the variable %s in the server URL has invalid value %v. Must be %v", 131 | name, 132 | value, 133 | variable.EnumValues, 134 | ) 135 | } 136 | url = strings.Replace(url, "{"+name+"}", value, -1) 137 | } else { 138 | url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1) 139 | } 140 | } 141 | return url, nil 142 | } 143 | 144 | // ServerURL returns URL based on server settings 145 | func (c *Configuration) ServerURL( 146 | index int, 147 | variables map[string]string, 148 | ) (string, error) { 149 | return c.Servers.URL(index, variables) 150 | } 151 | 152 | func getServerIndex(ctx context.Context) (int, error) { 153 | si := ctx.Value(ContextServerIndex) 154 | if si != nil { 155 | if index, ok := si.(int); ok { 156 | return index, nil 157 | } 158 | return 0, reportError("Invalid type %T should be int", si) 159 | } 160 | return 0, nil 161 | } 162 | 163 | func getServerOperationIndex( 164 | ctx context.Context, 165 | endpoint string, 166 | ) (int, error) { 167 | osi := ctx.Value(ContextOperationServerIndices) 168 | if osi != nil { 169 | if operationIndices, ok := osi.(map[string]int); !ok { 170 | return 0, reportError( 171 | "Invalid type %T should be map[string]int", 172 | osi, 173 | ) 174 | } else { 175 | index, ok := operationIndices[endpoint] 176 | if ok { 177 | return index, nil 178 | } 179 | } 180 | } 181 | return getServerIndex(ctx) 182 | } 183 | 184 | func getServerVariables(ctx context.Context) (map[string]string, error) { 185 | sv := ctx.Value(ContextServerVariables) 186 | if sv != nil { 187 | if variables, ok := sv.(map[string]string); ok { 188 | return variables, nil 189 | } 190 | return nil, reportError( 191 | "ctx value of ContextServerVariables has invalid type %T should be map[string]string", 192 | sv, 193 | ) 194 | } 195 | return nil, nil 196 | } 197 | 198 | func getServerOperationVariables( 199 | ctx context.Context, 200 | endpoint string, 201 | ) (map[string]string, error) { 202 | osv := ctx.Value(ContextOperationServerVariables) 203 | if osv != nil { 204 | if operationVariables, ok := osv.(map[string]map[string]string); !ok { 205 | return nil, reportError( 206 | "ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string", 207 | osv, 208 | ) 209 | } else { 210 | variables, ok := operationVariables[endpoint] 211 | if ok { 212 | return variables, nil 213 | } 214 | } 215 | } 216 | return getServerVariables(ctx) 217 | } 218 | 219 | // ServerURLWithContext returns a new server URL given an endpoint 220 | func (c *Configuration) ServerURLWithContext( 221 | ctx context.Context, 222 | endpoint string, 223 | ) (string, error) { 224 | sc, ok := c.OperationServers[endpoint] 225 | if !ok { 226 | sc = c.Servers 227 | } 228 | 229 | if ctx == nil { 230 | return sc.URL(0, nil) 231 | } 232 | 233 | index, err := getServerOperationIndex(ctx, endpoint) 234 | if err != nil { 235 | return "", err 236 | } 237 | 238 | variables, err := getServerOperationVariables(ctx, endpoint) 239 | if err != nil { 240 | return "", err 241 | } 242 | 243 | return sc.URL(index, variables) 244 | } 245 | -------------------------------------------------------------------------------- /internal/utxorpc/sync.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Blink Labs Software 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utxorpc 16 | 17 | import ( 18 | "context" 19 | "errors" 20 | "fmt" 21 | "log" 22 | 23 | connect "connectrpc.com/connect" 24 | "github.com/blinklabs-io/adder/event" 25 | "github.com/blinklabs-io/cardano-node-api/internal/node" 26 | ocommon "github.com/blinklabs-io/gouroboros/protocol/common" 27 | sync "github.com/utxorpc/go-codegen/utxorpc/v1alpha/sync" 28 | "github.com/utxorpc/go-codegen/utxorpc/v1alpha/sync/syncconnect" 29 | ) 30 | 31 | // chainSyncServiceServer implements the ChainSyncService API 32 | type chainSyncServiceServer struct { 33 | syncconnect.UnimplementedSyncServiceHandler 34 | } 35 | 36 | // FetchBlock 37 | func (s *chainSyncServiceServer) FetchBlock( 38 | ctx context.Context, 39 | req *connect.Request[sync.FetchBlockRequest], 40 | ) (*connect.Response[sync.FetchBlockResponse], error) { 41 | ref := req.Msg.GetRef() // BlockRef 42 | fieldMask := req.Msg.GetFieldMask() 43 | log.Printf( 44 | "Got a FetchBlock request with ref %v and fieldMask %v", 45 | ref, 46 | fieldMask, 47 | ) 48 | 49 | // Connect to node 50 | oConn, err := node.GetConnection(nil) 51 | if err != nil { 52 | return nil, err 53 | } 54 | defer func() { 55 | // Close Ouroboros connection 56 | oConn.Close() 57 | }() 58 | 59 | resp := &sync.FetchBlockResponse{} 60 | // Start client 61 | var points []ocommon.Point 62 | if len(ref) > 0 { 63 | for _, blockRef := range ref { 64 | blockIdx := blockRef.GetSlot() 65 | blockHash := blockRef.GetHash() 66 | slot := uint64(blockIdx) 67 | point := ocommon.NewPoint(slot, blockHash) 68 | points = append(points, point) 69 | } 70 | } else { 71 | tip, err := oConn.ChainSync().Client.GetCurrentTip() 72 | if err != nil { 73 | return nil, err 74 | } 75 | point := tip.Point 76 | points = append(points, point) 77 | } 78 | log.Printf("points: %v", points) 79 | // TODO: replace with something that works NtC 80 | // for _, point := range points { 81 | // log.Printf("Point Slot: %d, Hash: %x\n", point.Slot, point.Hash) 82 | // block, err := oConn.BlockFetch().Client.GetBlock( 83 | // ocommon.NewPoint(point.Slot, point.Hash), 84 | // ) 85 | // if err != nil { 86 | // return nil, err 87 | // } 88 | // var acb sync.AnyChainBlock 89 | // var acbc sync.AnyChainBlock_Cardano 90 | // ret := NewBlockFromBlock(block) 91 | // acbc.Cardano = &ret 92 | // acb.Chain = &acbc 93 | // resp.Block = append(resp.Block, &acb) 94 | // } 95 | 96 | return connect.NewResponse(resp), nil 97 | } 98 | 99 | // DumpHistory 100 | func (s *chainSyncServiceServer) DumpHistory( 101 | ctx context.Context, 102 | req *connect.Request[sync.DumpHistoryRequest], 103 | ) (*connect.Response[sync.DumpHistoryResponse], error) { 104 | startToken := req.Msg.GetStartToken() // BlockRef 105 | maxItems := req.Msg.GetMaxItems() 106 | fieldMask := req.Msg.GetFieldMask() 107 | log.Printf( 108 | "Got a DumpHistory request with token %v and maxItems %d and fieldMask %v", 109 | startToken, 110 | maxItems, 111 | fieldMask, 112 | ) 113 | 114 | // Connect to node 115 | oConn, err := node.GetConnection(nil) 116 | if err != nil { 117 | return nil, err 118 | } 119 | defer func() { 120 | // Close Ouroboros connection 121 | oConn.Close() 122 | }() 123 | 124 | resp := &sync.DumpHistoryResponse{} 125 | // Start client 126 | log.Printf("startToken: %#v\n", startToken) 127 | var startPoint ocommon.Point 128 | if startToken != nil { 129 | log.Printf("startToken != nil\n") 130 | blockRef := startToken 131 | blockIdx := blockRef.GetSlot() 132 | blockHash := blockRef.GetHash() 133 | slot := uint64(blockIdx) 134 | startPoint = ocommon.NewPoint(slot, blockHash) 135 | } else { 136 | log.Printf("getting tip\n") 137 | tip, err := oConn.ChainSync().Client.GetCurrentTip() 138 | if err != nil { 139 | return nil, err 140 | } 141 | startPoint = tip.Point 142 | } 143 | log.Printf( 144 | "startPoint slot %d, hash %x\n", 145 | startPoint.Slot, 146 | startPoint.Hash, 147 | ) 148 | // TODO: why is this giving us 0? 149 | start, end, err := oConn.ChainSync().Client.GetAvailableBlockRange( 150 | []ocommon.Point{startPoint}, 151 | ) 152 | if err != nil { 153 | return nil, err 154 | } 155 | log.Printf("Start: slot %d, hash %x\n", start.Slot, start.Hash) 156 | log.Printf("End (tip): slot %d, hash %x\n", end.Slot, end.Hash) 157 | 158 | return connect.NewResponse(resp), nil 159 | } 160 | 161 | // FollowTip 162 | func (s *chainSyncServiceServer) FollowTip( 163 | ctx context.Context, 164 | req *connect.Request[sync.FollowTipRequest], 165 | stream *connect.ServerStream[sync.FollowTipResponse], 166 | ) error { 167 | intersect := req.Msg.GetIntersect() // []*BlockRef 168 | log.Printf("Got a FollowTip request with intersect %v", intersect) 169 | 170 | // Setup event channel 171 | eventChan := make(chan event.Event, 10) 172 | connCfg := node.ConnectionConfig{ 173 | ChainSyncEventChan: eventChan, 174 | } 175 | // Connect to node 176 | oConn, err := node.GetConnection(&connCfg) 177 | if err != nil { 178 | return err 179 | } 180 | defer func() { 181 | // Close Ouroboros connection 182 | oConn.Close() 183 | }() 184 | 185 | // Get our starting point 186 | var point ocommon.Point 187 | if len(intersect) > 0 { 188 | for _, blockRef := range intersect { 189 | blockIdx := blockRef.GetSlot() 190 | blockHash := blockRef.GetHash() 191 | log.Printf("BlockRef: idx: %d, hash: %x", blockIdx, blockHash) 192 | slot := uint64(blockIdx) 193 | point = ocommon.NewPoint(slot, blockHash) 194 | } 195 | } else { 196 | tip, err := oConn.ChainSync().Client.GetCurrentTip() 197 | if err != nil { 198 | return fmt.Errorf("failed to get tip: %w", err) 199 | } 200 | if tip != nil { 201 | point = tip.Point 202 | } 203 | } 204 | 205 | // Start the sync with the node 206 | err = oConn.ChainSync().Client.Sync([]ocommon.Point{point}) 207 | if err != nil { 208 | log.Printf("ERROR: %s", err) 209 | return err 210 | } 211 | 212 | // Wait for events 213 | for { 214 | evt, ok := <-eventChan 215 | if !ok { 216 | log.Printf("ERROR: channel closed") 217 | return errors.New("ERROR: channel closed") 218 | } 219 | 220 | switch evt.Type { 221 | case "chainsync.block": 222 | resp := &sync.FollowTipResponse{} 223 | // Get event context to get the block chain information 224 | context := evt.Context 225 | if context == nil { 226 | log.Printf("ERROR: empty block context") 227 | return errors.New("ERROR: empty block context") 228 | } 229 | bc := context.(event.BlockContext) 230 | // Get event payload to get the block data 231 | payload := evt.Payload 232 | if payload == nil { 233 | log.Printf( 234 | "ERROR: empty payload: block: %d, slot: %d", 235 | bc.BlockNumber, 236 | bc.SlotNumber, 237 | ) 238 | return fmt.Errorf( 239 | "ERROR: empty payload: block: %d, slot: %d", 240 | bc.BlockNumber, 241 | bc.SlotNumber, 242 | ) 243 | } 244 | be := payload.(event.BlockEvent) 245 | block := be.Block // gOuroboros Block 246 | 247 | var acb sync.AnyChainBlock 248 | tmpBlock, err := block.Utxorpc() 249 | if err != nil { 250 | return fmt.Errorf("convert block: %w", err) 251 | } 252 | acbc := sync.AnyChainBlock_Cardano{ 253 | Cardano: tmpBlock, 254 | } 255 | acb.Chain = &acbc 256 | var ftra sync.FollowTipResponse_Apply 257 | ftra.Apply = &acb 258 | resp.Action = &ftra 259 | err = stream.Send(resp) 260 | if err != nil { 261 | return err 262 | } 263 | // Log event 264 | log.Printf( 265 | "block: slot: %d, hash: %s", 266 | block.SlotNumber(), 267 | block.Hash(), 268 | ) 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | tags: 7 | - 'v*.*.*' 8 | 9 | concurrency: ${{ github.ref }} 10 | 11 | jobs: 12 | create-draft-release: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | outputs: 17 | RELEASE_ID: ${{ steps.create-release.outputs.result }} 18 | steps: 19 | - run: "echo \"RELEASE_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV" 20 | - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 https://github.com/actions/github-script/releases/tag/v8.0.0 21 | id: create-release 22 | if: startsWith(github.ref, 'refs/tags/') 23 | with: 24 | github-token: ${{ secrets.GITHUB_TOKEN }} 25 | result-encoding: string 26 | script: | 27 | try { 28 | const response = await github.rest.repos.createRelease({ 29 | draft: true, 30 | generate_release_notes: true, 31 | name: process.env.RELEASE_TAG, 32 | owner: context.repo.owner, 33 | prerelease: false, 34 | repo: context.repo.repo, 35 | tag_name: process.env.RELEASE_TAG, 36 | }); 37 | 38 | return response.data.id; 39 | } catch (error) { 40 | core.setFailed(error.message); 41 | } 42 | 43 | build-binaries: 44 | strategy: 45 | matrix: 46 | os: [linux, darwin, freebsd, windows] 47 | arch: [amd64, arm64] 48 | runs-on: ubuntu-latest 49 | needs: [create-draft-release] 50 | permissions: 51 | actions: write 52 | attestations: write 53 | checks: write 54 | contents: write 55 | id-token: write 56 | packages: write 57 | statuses: write 58 | steps: 59 | - run: "echo \"RELEASE_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV" 60 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 61 | with: 62 | fetch-depth: '0' 63 | - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 https://github.com/actions/setup-go/releases/tag/v6.1.0 64 | with: 65 | go-version: 1.24.x 66 | - name: Build binary 67 | run: GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} make build 68 | - name: Upload release asset 69 | if: startsWith(github.ref, 'refs/tags/') 70 | run: | 71 | _filename=cardano-node-api-${{ env.RELEASE_TAG }}-${{ matrix.os }}-${{ matrix.arch }} 72 | if [[ ${{ matrix.os }} == windows ]]; then 73 | _filename=${_filename}.exe 74 | fi 75 | cp cardano-node-api ${_filename} 76 | curl \ 77 | -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 78 | -H "Content-Type: application/octet-stream" \ 79 | --data-binary @${_filename} \ 80 | https://uploads.github.com/repos/${{ github.repository_owner }}/cardano-node-api/releases/${{ needs.create-draft-release.outputs.RELEASE_ID }}/assets?name=${_filename} 81 | - name: Attest binary 82 | uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 https://github.com/actions/attest-build-provenance/releases/tag/v3.0.0 83 | with: 84 | subject-path: 'cardano-node-api' 85 | 86 | build-images: 87 | runs-on: ubuntu-latest 88 | needs: [create-draft-release] 89 | permissions: 90 | actions: write 91 | attestations: write 92 | checks: write 93 | contents: write 94 | id-token: write 95 | packages: write 96 | statuses: write 97 | steps: 98 | - run: "echo \"RELEASE_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV" 99 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 https://github.com/actions/checkout/releases/tag/v6.0.1 100 | with: 101 | fetch-depth: '0' 102 | - name: Set up QEMU 103 | uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 https://github.com/docker/setup-qemu-action/releases/tag/v3.7.0 104 | - name: Set up Docker Buildx 105 | uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 https://github.com/docker/setup-buildx-action/releases/tag/v3.11.1 106 | - name: Login to Docker Hub 107 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 https://github.com/docker/login-action/releases/tag/v3.6.0 108 | with: 109 | username: blinklabs 110 | password: ${{ secrets.DOCKER_PASSWORD }} # uses token 111 | - name: Login to GHCR 112 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 https://github.com/docker/login-action/releases/tag/v3.6.0 113 | with: 114 | username: ${{ github.repository_owner }} 115 | password: ${{ secrets.GITHUB_TOKEN }} 116 | registry: ghcr.io 117 | - id: meta 118 | uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 https://github.com/docker/metadata-action/releases/tag/v5.10.0 119 | with: 120 | images: | 121 | blinklabs/cardano-node-api 122 | ghcr.io/${{ github.repository }} 123 | tags: | 124 | # Only version, no revision 125 | type=match,pattern=v(.*)-(.*),group=1 126 | # branch 127 | type=ref,event=branch 128 | # semver 129 | type=semver,pattern={{version}} 130 | - name: Build images 131 | id: push 132 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 https://github.com/docker/build-push-action/releases/tag/v6.18.0 133 | with: 134 | outputs: "type=registry,push=true" 135 | platforms: linux/amd64,linux/arm64 136 | tags: ${{ steps.meta.outputs.tags }} 137 | labels: ${{ steps.meta.outputs.labels }} 138 | - name: Attest Docker Hub image 139 | uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 https://github.com/actions/attest-build-provenance/releases/tag/v3.0.0 140 | with: 141 | subject-name: index.docker.io/blinklabs/cardano-node-api 142 | subject-digest: ${{ steps.push.outputs.digest }} 143 | push-to-registry: true 144 | - name: Attest GHCR image 145 | uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 https://github.com/actions/attest-build-provenance/releases/tag/v3.0.0 146 | with: 147 | subject-name: ghcr.io/${{ github.repository }} 148 | subject-digest: ${{ steps.push.outputs.digest }} 149 | push-to-registry: true 150 | # Update Docker Hub from README 151 | - name: Docker Hub Description 152 | uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0 https://github.com/peter-evans/dockerhub-description/releases/tag/v5.0.0 153 | with: 154 | username: blinklabs 155 | password: ${{ secrets.DOCKER_PASSWORD }} 156 | repository: blinklabs/cardano-node-api 157 | readme-filepath: ./README.md 158 | short-description: "HTTP API over the Cardano Node UNIX socket" 159 | 160 | finalize-release: 161 | runs-on: ubuntu-latest 162 | permissions: 163 | contents: write 164 | needs: [create-draft-release, build-binaries, build-images] 165 | steps: 166 | - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 https://github.com/actions/github-script/releases/tag/v8.0.0 167 | if: startsWith(github.ref, 'refs/tags/') 168 | with: 169 | github-token: ${{ secrets.GITHUB_TOKEN }} 170 | script: | 171 | try { 172 | await github.rest.repos.updateRelease({ 173 | owner: context.repo.owner, 174 | repo: context.repo.repo, 175 | release_id: ${{ needs.create-draft-release.outputs.RELEASE_ID }}, 176 | draft: false, 177 | }); 178 | } catch (error) { 179 | core.setFailed(error.message); 180 | } 181 | # This updates the documentation on pkg.go.dev and the latest version available via the Go module proxy 182 | - name: Pull new module version 183 | if: startsWith(github.ref, 'refs/tags/') 184 | uses: andrewslotin/go-proxy-pull-action@0ef95ea50ab6c03f2f095a5102bbdecad8fd7602 # v1.3.0 https://github.com/andrewslotin/go-proxy-pull-action/releases/tag/v1.3.0 185 | -------------------------------------------------------------------------------- /openapi/model_api_response_local_state_query_tip.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "encoding/json" 16 | ) 17 | 18 | // checks if the ApiResponseLocalStateQueryTip type satisfies the MappedNullable interface at compile time 19 | var _ MappedNullable = &ApiResponseLocalStateQueryTip{} 20 | 21 | // ApiResponseLocalStateQueryTip struct for ApiResponseLocalStateQueryTip 22 | type ApiResponseLocalStateQueryTip struct { 23 | BlockNo *int32 `json:"block_no,omitempty"` 24 | EpochNo *int32 `json:"epoch_no,omitempty"` 25 | Era *string `json:"era,omitempty"` 26 | Hash *string `json:"hash,omitempty"` 27 | SlotNo *int32 `json:"slot_no,omitempty"` 28 | } 29 | 30 | // NewApiResponseLocalStateQueryTip instantiates a new ApiResponseLocalStateQueryTip object 31 | // This constructor will assign default values to properties that have it defined, 32 | // and makes sure properties required by API are set, but the set of arguments 33 | // will change when the set of required properties is changed 34 | func NewApiResponseLocalStateQueryTip() *ApiResponseLocalStateQueryTip { 35 | this := ApiResponseLocalStateQueryTip{} 36 | return &this 37 | } 38 | 39 | // NewApiResponseLocalStateQueryTipWithDefaults instantiates a new ApiResponseLocalStateQueryTip object 40 | // This constructor will only assign default values to properties that have it defined, 41 | // but it doesn't guarantee that properties required by API are set 42 | func NewApiResponseLocalStateQueryTipWithDefaults() *ApiResponseLocalStateQueryTip { 43 | this := ApiResponseLocalStateQueryTip{} 44 | return &this 45 | } 46 | 47 | // GetBlockNo returns the BlockNo field value if set, zero value otherwise. 48 | func (o *ApiResponseLocalStateQueryTip) GetBlockNo() int32 { 49 | if o == nil || IsNil(o.BlockNo) { 50 | var ret int32 51 | return ret 52 | } 53 | return *o.BlockNo 54 | } 55 | 56 | // GetBlockNoOk returns a tuple with the BlockNo field value if set, nil otherwise 57 | // and a boolean to check if the value has been set. 58 | func (o *ApiResponseLocalStateQueryTip) GetBlockNoOk() (*int32, bool) { 59 | if o == nil || IsNil(o.BlockNo) { 60 | return nil, false 61 | } 62 | return o.BlockNo, true 63 | } 64 | 65 | // HasBlockNo returns a boolean if a field has been set. 66 | func (o *ApiResponseLocalStateQueryTip) HasBlockNo() bool { 67 | if o != nil && !IsNil(o.BlockNo) { 68 | return true 69 | } 70 | 71 | return false 72 | } 73 | 74 | // SetBlockNo gets a reference to the given int32 and assigns it to the BlockNo field. 75 | func (o *ApiResponseLocalStateQueryTip) SetBlockNo(v int32) { 76 | o.BlockNo = &v 77 | } 78 | 79 | // GetEpochNo returns the EpochNo field value if set, zero value otherwise. 80 | func (o *ApiResponseLocalStateQueryTip) GetEpochNo() int32 { 81 | if o == nil || IsNil(o.EpochNo) { 82 | var ret int32 83 | return ret 84 | } 85 | return *o.EpochNo 86 | } 87 | 88 | // GetEpochNoOk returns a tuple with the EpochNo field value if set, nil otherwise 89 | // and a boolean to check if the value has been set. 90 | func (o *ApiResponseLocalStateQueryTip) GetEpochNoOk() (*int32, bool) { 91 | if o == nil || IsNil(o.EpochNo) { 92 | return nil, false 93 | } 94 | return o.EpochNo, true 95 | } 96 | 97 | // HasEpochNo returns a boolean if a field has been set. 98 | func (o *ApiResponseLocalStateQueryTip) HasEpochNo() bool { 99 | if o != nil && !IsNil(o.EpochNo) { 100 | return true 101 | } 102 | 103 | return false 104 | } 105 | 106 | // SetEpochNo gets a reference to the given int32 and assigns it to the EpochNo field. 107 | func (o *ApiResponseLocalStateQueryTip) SetEpochNo(v int32) { 108 | o.EpochNo = &v 109 | } 110 | 111 | // GetEra returns the Era field value if set, zero value otherwise. 112 | func (o *ApiResponseLocalStateQueryTip) GetEra() string { 113 | if o == nil || IsNil(o.Era) { 114 | var ret string 115 | return ret 116 | } 117 | return *o.Era 118 | } 119 | 120 | // GetEraOk returns a tuple with the Era field value if set, nil otherwise 121 | // and a boolean to check if the value has been set. 122 | func (o *ApiResponseLocalStateQueryTip) GetEraOk() (*string, bool) { 123 | if o == nil || IsNil(o.Era) { 124 | return nil, false 125 | } 126 | return o.Era, true 127 | } 128 | 129 | // HasEra returns a boolean if a field has been set. 130 | func (o *ApiResponseLocalStateQueryTip) HasEra() bool { 131 | if o != nil && !IsNil(o.Era) { 132 | return true 133 | } 134 | 135 | return false 136 | } 137 | 138 | // SetEra gets a reference to the given string and assigns it to the Era field. 139 | func (o *ApiResponseLocalStateQueryTip) SetEra(v string) { 140 | o.Era = &v 141 | } 142 | 143 | // GetHash returns the Hash field value if set, zero value otherwise. 144 | func (o *ApiResponseLocalStateQueryTip) GetHash() string { 145 | if o == nil || IsNil(o.Hash) { 146 | var ret string 147 | return ret 148 | } 149 | return *o.Hash 150 | } 151 | 152 | // GetHashOk returns a tuple with the Hash field value if set, nil otherwise 153 | // and a boolean to check if the value has been set. 154 | func (o *ApiResponseLocalStateQueryTip) GetHashOk() (*string, bool) { 155 | if o == nil || IsNil(o.Hash) { 156 | return nil, false 157 | } 158 | return o.Hash, true 159 | } 160 | 161 | // HasHash returns a boolean if a field has been set. 162 | func (o *ApiResponseLocalStateQueryTip) HasHash() bool { 163 | if o != nil && !IsNil(o.Hash) { 164 | return true 165 | } 166 | 167 | return false 168 | } 169 | 170 | // SetHash gets a reference to the given string and assigns it to the Hash field. 171 | func (o *ApiResponseLocalStateQueryTip) SetHash(v string) { 172 | o.Hash = &v 173 | } 174 | 175 | // GetSlotNo returns the SlotNo field value if set, zero value otherwise. 176 | func (o *ApiResponseLocalStateQueryTip) GetSlotNo() int32 { 177 | if o == nil || IsNil(o.SlotNo) { 178 | var ret int32 179 | return ret 180 | } 181 | return *o.SlotNo 182 | } 183 | 184 | // GetSlotNoOk returns a tuple with the SlotNo field value if set, nil otherwise 185 | // and a boolean to check if the value has been set. 186 | func (o *ApiResponseLocalStateQueryTip) GetSlotNoOk() (*int32, bool) { 187 | if o == nil || IsNil(o.SlotNo) { 188 | return nil, false 189 | } 190 | return o.SlotNo, true 191 | } 192 | 193 | // HasSlotNo returns a boolean if a field has been set. 194 | func (o *ApiResponseLocalStateQueryTip) HasSlotNo() bool { 195 | if o != nil && !IsNil(o.SlotNo) { 196 | return true 197 | } 198 | 199 | return false 200 | } 201 | 202 | // SetSlotNo gets a reference to the given int32 and assigns it to the SlotNo field. 203 | func (o *ApiResponseLocalStateQueryTip) SetSlotNo(v int32) { 204 | o.SlotNo = &v 205 | } 206 | 207 | func (o ApiResponseLocalStateQueryTip) MarshalJSON() ([]byte, error) { 208 | toSerialize, err := o.ToMap() 209 | if err != nil { 210 | return []byte{}, err 211 | } 212 | return json.Marshal(toSerialize) 213 | } 214 | 215 | func (o ApiResponseLocalStateQueryTip) ToMap() (map[string]interface{}, error) { 216 | toSerialize := map[string]interface{}{} 217 | if !IsNil(o.BlockNo) { 218 | toSerialize["block_no"] = o.BlockNo 219 | } 220 | if !IsNil(o.EpochNo) { 221 | toSerialize["epoch_no"] = o.EpochNo 222 | } 223 | if !IsNil(o.Era) { 224 | toSerialize["era"] = o.Era 225 | } 226 | if !IsNil(o.Hash) { 227 | toSerialize["hash"] = o.Hash 228 | } 229 | if !IsNil(o.SlotNo) { 230 | toSerialize["slot_no"] = o.SlotNo 231 | } 232 | return toSerialize, nil 233 | } 234 | 235 | type NullableApiResponseLocalStateQueryTip struct { 236 | value *ApiResponseLocalStateQueryTip 237 | isSet bool 238 | } 239 | 240 | func (v NullableApiResponseLocalStateQueryTip) Get() *ApiResponseLocalStateQueryTip { 241 | return v.value 242 | } 243 | 244 | func (v *NullableApiResponseLocalStateQueryTip) Set( 245 | val *ApiResponseLocalStateQueryTip, 246 | ) { 247 | v.value = val 248 | v.isSet = true 249 | } 250 | 251 | func (v NullableApiResponseLocalStateQueryTip) IsSet() bool { 252 | return v.isSet 253 | } 254 | 255 | func (v *NullableApiResponseLocalStateQueryTip) Unset() { 256 | v.value = nil 257 | v.isSet = false 258 | } 259 | 260 | func NewNullableApiResponseLocalStateQueryTip( 261 | val *ApiResponseLocalStateQueryTip, 262 | ) *NullableApiResponseLocalStateQueryTip { 263 | return &NullableApiResponseLocalStateQueryTip{value: val, isSet: true} 264 | } 265 | 266 | func (v NullableApiResponseLocalStateQueryTip) MarshalJSON() ([]byte, error) { 267 | return json.Marshal(v.value) 268 | } 269 | 270 | func (v *NullableApiResponseLocalStateQueryTip) UnmarshalJSON( 271 | src []byte, 272 | ) error { 273 | v.isSet = true 274 | return json.Unmarshal(src, &v.value) 275 | } 276 | -------------------------------------------------------------------------------- /openapi/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | cardano-node-api 3 | 4 | Cardano Node API 5 | 6 | API version: 1.0 7 | Contact: support@blinklabs.io 8 | */ 9 | 10 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 | 12 | package openapi 13 | 14 | import ( 15 | "bytes" 16 | "encoding/json" 17 | "fmt" 18 | "reflect" 19 | "time" 20 | ) 21 | 22 | // PtrBool is a helper routine that returns a pointer to given boolean value. 23 | func PtrBool(v bool) *bool { return &v } 24 | 25 | // PtrInt is a helper routine that returns a pointer to given integer value. 26 | func PtrInt(v int) *int { return &v } 27 | 28 | // PtrInt32 is a helper routine that returns a pointer to given integer value. 29 | func PtrInt32(v int32) *int32 { return &v } 30 | 31 | // PtrInt64 is a helper routine that returns a pointer to given integer value. 32 | func PtrInt64(v int64) *int64 { return &v } 33 | 34 | // PtrFloat32 is a helper routine that returns a pointer to given float value. 35 | func PtrFloat32(v float32) *float32 { return &v } 36 | 37 | // PtrFloat64 is a helper routine that returns a pointer to given float value. 38 | func PtrFloat64(v float64) *float64 { return &v } 39 | 40 | // PtrString is a helper routine that returns a pointer to given string value. 41 | func PtrString(v string) *string { return &v } 42 | 43 | // PtrTime is helper routine that returns a pointer to given Time value. 44 | func PtrTime(v time.Time) *time.Time { return &v } 45 | 46 | type NullableBool struct { 47 | value *bool 48 | isSet bool 49 | } 50 | 51 | func (v NullableBool) Get() *bool { 52 | return v.value 53 | } 54 | 55 | func (v *NullableBool) Set(val *bool) { 56 | v.value = val 57 | v.isSet = true 58 | } 59 | 60 | func (v NullableBool) IsSet() bool { 61 | return v.isSet 62 | } 63 | 64 | func (v *NullableBool) Unset() { 65 | v.value = nil 66 | v.isSet = false 67 | } 68 | 69 | func NewNullableBool(val *bool) *NullableBool { 70 | return &NullableBool{value: val, isSet: true} 71 | } 72 | 73 | func (v NullableBool) MarshalJSON() ([]byte, error) { 74 | return json.Marshal(v.value) 75 | } 76 | 77 | func (v *NullableBool) UnmarshalJSON(src []byte) error { 78 | v.isSet = true 79 | return json.Unmarshal(src, &v.value) 80 | } 81 | 82 | type NullableInt struct { 83 | value *int 84 | isSet bool 85 | } 86 | 87 | func (v NullableInt) Get() *int { 88 | return v.value 89 | } 90 | 91 | func (v *NullableInt) Set(val *int) { 92 | v.value = val 93 | v.isSet = true 94 | } 95 | 96 | func (v NullableInt) IsSet() bool { 97 | return v.isSet 98 | } 99 | 100 | func (v *NullableInt) Unset() { 101 | v.value = nil 102 | v.isSet = false 103 | } 104 | 105 | func NewNullableInt(val *int) *NullableInt { 106 | return &NullableInt{value: val, isSet: true} 107 | } 108 | 109 | func (v NullableInt) MarshalJSON() ([]byte, error) { 110 | return json.Marshal(v.value) 111 | } 112 | 113 | func (v *NullableInt) UnmarshalJSON(src []byte) error { 114 | v.isSet = true 115 | return json.Unmarshal(src, &v.value) 116 | } 117 | 118 | type NullableInt32 struct { 119 | value *int32 120 | isSet bool 121 | } 122 | 123 | func (v NullableInt32) Get() *int32 { 124 | return v.value 125 | } 126 | 127 | func (v *NullableInt32) Set(val *int32) { 128 | v.value = val 129 | v.isSet = true 130 | } 131 | 132 | func (v NullableInt32) IsSet() bool { 133 | return v.isSet 134 | } 135 | 136 | func (v *NullableInt32) Unset() { 137 | v.value = nil 138 | v.isSet = false 139 | } 140 | 141 | func NewNullableInt32(val *int32) *NullableInt32 { 142 | return &NullableInt32{value: val, isSet: true} 143 | } 144 | 145 | func (v NullableInt32) MarshalJSON() ([]byte, error) { 146 | return json.Marshal(v.value) 147 | } 148 | 149 | func (v *NullableInt32) UnmarshalJSON(src []byte) error { 150 | v.isSet = true 151 | return json.Unmarshal(src, &v.value) 152 | } 153 | 154 | type NullableInt64 struct { 155 | value *int64 156 | isSet bool 157 | } 158 | 159 | func (v NullableInt64) Get() *int64 { 160 | return v.value 161 | } 162 | 163 | func (v *NullableInt64) Set(val *int64) { 164 | v.value = val 165 | v.isSet = true 166 | } 167 | 168 | func (v NullableInt64) IsSet() bool { 169 | return v.isSet 170 | } 171 | 172 | func (v *NullableInt64) Unset() { 173 | v.value = nil 174 | v.isSet = false 175 | } 176 | 177 | func NewNullableInt64(val *int64) *NullableInt64 { 178 | return &NullableInt64{value: val, isSet: true} 179 | } 180 | 181 | func (v NullableInt64) MarshalJSON() ([]byte, error) { 182 | return json.Marshal(v.value) 183 | } 184 | 185 | func (v *NullableInt64) UnmarshalJSON(src []byte) error { 186 | v.isSet = true 187 | return json.Unmarshal(src, &v.value) 188 | } 189 | 190 | type NullableFloat32 struct { 191 | value *float32 192 | isSet bool 193 | } 194 | 195 | func (v NullableFloat32) Get() *float32 { 196 | return v.value 197 | } 198 | 199 | func (v *NullableFloat32) Set(val *float32) { 200 | v.value = val 201 | v.isSet = true 202 | } 203 | 204 | func (v NullableFloat32) IsSet() bool { 205 | return v.isSet 206 | } 207 | 208 | func (v *NullableFloat32) Unset() { 209 | v.value = nil 210 | v.isSet = false 211 | } 212 | 213 | func NewNullableFloat32(val *float32) *NullableFloat32 { 214 | return &NullableFloat32{value: val, isSet: true} 215 | } 216 | 217 | func (v NullableFloat32) MarshalJSON() ([]byte, error) { 218 | return json.Marshal(v.value) 219 | } 220 | 221 | func (v *NullableFloat32) UnmarshalJSON(src []byte) error { 222 | v.isSet = true 223 | return json.Unmarshal(src, &v.value) 224 | } 225 | 226 | type NullableFloat64 struct { 227 | value *float64 228 | isSet bool 229 | } 230 | 231 | func (v NullableFloat64) Get() *float64 { 232 | return v.value 233 | } 234 | 235 | func (v *NullableFloat64) Set(val *float64) { 236 | v.value = val 237 | v.isSet = true 238 | } 239 | 240 | func (v NullableFloat64) IsSet() bool { 241 | return v.isSet 242 | } 243 | 244 | func (v *NullableFloat64) Unset() { 245 | v.value = nil 246 | v.isSet = false 247 | } 248 | 249 | func NewNullableFloat64(val *float64) *NullableFloat64 { 250 | return &NullableFloat64{value: val, isSet: true} 251 | } 252 | 253 | func (v NullableFloat64) MarshalJSON() ([]byte, error) { 254 | return json.Marshal(v.value) 255 | } 256 | 257 | func (v *NullableFloat64) UnmarshalJSON(src []byte) error { 258 | v.isSet = true 259 | return json.Unmarshal(src, &v.value) 260 | } 261 | 262 | type NullableString struct { 263 | value *string 264 | isSet bool 265 | } 266 | 267 | func (v NullableString) Get() *string { 268 | return v.value 269 | } 270 | 271 | func (v *NullableString) Set(val *string) { 272 | v.value = val 273 | v.isSet = true 274 | } 275 | 276 | func (v NullableString) IsSet() bool { 277 | return v.isSet 278 | } 279 | 280 | func (v *NullableString) Unset() { 281 | v.value = nil 282 | v.isSet = false 283 | } 284 | 285 | func NewNullableString(val *string) *NullableString { 286 | return &NullableString{value: val, isSet: true} 287 | } 288 | 289 | func (v NullableString) MarshalJSON() ([]byte, error) { 290 | return json.Marshal(v.value) 291 | } 292 | 293 | func (v *NullableString) UnmarshalJSON(src []byte) error { 294 | v.isSet = true 295 | return json.Unmarshal(src, &v.value) 296 | } 297 | 298 | type NullableTime struct { 299 | value *time.Time 300 | isSet bool 301 | } 302 | 303 | func (v NullableTime) Get() *time.Time { 304 | return v.value 305 | } 306 | 307 | func (v *NullableTime) Set(val *time.Time) { 308 | v.value = val 309 | v.isSet = true 310 | } 311 | 312 | func (v NullableTime) IsSet() bool { 313 | return v.isSet 314 | } 315 | 316 | func (v *NullableTime) Unset() { 317 | v.value = nil 318 | v.isSet = false 319 | } 320 | 321 | func NewNullableTime(val *time.Time) *NullableTime { 322 | return &NullableTime{value: val, isSet: true} 323 | } 324 | 325 | func (v NullableTime) MarshalJSON() ([]byte, error) { 326 | return json.Marshal(v.value) 327 | } 328 | 329 | func (v *NullableTime) UnmarshalJSON(src []byte) error { 330 | v.isSet = true 331 | return json.Unmarshal(src, &v.value) 332 | } 333 | 334 | // IsNil checks if an input is nil 335 | func IsNil(i interface{}) bool { 336 | if i == nil { 337 | return true 338 | } 339 | switch reflect.TypeOf(i).Kind() { 340 | case reflect.Chan, 341 | reflect.Func, 342 | reflect.Map, 343 | reflect.Ptr, 344 | reflect.UnsafePointer, 345 | reflect.Interface, 346 | reflect.Slice: 347 | return reflect.ValueOf(i).IsNil() 348 | case reflect.Array: 349 | return reflect.ValueOf(i).IsZero() 350 | } 351 | return false 352 | } 353 | 354 | type MappedNullable interface { 355 | ToMap() (map[string]interface{}, error) 356 | } 357 | 358 | // A wrapper for strict JSON decoding 359 | func newStrictDecoder(data []byte) *json.Decoder { 360 | dec := json.NewDecoder(bytes.NewBuffer(data)) 361 | dec.DisallowUnknownFields() 362 | return dec 363 | } 364 | 365 | // Prevent trying to import "fmt" 366 | func reportError(format string, a ...interface{}) error { 367 | return fmt.Errorf(format, a...) 368 | } 369 | --------------------------------------------------------------------------------