Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7575078
test(arch): vendor fixtures from sub-projects 1+2 for parity tests
EvgenyNasretdinov Jun 29, 2026
6e3aa68
feat(arch): package scaffold + Pubkey/EthAddress/U256/OracleIntent types
EvgenyNasretdinov Jun 29, 2026
d0d8184
feat(arch): Borsh codec for OracleIntent with parity against intent_a…
EvgenyNasretdinov Jun 29, 2026
a838658
feat(arch): HandleIntentUpdate instruction encoder
EvgenyNasretdinov Jun 29, 2026
eac9ae2
feat(arch): PDA derivation with Solana-compatible off-curve check
EvgenyNasretdinov Jun 29, 2026
b3d8aca
feat(arch): BIP-340 Schnorr signer for Taproot tx signing
EvgenyNasretdinov Jun 29, 2026
ada9297
fix(arch): swap to btcec/v2/schnorr for actual BIP-340 compliance
EvgenyNasretdinov Jun 29, 2026
78d3e36
feat(arch): DIA_ORACLE.* log parser (update/stale/rejected)
EvgenyNasretdinov Jun 29, 2026
3ca8acb
feat(arch): JSON-RPC client + RuntimeTransaction wire format
EvgenyNasretdinov Jun 29, 2026
3d68ea1
feat(bridge): Destination interface, EVM WriteClient as adapter (no r…
EvgenyNasretdinov Jun 29, 2026
2cd3227
feat(bridge): ArchWriteClient + mock-based unit tests
EvgenyNasretdinov Jun 29, 2026
37d374f
feat(bridge): chain.Kind discriminator + buildDestination factory
EvgenyNasretdinov Jun 29, 2026
108b428
feat(database): arch_logs column + dia_arch_rejections table
EvgenyNasretdinov Jun 29, 2026
263def4
feat(bridge): persist Arch rejections in TransactionHandler
EvgenyNasretdinov Jun 29, 2026
d318660
fix(bridge): task-13 review fixes (status TODO + go.mod direct dep)
EvgenyNasretdinov Jun 29, 2026
7051641
feat(metrics): Arch destination Prometheus collectors
EvgenyNasretdinov Jun 29, 2026
066f8ae
feat(bridge): background poller for fee_vault and payer balance gauges
EvgenyNasretdinov Jun 29, 2026
afbf3b9
docs(bridge): operator-facing Arch destination config examples
EvgenyNasretdinov Jun 29, 2026
c6f4984
test(integration): live Arch destination end-to-end test
EvgenyNasretdinov Jun 29, 2026
f396347
docs(bridge): operator runbook + README section for Arch destinations
EvgenyNasretdinov Jun 29, 2026
8f49da8
fix(bridge): final-review must-fix items (per-router key, tx-confirma…
EvgenyNasretdinov Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions services/bridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Bridge service

The bridge service relays signed oracle intents from Lasernet to destination
chains. It is configured via YAML files in `config/` and exposes Prometheus
metrics for operational observability.

## Supported destinations

### EVM destinations

The bridge supports arbitrary EVM-compatible destinations. Each destination is
identified by its chain ID in `chains.yaml` and wired to a receiver contract in
`contracts.yaml`.

### Arch Network destinations

The bridge supports `kind: "arch"` destinations alongside the existing EVM
backends. An Arch destination relays a signed `OracleIntent` to the receiver
program deployed via the `arch-oracle-cli` (sub-project 2). Two reserved
chain-ID sentinels:

- `-1` — Arch testnet
- `-2` — Arch mainnet

See `docs/ARCH_DESTINATION.md` for the operator runbook and
`config/examples/` for a paste-ready YAML stub.
15 changes: 9 additions & 6 deletions services/bridge/config/event_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,15 @@ type LegacyRouterDestination struct {

// DestinationMethodConfig defines a contract method call for generic routing
type DestinationMethodConfig struct {
Name string `json:"name"`
ABI string `json:"abi"`
Params map[string]string `json:"params"`
Value string `json:"value"`
GasLimit uint64 `json:"gas_limit"`
GasMultiplier float64 `json:"gas_multiplier"`
Name string `json:"name" yaml:"name"`
ABI string `json:"abi" yaml:"abi"`
Params map[string]string `json:"params" yaml:"params"`
Value string `json:"value" yaml:"value"`
GasLimit uint64 `json:"gas_limit" yaml:"gas_limit"`
GasMultiplier float64 `json:"gas_multiplier" yaml:"gas_multiplier"`
// Kind optionally tags this method as belonging to a specific chain backend.
// Defaults to empty/"evm". Set to "arch" for Arch Network destinations.
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

// ExtractedData represents data extracted from an event
Expand Down
19 changes: 19 additions & 0 deletions services/bridge/config/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Arch destination config examples

To enable Arch as a destination in your bridge deployment:

1. Use `arch-oracle-cli` (sub-project 2) to deploy the receiver + fee-hook
programs, initialize them, and authorize at least one signer.
2. Copy the two `address` / `fee_hook_program_id` values from
`arch-oracle profile show <name>` into `arch-testnet.contracts.yaml`.
3. Merge `arch-testnet.chains.yaml` into your `chains.yaml`.
4. Merge `arch-testnet.contracts.yaml` into your `contracts.yaml`.
5. Drop `arch-testnet.router.yaml` into your `routers/` directory.
6. Generate a relayer keypair (`arch-oracle wallet create --name arch-relayer`)
and set `ARCH_RELAYER_PRIVATE_KEY` to its `secretKeyHex` field. On localnet
/devnet fund it via `arch-oracle wallet faucet`; on testnet/mainnet fund
out-of-band.
7. Restart the bridge.

For mainnet, mirror the same steps with `chain_id: -2`, the mainnet RPC URL,
the mainnet receiver/fee-hook program IDs, and `enabled: true`.
15 changes: 15 additions & 0 deletions services/bridge/config/examples/arch-testnet.chains.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
arch-testnet:
chain_id: -1
name: "Arch Network (Testnet)"
kind: "arch"
rpc_urls:
- "https://explorer.arch.network/api/v1/testnet/rpc"
enabled: true

arch-mainnet:
chain_id: -2
name: "Arch Network (Mainnet)"
kind: "arch"
rpc_urls:
- "https://explorer.arch.network/api/v1/mainnet/rpc"
enabled: false
9 changes: 9 additions & 0 deletions services/bridge/config/examples/arch-testnet.contracts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
arch-testnet-receiver:
chain_id: -1
type: "arch-oracle-receiver"
# Replace with the program ID printed by:
# arch-oracle deploy ../arch-oracle-program/target/deploy/dia_arch_oracle_receiver.so --profile testnet
address: "0000000000000000000000000000000000000000000000000000000000000000"
# Replace with the fee-hook program ID printed by:
# arch-oracle deploy ../arch-oracle-program/target/deploy/dia_arch_fee_hook.so --profile testnet
fee_hook_program_id: "0000000000000000000000000000000000000000000000000000000000000000"
16 changes: 16 additions & 0 deletions services/bridge/config/examples/arch-testnet.router.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
id: "dia-arch-testnet"
type: "intent-relayer"
enabled: true
# The env var holding the 64-char hex secp256k1 secret key of the relayer.
# Generate via:
# arch-oracle wallet create --name arch-relayer
# and read out the secretKeyHex field from the resulting JSON.
private_key_env: "ARCH_RELAYER_PRIVATE_KEY"
triggers:
events: ["IntentRegistered"]
destinations:
- chain_id: -1
contract: "arch-testnet-receiver"
method:
kind: "arch-handle-intent-update"
gas_limit: 200000 # repurposed as an Arch compute-budget hint (optional)
7 changes: 7 additions & 0 deletions services/bridge/config/modular_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ type ChainConfig struct {
DefaultGasLimit uint64 `yaml:"default_gas_limit,omitempty" json:"default_gas_limit,omitempty"`
GasMultiplier float64 `yaml:"gas_multiplier,omitempty" json:"gas_multiplier,omitempty"`
MaxGasPrice string `yaml:"max_gas_price,omitempty" json:"max_gas_price,omitempty"`
// Kind discriminates the chain backend. Empty string or "evm" selects the
// default EVM path. "arch" selects the Arch Network path.
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
}

type ContractConfig struct {
Expand All @@ -64,6 +67,10 @@ type ContractConfig struct {

// Method configuration
Methods map[string]MethodConfig `yaml:"methods,omitempty" json:"methods,omitempty"`

// FeeHookProgramID is the hex-encoded 32-byte program ID of the fee-hook
// program on Arch Network. Required when the chain Kind is "arch".
FeeHookProgramID string `yaml:"fee_hook_program_id,omitempty" json:"fee_hook_program_id,omitempty"`
}

type RouterConfig struct {
Expand Down
63 changes: 63 additions & 0 deletions services/bridge/docs/ARCH_DESTINATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Arch destination runbook

Sub-project 3 of the DIA × Arch Network integration.

## Prerequisites

- A deployed receiver + fee-hook program on the target Arch network. Use
`arch-oracle-cli` (sibling repo): `arch-oracle deploy`, `arch-oracle init`,
`arch-oracle configure set-signer`.
- A relayer keypair generated with `arch-oracle wallet create`. The 64-char
`secretKeyHex` becomes the value of the env var named in `private_key_env`.
- A funded relayer account. Localnet/devnet: `arch-oracle wallet faucet`.
Testnet/mainnet: fund out-of-band, at least 0.01 BTC equivalent in lamports.

## Config

Three YAML changes:

1. `chains.yaml`: add an entry with `kind: "arch"` and one of the reserved
chain IDs (-1 testnet, -2 mainnet).
2. `contracts.yaml`: add a contract entry with `address` set to the receiver
program ID and `fee_hook_program_id` set to the fee-hook program ID.
3. `routers/<name>.yaml`: add a router with `destinations[].method.kind:
"arch-handle-intent-update"`.

Paste-ready stubs live under `config/examples/`.

## Behavior

The bridge sends one `HandleIntentUpdate` transaction per intent
(V1 — no batching). It signs with BIP-340 Schnorr (Taproot key-path),
waits for processed status (timeout 30s), then parses the receiver's
`DIA_ORACLE.*` log lines.

- Successful update → rejection-free `TxResult`; logged with outcome
`delivered`. No DB writes from the Arch path beyond the upstream
processed_events row.
- Per-intent rejection (UnauthorizedSigner, AlreadyProcessed,
InvalidSignature) → one row per rejection in `dia_arch_rejections`;
logged with outcome `partially_delivered`. (A `processed_events.status`
column is planned but not yet schema-migrated.)
- Tx-level failure (BatchTooLarge, InvalidAccountList, RPC error) →
logged with outcome `failed`. No persistence on the Arch path; the
existing EVM-style metrics path handles transaction-failure surfacing.

## Metrics

| Metric | Type | Labels |
|---|---|---|
| `dia_arch_intent_updates_total` | counter | router, chain_id, symbol |
| `dia_arch_intent_stale_total` | counter | router, chain_id, symbol |
| `dia_arch_intent_rejected_total` | counter | router, chain_id, reason |
| `dia_arch_tx_confirmation_seconds` | histogram | router, chain_id, outcome |
| `dia_arch_fee_vault_lamports` | gauge | router, chain_id |
| `dia_arch_payer_balance_lamports` | gauge | router, chain_id |

The two gauges are polled every 30s by a per-destination goroutine. Use the
payer balance gauge for relayer-out-of-funds alerting.

## Testing

- Unit tests (no live infra): `go test ./internal/arch/ ./internal/bridge/`.
- Live end-to-end test: see `test/integration/README.md`.
6 changes: 5 additions & 1 deletion services/bridge/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ replace github.com/diadata.org/Spectra-interoperability/proto => ../../proto
replace github.com/diadata.org/Spectra-interoperability => ../../

require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1
github.com/diadata.org/Spectra-interoperability v0.0.0-00010101000000-000000000000
github.com/diadata.org/Spectra-interoperability/proto v0.0.0-00010101000000-000000000000
github.com/ethereum/go-ethereum v1.16.4
Expand All @@ -28,13 +31,14 @@ require (
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.3 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
Expand Down
15 changes: 11 additions & 4 deletions services/bridge/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
Expand All @@ -10,6 +12,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down Expand Up @@ -41,10 +47,10 @@ github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
Expand Down Expand Up @@ -116,6 +122,7 @@ github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
Expand Down
Loading