def test_paymentchannel_typical_zeroconf(): # Create mocked dependencies bc = mock.MockBlockchain() wallet = walletwrapper.Two1WalletWrapper(mock.MockTwo1Wallet(), bc) db = database.Sqlite3Database(":memory:") # Link the mock blockchain to the mock payment channel server as it is a # non-injected dependency. mock.MockPaymentChannelServer.blockchain = bc # Clear mock payment channel server channels. mock.MockPaymentChannelServer.channels = {} # Open a payment channel with 100000 deposit, default expiration, and 30000 fee pc = paymentchannel.PaymentChannel.open(db, wallet, bc, 'mock://test', 100000, DEFAULT_EXPIRATION, 30000, True) # Assert payment channel properties expected_state = {} expected_state['url'] = "mock://test/" + pc.deposit_txid expected_state['state'] = statemachine.PaymentChannelState.READY expected_state['ready'] = True expected_state['balance'] = 100000 expected_state['deposit'] = 100000 expected_state['fee'] = 30000 expected_state['creation_time'] = lambda pc: pc.creation_time > 0 expected_state['expiration_time'] = int(pc.creation_time + DEFAULT_EXPIRATION) expected_state['expired'] = False expected_state['refund_tx'] = lambda pc: pc.refund_tx expected_state['refund_txid'] = lambda pc: pc.refund_txid expected_state['deposit_tx'] = lambda pc: pc.deposit_tx expected_state['deposit_txid'] = lambda pc: pc.deposit_txid expected_state['payment_tx'] = None expected_state['spend_tx'] = None expected_state['spend_txid'] = None assert_paymentchannel_state(expected_state, pc) # Make a few payments and close the channel pc.pay(1) pc.pay(1) pc.pay(1) pc.close() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND expected_state['ready'] = False expected_state['payment_tx'] = lambda pc: pc.payment_tx expected_state['balance'] = 96998 expected_state['spend_txid'] = lambda pc: pc.spend_txid assert_paymentchannel_state(expected_state, pc)
def test_paymentchannel_typical(): # Create mocked dependencies bc = mock.MockBlockchain() wallet = walletwrapper.Two1WalletWrapper(mock.MockTwo1Wallet(), bc) db = database.Sqlite3Database(":memory:") # Link the mock blockchain to the mock payment channel server as it is a # non-injected dependency. mock.MockPaymentChannelServer.blockchain = bc # Clear mock payment channel server channels. mock.MockPaymentChannelServer.channels = {} # Open a payment channel with 100000 deposit, default expiration, and 30000 fee pc = paymentchannel.PaymentChannel.open(db, wallet, bc, 'mock://test', 100000, DEFAULT_EXPIRATION, 30000, False) # Assert payment channel properties expected_state = {} expected_state['url'] = "mock://test/" + pc.deposit_txid expected_state[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_DEPOSIT expected_state['ready'] = False expected_state['balance'] = 100000 expected_state['deposit'] = 100000 expected_state['fee'] = 30000 expected_state['creation_time'] = lambda pc: pc.creation_time > 0 expected_state['expiration_time'] = int(pc.creation_time + DEFAULT_EXPIRATION) expected_state['expired'] = False expected_state['refund_tx'] = lambda pc: pc.refund_tx expected_state['refund_txid'] = lambda pc: pc.refund_txid expected_state['deposit_tx'] = lambda pc: pc.deposit_tx expected_state['deposit_txid'] = lambda pc: pc.deposit_txid expected_state['payment_tx'] = None expected_state['spend_tx'] = None expected_state['spend_txid'] = None assert_paymentchannel_state(expected_state, pc) # Check database with db: assert db.list() == [pc.url] assert db.read(pc.url) # Check blockchain assert bc.check_confirmed(pc.deposit_txid) is False assert bc.lookup_tx(pc.deposit_txid) == pc.deposit_tx # Try premature payment with pytest.raises(paymentchannel.NotReadyError): pc.pay(1) # Try premature close with pytest.raises(paymentchannel.NotReadyError): pc.close() # Sync payment channel pc.sync() expected_state[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_DEPOSIT expected_state['ready'] = False assert_paymentchannel_state(expected_state, pc) # Confirm deposit bc.mock_confirm(pc.deposit_txid) # Sync payment channel pc.sync() expected_state['state'] = statemachine.PaymentChannelState.READY expected_state['ready'] = True assert_paymentchannel_state(expected_state, pc) # Try excess payment with pytest.raises(paymentchannel.InsufficientBalanceError): pc.pay(pc.balance + 1) # Try premature close with pytest.raises(paymentchannel.NoPaymentError): pc.close() # Make regular payments assert pc.pay(1500) expected_state['payment_tx'] = lambda pc: pc.payment_tx expected_state['balance'] = 97000 assert_paymentchannel_state(expected_state, pc) assert pc.pay(1) expected_state['payment_tx'] = lambda pc: pc.payment_tx expected_state['balance'] = 96999 assert_paymentchannel_state(expected_state, pc) assert pc.pay(15) expected_state['payment_tx'] = lambda pc: pc.payment_tx expected_state['balance'] = 96984 assert_paymentchannel_state(expected_state, pc) assert pc.pay(20000) expected_state['payment_tx'] = lambda pc: pc.payment_tx expected_state['balance'] = 76984 assert_paymentchannel_state(expected_state, pc) # Close payment channel pc.close() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND expected_state['ready'] = False expected_state['spend_txid'] = str(mock.MockPaymentChannelServer.channels[ pc.deposit_txid]['payment_tx'].hash) assert_paymentchannel_state(expected_state, pc) # Sync payment channel pc.sync() assert_paymentchannel_state(expected_state, pc) # Confirm spend bc.mock_confirm(pc.spend_txid) # Sync payment channel pc.sync() expected_state['state'] = statemachine.PaymentChannelState.CLOSED expected_state['spend_tx'] = mock.MockPaymentChannelServer.channels[ pc.deposit_txid]['payment_tx'].to_hex() assert_paymentchannel_state(expected_state, pc) # Try payment on closed channel with pytest.raises(paymentchannel.ClosedError): pc.pay(1)
def test_paymentchannel_expiration(): # Create mocked dependencies bc = mock.MockBlockchain() wallet = walletwrapper.Two1WalletWrapper(mock.MockTwo1Wallet(), bc) db = database.Sqlite3Database(":memory:") # Link the mock blockchain to the mock payment channel server as it is a # non-injected dependency. mock.MockPaymentChannelServer.blockchain = bc # Clear mock payment channel server channels. mock.MockPaymentChannelServer.channels = {} # Open a payment channel with 100000 deposit, default expiration, and 30000 fee pc = paymentchannel.PaymentChannel.open(db, wallet, bc, 'mock://test', 100000, DEFAULT_EXPIRATION, 30000, False) # Confirm the deposit tx bc.mock_confirm(pc.deposit_txid) pc.sync() # Assert payment channel properties expected_state = {} expected_state['url'] = "mock://test/" + pc.deposit_txid expected_state['state'] = statemachine.PaymentChannelState.READY expected_state['ready'] = True expected_state['balance'] = 100000 expected_state['deposit'] = 100000 expected_state['fee'] = 30000 expected_state['creation_time'] = lambda pc: pc.creation_time > 0 expected_state['expiration_time'] = int(pc.creation_time + DEFAULT_EXPIRATION) expected_state['expired'] = False expected_state['refund_tx'] = lambda pc: pc.refund_tx expected_state['refund_txid'] = lambda pc: pc.refund_txid expected_state['deposit_tx'] = lambda pc: pc.deposit_tx expected_state['deposit_txid'] = lambda pc: pc.deposit_txid expected_state['payment_tx'] = None expected_state['spend_tx'] = None expected_state['spend_txid'] = None assert_paymentchannel_state(expected_state, pc) # Make a few payments pc.pay(1) pc.pay(1) pc.pay(1) # Check payment channel properties expected_state['balance'] = 96998 expected_state['expired'] = False expected_state['payment_tx'] = lambda pc: pc.payment_tx assert_paymentchannel_state(expected_state, pc) # Monkey-patch time.time() for expiration orig_time_time = time.time time.time = lambda: pc.expiration_time + paymentchannel.PaymentChannel.REFUND_BROADCAST_TIME_OFFSET + 1 # Check payment channel properties expected_state['expired'] = True assert_paymentchannel_state(expected_state, pc) # Sync to trigger a refund pc.sync() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND expected_state['ready'] = False expected_state['expired'] = True expected_state['spend_txid'] = lambda pc: pc.refund_txid assert_paymentchannel_state(expected_state, pc) # Confirm refund tx bc.mock_confirm(pc.refund_txid) # Sync pc.sync() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CLOSED expected_state['spend_tx'] = lambda pc: pc.refund_tx # Restore time.time() time.time = orig_time_time
def test_paymentchannel_serverside_close(): # Create mocked dependencies bc = mock.MockBlockchain() wallet = walletwrapper.Two1WalletWrapper(mock.MockTwo1Wallet(), bc) db = database.Sqlite3Database(":memory:") # Link the mock blockchain to the mock payment channel server as it is a # non-injected dependency. mock.MockPaymentChannelServer.blockchain = bc # Clear mock payment channel server channels. mock.MockPaymentChannelServer.channels = {} # Open a payment channel with 100000 deposit, 86400 seconds expiration, and 30000 fee pc = paymentchannel.PaymentChannel.open(db, wallet, bc, 'mock://test', 100000, 86400, 30000, False) # Confirm the deposit tx bc.mock_confirm(pc.deposit_txid) pc.sync() # Assert payment channel properties expected_state = {} expected_state['url'] = "mock://test/" + pc.deposit_txid expected_state['state'] = statemachine.PaymentChannelState.READY expected_state['ready'] = True expected_state['balance'] = 100000 expected_state['deposit'] = 100000 expected_state['fee'] = 30000 expected_state['creation_time'] = lambda pc: pc.creation_time > 0 expected_state['expiration_time'] = int(pc.creation_time + 86400) expected_state['expired'] = False expected_state['refund_tx'] = lambda pc: pc.refund_tx expected_state['refund_txid'] = lambda pc: pc.refund_txid expected_state['deposit_tx'] = lambda pc: pc.deposit_tx expected_state['deposit_txid'] = lambda pc: pc.deposit_txid expected_state['payment_tx'] = None expected_state['spend_tx'] = None expected_state['spend_txid'] = None assert_paymentchannel_state(expected_state, pc) # Make a few payments pc.pay(1) pc.pay(1) pc.pay(1) # Check payment channel properties expected_state['balance'] = 96998 expected_state['expired'] = False expected_state['payment_tx'] = lambda pc: pc.payment_tx assert_paymentchannel_state(expected_state, pc) # Close the channel server-side by broadcasting the last payment tx bc.broadcast_tx(mock.MockPaymentChannelServer.channels[pc.deposit_txid] ['payment_tx'].to_hex()) # Sync pc.sync() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND expected_state['ready'] = False expected_state['spend_txid'] = lambda pc: pc.spend_txid assert_paymentchannel_state(expected_state, pc) # Confirm payment tx bc.mock_confirm(pc.spend_txid) # Sync pc.sync() # Check payment channel properties expected_state['state'] = statemachine.PaymentChannelState.CLOSED expected_state['spend_tx'] = lambda pc: pc.spend_tx assert_paymentchannel_state(expected_state, pc)
def test_paymentchannelclient(): # Create mocked dependencies wallet = mock.MockTwo1Wallet() db = database.Sqlite3Database(":memory:") bc = mock.MockBlockchain() # Link the mock blockchain to the mock payment channel server as it is a # non-injected dependency. mock.MockPaymentChannelServer.blockchain = bc # Clear mock payment channel server channels. mock.MockPaymentChannelServer.channels = {} # Create a payment channel client pc = paymentchannelclient.PaymentChannelClient(wallet, _database=db, _blockchain=bc) # Check channel list assert pc.list() == [] # Open a payment channel with 100000 deposit, default expiration, and 10000 fee url1 = pc.open('mock://test', 100000, DEFAULT_EXPIRATION, 10000, False) # Open a payment channel with 300000 deposit, 500000 seconds expiration, and 20000 fee url2 = pc.open('mock://test', 300000, 500000, 20000, True) # Check channel list assert len(pc.list()) == 2 # Check url1 properties status = pc.status(url1) url1_expected_status = {} url1_expected_status[ 'url'] = lambda status: "mock://test/" + status.deposit_txid url1_expected_status[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_DEPOSIT url1_expected_status['ready'] = False url1_expected_status['balance'] = 100000 url1_expected_status['deposit'] = 100000 url1_expected_status['fee'] = 10000 url1_expected_status[ 'creation_time'] = lambda status: status.creation_time > 0 url1_expected_status['expiration_time'] = int(status.creation_time + DEFAULT_EXPIRATION) url1_expected_status['expired'] = False url1_expected_status['deposit_txid'] = lambda status: status.deposit_txid url1_expected_status['spend_txid'] = None assert_paymentchannel_status(url1_expected_status, status) # Check url2 properties status = pc.status(url2) url2_expected_status = {} url2_expected_status[ 'url'] = lambda status: "mock://test/" + status.deposit_txid url2_expected_status['state'] = statemachine.PaymentChannelState.READY url2_expected_status['ready'] = True url2_expected_status['balance'] = 300000 url2_expected_status['deposit'] = 300000 url2_expected_status['fee'] = 20000 url2_expected_status[ 'creation_time'] = lambda status: status.creation_time > 0 url2_expected_status['expiration_time'] = int(status.creation_time + 500000) url2_expected_status['expired'] = False url2_expected_status['deposit_txid'] = lambda status: status.deposit_txid url2_expected_status['spend_txid'] = None assert_paymentchannel_status(url2_expected_status, status) # Try premature close on url1 with pytest.raises(paymentchannel.NotReadyError): pc.close(url1) # Try premature pay on url1 with pytest.raises(paymentchannel.NotReadyError): pc.pay(url1, 1) # Confirm url1 deposit bc.mock_confirm(pc.status(url1).deposit_txid) # Sync channels pc.sync() # Check url1 readiness status = pc.status(url1) url1_expected_status['state'] = statemachine.PaymentChannelState.READY url1_expected_status['ready'] = True assert_paymentchannel_status(url1_expected_status, status) # Try to pay to an invalid channel with pytest.raises(paymentchannelclient.NotFoundError): pc.pay("foo", 1) # Try to pay excess amount with pytest.raises(paymentchannel.InsufficientBalanceError): pc.pay(url1, 500000) # Try to close without payment with pytest.raises(paymentchannel.NoPaymentError): pc.close(url1) # Pay to both channels pc.pay(url1, 1500) pc.pay(url2, 3500) pc.pay(url1, 123) pc.pay(url2, 1) pc.pay(url1, 400) pc.pay(url2, 10000) pc.pay(url1, 20) pc.pay(url2, 3123) # Check url1 properties status = pc.status(url1) url1_expected_status['balance'] = 96457 assert_paymentchannel_status(url1_expected_status, status) # Check url2 properties status = pc.status(url2) url2_expected_status['balance'] = 283376 assert_paymentchannel_status(url2_expected_status, status) # Close url1 pc.close(url1) # Check url1 properties status = pc.status(url1) url1_expected_status[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND url1_expected_status['ready'] = False url1_expected_status['spend_txid'] = str( mock.MockPaymentChannelServer.channels[ status.deposit_txid]['payment_tx'].hash) assert_paymentchannel_status(url1_expected_status, status) # Confirm close bc.mock_confirm(status.spend_txid) # Sync pc.sync() # Check url1 properties status = pc.status(url1) url1_expected_status['state'] = statemachine.PaymentChannelState.CLOSED assert_paymentchannel_status(url1_expected_status, status) # Check url2 properties status = pc.status(url2) assert_paymentchannel_status(url2_expected_status, status) # Close url2 server-side bc.broadcast_tx(mock.MockPaymentChannelServer.channels[status.deposit_txid] ['payment_tx'].to_hex()) # Sync pc.sync() # Check url1 properties status = pc.status(url1) assert_paymentchannel_status(url1_expected_status, status) # Try pay after close on url1 with pytest.raises(paymentchannel.ClosedError): pc.pay(url1, 1) # Check url2 properties status = pc.status(url2) url2_expected_status[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND url2_expected_status['ready'] = False url2_expected_status['spend_txid'] = str( mock.MockPaymentChannelServer.channels[ status.deposit_txid]['payment_tx'].hash) assert_paymentchannel_status(url2_expected_status, status) # Confirm close bc.mock_confirm(status.spend_txid) # Sync pc.sync() # Check url1 properties status = pc.status(url1) assert_paymentchannel_status(url1_expected_status, status) # Check url2 properties status = pc.status(url2) url2_expected_status['state'] = statemachine.PaymentChannelState.CLOSED assert_paymentchannel_status(url2_expected_status, status) # Open a payment channel with 400000 deposit, 500000 seconds expiration, and 20000 fee url3 = pc.open('mock://test', 400000, 500000, 20000, True) # Check url3 properties status = pc.status(url3) url3_expected_status = {} url3_expected_status['url'] = 'mock://test/' + status.deposit_txid url3_expected_status['state'] = statemachine.PaymentChannelState.READY url3_expected_status['ready'] = True url3_expected_status['balance'] = 400000 url3_expected_status['deposit'] = 400000 url3_expected_status['fee'] = 20000 url3_expected_status[ 'creation_time'] = lambda status: status.creation_time > 0 url3_expected_status['expiration_time'] = int(status.creation_time + 500000) url3_expected_status['expired'] = False url3_expected_status['deposit_txid'] = lambda status: status.deposit_txid url3_expected_status['spend_txid'] = None assert_paymentchannel_status(url3_expected_status, status) # Monkey-patch time.time() for expiration orig_time_time = time.time time.time = lambda: status.expiration_time + paymentchannel.PaymentChannel.REFUND_BROADCAST_TIME_OFFSET + 1 # Sync pc.sync() # Check url3 properties status = pc.status(url3, include_txs=True) url3_expected_status[ 'state'] = statemachine.PaymentChannelState.CONFIRMING_SPEND url3_expected_status['ready'] = False url3_expected_status['expired'] = True url3_expected_status['spend_txid'] = str( bitcoin.Transaction.from_hex(status.transactions.refund_tx).hash) assert_paymentchannel_status(url3_expected_status, status) # Confirm refund bc.mock_confirm(status.spend_txid) # Sync pc.sync() # Check url3 properties status = pc.status(url3, include_txs=True) url3_expected_status['state'] = statemachine.PaymentChannelState.CLOSED assert_paymentchannel_status(url3_expected_status, status) # Restore time.time() time.time = orig_time_time # Ensure all channels are closed for url in pc.list(): assert pc.status(url).state == statemachine.PaymentChannelState.CLOSED
import pytest import two1.bitcoin as bitcoin import two1.channels.statemachine as statemachine import two1.channels.database as database @pytest.fixture(params=[ database.Sqlite3Database(":memory:"), ]) def db(request): return request.param def test_database_sqlite3_create(db): # Model test vectors models = [ statemachine.PaymentChannelModel( url='test0', state=statemachine.PaymentChannelState.OPENING, ), statemachine.PaymentChannelModel( url='test1', state=statemachine.PaymentChannelState.READY, creation_time=42, deposit_tx=bitcoin.Transaction.from_hex( "010000000119de54dd7043927219cca4c06cc8b94c7c862b6486b0f989ea4c6569fb34383d010000006b483045022100c45e5bd8d00caa1cd3ad46e078ec132c9c505b3168d1d1ffe6285cf054f54ed302203ea12c4203ccee8a9de616cc22f081eed47a78660ce0a01cb3a97e302178a573012103ee071c95cb772e57a6d8f4f987e9c61b857e63d9f3b5be7a84bdba0b5847099dffffffff0198b101000000000017a9149bc3354ccfd998cf16628449b940e6914210f1098700000000" ), # nopep8 refund_tx=bitcoin.Transaction.from_hex( "0100000001ef513a66dd5f79c0b6cac9b74192b6d405724a7f559979f5aad5ab848c551a7e000000009c47304402207c866a5d8d46c767975c95b9fa65051578898445c85f367c4d6b56c6b795491102202db45315bfd27aa19bd7156aa70aed48ebe331c88297711ff675da5ff069f7b90101004c5063210316f5d704b828c3252432886a843649730e08ae01bbbd5c6bde63756d7f54f961ad670432a77056b175682103ee071c95cb772e57a6d8f4f987e9c61b857e63d9f3b5be7a84bdba0b5847099dac0000000001888a0100000000001976a914b42fb00f78266bba89feee86036df44401320fba88ac32a77056" ), # nopep8