From 08d5795e7cd128d39b9d4f5ce185fdcbd4c55bb4 Mon Sep 17 00:00:00 2001 From: Anmol Date: Sat, 24 Dec 2022 11:26:48 +0530 Subject: [PATCH] Add initial local chain registry service (#24) * Add initial local chain registry service * Half finished handler for chain registry * Add dummy handlers and code to be filled later * upgrade chain minor version * delete values schema * add newline at end of the file * add newline at end of file * cleanup creation of new items --- charts/devnet/Chart.yaml | 2 +- charts/devnet/values.schema.json | 1707 ------------------------------ charts/devnet/values.yaml | 2 +- local-chain-registry/.gitignore | 23 + local-chain-registry/Dockerfile | 27 + local-chain-registry/Makefile | 25 + local-chain-registry/app.go | 135 +++ local-chain-registry/config.go | 109 ++ local-chain-registry/const.go | 21 + local-chain-registry/error.go | 63 ++ local-chain-registry/go.mod | 19 + local-chain-registry/go.sum | 35 + local-chain-registry/handler.go | 152 +++ local-chain-registry/main.go | 60 ++ local-chain-registry/models.go | 19 + 15 files changed, 690 insertions(+), 1709 deletions(-) delete mode 100644 charts/devnet/values.schema.json create mode 100644 local-chain-registry/.gitignore create mode 100644 local-chain-registry/Dockerfile create mode 100644 local-chain-registry/Makefile create mode 100644 local-chain-registry/app.go create mode 100644 local-chain-registry/config.go create mode 100644 local-chain-registry/const.go create mode 100644 local-chain-registry/error.go create mode 100644 local-chain-registry/go.mod create mode 100644 local-chain-registry/go.sum create mode 100644 local-chain-registry/handler.go create mode 100644 local-chain-registry/main.go create mode 100644 local-chain-registry/models.go diff --git a/charts/devnet/Chart.yaml b/charts/devnet/Chart.yaml index 3a52eba8..63cde6ca 100644 --- a/charts/devnet/Chart.yaml +++ b/charts/devnet/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.13 +version: 0.1.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/devnet/values.schema.json b/charts/devnet/values.schema.json deleted file mode 100644 index 476dddd8..00000000 --- a/charts/devnet/values.schema.json +++ /dev/null @@ -1,1707 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://github.com/Anmol1696/shuttle/values.schema.json", - "type": "object", - "default": {}, - "title": "Root Schema", - "required": [ - "nameOverride", - "fullnameOverride", - "resources", - "nodeSelector", - "tolerations", - "affinity", - "exposer", - "timeouts", - "defaultChains", - "defaultRelayers", - "chains", - "relayers", - "explorer", - "faucet", - "chainRegistry" - ], - "properties": { - "nameOverride": { - "type": "string", - "default": "", - "title": "The nameOverride Schema", - "examples": [ - "" - ] - }, - "fullnameOverride": { - "type": "string", - "default": "", - "title": "The fullnameOverride Schema", - "examples": [ - "" - ] - }, - "resources": { - "type": "object", - "default": {}, - "title": "The resources Schema", - "required": [], - "properties": {}, - "examples": [{}] - }, - "nodeSelector": { - "type": "object", - "default": {}, - "title": "The nodeSelector Schema", - "required": [], - "properties": {}, - "examples": [{}] - }, - "tolerations": { - "type": "array", - "default": [], - "title": "The tolerations Schema", - "items": {}, - "examples": [ - [] - ] - }, - "affinity": { - "type": "object", - "default": {}, - "title": "The affinity Schema", - "required": [], - "properties": {}, - "examples": [{}] - }, - "exposer": { - "type": "object", - "default": {}, - "title": "The exposer Schema", - "required": [ - "image", - "port", - "resources" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/exposer:latest" - ] - }, - "port": { - "type": "integer", - "default": 0, - "title": "The port Schema", - "examples": [ - 8081 - ] - }, - "resources": { - "type": "object", - "default": {}, - "title": "The resources Schema", - "required": [ - "limits", - "requests" - ], - "properties": { - "limits": { - "type": "object", - "default": {}, - "title": "The limits Schema", - "required": [ - "cpu", - "memory" - ], - "properties": { - "cpu": { - "type": "string", - "default": "", - "title": "The cpu Schema", - "examples": [ - "0.1" - ] - }, - "memory": { - "type": "string", - "default": "", - "title": "The memory Schema", - "examples": [ - "100M" - ] - } - }, - "examples": [{ - "cpu": "0.1", - "memory": "100M" - }] - }, - "requests": { - "type": "object", - "default": {}, - "title": "The requests Schema", - "required": [ - "cpu", - "memory" - ], - "properties": { - "cpu": { - "type": "string", - "default": "", - "title": "The cpu Schema", - "examples": [ - "0.1" - ] - }, - "memory": { - "type": "string", - "default": "", - "title": "The memory Schema", - "examples": [ - "100M" - ] - } - }, - "examples": [{ - "cpu": "0.1", - "memory": "100M" - }] - } - }, - "examples": [{ - "limits": { - "cpu": "0.1", - "memory": "100M" - }, - "requests": { - "cpu": "0.1", - "memory": "100M" - } - }] - } - }, - "examples": [{ - "image": "anmol1696/exposer:latest", - "port": 8081, - "resources": { - "limits": { - "cpu": "0.1", - "memory": "100M" - }, - "requests": { - "cpu": "0.1", - "memory": "100M" - } - } - }] - }, - "timeouts": { - "type": "object", - "default": {}, - "title": "The timeouts Schema", - "required": [ - "time_iota_ms", - "timeout_propose", - "timeout_propose_delta", - "timeout_prevote", - "timeout_prevote_delta", - "timeout_precommit", - "timeout_precommit_delta", - "timeout_commit" - ], - "properties": { - "time_iota_ms": { - "type": "integer", - "default": 0, - "title": "The time_iota_ms Schema", - "examples": [ - 10 - ] - }, - "timeout_propose": { - "type": "string", - "default": "", - "title": "The timeout_propose Schema", - "examples": [ - "400ms" - ] - }, - "timeout_propose_delta": { - "type": "string", - "default": "", - "title": "The timeout_propose_delta Schema", - "examples": [ - "400ms" - ] - }, - "timeout_prevote": { - "type": "string", - "default": "", - "title": "The timeout_prevote Schema", - "examples": [ - "400ms" - ] - }, - "timeout_prevote_delta": { - "type": "string", - "default": "", - "title": "The timeout_prevote_delta Schema", - "examples": [ - "400ms" - ] - }, - "timeout_precommit": { - "type": "string", - "default": "", - "title": "The timeout_precommit Schema", - "examples": [ - "400ms" - ] - }, - "timeout_precommit_delta": { - "type": "string", - "default": "", - "title": "The timeout_precommit_delta Schema", - "examples": [ - "400ms" - ] - }, - "timeout_commit": { - "type": "string", - "default": "", - "title": "The timeout_commit Schema", - "examples": [ - "800ms" - ] - } - }, - "examples": [{ - "time_iota_ms": 10, - "timeout_propose": "400ms", - "timeout_propose_delta": "400ms", - "timeout_prevote": "400ms", - "timeout_prevote_delta": "400ms", - "timeout_precommit": "400ms", - "timeout_precommit_delta": "400ms", - "timeout_commit": "800ms" - }] - }, - "defaultChains": { - "type": "object", - "default": {}, - "title": "The defaultChains Schema", - "required": [ - "osmosis", - "juno", - "wasmd", - "cosmos", - "persistencecore" - ], - "properties": { - "osmosis": { - "type": "object", - "default": {}, - "title": "The osmosis Schema", - "required": [ - "image", - "home", - "binary", - "prefix", - "denom", - "coins", - "hdPath", - "coinType", - "repo" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/osmosis:latest" - ] - }, - "home": { - "type": "string", - "default": "", - "title": "The home Schema", - "examples": [ - "/root/.osmosisd" - ] - }, - "binary": { - "type": "string", - "default": "", - "title": "The binary Schema", - "examples": [ - "osmosisd" - ] - }, - "prefix": { - "type": "string", - "default": "", - "title": "The prefix Schema", - "examples": [ - "osmo" - ] - }, - "denom": { - "type": "string", - "default": "", - "title": "The denom Schema", - "examples": [ - "uosmo" - ] - }, - "coins": { - "type": "string", - "default": "", - "title": "The coins Schema", - "examples": [ - "100000000000000uosmo,100000000000000uion" - ] - }, - "hdPath": { - "type": "string", - "default": "", - "title": "The hdPath Schema", - "examples": [ - "m/44'/118'/0'/0/0" - ] - }, - "coinType": { - "type": "integer", - "default": 0, - "title": "The coinType Schema", - "examples": [ - 118 - ] - }, - "repo": { - "type": "string", - "default": "", - "title": "The repo Schema", - "examples": [ - "https://github.com/osmosis-labs/osmosis" - ] - } - }, - "examples": [{ - "image": "anmol1696/osmosis:latest", - "home": "/root/.osmosisd", - "binary": "osmosisd", - "prefix": "osmo", - "denom": "uosmo", - "coins": "100000000000000uosmo,100000000000000uion", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/osmosis-labs/osmosis" - }] - }, - "juno": { - "type": "object", - "default": {}, - "title": "The juno Schema", - "required": [ - "image", - "home", - "binary", - "prefix", - "denom", - "coins", - "hdPath", - "coinType", - "repo" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/juno:latest" - ] - }, - "home": { - "type": "string", - "default": "", - "title": "The home Schema", - "examples": [ - "/root/.juno" - ] - }, - "binary": { - "type": "string", - "default": "", - "title": "The binary Schema", - "examples": [ - "junod" - ] - }, - "prefix": { - "type": "string", - "default": "", - "title": "The prefix Schema", - "examples": [ - "juno" - ] - }, - "denom": { - "type": "string", - "default": "", - "title": "The denom Schema", - "examples": [ - "ujuno" - ] - }, - "coins": { - "type": "string", - "default": "", - "title": "The coins Schema", - "examples": [ - "100000000000000ujuno" - ] - }, - "hdPath": { - "type": "string", - "default": "", - "title": "The hdPath Schema", - "examples": [ - "m/44'/118'/0'/0/0" - ] - }, - "coinType": { - "type": "integer", - "default": 0, - "title": "The coinType Schema", - "examples": [ - 118 - ] - }, - "repo": { - "type": "string", - "default": "", - "title": "The repo Schema", - "examples": [ - "https://github.com/CosmosContracts/juno" - ] - } - }, - "examples": [{ - "image": "anmol1696/juno:latest", - "home": "/root/.juno", - "binary": "junod", - "prefix": "juno", - "denom": "ujuno", - "coins": "100000000000000ujuno", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmosContracts/juno" - }] - }, - "wasmd": { - "type": "object", - "default": {}, - "title": "The wasmd Schema", - "required": [ - "image", - "home", - "binary", - "prefix", - "denom", - "coins", - "hdPath", - "coinType", - "repo" - ], - "properties": { - "name": { - "type": "string", - "default": "", - "title": "The name Schema", - "examples": [ - "wasmd" - ] - }, - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/wasmd:latest" - ] - }, - "home": { - "type": "string", - "default": "", - "title": "The home Schema", - "examples": [ - "/root/.wasmd" - ] - }, - "binary": { - "type": "string", - "default": "", - "title": "The binary Schema", - "examples": [ - "wasmd" - ] - }, - "prefix": { - "type": "string", - "default": "", - "title": "The prefix Schema", - "examples": [ - "wasm" - ] - }, - "denom": { - "type": "string", - "default": "", - "title": "The denom Schema", - "examples": [ - "stake" - ] - }, - "coins": { - "type": "string", - "default": "", - "title": "The coins Schema", - "examples": [ - "100000000000000ucosm,100000000000000stake" - ] - }, - "hdPath": { - "type": "string", - "default": "", - "title": "The hdPath Schema", - "examples": [ - "m/44'/118'/0'/0/0" - ] - }, - "coinType": { - "type": "integer", - "default": 0, - "title": "The coinType Schema", - "examples": [ - 118 - ] - }, - "repo": { - "type": "string", - "default": "", - "title": "The repo Schema", - "examples": [ - "https://github.com/CosmWasm/wasmd" - ] - } - }, - "examples": [{ - "name": "wasmd", - "image": "anmol1696/wasmd:latest", - "home": "/root/.wasmd", - "binary": "wasmd", - "prefix": "wasm", - "denom": "stake", - "coins": "100000000000000ucosm,100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmWasm/wasmd" - }] - }, - "cosmos": { - "type": "object", - "default": {}, - "title": "The cosmos Schema", - "required": [ - "image", - "binary", - "home", - "prefix", - "denom", - "coins", - "hdPath", - "coinType", - "repo" - ], - "properties": { - "name": { - "type": "string", - "default": "", - "title": "The name Schema", - "examples": [ - "cosmos" - ] - }, - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/gaia:latest" - ] - }, - "binary": { - "type": "string", - "default": "", - "title": "The binary Schema", - "examples": [ - "gaiad" - ] - }, - "prefix": { - "type": "string", - "default": "", - "title": "The prefix Schema", - "examples": [ - "cosmos" - ] - }, - "denom": { - "type": "string", - "default": "", - "title": "The denom Schema", - "examples": [ - "stake" - ] - }, - "coins": { - "type": "string", - "default": "", - "title": "The coins Schema", - "examples": [ - "100000000000000stake" - ] - }, - "hdPath": { - "type": "string", - "default": "", - "title": "The hdPath Schema", - "examples": [ - "m/44'/118'/0'/0/0" - ] - }, - "coinType": { - "type": "integer", - "default": 0, - "title": "The coinType Schema", - "examples": [ - 118 - ] - }, - "repo": { - "type": "string", - "default": "", - "title": "The repo Schema", - "examples": [ - "https://github.com/cosmos/gaia" - ] - } - }, - "examples": [{ - "name": "cosmos", - "image": "anmol1696/gaia:latest", - "binary": "gaiad", - "prefix": "cosmos", - "denom": "stake", - "coins": "100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/cosmos/gaia" - }] - }, - "persistencecore": { - "type": "object", - "default": {}, - "title": "The persistencecore Schema", - "required": [ - "image", - "home", - "binary", - "prefix", - "denom", - "coins", - "hdPath", - "coinType", - "repo" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/persistencecore:latest" - ] - }, - "home": { - "type": "string", - "default": "", - "title": "The home Schema", - "examples": [ - "/root/.persistenceCore" - ] - }, - "binary": { - "type": "string", - "default": "", - "title": "The binary Schema", - "examples": [ - "persistenceCore" - ] - }, - "prefix": { - "type": "string", - "default": "", - "title": "The prefix Schema", - "examples": [ - "persistence" - ] - }, - "denom": { - "type": "string", - "default": "", - "title": "The denom Schema", - "examples": [ - "uxprt" - ] - }, - "coins": { - "type": "string", - "default": "", - "title": "The coins Schema", - "examples": [ - "100000000000000uxprt" - ] - }, - "hdPath": { - "type": "string", - "default": "", - "title": "The hdPath Schema", - "examples": [ - "m/44'/118'/0'/0/0" - ] - }, - "coinType": { - "type": "integer", - "default": 0, - "title": "The coinType Schema", - "examples": [ - 118 - ] - }, - "repo": { - "type": "string", - "default": "", - "title": "The repo Schema", - "examples": [ - "https://github.com/persistenceOne/persistenceCore" - ] - } - }, - "examples": [{ - "image": "anmol1696/persistencecore:latest", - "home": "/root/.persistenceCore", - "binary": "persistenceCore", - "prefix": "persistence", - "denom": "uxprt", - "coins": "100000000000000uxprt", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/persistenceOne/persistenceCore" - }] - } - }, - "examples": [{ - "osmosis": { - "image": "anmol1696/osmosis:latest", - "home": "/root/.osmosisd", - "binary": "osmosisd", - "prefix": "osmo", - "denom": "uosmo", - "coins": "100000000000000uosmo,100000000000000uion", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/osmosis-labs/osmosis" - }, - "juno": { - "image": "anmol1696/juno:latest", - "home": "/root/.juno", - "binary": "junod", - "prefix": "juno", - "denom": "ujuno", - "coins": "100000000000000ujuno", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmosContracts/juno" - }, - "wasmd": { - "name": "wasmd", - "image": "anmol1696/wasmd:latest", - "home": "/root/.wasmd", - "binary": "wasmd", - "prefix": "wasm", - "denom": "stake", - "coins": "100000000000000ucosm,100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmWasm/wasmd" - }, - "cosmos": { - "name": "cosmos", - "image": "anmol1696/gaia:latest", - "binary": "gaiad", - "prefix": "cosmos", - "denom": "stake", - "coins": "100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/cosmos/gaia" - }, - "persistencecore": { - "image": "anmol1696/persistencecore:latest", - "home": "/root/.persistenceCore", - "binary": "persistenceCore", - "prefix": "persistence", - "denom": "uxprt", - "coins": "100000000000000uxprt", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/persistenceOne/persistenceCore" - } - }] - }, - "defaultRelayers": { - "type": "object", - "default": {}, - "title": "The defaultRelayers Schema", - "required": [ - "ts-relayer", - "hermes", - "go-relayer" - ], - "properties": { - "ts-relayer": { - "type": "object", - "default": {}, - "title": "The ts-relayer Schema", - "required": [ - "image" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/ts-relayer:latest" - ] - } - }, - "examples": [{ - "image": "anmol1696/ts-relayer:latest" - }] - }, - "hermes": { - "type": "object", - "default": {}, - "title": "The hermes Schema", - "required": [ - "image" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/hermes:latest" - ] - } - }, - "examples": [{ - "image": "anmol1696/hermes:latest" - }] - }, - "go-relayer": { - "type": "object", - "default": {}, - "title": "The go-relayer Schema", - "required": [ - "image" - ], - "properties": { - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/go-relayer:latest" - ] - } - }, - "examples": [{ - "image": "anmol1696/go-relayer:latest" - }] - } - }, - "examples": [{ - "ts-relayer": { - "image": "anmol1696/ts-relayer:latest" - }, - "hermes": { - "image": "anmol1696/hermes:latest" - }, - "go-relayer": { - "image": "anmol1696/go-relayer:latest" - } - }] - }, - "chains": { - "type": "array", - "default": [], - "title": "The chains Schema", - "items": { - "type": "object", - "title": "A Schema", - "required": [ - "name", - "type", - "numValidators" - ], - "properties": { - "name": { - "type": "string", - "title": "The name Schema", - "examples": [ - "osmosis-1", - "core-1", - "juno-1", - "wasmd" - ] - }, - "type": { - "type": "string", - "title": "The type Schema", - "examples": [ - "osmosis", - "persistencecore", - "juno", - "wasmd" - ] - }, - "numValidators": { - "type": "integer", - "title": "The numValidators Schema", - "examples": [ - 4, - 3, - 2 - ] - }, - "upgrade": { - "type": "object", - "title": "The upgrade Schema", - "required": [ - "enabled", - "type", - "genesis", - "upgrades" - ], - "properties": { - "enabled": { - "type": "boolean", - "title": "The enabled Schema", - "examples": [ - true - ] - }, - "type": { - "type": "string", - "title": "The type Schema", - "examples": [ - "build" - ] - }, - "genesis": { - "type": "string", - "title": "The genesis Schema", - "examples": [ - "v11.0.1", - "v3.2.0" - ] - }, - "upgrades": { - "type": "array", - "title": "The upgrades Schema", - "items": { - "type": "object", - "title": "A Schema", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "type": "string", - "title": "The name Schema", - "examples": [ - "v12", - "v4" - ] - }, - "version": { - "type": "string", - "title": "The version Schema", - "examples": [ - "v12.2.0", - "v4.0.0" - ] - } - }, - "examples": [{ - "name": "v12", - "version": "v12.2.0" - }, - { - "name": "v4", - "version": "v4.0.0" - }] - }, - "examples": [ - [{ - "name": "v12", - "version": "v12.2.0" - }], - [{ - "name": "v4", - "version": "v4.0.0" - }] - ] - } - }, - "examples": [{ - "enabled": true, - "type": "build", - "genesis": "v11.0.1", - "upgrades": [{ - "name": "v12", - "version": "v12.2.0" - }] - }, - { - "enabled": true, - "type": "build", - "genesis": "v3.2.0", - "upgrades": [{ - "name": "v4", - "version": "v4.0.0" - }] - }] - }, - "ports": { - "type": "object", - "title": "The ports Schema", - "required": [ - "rpc", - "rest" - ], - "properties": { - "rpc": { - "type": "integer", - "title": "The rpc Schema", - "examples": [ - 26653, - 26657, - 26654, - 26659 - ] - }, - "rest": { - "type": "integer", - "title": "The rest Schema", - "examples": [ - 1313, - 1317, - 1314, - 1319 - ] - } - }, - "examples": [{ - "rpc": 26653, - "rest": 1313 - }, - { - "rpc": 26657, - "rest": 1317 - }, - { - "rpc": 26654, - "rest": 1314 - }, - { - "rpc": 26659, - "rest": 1319 - }] - } - }, - "examples": [{ - "name": "osmosis-1", - "type": "osmosis", - "numValidators": 4, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v11.0.1", - "upgrades": [{ - "name": "v12", - "version": "v12.2.0" - }] - }, - "ports": { - "rpc": 26653, - "rest": 1313 - } - }, - { - "name": "core-1", - "type": "persistencecore", - "numValidators": 3, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v3.2.0", - "upgrades": [{ - "name": "v4", - "version": "v4.0.0" - }] - }, - "ports": { - "rpc": 26657, - "rest": 1317 - } - }, - { - "name": "juno-1", - "type": "juno", - "numValidators": 2, - "ports": { - "rpc": 26654, - "rest": 1314 - } - }, - { - "name": "wasmd", - "type": "wasmd", - "numValidators": 3, - "ports": { - "rpc": 26659, - "rest": 1319 - } - }] - }, - "examples": [ - [{ - "name": "osmosis-1", - "type": "osmosis", - "numValidators": 4, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v11.0.1", - "upgrades": [{ - "name": "v12", - "version": "v12.2.0" - }] - }, - "ports": { - "rpc": 26653, - "rest": 1313 - } - }, - { - "name": "core-1", - "type": "persistencecore", - "numValidators": 3, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v3.2.0", - "upgrades": [{ - "name": "v4", - "version": "v4.0.0" - }] - }, - "ports": { - "rpc": 26657, - "rest": 1317 - } - }, - { - "name": "juno-1", - "type": "juno", - "numValidators": 2, - "ports": { - "rpc": 26654, - "rest": 1314 - } - }, - { - "name": "wasmd", - "type": "wasmd", - "numValidators": 3, - "ports": { - "rpc": 26659, - "rest": 1319 - } - }] - ] - }, - "relayers": { - "type": "array", - "default": [], - "title": "The relayers Schema", - "items": { - "type": "object", - "title": "A Schema", - "required": [ - "name", - "type", - "replicas", - "chains" - ], - "properties": { - "name": { - "type": "string", - "title": "The name Schema", - "examples": [ - "osmo-wasm", - "osmo-juno" - ] - }, - "type": { - "type": "string", - "title": "The type Schema", - "examples": [ - "ts-relayer" - ] - }, - "replicas": { - "type": "integer", - "title": "The replicas Schema", - "examples": [ - 1 - ] - }, - "chains": { - "type": "array", - "title": "The chains Schema", - "items": { - "type": "string", - "title": "A Schema", - "examples": [ - "osmosis-1", - "wasmd", - "juno-1" - ] - }, - "examples": [ - ["osmosis-1", - "wasmd" - ], - ["osmosis-1", - "juno-1" - ] - ] - } - }, - "examples": [{ - "name": "osmo-wasm", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "wasmd" - ] - }, - { - "name": "osmo-juno", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "juno-1" - ] - }] - }, - "examples": [ - [{ - "name": "osmo-wasm", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "wasmd" - ] - }, - { - "name": "osmo-juno", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "juno-1" - ] - }] - ] - }, - "explorer": { - "type": "object", - "default": {}, - "title": "The explorer Schema", - "required": [ - "enabled", - "type", - "image", - "localhost", - "ports" - ], - "properties": { - "enabled": { - "type": "boolean", - "default": false, - "title": "The enabled Schema", - "examples": [ - false - ] - }, - "type": { - "type": "string", - "default": "", - "title": "The type Schema", - "examples": [ - "ping-pub" - ] - }, - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/explorer" - ] - }, - "localhost": { - "type": "boolean", - "default": false, - "title": "The localhost Schema", - "examples": [ - true - ] - }, - "ports": { - "type": "object", - "default": {}, - "title": "The ports Schema", - "required": [ - "rest" - ], - "properties": { - "rest": { - "type": "integer", - "default": 0, - "title": "The rest Schema", - "examples": [ - 8080 - ] - } - }, - "examples": [{ - "rest": 8080 - }] - } - }, - "examples": [{ - "enabled": false, - "type": "ping-pub", - "image": "anmol1696/explorer", - "localhost": true, - "ports": { - "rest": 8080 - } - }] - }, - "faucet": { - "type": "object", - "default": {}, - "title": "The faucet Schema", - "required": [ - "enabled" - ], - "properties": { - "enabled": { - "type": "boolean", - "default": false, - "title": "The enabled Schema", - "examples": [ - false - ] - } - }, - "examples": [{ - "enabled": false - }] - }, - "chainRegistry": { - "type": "object", - "default": {}, - "title": "The chainRegistry Schema", - "required": [ - "enabled", - "image", - "ports" - ], - "properties": { - "enabled": { - "type": "boolean", - "default": false, - "title": "The enabled Schema", - "examples": [ - false - ] - }, - "image": { - "type": "string", - "default": "", - "title": "The image Schema", - "examples": [ - "anmol1696/chain-registry" - ] - }, - "ports": { - "type": "object", - "default": {}, - "title": "The ports Schema", - "required": [ - "rest" - ], - "properties": { - "rest": { - "type": "integer", - "default": 0, - "title": "The rest Schema", - "examples": [ - 8090 - ] - } - }, - "examples": [{ - "rest": 8090 - }] - } - }, - "examples": [{ - "enabled": false, - "image": "anmol1696/chain-registry", - "ports": { - "rest": 8090 - } - }] - } - }, - "examples": [{ - "nameOverride": "", - "fullnameOverride": "", - "resources": {}, - "nodeSelector": {}, - "tolerations": [], - "affinity": {}, - "exposer": { - "image": "anmol1696/exposer:latest", - "port": 8081, - "resources": { - "limits": { - "cpu": "0.1", - "memory": "100M" - }, - "requests": { - "cpu": "0.1", - "memory": "100M" - } - } - }, - "timeouts": { - "time_iota_ms": 10, - "timeout_propose": "400ms", - "timeout_propose_delta": "400ms", - "timeout_prevote": "400ms", - "timeout_prevote_delta": "400ms", - "timeout_precommit": "400ms", - "timeout_precommit_delta": "400ms", - "timeout_commit": "800ms" - }, - "defaultChains": { - "osmosis": { - "image": "anmol1696/osmosis:latest", - "home": "/root/.osmosisd", - "binary": "osmosisd", - "prefix": "osmo", - "denom": "uosmo", - "coins": "100000000000000uosmo,100000000000000uion", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/osmosis-labs/osmosis" - }, - "juno": { - "image": "anmol1696/juno:latest", - "home": "/root/.juno", - "binary": "junod", - "prefix": "juno", - "denom": "ujuno", - "coins": "100000000000000ujuno", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmosContracts/juno" - }, - "wasmd": { - "name": "wasmd", - "image": "anmol1696/wasmd:latest", - "home": "/root/.wasmd", - "binary": "wasmd", - "prefix": "wasm", - "denom": "stake", - "coins": "100000000000000ucosm,100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/CosmWasm/wasmd" - }, - "cosmos": { - "name": "cosmos", - "image": "anmol1696/gaia:latest", - "binary": "gaiad", - "prefix": "cosmos", - "denom": "stake", - "coins": "100000000000000stake", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/cosmos/gaia" - }, - "persistencecore": { - "image": "anmol1696/persistencecore:latest", - "home": "/root/.persistenceCore", - "binary": "persistenceCore", - "prefix": "persistence", - "denom": "uxprt", - "coins": "100000000000000uxprt", - "hdPath": "m/44'/118'/0'/0/0", - "coinType": 118, - "repo": "https://github.com/persistenceOne/persistenceCore" - } - }, - "defaultRelayers": { - "ts-relayer": { - "image": "anmol1696/ts-relayer:latest" - }, - "hermes": { - "image": "anmol1696/hermes:latest" - }, - "go-relayer": { - "image": "anmol1696/go-relayer:latest" - } - }, - "chains": [{ - "name": "osmosis-1", - "type": "osmosis", - "numValidators": 4, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v11.0.1", - "upgrades": [{ - "name": "v12", - "version": "v12.2.0" - }] - }, - "ports": { - "rpc": 26653, - "rest": 1313 - } - }, - { - "name": "core-1", - "type": "persistencecore", - "numValidators": 3, - "upgrade": { - "enabled": true, - "type": "build", - "genesis": "v3.2.0", - "upgrades": [{ - "name": "v4", - "version": "v4.0.0" - }] - }, - "ports": { - "rpc": 26657, - "rest": 1317 - } - }, - { - "name": "juno-1", - "type": "juno", - "numValidators": 2, - "ports": { - "rpc": 26654, - "rest": 1314 - } - }, - { - "name": "wasmd", - "type": "wasmd", - "numValidators": 3, - "ports": { - "rpc": 26659, - "rest": 1319 - } - }], - "relayers": [{ - "name": "osmo-wasm", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "wasmd" - ] - }, - { - "name": "osmo-juno", - "type": "ts-relayer", - "replicas": 1, - "chains": [ - "osmosis-1", - "juno-1" - ] - }], - "explorer": { - "enabled": false, - "type": "ping-pub", - "image": "anmol1696/explorer", - "localhost": true, - "ports": { - "rest": 8080 - } - }, - "faucet": { - "enabled": false - }, - "chainRegistry": { - "enabled": false, - "image": "anmol1696/chain-registry", - "ports": { - "rest": 8090 - } - } - }] -} \ No newline at end of file diff --git a/charts/devnet/values.yaml b/charts/devnet/values.yaml index 63fad2e7..7e14b26b 100644 --- a/charts/devnet/values.yaml +++ b/charts/devnet/values.yaml @@ -193,4 +193,4 @@ collector: image: anmol1696/collector:latest localhost: true ports: - rest: 8070 \ No newline at end of file + rest: 8070 diff --git a/local-chain-registry/.gitignore b/local-chain-registry/.gitignore new file mode 100644 index 00000000..ad148f27 --- /dev/null +++ b/local-chain-registry/.gitignore @@ -0,0 +1,23 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +bin/ diff --git a/local-chain-registry/Dockerfile b/local-chain-registry/Dockerfile new file mode 100644 index 00000000..9c2d22c0 --- /dev/null +++ b/local-chain-registry/Dockerfile @@ -0,0 +1,27 @@ +FROM golang:1.18-alpine AS build-env + +# Set up dependencies +ENV PACKAGES curl make git libc-dev bash gcc linux-headers + +# Set working directory for the build +WORKDIR /usr/local/share/app + +# Add source files +COPY . . + +# Install minimum necessary dependencies, build Cosmos SDK, remove packages +RUN apk add --no-cache $PACKAGES && go build -mod readonly -o lcr ./... + +# Final image +FROM alpine:3.16 + +# Install ca-certificates +RUN apk add --update ca-certificates jq bash curl +WORKDIR /usr/local/share/app + +RUN ls /usr/bin + +# Copy over binaries from the build-env +COPY --from=build-env /usr/local/share/app/lcr /usr/bin/lcr + +EXPOSE 8080 diff --git a/local-chain-registry/Makefile b/local-chain-registry/Makefile new file mode 100644 index 00000000..84121a51 --- /dev/null +++ b/local-chain-registry/Makefile @@ -0,0 +1,25 @@ +DOCKER := $(shell which docker) +DOCKER_REPO_NAME := anmol1696 +DOCKER_IMAGE := lcr +DOCKER_TAG_NAME := latest + +DOCKER_ARGS += --platform linux/amd64 + +docker-build: + $(DOCKER) buildx build $(DOCKER_ARGS) \ + -t $(DOCKER_REPO_NAME)/$(DOCKER_IMAGE):$(DOCKER_TAG_NAME) . + +docker-build-push: docker-build + $(DOCKER) push $(DOCKER_REPO_NAME)/$(DOCKER_IMAGE):$(DOCKER_TAG_NAME) + +docker-run: + $(DOCKER) run --rm -it --entrypoint /bin/bash $(DOCKER_REPO_NAME)/$(DOCKER_IMAGE):$(DOCKER_TAG_NAME) + +go.mod: + go mod tidy + +build: go.mod + go build -mod readonly -o bin/lcr ./... + +run: + ./bin/lcr --chain-registry bin/configs diff --git a/local-chain-registry/app.go b/local-chain-registry/app.go new file mode 100644 index 00000000..6382761b --- /dev/null +++ b/local-chain-registry/app.go @@ -0,0 +1,135 @@ +package main + +import ( + "fmt" + "net/http" + "time" + + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/go-chi/render" + "go.uber.org/zap" +) + +type AppServer struct { + config *Config + logger *zap.Logger + server *http.Server + router http.Handler +} + +func NewAppServer(config *Config) (*AppServer, error) { + log, err := NewLogger(config) + if err != nil { + return nil, err + } + log.Info( + "Starting the service", + zap.String("prog", Prog), + zap.String("version", Version), + zap.Any("config", config), + ) + + app := &AppServer{ + config: config, + logger: log, + } + + // Setup routes + router, err := app.Router() + if err != nil { + log.Error("Error setting up routes", zap.Error(err)) + return nil, err + } + app.router = router + + return app, err +} + +func (a *AppServer) Router() (*chi.Mux, error) { + router := chi.NewRouter() + router.MethodNotAllowed(MethodNotAllowed) + router.NotFound(NotFound) + + // Set middleware + router.Use(a.panicRecovery) + router.Use(render.SetContentType(render.ContentTypeJSON)) + + // Setup routes + router.Get("/chain_ids", a.GetChainIDs) + router.Route("/chains", func(r chi.Router) { + r.Get("/", a.GetChains) + r.Get("/{chain}", a.GetChain) + r.Get("/{chain}/assets", a.GetChainAssets) + }) + router.Route("/ibc", func(r chi.Router) { + r.Get("/", a.GetAllIBC) + r.Get("/{chain-a}/{chain-b}", a.GetIBCChainsData) + r.Post("/{chain-a}/{chain-b}", a.SetIBCChainsData) + r.Get("/{chain-a}/{chain-b}/channels", a.GetIBCChainsChannels) + r.Patch("/{chain-a}/{chain-b}/channels", a.AddIBCChainChannel) + }) + + return router, nil +} + +func (a *AppServer) loggingMiddleware(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + start := time.Now() + defer func() { + a.logger.Info("client request", + zap.Duration("latency", time.Since(start)), + zap.Int("status", ww.Status()), + zap.Int("bytes", ww.BytesWritten()), + zap.String("client_ip", r.RemoteAddr), + zap.String("method", r.Method), + zap.String("path", r.URL.Path), + zap.String("request-id", middleware.GetReqID(r.Context()))) + }() + + next.ServeHTTP(ww, r) + } + return http.HandlerFunc(fn) +} + +func (a *AppServer) panicRecovery(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + defer func() { + if rc := recover(); rc != nil { + err, ok := rc.(error) + if !ok { + err = fmt.Errorf("panic: %v", rc) + } + a.logger.Error("panic error", + zap.String("request-id", middleware.GetReqID(r.Context())), + zap.Error(err)) + + render.Render(w, r, ErrInternalServer) + return + } + }() + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) +} + +func (a *AppServer) Run() error { + a.logger.Info("App starting", zap.Any("Config", a.config)) + + // Setup server + server := &http.Server{ + Addr: a.config.Addr, + Handler: a.router, + } + a.server = server + + // Start http server as long-running go routine + go func() { + if err := server.ListenAndServe(); err != nil { + a.logger.Error("failed to start the App HTTP server", zap.Error(err)) + } + }() + + return nil +} diff --git a/local-chain-registry/config.go b/local-chain-registry/config.go new file mode 100644 index 00000000..000eb489 --- /dev/null +++ b/local-chain-registry/config.go @@ -0,0 +1,109 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/urfave/cli" + "go.uber.org/zap" +) + +func NewDefaultConfig() *Config { + return &Config{ + Addr: ":8080", + ChainRegistry: "chains/", + } +} + +type Config struct { + // Addr is the interface and port to bind the HTTP service on + Addr string `name:"addr" json:"addr" env:"ADDR" usage:"IP address and port to listen on"` + // ChainRegistry is full path to the directory containing various chain registry information + ChainRegistry string `name:"chain-registry" json:"chain_registry" env:"CHAIN_REGISTRY" usage:"Path of chain registry files"` + // Verbose switches on debug logging + Verbose bool `name:"verbose" json:"verbose" usage:"switch on debug / verbose logging"` + // OnlyFatalLog set log level as fatal to ignore logs + OnlyFatalLog bool `name:"only-fatal-log" json:"only-fatal-log" usage:"used while running test"` +} + +func GetCommandLineOptions() []cli.Flag { + defaults := NewDefaultConfig() + var flags []cli.Flag + count := reflect.TypeOf(Config{}).NumField() + for i := 0; i < count; i++ { + field := reflect.TypeOf(Config{}).Field(i) + usage, found := field.Tag.Lookup("usage") + if !found { + continue + } + envName := field.Tag.Get("env") + if envName != "" { + envName = envPrefix + envName + } + optName := field.Tag.Get("name") + + switch t := field.Type; t.Kind() { + case reflect.Bool: + dv := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).Bool() + msg := fmt.Sprintf("%s (default: %t)", usage, dv) + flags = append(flags, cli.BoolTFlag{ + Name: optName, + Usage: msg, + EnvVar: envName, + }) + case reflect.String: + defaultValue := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).String() + flags = append(flags, cli.StringFlag{ + Name: optName, + Usage: usage, + EnvVar: envName, + Value: defaultValue, + }) + } + } + + return flags +} + +func ParseCLIOptions(cx *cli.Context, config *Config) (err error) { + // iterate the Config and grab command line options via reflection + count := reflect.TypeOf(config).Elem().NumField() + for i := 0; i < count; i++ { + field := reflect.TypeOf(config).Elem().Field(i) + name := field.Tag.Get("name") + + if cx.IsSet(name) { + switch field.Type.Kind() { + case reflect.Bool: + reflect.ValueOf(config).Elem().FieldByName(field.Name).SetBool(cx.Bool(name)) + case reflect.String: + reflect.ValueOf(config).Elem().FieldByName(field.Name).SetString(cx.String(name)) + } + } + } + return nil +} + +func NewLogger(config *Config) (*zap.Logger, error) { + c := zap.NewProductionConfig() + c.DisableCaller = true + // c.Encoding = "console" + + if config.Verbose { + c.DisableCaller = false + c.Development = true + c.DisableStacktrace = true // Disable stack trace for development + c.Level = zap.NewAtomicLevelAt(zap.DebugLevel) + } + + if config.OnlyFatalLog { + c.Level = zap.NewAtomicLevelAt(zap.FatalLevel) + } + + log, err := c.Build() + if err != nil { + return nil, err + } + zap.ReplaceGlobals(log) // Set zap global logger + return log, err +} diff --git a/local-chain-registry/const.go b/local-chain-registry/const.go new file mode 100644 index 00000000..c139800f --- /dev/null +++ b/local-chain-registry/const.go @@ -0,0 +1,21 @@ +package main + +var ( + Version = "v0" + RequestIdCtxKey = &contextKey{"RequestId"} +) + +const ( + Prog = "lcr" + Description = "is a local chain registry api that exposes chain registry based on configs" + envPrefix = "LCR_" +) + +// copied and modified from net/http/http.go +// contextKey is a value for use with context.WithValue. It's used as +// a pointer, so it fits in an interface{} without allocation. +type contextKey struct { + name string +} + +func (k *contextKey) String() string { return Prog + " context value " + k.name } diff --git a/local-chain-registry/error.go b/local-chain-registry/error.go new file mode 100644 index 00000000..567e4909 --- /dev/null +++ b/local-chain-registry/error.go @@ -0,0 +1,63 @@ +package main + +import ( + "net/http" + + "github.com/go-chi/render" + "go.uber.org/zap" +) + +type ErrResponse struct { + Err error `json:"-"` + HTTPStatusCode int `json:"-"` + + MessageText string `json:"message"` +} + +func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error { + render.Status(r, e.HTTPStatusCode) + return nil +} + +// NewErrResponse create http aware errors from custom errors +func NewErrResponse(err error) *ErrResponse { + return &ErrResponse{ + Err: err, + HTTPStatusCode: http.StatusInternalServerError, + MessageText: err.Error(), + } +} + +var ( + ErrValidation = &ErrResponse{HTTPStatusCode: http.StatusBadRequest, MessageText: "Validation error."} + ErrNotFound = &ErrResponse{HTTPStatusCode: http.StatusNotFound, MessageText: "Resource not found."} + ErrNotImplemented = &ErrResponse{HTTPStatusCode: http.StatusNotImplemented, MessageText: "Not Implemented."} + ErrMethodNotAllowed = &ErrResponse{HTTPStatusCode: http.StatusMethodNotAllowed, MessageText: "Method not allowed."} + ErrInternalServer = &ErrResponse{HTTPStatusCode: http.StatusInternalServerError, MessageText: "Internal server error."} +) + +// NotFound Method to render Json respose, used by middleware +func NotFound(w http.ResponseWriter, r *http.Request) { + _ = render.Render(w, r, ErrNotFound) +} + +// MethodNotAllowed Method to render Json respose, used by middleware +func MethodNotAllowed(w http.ResponseWriter, r *http.Request) { + _ = render.Render(w, r, ErrMethodNotAllowed) +} + +func (a *AppServer) renderError(w http.ResponseWriter, r *http.Request, err error, msg ...string) { + log := a.logger + errResp := NewErrResponse(err) + // Logging error at different levels depending on status + switch code := errResp.HTTPStatusCode; { + case code < http.StatusInternalServerError: + log.Warn(err.Error()) + default: + log.Error( + "Internal server error", + zap.Error(err), + ) + } + _ = render.Render(w, r, errResp) +} diff --git a/local-chain-registry/go.mod b/local-chain-registry/go.mod new file mode 100644 index 00000000..e66a6bf3 --- /dev/null +++ b/local-chain-registry/go.mod @@ -0,0 +1,19 @@ +module lcr + +go 1.19 + +require ( + github.com/go-chi/chi v1.5.4 + github.com/go-chi/render v1.0.2 + github.com/urfave/cli v1.22.10 + go.uber.org/zap v1.23.0 +) + +require ( + github.com/ajg/form v1.5.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect +) diff --git a/local-chain-registry/go.sum b/local-chain-registry/go.sum new file mode 100644 index 00000000..9f8f92ef --- /dev/null +++ b/local-chain-registry/go.sum @@ -0,0 +1,35 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= +github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= +github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= +github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= +github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/local-chain-registry/handler.go b/local-chain-registry/handler.go new file mode 100644 index 00000000..51821cc5 --- /dev/null +++ b/local-chain-registry/handler.go @@ -0,0 +1,152 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/go-chi/chi" + "io" + "net/http" + "os" + "path/filepath" + + "github.com/go-chi/render" + "go.uber.org/zap" +) + +func (a *AppServer) renderJSONFile(w http.ResponseWriter, r *http.Request, filePath string) { + jsonFile, err := os.Open(filePath) + if err != nil { + a.logger.Error("Error opening file", + zap.String("file", filePath), + zap.Error(err)) + a.renderError(w, r, fmt.Errorf("error opening json file: %s", filePath)) + } + + byteValue, _ := io.ReadAll(jsonFile) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(byteValue) +} + +func readJSONFile(file string) (map[string]interface{}, error) { + jsonFile, err := os.Open(file) + if err != nil { + return nil, err + } + + byteValue, err := io.ReadAll(jsonFile) + if err != nil { + return nil, err + } + + var result map[string]interface{} + json.Unmarshal(byteValue, &result) + + return result, nil +} + +func (a *AppServer) GetChains(w http.ResponseWriter, r *http.Request) { + files, err := os.ReadDir(a.config.ChainRegistry) + if err != nil { + a.renderError(w, r, err) + return + } + + var chains []interface{} + for _, f := range files { + filename := filepath.Join(a.config.ChainRegistry, f.Name(), "chain.json") + info, err := readJSONFile(filename) + if err != nil { + a.renderError(w, r, fmt.Errorf("unable to read file %s, err: %d", filename, err)) + return + } + chains = append(chains, info) + } + + render.JSON(w, r, NewItemsResponse(chains)) +} + +func (a *AppServer) GetChainIDs(w http.ResponseWriter, r *http.Request) { + files, err := os.ReadDir(a.config.ChainRegistry) + if err != nil { + a.renderError(w, r, err) + return + } + + var chainIDs []string + for _, f := range files { + filename := filepath.Join(a.config.ChainRegistry, f.Name(), "chain.json") + info, err := readJSONFile(filename) + if err != nil { + a.renderError(w, r, fmt.Errorf("unable to read file %s, err: %d", filename, err)) + return + } + chainID, ok := info["chain_id"].(string) + if !ok { + a.renderError(w, r, fmt.Errorf("unable to get chain id for %s", filename)) + return + } + chainIDs = append(chainIDs, chainID) + } + + render.JSON(w, r, NewItemsResponse(chainIDs)) +} + +// GetChain handles the incoming request for a single chain given the chain id +// Note, we use chain-id instead of chain type, since it is expected, that there +// can be multiple chains of same type by unique chain ids +func (a *AppServer) GetChain(w http.ResponseWriter, r *http.Request) { + chainID := chi.URLParam(r, "chain") + + filename := filepath.Join(a.config.ChainRegistry, chainID, "chain.json") + + info, err := readJSONFile(filename) + if errors.Is(err, os.ErrNotExist) { + render.Render(w, r, ErrNotFound) + return + } else if err != nil { + a.renderError(w, r, fmt.Errorf("unable to read file %s, err: %d", filename, err)) + return + } + + render.JSON(w, r, info) +} + +func (a *AppServer) GetChainAssets(w http.ResponseWriter, r *http.Request) { + chainID := chi.URLParam(r, "chain") + + filename := filepath.Join(a.config.ChainRegistry, chainID, "assetlist.json") + + info, err := readJSONFile(filename) + if errors.Is(err, os.ErrNotExist) { + render.Render(w, r, ErrNotFound) + return + } else if err != nil { + a.renderError(w, r, fmt.Errorf("unable to read file %s, err: %d", filename, err)) + return + } + + render.JSON(w, r, info) +} + +func (a *AppServer) GetAllIBC(w http.ResponseWriter, r *http.Request) { + render.Render(w, r, ErrNotImplemented) +} + +func (a *AppServer) GetIBCChainsData(w http.ResponseWriter, r *http.Request) { + render.Render(w, r, ErrNotImplemented) +} + +func (a *AppServer) SetIBCChainsData(w http.ResponseWriter, r *http.Request) { + render.Render(w, r, ErrNotImplemented) +} + +func (a *AppServer) GetIBCChainsChannels(w http.ResponseWriter, r *http.Request) { + render.Render(w, r, ErrNotImplemented) +} + +func (a *AppServer) AddIBCChainChannel(w http.ResponseWriter, r *http.Request) { + render.Render(w, r, ErrNotImplemented) +} diff --git a/local-chain-registry/main.go b/local-chain-registry/main.go new file mode 100644 index 00000000..2c6ba806 --- /dev/null +++ b/local-chain-registry/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "github.com/urfave/cli" + "os" + "os/signal" + "runtime" + "syscall" + "time" +) + +func init() { + time.LoadLocation("UTC") // ensure all time is in UTC + runtime.GOMAXPROCS(runtime.NumCPU()) // set the core +} + +// NewApp creates a cli app that can be setup using args flags +// gracefull shutdown using termination signals +func NewApp() *cli.App { + conf := NewDefaultConfig() + app := cli.NewApp() + app.Name = Prog + app.Usage = Description + app.Version = Version + app.Flags = GetCommandLineOptions() + app.UsageText = "lcr [options]" + + app.Action = func(ctx *cli.Context) error { + if err := ParseCLIOptions(ctx, conf); err != nil { + return cli.NewExitError(err.Error(), 1) + } + + // Alternative Sentry Setup point where we can actually pass along configuration options + // SetupSentry(conf) + + server, err := NewAppServer(conf) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + + defer server.logger.Sync() + if err := server.Run(); err != nil { + return cli.NewExitError(err.Error(), 1) + } + + // Setup the termination signals + signalChannel := make(chan os.Signal) + signal.Notify(signalChannel, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + <-signalChannel + + return nil + } + + return app +} + +func main() { + app := NewApp() + app.Run(os.Args) +} diff --git a/local-chain-registry/models.go b/local-chain-registry/models.go new file mode 100644 index 00000000..edd888f1 --- /dev/null +++ b/local-chain-registry/models.go @@ -0,0 +1,19 @@ +package main + +type ItemsResponse struct { + Items interface{} `json:"items"` + TotalItems int `json:"total_items"` +} + +func NewItemsResponse(items interface{}) ItemsResponse { + if items == nil { + return ItemsResponse{[]string{}, 0} + } + + listItems, ok := items.([]interface{}) + if !ok { + return ItemsResponse{[]interface{}{items}, 1} + } + + return ItemsResponse{listItems, len(listItems)} +}