mirror of
https://github.com/luxfi/bridge.git
synced 2026-09-24 00:26:46 +00:00
Two additive integrations from origin/whispers/bridgev2 — both pure
insertions, no consumer-contract changes.
LP-333 (internal/bchain/client.go):
Adds SignerMember, SignerSetInfo, EpochInfo + GetSignerSetInfo /
GetEpoch methods on the b-chain JSON-RPC client. The bridge becomes
a CONSUMER of BridgeVM signer-set state: it observes which MPC
nodes are in the active quorum, the threshold, and epoch
transitions, then points new keygen/signing at the current epoch's
members. mpcd remains authoritative on key share material; BridgeVM
is the membership + epoch ledger.
Methods are read-only — the bridge never proposes rotations. Node
operators do that out-of-band via BridgeVM governance. Surface lives
on the same `${nodeUrl}/ext/bc/B/rpc` endpoint as bridge_getInfo,
not on ThresholdVM.
internal/mchain/pool.go:
Adds Pool — round-robin release-wallet selector that the daemon
drains for destination-chain broadcasts. Pure additive; existing
callers don't see it until they choose to consume it.
Tests: bchain (2.3s) + mchain (2.6s) all green. Includes
TestGetSignerSetInfo_HappyPath + TestGetEpoch round-trips against the
mock node.
NOT included in this PR (deliberately scoped out):
- internal/mchain/client.go modifications — branch's version is
behind main (no DashboardURL/Curve/sessions). Main's BTC support
already supersedes the branch's testnet-stopgap path.
- internal/mchain/btc_address.go — branch's file is a TESTNET
base58 stopgap; main's file is the production bech32 derivation
(deriveBTCBech32Address, compressSecp256k1Pubkey). Different
purposes, main's wins.
This is the second slice of the whispers/bridgev2 integration. Per
"x.x.x+1, never lazy major" the bridge's next tag is v1.2.0 once the
substantive features land.
82 lines
3.2 KiB
Go
82 lines
3.2 KiB
Go
package mchain
|
|
|
|
// pool.go — layered MPC routing.
|
|
//
|
|
// REQUIREMENTS.md §6 + the SDK's BridgeMPCConfig declare two MPC URL
|
|
// slots:
|
|
//
|
|
// publicUrl — m-chain, public/permissionless validator MPC. Per-swap
|
|
// deposit-wallet keygen + refund signing run here. Blast
|
|
// radius on compromise is a single in-flight swap because
|
|
// a fresh wallet is minted every time.
|
|
//
|
|
// privateUrl — Lux treasury cluster, smaller internal quorum. Long-
|
|
// lived release-wallet keygen + settlement signing run
|
|
// here. These wallets hold operator-funded liquidity, so
|
|
// they need both a smaller-quorum threshold (faster +
|
|
// tighter access) and persistence so funds don't strand
|
|
// across restarts.
|
|
//
|
|
// The role-based split lets a tenant point the layered cosigner stack
|
|
// at the right cluster for the right operation without the bridge
|
|
// embedding any "is this a treasury op?" decision in the call path.
|
|
//
|
|
// Back-compat: when only publicUrl is configured, NewPool sets Private
|
|
// to the same *Client. Every existing single-cluster deploy keeps
|
|
// working without flag changes; the split is purely additive.
|
|
|
|
// Pool routes MPC calls between the public (m-chain) and private
|
|
// (treasury) clusters. Both fields are non-nil after construction via
|
|
// NewPool / NewSplitPool — single-cluster deploys get Private == Public.
|
|
//
|
|
// Pool is concurrency-safe iff both Client values are (which they are —
|
|
// *Client docstring guarantees this).
|
|
type Pool struct {
|
|
// Public handles per-swap deposit-wallet keygen + signing FROM the
|
|
// deposit wallet (i.e. refund-leg signing). User-funded; ephemeral.
|
|
Public *Client
|
|
|
|
// Private handles release-wallet keygen + signing FROM the release
|
|
// wallet (i.e. settlement-leg signing). Treasury-funded; long-lived.
|
|
// In single-cluster deploys, Private == Public.
|
|
Private *Client
|
|
}
|
|
|
|
// NewPool constructs a single-cluster pool. Both Public and Private
|
|
// point at the same *Client. Use this when only the legacy --mpc-url
|
|
// flag is set; the operator has not opted into a treasury cluster yet.
|
|
//
|
|
// Passing a nil Client returns nil — callers should treat the pool as
|
|
// "MPC disabled" the same way they treat a nil *Client today.
|
|
func NewPool(c *Client) *Pool {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return &Pool{Public: c, Private: c}
|
|
}
|
|
|
|
// NewSplitPool constructs a two-cluster pool. When private is nil it
|
|
// falls back to single-cluster (Private == Public), matching NewPool.
|
|
// Passing a nil public returns nil — the public cluster is mandatory
|
|
// for any swap to proceed (deposit wallet keygen lives there).
|
|
func NewSplitPool(public, private *Client) *Pool {
|
|
if public == nil {
|
|
return nil
|
|
}
|
|
if private == nil {
|
|
return &Pool{Public: public, Private: public}
|
|
}
|
|
return &Pool{Public: public, Private: private}
|
|
}
|
|
|
|
// IsSplit reports whether the pool has distinct public + private
|
|
// clients. False when single-cluster (Private == Public). Surfaced
|
|
// for startup logging + /health so operators can confirm their
|
|
// --mpc-private-url flag actually took effect.
|
|
func (p *Pool) IsSplit() bool {
|
|
if p == nil {
|
|
return false
|
|
}
|
|
return p.Public != p.Private
|
|
}
|