nomad-monorepo
nomad-monorepo copied to clipboard
design: unify configuration files into single source of truth
We currently have several sources of configuration for the various parts of the stack, it makes for a really annoying time when performing housekeeping tasks like updating contracts after a deployment.
Currently we have a web of dependencies starting with the rust configs and contract metadata outputted by nomad-deploy. Tools and applications that get configured based on the output of nomad-deploy
are:
-
nomad-sdk
-
nomad-monitor
-
Nomad GUI
-
Nomad Agent Deployment
Proposal:
Build a single config file that acts as the single source of truth for the entire stack. It should contain all necessary data about the shape of a deployment, including fields for:
- Contract addresses for each component of nomad-core (incl. proxies, upgrade beacon & Implementation)
- Home addresses on each network
- Replica addresses corresponding to each home
- GovernanceRouter addresses
- BridgeRouter addresses
- EthHelper addresses
- TokenRegistry addresses
- Governor Address(es)
- RecoveryManager Address(es)
- Updater and Watcher addresses for each domain
- Default public RPC Endpoints
- block times and consensus-related configuration
- fraud-proof period configuration
- default agent parameters (?)
- default agent signers (?)
These config files (one for each environment) would be persisted in the monorepo, referenced by the SDK, and any downstream packages that use the SDK will be automagically configured upon SDK updates.
default agent parameters (?) default agent signers (?)
wdym by this?
sketched a flow of what it would look like to automate this process
related: https://github.com/nomad-xyz/nomad-monorepo/issues/42
default agent parameters (?) default agent signers (?)
wdym by this?
Agent configs currently have placeholders for signer keys, want to ensure this new thing has parity there.
Took a crack at unifying this today, here's an example of that the mainnet config could look like:
{
"environment": "production",
"networks": ["ethereum", "moonbeam"],
"rpcs": {
"moonbeam": ["https://moonriver.api.onfinality.io/public"],
"ethereum": ["https://main-light.eth.linkpool.io/"]
},
"core": {
"ethereum": {
"name": "ethereum",
"domain": 6648936,
"connections": ["moonbeam"],
"consensus": {
"blockTime": 15,
"optimisticSeconds": 1800
},
"contracts": {
"upgradeBeaconController": "0xdB378579c2Af11817EEA21474A39F95B5b9DfD7e",
"xAppConnectionManager": "0xFe8874778f946Ac2990A29eba3CFd50760593B2F",
"updaterManager": "0x9272C9d5fa902Ef3804EC81e0333Ae420D57f715",
"governanceRouter": {
"implementation": "0x569D80f7FC17316B4C83f072b92EF37B72819DE0",
"proxy": "0x3009C99D370B780304D2098196f1EBF779a4777a",
"beacon": "0x67833a48b3F509d4252ac2c19cd604556eD6c981"
},
"home": {
"implementation": "0x8F184D6Aa1977fd2F9d9024317D0ea5cF5815b6f",
"proxy": "0x92d3404a7E6c91455BbD81475Cd9fAd96ACFF4c8",
"beacon": "0x063e871f8DB991CEAd34B557A00B157B360084cc"
},
"remoteReplicas": {
"moonbeam": {
"implementation": "0x67833a48b3F509d4252ac2c19cd604556eD6c981",
"proxy": "0x7F58bb8311DB968AB110889F2Dfa04ab7E8E831B",
"beacon": "0x3009C99D370B780304D2098196f1EBF779a4777a"
}
}
},
"governance": {
"governor": {
"address": "0x93277b8f5939975b9e6694d5fd2837143afbf68a",
"domain": 6648936
},
"recoveryManager": "0xda2f881f7f4e9d2b9559f97c7670472a85c1986a",
"recoveryTimelock": 86400
},
"updaters": ["0x71dC76C07E92325e7Cc09117AB94310Da63Fc2b9"],
"watchers": ["0x9782A3C8128f5D1BD3C9655d03181ba5b420883E"],
"agents": {
"rpcStyle": "ethereum",
"timelag": 20,
"db": "/path/to/db",
"logging": {
"level": "debug",
"fmt": "json"
},
"index": {
"from": 13983724,
"chunk": 2000
},
"updater": {
"enabled": true,
"signers": {
"transaction": {},
"attestation": ""
},
"interval": 5
},
"relayer": {
"enabled": true,
"signers": {
"transaction": {}
},
"interval": 10
},
"processor": {
"enabled": true,
"signers": {
"transaction": {}
},
"interval": 5
},
"watcher": {
"enabled": true,
"signers": {
"transaction": {},
"attestation": ""
},
"interval": 10
},
"kathy": {
"enabled": false,
"signers": {},
"interval": 100
}
}
},
"moonbeam": {
"name": "moonbeam",
"domain": 1650811245,
"connections": ["ethereum"],
"consensus": {
"blockTime": 15,
"optimisticSeconds": 1800
},
"contracts": {
"upgradeBeaconController": "0xdCe06fFE78AaAc2894109A56BA83C3C33B073F44",
"xAppConnectionManager": "0xdB378579c2Af11817EEA21474A39F95B5b9DfD7e",
"updaterManager": "0x2e09EdD238EeaEA6e4da705fbe5922B1979e03aC",
"governanceRouter": {
"implementation": "0x7B39dA90C9eAF87e85C553964BC3CBd674e7CCc1",
"proxy": "0x569D80f7FC17316B4C83f072b92EF37B72819DE0",
"beacon": "0xA84e233A12b36125A731e1362121d8d4ea030c91"
},
"home": {
"implementation": "0x9272C9d5fa902Ef3804EC81e0333Ae420D57f715",
"proxy": "0x8F184D6Aa1977fd2F9d9024317D0ea5cF5815b6f",
"beacon": "0xFe8874778f946Ac2990A29eba3CFd50760593B2F"
},
"remoteReplicas": {
"ethereum": {
"implementation": "0x7F58bb8311DB968AB110889F2Dfa04ab7E8E831B",
"proxy": "0x049b51e531Fd8f90da6d92EA83dC4125002F20EF",
"beacon": "0x0876dFe4AcAe0e1c0a43302716483f5752298b71"
}
}
},
"governance": {
"governor": {
"address": "0x93277b8f5939975b9e6694d5fd2837143afbf68a",
"domain": 6648936
},
"recoveryManager": "0xea24Ac04DEFb338CA8595C3750E20166F3b4998A",
"recoveryTimelock": 86400
},
"updaters": ["0x40FD91557B318BD5d52D12535795265c88702681"],
"watchers": ["0x297BBC2F2EAAEB17Ee53F514020bC8173F0570dC"],
"agents": {
"rpcStyle": "ethereum",
"timelag": 20,
"db": "/path/to/db",
"logging": {
"level": "debug",
"fmt": "json"
},
"index": {
"from": "171256",
"chunk": "2000"
},
"updater": {
"enabled": true,
"signers": {
"transaction": {},
"attestation": ""
},
"interval": 5
},
"relayer": {
"enabled": true,
"signers": {
"transaction": {}
},
"interval": 10
},
"processor": {
"enabled": true,
"signers": {
"transaction": {}
},
"interval": 5
},
"watcher": {
"enabled": true,
"signers": {
"transaction": {},
"attestation": ""
},
"interval": 10
},
"kathy": {
"enabled": false,
"signers": {},
"interval": 100
}
}
}
},
"bridge": {
"ethereum": {
"contracts": {
"bridgeRouter": {
"implementation": "0xD3dfD3eDe74E0DCEBC1AA685e151332857efCe2d",
"proxy": "0x88A69B4E698A4B090DF6CF5Bd7B2D47325Ad30A3",
"beacon": "0xB70588b1A51F847d13158ff18E9Cac861dF5Fb00"
},
"tokenRegistry": {
"implementation": "0xa7E4Fea3c1468D6C1A3A77e21e6e43Daed855C1b",
"proxy": "0x0A6f564C5c9BeBD66F1595f1B51D1F3de6Ef3b79",
"beacon": "0x4D5ff8A01ed833E11Aba43821D2881A5F2911F98"
},
"bridgeToken": {
"implementation": "0x4ad6444b55729f657A71a82a5448F85aC8aA47ba",
"proxy": "0x9f7eA856bA1fB88d35e000c45E75F134A756Ac4F",
"beacon": "0x8ca56E6235D83ff2F4E779F0b35A6c856d5a2fb2"
},
"ethHelper": "0x2d6775C1673d4cE55e1f827A0D53e62C43d1F304"
}
},
"moonbeam": {
"contracts": {
"bridgeRouter": {
"implementation": "0x4D5ff8A01ed833E11Aba43821D2881A5F2911F98",
"proxy": "0xD3dfD3eDe74E0DCEBC1AA685e151332857efCe2d",
"beacon": "0x0A6f564C5c9BeBD66F1595f1B51D1F3de6Ef3b79"
},
"tokenRegistry": {
"implementation": "0x8ca56E6235D83ff2F4E779F0b35A6c856d5a2fb2",
"proxy": "0xa7E4Fea3c1468D6C1A3A77e21e6e43Daed855C1b",
"beacon": "0x9f7eA856bA1fB88d35e000c45E75F134A756Ac4F"
},
"bridgeToken": {
"implementation": "0xEbB99A5B3021C86301dF241d3A32DBEBa5C15801",
"proxy": "0x4ad6444b55729f657A71a82a5448F85aC8aA47ba",
"beacon": "0x969d515486Ba6133400aC9C73298586426a090F5"
},
"ethHelper": "0xB70588b1A51F847d13158ff18E9Cac861dF5Fb00"
}
}
}
}
We should write a tool that validates this config:
- Ensure
core
andbridge
have keys that are present innetworks
- Ensure
core
andbridge
have no keys that are not present innetworks
- Ensure
core.<network>.contracts.remoteReplicas
has keys that are present incore.<network>.connections
- Ensure
core.<network>.contracts.remoteReplicas
has no keys that are not present incore.<network>.connections
- Warn if contract address between multiple networks are identical
- Warn if updater and watcher addresses between multiple networks are identical
Looks good. We also have chainID (https://chainlist.org/) and block explorer url in the GUI configs
Add a connections: []
field to the bridge config to allow for different connection graphs between bridge and core
Take two on unified config, splitting up the files on a logical Home
deployment.
https://gist.github.com/yourbuddyconner/f99bb628599a234c1f2d515fe579ed4e
Having a unified file configuration structure consumed by more than one software component (nomad-deploy, nomad-sdk, agent, ....) will introduce some software development and management challenges. I believe, each component should consume configurations from their own config files (i-e: agent.json, nomad-deploy.json,....) with the data structure of their choice. Even if we have duplicated values from one config files to the other, centralizing configuration can be handled at a higher level of our stack instead.
Below are the pros and cons of the current proposal:
Pros:
- Prevents configuration duplication
Cons:
- Creates a dependency on the configuration data structure across all stack components.
- Hard to know what config is consumed by which components without having to look at the code. Over time, we will have stale configuration because it will be hard to cleanup without risking to break other components.
- When updating configuration we will have to redeploy all components regardless if it's used by the component or not.
I think a solution would be to start refactoring the configuration structure of each component to easily add or remove a network and separate the component specific config. Once done, we should work on a solution to centralize these configurations by engineering or introducing an existing system.
Creates a dependency on the configuration data structure across all stack components.
this seems better to me than the current state, where there are >=1 struct per component.
other cons I agree with