Support agents with AWS keys (#361)

* Add some tracing to S3 reading and writing

* Add AgentAwsUser, refactor into aws dir

* Some signer changes

* Add AWS credentials to relayer external secret

* Redeploy dev

* upgrade agents

* Log when indexed messages

* Looks like relayer is all good

* Validator workin

* Needs cleanup but nearly there

* Get AWS credentials when needed

* cleaning

* mv some common fns

* Fix output env vars

* Final clean (I think)

* Better fix for annoying race cond

* prettier

* rm some tracing

* Add message_count to messages indexed log
pull/372/head
Trevor Porter 3 years ago committed by GitHub
parent 2d7234455d
commit 6593e47f1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      rust/abacus-base/src/contract_sync/mod.rs
  2. 6
      rust/config/dev/alfajores_config.json
  3. 6
      rust/config/dev/kovan_config.json
  4. 19
      rust/helm/abacus-agent/templates/checkpointer-external-secret.yaml
  5. 9
      rust/helm/abacus-agent/templates/checkpointer-statefulset.yaml
  6. 12
      rust/helm/abacus-agent/templates/external-secret.yaml
  7. 17
      rust/helm/abacus-agent/templates/kathy-external-secret.yaml
  8. 9
      rust/helm/abacus-agent/templates/kathy-statefulset.yaml
  9. 17
      rust/helm/abacus-agent/templates/relayer-external-secret.yaml
  10. 9
      rust/helm/abacus-agent/templates/relayer-statefulset.yaml
  11. 9
      rust/helm/abacus-agent/templates/validator-external-secret.yaml
  12. 10
      rust/helm/abacus-agent/templates/validator-statefulset.yaml
  13. 5
      typescript/infra/config/environments/dev/agent.ts
  14. 6
      typescript/infra/config/environments/dev/core/rust/alfajores_config.json
  15. 6
      typescript/infra/config/environments/dev/core/rust/kovan_config.json
  16. 40
      typescript/infra/config/environments/dev/core/verification/alfajores.json
  17. 40
      typescript/infra/config/environments/dev/core/verification/kovan.json
  18. 3
      typescript/infra/config/environments/test/agent.ts
  19. 23
      typescript/infra/scripts/deploy-agents.ts
  20. 18
      typescript/infra/src/agents/agent.ts
  21. 3
      typescript/infra/src/agents/aws/index.ts
  22. 278
      typescript/infra/src/agents/aws/key.ts
  23. 140
      typescript/infra/src/agents/aws/user.ts
  24. 104
      typescript/infra/src/agents/aws/validator-user.ts
  25. 25
      typescript/infra/src/agents/gcp.ts
  26. 124
      typescript/infra/src/agents/index.ts
  27. 182
      typescript/infra/src/config/agent.ts
  28. 44
      typescript/sdk/src/core/environments/dev.ts

@ -164,16 +164,17 @@ where
let candidate = from + chunk_size;
let to = min(tip, candidate);
let sorted_messages = indexer.fetch_sorted_messages(from, to).await?;
info!(
from = from,
to = to,
"[Messages]: indexing block heights {}...{}",
message_count = sorted_messages.len(),
"[Messages]: indexed block heights {}...{}",
from,
to
);
let sorted_messages = indexer.fetch_sorted_messages(from, to).await?;
// If no messages found, update last seen block and next height
// and continue
if sorted_messages.is_empty() {

@ -11,14 +11,14 @@
"url": ""
},
"addresses": {
"inbox": "0x0bA30c539f67027797209B314afE7A0390F536ab",
"validatorManager": "0x73B075e931B7dA89E00422Dbc8205369211C40e2"
"inbox": "0x125379056774C4246d016b2C58c6fbb80ab8829b",
"validatorManager": "0x63C950170534907d40B77ecF61bd07980B51f2a4"
}
}
},
"outbox": {
"addresses": {
"outbox": "0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559"
"outbox": "0x71f37188d27788ea86E12557edbc9b07E5BDdf03"
},
"domain": "1000",
"name": "alfajores",

@ -11,14 +11,14 @@
"url": ""
},
"addresses": {
"inbox": "0x65D545ae04b394fAfBE2EeD4036aBbc0485De8Db",
"validatorManager": "0x70a122D942B38EDC1C3E691aF19209CD17aDe555"
"inbox": "0x0fda1FDfEaDADdAE9C07C2bdA184935E317688C6",
"validatorManager": "0x78BEAd8669B1657BCA3F7aC202D8098f37Eb6c6A"
}
}
},
"outbox": {
"addresses": {
"outbox": "0xa42Eb261e74Fd92bC376Bb4CC19Ca51Ca7EBFf6C"
"outbox": "0x24D05ae06F3Ce4CF146958714c2B2FBE8B6a29c4"
},
"domain": "3000",
"name": "kovan",

@ -20,18 +20,29 @@ spec:
{{- include "abacus-agent.labels" . | nindent 10 }}
data:
{{- range .Values.abacus.checkpointer.signers }}
{{- if not (hasKey . "aws") }}
OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE: "hexKey"
{{- if eq .keyConfig.type "hexKey" }}
OPT_BASE_SIGNERS_{{ .name | upper }}_KEY: {{ printf "'{{ .%s_signer_key | toString }}'" .name }}
{{- end }}
{{- end }}
{{- if .Values.abacus.checkpointer.aws }}
AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }}
AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }}
{{- end }}
data:
{{- range .Values.abacus.checkpointer.signers }}
{{- if not (hasKey . "aws") }}
{{- if eq .keyConfig.type "hexKey" }}
- secretKey: {{ printf "%s_signer_key" .name }}
remoteRef:
key: {{ printf "abacus-%s-key-checkpointer" $.Values.abacus.runEnv }}
property: privateKey
{{- end }}
{{- end }}
{{- end }}
{{- if .Values.abacus.checkpointer.aws }}
- secretKey: aws_access_key_id
remoteRef:
key: {{ printf "abacus-%s-checkpointer-aws-access-key-id" .Values.abacus.runEnv }}
- secretKey: aws_secret_access_key
remoteRef:
key: {{ printf "abacus-%s-checkpointer-aws-secret-access-key" .Values.abacus.runEnv }}
{{- end }}
{{- end }}

