The bulk retag moved published tags. go.sum and sum.golang.org hold the
original bytes, proxy.golang.org answers not-found, and the direct fetch
disagrees, so builds stopped at a checksum before reaching a compiler.
Pin forward to versions the proxy still serves.
The estate was spread across ten zip versions while the zipdoc/graft refactor
landed at v1.18.22. Measured before bumping: the API is additive across that
range -- 57 exported symbols at v1.17.2, 83 at v1.18.22, with exactly one
removal (`PluginStatus`), and no repo that pins zip references it.
Verified by building this module against the new version, not by assuming.
Co-authored-by: Hanzo Dev <[email protected]>
zap-proto/zip is the one zip. The hanzoai/zip fork is behind it and has no
unique surface left: canonical supersedes its closers with a context-aware
OnShutdown, keeps ZAPRegistry in the zaprpc subpackage, and exports the
primitives its App.Mount wrapped.
Consolidates origin/z/bridgev2-ton-broadcast — final of the five chain
broadcaster branches. With TON wired, all six families (EVM, BTC, SOL,
DOT, XRP, TON) are now implemented end-to-end.
Conflicts resolved:
- internal/broadcast/client.go: TON case added alongside the existing
five; ErrFamilyNotImplemented is now reserved for future families
- cmd/bridge/release_pool.go: dropped TON agent's separate Wallet field
shape (ECDSAPubKey string + EDDSAPubKey string) in favor of the
merged shape from earlier chains:
* Wallet.ECDSAPubKey stays []byte (BTC's choice — XRP/DOT decode hex)
* Wallet.EDDSAPubKeyHex added as hex string (TON's choice — uniform
with the ed25519-only wallet families)
ReleasePoolEntry now carries both ECDSAPubKey (hex) and EDDSAPubKey
(hex), plus AddressType (mchain type — distinct from Family string)
- cmd/bridge/signing_driver.go: TON dispatch added to the family
switch in signOne after the SOL+XRP branches but before the DOT/EVM
assembler arm; threaded tonAssembler + tonUnsigned through the
PreSign/MPC-sign/Finalize flow with TON-specific gas pre-check;
lookupPoolEntry helper added for fetching the EDDSAPubKey alongside
the address; tonAmountToNano + isTONNativeAsset utilities
- cmd/bridge/main.go: TON assembler wired through signer.SetTONAssembler
alongside EVM/SOL/DOT; balance probe TON endpoints + API keys wired
via networks.yaml + balanceProbe.TONBalanceURLs/TONAPIKeys
- cmd/bridge/balance_probe.go: TON branch added to BalanceAt switch
(calls balanceAtTON via GET getAddressBalance); SOL kept in the
dedicated branch; isLikelyNonEVMNetwork docstring updated to
acknowledge SOL+TON are routed elsewhere
- internal/mchain/client.go: Wallet.ECDSAPubKey stays []byte; new
EDDSAPubKeyHex string field for Ed25519 wallets (TON); pickAddress
TON case derives v4r2 raw address from EDDSAPubKey when present,
falls back to SOLAddress slot for legacy mpcd
- go.mod: added xssnick/tonutils-go + ed25519 dependency chain; took
golang.org/x/crypto v0.51.0 + golang.org/x/sys v0.44.0 (TON's
newer minimums)
- internal/broadcast/client_test.go: TestBroadcast_FamilyNotImplemented
retired — every address type now has a concrete broadcaster
- Test refactors: ReleasePool tests use NewReleasePoolForFamily +
PutEntry(family, idx, entry); FamilyTON constant added
Consolidates origin/z/bridgev2-xrp-broadcast. Conflicts resolved:
- internal/broadcast/client.go: XRP case added alongside EVM/BTC/SOL/DOT;
ErrFamilyNotImplemented now lists only TON as pending
- cmd/bridge/release_pool.go: dropped XRP agent's PubKeyHex field +
AcquireForFamily/SizeByFamily/Reload accessors that re-implemented
family routing inside ReleasePool. Kept BTC's family-keyed store
approach (LoadEntries(ctx, family)) as the single source of truth.
XRP pubkey persistence rides on ReleasePoolEntry.ECDSAPubKey (hex
string field added by DOT merge — XRP signing driver reads the same
field for its SigningPubKey embedding). Added FamilyXRP constant.
- cmd/bridge/release_pool_set.go: added XRP slot to ReleasePoolSet +
Size/FamilySizes/PoolFor/poolFor coverage
- cmd/bridge/signing_driver.go: XRP dispatch added before EVM/DOT
assembler path; threaded poolEntry through both poolSet acquire and
legacy fallback acquire so signOneXRP can read poolEntry.ECDSAPubKey
- cmd/bridge/main.go: XRP release pool wired through releasePoolSet.XRP
via the same NewReleasePoolForFamily(store, FamilyXRP, …) shape as
BTC/SOL/DOT — no more shared-store-with-different-indices hack
- internal/mchain/client.go: Wallet.ECDSAPubKey stays []byte (BTC's
choice) — XRP consumers hex-encode when persisting. XRP r-address
derivation runs in pickAddress when ECDSAPubKey present, falls back
to ETHAddress otherwise (matches the merged-state shape-only tests)
- cmd/bridge/broadcast_driver.go: humanizeBroadcastErr merges BTC's
txn-* rejections with XRP's tecINSUFFICIENT_FUNDS / tecUNFUNDED
- go.mod: added rubblelabs/ripple + bits-and-blooms/bitset to
union of all branches' deps
- Test refactors: ECDSAPubKeyHex → ECDSAPubKey ([]byte); PutEntry takes
family arg; XRP-specific tests using AcquireForFamily/SizeByFamily
replaced with TestReleasePool_XRPFamily_BootstrapAndAcquire matching
the BTC family-pool shape
Consolidates origin/z/bridgev2-sol-broadcast. Conflicts resolved:
- internal/broadcast/client.go: keep BTC + SOL cases in family switch
- cmd/bridge/signing_driver.go: BTC dispatch first, then SOL branch
- cmd/bridge/main.go: BTC pool block + SOL pool block + multi-family
router (releasePoolSet) that BOTH families register into
- go.mod/go.sum: union of dependencies, tidy applied
- Replaced SOL agent's NewInMemoryStoreFamily wrappers with BTC agent's
family-scoped ReleasePoolStore (LoadEntries(ctx, family)) — single
source of truth, no parallel wrappers
- release_pool_family_store.go: deleted (redundant with BTC design)
- FamilySOL constant added alongside FamilyEVM + FamilyBTC
Extend the MPC keygen client to surface the compressed secp256k1
public key alongside the chain-appropriate address, and derive a
bech32 P2WPKH locally for BITCOIN_* networks instead of relying on
mpcd's legacy P2PKH "1..." output.
Why: bridge release on BTC needs P2WPKH (witness-discount fees,
direct btcsuite/txscript compatibility, broader wallet support).
mpcd's pubKeyToBtcAddress emits Base58Check P2PKH; rather than
round-tripping a fix through mpcd, the bridge re-derives locally
from the same ECDSAPubKey the keygen already returns.
Changes:
- Wallet gains ECDSAPubKey []byte; 33-byte compressed form preferred,
uncompressed (65/64) and x-only (32) inputs all compress canonically
- keygenResult parses both eth_address and evm_address (mpcd renamed)
- pickAddress() takes the network internal_name so BITCOIN_MAINNET vs
BITCOIN_TESTNET selects the right bech32 hrp (bc / tb)
- new btc_address.go: deriveBTCBech32Address + compressSecp256k1Pubkey
+ bitcoinChainParams; uses btcd/btcutil for canonical HASH160 +
segwit-v0 encoding
- 11 new tests covering mainnet/testnet hrp selection, 32/33/64/65
byte pubkey shapes, empty/malformed input, keygen integration
Introduces internal/xrpl, the bridge's XRP Ledger primitives package:
- AddressFromPubKey / AccountIDFromPubKey: derive the canonical r-address
from a 33-byte compressed-secp256k1 public key. Hash chain is
RIPEMD160(SHA256(pubkey)), base58check-encoded with the XRPL alphabet
+ 0x00 version byte. Round-trips through the rubblelabs codec.
- PreSign(PaymentUnsigned) → (32-byte digest, *SignCtx): builds a native
XRP Payment, embeds the signing pubkey on the wire, returns the
SHA-512Half of (0x53545800 || serialized_body) — exactly the message
the MPC quorum must sign.
- Finalize(DER, *SignCtx) → (signedBlob, txHash): attaches the DER
signature to TxnSignature, returns the full wire-format binary blob
(suitable for submit's tx_blob param as hex) and the canonical XRPL
tx hash (SHA-512Half of 0x54584E00 || blob).
- EncodeDERSignature(r, s): ASN.1 DER encode (r, s) with low-s
normalization (rejects high-s as XRPL's tfFullyCanonicalSig requires)
and the 0x00 leading-byte rule for INTEGERs with the MSB set.
- CanonicalizeS / SHA512Half exposed for tests + future TrustSet path.
Uses github.com/rubblelabs/ripple for the deep binary codec — every
field-id, type-byte, and length-prefix detail is the library's
responsibility. The bridge owns only the signing flow on top, so MPC
ECDSA can stand in for the library's local-key Sign helper.
Tests cover: canonical pubkey → r-address round-trip, rejection of
bad pubkey lengths/prefixes, DER encoding of standard and high-bit
cases, low-s flip for s > N/2, validation that high-s gets canonicalized
in EncodeDERSignature, PreSign digest matching independent SHA512Half
recomputation, full PreSign → Finalize round-trip via the parser.
Add the wire-correct TON destination handler so the bridge can release
into TON_MAINNET / TON_TESTNET via TON Center.
internal/broadcast/ton.go:
- broadcastTON pushes a signed external-message BOC to TON Center's
sendBoc endpoint (mainnet: toncenter.com/api/v2/sendBoc; testnet:
testnet.toncenter.com/api/v2/sendBoc).
- X-API-Key header sourced per-network from Client.TONAPIKeys (set via
SetTONAPIKey or populated by main.go from networks.yaml). Required
at production rates — public gateway throttles to ~1 req/s.
- normalizeTONPayload accepts the hex shape the txassembler emits (to
match the EVM raw-tx convention) and converts to base64 for the
toncenter wire. Pre-base64 payloads pass through.
- parseTONResponse classifies error responses: 429 / "rate" →
ErrTONRateLimited, "account not initialized" / "state_init" →
ErrTONAccountNotInitialized, "invalid boc" / "wrong signature" →
ErrTONMalformedBOC (fatal — signer must re-build), other 5xx /
transport → TONError{Retryable: true}.
- Returns the in-message hash as BroadcastResult.TxHash. Callers
resolve the on-chain tx hash later via getTransactions polling.
internal/txassembler/ton.go:
- Two-phase PreSign / Finalize parallel to the EVM path. PreSignTON
builds the wallet-v4r2 unsigned body cell (subwallet || valid_until
|| seqno || op=0 || message_mode || ref_to_internal_msg) and
surfaces its representation hash for the MPC Ed25519 quorum to
sign. FinalizeTON / FinalizeTONHex re-assemble with the 64-byte
signature, wrap in an external-in message (with state_init when
SourceInitialized=false so first sends self-deploy), and emit BOC
bytes (or base64) for the broadcast layer.
- Native TON transfer + jetton transfer (TRC-3 op=0x0f8a7ea5) under
one spec. Jetton path resolves the source's jetton-wallet address
off the master either via TONJettonWalletAddressProvider (live
runMethod) or operator-supplied TONSpec.JettonSourceWallet. Forward
TON convention 0.05 TON (50_000_000 nanoton) for gas — same
amount the body itself carries to the master.
- Wallet v4r2 (= tonwallet.V4R2) + sub-wallet 698983191 (=
tonwallet.DefaultSubwallet). messages_ttl=180s matches upstream.
- Address helpers: TONAddressFromPubKey (UQ), …Bounceable… (EQ),
TONRawAddress ("<workchain>:<hex>"), ParseTONAddress (accepts any
form). Exported so mchain.pickAddress can derive a real TON
address from EDDSAPubKey once mpcd surfaces it, and so the
deposit-watcher can match incoming-deposit addresses.
internal/broadcast/client.go:
- Add TONAPIKeys map on Client + AddressTypeTON dispatch case
routing to broadcastTON. ErrFamilyNotImplemented message updated
(BTC/SOL/XRP/DOT remain not implemented; EVM + TON are in).
Adds SOLAssembler — companion to the EVM Assembler — that constructs
Solana transactions for the bridge's destination-release flow:
- PreSign: fetch a recent blockhash via JSON-RPC getLatestBlockhash,
build a v0 message (one signer = the MPC release wallet as fee
payer, one recipient, lamport amount), serialize the bytes to be
signed. SPL token mode (when SourceMint is set) derives source +
destination ATAs via solana.FindAssociatedTokenAddress and probes
the destination ATA via getAccountInfo — if absent, the assembler
prepends a CreateAssociatedTokenAccount instruction so the release
wallet pays the ~2_039_280 lamport rent + creates the account.
- Finalize: slot the 64-byte Ed25519 signature into Transaction
.Signatures[0], serialize the full wire tx with MarshalBinary, and
base64-encode for JSON-RPC sendTransaction (encoding:base64). The
signature itself doubles as the canonical Solana "tx hash" — return
it base58-encoded for SDK consumption.
Dependency: github.com/gagliardetto/solana-go v1.20.0 — the de facto
standard Go client. The assembler stays decoupled from the broader
*rpc.Client beyond GetLatestBlockhash + GetAccountInfo, so the
broadcast layer can use its own JSON-RPC endpoint.
Helpers:
- LamportsFromFloat(amount, decimals): safe float → uint64 conversion
with overflow protection. 9dp for native SOL; varies for SPL.
- ParseSOLSignatureHex(s): decodes a 128-char hex string to a fixed
[64]byte buffer — used by the signing driver when the dashboard
returns the Ed25519 sig in hex form.
- SOLDefaultBlockhashURL(network): canonical RPC URL per network.
Tests (sol_test.go) cover:
- Native transfer happy path: instruction is system.Transfer at
Account[0] = payer; round-trip of MessageBytes through
Message.Unmarshal yields an identical message.
- SPL transfer with destination ATA missing: 2 instructions
(ata.Create, then token.Transfer), destination ATA correctly
derived and probed.
- SPL transfer with destination ATA present: only 1 instruction.
- Finalize round-trip: rawTx parses back through solana-go's
Transaction decoder with matching signature + payer + blockhash.
- Error paths: unknown network, zero amount, malformed payer addr.
- ParseSOLSignatureHex + LamportsFromFloat: corner cases.
Total: 12 SOL tests, all green.
The lux-mpc cluster supports both ECDSA/secp256k1 and EdDSA/Ed25519
(FROST) keygens. The bridge previously hardcoded secp256k1 because
every active swap path was EVM/BTC. To wire Solana end-to-end we need
the cluster to mint an Ed25519 wallet and sign over raw Solana message
bytes — neither of which work under secp256k1.
Add a Curve type with two members (secp256k1, ed25519) and a
CurveFor(AddressType) mapping. Forward the curve hint on both the
keygen and dashboard /v1/mpc/sign request bodies; older daemons that
don't recognize the field fall back to their compile-time default
(secp256k1) — which produces an ECDSA-shaped signature the SOL
finalizer will reject with a clear error, so a deployment mismatch
fails fast instead of silently producing bad release txs.
SignForWallet and SignForWalletWithOrg stay on secp256k1 (default
behaviour preserved). The new SignForWalletOnCurve/OnCurveWithOrg
entry points let the signing driver pass an Ed25519 hint per swap.
Tests:
- TestKeygen_SOL_SendsEd25519Curve: SOL keygen body has curve=ed25519
- TestKeygen_ETH_SendsSecp256k1Curve: ETH stays on secp256k1
- TestCurveFor: coverage across all six AddressTypes
Mirrors the luxfi/explorer pattern: cmd/bridge/main.go embeds the SPA
build via go:embed and exposes /v1/bridge/* on a single port. Read paths
(networks, tokens, exchanges, limits) are answered natively from a YAML
config — no Postgres. MPC-heavy paths (quote, rate, swaps, explorer)
reverse-proxy to the legacy Node backend until the native port lands.
Image: cmd/bridge/Dockerfile produces ~25 MB distroless.
Config: cmd/bridge/networks.example.yaml.
Migration target: Hanzo Base (SQLite) + luxfi/mpc direct calls.