44from time import sleep
55
66from commander import Commander
7- from ln_framework .ln import Policy
7+ from ln_framework .ln import (
8+ Policy ,
9+ CHANNEL_OPEN_START_HEIGHT ,
10+ CHANNEL_OPENS_PER_BLOCK ,
11+ MAX_FEE_RATE ,
12+ FEE_RATE_DECREMENT
13+ )
14+ from test_framework .messages import (
15+ COIN ,
16+ CTransaction ,
17+ CTxOut ,
18+ )
19+ from test_framework .address import address_to_scriptpubkey
820
921
1022class LNInit (Commander ):
@@ -39,13 +51,13 @@ def gen(n):
3951 # WALLET ADDRESSES
4052 ##
4153 self .log .info ("Getting LN wallet addresses..." )
42- ln_addrs = []
54+ ln_addrs = {}
4355
4456 def get_ln_addr (self , ln ):
4557 while True :
4658 try :
4759 address = ln .newaddress ()
48- ln_addrs . append ( address )
60+ ln_addrs [ ln . name ] = address
4961 self .log .info (f"Got wallet address { address } from { ln .name } " )
5062 break
5163 except Exception as e :
@@ -67,45 +79,71 @@ def get_ln_addr(self, ln):
6779 # FUNDS
6880 ##
6981 self .log .info ("Funding LN wallets..." )
70- # 298 block base for miner wallet
71- gen (297 )
82+ # One past block generated already to lock out IBD
83+ # One next block to consolidate the miner's coins
84+ # One next block to confirm the distributed coins
85+ # Then the channel open TXs go in the expected block height
86+ gen (CHANNEL_OPEN_START_HEIGHT - 4 )
7287 # divvy up the goods, except fee.
73- # 10 UTXOs per node means 10 channel opens per node per block
74- split = (miner .getbalance () - 1 ) // len (ln_addrs ) // 10
75- sends = {}
76- for _ in range (10 ):
77- for addr in ln_addrs :
78- sends [addr ] = split
79- miner .sendmany ("" , sends )
80- # confirm funds in block 299
88+ # Multiple UTXOs per LN wallet so multiple channels can be opened per block
89+ miner_balance = int (miner .getbalance ())
90+ # To reduce individual TX weight, consolidate all outputs before distribution
91+ miner .sendtoaddress (miner_addr , miner_balance - 1 )
8192 gen (1 )
93+ helicopter = CTransaction ()
8294
95+ # Provide the source LN node for each channel with a UTXO just big enough
96+ # to open that channel with its capacity plus fee.
97+ channel_openers = []
98+ for ch in self .channels :
99+ if ch ["source" ] not in channel_openers :
100+ channel_openers .append (ch ["source" ])
101+ addr = ln_addrs [ch ["source" ]]
102+ # More than enough to open the channel plus fee and cover LND's "maxFeeRatio"
103+ # As long as all channel capacities are < 4 BTC the change output will be
104+ # larger and occupy tx output 1, leaving the actual channel open at output 0
105+ sat_amt = 10 * COIN
106+ helicopter .vout .append (CTxOut (sat_amt , address_to_scriptpubkey (addr )))
107+ rawtx = miner .fundrawtransaction (helicopter .serialize ().hex ())
108+ signed_tx = miner .signrawtransactionwithwallet (rawtx ['hex' ])['hex' ]
109+ txid = miner .sendrawtransaction (signed_tx )
110+ # confirm funds in last block before channel opens
111+ gen (1 )
112+
113+ txstats = miner .gettransaction (txid )
83114 self .log .info (
84- f"Waiting for funds to be spendable: 10x{ split } BTC UTXOs each for { len (ln_addrs )} LN nodes"
115+ "Funds distribution from miner:\n "
116+ + f"txid: { txid } \n "
117+ + f"# outputs: { len (txstats ['details' ])} \n "
118+ + f"total amount: { txstats ['amount' ]} \n "
119+ + f"remaining miner balance: { miner .getbalance ()} "
85120 )
86121
87- def confirm_ln_balance (self , ln ):
122+ self .log .info ("Waiting for funds to be spendable by channel-openers" )
123+
124+ def confirm_ln_balance (self , ln_name ):
125+ ln = self .lns [ln_name ]
88126 while True :
89127 try :
90128 bal = ln .walletbalance ()
91- if bal >= ( split * 100000000 ) :
92- self .log .info (f"LN node { ln . name } confirmed funds" )
129+ if bal >= 0 :
130+ self .log .info (f"LN node { ln_name } confirmed funds" )
93131 break
94132 else :
95- self .log .info (f"Got balance from { ln . name } but less than expected, retrying in 5 seconds..." )
133+ self .log .info (f"Got 0 balance from { ln_name } retrying in 5 seconds..." )
96134 sleep (5 )
97135 except Exception as e :
98- self .log .info (f"Couldn't get balance from { ln . name } because { e } , retrying in 5 seconds..." )
136+ self .log .info (f"Couldn't get balance from { ln_name } because { e } , retrying in 5 seconds..." )
99137 sleep (5 )
100138
101139 fund_threads = [
102- threading .Thread (target = confirm_ln_balance , args = (self , ln )) for ln in self . lns . values ()
140+ threading .Thread (target = confirm_ln_balance , args = (self , ln_name )) for ln_name in channel_openers
103141 ]
104142 for thread in fund_threads :
105143 thread .start ()
106144
107145 all (thread .join () is None for thread in fund_threads )
108- self .log .info ("All LN nodes are funded" )
146+ self .log .info ("All channel-opening LN nodes are funded" )
109147
110148 ##
111149 # URIs
@@ -247,12 +285,14 @@ def open_channel(self, ch, fee_rate):
247285 sleep (5 )
248286
249287 channels = sorted (ch_by_block [target_block ], key = lambda ch : ch ["id" ]["index" ])
288+ if len (channels ) > CHANNEL_OPENS_PER_BLOCK :
289+ raise Exception (f"Too many channels in block { target_block } : { len (channels )} / Maximum: { CHANNEL_OPENS_PER_BLOCK } " )
250290 index = 0
251- fee_rate = 5006 # s/vB, decreases by 20 per tx for up to 250 txs per block
291+ fee_rate = MAX_FEE_RATE
252292 ch_threads = []
253293 for ch in channels :
254294 index += 1 # noqa
255- fee_rate -= 20
295+ fee_rate -= FEE_RATE_DECREMENT
256296 assert index == ch ["id" ]["index" ], "Channel ID indexes are not consecutive"
257297 assert fee_rate >= 1 , "Too many TXs in block, out of fee range"
258298 t = threading .Thread (target = open_channel , args = (self , ch , fee_rate ))
0 commit comments