@ -57,14 +57,7 @@ spec:
env:
{{- include "abacus-agent.config-env-vars" (dict "config" .Values.abacus.checkpointer.config "agent_name" "checkpointer") | indent 10 }}
{{- range .Values.abacus.checkpointer.signers }}
{{- if (hasKey . "aws") }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE
value: "aws"
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_ID
value: {{ .aws.keyId }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_REGION
value: {{ .aws.region }}
{{- end }}
{{- include "abacus-agent.config-env-vars" (dict "config" .keyConfig "agent_name" "base" "key_name_prefix" (printf "SIGNERS_%s_" (.name | upper))) | indent 10 }}
{{- end }}
{{- if .Values.abacus.tracing.uri }}
- name: OPT_BASE_TRACING_JAEGER_NAME

@ -18,10 +18,6 @@ spec:
labels:
{{- include "abacus-agent.labels" . | nindent 10 }}
data:
{{- if .Values.abacus.aws }}
AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }}
AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }}
{{- end }}
{{/* RPC URLs */}}
OPT_BASE_OUTBOX_CONNECTION_URL: {{ print "'{{ .home_rpc | toString }}'" }}
{{/*
@ -33,14 +29,6 @@ spec:
OPT_BASE_INBOXES_{{ .name | upper }}_CONNECTION_URL: {{ printf "'{{ .%s_rpc | toString }}'" .name }}
{{- end }}
data:
{{- if .Values.abacus.aws }}
- secretKey: aws_access_key_id
remoteRef:
key: {{ printf "%s-aws-access-key-id" .Values.abacus.runEnv }}
- secretKey: aws_secret_access_key
remoteRef:
key: {{ printf "%s-aws-secret-access-key" .Values.abacus.runEnv }}
{{- end }}
- secretKey: home_rpc
remoteRef:
key: {{ printf "%s-rpc-endpoint-%s" .Values.abacus.runEnv .Values.abacus.outboxChain.name }}

@ -20,18 +20,29 @@ spec:
{{- include "abacus-agent.labels" . | nindent 10 }}
data:
{{- range .Values.abacus.kathy.signers }}
{{- if not (hasKey . "aws") }}
OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE: "hexKey"
{{- if eq .keyConfig.type "hexKey" }}
OPT_BASE_SIGNERS_{{ .name | upper }}_KEY: {{ printf "'{{ .%s_signer_key | toString }}'" .name }}
{{- end }}
{{- end }}
{{- if .Values.abacus.kathy.aws }}
AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }}
AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }}
{{- end }}
data:
{{- range .Values.abacus.kathy.signers }}
{{- if not (hasKey . "aws") }}
{{- if eq .keyConfig.type "hexKey" }}
- secretKey: {{ printf "%s_signer_key" .name }}
remoteRef:
key: {{ printf "abacus-%s-key-kathy" $.Values.abacus.runEnv }}
property: privateKey
{{- end }}
{{- end }}
{{- if .Values.abacus.kathy.aws }}
- secretKey: aws_access_key_id
remoteRef:
key: {{ printf "abacus-%s-kathy-aws-access-key-id" .Values.abacus.runEnv }}
- secretKey: aws_secret_access_key
remoteRef:
key: {{ printf "abacus-%s-kathy-aws-secret-access-key" .Values.abacus.runEnv }}
{{- end }}
{{- end }}

@ -57,14 +57,7 @@ spec:
env:
{{- include "abacus-agent.config-env-vars" (dict "config" .Values.abacus.kathy.config "agent_name" "kathy") | indent 10 }}
{{- range .Values.abacus.kathy.signers }}
{{- if (hasKey . "aws") }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE
value: "aws"
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_ID
value: {{ .aws.keyId }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_REGION
value: {{ .aws.region }}
{{- end }}
{{- include "abacus-agent.config-env-vars" (dict "config" .keyConfig "agent_name" "base" "key_name_prefix" (printf "SIGNERS_%s_" (.name | upper))) | indent 10 }}
{{- end }}
{{- if .Values.abacus.tracing.uri }}
- name: OPT_BASE_TRACING_JAEGER_NAME

@ -20,18 +20,29 @@ spec:
{{- include "abacus-agent.labels" . | nindent 10 }}
data:
{{- range .Values.abacus.relayer.signers }}
{{- if not (hasKey . "aws") }}
OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE: "hexKey"
{{- if eq .keyConfig.type "hexKey" }}
OPT_BASE_SIGNERS_{{ .name | upper }}_KEY: {{ printf "'{{ .%s_signer_key | toString }}'" .name }}
{{- end }}
{{- end }}
{{- if .Values.abacus.relayer.aws }}
AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }}
AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }}
{{- end }}
data:
{{- range .Values.abacus.relayer.signers }}
{{- if not (hasKey . "aws") }}
{{- if eq .keyConfig.type "hexKey" }}
- secretKey: {{ printf "%s_signer_key" .name }}
remoteRef:
key: {{ printf "abacus-%s-key-relayer" $.Values.abacus.runEnv }}
property: privateKey
{{- end }}
{{- end }}
{{- if .Values.abacus.relayer.aws }}
- secretKey: aws_access_key_id
remoteRef:
key: {{ printf "abacus-%s-relayer-aws-access-key-id" .Values.abacus.runEnv }}
- secretKey: aws_secret_access_key
remoteRef:
key: {{ printf "abacus-%s-relayer-aws-secret-access-key" .Values.abacus.runEnv }}
{{- end }}
{{- end }}

@ -57,14 +57,7 @@ spec:
env:
{{- include "abacus-agent.config-env-vars" (dict "config" .Values.abacus.relayer.config "agent_name" "relayer") | indent 10 }}
{{- range .Values.abacus.relayer.signers }}
{{- if (hasKey . "aws") }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE
value: "aws"
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_ID
value: {{ .aws.keyId }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_REGION
value: {{ .aws.region }}
{{- end }}
{{- include "abacus-agent.config-env-vars" (dict "config" .keyConfig "agent_name" "base" "key_name_prefix" (printf "SIGNERS_%s_" (.name | upper))) | indent 10 }}
{{- end }}
{{- if .Values.abacus.tracing.uri }}
- name: OPT_BASE_TRACING_JAEGER_NAME

@ -22,9 +22,10 @@ spec:
{{ $index := 0 }}
{{- range .Values.abacus.validator.configs }}
validator-{{ $index }}.env: |
OPT_VALIDATOR_VALIDATOR_TYPE="hexKey"
{{- if eq .validator.type "hexKey" }}
OPT_VALIDATOR_VALIDATOR_KEY={{ printf "'{{ .signer_key_%d | toString }}'" $index }}
{{- if eq .checkpointSyncer.type "s3" }}
{{- end }}
{{- if or (eq .checkpointSyncer.type "s3") $.Values.abacus.aws }}
AWS_ACCESS_KEY_ID={{ printf "'{{ .aws_access_key_id_%d | toString }}'" $index }}
AWS_SECRET_ACCESS_KEY={{ printf "'{{ .aws_secret_access_key_%d | toString }}'" $index }}
{{- end }}
@ -33,11 +34,13 @@ spec:
data:
{{ $index := 0 }}
{{- range .Values.abacus.validator.configs }}
{{- if eq .validator.type "hexKey" }}
- secretKey: signer_key_{{ $index }}
remoteRef:
key: {{ printf "abacus-%s-key-%s-validator-%d" $.Values.abacus.runEnv $.Values.abacus.outboxChain.name $index }}
property: privateKey
{{- if eq .checkpointSyncer.type "s3" }}
{{- end }}
{{- if or (eq .checkpointSyncer.type "s3") $.Values.abacus.aws }}
- secretKey: aws_access_key_id_{{ $index }}
remoteRef:
key: {{ printf "abacus-%s-%s-validator-%d-aws-access-key-id" $.Values.abacus.runEnv $.Values.abacus.outboxChain.name $index }}

@ -64,16 +64,6 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
{{- range .Values.abacus.validator.signers }}
{{- if (hasKey . "aws") }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_TYPE
value: "aws"
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_ID
value: {{ .aws.keyId }}
- name: OPT_BASE_SIGNERS_{{ .name | upper }}_REGION
value: {{ .aws.region }}
{{- end }}
{{- end }}
{{- if .Values.abacus.tracing.uri }}
- name: OPT_BASE_TRACING_JAEGER_NAME
value: {{ include "abacus-agent.fullname" . }}-validator

@ -1,6 +1,6 @@
import { AgentConfig } from '../../../src/config';
import { ENVIRONMENTS_ENUM } from '../../../src/config/environment';
import { DevNetworks } from './domains';
import { DevNetworks, domainNames } from './domains';
import { validators } from './validators';
export const agent: AgentConfig<DevNetworks> = {
@ -9,8 +9,9 @@ export const agent: AgentConfig<DevNetworks> = {
runEnv: ENVIRONMENTS_ENUM.Dev,
docker: {
repo: 'gcr.io/abacus-labs-dev/abacus-agent',
tag: '8ee03796167efdf80daf6d7fc4673de47f8657f9',
tag: 'f30aa0a68a645bf966638e145ba8a4e15f80280e',
},
domainNames,
validatorSets: validators,
validator: {
default: {

@ -11,14 +11,14 @@
"url": ""
},
"addresses": {
"inbox": "0x0bA30c539f67027797209B314afE7A0390F536ab",
"validatorManager": "0x73B075e931B7dA89E00422Dbc8205369211C40e2"
"inbox": "0x125379056774C4246d016b2C58c6fbb80ab8829b",
"validatorManager": "0x63C950170534907d40B77ecF61bd07980B51f2a4"
}
}
},
"outbox": {
"addresses": {
"outbox": "0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559"
"outbox": "0x71f37188d27788ea86E12557edbc9b07E5BDdf03"
},
"domain": "1000",
"name": "alfajores",

@ -11,14 +11,14 @@
"url": ""
},
"addresses": {
"inbox": "0x65D545ae04b394fAfBE2EeD4036aBbc0485De8Db",
"validatorManager": "0x70a122D942B38EDC1C3E691aF19209CD17aDe555"
"inbox": "0x0fda1FDfEaDADdAE9C07C2bdA184935E317688C6",
"validatorManager": "0x78BEAd8669B1657BCA3F7aC202D8098f37Eb6c6A"
}
}
},
"outbox": {
"addresses": {
"outbox": "0xa42Eb261e74Fd92bC376Bb4CC19Ca51Ca7EBFf6C"
"outbox": "0x24D05ae06F3Ce4CF146958714c2B2FBE8B6a29c4"
},
"domain": "3000",
"name": "kovan",

@ -1,13 +1,13 @@
[
{
"name": "UpgradeBeaconController",
"address": "0xeCa505b0777152e68776f1C5a875EE4dFbf59b94",
"address": "0x1f15e8b9C5ABf2D6f7f9033f95Fcd66b90c8aa27",
"constructorArguments": [],
"isProxy": false
},
{
"name": "OutboxValidatorManager",
"address": "0xf78caF314423c749681D3C7B08FCd66d7a4BA683",
"address": "0xCfC4862Df4544923265873f2d288AcEd9919d679",
"constructorArguments": [
1000,
[
@ -21,43 +21,43 @@
},
{
"name": "Outbox Implementation",
"address": "0x63C950170534907d40B77ecF61bd07980B51f2a4",
"address": "0x3219827B9D028Cec62b996147076b4cFCeac282a",
"constructorArguments": [1000],
"isProxy": false
},
{
"name": "Outbox UpgradeBeacon",
"address": "0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68",
"address": "0xe0B98662f15B12E4aCfd950cBe53FAad4b0C0b98",
"constructorArguments": [
"0x63C950170534907d40B77ecF61bd07980B51f2a4",
"0xeCa505b0777152e68776f1C5a875EE4dFbf59b94"
"0x3219827B9D028Cec62b996147076b4cFCeac282a",
"0x1f15e8b9C5ABf2D6f7f9033f95Fcd66b90c8aa27"
],
"isProxy": false
},
{
"name": "Outbox Proxy",
"address": "0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559",
"address": "0x71f37188d27788ea86E12557edbc9b07E5BDdf03",
"constructorArguments": [
"0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68",
"0xc4d66de8000000000000000000000000f78caf314423c749681d3c7b08fcd66d7a4ba683"
"0xe0B98662f15B12E4aCfd950cBe53FAad4b0C0b98",
"0xc4d66de8000000000000000000000000cfc4862df4544923265873f2d288aced9919d679"
],
"isProxy": true
},
{
"name": "InterchainGasPaymaster",
"address": "0x125379056774C4246d016b2C58c6fbb80ab8829b",
"address": "0x63BDfEF81688ddd5f135D8a8680f942205E030c2",
"constructorArguments": [],
"isProxy": false
},
{
"name": "XAppConnectionManager",
"address": "0xB01b98d52767280934e98cf5F7B57DaC434473B9",
"name": "AbacusConnectionManager",
"address": "0xD57Ff4d8d3872322bD628ed8f9C6bB3617A5ef13",
"constructorArguments": [],
"isProxy": false
},
{
"name": "InboxValidatorManager",
"address": "0x70a122D942B38EDC1C3E691aF19209CD17aDe555",
"address": "0x78BEAd8669B1657BCA3F7aC202D8098f37Eb6c6A",
"constructorArguments": [
3000,
[
@ -71,25 +71,25 @@
},
{
"name": "Inbox Implementation",
"address": "0x5908D50cC36d40ABe284BAb9f43d290D7B083020",
"address": "0x9632B89f335F6f48d8ea32fC95ccc2eA36B33570",
"constructorArguments": [1000],
"isProxy": false
},
{
"name": "Inbox UpgradeBeacon",
"address": "0xDE9b9CbAaB774E9DCC4Af1bdd2b510d97dC1705c",
"address": "0xc8571652Fe648d2873Af278C4982Ecd07dBb0870",
"constructorArguments": [
"0x5908D50cC36d40ABe284BAb9f43d290D7B083020",
"0xeCa505b0777152e68776f1C5a875EE4dFbf59b94"
"0x9632B89f335F6f48d8ea32fC95ccc2eA36B33570",
"0x1f15e8b9C5ABf2D6f7f9033f95Fcd66b90c8aa27"
],
"isProxy": false
},
{
"name": "Inbox Proxy",
"address": "0x65D545ae04b394fAfBE2EeD4036aBbc0485De8Db",
"address": "0x0fda1FDfEaDADdAE9C07C2bdA184935E317688C6",
"constructorArguments": [
"0xDE9b9CbAaB774E9DCC4Af1bdd2b510d97dC1705c",
"0xe7e7a7b70000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000070a122d942b38edc1c3e691af19209cd17ade55500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"0xc8571652Fe648d2873Af278C4982Ecd07dBb0870",
"0xe7e7a7b70000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000078bead8669b1657bca3f7ac202d8098f37eb6c6a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
],
"isProxy": true
}

@ -1,13 +1,13 @@
[
{
"name": "UpgradeBeaconController",
"address": "0x943bA1d4c8A20dd3c568cE16FbaB297F7d3178C3",
"address": "0x74A77C554685651F7dB3784359E1436604794438",
"constructorArguments": [],
"isProxy": false
},
{
"name": "OutboxValidatorManager",
"address": "0x81485d5C439a393375A65b9060aaD791aCD90439",
"address": "0x3f0e80FfACE85DC573F57b2f552f6F98Fc5984c4",
"constructorArguments": [
3000,
[
@ -21,43 +21,43 @@
},
{
"name": "Outbox Implementation",
"address": "0xB5a43311274bEb8478Bd5909e08Fd940FC2A75C9",
"address": "0xbA2C920a863122a42c249d89D5954506F00513C3",
"constructorArguments": [3000],
"isProxy": false
},
{
"name": "Outbox UpgradeBeacon",
"address": "0xB4dD7B062A927ff166312a9B49975184965950f7",
"address": "0xE8bF352efbC50754442095a04e81A5141fe75e16",
"constructorArguments": [
"0xB5a43311274bEb8478Bd5909e08Fd940FC2A75C9",
"0x943bA1d4c8A20dd3c568cE16FbaB297F7d3178C3"
"0xbA2C920a863122a42c249d89D5954506F00513C3",
"0x74A77C554685651F7dB3784359E1436604794438"
],
"isProxy": false
},
{
"name": "Outbox Proxy",
"address": "0xa42Eb261e74Fd92bC376Bb4CC19Ca51Ca7EBFf6C",
"address": "0x24D05ae06F3Ce4CF146958714c2B2FBE8B6a29c4",
"constructorArguments": [
"0xB4dD7B062A927ff166312a9B49975184965950f7",
"0xc4d66de800000000000000000000000081485d5c439a393375a65b9060aad791acd90439"
"0xE8bF352efbC50754442095a04e81A5141fe75e16",
"0xc4d66de80000000000000000000000003f0e80fface85dc573f57b2f552f6f98fc5984c4"
],
"isProxy": true
},
{
"name": "InterchainGasPaymaster",
"address": "0x93Ea081a3F8c5e34F51FA4d164a43d2925e24238",
"address": "0x33bE4eAa1A7c04E6Bba63e4F9cfff1eb98CF59F0",
"constructorArguments": [],
"isProxy": false
},
{
"name": "XAppConnectionManager",
"address": "0x9318D51F02EcAf21682d546D8260c96580D4b49e",
"name": "AbacusConnectionManager",
"address": "0x3c3b0367A960a6ea1cDe58Bb8A7E33bfC38554D3",
"constructorArguments": [],
"isProxy": false
},
{
"name": "InboxValidatorManager",
"address": "0x73B075e931B7dA89E00422Dbc8205369211C40e2",
"address": "0x63C950170534907d40B77ecF61bd07980B51f2a4",
"constructorArguments": [
1000,
[
@ -71,25 +71,25 @@
},
{
"name": "Inbox Implementation",
"address": "0x84710af0473Fe6672C9C9e7B2d8B16eCE6D1b581",
"address": "0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68",
"constructorArguments": [3000],
"isProxy": false
},
{
"name": "Inbox UpgradeBeacon",
"address": "0x75f3E7BB237cAEADdf8BA2B22f93884eD3923B8C",
"address": "0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559",
"constructorArguments": [
"0x84710af0473Fe6672C9C9e7B2d8B16eCE6D1b581",
"0x943bA1d4c8A20dd3c568cE16FbaB297F7d3178C3"
"0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68",
"0x74A77C554685651F7dB3784359E1436604794438"
],
"isProxy": false
},
{
"name": "Inbox Proxy",
"address": "0x0bA30c539f67027797209B314afE7A0390F536ab",
"address": "0x125379056774C4246d016b2C58c6fbb80ab8829b",
"constructorArguments": [
"0x75f3E7BB237cAEADdf8BA2B22f93884eD3923B8C",
"0xe7e7a7b700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000073b075e931b7da89e00422dbc8205369211c40e200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559",
"0xe7e7a7b700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000063c950170534907d40b77ecf61bd07980b51f2a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
],
"isProxy": true
}

@ -1,4 +1,4 @@
import { TestNetworks } from './domains';
import { domainNames, TestNetworks } from './domains';
import { validators } from './validators';
import { AgentConfig } from '../../../src/config';
import { ENVIRONMENTS_ENUM } from '../../../src/config/environment';
@ -11,6 +11,7 @@ export const agent: AgentConfig<TestNetworks> = {
repo: 'gcr.io/abacus-labs-dev/abacus-agent',
tag: '8852db3d88e87549269487da6da4ea5d67fdbfed',
},
domainNames,
validatorSets: validators,
validator: {
default: {

@ -6,16 +6,19 @@ async function deploy() {
const environment = await getEnvironment();
const agentConfig = await getAgentConfig(environment);
const domainNames = await getDomainNames(environment);
await Promise.all(
domainNames.map((name) => {
return runAgentHelmCommand(
HelmCommand.InstallOrUpgrade,
agentConfig,
name,
domainNames,
);
}),
);
// We intentionally do not Promise.all here to avoid race conditions that could result
// in attempting to create a user or key multiple times. This was found to happen in
// situations where agents for different domains would share the same AWS user or
// AWS KMS key.
for (const name of domainNames) {
await runAgentHelmCommand(
HelmCommand.InstallOrUpgrade,
agentConfig,
name,
domainNames,
);
}
}
deploy().then(console.log).catch(console.error);

@ -1,7 +1,23 @@
import { KEY_ROLE_ENUM } from '../agents';
export abstract class AgentKey {
abstract get identifier(): string;
abstract get address(): string;
abstract get credentialsAsHelmValue(): any;
abstract fetch(): Promise<void>;
}
export function isValidatorKey(role: string) {
return role === KEY_ROLE_ENUM.Validator;
}
export function identifier(
environment: string,
role: string,
chainName: string,
index: number | undefined,
) {
return isValidatorKey(role)
? `abacus-${environment}-key-${chainName}-${role}-${index}`
: `abacus-${environment}-key-${role}`;
}

@ -0,0 +1,3 @@
export { AgentAwsKey } from './key';
export { AgentAwsUser } from './user';
export { ValidatorAgentAwsUser } from './validator-user';

@ -1,6 +1,6 @@
import { ChainName } from '@abacus-network/sdk';
import { getSecretAwsCredentials, KEY_ROLE_ENUM } from '../agents';
import { AgentConfig } from '../../src/config/agent';
import { KEY_ROLE_ENUM } from '..';
import { AgentConfig, AwsKeyConfig, KeyType } from '../../config/agent';
import {
CreateAliasCommand,
CreateKeyCommand,
@ -11,23 +11,12 @@ import {
KMSClient,
ListAliasesCommand,
OriginType,
PutKeyPolicyCommand,
UpdateAliasCommand,
} from '@aws-sdk/client-kms';
import {
IAMClient,
CreateAccessKeyCommand,
CreateUserCommand,
ListUsersCommand,
} from '@aws-sdk/client-iam';
import {
S3Client,
CreateBucketCommand,
ListBucketsCommand,
PutBucketPolicyCommand,
} from '@aws-sdk/client-s3';
import { getEthereumAddress } from '../utils/utils';
import { AgentKey } from './agent';
import { gcpSecretExists, setGCPSecret } from '../utils/gcloud';
import { identifier } from '../agent';
import { getEthereumAddress, sleep } from '../../utils/utils';
import { AgentKey } from '../agent';
interface UnfetchedKey {
fetched: false;
@ -40,194 +29,26 @@ interface FetchedKey {
type RemoteKey = UnfetchedKey | FetchedKey;
export class ValidatorAgentAwsUser<Networks extends ChainName> {
private adminIamClient: IAMClient;
private adminS3Client: S3Client;
private _arn: string | undefined;
constructor(
public readonly environment: string,
public readonly chainName: Networks,
public readonly index: number,
public readonly region: string,
public readonly bucket: string,
) {
this.adminIamClient = new IAMClient({ region });
this.adminS3Client = new S3Client({ region });
}
// Creates the AWS user if it doesn't exist.
// Gets API access keys and saves them in GCP secret manager if they do not already exist.
// Populates `this._arn` with the ARN of the user.
async createIfNotExists() {
const cmd = new ListUsersCommand({});
const result = await this.adminIamClient.send(cmd);
const match = result.Users?.find((user) => user.UserName === this.userName);
if (match) {
this._arn = match?.Arn;
} else {
this._arn = await this.create();
}
if (!(await this.accessKeysExist())) {
await this.createAndSaveAccessKey();
}
}
// Creates the AWS user
async create() {
const cmd = new CreateUserCommand({
UserName: this.userName,
Tags: this.awsTags,
});
const response = await this.adminIamClient.send(cmd);
if (!response.User) {
throw Error('Expected User');
}
return response.User.Arn;
}
async accessKeysExist(): Promise<boolean> {
const accessKeyIdExists = await gcpSecretExists(this.accessKeyIdSecretName);
const secretAccessKeyExists = await gcpSecretExists(
this.secretAccessKeySecretName,
);
return accessKeyIdExists && secretAccessKeyExists;
}
async createAndSaveAccessKey() {
const cmd = new CreateAccessKeyCommand({
UserName: this.userName,
});
const { AccessKey: accessKey } = await this.adminIamClient.send(cmd);
if (!accessKey || !accessKey.AccessKeyId || !accessKey.SecretAccessKey) {
throw Error('Expected fully defined access key');
}
await setGCPSecret(
this.accessKeyIdSecretName,
accessKey.AccessKeyId,
this.tags,
);
await setGCPSecret(
this.secretAccessKeySecretName,
accessKey.SecretAccessKey,
this.tags,
);
}
async createBucketIfNotExists() {
if (!(await this.bucketExists())) {
await this.createBucket();
}
await this.putBucketAccessPolicy();
}
async bucketExists(): Promise<boolean> {
const cmd = new ListBucketsCommand({});
const result = await this.adminS3Client.send(cmd);
return (
result.Buckets?.find((bucket) => bucket.Name === this.bucket) !==
undefined
);
}
async createBucket() {
const cmd = new CreateBucketCommand({
Bucket: this.bucket,
});
await this.adminS3Client.send(cmd);
}
async putBucketAccessPolicy() {
const policy = {
Statement: [
// Make the bucket publicly readable
{
Effect: 'Allow',
Principal: '*',
Action: ['s3:GetObject', 's3:ListBucket'],
Resource: [
`arn:aws:s3:::${this.bucket}`,
`arn:aws:s3:::${this.bucket}/*`,
],
},
// Allow the user to modify objects
{
Effect: 'Allow',
Principal: {
AWS: this.arn,
},
Action: ['s3:DeleteObject', 's3:PutObject'],
Resource: `arn:aws:s3:::${this.bucket}/*`,
},
],
};
const cmd = new PutBucketPolicyCommand({
Bucket: this.bucket,
Policy: JSON.stringify(policy),
});
await this.adminS3Client.send(cmd);
}
get awsTags() {
const tags = this.tags;
return Object.keys(tags).map((key) => ({
Key: key,
Value: tags[key],
}));
}
get tags(): Record<string, string> {
return {
environment: this.environment,
role: this.role,
chain: this.chainName,
index: this.index!.toString(),
};
}
get role() {
return KEY_ROLE_ENUM.Validator;
}
get userName() {
return `abacus-${this.environment}-${this.chainName}-${this.role}-${this.index}`;
}
get accessKeyIdSecretName() {
return `${this.userName}-aws-access-key-id`;
}
get secretAccessKeySecretName() {
return `${this.userName}-aws-secret-access-key`;
}
get arn(): string {
if (!this._arn) {
throw Error('ARN is undefined');
}
return this._arn;
}
}
export class AgentAwsKey<Networks extends ChainName> extends AgentKey {
private environment: string;
private agentConfig: AgentConfig<Networks>;
private client: KMSClient | undefined;
private region: string;
public remoteKey: RemoteKey = { fetched: false };
constructor(
agentConfig: AgentConfig<Networks>,
public readonly role: KEY_ROLE_ENUM,
public readonly chainName: Networks,
public readonly role: KEY_ROLE_ENUM,
public readonly index?: number,
) {
super();
if (!agentConfig.aws) {
throw new Error('No AWS env vars set');
throw new Error('Not configured as AWS');
}
if (role === KEY_ROLE_ENUM.Validator && index === undefined) {
throw new Error('Expected index for validator key');
}
this.environment = agentConfig.environment;
this.agentConfig = agentConfig;
this.region = agentConfig.aws.region;
}
@ -235,24 +56,26 @@ export class AgentAwsKey<Networks extends ChainName> extends AgentKey {
if (this.client) {
return this.client;
}
const awsCredentials = await getSecretAwsCredentials(this.agentConfig);
this.client = new KMSClient({
region: this.region,
credentials: awsCredentials,
});
return this.client;
}
get identifier() {
return `alias/${this.environment}-${this.chainName}-${this.role}`;
return `alias/${identifier(
this.environment,
this.role,
this.chainName,
this.index,
)}`;
}
get credentialsAsHelmValue() {
get keyConfig(): AwsKeyConfig {
return {
aws: {
keyId: this.identifier,
region: this.region,
},
type: KeyType.Aws,
id: this.identifier,
region: this.region,
};
}
@ -270,6 +93,63 @@ export class AgentAwsKey<Networks extends ChainName> extends AgentKey {
};
}
async createIfNotExists() {
let keyId = await this.getId();
// If it doesn't exist, create it
if (!keyId) {
this.create();
// It can take a moment for the change to propagate
await sleep(1000);
}
}
// Allows the `userArn` to use the key
async putKeyPolicy(userArn: string) {
const client = await this.getClient();
const policy = {
Version: '2012-10-17',
Id: 'key-default-1',
Statement: [
{
Sid: 'Enable IAM User Permissions',
Effect: 'Allow',
Principal: {
AWS: 'arn:aws:iam::625457692493:root',
},
Action: 'kms:*',
Resource: '*',
},
{
Effect: 'Allow',
Principal: {
AWS: userArn,
},
Action: ['kms:GetPublicKey', 'kms:Sign'],
Resource: '*',
},
],
};
const cmd = new PutKeyPolicyCommand({
KeyId: await this.getId(),
Policy: JSON.stringify(policy),
PolicyName: 'default', // This is the only accepted name
});
await client.send(cmd);
}
// Gets the Key's ID if it exists, undefined otherwise
async getId() {
const client = await this.getClient();
const listAliasResponse = await client.send(
new ListAliasesCommand({ Limit: 100 }),
);
const match = listAliasResponse.Aliases!.find(
(_) => _.AliasName === this.identifier,
);
return match?.TargetKeyId;
}
async create() {
this._create(false);
}

@ -0,0 +1,140 @@
import { ChainName } from '@abacus-network/sdk';
import {
IAMClient,
CreateAccessKeyCommand,
CreateUserCommand,
ListUsersCommand,
} from '@aws-sdk/client-iam';
import { KEY_ROLE_ENUM } from '../../agents';
import { AgentConfig } from '../../config';
import {
fetchGCPSecret,
gcpSecretExists,
setGCPSecret,
} from '../../utils/gcloud';
import { AgentAwsKey } from './key';
export class AgentAwsUser<Networks extends ChainName> {
private adminIamClient: IAMClient;
private _arn: string | undefined;
constructor(
public readonly environment: string,
public readonly chainName: Networks,
public readonly role: KEY_ROLE_ENUM,
public readonly region: string,
) {
this.adminIamClient = new IAMClient({ region });
}
// Creates the AWS user if it doesn't exist.
// Gets API access keys and saves them in GCP secret manager if they do not already exist.
// Populates `this._arn` with the ARN of the user.
async createIfNotExists() {
const cmd = new ListUsersCommand({});
const result = await this.adminIamClient.send(cmd);
const match = result.Users?.find((user) => user.UserName === this.userName);
if (match) {
this._arn = match?.Arn;
} else {
this._arn = await this.create();
}
if (!(await this.accessKeysExist())) {
await this.createAndSaveAccessKey();
}
}
// Creates the AWS user
async create() {
const cmd = new CreateUserCommand({
UserName: this.userName,
Tags: this.awsTags,
});
const response = await this.adminIamClient.send(cmd);
if (!response.User) {
throw Error('Expected User');
}
return response.User.Arn;
}
async accessKeysExist(): Promise<boolean> {
const accessKeyIdExists = await gcpSecretExists(this.accessKeyIdSecretName);
const secretAccessKeyExists = await gcpSecretExists(
this.secretAccessKeySecretName,
);
return accessKeyIdExists && secretAccessKeyExists;
}
async getAccessKeys(): Promise<{
accessKeyId: string;
secretAccessKey: string;
}> {
return {
accessKeyId: await fetchGCPSecret(this.accessKeyIdSecretName, false),
secretAccessKey: await fetchGCPSecret(
this.secretAccessKeySecretName,
false,
),
};
}
async createAndSaveAccessKey() {
const cmd = new CreateAccessKeyCommand({
UserName: this.userName,
});
const { AccessKey: accessKey } = await this.adminIamClient.send(cmd);
if (!accessKey || !accessKey.AccessKeyId || !accessKey.SecretAccessKey) {
throw Error('Expected fully defined access key');
}
await setGCPSecret(
this.accessKeyIdSecretName,
accessKey.AccessKeyId,
this.tags,
);
await setGCPSecret(
this.secretAccessKeySecretName,
accessKey.SecretAccessKey,
this.tags,
);
}
key(agentConfig: AgentConfig<Networks>): AgentAwsKey<Networks> {
return new AgentAwsKey<Networks>(agentConfig, this.chainName, this.role);
}
get awsTags() {
const tags = this.tags;
return Object.keys(tags).map((key) => ({
Key: key,
Value: tags[key],
}));
}
get tags(): Record<string, string> {
return {
environment: this.environment,
role: this.role,
chain: this.chainName,
};
}
get userName() {
return `abacus-${this.environment}-${this.role}`;
}
get accessKeyIdSecretName() {
return `${this.userName}-aws-access-key-id`;
}
get secretAccessKeySecretName() {
return `${this.userName}-aws-secret-access-key`;
}
get arn(): string {
if (!this._arn) {
throw Error('ARN is undefined');
}
return this._arn;
}
}

@ -0,0 +1,104 @@
import { ChainName } from '@abacus-network/sdk';
import {
S3Client,
CreateBucketCommand,
ListBucketsCommand,
PutBucketPolicyCommand,
} from '@aws-sdk/client-s3';
import { KEY_ROLE_ENUM } from '../../agents';
import { AgentConfig } from '../../config';
import { AgentAwsKey } from './key';
import { AgentAwsUser } from './user';
export class ValidatorAgentAwsUser<
Networks extends ChainName,
> extends AgentAwsUser<Networks> {
private adminS3Client: S3Client;
constructor(
environment: string,
chainName: Networks,
public readonly index: number,
region: string,
public readonly bucket: string,
) {
super(environment, chainName, KEY_ROLE_ENUM.Validator, region);
this.adminS3Client = new S3Client({ region });
}
async createBucketIfNotExists() {
if (!(await this.bucketExists())) {
await this.createBucket();
}
await this.putBucketAccessPolicy();
}
async bucketExists(): Promise<boolean> {
const cmd = new ListBucketsCommand({});
const result = await this.adminS3Client.send(cmd);
return (
result.Buckets?.find((bucket) => bucket.Name === this.bucket) !==
undefined
);
}
async createBucket() {
const cmd = new CreateBucketCommand({
Bucket: this.bucket,
});
await this.adminS3Client.send(cmd);
}
async putBucketAccessPolicy() {
const policy = {
Statement: [
// Make the bucket publicly readable
{
Effect: 'Allow',
Principal: '*',
Action: ['s3:GetObject', 's3:ListBucket'],
Resource: [
`arn:aws:s3:::${this.bucket}`,
`arn:aws:s3:::${this.bucket}/*`,
],
},
// Allow the user to modify objects
{
Effect: 'Allow',
Principal: {
AWS: this.arn,
},
Action: ['s3:DeleteObject', 's3:PutObject'],
Resource: `arn:aws:s3:::${this.bucket}/*`,
},
],
};
const cmd = new PutBucketPolicyCommand({
Bucket: this.bucket,
Policy: JSON.stringify(policy),
});
await this.adminS3Client.send(cmd);
}
key(agentConfig: AgentConfig<Networks>): AgentAwsKey<Networks> {
return new AgentAwsKey<Networks>(
agentConfig,
this.chainName,
this.role,
this.index,
);
}
get tags(): Record<string, string> {
return {
environment: this.environment,
role: this.role,
chain: this.chainName,
index: this.index!.toString(),
};
}
get userName() {
return `abacus-${this.environment}-${this.chainName}-${this.role}-${this.index}`;
}
}

@ -1,13 +1,9 @@
import { Wallet } from 'ethers';
import { KEY_ROLES, KEY_ROLE_ENUM } from '../agents';
import { execCmd, include, strip0x } from '../utils/utils';
import { AgentKey } from './agent';
import { execCmd, include } from '../utils/utils';
import { AgentKey, isValidatorKey, identifier } from './agent';
import { fetchGCPSecret, setGCPSecret } from '../utils/gcloud';
function isValidatorKey(role: string) {
return role === KEY_ROLE_ENUM.Validator;
}
// This is the type for how the keys are persisted in GCP
export interface SecretManagerPersistedKeys {
privateKey: string;
@ -24,17 +20,6 @@ interface KeyAsAddress {
address: string;
}
function identifier(
environment: string,
role: string,
chainName: string,
index: number | undefined,
) {
return isValidatorKey(role)
? `abacus-${environment}-key-${chainName}-${role}-${index}`
: `abacus-${environment}-key-${role}`;
}
interface UnfetchedKey {
fetched: false;
}
@ -91,12 +76,6 @@ export class AgentGCPKey extends AgentKey {
return identifier(this.environment, this.role, this.chainName, this.index);
}
get credentialsAsHelmValue() {
return {
hexKey: strip0x(this.privateKey),
};
}
// The identifier for this key within a set of keys for an enrivonment
get memoryKeyIdentifier() {
return memoryKeyIdentifier(this.role, this.chainName, this.index);

@ -6,8 +6,9 @@ import { fetchGCPSecret } from '../utils/gcloud';
import { HelmCommand, helmifyValues } from '../utils/helm';
import { ensure0x, execCmd, strip0x } from '../utils/utils';
import { AgentGCPKey, fetchAgentGCPKeys, memoryKeyIdentifier } from './gcp';
import { AgentAwsKey } from './aws';
import { ChainAgentConfig } from '../config/agent';
import { AgentAwsKey } from './aws/key';
import { ChainAgentConfig, CheckpointSyncerType } from '../config/agent';
import { AgentAwsUser, ValidatorAgentAwsUser } from './aws';
export enum KEY_ROLE_ENUM {
Validator = 'validator',
@ -32,20 +33,8 @@ async function helmValuesForChain<Networks extends ChainName>(
agentConfig: AgentConfig<Networks>,
chainNames: Networks[],
) {
// Credentials are only needed if AWS keys are needed -- otherwise, the
// key is pulled from GCP Secret Manager by the helm chart
const credentials = (role: KEY_ROLE_ENUM) => {
if (agentConfig.aws) {
const key = new AgentAwsKey(agentConfig, role, chainName);
return key.credentialsAsHelmValue;
}
return undefined;
};
const chainAgentConfig = new ChainAgentConfig(agentConfig, chainName);
const kathyConfig = chainAgentConfig.kathyConfig;
return {
image: {
repository: agentConfig.docker.repo,
@ -67,34 +56,25 @@ async function helmValuesForChain<Networks extends ChainName>(
}),
validator: {
enabled: true,
signer: {
...credentials(KEY_ROLE_ENUM.Validator),
},
configs: await chainAgentConfig.validatorConfigs(),
},
relayer: {
enabled: true,
signers: chainNames.map((name) => ({
name,
...credentials(KEY_ROLE_ENUM.Relayer),
})),
aws: await chainAgentConfig.relayerRequiresAwsCredentials(),
signers: chainAgentConfig.relayerSigners,
config: chainAgentConfig.relayerConfig,
},
checkpointer: {
enabled: true,
signers: chainNames.map((name) => ({
name,
...credentials(KEY_ROLE_ENUM.Checkpointer),
})),
aws: chainAgentConfig.checkpointerRequiresAwsCredentials,
signers: await chainAgentConfig.checkpointerSigners(),
config: chainAgentConfig.checkpointerConfig,
},
kathy: {
signers: chainNames.map((name) => ({
name,
...credentials(KEY_ROLE_ENUM.Kathy),
})),
enabled: kathyConfig !== undefined,
config: kathyConfig,
enabled: chainAgentConfig.kathyEnabled,
aws: chainAgentConfig.kathyRequiresAwsCredentials,
signers: await chainAgentConfig.kathySigners(),
config: chainAgentConfig.kathyConfig,
},
},
};
@ -140,7 +120,8 @@ export async function getAgentEnvVars<Networks extends ChainName>(
}-db`,
);
try {
// GCP keys
if (!agentConfig.aws) {
const gcpKeys = await fetchAgentGCPKeys(
agentConfig.environment,
outboxChainName,
@ -166,52 +147,59 @@ export async function getAgentEnvVars<Networks extends ChainName>(
`OPT_VALIDATOR_VALIDATOR_TYPE=hexKey`,
);
}
} catch (error) {
// This happens if you don't have a result type
if ((error as any).toString().includes('Panic')) {
throw error;
} else {
// AWS keys
let user: AgentAwsUser<Networks>;
if (role === KEY_ROLE_ENUM.Validator) {
const checkpointSyncer =
agentConfig.validatorSets[outboxChainName].validators[index!]
.checkpointSyncer;
if (checkpointSyncer.type !== CheckpointSyncerType.S3) {
throw Error(
'Expected S3 checkpoint syncer for validator with AWS keys',
);
}
user = new ValidatorAgentAwsUser(
agentConfig.environment,
outboxChainName,
index!,
checkpointSyncer.region,
checkpointSyncer.bucket,
);
} else {
user = new AgentAwsUser(
agentConfig.environment,
outboxChainName,
role,
agentConfig.aws!.region,
);
}
// Keys are in AWS
const awsKeys = await getSecretAwsCredentials(agentConfig);
const accessKeys = await user.getAccessKeys();
envVars.push(`AWS_ACCESS_KEY_ID=${awsKeys.accessKeyId}`);
envVars.push(`AWS_SECRET_ACCESS_KEY=${awsKeys.secretAccessKey}`);
envVars.push(`AWS_ACCESS_KEY_ID=${accessKeys.accessKeyId}`);
envVars.push(`AWS_SECRET_ACCESS_KEY=${accessKeys.secretAccessKey}`);
// Only checkpointer and relayer need to sign txs
if (role === KEY_ROLE_ENUM.Checkpointer || role === KEY_ROLE_ENUM.Relayer) {
if (
role === KEY_ROLE_ENUM.Checkpointer ||
role === KEY_ROLE_ENUM.Relayer ||
role === KEY_ROLE_ENUM.Kathy
) {
chainNames.forEach((name) => {
const key = new AgentAwsKey(agentConfig, role, name);
envVars.push(`OPT_BASE_SIGNERS_${name.toUpperCase()}_TYPE=aws`);
envVars.push(
`OPT_BASE_SIGNERS_${name.toUpperCase()}_ID=${
key.credentialsAsHelmValue.aws.keyId
}`,
);
envVars.push(
`OPT_BASE_SIGNERS_${name.toUpperCase()}_REGION=${
key.credentialsAsHelmValue.aws.region
}`,
const key = new AgentAwsKey(agentConfig, name, role);
envVars = envVars.concat(
configEnvVars(key.keyConfig, 'BASE', 'SIGNERS_'),
);
});
}
// Validator attestation key
if (role === KEY_ROLE_ENUM.Validator) {
const key = new AgentAwsKey(agentConfig, role, outboxChainName);
envVars.push(`OPT_BASE_VALIDATOR_TYPE=aws`);
envVars.push(
`OPT_BASE_VALIDATOR_ID=${key.credentialsAsHelmValue.aws.keyId}`,
);
envVars.push(
`OPT_BASE_VALIDATOR_REGION=${key.credentialsAsHelmValue.aws.region}`,
);
}
}
switch (role) {
case KEY_ROLE_ENUM.Validator:
envVars.concat(
envVars = envVars.concat(
configEnvVars(
valueDict.abacus.validator.configs[index!],
KEY_ROLE_ENUM.Validator,
@ -219,12 +207,12 @@ export async function getAgentEnvVars<Networks extends ChainName>(
);
break;
case KEY_ROLE_ENUM.Relayer:
envVars.concat(
envVars = envVars.concat(
configEnvVars(valueDict.abacus.relayer.config, KEY_ROLE_ENUM.Relayer),
);
break;
case KEY_ROLE_ENUM.Checkpointer:
envVars.concat(
envVars = envVars.concat(
configEnvVars(
valueDict.abacus.checkpointer.config,
KEY_ROLE_ENUM.Checkpointer,
@ -233,7 +221,7 @@ export async function getAgentEnvVars<Networks extends ChainName>(
break;
case KEY_ROLE_ENUM.Kathy:
if (valueDict.abacus.kathy.config) {
envVars.concat(
envVars = envVars.concat(
configEnvVars(valueDict.abacus.kathy.config, KEY_ROLE_ENUM.Kathy),
);
}

@ -1,7 +1,12 @@
import { types } from '@abacus-network/utils';
import { ChainName, ChainSubsetMap } from '@abacus-network/sdk';
import { DeployEnvironment } from './environment';
import { ValidatorAgentAwsUser } from '../agents/aws';
import {
AgentAwsKey,
AgentAwsUser,
ValidatorAgentAwsUser,
} from '../agents/aws';
import { KEY_ROLE_ENUM } from '../agents';
// Allows a "default" config to be specified and any per-network overrides.
interface ChainOverridableConfig<Networks extends ChainName, T> {
@ -122,6 +127,7 @@ type ChainValidatorConfigs<Networks extends ChainName> = ChainOverridableConfig<
// Full validator agent config for a single chain
interface ValidatorConfig extends BaseValidatorConfig {
checkpointSyncer: CheckpointSyncerConfig;
validator: KeyConfig;
}
// ======================================
@ -156,6 +162,27 @@ type ChainKathyConfigs<Networks extends ChainName> = ChainOverridableConfig<
KathyConfig
>;
// Eventually consumed by Rust, which expects camelCase values
export enum KeyType {
Aws = 'aws',
Hex = 'hexKey',
}
export interface AwsKeyConfig {
type: KeyType.Aws;
// ID of the key, can be an alias of the form `alias/foo-bar`
id: string;
// AWS region where the key is
region: string;
}
// The private key is omitted so it can be fetched using external-secrets
export interface HexKeyConfig {
type: KeyType.Hex;
}
export type KeyConfig = AwsKeyConfig | HexKeyConfig;
interface IndexingConfig {
from: number;
chunk: number;
@ -177,6 +204,7 @@ export interface AgentConfig<Networks extends ChainName> {
docker: DockerConfig;
index?: IndexingConfig;
aws?: AwsConfig;
domainNames: Networks[];
validatorSets: ChainValidatorSets<Networks>;
validator: ChainValidatorConfigs<Networks>;
relayer: ChainRelayerConfigs<Networks>;
@ -230,8 +258,23 @@ export class ChainAgentConfig<Networks extends ChainName> {
public readonly chainName: Networks,
) {}
get validatorSet(): ValidatorSet {
return this.agentConfig.validatorSets[this.chainName];
// Credentials are only needed if AWS keys are needed -- otherwise, the
// key is pulled from GCP Secret Manager by the helm chart
keyConfig(role: KEY_ROLE_ENUM): KeyConfig {
if (this.awsKeys) {
const key = new AgentAwsKey(this.agentConfig, this.chainName, role);
return key.keyConfig;
}
return {
type: KeyType.Hex,
};
}
signers(role: KEY_ROLE_ENUM) {
return this.agentConfig.domainNames.map((name) => ({
name,
keyConfig: this.keyConfig(role),
}));
}
async validatorConfigs(): Promise<Array<ValidatorConfig>> {
@ -242,25 +285,79 @@ export class ChainAgentConfig<Networks extends ChainName> {
return Promise.all(
this.validatorSet.validators.map(async (val, i) => {
if (val.checkpointSyncer.type === CheckpointSyncerType.S3) {
const awsUser = new ValidatorAgentAwsUser(
this.agentConfig.environment,
this.chainName,
i,
val.checkpointSyncer.region,
val.checkpointSyncer.bucket,
if (val.checkpointSyncer.type !== CheckpointSyncerType.S3) {
throw Error(
'Expected k8s-based validator to use S3 checkpoint syncer',
);
await awsUser.createIfNotExists();
await awsUser.createBucketIfNotExists();
}
const awsUser = new ValidatorAgentAwsUser(
this.agentConfig.environment,
this.chainName,
i,
val.checkpointSyncer.region,
val.checkpointSyncer.bucket,
);
await awsUser.createIfNotExists();
await awsUser.createBucketIfNotExists();
let validator: KeyConfig = {
type: KeyType.Hex,
};
if (this.awsKeys) {
const key = awsUser.key(this.agentConfig);
await key.createIfNotExists();
await key.putKeyPolicy(awsUser.arn);
validator = key.keyConfig;
}
return {
...baseConfig,
checkpointSyncer: val.checkpointSyncer,
validator,
};
}),
);
}
// Returns whetehr the relayer requires AWS credentials, creating them if required.
async relayerRequiresAwsCredentials(): Promise<boolean> {
// If there is an S3 checkpoint syncer, we need AWS credentials.
// We ensure they are created here, but they are actually read from using `external-secrets`
// on the cluster.
const firstS3Syncer = this.validatorSet.validators.find(
(validator) =>
validator.checkpointSyncer.type === CheckpointSyncerType.S3,
)?.checkpointSyncer as S3CheckpointSyncerConfig | undefined;
// If AWS is present on the agentConfig, we are using AWS keys and need credentials regardless.
// This is undefined if AWS is not required
const awsRegion: string | undefined =
this.agentConfig.aws?.region ?? firstS3Syncer?.region;
if (awsRegion !== undefined) {
const awsUser = new AgentAwsUser(
this.agentConfig.environment,
this.chainName,
KEY_ROLE_ENUM.Relayer,
awsRegion,
);
await awsUser.createIfNotExists();
// If we're using AWS keys, ensure the key is created and the user can use it
if (this.awsKeys) {
const key = awsUser.key(this.agentConfig);
await key.createIfNotExists();
await key.putKeyPolicy(awsUser.arn);
}
return true;
}
return false;
}
get relayerSigners() {
return this.signers(KEY_ROLE_ENUM.Relayer);
}
get relayerConfig(): RelayerConfig {
const baseConfig = getChainOverriddenConfig(
this.agentConfig.relayer,
@ -284,6 +381,33 @@ export class ChainAgentConfig<Networks extends ChainName> {
};
}
// Gets signers for a provided role. If AWS keys are used, the corresponding
// key and users are created if necessary.
async getAndPrepareSigners(role: KEY_ROLE_ENUM) {
if (this.awsKeys) {
const awsUser = new AgentAwsUser(
this.agentConfig.environment,
this.chainName,
role,
this.agentConfig.aws!.region,
);
await awsUser.createIfNotExists();
const key = awsUser.key(this.agentConfig);
await key.createIfNotExists();
await key.putKeyPolicy(awsUser.arn);
}
return this.signers(role);
}
// Gets signer info, creating them if necessary
checkpointerSigners() {
return this.getAndPrepareSigners(KEY_ROLE_ENUM.Checkpointer);
}
get checkpointerRequiresAwsCredentials() {
return this.awsKeys;
}
get checkpointerConfig(): CheckpointerConfig {
return getChainOverriddenConfig(
this.agentConfig.checkpointer,
@ -291,10 +415,44 @@ export class ChainAgentConfig<Networks extends ChainName> {
);
}
// Gets signer info, creating them if necessary
kathySigners() {
if (!this.kathyEnabled) {
return [];
}
return this.getAndPrepareSigners(KEY_ROLE_ENUM.Kathy);
}
get kathyRequiresAwsCredentials() {
return this.awsKeys;
}
get kathyConfig(): KathyConfig | undefined {
if (!this.agentConfig.kathy) {
return undefined;
}
return getChainOverriddenConfig(this.agentConfig.kathy, this.chainName);
}
get kathyEnabled() {
return this.kathyConfig !== undefined;
}
get validatorSet(): ValidatorSet {
return this.agentConfig.validatorSets[this.chainName];
}
// Returns true if any of the validators in the validator set are using an S3 checkpoint syncer.
get s3CheckpointSyncerExists(): boolean {
return (
this.validatorSet.validators.find(
(validator) =>
validator.checkpointSyncer.type === CheckpointSyncerType.S3,
) !== undefined
);
}
get awsKeys(): boolean {
return this.agentConfig.aws !== undefined;
}
}

@ -1,43 +1,43 @@
export const addresses = {
alfajores: {
upgradeBeaconController: '0xeCa505b0777152e68776f1C5a875EE4dFbf59b94',
xAppConnectionManager: '0xB01b98d52767280934e98cf5F7B57DaC434473B9',
interchainGasPaymaster: '0x125379056774C4246d016b2C58c6fbb80ab8829b',
outboxValidatorManager: '0xf78caF314423c749681D3C7B08FCd66d7a4BA683',
upgradeBeaconController: '0x1f15e8b9C5ABf2D6f7f9033f95Fcd66b90c8aa27',
abacusConnectionManager: '0xD57Ff4d8d3872322bD628ed8f9C6bB3617A5ef13',
interchainGasPaymaster: '0x63BDfEF81688ddd5f135D8a8680f942205E030c2',
outboxValidatorManager: '0xCfC4862Df4544923265873f2d288AcEd9919d679',
inboxValidatorManagers: {
kovan: '0x70a122D942B38EDC1C3E691aF19209CD17aDe555',
kovan: '0x78BEAd8669B1657BCA3F7aC202D8098f37Eb6c6A',
},
outbox: {
proxy: '0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559',
implementation: '0x63C950170534907d40B77ecF61bd07980B51f2a4',
beacon: '0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68',
proxy: '0x71f37188d27788ea86E12557edbc9b07E5BDdf03',
implementation: '0x3219827B9D028Cec62b996147076b4cFCeac282a',
beacon: '0xe0B98662f15B12E4aCfd950cBe53FAad4b0C0b98',
},
inboxes: {
kovan: {
proxy: '0x65D545ae04b394fAfBE2EeD4036aBbc0485De8Db',
implementation: '0x5908D50cC36d40ABe284BAb9f43d290D7B083020',
beacon: '0xDE9b9CbAaB774E9DCC4Af1bdd2b510d97dC1705c',
proxy: '0x0fda1FDfEaDADdAE9C07C2bdA184935E317688C6',
implementation: '0x9632B89f335F6f48d8ea32fC95ccc2eA36B33570',
beacon: '0xc8571652Fe648d2873Af278C4982Ecd07dBb0870',
},
},
},
kovan: {
upgradeBeaconController: '0x943bA1d4c8A20dd3c568cE16FbaB297F7d3178C3',
xAppConnectionManager: '0x9318D51F02EcAf21682d546D8260c96580D4b49e',
interchainGasPaymaster: '0x93Ea081a3F8c5e34F51FA4d164a43d2925e24238',
outboxValidatorManager: '0x81485d5C439a393375A65b9060aaD791aCD90439',
upgradeBeaconController: '0x74A77C554685651F7dB3784359E1436604794438',
abacusConnectionManager: '0x3c3b0367A960a6ea1cDe58Bb8A7E33bfC38554D3',
interchainGasPaymaster: '0x33bE4eAa1A7c04E6Bba63e4F9cfff1eb98CF59F0',
outboxValidatorManager: '0x3f0e80FfACE85DC573F57b2f552f6F98Fc5984c4',
inboxValidatorManagers: {
alfajores: '0x73B075e931B7dA89E00422Dbc8205369211C40e2',
alfajores: '0x63C950170534907d40B77ecF61bd07980B51f2a4',
},
outbox: {
proxy: '0xa42Eb261e74Fd92bC376Bb4CC19Ca51Ca7EBFf6C',
implementation: '0xB5a43311274bEb8478Bd5909e08Fd940FC2A75C9',
beacon: '0xB4dD7B062A927ff166312a9B49975184965950f7',
proxy: '0x24D05ae06F3Ce4CF146958714c2B2FBE8B6a29c4',
implementation: '0xbA2C920a863122a42c249d89D5954506F00513C3',
beacon: '0xE8bF352efbC50754442095a04e81A5141fe75e16',
},
inboxes: {
alfajores: {
proxy: '0x0bA30c539f67027797209B314afE7A0390F536ab',
implementation: '0x84710af0473Fe6672C9C9e7B2d8B16eCE6D1b581',
beacon: '0x75f3E7BB237cAEADdf8BA2B22f93884eD3923B8C',
proxy: '0x125379056774C4246d016b2C58c6fbb80ab8829b',
implementation: '0x6D91F26Fb1430bC0F557cC8BD7F815b441541c68',
beacon: '0xdCCA08dC4ec34d58f69415b0C7C8e0042779c559',
},
},
},

Loading…
Cancel
Save