From 4566cb549448d67bec1e1b55cc7809f198e757c5 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Tue, 1 Sep 2015 09:51:31 -0700 Subject: [PATCH 01/11] need to diff to debug --- channel.py | 16 +- test/test_integration.py | 348 +++++++++++++++++++-------------------- 2 files changed, 187 insertions(+), 177 deletions(-) diff --git a/channel.py b/channel.py index 0a6127b..da9b5e9 100644 --- a/channel.py +++ b/channel.py @@ -175,12 +175,15 @@ def get_pubkey(): def update_db(address, amount, sig): """Update the db for a payment.""" - channel = Channel.query.get(address) + channel = Channel.query.get(address) # address is lightning address, address is primary key + + # verify that signature is valid here + channel.our_balance += amount channel.their_balance -= amount channel.their_sig = sig database.session.commit() - return channel.signature(channel.commitment()) + return channel.signature(channel.commitment()) # this is our signature of the comittment def create(url, mymoney, theirmoney, fees=10000): """Open a payment channel. @@ -191,10 +194,15 @@ def create(url, mymoney, theirmoney, fees=10000): setup and teardown of the channel should be collected at this time. """ bob = jsonrpcproxy.Proxy(url+'channel/') + print ("bob: " + url + "channel/") # Choose inputs and change output coins, change = select_coins(mymoney + 2 * fees) + print ("coins: " + coins + ", change: ") pubkey = get_pubkey() + print ("pubkey: " + pubkey) my_out_addr = g.bit.getnewaddress() + print ("my out (bitcoin adddress for money when we close the channel) addr: " + my_out_addr) + print ("g.addr (our lightning address: " + g.addr ) # Tell Bob we want to open a channel transaction, redeem, their_out_addr = bob.open_channel( g.addr, theirmoney, mymoney, fees, @@ -219,7 +227,9 @@ def create(url, mymoney, theirmoney, fees=10000): # Exchange signatures for the inital commitment transaction channel.their_sig = \ bob.update_anchor(g.addr, transaction.GetHash(), - channel.signature(channel.commitment())) + channel.signature(channel.commitment())) + # channel.signature(channel.commitment()) is our signature for the comitment + # database.session.add(channel) database.session.commit() # Event: channel opened diff --git a/test/test_integration.py b/test/test_integration.py index a3d2628..41764c3 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -68,191 +68,191 @@ def test_basic(self): self.assertGreaterEqual(self.alice.bit.getbalance(), 95000000 - afee) self.assertGreaterEqual(self.bob.bit.getbalance(), 105000000 - bfee) - def test_stress(self): - """Test edge cases in payment channels.""" - # Open *two* payment channels Bob - Alice - Carol - self.alice.lit.create(self.bob.lurl, 25000000, 50000000) - self.propagate() - self.carol.lit.create(self.alice.lurl, 50000000, 25000000) - self.propagate() - # Account for fees - afee = 50000000 - self.alice.bit.getbalance() - bfee = 50000000 - self.bob.bit.getbalance() - self.assertGreaterEqual(afee, 0) - self.assertGreaterEqual(bfee, 0) - # Balance (A-C) Alice: 0.25 BTC, Carol: 0.50 BTC - # Balance (B-A) Bob: 0.50 BTC, Alice: 0.25 BTC - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 25000000) - self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 25000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 50000000) - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) - # Carol sends 0.25 BTC to Alice - self.carol.lit.send(self.alice.lurl, 25000000) - # Balance (A-C) Alice: 0.50 BTC, Carol: 0.25 BTC - self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 25000000) - # Alice sends 0.15 BTC to Carol - self.alice.lit.send(self.carol.lurl, 15000000) - # Balance (A-C) Alice: 0.35 BTC, Carol: 0.40 BTC - self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 35000000) - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 40000000) - # Bob sends Alice 0.50 BTC (his whole balance) - self.bob.lit.send(self.alice.lurl, 50000000) - # Balance (B-A) Bob: 0.00 BTC, Alice: 0.75 BTC - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 75000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 0) - # Alice sends Bob 0.75 BTC (her whole balance) - self.alice.lit.send(self.bob.lurl, 75000000) - # Balance (B-A) Bob: 0.75 BTC, Alice: 0.00 BTC - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 0) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 75000000) - # Alice closes the channel with Bob, on an empty account (Alice opened) - self.alice.lit.close(self.bob.lurl) - self.propagate() - self.assertGreaterEqual(self.alice.bit.getbalance(), 50000000 - afee) - self.assertGreaterEqual(self.bob.bit.getbalance(), 125000000 - bfee) - # Alice closes the channel with Carol (Carol opened) - self.alice.lit.close(self.carol.lurl) - self.propagate() - self.assertGreaterEqual(self.alice.bit.getbalance(), 85000000 - afee) +# def test_stress(self): +# """Test edge cases in payment channels.""" +# # Open *two* payment channels Bob - Alice - Carol +# self.alice.lit.create(self.bob.lurl, 25000000, 50000000) +# self.propagate() +# self.carol.lit.create(self.alice.lurl, 50000000, 25000000) +# self.propagate() +# # Account for fees +# afee = 50000000 - self.alice.bit.getbalance() +# bfee = 50000000 - self.bob.bit.getbalance() +# self.assertGreaterEqual(afee, 0) +# self.assertGreaterEqual(bfee, 0) +# # Balance (A-C) Alice: 0.25 BTC, Carol: 0.50 BTC +# # Balance (B-A) Bob: 0.50 BTC, Alice: 0.25 BTC +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 25000000) +# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 25000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 50000000) +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) +# # Carol sends 0.25 BTC to Alice +# self.carol.lit.send(self.alice.lurl, 25000000) +# # Balance (A-C) Alice: 0.50 BTC, Carol: 0.25 BTC +# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 25000000) +# # Alice sends 0.15 BTC to Carol +# self.alice.lit.send(self.carol.lurl, 15000000) +# # Balance (A-C) Alice: 0.35 BTC, Carol: 0.40 BTC +# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 35000000) +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 40000000) +# # Bob sends Alice 0.50 BTC (his whole balance) +# self.bob.lit.send(self.alice.lurl, 50000000) +# # Balance (B-A) Bob: 0.00 BTC, Alice: 0.75 BTC +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 75000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 0) +# # Alice sends Bob 0.75 BTC (her whole balance) +# self.alice.lit.send(self.bob.lurl, 75000000) +# # Balance (B-A) Bob: 0.75 BTC, Alice: 0.00 BTC +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 0) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 75000000) +# # Alice closes the channel with Bob, on an empty account (Alice opened) +# self.alice.lit.close(self.bob.lurl) +# self.propagate() +# self.assertGreaterEqual(self.alice.bit.getbalance(), 50000000 - afee) +# self.assertGreaterEqual(self.bob.bit.getbalance(), 125000000 - bfee) +# # Alice closes the channel with Carol (Carol opened) +# self.alice.lit.close(self.carol.lurl) +# self.propagate() +# self.assertGreaterEqual(self.alice.bit.getbalance(), 85000000 - afee) - def test_unilateral_close(self): - """Test unilateral close.""" - # Set up channel between Alice and Bob - self.alice.lit.create(self.bob.lurl, 50000000, 25000000) - self.propagate() - afee = 50000000 - self.alice.bit.getbalance() - bfee = 75000000 - self.bob.bit.getbalance() - self.assertGreaterEqual(afee, 0) - self.assertGreaterEqual(bfee, 0) - # Do some transactions - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) - self.bob.lit.send(self.alice.lurl, 5000000) - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) - # Pause Bob - with self.bob.paused(): - # Publish Alice's commitment transactions - commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) - for transaction in commitment: - self.alice.bit.sendrawtransaction(transaction) - self.propagate() - time.sleep(1) - self.propagate() - self.propagate() - # Alice and Bob get their money out - self.assertGreaterEqual(self.bob.bit.getbalance(), 95000000 - bfee) - self.assertGreaterEqual(self.alice.bit.getbalance(), 105000000 - afee) +# def test_unilateral_close(self): +# """Test unilateral close.""" +# # Set up channel between Alice and Bob +# self.alice.lit.create(self.bob.lurl, 50000000, 25000000) +# self.propagate() +# afee = 50000000 - self.alice.bit.getbalance() +# bfee = 75000000 - self.bob.bit.getbalance() +# self.assertGreaterEqual(afee, 0) +# self.assertGreaterEqual(bfee, 0) +# # Do some transactions +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) +# self.bob.lit.send(self.alice.lurl, 5000000) +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) +# # Pause Bob +# with self.bob.paused(): +# # Publish Alice's commitment transactions +# commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) +# for transaction in commitment: +# self.alice.bit.sendrawtransaction(transaction) +# self.propagate() +# time.sleep(1) +# self.propagate() +# self.propagate() +# # Alice and Bob get their money out +# self.assertGreaterEqual(self.bob.bit.getbalance(), 95000000 - bfee) +# self.assertGreaterEqual(self.alice.bit.getbalance(), 105000000 - afee) - @unittest.expectedFailure - def test_revoked(self): - """Test a revoked commitment transaction being published.""" - # Set up channel between Alice and Bob - self.alice.lit.create(self.bob.lurl, 50000000, 25000000) - self.propagate() - afee = 50000000 - self.alice.bit.getbalance() - bfee = 75000000 - self.bob.bit.getbalance() - self.assertGreaterEqual(afee, 0) - self.assertGreaterEqual(bfee, 0) - # Make a transaction - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) - self.bob.lit.send(self.alice.lurl, 5000000) - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) - # Save Alice's old commitment transactions - commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) - # Do annother transaction, Alice sends Bob money - self.alice.lit.send(self.bob.lurl, 10000000) - self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 45000000) - self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 30000000) - # Alice publishes her old, revoked commitment transactions - for transaction in commitment: - self.alice.bit.sendrawtransaction(transaction) - self.propagate() - time.sleep(1) - self.propagate() - # Bob ends up with all the money - self.assertGreaterEqual(self.bob.bit.getbalance(), 150000000 - bfee) +# @unittest.expectedFailure +# def test_revoked(self): +# """Test a revoked commitment transaction being published.""" +# # Set up channel between Alice and Bob +# self.alice.lit.create(self.bob.lurl, 50000000, 25000000) +# self.propagate() +# afee = 50000000 - self.alice.bit.getbalance() +# bfee = 75000000 - self.bob.bit.getbalance() +# self.assertGreaterEqual(afee, 0) +# self.assertGreaterEqual(bfee, 0) +# # Make a transaction +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) +# self.bob.lit.send(self.alice.lurl, 5000000) +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) +# # Save Alice's old commitment transactions +# commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) +# # Do annother transaction, Alice sends Bob money +# self.alice.lit.send(self.bob.lurl, 10000000) +# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 45000000) +# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 30000000) +# # Alice publishes her old, revoked commitment transactions +# for transaction in commitment: +# self.alice.bit.sendrawtransaction(transaction) +# self.propagate() +# time.sleep(1) +# self.propagate() +# # Bob ends up with all the money +# self.assertGreaterEqual(self.bob.bit.getbalance(), 150000000 - bfee) -class TestLightning(unittest.TestCase): - """Run basic tests on payment channels.""" +# class TestLightning(unittest.TestCase): +# """Run basic tests on payment channels.""" - @classmethod - def setUpClass(cls): - cls.cache = regnet.make_cache() +# @classmethod +# def setUpClass(cls): +# cls.cache = regnet.make_cache() - @classmethod - def tearDownClass(cls): - cls.cache.cleanup() +# @classmethod +# def tearDownClass(cls): +# cls.cache.cleanup() - def propagate(self): - """Ensure all nodes up to date.""" - self.net.generate() +# def propagate(self): +# """Ensure all nodes up to date.""" +# self.net.generate() - def setUp(self): - # As in TestChannel, set up 3 nodes - self.net = regnet.create(datadir=None, cache=self.cache) - self.alice, self.bob, self.carol = self.net[0], self.net[1], self.net[2] - # Set up channels between so the network is Alice - Carol - Bob - self.alice.lit.create(self.carol.lurl, 50000000, 50000000) - self.propagate() - self.bob.lit.create(self.carol.lurl, 50000000, 50000000) - self.propagate() +# def setUp(self): +# # As in TestChannel, set up 3 nodes +# self.net = regnet.create(datadir=None, cache=self.cache) +# self.alice, self.bob, self.carol = self.net[0], self.net[1], self.net[2] +# # Set up channels between so the network is Alice - Carol - Bob +# self.alice.lit.create(self.carol.lurl, 50000000, 50000000) +# self.propagate() +# self.bob.lit.create(self.carol.lurl, 50000000, 50000000) +# self.propagate() - def tearDown(self): - self.net.stop(hard=True, cleanup=True) +# def tearDown(self): +# self.net.stop(hard=True, cleanup=True) - def test_setup(self): - """Test that the setup worked.""" - # (Balance) Alice-Carol: Alice: 0.50 BTC, Carol 0.50 BTC - # Carol-Bob : Carol: 0.50 BTC, Bob 0.50 BTC - # (Total) Alice: 0.50 BTC, Carol: 1.00 BTC, Bob: 0.50 BTC - self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) - self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 50000000) - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) - self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 50000000) +# def test_setup(self): +# """Test that the setup worked.""" +# # (Balance) Alice-Carol: Alice: 0.50 BTC, Carol 0.50 BTC +# # Carol-Bob : Carol: 0.50 BTC, Bob 0.50 BTC +# # (Total) Alice: 0.50 BTC, Carol: 1.00 BTC, Bob: 0.50 BTC +# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) +# self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 50000000) +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) +# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 50000000) - def test_payment(self): - """Test multi-hop payment.""" - # Note Alice and Bob do not have a payment channel open directly. - # They are connected through Carol - self.alice.lit.send(self.bob.lurl, 5000000) - # There is a fee associated with multi-hop payments - fee = 45000000 - self.alice.lit.getbalance(self.carol.lurl) - self.assertGreaterEqual(fee, 0) - # (Balance) Alice-Carol: Alice: 0.45 - fee BTC, Carol 0.55 + fee BTC - # Carol-Bob : Carol: 0.45 BTC, Bob 0.55 BTC - # (Total) Alice: 0.45 - fee BTC, Carol: 1.00 + fee BTC, Bob: 0.55 BTC - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 55000000 + fee) - self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 55000000) - self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 45000000) - # Send money the other direction - self.bob.lit.send(self.alice.lurl, 10000000) - # Annother fee will be deducted - fee2 = 45000000 - self.bob.lit.getbalance(self.carol.lurl) - self.assertGreaterEqual(fee2, 0) - # (Balance) Alice-Carol: Alice: 0.55 - fee BTC, Carol 0.45 + fee BTC - # Carol-Bob : Carol: 0.55 + fee2 BTC, Bob 0.45 - fee2 BTC - # (Total) Alice: 0.55 - fee BTC, Carol: 1.00 + fee + fee2 BTC, Bob: 0.45 - fee2 BTC - self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 45000000 + fee) - self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 55000000 - fee) - self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 55000000 + fee2) +# def test_payment(self): +# """Test multi-hop payment.""" +# # Note Alice and Bob do not have a payment channel open directly. +# # They are connected through Carol +# self.alice.lit.send(self.bob.lurl, 5000000) +# # There is a fee associated with multi-hop payments +# fee = 45000000 - self.alice.lit.getbalance(self.carol.lurl) +# self.assertGreaterEqual(fee, 0) +# # (Balance) Alice-Carol: Alice: 0.45 - fee BTC, Carol 0.55 + fee BTC +# # Carol-Bob : Carol: 0.45 BTC, Bob 0.55 BTC +# # (Total) Alice: 0.45 - fee BTC, Carol: 1.00 + fee BTC, Bob: 0.55 BTC +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 55000000 + fee) +# self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 55000000) +# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 45000000) +# # Send money the other direction +# self.bob.lit.send(self.alice.lurl, 10000000) +# # Annother fee will be deducted +# fee2 = 45000000 - self.bob.lit.getbalance(self.carol.lurl) +# self.assertGreaterEqual(fee2, 0) +# # (Balance) Alice-Carol: Alice: 0.55 - fee BTC, Carol 0.45 + fee BTC +# # Carol-Bob : Carol: 0.55 + fee2 BTC, Bob 0.45 - fee2 BTC +# # (Total) Alice: 0.55 - fee BTC, Carol: 1.00 + fee + fee2 BTC, Bob: 0.45 - fee2 BTC +# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 45000000 + fee) +# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 55000000 - fee) +# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 55000000 + fee2) - @unittest.expectedFailure - def test_route_close(self): - """Test routing around closed channels.""" - # Create a new channel between Alice and Bob - # so all 3 are connected to each other. - self.alice.lit.create(self.bob.lurl, 25000000, 25000000) - # Close the connection between Alice and Carol - self.alice.lit.close(self.carol.lurl) - self.propagate() - # Alice sends 0.10 BTC to Carol - self.alice.lit.send(self.carol.lurl, 10000000) - # Carol should have recieved money from Bob - self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 60000000) +# @unittest.expectedFailure +# def test_route_close(self): +# """Test routing around closed channels.""" +# # Create a new channel between Alice and Bob +# # so all 3 are connected to each other. +# self.alice.lit.create(self.bob.lurl, 25000000, 25000000) +# # Close the connection between Alice and Carol +# self.alice.lit.close(self.carol.lurl) +# self.propagate() +# # Alice sends 0.10 BTC to Carol +# self.alice.lit.send(self.carol.lurl, 10000000) +# # Carol should have recieved money from Bob +# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 60000000) if __name__ == '__main__': unittest.main() From ffa750ddbd6d4a01ec388aae042b462fca60b7d8 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Tue, 1 Sep 2015 09:57:24 -0700 Subject: [PATCH 02/11] need to diff to debug2 --- channel.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/channel.py b/channel.py index da9b5e9..7e91ca1 100644 --- a/channel.py +++ b/channel.py @@ -194,15 +194,15 @@ def create(url, mymoney, theirmoney, fees=10000): setup and teardown of the channel should be collected at this time. """ bob = jsonrpcproxy.Proxy(url+'channel/') - print ("bob: " + url + "channel/") + # print ("bob: " + url + "channel/") # Choose inputs and change output coins, change = select_coins(mymoney + 2 * fees) - print ("coins: " + coins + ", change: ") + # print ("coins: " + coins + ", change: ") pubkey = get_pubkey() - print ("pubkey: " + pubkey) + # print ("pubkey: " + pubkey) my_out_addr = g.bit.getnewaddress() - print ("my out (bitcoin adddress for money when we close the channel) addr: " + my_out_addr) - print ("g.addr (our lightning address: " + g.addr ) + # print ("my out (bitcoin adddress for money when we close the channel) addr: " + my_out_addr) + # print ("g.addr (our lightning address: " + g.addr ) # Tell Bob we want to open a channel transaction, redeem, their_out_addr = bob.open_channel( g.addr, theirmoney, mymoney, fees, From fe10b95600551501360da55d05b876a8278b10e9 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Tue, 1 Sep 2015 18:11:58 -0700 Subject: [PATCH 03/11] comitting again to debug previous --- channel.py | 107 ++++++++---- lightningd.py | 7 + test/test_integration.py | 351 ++++++++++++++++++++------------------- 3 files changed, 255 insertions(+), 210 deletions(-) diff --git a/channel.py b/channel.py index 7e91ca1..bdfeae5 100644 --- a/channel.py +++ b/channel.py @@ -8,9 +8,9 @@ - address -- the url of the counterparty init(conf) - Set up the database -create(url, mymoney, theirmoney) +create(url, my_money, their_money) - Open a channel with the node identified by url, - where you can send mymoney satoshis, and recieve theirmoney satoshis. + where you can send my_money satoshis, and recieve their_money satoshis. send(url, amount) - Update a channel by sending amount satoshis to the node at url. getbalance(url) @@ -106,7 +106,15 @@ def signature(self, transaction): """Signature for a transaction.""" sighash = SignatureHash(CScript(self.anchor_redeem), transaction, 0, SIGHASH_ALL) - sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) + g.logger.debug("### signature.sighash: \n" + str(sighash) ) + + sig = g.seckey.sign(sighash) + g.logger.debug("### signature.SIGHASH_ALL: " + str(SIGHASH_ALL) ) + g.logger.debug("### signature.bytes([SIGHASH_ALL]): \n" + str(bytes([SIGHASH_ALL])) ) + g.logger.debug("### signature.g.seckey.sign(sighash): \n" + str(sig) ) + + sig = sig + bytes([SIGHASH_ALL]) + g.logger.debug("### signature.sig: \n" + str(sig) ) return sig def sign(self, transaction): @@ -147,7 +155,7 @@ def settlement(self): return CMutableTransaction([CMutableTxIn(self.anchor_point)], [first, second]) -def select_coins(amount): +def select_outputs(amount): """Get a txin set and change to spend amount.""" coins = g.bit.listunspent() out = [] @@ -185,55 +193,76 @@ def update_db(address, amount, sig): database.session.commit() return channel.signature(channel.commitment()) # this is our signature of the comittment -def create(url, mymoney, theirmoney, fees=10000): +# def verify_sig() + +def create(theirUrl, my_money, their_money, fees=10000): """Open a payment channel. After this method returns, a payment channel will have been established - with the node identified by url, in which you can send mymoney satoshis - and recieve theirmoney satoshis. Any blockchain fees involved in the + with the node identified by theirUrl, in which you can send my_money satoshis + and recieve their_money satoshis. Any blockchain fees involved in the setup and teardown of the channel should be collected at this time. """ - bob = jsonrpcproxy.Proxy(url+'channel/') - # print ("bob: " + url + "channel/") + bob = jsonrpcproxy.Proxy(theirUrl+'channel/') + g.logger.debug("### bob: " + theirUrl + "channel/") # Choose inputs and change output - coins, change = select_coins(mymoney + 2 * fees) - # print ("coins: " + coins + ", change: ") - pubkey = get_pubkey() - # print ("pubkey: " + pubkey) + my_coins, my_change = select_outputs(my_money + 2 * fees) + g.logger.debug("### my_coins: " + str(my_coins)) + g.logger.debug("### my_change: " + str(my_change)) + my_pubkey = get_pubkey() + g.logger.debug("### my_pubkey: " + str(my_pubkey)) my_out_addr = g.bit.getnewaddress() - # print ("my out (bitcoin adddress for money when we close the channel) addr: " + my_out_addr) - # print ("g.addr (our lightning address: " + g.addr ) + g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + \ + str(my_out_addr)) + g.logger.debug("### g.addr, our lightning address: " + str(g.addr) ) # Tell Bob we want to open a channel transaction, redeem, their_out_addr = bob.open_channel( - g.addr, theirmoney, mymoney, fees, - coins, change, - pubkey, my_out_addr) + g.addr, their_money, my_money, fees, + my_coins, my_change, + my_pubkey, my_out_addr) # Sign and send the anchor + g.logger.debug("### transaction: " + str(transaction) ) + g.logger.debug("### redeem: " + str(redeem) ) + g.logger.debug("### their_out_addr: " + str(their_out_addr) ) transaction = g.bit.signrawtransaction(transaction) + g.logger.debug("### transaction post signing: " + str(redeem) ) + + + assert transaction['complete'] transaction = transaction['tx'] g.bit.sendrawtransaction(transaction) # Set up the channel in the DB - channel = Channel(address=url, + channel = Channel(address=theirUrl, anchor_point=COutPoint(transaction.GetHash(), 0), anchor_index=1, their_sig=b'', anchor_redeem=redeem, - our_balance=mymoney, + our_balance=my_money, our_addr=my_out_addr, - their_balance=theirmoney, + their_balance=their_money, their_addr=their_out_addr, ) # Exchange signatures for the inital commitment transaction - channel.their_sig = \ - bob.update_anchor(g.addr, transaction.GetHash(), - channel.signature(channel.commitment())) - # channel.signature(channel.commitment()) is our signature for the comitment - # + their_sig = bob.update_anchor(g.addr, transaction.GetHash(), + channel.signature(channel.commitment()), my_pubkey) + # channel.signature(channel.commitment()) is our signature for the comitment + # their_sig = channel.signature(channel.commitment()) + # channel.signature() returns + # transaction.GetHash() = TXID + + + # Verify Bob's signature + + + channel.their_sig = their_sig + + + database.session.add(channel) database.session.commit() # Event: channel opened - CHANNEL_OPENED.send('channel', address=url) + CHANNEL_OPENED.send('channel', address=theirUrl) def send(url, amount): """Send coin in the channel. @@ -286,14 +315,14 @@ def get_address(): return str(g.bit.getnewaddress()) @REMOTE -def open_channel(address, mymoney, theirmoney, fees, their_coins, their_change, their_pubkey, their_out_addr): # pylint: disable=too-many-arguments, line-too-long +def open_channel(address, my_money, their_money, fees, their_coins, their_change, their_pubkey, their_out_addr): # pylint: disable=too-many-arguments, line-too-long """Open a payment channel.""" # Get inputs and change output - coins, change = select_coins(mymoney + 2 * fees) + coins, change = select_outputs(my_money + 2 * fees) # Make the anchor script anchor_output_script = anchor_script(get_pubkey(), their_pubkey) # Construct the anchor utxo - payment = CMutableTxOut(mymoney + theirmoney + 2 * fees, + payment = CMutableTxOut(my_money + their_money + 2 * fees, anchor_output_script.to_p2sh_scriptPubKey()) # Anchor tx transaction = CMutableTransaction( @@ -302,28 +331,34 @@ def open_channel(address, mymoney, theirmoney, fees, their_coins, their_change, # Half-sign transaction = g.bit.signrawtransaction(transaction)['tx'] # Create channel in DB - our_addr = g.bit.getnewaddress() + our_btc_addr = g.bit.getnewaddress() channel = Channel(address=address, anchor_point=COutPoint(transaction.GetHash(), 0), anchor_index=0, their_sig=b'', anchor_redeem=anchor_output_script, - our_balance=mymoney, + our_balance=my_money, our_addr=our_addr, - their_balance=theirmoney, + their_balance=their_money, their_addr=their_out_addr, ) database.session.add(channel) database.session.commit() # Event: channel opened CHANNEL_OPENED.send('channel', address=address) - return (transaction, anchor_output_script, our_addr) + return (transaction, anchor_output_script, our_btc_addr) @REMOTE -def update_anchor(address, new_anchor, their_sig): +def update_anchor(their_lightning_address, new_anchor, their_sig, their_pubkey): """Update the anchor txid after both have signed.""" - channel = Channel.query.get(address) + channel = Channel.query.get(their_lightning_address) + # COoutPoint = The combination of a transaction hash and an index n into its vout ['hash', 'n'] channel.anchor_point = COutPoint(new_anchor, channel.anchor_point.n) + + # g.logger.debug("### verifying signature in update_anchor" ) + # comitment_to_verify = getcommitmenttransactions(their_lightning_address) + # VerifyScript(their_sig, their_pubkey, comitment_to_verify, 0, (SCRIPT_VERIFY_P2SH,)) + channel.their_sig = their_sig database.session.commit() return channel.signature(channel.commitment()) diff --git a/lightningd.py b/lightningd.py index 068d05e..2605f83 100755 --- a/lightningd.py +++ b/lightningd.py @@ -16,6 +16,7 @@ Flag options can be turned off by prefixing with 'no' (Ex: -nodaemon). """ +import logging import argparse import config import os @@ -108,5 +109,11 @@ def add_switch(name): app.register_blueprint(lightning.API) app.register_blueprint(local.API) + handler = logging.StreamHandler() + handler.setLevel(logging.DEBUG) + app.logger.addHandler(handler) + app.logger.setLevel(logging.DEBUG) + app.logger.debug("i'm logging!") + app.run(port=port, debug=conf.getboolean('debug'), use_reloader=False, processes=3) diff --git a/test/test_integration.py b/test/test_integration.py index 41764c3..48f5e6c 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -40,6 +40,9 @@ def test_basic(self): """Test basic operation of a payment channel.""" # Open a channel between Alice and Bob self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + + self.alice.lightning.print_log() + self.propagate() # There are some fees associated with opening a channel afee = 50000000 - self.alice.bit.getbalance() @@ -68,191 +71,191 @@ def test_basic(self): self.assertGreaterEqual(self.alice.bit.getbalance(), 95000000 - afee) self.assertGreaterEqual(self.bob.bit.getbalance(), 105000000 - bfee) -# def test_stress(self): -# """Test edge cases in payment channels.""" -# # Open *two* payment channels Bob - Alice - Carol -# self.alice.lit.create(self.bob.lurl, 25000000, 50000000) -# self.propagate() -# self.carol.lit.create(self.alice.lurl, 50000000, 25000000) -# self.propagate() -# # Account for fees -# afee = 50000000 - self.alice.bit.getbalance() -# bfee = 50000000 - self.bob.bit.getbalance() -# self.assertGreaterEqual(afee, 0) -# self.assertGreaterEqual(bfee, 0) -# # Balance (A-C) Alice: 0.25 BTC, Carol: 0.50 BTC -# # Balance (B-A) Bob: 0.50 BTC, Alice: 0.25 BTC -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 25000000) -# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 25000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 50000000) -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) -# # Carol sends 0.25 BTC to Alice -# self.carol.lit.send(self.alice.lurl, 25000000) -# # Balance (A-C) Alice: 0.50 BTC, Carol: 0.25 BTC -# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 25000000) -# # Alice sends 0.15 BTC to Carol -# self.alice.lit.send(self.carol.lurl, 15000000) -# # Balance (A-C) Alice: 0.35 BTC, Carol: 0.40 BTC -# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 35000000) -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 40000000) -# # Bob sends Alice 0.50 BTC (his whole balance) -# self.bob.lit.send(self.alice.lurl, 50000000) -# # Balance (B-A) Bob: 0.00 BTC, Alice: 0.75 BTC -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 75000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 0) -# # Alice sends Bob 0.75 BTC (her whole balance) -# self.alice.lit.send(self.bob.lurl, 75000000) -# # Balance (B-A) Bob: 0.75 BTC, Alice: 0.00 BTC -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 0) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 75000000) -# # Alice closes the channel with Bob, on an empty account (Alice opened) -# self.alice.lit.close(self.bob.lurl) -# self.propagate() -# self.assertGreaterEqual(self.alice.bit.getbalance(), 50000000 - afee) -# self.assertGreaterEqual(self.bob.bit.getbalance(), 125000000 - bfee) -# # Alice closes the channel with Carol (Carol opened) -# self.alice.lit.close(self.carol.lurl) -# self.propagate() -# self.assertGreaterEqual(self.alice.bit.getbalance(), 85000000 - afee) + def test_stress(self): + """Test edge cases in payment channels.""" + # Open *two* payment channels Bob - Alice - Carol + self.alice.lit.create(self.bob.lurl, 25000000, 50000000) + self.propagate() + self.carol.lit.create(self.alice.lurl, 50000000, 25000000) + self.propagate() + # Account for fees + afee = 50000000 - self.alice.bit.getbalance() + bfee = 50000000 - self.bob.bit.getbalance() + self.assertGreaterEqual(afee, 0) + self.assertGreaterEqual(bfee, 0) + # Balance (A-C) Alice: 0.25 BTC, Carol: 0.50 BTC + # Balance (B-A) Bob: 0.50 BTC, Alice: 0.25 BTC + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 25000000) + self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 25000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 50000000) + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) + # Carol sends 0.25 BTC to Alice + self.carol.lit.send(self.alice.lurl, 25000000) + # Balance (A-C) Alice: 0.50 BTC, Carol: 0.25 BTC + self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 25000000) + # Alice sends 0.15 BTC to Carol + self.alice.lit.send(self.carol.lurl, 15000000) + # Balance (A-C) Alice: 0.35 BTC, Carol: 0.40 BTC + self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 35000000) + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 40000000) + # Bob sends Alice 0.50 BTC (his whole balance) + self.bob.lit.send(self.alice.lurl, 50000000) + # Balance (B-A) Bob: 0.00 BTC, Alice: 0.75 BTC + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 75000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 0) + # Alice sends Bob 0.75 BTC (her whole balance) + self.alice.lit.send(self.bob.lurl, 75000000) + # Balance (B-A) Bob: 0.75 BTC, Alice: 0.00 BTC + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 0) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 75000000) + # Alice closes the channel with Bob, on an empty account (Alice opened) + self.alice.lit.close(self.bob.lurl) + self.propagate() + self.assertGreaterEqual(self.alice.bit.getbalance(), 50000000 - afee) + self.assertGreaterEqual(self.bob.bit.getbalance(), 125000000 - bfee) + # Alice closes the channel with Carol (Carol opened) + self.alice.lit.close(self.carol.lurl) + self.propagate() + self.assertGreaterEqual(self.alice.bit.getbalance(), 85000000 - afee) -# def test_unilateral_close(self): -# """Test unilateral close.""" -# # Set up channel between Alice and Bob -# self.alice.lit.create(self.bob.lurl, 50000000, 25000000) -# self.propagate() -# afee = 50000000 - self.alice.bit.getbalance() -# bfee = 75000000 - self.bob.bit.getbalance() -# self.assertGreaterEqual(afee, 0) -# self.assertGreaterEqual(bfee, 0) -# # Do some transactions -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) -# self.bob.lit.send(self.alice.lurl, 5000000) -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) -# # Pause Bob -# with self.bob.paused(): -# # Publish Alice's commitment transactions -# commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) -# for transaction in commitment: -# self.alice.bit.sendrawtransaction(transaction) -# self.propagate() -# time.sleep(1) -# self.propagate() -# self.propagate() -# # Alice and Bob get their money out -# self.assertGreaterEqual(self.bob.bit.getbalance(), 95000000 - bfee) -# self.assertGreaterEqual(self.alice.bit.getbalance(), 105000000 - afee) + def test_unilateral_close(self): + """Test unilateral close.""" + # Set up channel between Alice and Bob + self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + self.propagate() + afee = 50000000 - self.alice.bit.getbalance() + bfee = 75000000 - self.bob.bit.getbalance() + self.assertGreaterEqual(afee, 0) + self.assertGreaterEqual(bfee, 0) + # Do some transactions + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) + self.bob.lit.send(self.alice.lurl, 5000000) + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) + # Pause Bob + with self.bob.paused(): + # Publish Alice's commitment transactions + commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) + for transaction in commitment: + self.alice.bit.sendrawtransaction(transaction) + self.propagate() + time.sleep(1) + self.propagate() + self.propagate() + # Alice and Bob get their money out + self.assertGreaterEqual(self.bob.bit.getbalance(), 95000000 - bfee) + self.assertGreaterEqual(self.alice.bit.getbalance(), 105000000 - afee) -# @unittest.expectedFailure -# def test_revoked(self): -# """Test a revoked commitment transaction being published.""" -# # Set up channel between Alice and Bob -# self.alice.lit.create(self.bob.lurl, 50000000, 25000000) -# self.propagate() -# afee = 50000000 - self.alice.bit.getbalance() -# bfee = 75000000 - self.bob.bit.getbalance() -# self.assertGreaterEqual(afee, 0) -# self.assertGreaterEqual(bfee, 0) -# # Make a transaction -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) -# self.bob.lit.send(self.alice.lurl, 5000000) -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) -# # Save Alice's old commitment transactions -# commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) -# # Do annother transaction, Alice sends Bob money -# self.alice.lit.send(self.bob.lurl, 10000000) -# self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 45000000) -# self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 30000000) -# # Alice publishes her old, revoked commitment transactions -# for transaction in commitment: -# self.alice.bit.sendrawtransaction(transaction) -# self.propagate() -# time.sleep(1) -# self.propagate() -# # Bob ends up with all the money -# self.assertGreaterEqual(self.bob.bit.getbalance(), 150000000 - bfee) + @unittest.expectedFailure + def test_revoked(self): + """Test a revoked commitment transaction being published.""" + # Set up channel between Alice and Bob + self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + self.propagate() + afee = 50000000 - self.alice.bit.getbalance() + bfee = 75000000 - self.bob.bit.getbalance() + self.assertGreaterEqual(afee, 0) + self.assertGreaterEqual(bfee, 0) + # Make a transaction + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) + self.bob.lit.send(self.alice.lurl, 5000000) + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) + # Save Alice's old commitment transactions + commitment = self.alice.lit.getcommitmenttransactions(self.bob.lurl) + # Do annother transaction, Alice sends Bob money + self.alice.lit.send(self.bob.lurl, 10000000) + self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 45000000) + self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 30000000) + # Alice publishes her old, revoked commitment transactions + for transaction in commitment: + self.alice.bit.sendrawtransaction(transaction) + self.propagate() + time.sleep(1) + self.propagate() + # Bob ends up with all the money + self.assertGreaterEqual(self.bob.bit.getbalance(), 150000000 - bfee) -# class TestLightning(unittest.TestCase): -# """Run basic tests on payment channels.""" +class TestLightning(unittest.TestCase): + """Run basic tests on payment channels.""" -# @classmethod -# def setUpClass(cls): -# cls.cache = regnet.make_cache() + @classmethod + def setUpClass(cls): + cls.cache = regnet.make_cache() -# @classmethod -# def tearDownClass(cls): -# cls.cache.cleanup() + @classmethod + def tearDownClass(cls): + cls.cache.cleanup() -# def propagate(self): -# """Ensure all nodes up to date.""" -# self.net.generate() + def propagate(self): + """Ensure all nodes up to date.""" + self.net.generate() -# def setUp(self): -# # As in TestChannel, set up 3 nodes -# self.net = regnet.create(datadir=None, cache=self.cache) -# self.alice, self.bob, self.carol = self.net[0], self.net[1], self.net[2] -# # Set up channels between so the network is Alice - Carol - Bob -# self.alice.lit.create(self.carol.lurl, 50000000, 50000000) -# self.propagate() -# self.bob.lit.create(self.carol.lurl, 50000000, 50000000) -# self.propagate() + def setUp(self): + # As in TestChannel, set up 3 nodes + self.net = regnet.create(datadir=None, cache=self.cache) + self.alice, self.bob, self.carol = self.net[0], self.net[1], self.net[2] + # Set up channels between so the network is Alice - Carol - Bob + self.alice.lit.create(self.carol.lurl, 50000000, 50000000) + self.propagate() + self.bob.lit.create(self.carol.lurl, 50000000, 50000000) + self.propagate() -# def tearDown(self): -# self.net.stop(hard=True, cleanup=True) + def tearDown(self): + self.net.stop(hard=True, cleanup=True) -# def test_setup(self): -# """Test that the setup worked.""" -# # (Balance) Alice-Carol: Alice: 0.50 BTC, Carol 0.50 BTC -# # Carol-Bob : Carol: 0.50 BTC, Bob 0.50 BTC -# # (Total) Alice: 0.50 BTC, Carol: 1.00 BTC, Bob: 0.50 BTC -# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) -# self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 50000000) -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) -# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 50000000) + def test_setup(self): + """Test that the setup worked.""" + # (Balance) Alice-Carol: Alice: 0.50 BTC, Carol 0.50 BTC + # Carol-Bob : Carol: 0.50 BTC, Bob 0.50 BTC + # (Total) Alice: 0.50 BTC, Carol: 1.00 BTC, Bob: 0.50 BTC + self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 50000000) + self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 50000000) + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 50000000) + self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 50000000) -# def test_payment(self): -# """Test multi-hop payment.""" -# # Note Alice and Bob do not have a payment channel open directly. -# # They are connected through Carol -# self.alice.lit.send(self.bob.lurl, 5000000) -# # There is a fee associated with multi-hop payments -# fee = 45000000 - self.alice.lit.getbalance(self.carol.lurl) -# self.assertGreaterEqual(fee, 0) -# # (Balance) Alice-Carol: Alice: 0.45 - fee BTC, Carol 0.55 + fee BTC -# # Carol-Bob : Carol: 0.45 BTC, Bob 0.55 BTC -# # (Total) Alice: 0.45 - fee BTC, Carol: 1.00 + fee BTC, Bob: 0.55 BTC -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 55000000 + fee) -# self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 55000000) -# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 45000000) -# # Send money the other direction -# self.bob.lit.send(self.alice.lurl, 10000000) -# # Annother fee will be deducted -# fee2 = 45000000 - self.bob.lit.getbalance(self.carol.lurl) -# self.assertGreaterEqual(fee2, 0) -# # (Balance) Alice-Carol: Alice: 0.55 - fee BTC, Carol 0.45 + fee BTC -# # Carol-Bob : Carol: 0.55 + fee2 BTC, Bob 0.45 - fee2 BTC -# # (Total) Alice: 0.55 - fee BTC, Carol: 1.00 + fee + fee2 BTC, Bob: 0.45 - fee2 BTC -# self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 45000000 + fee) -# self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 55000000 - fee) -# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 55000000 + fee2) + def test_payment(self): + """Test multi-hop payment.""" + # Note Alice and Bob do not have a payment channel open directly. + # They are connected through Carol + self.alice.lit.send(self.bob.lurl, 5000000) + # There is a fee associated with multi-hop payments + fee = 45000000 - self.alice.lit.getbalance(self.carol.lurl) + self.assertGreaterEqual(fee, 0) + # (Balance) Alice-Carol: Alice: 0.45 - fee BTC, Carol 0.55 + fee BTC + # Carol-Bob : Carol: 0.45 BTC, Bob 0.55 BTC + # (Total) Alice: 0.45 - fee BTC, Carol: 1.00 + fee BTC, Bob: 0.55 BTC + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 55000000 + fee) + self.assertEqual(self.bob.lit.getbalance(self.carol.lurl), 55000000) + self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 45000000) + # Send money the other direction + self.bob.lit.send(self.alice.lurl, 10000000) + # Annother fee will be deducted + fee2 = 45000000 - self.bob.lit.getbalance(self.carol.lurl) + self.assertGreaterEqual(fee2, 0) + # (Balance) Alice-Carol: Alice: 0.55 - fee BTC, Carol 0.45 + fee BTC + # Carol-Bob : Carol: 0.55 + fee2 BTC, Bob 0.45 - fee2 BTC + # (Total) Alice: 0.55 - fee BTC, Carol: 1.00 + fee + fee2 BTC, Bob: 0.45 - fee2 BTC + self.assertEqual(self.carol.lit.getbalance(self.alice.lurl), 45000000 + fee) + self.assertEqual(self.alice.lit.getbalance(self.carol.lurl), 55000000 - fee) + self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 55000000 + fee2) -# @unittest.expectedFailure -# def test_route_close(self): -# """Test routing around closed channels.""" -# # Create a new channel between Alice and Bob -# # so all 3 are connected to each other. -# self.alice.lit.create(self.bob.lurl, 25000000, 25000000) -# # Close the connection between Alice and Carol -# self.alice.lit.close(self.carol.lurl) -# self.propagate() -# # Alice sends 0.10 BTC to Carol -# self.alice.lit.send(self.carol.lurl, 10000000) -# # Carol should have recieved money from Bob -# self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 60000000) + @unittest.expectedFailure + def test_route_close(self): + """Test routing around closed channels.""" + # Create a new channel between Alice and Bob + # so all 3 are connected to each other. + self.alice.lit.create(self.bob.lurl, 25000000, 25000000) + # Close the connection between Alice and Carol + self.alice.lit.close(self.carol.lurl) + self.propagate() + # Alice sends 0.10 BTC to Carol + self.alice.lit.send(self.carol.lurl, 10000000) + # Carol should have recieved money from Bob + self.assertEqual(self.carol.lit.getbalance(self.bob.lurl), 60000000) if __name__ == '__main__': unittest.main() From a3959a22ef82e02bd4611205d6b38e7f3649a55a Mon Sep 17 00:00:00 2001 From: sideburnie Date: Tue, 1 Sep 2015 18:30:30 -0700 Subject: [PATCH 04/11] fixed bug in open_channel, currently passing, uncommented VerifyScript in update_anchor --- channel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channel.py b/channel.py index bdfeae5..cdd9aa5 100644 --- a/channel.py +++ b/channel.py @@ -338,7 +338,7 @@ def open_channel(address, my_money, their_money, fees, their_coins, their_change their_sig=b'', anchor_redeem=anchor_output_script, our_balance=my_money, - our_addr=our_addr, + our_addr=our_btc_addr, their_balance=their_money, their_addr=their_out_addr, ) From 2d2617e7a3383f8edc193bb8ca6c47ff70aa43a9 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Wed, 2 Sep 2015 16:30:28 -0700 Subject: [PATCH 05/11] working before pulling out logging lines --- channel.py | 68 +++++++++++++++++++++++++++++++++++----- test/test_integration.py | 1 + 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/channel.py b/channel.py index cdd9aa5..6f002a7 100644 --- a/channel.py +++ b/channel.py @@ -33,16 +33,20 @@ from sqlalchemy import Column, Integer, String, LargeBinary from flask import g from blinker import Namespace -from bitcoin.core import COutPoint, CMutableTxOut, CMutableTxIn +from bitcoin.core import b2x, COutPoint, CMutableTxOut, CMutableTxIn from bitcoin.core import CMutableTransaction from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH from bitcoin.core.script import CScript, SignatureHash, SIGHASH_ALL from bitcoin.core.script import OP_CHECKMULTISIG, OP_PUBKEY +from bitcoin.core.key import CPubKey from bitcoin.wallet import CBitcoinAddress import jsonrpcproxy from serverutil import api_factory from serverutil import database from serverutil import ImmutableSerializableType, Base58DataType +import binascii +# from bitcoin.core.signmessage import VerifyMessage +# from bitcoin.wallet import P2PKHBitcoinAddress API, REMOTE, Model = api_factory('channel') @@ -193,8 +197,6 @@ def update_db(address, amount, sig): database.session.commit() return channel.signature(channel.commitment()) # this is our signature of the comittment -# def verify_sig() - def create(theirUrl, my_money, their_money, fees=10000): """Open a payment channel. @@ -244,8 +246,21 @@ def create(theirUrl, my_money, their_money, fees=10000): their_addr=their_out_addr, ) # Exchange signatures for the inital commitment transaction - their_sig = bob.update_anchor(g.addr, transaction.GetHash(), - channel.signature(channel.commitment()), my_pubkey) + + their_lightning_address = g.addr + new_anchor = transaction.GetHash() + channelcommit = channel.commitment() + channelcommitsig = channel.signature(channelcommit) + + g.logger.debug("### g.addr: \n" + str(their_lightning_address) ) + g.logger.debug("### transaction.GetHash: \n" + str(new_anchor) ) + g.logger.debug("### transaction.GetHash: \n" + str(b2x(new_anchor)) ) + g.logger.debug("### channel.comitment: \n" + str(channelcommit) ) + g.logger.debug("### channelcommitsig: \n" + str(channelcommitsig) ) + # g.logger.debug("### channelcommitsig: \n" + str(binascii.hexlify(channelcommitsig)) ) + g.logger.debug("### channelcommitsig: \n" + str(b2x(channelcommitsig)) ) + their_sig = bob.update_anchor(their_lightning_address, new_anchor, + channelcommitsig, my_pubkey) # channel.signature(channel.commitment()) is our signature for the comitment # their_sig = channel.signature(channel.commitment()) # channel.signature() returns @@ -304,6 +319,37 @@ def close(url): database.session.delete(channel) database.session.commit() +def verify_commitment_signature(pubkey, sighash, signature): + """Verify that an updated commitment has been signed by our counterpaty""" + + # recovered_pubkey = CPubKey.recover_compact(sighash, signature) + + if not pubkey.verify(sighash, signature): + raise Exception("invalid comitment signature for transaction: " + str(transaction)) + else: + g.logger.debug("********signature was valid!") + return True + + # print("### recovered pubkey: " + recovered_pubkey) + # print("### pubkey: " + pubkey) + + # if not str(recovered_pubkey) == str(pubkey): + # raise Exception("invalid comitment signature for transaction: " + str(transaction)) + # else: + # print ("signature was valid!") + # return true + + # return true + + + # def VerifyMessage(address, message, sig): + # sig = base64.b64decode(sig) + # hash = message.GetHash() + + # pubkey = CPubKey.recover_compact(hash, sig) + + # return str(P2PKHBitcoinAddress.from_pubkey(pubkey)) == str(address) + @REMOTE def info(): """Get bitcoind info.""" @@ -355,10 +401,18 @@ def update_anchor(their_lightning_address, new_anchor, their_sig, their_pubkey): # COoutPoint = The combination of a transaction hash and an index n into its vout ['hash', 'n'] channel.anchor_point = COutPoint(new_anchor, channel.anchor_point.n) - # g.logger.debug("### verifying signature in update_anchor" ) - # comitment_to_verify = getcommitmenttransactions(their_lightning_address) + # g.logger.debug("### verifying signature in update_anchor") + g.logger.debug("### their lighthning address:\n" + str(their_lightning_address) ) + #comitment_to_verify = getcommitmenttransactions(their_lightning_address) + # g.logger.debug("### commitment to verify: " + comitment_to_verify) # VerifyScript(their_sig, their_pubkey, comitment_to_verify, 0, (SCRIPT_VERIFY_P2SH,)) + commit_tx = channel.commitment(ours=True) + sighash = SignatureHash(CScript(channel.anchor_redeem), + commit_tx, 0, SIGHASH_ALL) + + verify_commitment_signature(CPubKey(their_pubkey), sighash, their_sig) + channel.their_sig = their_sig database.session.commit() return channel.signature(channel.commitment()) diff --git a/test/test_integration.py b/test/test_integration.py index 48f5e6c..65a868c 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -42,6 +42,7 @@ def test_basic(self): self.alice.lit.create(self.bob.lurl, 50000000, 25000000) self.alice.lightning.print_log() + self.bob.lightning.print_log() self.propagate() # There are some fees associated with opening a channel From effa9afa687be441f08e1e39bc7e0b3cdcb86235 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 11:30:39 -0700 Subject: [PATCH 06/11] working before adding sighash helper function and after adding db funcs and verifying bobs siganture --- channel.py | 89 +++++++++++++++++++--------------------- test/test_integration.py | 9 ++-- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/channel.py b/channel.py index 6f002a7..3064391 100644 --- a/channel.py +++ b/channel.py @@ -105,6 +105,8 @@ class Channel(Model): our_addr = Column(Base58DataType(CBitcoinAddress)) their_balance = Column(Integer) their_addr = Column(Base58DataType(CBitcoinAddress)) + their_pubkey = Column(LargeBinary) + my_pubkey = Column(LargeBinary) def signature(self, transaction): """Signature for a transaction.""" @@ -214,11 +216,11 @@ def create(theirUrl, my_money, their_money, fees=10000): my_pubkey = get_pubkey() g.logger.debug("### my_pubkey: " + str(my_pubkey)) my_out_addr = g.bit.getnewaddress() - g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + \ + g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + str(my_out_addr)) g.logger.debug("### g.addr, our lightning address: " + str(g.addr) ) # Tell Bob we want to open a channel - transaction, redeem, their_out_addr = bob.open_channel( + transaction, redeem, their_out_addr, their_pubkey = bob.open_channel( g.addr, their_money, my_money, fees, my_coins, my_change, my_pubkey, my_out_addr) @@ -244,36 +246,46 @@ def create(theirUrl, my_money, their_money, fees=10000): our_addr=my_out_addr, their_balance=their_money, their_addr=their_out_addr, + their_pubkey=their_pubkey, + my_pubkey=my_pubkey, ) # Exchange signatures for the inital commitment transaction their_lightning_address = g.addr - new_anchor = transaction.GetHash() + new_anchor = transaction.GetHash() + #getting the hash of everything including scriptsigs (which are nullified in the signature hash ) + # transaction.GetHash() getting the transaction ID channelcommit = channel.commitment() channelcommitsig = channel.signature(channelcommit) - g.logger.debug("### g.addr: \n" + str(their_lightning_address) ) - g.logger.debug("### transaction.GetHash: \n" + str(new_anchor) ) - g.logger.debug("### transaction.GetHash: \n" + str(b2x(new_anchor)) ) - g.logger.debug("### channel.comitment: \n" + str(channelcommit) ) - g.logger.debug("### channelcommitsig: \n" + str(channelcommitsig) ) - # g.logger.debug("### channelcommitsig: \n" + str(binascii.hexlify(channelcommitsig)) ) - g.logger.debug("### channelcommitsig: \n" + str(b2x(channelcommitsig)) ) + # channel.anchor = input to comitment transaction spending the anchor transaction + + # g.logger.debug("typeof my_pubkey: " + str(type(my_pubkey))) + g.logger.debug("str(my_pubkey): " + str(my_pubkey)) + # g.logger.debug("### g.addr: \n" + str(their_lightning_address) ) + # g.logger.debug("### transaction.GetHash: \n" + str(new_anchor) ) + # g.logger.debug("### transaction.GetHash: \n" + str(b2x(new_anchor)) ) + # g.logger.debug("### channel.comitment: \n" + str(channelcommit) ) + # g.logger.debug("### channelcommitsig: \n" + str(channelcommitsig) ) + # # g.logger.debug("### channelcommitsig: \n" + str(binascii.hexlify(channelcommitsig)) ) + # g.logger.debug("### channelcommitsig: \n" + str(b2x(channelcommitsig)) ) their_sig = bob.update_anchor(their_lightning_address, new_anchor, channelcommitsig, my_pubkey) # channel.signature(channel.commitment()) is our signature for the comitment # their_sig = channel.signature(channel.commitment()) # channel.signature() returns # transaction.GetHash() = TXID - # Verify Bob's signature - + g.logger.debug("### verifying Bob's signature: \n") + commit_tx = channel.commitment(ours=True) + # should just be one anchor redeem -- redeem script the same for everyone + sighash = SignatureHash(CScript(channel.anchor_redeem), + commit_tx, 0, SIGHASH_ALL) + verify_commitment_signature(CPubKey(their_pubkey), sighash, their_sig) + g.logger.debug("### SUCCESS: verified bob's signature \n") channel.their_sig = their_sig - - - database.session.add(channel) database.session.commit() # Event: channel opened @@ -325,31 +337,11 @@ def verify_commitment_signature(pubkey, sighash, signature): # recovered_pubkey = CPubKey.recover_compact(sighash, signature) if not pubkey.verify(sighash, signature): - raise Exception("invalid comitment signature for transaction: " + str(transaction)) + raise Exception("invalid comitment signature for transaction: " + str(sighash)) else: - g.logger.debug("********signature was valid!") + g.logger.debug("comitment signature verified for comitment with sighash: " + str(sighash)) return True - # print("### recovered pubkey: " + recovered_pubkey) - # print("### pubkey: " + pubkey) - - # if not str(recovered_pubkey) == str(pubkey): - # raise Exception("invalid comitment signature for transaction: " + str(transaction)) - # else: - # print ("signature was valid!") - # return true - - # return true - - - # def VerifyMessage(address, message, sig): - # sig = base64.b64decode(sig) - # hash = message.GetHash() - - # pubkey = CPubKey.recover_compact(hash, sig) - - # return str(P2PKHBitcoinAddress.from_pubkey(pubkey)) == str(address) - @REMOTE def info(): """Get bitcoind info.""" @@ -366,7 +358,8 @@ def open_channel(address, my_money, their_money, fees, their_coins, their_change # Get inputs and change output coins, change = select_outputs(my_money + 2 * fees) # Make the anchor script - anchor_output_script = anchor_script(get_pubkey(), their_pubkey) + my_pubkey = get_pubkey() + anchor_output_script = anchor_script(my_pubkey, their_pubkey) # Construct the anchor utxo payment = CMutableTxOut(my_money + their_money + 2 * fees, anchor_output_script.to_p2sh_scriptPubKey()) @@ -387,34 +380,38 @@ def open_channel(address, my_money, their_money, fees, their_coins, their_change our_addr=our_btc_addr, their_balance=their_money, their_addr=their_out_addr, + my_pubkey=my_pubkey, + their_pubkey=their_pubkey, ) database.session.add(channel) database.session.commit() # Event: channel opened CHANNEL_OPENED.send('channel', address=address) - return (transaction, anchor_output_script, our_btc_addr) + return (transaction, anchor_output_script, our_btc_addr, my_pubkey) @REMOTE def update_anchor(their_lightning_address, new_anchor, their_sig, their_pubkey): """Update the anchor txid after both have signed.""" + g.logger.debug("bob1") channel = Channel.query.get(their_lightning_address) # COoutPoint = The combination of a transaction hash and an index n into its vout ['hash', 'n'] channel.anchor_point = COutPoint(new_anchor, channel.anchor_point.n) # g.logger.debug("### verifying signature in update_anchor") - g.logger.debug("### their lighthning address:\n" + str(their_lightning_address) ) - #comitment_to_verify = getcommitmenttransactions(their_lightning_address) - # g.logger.debug("### commitment to verify: " + comitment_to_verify) - # VerifyScript(their_sig, their_pubkey, comitment_to_verify, 0, (SCRIPT_VERIFY_P2SH,)) - + # g.logger.debug("### their lighthning address:\n" + str(their_lightning_address) ) commit_tx = channel.commitment(ours=True) sighash = SignatureHash(CScript(channel.anchor_redeem), commit_tx, 0, SIGHASH_ALL) + g.logger.debug("bob about to verify pubkey: " + str(their_pubkey)) + their_pubkey = CPubKey(their_pubkey) + g.logger.debug("converted pubkey: " + str(their_pubkey)) + verify_commitment_signature(their_pubkey, sighash, their_sig) - verify_commitment_signature(CPubKey(their_pubkey), sighash, their_sig) - + g.logger.debug("") channel.their_sig = their_sig + g.logger.debug("about to do db commit") database.session.commit() + g.logger.debug("bob2") return channel.signature(channel.commitment()) @REMOTE diff --git a/test/test_integration.py b/test/test_integration.py index 65a868c..b6647cb 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -39,10 +39,11 @@ def test_setup(self): def test_basic(self): """Test basic operation of a payment channel.""" # Open a channel between Alice and Bob - self.alice.lit.create(self.bob.lurl, 50000000, 25000000) - - self.alice.lightning.print_log() - self.bob.lightning.print_log() + try: + self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + finally: + self.alice.lightning.print_log() + self.bob.lightning.print_log() self.propagate() # There are some fees associated with opening a channel From e08f929e22f341b38e697803319169b2e4c1b34c Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 14:45:37 -0700 Subject: [PATCH 07/11] working commitmentsighash helper function, working verify_commitment_signature function in open and update, going to start pulling out g.logger.debug --- channel.py | 77 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/channel.py b/channel.py index 3064391..f924e28 100644 --- a/channel.py +++ b/channel.py @@ -160,6 +160,13 @@ def settlement(self): raise Exception("Unknown index", self.anchor_index) return CMutableTransaction([CMutableTxIn(self.anchor_point)], [first, second]) + + def commitmentsighash(self, ours=True): + commit_tx = self.commitment(ours) + # should just be one anchor redeem -- redeem script the same for everyone + sighash = SignatureHash(CScript(self.anchor_redeem), + commit_tx, 0, SIGHASH_ALL) + return sighash def select_outputs(amount): """Get a txin set and change to spend amount.""" @@ -191,8 +198,9 @@ def update_db(address, amount, sig): """Update the db for a payment.""" channel = Channel.query.get(address) # address is lightning address, address is primary key - # verify that signature is valid here - + # make sure we have a valid signature from our counterparty before updating accounts + verify_commitment_signature(CPubKey(channel.their_pubkey), + channel.commitmentsighash(), sig) channel.our_balance += amount channel.their_balance -= amount channel.their_sig = sig @@ -211,28 +219,26 @@ def create(theirUrl, my_money, their_money, fees=10000): g.logger.debug("### bob: " + theirUrl + "channel/") # Choose inputs and change output my_coins, my_change = select_outputs(my_money + 2 * fees) - g.logger.debug("### my_coins: " + str(my_coins)) - g.logger.debug("### my_change: " + str(my_change)) + # g.logger.debug("### my_coins: " + str(my_coins)) + # g.logger.debug("### my_change: " + str(my_change)) my_pubkey = get_pubkey() - g.logger.debug("### my_pubkey: " + str(my_pubkey)) + # g.logger.debug("### my_pubkey: " + str(my_pubkey)) my_out_addr = g.bit.getnewaddress() - g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + - str(my_out_addr)) - g.logger.debug("### g.addr, our lightning address: " + str(g.addr) ) + # g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + + # str(my_out_addr)) + # g.logger.debug("### g.addr, our lightning address: " + str(g.addr) ) # Tell Bob we want to open a channel transaction, redeem, their_out_addr, their_pubkey = bob.open_channel( g.addr, their_money, my_money, fees, my_coins, my_change, my_pubkey, my_out_addr) # Sign and send the anchor - g.logger.debug("### transaction: " + str(transaction) ) - g.logger.debug("### redeem: " + str(redeem) ) - g.logger.debug("### their_out_addr: " + str(their_out_addr) ) + # g.logger.debug("### transaction: " + str(transaction) ) + # g.logger.debug("### redeem: " + str(redeem) ) + # g.logger.debug("### their_out_addr: " + str(their_out_addr) ) transaction = g.bit.signrawtransaction(transaction) g.logger.debug("### transaction post signing: " + str(redeem) ) - - assert transaction['complete'] transaction = transaction['tx'] g.bit.sendrawtransaction(transaction) @@ -278,11 +284,15 @@ def create(theirUrl, my_money, their_money, fees=10000): # Verify Bob's signature g.logger.debug("### verifying Bob's signature: \n") - commit_tx = channel.commitment(ours=True) - # should just be one anchor redeem -- redeem script the same for everyone - sighash = SignatureHash(CScript(channel.anchor_redeem), - commit_tx, 0, SIGHASH_ALL) - verify_commitment_signature(CPubKey(their_pubkey), sighash, their_sig) + + # commit_tx = channel.commitment(ours=True) + # # should just be one anchor redeem -- redeem script the same for everyone + # sighash = SignatureHash(CScript(channel.anchor_redeem), + # commit_tx, 0, SIGHASH_ALL) + + verify_commitment_signature(CPubKey(their_pubkey), + channel.commitmentsighash(), their_sig) + g.logger.debug("### SUCCESS: verified bob's signature \n") channel.their_sig = their_sig @@ -333,9 +343,8 @@ def close(url): def verify_commitment_signature(pubkey, sighash, signature): """Verify that an updated commitment has been signed by our counterpaty""" - # recovered_pubkey = CPubKey.recover_compact(sighash, signature) - + pubkey = CPubKey(pubkey) if not pubkey.verify(sighash, signature): raise Exception("invalid comitment signature for transaction: " + str(sighash)) else: @@ -399,19 +408,25 @@ def update_anchor(their_lightning_address, new_anchor, their_sig, their_pubkey): # g.logger.debug("### verifying signature in update_anchor") # g.logger.debug("### their lighthning address:\n" + str(their_lightning_address) ) - commit_tx = channel.commitment(ours=True) - sighash = SignatureHash(CScript(channel.anchor_redeem), - commit_tx, 0, SIGHASH_ALL) - g.logger.debug("bob about to verify pubkey: " + str(their_pubkey)) - their_pubkey = CPubKey(their_pubkey) - g.logger.debug("converted pubkey: " + str(their_pubkey)) - verify_commitment_signature(their_pubkey, sighash, their_sig) - - g.logger.debug("") + + # sighash = commitmentsighash() + + # commit_tx = channel.commitment(ours=True) + # sighash = SignatureHash(CScript(channel.anchor_redeem), + # commit_tx, 0, SIGHASH_ALL) + # g.logger.debug("first sighash: " + str(sighash)) + # sighash = channel.commitmentsighash() + # g.logger.debug("second sighash: " + str(sighash)) + + # g.logger.debug("bob about to verify pubkey: " + str(their_pubkey)) + # their_pubkey = CPubKey(their_pubkey) + # g.logger.debug("converted pubkey: " + str(their_pubkey)) + + verify_commitment_signature(their_pubkey, channel.commitmentsighash(), their_sig) + # verify_commitment_signature(their_pubkey, sighash, their_sig) + channel.their_sig = their_sig - g.logger.debug("about to do db commit") database.session.commit() - g.logger.debug("bob2") return channel.signature(channel.commitment()) @REMOTE From 705d0bc22198f00d01149f3741213ca19b423149 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 15:24:26 -0700 Subject: [PATCH 08/11] fixed bug in verify in update_db --- channel.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/channel.py b/channel.py index f924e28..49c8737 100644 --- a/channel.py +++ b/channel.py @@ -198,11 +198,13 @@ def update_db(address, amount, sig): """Update the db for a payment.""" channel = Channel.query.get(address) # address is lightning address, address is primary key + # need to make sure we update balances prior to checking signatures + # (so that we get the right sighash) + channel.our_balance += amount + channel.their_balance -= amount # make sure we have a valid signature from our counterparty before updating accounts verify_commitment_signature(CPubKey(channel.their_pubkey), channel.commitmentsighash(), sig) - channel.our_balance += amount - channel.their_balance -= amount channel.their_sig = sig database.session.commit() return channel.signature(channel.commitment()) # this is our signature of the comittment @@ -434,6 +436,7 @@ def propose_update(address, amount): """Sign commitment transactions.""" channel = Channel.query.get(address) assert amount > 0 + # need to decrement to generate commitment channel.our_balance += amount channel.their_balance -= amount # don't persist yet From c7117e141a04f98016e6da6bec5e29642871a295 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 15:31:02 -0700 Subject: [PATCH 09/11] removed excess logging --- channel.py | 79 +++------------------------------------- lightningd.py | 2 +- test/test_integration.py | 2 + 3 files changed, 9 insertions(+), 74 deletions(-) diff --git a/channel.py b/channel.py index 49c8737..7283861 100644 --- a/channel.py +++ b/channel.py @@ -45,8 +45,6 @@ from serverutil import database from serverutil import ImmutableSerializableType, Base58DataType import binascii -# from bitcoin.core.signmessage import VerifyMessage -# from bitcoin.wallet import P2PKHBitcoinAddress API, REMOTE, Model = api_factory('channel') @@ -112,15 +110,8 @@ def signature(self, transaction): """Signature for a transaction.""" sighash = SignatureHash(CScript(self.anchor_redeem), transaction, 0, SIGHASH_ALL) - g.logger.debug("### signature.sighash: \n" + str(sighash) ) - sig = g.seckey.sign(sighash) - g.logger.debug("### signature.SIGHASH_ALL: " + str(SIGHASH_ALL) ) - g.logger.debug("### signature.bytes([SIGHASH_ALL]): \n" + str(bytes([SIGHASH_ALL])) ) - g.logger.debug("### signature.g.seckey.sign(sighash): \n" + str(sig) ) - sig = sig + bytes([SIGHASH_ALL]) - g.logger.debug("### signature.sig: \n" + str(sig) ) return sig def sign(self, transaction): @@ -218,29 +209,19 @@ def create(theirUrl, my_money, their_money, fees=10000): setup and teardown of the channel should be collected at this time. """ bob = jsonrpcproxy.Proxy(theirUrl+'channel/') - g.logger.debug("### bob: " + theirUrl + "channel/") + # g.logger.debug("### creating channel with bob: " + theirUrl + "channel/") # Choose inputs and change output my_coins, my_change = select_outputs(my_money + 2 * fees) - # g.logger.debug("### my_coins: " + str(my_coins)) - # g.logger.debug("### my_change: " + str(my_change)) my_pubkey = get_pubkey() - # g.logger.debug("### my_pubkey: " + str(my_pubkey)) my_out_addr = g.bit.getnewaddress() - # g.logger.debug("### my out addr, bitcoin adddress for money when we close the channel) addr: " + - # str(my_out_addr)) - # g.logger.debug("### g.addr, our lightning address: " + str(g.addr) ) # Tell Bob we want to open a channel transaction, redeem, their_out_addr, their_pubkey = bob.open_channel( g.addr, their_money, my_money, fees, my_coins, my_change, my_pubkey, my_out_addr) # Sign and send the anchor - # g.logger.debug("### transaction: " + str(transaction) ) - # g.logger.debug("### redeem: " + str(redeem) ) - # g.logger.debug("### their_out_addr: " + str(their_out_addr) ) transaction = g.bit.signrawtransaction(transaction) - g.logger.debug("### transaction post signing: " + str(redeem) ) - + assert transaction['complete'] transaction = transaction['tx'] g.bit.sendrawtransaction(transaction) @@ -258,45 +239,17 @@ def create(theirUrl, my_money, their_money, fees=10000): my_pubkey=my_pubkey, ) # Exchange signatures for the inital commitment transaction - their_lightning_address = g.addr - new_anchor = transaction.GetHash() - #getting the hash of everything including scriptsigs (which are nullified in the signature hash ) - # transaction.GetHash() getting the transaction ID + # get the hash of everything including scripsigs (which are nullified in sighash) + new_anchor = transaction.GetHash() # also the TXID channelcommit = channel.commitment() channelcommitsig = channel.signature(channelcommit) - # channel.anchor = input to comitment transaction spending the anchor transaction - - # g.logger.debug("typeof my_pubkey: " + str(type(my_pubkey))) - g.logger.debug("str(my_pubkey): " + str(my_pubkey)) - # g.logger.debug("### g.addr: \n" + str(their_lightning_address) ) - # g.logger.debug("### transaction.GetHash: \n" + str(new_anchor) ) - # g.logger.debug("### transaction.GetHash: \n" + str(b2x(new_anchor)) ) - # g.logger.debug("### channel.comitment: \n" + str(channelcommit) ) - # g.logger.debug("### channelcommitsig: \n" + str(channelcommitsig) ) - # # g.logger.debug("### channelcommitsig: \n" + str(binascii.hexlify(channelcommitsig)) ) - # g.logger.debug("### channelcommitsig: \n" + str(b2x(channelcommitsig)) ) their_sig = bob.update_anchor(their_lightning_address, new_anchor, channelcommitsig, my_pubkey) - # channel.signature(channel.commitment()) is our signature for the comitment - # their_sig = channel.signature(channel.commitment()) - # channel.signature() returns - # transaction.GetHash() = TXID - # Verify Bob's signature - g.logger.debug("### verifying Bob's signature: \n") - - # commit_tx = channel.commitment(ours=True) - # # should just be one anchor redeem -- redeem script the same for everyone - # sighash = SignatureHash(CScript(channel.anchor_redeem), - # commit_tx, 0, SIGHASH_ALL) - verify_commitment_signature(CPubKey(their_pubkey), channel.commitmentsighash(), their_sig) - - g.logger.debug("### SUCCESS: verified bob's signature \n") - channel.their_sig = their_sig database.session.add(channel) database.session.commit() @@ -345,12 +298,12 @@ def close(url): def verify_commitment_signature(pubkey, sighash, signature): """Verify that an updated commitment has been signed by our counterpaty""" - # recovered_pubkey = CPubKey.recover_compact(sighash, signature) + # recovered_pubkey = CPubKey.recover_compact(sighash, signature) # need updated bitcoin lib pubkey = CPubKey(pubkey) if not pubkey.verify(sighash, signature): raise Exception("invalid comitment signature for transaction: " + str(sighash)) else: - g.logger.debug("comitment signature verified for comitment with sighash: " + str(sighash)) + # g.logger.debug("comitment signature verified for comitment with sighash: " + str(sighash)) return True @REMOTE @@ -403,30 +356,10 @@ def open_channel(address, my_money, their_money, fees, their_coins, their_change @REMOTE def update_anchor(their_lightning_address, new_anchor, their_sig, their_pubkey): """Update the anchor txid after both have signed.""" - g.logger.debug("bob1") channel = Channel.query.get(their_lightning_address) # COoutPoint = The combination of a transaction hash and an index n into its vout ['hash', 'n'] channel.anchor_point = COutPoint(new_anchor, channel.anchor_point.n) - - # g.logger.debug("### verifying signature in update_anchor") - # g.logger.debug("### their lighthning address:\n" + str(their_lightning_address) ) - - # sighash = commitmentsighash() - - # commit_tx = channel.commitment(ours=True) - # sighash = SignatureHash(CScript(channel.anchor_redeem), - # commit_tx, 0, SIGHASH_ALL) - # g.logger.debug("first sighash: " + str(sighash)) - # sighash = channel.commitmentsighash() - # g.logger.debug("second sighash: " + str(sighash)) - - # g.logger.debug("bob about to verify pubkey: " + str(their_pubkey)) - # their_pubkey = CPubKey(their_pubkey) - # g.logger.debug("converted pubkey: " + str(their_pubkey)) - verify_commitment_signature(their_pubkey, channel.commitmentsighash(), their_sig) - # verify_commitment_signature(their_pubkey, sighash, their_sig) - channel.their_sig = their_sig database.session.commit() return channel.signature(channel.commitment()) diff --git a/lightningd.py b/lightningd.py index 2605f83..5742862 100755 --- a/lightningd.py +++ b/lightningd.py @@ -113,7 +113,7 @@ def add_switch(name): handler.setLevel(logging.DEBUG) app.logger.addHandler(handler) app.logger.setLevel(logging.DEBUG) - app.logger.debug("i'm logging!") + # app.logger.debug("i'm logging!") app.run(port=port, debug=conf.getboolean('debug'), use_reloader=False, processes=3) diff --git a/test/test_integration.py b/test/test_integration.py index b6647cb..710862e 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -54,8 +54,10 @@ def test_basic(self): # (Balance) Alice: 0.50 BTC, Bob: 0.25 BTC self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 50000000) self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 25000000) + # Bob sends Alice 0.05 BTC self.bob.lit.send(self.alice.lurl, 5000000) + # (Balance) Alice: 0.55 BTC, Bob: 0.20 BTC self.assertEqual(self.alice.lit.getbalance(self.bob.lurl), 55000000) self.assertEqual(self.bob.lit.getbalance(self.alice.lurl), 20000000) From f27cab69ef9a728179cf62bb5749b0f43078130d Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 15:39:58 -0700 Subject: [PATCH 10/11] pre-push cleanup --- channel.py | 4 +--- test/test_integration.py | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/channel.py b/channel.py index 7283861..a3a1091 100644 --- a/channel.py +++ b/channel.py @@ -44,7 +44,6 @@ from serverutil import api_factory from serverutil import database from serverutil import ImmutableSerializableType, Base58DataType -import binascii API, REMOTE, Model = api_factory('channel') @@ -110,8 +109,7 @@ def signature(self, transaction): """Signature for a transaction.""" sighash = SignatureHash(CScript(self.anchor_redeem), transaction, 0, SIGHASH_ALL) - sig = g.seckey.sign(sighash) - sig = sig + bytes([SIGHASH_ALL]) + sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig def sign(self, transaction): diff --git a/test/test_integration.py b/test/test_integration.py index 710862e..fee762e 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -39,11 +39,13 @@ def test_setup(self): def test_basic(self): """Test basic operation of a payment channel.""" # Open a channel between Alice and Bob - try: - self.alice.lit.create(self.bob.lurl, 50000000, 25000000) - finally: - self.alice.lightning.print_log() - self.bob.lightning.print_log() + self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + + # try: + # self.alice.lit.create(self.bob.lurl, 50000000, 25000000) + # finally: + # self.alice.lightning.print_log() + # self.bob.lightning.print_log() self.propagate() # There are some fees associated with opening a channel From 2c85268a70cd8db43a29b3ce3536a5e5175b64f7 Mon Sep 17 00:00:00 2001 From: sideburnie Date: Thu, 3 Sep 2015 16:17:47 -0700 Subject: [PATCH 11/11] pylint, cleanup --- channel.py | 74 ++++++++++++++++++++++++--------------------------- lightningd.py | 2 +- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/channel.py b/channel.py index a3a1091..fc2af5f 100644 --- a/channel.py +++ b/channel.py @@ -33,7 +33,7 @@ from sqlalchemy import Column, Integer, String, LargeBinary from flask import g from blinker import Namespace -from bitcoin.core import b2x, COutPoint, CMutableTxOut, CMutableTxIn +from bitcoin.core import COutPoint, CMutableTxOut, CMutableTxIn from bitcoin.core import CMutableTransaction from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH from bitcoin.core.script import CScript, SignatureHash, SIGHASH_ALL @@ -109,7 +109,7 @@ def signature(self, transaction): """Signature for a transaction.""" sighash = SignatureHash(CScript(self.anchor_redeem), transaction, 0, SIGHASH_ALL) - sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) + sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig def sign(self, transaction): @@ -149,15 +149,16 @@ def settlement(self): raise Exception("Unknown index", self.anchor_index) return CMutableTransaction([CMutableTxIn(self.anchor_point)], [first, second]) - - def commitmentsighash(self, ours=True): + + def commitmentsighash(self, ours=True): + """Generate the sighash for the most recent comitment""" commit_tx = self.commitment(ours) # should just be one anchor redeem -- redeem script the same for everyone - sighash = SignatureHash(CScript(self.anchor_redeem), - commit_tx, 0, SIGHASH_ALL) + sighash = SignatureHash(CScript(self.anchor_redeem), + commit_tx, 0, SIGHASH_ALL) return sighash -def select_outputs(amount): +def select_coins(amount): """Get a txin set and change to spend amount.""" coins = g.bit.listunspent() out = [] @@ -185,31 +186,30 @@ def get_pubkey(): def update_db(address, amount, sig): """Update the db for a payment.""" - channel = Channel.query.get(address) # address is lightning address, address is primary key - - # need to make sure we update balances prior to checking signatures + channel = Channel.query.get(address) # address is lightning address, address is primary key + # need to make sure we update balances prior to checking signatures # (so that we get the right sighash) channel.our_balance += amount channel.their_balance -= amount # make sure we have a valid signature from our counterparty before updating accounts - verify_commitment_signature(CPubKey(channel.their_pubkey), - channel.commitmentsighash(), sig) + verify_commitment_signature(CPubKey(channel.their_pubkey), + channel.commitmentsighash(), sig) channel.their_sig = sig database.session.commit() - return channel.signature(channel.commitment()) # this is our signature of the comittment + return channel.signature(channel.commitment()) # this is our signature of the comittment -def create(theirUrl, my_money, their_money, fees=10000): +def create(their_url, my_money, their_money, fees=10000): """Open a payment channel. After this method returns, a payment channel will have been established - with the node identified by theirUrl, in which you can send my_money satoshis + with the node identified by their_url, in which you can send my_money satoshis and recieve their_money satoshis. Any blockchain fees involved in the setup and teardown of the channel should be collected at this time. """ - bob = jsonrpcproxy.Proxy(theirUrl+'channel/') - # g.logger.debug("### creating channel with bob: " + theirUrl + "channel/") + bob = jsonrpcproxy.Proxy(their_url+'channel/') + # g.logger.debug("### creating channel with bob: " + their_url + "channel/") # Choose inputs and change output - my_coins, my_change = select_outputs(my_money + 2 * fees) + my_coins, my_change = select_coins(my_money + 2 * fees) my_pubkey = get_pubkey() my_out_addr = g.bit.getnewaddress() # Tell Bob we want to open a channel @@ -219,12 +219,12 @@ def create(theirUrl, my_money, their_money, fees=10000): my_pubkey, my_out_addr) # Sign and send the anchor transaction = g.bit.signrawtransaction(transaction) - + assert transaction['complete'] transaction = transaction['tx'] g.bit.sendrawtransaction(transaction) # Set up the channel in the DB - channel = Channel(address=theirUrl, + channel = Channel(address=their_url, anchor_point=COutPoint(transaction.GetHash(), 0), anchor_index=1, their_sig=b'', @@ -237,22 +237,18 @@ def create(theirUrl, my_money, their_money, fees=10000): my_pubkey=my_pubkey, ) # Exchange signatures for the inital commitment transaction - their_lightning_address = g.addr - # get the hash of everything including scripsigs (which are nullified in sighash) - new_anchor = transaction.GetHash() # also the TXID - channelcommit = channel.commitment() - channelcommitsig = channel.signature(channelcommit) - # channel.anchor = input to comitment transaction spending the anchor transaction - their_sig = bob.update_anchor(their_lightning_address, new_anchor, - channelcommitsig, my_pubkey) - # Verify Bob's signature - verify_commitment_signature(CPubKey(their_pubkey), - channel.commitmentsighash(), their_sig) + # get the hash of everything including scripsigs (which are nullified in sighash) + # g.addr (our lightning addr), transaction.GetHash() (the TXID) + their_sig = bob.update_anchor(g.addr, transaction.GetHash(), + channel.signature(channel.commitment()), my_pubkey) + # Verify Bob's signature + verify_commitment_signature(CPubKey(their_pubkey), + channel.commitmentsighash(), their_sig) channel.their_sig = their_sig database.session.add(channel) database.session.commit() # Event: channel opened - CHANNEL_OPENED.send('channel', address=theirUrl) + CHANNEL_OPENED.send('channel', address=their_url) def send(url, amount): """Send coin in the channel. @@ -294,15 +290,15 @@ def close(url): database.session.delete(channel) database.session.commit() -def verify_commitment_signature(pubkey, sighash, signature): +def verify_commitment_signature(pubkey, sighash, signature): """Verify that an updated commitment has been signed by our counterpaty""" # recovered_pubkey = CPubKey.recover_compact(sighash, signature) # need updated bitcoin lib pubkey = CPubKey(pubkey) - if not pubkey.verify(sighash, signature): + if not pubkey.verify(sighash, signature): raise Exception("invalid comitment signature for transaction: " + str(sighash)) - else: + else: # g.logger.debug("comitment signature verified for comitment with sighash: " + str(sighash)) - return True + return True @REMOTE def info(): @@ -315,10 +311,10 @@ def get_address(): return str(g.bit.getnewaddress()) @REMOTE -def open_channel(address, my_money, their_money, fees, their_coins, their_change, their_pubkey, their_out_addr): # pylint: disable=too-many-arguments, line-too-long +def open_channel(address, my_money, their_money, fees, their_coins, their_change, their_pubkey, their_out_addr): # pylint: disable=too-many-arguments, line-too-long, too-many-locals """Open a payment channel.""" # Get inputs and change output - coins, change = select_outputs(my_money + 2 * fees) + coins, change = select_coins(my_money + 2 * fees) # Make the anchor script my_pubkey = get_pubkey() anchor_output_script = anchor_script(my_pubkey, their_pubkey) @@ -367,7 +363,7 @@ def propose_update(address, amount): """Sign commitment transactions.""" channel = Channel.query.get(address) assert amount > 0 - # need to decrement to generate commitment + # need to decrement to generate commitment channel.our_balance += amount channel.their_balance -= amount # don't persist yet diff --git a/lightningd.py b/lightningd.py index 5742862..be6d91a 100755 --- a/lightningd.py +++ b/lightningd.py @@ -109,7 +109,7 @@ def add_switch(name): app.register_blueprint(lightning.API) app.register_blueprint(local.API) - handler = logging.StreamHandler() + handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) app.logger.addHandler(handler) app.logger.setLevel(logging.DEBUG)