예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #5
0
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
예제 #6
0
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