123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /* Import modules. */
- import { defineStore } from 'pinia'
- // used for tagging fusions in a way privately derived from wallet name
- const tag_seed = secrets.token_bytes(16)
- // Safety limits to prevent loss of funds / limit fees:
- // (Note that if we enter multiply into the same fusion, our limits apply
- // separately for each "player".)
- // Deny server that asks for more than this component feerate (sat/kbyte).
- const MAX_COMPONENT_FEERATE = 5000
- // The largest 'excess fee' that we are willing to pay in a fusion (fees beyond
- // those needed to pay for our components' inclusion)
- const MAX_EXCESS_FEE = 10000
- // Even if the server allows more, put at most this many inputs+outputs+blanks
- const MAX_COMPONENTS = 40
- // The largest total fee we are willing to pay (our contribution to transaction
- // size should not exceed 7 kB even with 40 largest components).
- const MAX_FEE = MAX_COMPONENT_FEERATE * 7 + MAX_EXCESS_FEE
- // For privacy reasons, don't submit less than this many distinct tx components.
- // (distinct tx inputs, and tx outputs)
- const MIN_TX_COMPONENTS = 11
- const TOR_PORTS = [9050, 9150]
- // # if more than <N> tor connections have been made recently (see covert.py) then don't start auto-fuses.
- const AUTOFUSE_RECENT_TOR_LIMIT_LOWER = 60
- // # if more than <N> tor connections have been made recently (see covert.py) then shut down auto-fuses that aren't yet started
- const AUTOFUSE_RECENT_TOR_LIMIT_UPPER = 120
- // # heuristic factor: guess that expected number of coins in wallet in equilibrium is = (this number) / fraction
- const COIN_FRACTION_FUDGE_FACTOR = 10
- // # for semi-linked addresses (that share txids in their history), allow linking them with this probability:
- const KEEP_LINKED_PROBABILITY = 0.1
- // # how long an auto-fusion may stay in 'waiting' state (without starting-soon) before it cancels itself
- const AUTOFUSE_INACTIVE_TIMEOUT = 600
- // # how many random coins to select max in 1 batch -- used by select_random_coins
- const DEFAULT_MAX_COINS = 20
- // assert DEFAULT_MAX_COINS > 10
- // # how many autofusions can be running per-wallet
- const MAX_AUTOFUSIONS_PER_WALLET = 10
- const CONSOLIDATE_MAX_OUTPUTS = MIN_TX_COMPONENTS // 3
- // # Threshold for proportion of total wallet value fused before stopping fusion. This avoids re-fusion due to dust.
- const FUSE_DEPTH_THRESHOLD = 0.999
- // # We don't allow a fuse depth beyond this in the wallet UI
- const MAX_LIMIT_FUSE_DEPTH = 10
- const Autofuse = False
- const AutofuseCoinbase = False
- const AutofuseConfirmedOnly = False
- const CoinbaseSeenLatch = False
- const FusionMode = 'normal'
- const QueudAutofuse = 4
- const FuseDepth = 0 # Fuse forever by default
- const Selector = ('fraction', 0.1) # coin selector options
- const SelfFusePlayers = 1 # self-fusing control (1 = just self, more than 1 = self fuse up to N times)
- const SpendOnlyFusedCoins = False # spendable_coin_filter @hook
- const HUSH_OP_DATA_HEX = '48555348' // HUSH (as hex)
- const HUSH_DERIVATION_PATH = `m/44'/0'/1213551432'` // HUSH (as decimal)
- def size_of_input(pubkey):
- # Sizes of inputs after signing:
- # 32+8+1+1+[length of sig]+1+[length of pubkey]
- # == 141 for compressed pubkeys, 173 for uncompressed.
- # (we use schnorr signatures, always)
- assert 1 < len(pubkey) < 76 # need to assume regular push opcode
- return 108 + len(pubkey)
- def size_of_output(scriptpubkey):
- # == 34 for P2PKH, 32 for P2SH
- assert len(scriptpubkey) < 253 # need to assume 1-byte varint
- return 9 + len(scriptpubkey)
- def component_fee(size, feerate):
- # feerate in sat/kB
- # size and feerate should both be integer
- # fee is always rounded up
- return (size * feerate + 999) // 1000
- def dust_limit(lenscriptpubkey):
- return 3*(lenscriptpubkey + 148)
- /**
- * Fusion Store
- */
- export const useFusionStore = defineStore('fusion', {
- state: () => ({
- /* Initialize session. */
- _session: null,
- _apiKeys: {},
- }),
- getters: {
- session(_state) {
- return _state._session || null
- },
- sessionid(_state) {
- return _state._session?.id || null
- },
- challenge(_state) {
- return _state._session?.challenge || null
- },
- apiKey(_state) {
- return (_exchangeid) => _state._apiKeys[_exchangeid] || null
- },
- // db() {
- // return Db.fusions
- // },
- },
- actions: {
- async initFusion () {
- console.log('INIT FUSION (before):', this._session)
- /* Check for existing session. */
- if (this._session) {
- return this._session
- }
- /* Request new session. */
- const session = await $fetch('/api/newFusion')
- console.log('INIT FUSION (after fetch):', session)
- /* Set session. */
- this._setFusion(session)
- /* Return session. */
- return session
- },
- async run() {
- // # Version check and download server params.
- self.greet()
- server_connected_and_greeted = True
- self.notify_server_status(True)
- # In principle we can hook a pause in here -- user can insert coins after seeing server params.
- if not self.coins:
- raise FusionError('Started with no coins')
- self.allocate_outputs()
- # In principle we can hook a pause in here -- user can tweak tier_outputs, perhaps cancelling some unwanted tiers.
- # Register for tiers, wait for a pool.
- self.register_and_wait()
- # launch the covert submitter
- covert = self.start_covert()
- try:
- # Pool started. Keep running rounds until fail or complete.
- while True:
- self.roundcount += 1
- if self.run_round(covert):
- break
- finally:
- covert.stop()
- }
- deleteFusion() {
- /* Set session. */
- this._setFusion(null)
- },
- saveFusion(_session) {
- /* Set session. */
- this._setFusion(_session)
- },
- /**
- * Set Fusion
- *
- * @param {Object} _session Save session details.
- */
- _setFusion (_session) {
- /* Set session. */
- this._session = _session
- console.log('SET FUSION', this._session)
- },
- /**
- * Set API Key
- *
- * @param {Object} _key Information for the Exchange's API key.
- */
- setApiKey (_key: Object) {
- /* Set session. */
- this._apiKeys[_key.exchangeid] = _key
- console.log('SET API KEY', this._apiKeys)
- },
- /**
- * Make up to `max_number` random output values, chosen using exponential
- distribution function. All parameters should be positive `int`s.
- None can be returned for expected types of failures, which will often occur
- when the input_amount is too small or too large, since it becomes uncommon
- to find a random assortment of values that satisfy the desired constraints.
- On success, this returns a list of length 1 to max_count, of non-negative
- integer values that sum up to exactly input_amount.
- The returned values will always exactly sum up to input_amount. This is done
- by renormalizing them, which means the actual effective `scale` will vary
- depending on random conditions.
- If `allow_extra_change` is passed (this is abnormal!) then this may return
- max_count+1 outputs; the last output will be the leftover change if all
- max_counts outputs were exhausted.
- */
- random_outputs_for_tier(rng, input_amount, scale, offset, max_count, allow_extra_change=False),
- /**
- * Generate a full set of fusion components, commitments, keys, and proofs.
- count: int
- inputs: dict of {(prevout_hash, prevout_n): (pubkey, integer value in sats)}
- outputs: list of [(value, addr), (value, addr) ...]
- feerate: int (sat/kB)
- Returns:
- list of InitialCommitment,
- list of component original indices (inputs then outputs then blanks),
- list of serialized Component,
- list of Proof,
- list of communication privkey,
- Pedersen amount for total, (== excess fee)
- Pedersen nonce for total,
- */
- gen_components(num_blanks, inputs, outputs, feerate),
- },
- })
|