def part2(self): paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) # Now we'll add HTLC of 3.5 BTC to Alice's commitment, this should put # Alice's balance at 1.5 BTC. # # Resulting balances: # Alice: 1.5 # Bob: 9.5 htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : int(3.5 * one_bitcoin_in_msat), 'cltv_expiry' : 5, } self.alice_channel.add_htlc(htlc_dict) self.bob_channel.receive_htlc(htlc_dict) # Add a second HTLC of 1 BTC. This should fail because it will take # Alice's balance all the way down to her channel reserve, but since # she is the initiator the additional transaction fee makes her # balance dip below. htlc_dict['amount_msat'] = one_bitcoin_in_msat with self.assertRaises(lnutil.PaymentFailure): self.alice_channel.add_htlc(htlc_dict) with self.assertRaises(lnutil.RemoteMisbehaving): self.bob_channel.receive_htlc(htlc_dict)
def part3(self): # Add a HTLC of 2 BTC to Alice, and the settle it. # Resulting balances: # Alice: 3.0 # Bob: 7.0 paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : int(2 * one_bitcoin_in_msat), 'cltv_expiry' : 5, 'timestamp' : 0, } alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id force_state_transition(self.alice_channel, self.bob_channel) self.check_bals(one_bitcoin_in_msat * 3 - self.alice_channel.get_next_fee(LOCAL), one_bitcoin_in_msat * 5) self.bob_channel.settle_htlc(paymentPreimage, bob_idx) self.alice_channel.receive_htlc_settle(paymentPreimage, alice_idx) force_state_transition(self.alice_channel, self.bob_channel) self.check_bals(one_bitcoin_in_msat * 3 - self.alice_channel.get_next_fee(LOCAL), one_bitcoin_in_msat * 7) # And now let Bob add an HTLC of 1 BTC. This will take Bob's balance # all the way down to his channel reserve, but since he is not paying # the fee this is okay. htlc_dict['amount_msat'] = one_bitcoin_in_msat self.bob_channel.add_htlc(htlc_dict) self.alice_channel.receive_htlc(htlc_dict) force_state_transition(self.alice_channel, self.bob_channel) self.check_bals(one_bitcoin_in_msat * 3 \ - self.alice_channel.get_next_fee(LOCAL), one_bitcoin_in_msat * 6)
def setUp(self): super().setUp() # Create a test channel which will be used for the duration of this # unittest. The channel will be funded evenly with Alice having 5 BTC, # and Bob having 5 BTC. self.alice_channel, self.bob_channel = create_test_channels() self.paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(self.paymentPreimage) self.htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : one_bitcoin_in_msat, 'cltv_expiry' : 5, 'timestamp' : 0, } # First Alice adds the outgoing HTLC to her local channel's state # update log. Then Alice sends this wire message over to Bob who adds # this htlc to his remote state update log. self.aliceHtlcIndex = self.alice_channel.add_htlc(self.htlc_dict).htlc_id self.assertNotEqual(list(self.alice_channel.hm.htlcs_by_direction(REMOTE, RECEIVED, 1).values()), []) before = self.bob_channel.balance_minus_outgoing_htlcs(REMOTE) beforeLocal = self.bob_channel.balance_minus_outgoing_htlcs(LOCAL) self.bobHtlcIndex = self.bob_channel.receive_htlc(self.htlc_dict).htlc_id self.htlc = self.bob_channel.hm.log[REMOTE]['adds'][0]
def test_DustLimit(self): alice_channel, bob_channel = create_test_channels() paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) fee_per_kw = alice_channel.get_next_feerate(LOCAL) self.assertEqual(fee_per_kw, 6000) htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000) self.assertEqual(htlcAmt, 4478) htlc = { 'payment_hash' : paymentHash, 'amount_msat' : 1000 * htlcAmt, 'cltv_expiry' : 5, # also in create_test_channels 'timestamp' : 0, } old_values = [x.value for x in bob_channel.get_latest_commitment(LOCAL).outputs()] aliceHtlcIndex = alice_channel.add_htlc(htlc).htlc_id bobHtlcIndex = bob_channel.receive_htlc(htlc).htlc_id force_state_transition(alice_channel, bob_channel) alice_ctx = alice_channel.get_latest_commitment(LOCAL) bob_ctx = bob_channel.get_latest_commitment(LOCAL) new_values = [x.value for x in bob_ctx.outputs()] self.assertNotEqual(old_values, new_values) self.assertEqual(len(alice_ctx.outputs()), 3) self.assertEqual(len(bob_ctx.outputs()), 2) default_fee = calc_static_fee(0) self.assertEqual(bob_channel.get_next_fee(LOCAL), default_fee + htlcAmt) bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex) alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex) force_state_transition(bob_channel, alice_channel) self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2) self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt)
class TestPeer(ElectrumTestCase): @classmethod def setUpClass(cls): super().setUpClass() console_stderr_handler.setLevel(logging.DEBUG) def setUp(self): super().setUp() self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() def tearDown(self): super().tearDown() self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) self._loop_thread.join(timeout=1) def prepare_peers(self, alice_channel, bob_channel): k1, k2 = keypair(), keypair() t1, t2 = transport_pair(alice_channel.name, bob_channel.name) q1, q2 = asyncio.Queue(), asyncio.Queue() w1 = MockLNWallet(k1, k2, alice_channel, tx_queue=q1) w2 = MockLNWallet(k2, k1, bob_channel, tx_queue=q2) p1 = Peer(w1, k1.pubkey, t1) p2 = Peer(w2, k2.pubkey, t2) w1.peer = p1 w2.peer = p2 # mark_open won't work if state is already OPEN. # so set it to FUNDED alice_channel._state = channel_states.FUNDED bob_channel._state = channel_states.FUNDED # this populates the channel graph: p1.mark_open(alice_channel) p2.mark_open(bob_channel) return p1, p2, w1, w2, q1, q2 @staticmethod def prepare_invoice( w2, # receiver *, amount_sat=100_000, ): amount_btc = amount_sat/Decimal(COIN) payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) info = PaymentInfo(RHASH, amount_sat, RECEIVED, PR_UNPAID) w2.save_preimage(RHASH, payment_preimage) w2.save_payment_info(info) lnaddr = LnAddr( RHASH, amount_btc, tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE), ('d', 'coffee') ]) return lnencode(lnaddr, w2.node_keypair.privkey)
def prepare_invoice(w2 # receiver ): amount_btc = 100000 / Decimal(COIN) payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) addr = LnAddr(RHASH, amount_btc, tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE), ('d', 'coffee')]) pay_req = lnencode(addr, w2.node_keypair.privkey) w2.preimages[bh2u(RHASH)] = bh2u(payment_preimage) w2.invoices[bh2u(RHASH)] = (pay_req, True, False) return pay_req
def test_part1(self): # Add an HTLC that will increase Bob's balance. This should succeed, # since Alice stays above her channel reserve, and Bob increases his # balance (while still being below his channel reserve). # # Resulting balances: # Alice: 4.5 # Bob: 5.0 paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : int(.5 * one_bitcoin_in_msat), 'cltv_expiry' : 5, 'timestamp' : 0, } self.alice_channel.add_htlc(htlc_dict) self.bob_channel.receive_htlc(htlc_dict) # Force a state transition, making sure this HTLC is considered valid # even though the channel reserves are not met. force_state_transition(self.alice_channel, self.bob_channel) aliceSelfBalance = self.alice_channel.balance(LOCAL)\ - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(LOCAL, SENT).values()) bobBalance = self.bob_channel.balance(REMOTE)\ - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(REMOTE, SENT).values()) self.assertEqual(aliceSelfBalance, one_bitcoin_in_msat*4.5) self.assertEqual(bobBalance, one_bitcoin_in_msat*5) # Now let Bob try to add an HTLC. This should fail, since it will # decrease his balance, which is already below the channel reserve. # # Resulting balances: # Alice: 4.5 # Bob: 5.0 with self.assertRaises(lnutil.PaymentFailure): htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02') self.bob_channel.add_htlc(htlc_dict) with self.assertRaises(lnutil.RemoteMisbehaving): self.alice_channel.receive_htlc(htlc_dict)
def prepare_invoice(w2 # receiver ): amount_sat = 100000 amount_btc = amount_sat / Decimal(COIN) payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) info = PaymentInfo(RHASH, amount_sat, RECEIVED, PR_UNPAID) w2.save_preimage(RHASH, payment_preimage) w2.save_payment_info(info) lnaddr = LnAddr(RHASH, amount_btc, tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE), ('d', 'coffee')]) return lnencode(lnaddr, w2.node_keypair.privkey)
def test_AddHTLCNegativeBalance(self): # the test in lnd doesn't set the fee to zero. # probably lnd subtracts commitment fee after deciding weather # an htlc can be added. so we set the fee to zero so that # the test can work. self.alice_to_bob_fee_update(0) force_state_transition(self.alice_channel, self.bob_channel) self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02') self.alice_channel.add_htlc(self.htlc_dict) self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x03') self.alice_channel.add_htlc(self.htlc_dict) # now there are three htlcs (one was in setUp) # Alice now has an available balance of 2 BTC. We'll add a new HTLC of # value 2 BTC, which should make Alice's balance negative (since she # has to pay a commitment fee). new = dict(self.htlc_dict) new['amount_msat'] *= 2.5 new['payment_hash'] = bitcoin.sha256(32 * b'\x04') with self.assertRaises(lnutil.PaymentFailure) as cm: self.alice_channel.add_htlc(new) self.assertIn('Not enough local balance', cm.exception.args[0])
def test_shachain_produce_consume(self): seed = bitcoin.sha256(b"shachaintest") consumer = RevocationStore(StoredDict({}, None, [])) for i in range(10000): secret = get_per_commitment_secret_from_seed(seed, RevocationStore.START_INDEX - i) try: consumer.add_next_entry(secret) except Exception as e: raise Exception("iteration " + str(i) + ": " + str(e)) if i % 1000 == 0: c1 = consumer s1 = json.dumps(c1.storage, cls=MyEncoder) c2 = RevocationStore(StoredDict(json.loads(s1), None, [])) s2 = json.dumps(c2.storage, cls=MyEncoder) self.assertEqual(s1, s2)
def test_shachain_produce_consume(self): seed = bitcoin.sha256(b"shachaintest") consumer = RevocationStore() for i in range(10000): secret = get_per_commitment_secret_from_seed( seed, RevocationStore.START_INDEX - i) try: consumer.add_next_entry(secret) except Exception as e: raise Exception("iteration " + str(i) + ": " + str(e)) if i % 1000 == 0: self.assertEqual( consumer.serialize(), RevocationStore.from_json_obj( json.loads(json.dumps( consumer.serialize()))).serialize())
def test_DesyncHTLCs(self): alice_channel, bob_channel = create_test_channels() self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : one_bitcoin_in_msat * 41 // 10, 'cltv_expiry' : 5, 'timestamp' : 0, } alice_idx = alice_channel.add_htlc(htlc_dict).htlc_id bob_idx = bob_channel.receive_htlc(htlc_dict).htlc_id self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) force_state_transition(alice_channel, bob_channel) bob_channel.fail_htlc(bob_idx) alice_channel.receive_fail_htlc(alice_idx, error_bytes=None) self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) # Alice now has gotten all her original balance (5 BTC) back, however, # adding a new HTLC at this point SHOULD fail, since if she adds the # HTLC and signs the next state, Bob cannot assume she received the # FailHTLC, and must assume she doesn't have the necessary balance # available. # We try adding an HTLC of value 1 BTC, which should fail because the # balance is unavailable. htlc_dict = { 'payment_hash' : paymentHash, 'amount_msat' : one_bitcoin_in_msat, 'cltv_expiry' : 5, 'timestamp' : 0, } with self.assertRaises(lnutil.PaymentFailure): alice_channel.add_htlc(htlc_dict) # Now do a state transition, which will ACK the FailHTLC, making Alice # able to add the new HTLC. force_state_transition(alice_channel, bob_channel) self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) alice_channel.add_htlc(htlc_dict)
def test_max_htlc_value(self): alice_channel, bob_channel = create_test_channels() paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { 'payment_hash': paymentHash, 'amount_msat': one_bitcoin_in_msat * 41 // 10, 'cltv_expiry': 5, 'timestamp': 0, } #I can't prepare the quantity for test. TODO monacoin is OK? #alice_channel._ignore_max_htlc_value = False #bob_channel._ignore_max_htlc_value = False #with self.assertRaises(lnutil.PaymentFailure): # alice_channel.add_htlc(htlc_dict) #with self.assertRaises(lnutil.RemoteMisbehaving): # bob_channel.receive_htlc(htlc_dict) alice_channel._ignore_max_htlc_value = True bob_channel._ignore_max_htlc_value = True alice_channel.add_htlc(htlc_dict) bob_channel.receive_htlc(htlc_dict)
def test_concurrent_reversed_payment(self): self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02') self.htlc_dict['amount_msat'] += 1000 self.bob_channel.add_htlc(self.htlc_dict) self.alice_channel.receive_htlc(self.htlc_dict) self.assertEqual( len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 2) self.assertEqual( len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) self.assertEqual( len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) self.assertEqual( len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3) self.alice_channel.receive_new_commitment( *self.bob_channel.sign_next_commitment()) self.assertEqual( len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3) self.assertEqual( len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) self.assertEqual( len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) self.assertEqual( len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3) self.alice_channel.revoke_current_commitment() self.assertEqual( len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3) self.assertEqual( len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) self.assertEqual( len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) self.assertEqual( len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 4)
chan_ca=chan_ca, chan_cd=chan_cd, chan_db=chan_db, chan_dc=chan_dc, ) @staticmethod async def prepare_invoice( w2: MockLNWallet, # receiver *, amount_sat=100_000, include_routing_hints=False, ): amount_btc = amount_sat / Decimal(COIN) payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) info = PaymentInfo(RHASH, amount_sat, RECEIVED, PR_UNPAID) w2.save_preimage(RHASH, payment_preimage) w2.save_payment_info(info) if include_routing_hints: routing_hints = await w2._calc_routing_hints_for_invoice(amount_sat ) else: routing_hints = [] lnaddr = LnAddr(paymenthash=RHASH, amount=amount_btc, tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE), ('d', 'coffee')] + routing_hints) return lnencode(lnaddr, w2.node_keypair.privkey) def test_reestablish(self):
def test_commitment_tx_with_all_five_HTLCs_untrimmed_minimum_feerate(self): to_local_msat = 6988000000 to_remote_msat = 3000000000 local_feerate_per_kw = 0 # base commitment transaction fee = 0 # actual commitment transaction fee = 0 per_commitment_secret = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 per_commitment_point = secret_to_pubkey(per_commitment_secret) remote_htlcpubkey = remotepubkey local_htlcpubkey = localpubkey htlc_cltv_timeout = {} htlc_payment_preimage = {} htlc = {} htlc_cltv_timeout[2] = 502 htlc_payment_preimage[2] = b"\x02" * 32 htlc[2] = make_offered_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[2])) htlc_cltv_timeout[3] = 503 htlc_payment_preimage[3] = b"\x03" * 32 htlc[3] = make_offered_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[3])) htlc_cltv_timeout[0] = 500 htlc_payment_preimage[0] = b"\x00" * 32 htlc[0] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[0]), htlc_cltv_timeout[0]) htlc_cltv_timeout[1] = 501 htlc_payment_preimage[1] = b"\x01" * 32 htlc[1] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[1]), htlc_cltv_timeout[1]) htlc_cltv_timeout[4] = 504 htlc_payment_preimage[4] = b"\x04" * 32 htlc[4] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[4]), htlc_cltv_timeout[4]) remote_signature = "304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b70606" output_commit_tx = "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad007000000000000220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110e0a06a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004730440220275b0c325a5e9355650dc30c0eccfbc7efb23987c24b556b9dfdd40effca18d202206caceb2c067836c51f296740c7ae807ffcbfbf1dd3a0d56b6de9a5b247985f060147304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b7060601475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" htlc_obj = {} for num, msat in [(0, 1000 * 1000), (2, 2000 * 1000), (1, 2000 * 1000), (3, 3000 * 1000), (4, 4000 * 1000)]: htlc_obj[num] = UpdateAddHtlc(amount_msat=msat, payment_hash=bitcoin.sha256(htlc_payment_preimage[num]), cltv_expiry=0, htlc_id=None, timestamp=0) htlcs = [ScriptHtlc(htlc[x], htlc_obj[x]) for x in range(5)] our_commit_tx = make_commitment( commitment_number, local_funding_pubkey, remote_funding_pubkey, remotepubkey, local_payment_basepoint, remote_payment_basepoint, local_revocation_pubkey, local_delayedpubkey, local_delay, funding_tx_id, funding_output_index, funding_amount_satoshi, to_local_msat, to_remote_msat, local_dust_limit_satoshi, calc_onchain_fees(len(htlcs), local_feerate_per_kw, True), htlcs=htlcs) self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey) self.assertEqual(str(our_commit_tx), output_commit_tx) signature_for_output_remote_htlc = {} signature_for_output_remote_htlc[0] = "304402206a6e59f18764a5bf8d4fa45eebc591566689441229c918b480fb2af8cc6a4aeb02205248f273be447684b33e3c8d1d85a8e0ca9fa0bae9ae33f0527ada9c162919a6" signature_for_output_remote_htlc[2] = "3045022100d5275b3619953cb0c3b5aa577f04bc512380e60fa551762ce3d7a1bb7401cff9022037237ab0dac3fe100cde094e82e2bed9ba0ed1bb40154b48e56aa70f259e608b" signature_for_output_remote_htlc[1] = "304402201b63ec807771baf4fdff523c644080de17f1da478989308ad13a58b51db91d360220568939d38c9ce295adba15665fa68f51d967e8ed14a007b751540a80b325f202" signature_for_output_remote_htlc[3] = "3045022100daee1808f9861b6c3ecd14f7b707eca02dd6bdfc714ba2f33bc8cdba507bb182022026654bf8863af77d74f51f4e0b62d461a019561bb12acb120d3f7195d148a554" signature_for_output_remote_htlc[4] = "304402207e0410e45454b0978a623f36a10626ef17b27d9ad44e2760f98cfa3efb37924f0220220bd8acd43ecaa916a80bd4f919c495a2c58982ce7c8625153f8596692a801d" output_htlc_tx = {} SUCCESS = True TIMEOUT = False output_htlc_tx[0] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219700000000000000000001e8030000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402206a6e59f18764a5bf8d4fa45eebc591566689441229c918b480fb2af8cc6a4aeb02205248f273be447684b33e3c8d1d85a8e0ca9fa0bae9ae33f0527ada9c162919a60147304402207cb324fa0de88f452ffa9389678127ebcf4cabe1dd848b8e076c1a1962bf34720220116ed922b12311bd602d67e60d2529917f21c5b82f25ff6506c0f87886b4dfd5012000000000000000000000000000000000000000000000000000000000000000008a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc688527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f401b175ac686800000000") output_htlc_tx[2] = (TIMEOUT, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219701000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0500483045022100d5275b3619953cb0c3b5aa577f04bc512380e60fa551762ce3d7a1bb7401cff9022037237ab0dac3fe100cde094e82e2bed9ba0ed1bb40154b48e56aa70f259e608b0147304402205735e9f335dfd123f730ac5bf184fd7d5b672e4d84c51a3f0478cc229bb44936022018b1cec3e3b29e5cc335d7e326bc29d75a7e063216427d081cb83ebdbd828b4d01008576a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac6868f6010000") output_htlc_tx[1] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219702000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402201b63ec807771baf4fdff523c644080de17f1da478989308ad13a58b51db91d360220568939d38c9ce295adba15665fa68f51d967e8ed14a007b751540a80b325f202014730440220481a48f83c358ae0f220e37f88e56b3d434cefaded82065b8e7a9fd78fee7a26022022674ab37a4c39e6efba302f760ca05931d8add8d65231c5bf34a6c2a76b15bf012001010101010101010101010101010101010101010101010101010101010101018a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f501b175ac686800000000") output_htlc_tx[3] = (TIMEOUT, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219703000000000000000001b80b0000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0500483045022100daee1808f9861b6c3ecd14f7b707eca02dd6bdfc714ba2f33bc8cdba507bb182022026654bf8863af77d74f51f4e0b62d461a019561bb12acb120d3f7195d148a554014730440220643aacb19bbb72bd2b635bc3f7375481f5981bace78cdd8319b2988ffcc6704202203d27784ec8ad51ed3bd517a05525a5139bb0b755dd719e0054332d186ac0872701008576a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac6868f7010000") output_htlc_tx[4] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219704000000000000000001a00f0000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402207e0410e45454b0978a623f36a10626ef17b27d9ad44e2760f98cfa3efb37924f0220220bd8acd43ecaa916a80bd4f919c495a2c58982ce7c8625153f8596692a801d014730440220549e80b4496803cbc4a1d09d46df50109f546d43fbbf86cd90b174b1484acd5402205f12a4f995cb9bded597eabfee195a285986aa6d93ae5bb72507ebc6a4e2349e012004040404040404040404040404040404040404040404040404040404040404048a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a91418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f801b175ac686800000000") htlc_output_index = {0: 0, 1: 2, 2: 1, 3: 3, 4: 4} for i in range(5): self.assertEqual(output_htlc_tx[i][1], self.htlc_tx(htlc[i], htlc_output_index[i], htlcs[i].htlc.amount_msat, htlc_payment_preimage[i], signature_for_output_remote_htlc[i], output_htlc_tx[i][0], htlc_cltv_timeout[i] if not output_htlc_tx[i][0] else 0, local_feerate_per_kw, our_commit_tx))
class TestPeer(ElectrumTestCase): @classmethod def setUpClass(cls): super().setUpClass() console_stderr_handler.setLevel(logging.DEBUG) def setUp(self): super().setUp() self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() self._lnworkers_created = [] # type: List[MockLNWallet] def tearDown(self): async def cleanup_lnworkers(): async with TaskGroup() as group: for lnworker in self._lnworkers_created: await group.spawn(lnworker.stop()) self._lnworkers_created.clear() run(cleanup_lnworkers()) self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) self._loop_thread.join(timeout=1) super().tearDown() def prepare_peers(self, alice_channel, bob_channel): k1, k2 = keypair(), keypair() alice_channel.node_id = k2.pubkey bob_channel.node_id = k1.pubkey t1, t2 = transport_pair(k1, k2, alice_channel.name, bob_channel.name) q1, q2 = asyncio.Queue(), asyncio.Queue() w1 = MockLNWallet(local_keypair=k1, chans=[alice_channel], tx_queue=q1, name=bob_channel.name) w2 = MockLNWallet(local_keypair=k2, chans=[bob_channel], tx_queue=q2, name=alice_channel.name) self._lnworkers_created.extend([w1, w2]) p1 = Peer(w1, k2.pubkey, t1) p2 = Peer(w2, k1.pubkey, t2) w1._peers[p1.pubkey] = p1 w2._peers[p2.pubkey] = p2 # mark_open won't work if state is already OPEN. # so set it to FUNDED alice_channel._state = ChannelState.FUNDED bob_channel._state = ChannelState.FUNDED # this populates the channel graph: p1.mark_open(alice_channel) p2.mark_open(bob_channel) return p1, p2, w1, w2, q1, q2 def prepare_chans_and_peers_in_square(self) -> SquareGraph: key_a, key_b, key_c, key_d = [keypair() for i in range(4)] chan_ab, chan_ba = create_test_channels(alice_name="alice", bob_name="bob", alice_pubkey=key_a.pubkey, bob_pubkey=key_b.pubkey) chan_ac, chan_ca = create_test_channels(alice_name="alice", bob_name="carol", alice_pubkey=key_a.pubkey, bob_pubkey=key_c.pubkey) chan_bd, chan_db = create_test_channels(alice_name="bob", bob_name="dave", alice_pubkey=key_b.pubkey, bob_pubkey=key_d.pubkey) chan_cd, chan_dc = create_test_channels(alice_name="carol", bob_name="dave", alice_pubkey=key_c.pubkey, bob_pubkey=key_d.pubkey) trans_ab, trans_ba = transport_pair(key_a, key_b, chan_ab.name, chan_ba.name) trans_ac, trans_ca = transport_pair(key_a, key_c, chan_ac.name, chan_ca.name) trans_bd, trans_db = transport_pair(key_b, key_d, chan_bd.name, chan_db.name) trans_cd, trans_dc = transport_pair(key_c, key_d, chan_cd.name, chan_dc.name) txq_a, txq_b, txq_c, txq_d = [asyncio.Queue() for i in range(4)] w_a = MockLNWallet(local_keypair=key_a, chans=[chan_ab, chan_ac], tx_queue=txq_a, name="alice") w_b = MockLNWallet(local_keypair=key_b, chans=[chan_ba, chan_bd], tx_queue=txq_b, name="bob") w_c = MockLNWallet(local_keypair=key_c, chans=[chan_ca, chan_cd], tx_queue=txq_c, name="carol") w_d = MockLNWallet(local_keypair=key_d, chans=[chan_db, chan_dc], tx_queue=txq_d, name="dave") self._lnworkers_created.extend([w_a, w_b, w_c, w_d]) peer_ab = Peer(w_a, key_b.pubkey, trans_ab) peer_ac = Peer(w_a, key_c.pubkey, trans_ac) peer_ba = Peer(w_b, key_a.pubkey, trans_ba) peer_bd = Peer(w_b, key_d.pubkey, trans_bd) peer_ca = Peer(w_c, key_a.pubkey, trans_ca) peer_cd = Peer(w_c, key_d.pubkey, trans_cd) peer_db = Peer(w_d, key_b.pubkey, trans_db) peer_dc = Peer(w_d, key_c.pubkey, trans_dc) w_a._peers[peer_ab.pubkey] = peer_ab w_a._peers[peer_ac.pubkey] = peer_ac w_b._peers[peer_ba.pubkey] = peer_ba w_b._peers[peer_bd.pubkey] = peer_bd w_c._peers[peer_ca.pubkey] = peer_ca w_c._peers[peer_cd.pubkey] = peer_cd w_d._peers[peer_db.pubkey] = peer_db w_d._peers[peer_dc.pubkey] = peer_dc w_b.network.config.set_key('lightning_forward_payments', True) w_c.network.config.set_key('lightning_forward_payments', True) # forwarding fees, etc chan_ab.forwarding_fee_proportional_millionths *= 500 chan_ab.forwarding_fee_base_msat *= 500 chan_ba.forwarding_fee_proportional_millionths *= 500 chan_ba.forwarding_fee_base_msat *= 500 chan_bd.forwarding_fee_proportional_millionths *= 500 chan_bd.forwarding_fee_base_msat *= 500 chan_db.forwarding_fee_proportional_millionths *= 500 chan_db.forwarding_fee_base_msat *= 500 # mark_open won't work if state is already OPEN. # so set it to FUNDED for chan in [chan_ab, chan_ac, chan_ba, chan_bd, chan_ca, chan_cd, chan_db, chan_dc]: chan._state = ChannelState.FUNDED # this populates the channel graph: peer_ab.mark_open(chan_ab) peer_ac.mark_open(chan_ac) peer_ba.mark_open(chan_ba) peer_bd.mark_open(chan_bd) peer_ca.mark_open(chan_ca) peer_cd.mark_open(chan_cd) peer_db.mark_open(chan_db) peer_dc.mark_open(chan_dc) return SquareGraph( w_a=w_a, w_b=w_b, w_c=w_c, w_d=w_d, peer_ab=peer_ab, peer_ac=peer_ac, peer_ba=peer_ba, peer_bd=peer_bd, peer_ca=peer_ca, peer_cd=peer_cd, peer_db=peer_db, peer_dc=peer_dc, chan_ab=chan_ab, chan_ac=chan_ac, chan_ba=chan_ba, chan_bd=chan_bd, chan_ca=chan_ca, chan_cd=chan_cd, chan_db=chan_db, chan_dc=chan_dc, ) @staticmethod async def prepare_invoice( w2: MockLNWallet, # receiver *, amount_msat=100_000_000, include_routing_hints=False, ) -> Tuple[LnAddr, str]: amount_btc = amount_msat/Decimal(COIN*1000) payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) info = PaymentInfo(RHASH, amount_msat, RECEIVED, PR_UNPAID) w2.save_preimage(RHASH, payment_preimage) w2.save_payment_info(info) if include_routing_hints: routing_hints = await w2._calc_routing_hints_for_invoice(amount_msat) else: routing_hints = [] trampoline_hints = [] for r in routing_hints: node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0] if len(r[1])== 1 and w2.is_trampoline_peer(node_id): trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta))) invoice_features = w2.features.for_invoice() if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT): payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage) else: payment_secret = None lnaddr1 = LnAddr( paymenthash=RHASH, amount=amount_btc, tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE), ('d', 'coffee'), ('9', invoice_features), ] + routing_hints + trampoline_hints, payment_secret=payment_secret, ) invoice = lnencode(lnaddr1, w2.node_keypair.privkey) lnaddr2 = lndecode(invoice) # unlike lnaddr1, this now has a pubkey set return lnaddr2, invoice