Proposal 1: the chain is picked in Simplex.toml
[build]
target = "bitcoin" # default is "elements"
This decides the jet set, the tapleaf and address format, the transaction environment, the sighash, and which nodes simplex regtest / simplex test start.
--target CLI flag would override [build] target.
Proposal 2: libraries are compiled with the consumer’s target
A dependency’s own [build.target] only applies when the library builds itself, so simplex install would ignore it.
Library authors keep chain-specific functions in their own modules (std already does) and check both chains in CI:
simplex build --target elements
simplex build --target bitcoin
Proposal 3: a Chain trait with Elements and Bitcoin impls
Chain-specific behaviour goes behind a small trait:
pub trait Chain {
type Network;
type Tx;
type Pst;
type Env: JetEnvironment;
type Address;
fn jet_hinter() -> Box<dyn JetHinter>;
fn leaf_version() -> LeafVersion;
fn env(program: &Program, pst: &Self::Pst, input: usize, network: &Self::Network)
-> Result<Self::Env, ProgramError>;
}
pub struct Elements;
pub struct Bitcoin;
Where each attribute is used:
- Chain::jet_hinter(): wherever .simf is compiled, i.e. simplex build codegen, include_simf!, Program::load(), and the pruning logger. The hinter decides which jet:: names exist and what they compile to, so it determines the CMR. These four examples have hardcoded ElementsJetHinter now, generated bindings will pass the project’s hinter into Program::new instead.
- Chain::leaf_version(): the taproot builder behind Program::get_tr_address and the control block. Elements uses 0xbe; Bitcoin would use the value Inquisition defines.
- Chain::env(): Program::execute, which hands it to prune_with_tracker and BitMachine::exec, and Signer::sign_program, which reads sighash_all from it.
- Chain::Network, Chain::Address: get_tr_address / get_script_pubkey.
- Chain::Tx, Chain::Pst: transaction builder, signer and providers.
Code that does not touch chain types stays as it is and serves both chains: compilation cache, ABI, storage tap tree, SigMessage, witness injection, fee estimation, key derivation.
The transaction builder, signer and providers do touch chain types (PSET vs PSBT, elements::TxOut vs bitcoin::TxOut), so each would have one implementation per chain: simplex::elements::{FinalTransaction, Signer, EsploraProvider, ...} and simplex::bitcoin::{...}. The project’s `target` decides which one is used:
- codegen imports types from
simplex::elementsorsimplex::bitcoinin the generated bindings simplex testbuildsTestContextwith that chain’s signer and providersimplex regteststartselementsd+electrsorbitcoind+electrs-bitcoin
Proposal 4: simplexup ships the Bitcoin node pair in the default install
Today simplexup installs one signed .tar per Simplex version (simplex, elementsd, electrs, smplx-nextest) and simplex regtest finds the node binaries on PATH by name. I propose:
- From new version the same .tar also would contain
bitcoind(Bitcoin Inquisition with Simplicity, pinned to one tag) andelectrs-bitcoin(Blockstream electrs withoutliquid). simplex regtest/simplex testpick the pair fromtarget.[regtest] bitcoind = "..."andelectrs = "..."(plus env vars) override the paths for people who build their own node.