示例#1
0
def test_spend_unvault_tx_two_traders(bitcoind):
    """
    This tests the unvault_tx spending with the signature of the two traders.
    """
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # The co-signing server, required by the spend tx
    serv_privkey = os.urandom(32)
    serv_pubkey = CKey(serv_privkey).pub
    # Create the vault and unvault transactions
    amount_vault = 50 * COIN - 500
    amount_unvault = amount_vault - 500
    txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey,
                           amount_vault, amount_unvault)
    amount_spend = amount_unvault - 500
    # The address to spend to
    addr = bitcoind.getnewaddress()
    CTx = create_spend_tx(txid, 0, {addr: amount_spend})
    # The first two stakeholders are the traders
    sigs = sign_spend_tx(CTx, stk_privkeys[:2], stk_pubkeys, serv_pubkey,
                         amount_unvault)
    # We need the cosigning server sig, too !
    sig_serv = sign_spend_tx(CTx, [serv_privkey], stk_pubkeys, serv_pubkey,
                             amount_unvault)
    # Ok we have all the sigs we need, let's spend it...
    CTx = form_spend_tx(CTx, stk_pubkeys, serv_pubkey,
                        [*sigs, bytes(0), *sig_serv])
    # ... After the relative locktime !
    for i in range(5):
        with pytest.raises(VerifyRejectedError, match="non-BIP68-final"):
            bitcoind.send_tx(b2x(CTx.serialize()))
        bitcoind.generate_block(1)
    bitcoind.send_tx(b2x(CTx.serialize()))
    assert bitcoind.has_utxo(addr)
示例#2
0
def test_emergency_unvault_tx(bitcoind):
    """This tests the emergency_unvault_tx() function."""
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # The stakeholders emergency keys
    emer_privkeys = [os.urandom(32) for i in range(4)]
    emer_pubkeys = [CKey(k).pub for k in emer_privkeys]
    # The co-signing server, required by the spend tx
    serv_privkey = CKey(os.urandom(32))
    serv_pubkey = serv_privkey.pub
    # Create the vault and unvault transactions
    amount_vault = 50 * COIN - 500
    amount_unvault = amount_vault - 500
    txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey,
                           amount_vault, amount_unvault)
    amount_emer = amount_unvault - 500
    # Actually vout MUST be 0.
    CTx = create_emer_unvault_tx(txid, 0, emer_pubkeys, amount_emer)
    sigs = [
        sign_emer_unvault_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault)
        for p in stk_privkeys
    ]
    CTx = form_emer_unvault_tx(CTx, sigs, stk_pubkeys, serv_pubkey)
    bitcoind.send_tx(b2x(CTx.serialize()))
示例#3
0
def creates_add_input(bitcoind, tx):
    """Creates and add an input to a CMutableTransaction, SIGHASH_ALL.

    :returns: The txid of the first stage fee bumping tx (for convenience)
    """
    # First we get some coins
    privkey = CKey(os.urandom(32))
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    # Let's say we want to increase the fees by 5000 sats
    amount = 5000

    # Bitcoind is nice and will create the first stage transaction
    first_txid = bitcoind.rpc.sendtoaddress(str(address), amount / COIN)
    vout_index = get_output_index(
        bitcoind.rpc.getrawtransaction(first_txid, 1), amount)
    # === We don't generate a block yet ! ===

    tx.vin.append(
        CTxIn(COutPoint(lx(first_txid), vout_index), nSequence=0xfffffffe))
    # Sign the new input with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            amount, SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))

    return first_txid
示例#4
0
 def __init__(self):
     """Uncommon pattern, but a handy one. We setup everything when the
     wrapper is initialized."""
     self.server = Flask(__name__)
     self.privkey = os.urandom(32)
     self.pubkey = CKey(self.privkey).pub
     # List of txids we already signed
     self.already_signed = []
     bitcoin.SelectParams("regtest")
     self.setup_routes()
示例#5
0
def test_spend_creation(vault_factory):
    """Test that the signature exchange between the traders and cosigner leads
    to a well-formed spend_tx, and that we can spend to ACKed addresses."""
    wallets = vault_factory.get_wallets()
    trader_A, trader_B = wallets[0], wallets[1]
    bitcoind = trader_A.bitcoind
    create_new_vaults(wallets, 1)

    # Try to spend from the newly created vault
    vault = trader_A.vaults[0]
    unvault_amount = vault["unvault_tx"].vout[0].nValue
    spend_amount = unvault_amount - 50000
    # We choose a valid address..
    addresses = {
        random.choice(trader_A.acked_addresses): spend_amount,
    }

    # The first trader creates the tx, signs it, pass both the tx and sig to B
    trader_A.initiate_spend(vault, addresses)
    # B hands his signature to A
    sigB = trader_B.accept_spend(vault["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    # Then A forms the transaction and tells everyone, we can broadcast it.
    tx, accepted = trader_A.complete_spend(vault, pubkeyB, sigB, addresses)
    assert accepted
    bitcoind.broadcast_and_mine(
        trader_A.get_signed_unvault_tx(vault).serialize().hex())
    # At this point we should have remarked the spend, and have removed
    # the vault.
    wait_for(lambda: all(len(w.vaults) == 0 for w in wallets))
    # Generate 5 blocks for the locktime !
    addr = bitcoind.getnewaddress()
    bitcoind.generatetoaddress(5, addr)
    bitcoind.broadcast_and_mine(b2x(tx.serialize()))
示例#6
0
def test_emergency_txout(bitcoind):
    """Test mostly the emergency tx locktime"""
    amount = Decimal("50") - Decimal("500") / Decimal(COIN)
    privkeys = [CKey(os.urandom(32)) for _ in range(4)]
    pubkeys = [k.pub for k in privkeys]
    txo = emergency_txout(pubkeys, COIN * amount)
    addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey))
    # This makes a transaction with only one vout
    txid = bitcoind.pay_to(addr, amount)
    new_amount = amount - Decimal("500") / Decimal(COIN)
    addr = bitcoind.getnewaddress()
    txin = CTxIn(COutPoint(lx(txid), 0), nSequence=4464)
    txout = CTxOut(new_amount * COIN, CBitcoinAddress(addr).to_scriptPubKey())
    tx = CMutableTransaction([txin], [txout], nVersion=2)
    tx_hash = SignatureHash(emergency_script(pubkeys), tx, 0, SIGHASH_ALL,
                            int(amount * COIN), SIGVERSION_WITNESS_V0)
    sigs = [k.sign(tx_hash) + bytes([SIGHASH_ALL]) for k in privkeys]
    witness_script = [bytes(0), *sigs, emergency_script(pubkeys)]
    tx.wit = CTxWitness([CTxInWitness(CScriptWitness(witness_script))])
    # 1 month of locktime
    bitcoind.generate_block(4464 - 2)
    with pytest.raises(VerifyRejectedError, match="non-BIP68-final"):
        bitcoind.send_tx(tx.serialize().hex())
    bitcoind.generate_block(1)
    bitcoind.send_tx(tx.serialize().hex())
    assert bitcoind.has_utxo(addr)
示例#7
0
def sign_unvault_revault(tx,
                         privkey,
                         pubkeys,
                         pub_server,
                         prev_value,
                         sign_all=False):
    """Signs a transaction revaulting an unvault transaction.

    This is the "all stakeholders sign" path of the script, not encumbered by a
    timelock.
    This path is used for both the emergency and cancel transactions.
    These transactions are crucial to preserve our security assumptions (i.e. a
    revaulting transaction will be confirmed), so stakeholders exchange
    SINGLE | ANYONECANPAY signatures to allow any of them to increase
    the feerate by appending an input and an output.

    :param tx: The unsigned transaction, a CMutableTransaction.
    :param privkey: (bytes) The private key to sign the transaction with.
    :param pubkeys: The pubkeys of the stakeholders.
    :param pub_server: The pubkey of the cosigning server.
    :param prev_value: The prevout's value in satoshis.
    :param sign_all: If set to True, sign we SIGHASH_ALL instead.

    :return: The signatures for the provided privkeys (a list).
    """
    sighash = SIGHASH_ALL if sign_all else SINGLE_ANYONECANPAY
    tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0,
                            sighash, prev_value, SIGVERSION_WITNESS_V0)
    return CKey(privkey).sign(tx_hash) + bytes([sighash])
示例#8
0
def sign_emergency_vault_tx(tx, privkey, pubkeys, prev_value, sign_all=False):
    """Signs the transaction which moves a vault's coins to the offline 4of4.

    This transaction is crucial to preserve our security assumptions (i.e. a
    revaulting transaction will be confirmed), so stakeholders exchange
    SINGLE | ANYONECANPAY signatures to allow any of them to increase
    the feerate by appending an input and an output.

    :param vault_txid: The id of the transaction funding the vault, as bytes.
    :param privkey: (bytes) The private key to sign the transaction with.
    :param pubkeys: A list containing the public key of each stakeholder.
    :param prev_value: The vault output (previout output) value in satoshis.
    :param sign_all: If set to True, sign we SIGHASH_ALL instead.

    :return: A list, one signature per given privkey.
    """
    sighash = SIGHASH_ALL if sign_all else SINGLE_ANYONECANPAY
    tx_hash = SignatureHash(vault_script(pubkeys),
                            tx,
                            0,
                            sighash,
                            amount=prev_value,
                            sigversion=SIGVERSION_WITNESS_V0)
    # A signature per pubkey
    return CKey(privkey).sign(tx_hash) + bytes([sighash])
示例#9
0
def add_input_output(bitcoind, tx):
    """Add an input and an output to a CMutableTransaction, SIGHASH_ALL."""
    # First we get some coins
    privkey = CKey(os.urandom(32))
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    amount = Decimal("50") * Decimal(COIN) - Decimal("500")
    # This creates a one-output transaction
    txid = bitcoind.pay_to(str(address), amount / Decimal(COIN))
    # We bump the fees by 5000
    tx.vout.append(CTxOut(amount - Decimal("5000"), scriptPubKey))
    tx.vin.append(CTxIn(COutPoint(lx(txid), 0)))
    # Sign the new output with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            int(amount), SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))
示例#10
0
def test_emergency_vault_tx(bitcoind):
    """This tests the emergency_vault_tx() function."""
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # The stakeholders emergency keys
    emer_privkeys = [os.urandom(32) for i in range(4)]
    emer_pubkeys = [CKey(k).pub for k in emer_privkeys]
    # Create the transaction funding the vault
    amount = 50 * COIN - 500
    vault_txid = lx(send_vault_tx(bitcoind, stk_pubkeys, amount))
    # Create the emergency transaction spending from the vault
    amount_min_fees = amount - 500
    emer_tx = create_emergency_vault_tx(vault_txid, 0, amount_min_fees,
                                        emer_pubkeys)
    # Simulate that each stakeholder sign the transaction separately
    sigs = [sign_emergency_vault_tx(emer_tx, stk_pubkeys, amount, [k])[0]
            for k in stk_privkeys]
    emer_tx = form_emergency_vault_tx(emer_tx, stk_pubkeys, sigs)
    bitcoind.send_tx(b2x(emer_tx.serialize()))
示例#11
0
def test_cancel_unvault_tx(bitcoind):
    """This tests that cancel_unvault_tx() produces a valid transaction."""
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # The co-signing server, required by the spend tx
    serv_privkey = CKey(os.urandom(32))
    serv_pubkey = serv_privkey.pub
    # Create the vault and unvault transactions
    amount_vault = 50 * COIN - 500
    amount_unvault = amount_vault - 500
    txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey,
                           amount_vault, amount_unvault)
    amount_cancel = amount_unvault - 500
    # We re-spend to the same vault
    CTx = create_cancel_tx(txid, 0, stk_pubkeys, amount_cancel)
    sigs = [sign_cancel_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault)
            for p in stk_privkeys]
    CTx = form_cancel_tx(CTx, sigs, stk_pubkeys, serv_pubkey)
    bitcoind.send_tx(b2x(CTx.serialize()))
示例#12
0
def test_unvault_tx(bitcoind):
    """This tests the unvault_tx() function."""
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # The co-signing server, required by the spend tx
    serv_privkey = CKey(os.urandom(32))
    serv_pubkey = serv_privkey.pub
    # Create the transaction funding the vault
    amount = 50 * COIN - 500
    vault_txid = lx(send_vault_tx(bitcoind, stk_pubkeys, amount))
    # Create the transaction spending from the vault
    amount_min_fees = amount - 500
    unvtx = create_unvault_tx(vault_txid, 0, stk_pubkeys, serv_pubkey,
                              amount_min_fees)
    assert len(unvtx.vout) == 1
    # Simulate that each stakeholder sign the transaction separately
    sigs = [sign_unvault_tx(unvtx, stk_pubkeys, amount, [k])[0]
            for k in stk_privkeys]
    unvtx = form_unvault_tx(unvtx, stk_pubkeys, sigs)
    bitcoind.send_tx(b2x(unvtx.serialize()))
示例#13
0
def test_vault_address_reuse(vault_factory):
    """Test that we are still safe if coins are sent to an already used vault.
    """
    wallets = vault_factory.get_wallets()
    trader_A = wallets[0]
    bitcoind = trader_A.bitcoind
    reused_address = trader_A.getnewaddress()
    # Concurrent sends to the same address should be fine
    for _ in range(2):
        bitcoind.pay_to(reused_address, 12)
    wait_for(lambda: all(len(wallet.vaults) == 2 for wallet in wallets))
    for wallet in wallets:
        wait_for(lambda: all(v["emergency_signed"] and v["unvault_signed"] and
                             v["unvault_secure"] for v in wallet.vaults))

    # Now test address reuse after a vault has been spent
    # We'll spend this one
    v = random.choice(trader_A.vaults)
    # And the second trader will sign with us the spend
    trader_B = wallets[1]
    spend_amount = 12 * COIN - 50000
    # We choose a valid address..
    addresses = {
        random.choice(trader_A.acked_addresses): spend_amount,
    }

    # The spend process, sig exchange, etc..
    trader_A.initiate_spend(v, addresses)
    sigB = trader_B.accept_spend(v["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    tx, spend_accepted = trader_A.complete_spend(v, pubkeyB, sigB, addresses)
    assert spend_accepted
    bitcoind.broadcast_and_mine(
        trader_A.get_signed_unvault_tx(v).serialize().hex())

    # At this point we should have remarked the spend, and have removed the
    # vault.
    wait_for(lambda: all(len(wallet.vaults) == 1 for wallet in wallets))
    # Generate 5 blocks for the locktime !
    addr = bitcoind.getnewaddress()
    bitcoind.generatetoaddress(5, addr)
    bitcoind.broadcast_and_mine(b2x(tx.serialize()))

    # Creating new vaults to this address should still be fine
    for _ in range(2):
        bitcoind.pay_to(reused_address, 8)
    # 2 - 1 + 2
    wait_for(lambda: all(len(wallet.vaults) == 3 for wallet in wallets))
    for wallet in wallets:
        # Separated for reseting the timeout
        wait_for(lambda: all(v["emergency_signed"] for v in wallet.vaults))
        wait_for(lambda: all(v["unvault_signed"] for v in wallet.vaults))
        assert all(v["unvault_secure"] for v in wallet.vaults)
示例#14
0
def add_input(bitcoind, tx, fees):
    """Add another input to the transaction to bump the feerate."""
    # Don't be dust!
    if fees < 294:
        fees = 294

    # Create the first stage transaction
    new_prevout_addr = P2WPKHBitcoinAddress(bitcoind.getnewaddress())
    txid = bitcoind.sendtoaddress(str(new_prevout_addr), fees / COIN)
    out_index = get_output_index(bitcoind.getrawtransaction(txid, decode=True),
                                 fees)
    # Then gather the private key to unlock its output
    privkey = CKey(wif_decode(bitcoind.dumpprivkey(str(new_prevout_addr))))
    # Add the fetched coin as a new input.
    tx.vin.append(CTxIn(COutPoint(lx(txid), out_index)))
    # We only do this once, sign it with ALL
    tx_hash = SignatureHash(new_prevout_addr.to_redeemScript(), tx, 1,
                            SIGHASH_ALL, fees, SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))

    return tx
示例#15
0
def add_input_output(bitcoind, tx, coin, fees):
    """Add another input to the transaction to bump the feerate."""
    coin_amount = Decimal(coin["amount"]) * Decimal(COIN)
    # First get the private key from bitcoind's wallet.
    privkey = CKey(wif_decode(bitcoind.dumpprivkey(coin["address"])))

    # Add the fetched coin as a new input.
    tx.vin.append(CTxIn(COutPoint(lx(coin["txid"]), coin["vout"])))
    # And likely add an output, otherwise all goes to the fees.
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    if coin_amount > fees + 294:
        # For simplicity, pay to the same script
        tx.vout.append(CTxOut(coin_amount - Decimal(fees), scriptPubKey))
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    # We only do this once, sign it with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            int(coin_amount), SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(
        CTxInWitness(CScriptWitness([sig, privkey.pub]))
    )
    return tx
示例#16
0
def test_vault_address_reuse(vault_factory):
    """Test that we are still safe if coins are sent to an already used vault.
    """
    wallets = vault_factory.get_wallets()
    trader_A = wallets[0]
    # FIXME: separate the Bitcoin backends !!
    bitcoind = trader_A.bitcoind
    reused_address = trader_A.getnewaddress()
    # Concurrent sends to the same address should be fine
    for _ in range(3):
        bitcoind.pay_to(reused_address, 12)
    wait_for(lambda: len(trader_A.vaults) == 3)
    for wallet in wallets:
        wait_for(lambda: all(v["emergency_signed"] and v["unvault_signed"]
                             and v["unvault_secure"] for v in wallet.vaults))

    # Now test address reuse after a vault has been spent
    # We'll spend this one
    v = random.choice(trader_A.vaults)
    # And the second trader will sign with us the spend
    trader_B = wallets[1]
    # FIXME hardcoded fees..
    spend_amount = 12 * COIN - 50000
    # We choose a valid address..
    addresses = {
        random.choice(trader_A.acked_addresses): spend_amount,
    }
    trader_A.initiate_spend(v, addresses)
    sigB = trader_B.accept_spend(v["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    tx = trader_A.complete_spend(v, pubkeyB, sigB, addresses)
    bitcoind.broadcast_and_mine(b2x(v["unvault_tx"].serialize()))
    # At this point we should have remarked the spend, and have either
    # broadcast the cancel_tx, or removed the vault.
    wait_for(lambda: all(len(trader.vaults) == 2 for trader in [trader_A,
                         trader_B]))
    # Generate 5 blocks for the locktime !
    addr = bitcoind.getnewaddress()
    bitcoind.generatetoaddress(5, addr)
    bitcoind.broadcast_and_mine(b2x(tx.serialize()))
    # Creating new vaults should to this address should still be fine
    for _ in range(3):
        bitcoind.pay_to(reused_address, 8)
    # 3 - 1 + 3
    wait_for(lambda: all(len(trader.vaults) == 5 for trader in [trader_A,
                         trader_B]))
    for trader in [trader_A, trader_B]:
        wait_for(lambda: all(v["emergency_signed"] and v["unvault_signed"]
                             and v["unvault_secure"]
                             for v in trader.vaults))
示例#17
0
def sign_spend_tx(tx, privkey, pubkeys, pub_server, prev_value):
    """Signs the transaction which spends the unvault_tx after the relative
    locktime with the given private keys.

    :param tx: The unsigned transaction, a CMutableTransaction.
    :param privkey: (bytes) The private key to sign the transaction with.
    :param pubkeys: A list of the 4 stakeholders' pubkeys, to form the script.
    :param pub_server: The public key of the cosigning server, to form the
                       script.
    :param prev_value: The prevout's value in satoshis.

    :return: A list of the signature for each given private key.
    """
    tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0,
                            SIGHASH_ALL, prev_value, SIGVERSION_WITNESS_V0)
    return CKey(privkey).sign(tx_hash) + bytes([SIGHASH_ALL])
示例#18
0
文件: vault.py 项目: kloaec/re-vault
    def complete_spend(self, vault, peer_pubkey, peer_sig, addresses):
        """Our fellow trader also signed the spend, now ask the cosigner and
        notify other stakeholders we are about to spend a vault. We wait
        synchronously for their response, once again an assumption that's
        a demo!

        :param vault: The vault to spend, an entry of self.vaults[]
        :param peer_pubkey: The other peer's pubkey.
        :param peer_sig: A signature for this spend_tx with the above pubkey.
        :param addresses: A dictionary containing address as keys and amount to
                          send in sats as value.

        :return: The fully signed transaction.
        """
        our_sig = self.create_sign_spend_tx(vault, addresses)
        unvault_txid = vault["unvault_tx"].GetTxid()
        assert len(vault["unvault_tx"].vout) == 1
        unvault_value = vault["unvault_tx"].vout[0].nValue
        cosig = \
            self.cosigner.get_cosignature(unvault_txid[::-1].hex(),
                                          vault["pubkeys"], addresses,
                                          unvault_value)
        spend_tx = create_spend_tx(unvault_txid, 0, addresses)
        # Now the fun part, correctly reconstruct the script
        all_sigs = [bytes(0)] * 3 + [cosig]
        our_pos = vault["pubkeys"].index(CKey(vault["privkey"]).pub)
        peer_pos = vault["pubkeys"].index(peer_pubkey)
        all_sigs[our_pos] = our_sig
        all_sigs[peer_pos] = peer_sig
        spend_tx = form_spend_tx(spend_tx, vault["pubkeys"],
                                 self.cosigner_pubkey, all_sigs)

        # Notify others
        self.sigserver.request_spend(vault["txid"], addresses)
        # Wait for their response, keep it simple..
        while True:
            res = self.sigserver.spend_accepted(vault["txid"])
            if res:
                break
            # May also be None !
            elif res is False:
                raise Exception("Spend rejected.")
            time.sleep(0.5)

        return spend_tx
示例#19
0
def broadcast_unvault(wallets, vault):
    """This broadcasts the unvault transaction in a clean manner."""
    trader_A, trader_B = (wallets[0], wallets[1])
    spend_amount = vault["amount"] - 50000
    # We choose a valid address..
    addresses = {
        random.choice(trader_A.acked_addresses): spend_amount,
    }
    # The first trader creates the tx, signs it, pass both the tx and sig to B
    trader_A.initiate_spend(vault, addresses)
    # B hands his signature to A
    sigB = trader_B.accept_spend(vault["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    # Then A forms the transaction and tells everyone, we can broadcast it.
    tx, accepted = trader_A.complete_spend(vault, pubkeyB, sigB, addresses)
    assert accepted
    trader_A.bitcoind.broadcast_and_mine(
        trader_A.get_signed_unvault_tx(vault).serialize().hex())
示例#20
0
def sign_unvault_spend(tx, privkeys, pubkeys, pub_server, prev_value):
    """Signs a transaction spending from an unvault transaction.

    This is the "all stakeholders sign" path of the script, not encumbered by a
    timelock.
    This path is used for both the emergency and cancel transactions.

    :param tx: The unsigned transaction, a CMutableTransaction.
    :param privkeys: The private keys to sign the transaction with (a list).
    :param pubkeys: The pubkeys of the stakeholders.
    :param pub_server: The pubkey of the cosigning server.
    :param prev_value: The prevout's value in satoshis.

    :return: The signatures for the provided privkeys (a list).
    """
    tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0,
                            SIGHASH_ALL, prev_value, SIGVERSION_WITNESS_V0)
    return [CKey(key).sign(tx_hash) + bytes([SIGHASH_ALL]) for key in privkeys]
示例#21
0
class CosigningServer:
    """
    A wrapper around a dead simple server co-signing spend transactions, but
    only once.
    """
    def __init__(self):
        """Uncommon pattern, but a handy one. We setup everything when the
        wrapper is initialized."""
        self.server = Flask(__name__)
        self.privkey = os.urandom(32)
        self.pubkey = CKey(self.privkey).pub
        # List of txids we already signed
        self.already_signed = []
        bitcoin.SelectParams("regtest")
        self.setup_routes()

    def setup_routes(self):
        @self.server.route("/sign", methods=["POST"])
        def get_signature():
            """Sign a spend transaction."""
            params = request.get_json()
            # Crash if it doesn't contain all entries !
            txid = params["txid"]
            if txid in self.already_signed:
                return jsonify({"sig": None}), 403
            pubkeys = params["pubkeys"]
            addresses = params["addresses"]
            prev_value = params["prev_value"]

            spend_tx = create_spend_tx(lx(txid), 0, addresses)
            pubkeys = [bytes.fromhex(pub) for pub in pubkeys]
            return jsonify({
                "sig":
                sign_spend_tx(spend_tx, self.privkey, pubkeys, self.pubkey,
                              prev_value).hex(),
            }), 200

        @self.server.route("/getpubkey", methods=["GET"])
        def get_pubkey():
            """Get our pubkey for the vault wallets to form the scripts."""
            return jsonify({"pubkey": self.pubkey.hex()}), 200

    def run(self, host, port, debug):
        self.server.run(host, port, debug)
示例#22
0
文件: utils.py 项目: kloaec/re-vault
 def get_wallets(self, emergency_privkeys=None):
     """Get 4 vaults, one for each stakeholder. Spin up the servers."""
     bip32s = [BIP32.from_seed(os.urandom(32), "test") for _ in range(4)]
     xpubs = [bip32.get_master_xpub() for bip32 in bip32s]
     if emergency_privkeys is None:
         emergency_privkeys = [CKey(os.urandom(32)) for _ in range(4)]
     emergency_pubkeys = [k.pub for k in emergency_privkeys]
     self.vaults = []
     # Generate some random 'OK' addresses
     acked_addresses = [self.bitcoind.getnewaddress() for _ in range(5)]
     for bip32 in bip32s:
         xpriv = bip32.get_master_xpriv()
         conf = self.bitcoind.rpc.__btc_conf_file__
         cosigner_url = "http://localhost:{}".format(self.cosigning_port)
         sigserv_url = "http://localhost:{}".format(self.sigserver_port)
         self.vaults.append(
             Vault(xpriv, xpubs, emergency_pubkeys, conf, cosigner_url,
                   sigserv_url, acked_addresses))
     return self.vaults
示例#23
0
def sign_spend_vault_txout(tx, pubkeys, prev_value, privkeys):
    """Signs a transaction spending a vault txout it with the given private keys.

    :param tx: The CMutableTransaction to sign.
    :param pubkeys: A list of each stakeholder's pubkey.
    :param prev_value: The value of the vault txout we spend.
    :param privkeys: A list of the private keys of the four stakeholders to
                     sign the transaction.

    :return: The signatures in the same order as the private keys.
    """
    tx_hash = SignatureHash(vault_script(pubkeys),
                            tx,
                            0,
                            SIGHASH_ALL,
                            amount=prev_value,
                            sigversion=SIGVERSION_WITNESS_V0)
    # A signature per pubkey
    sigs = [CKey(key).sign(tx_hash) + bytes([SIGHASH_ALL]) for key in privkeys]
    return sigs
示例#24
0
def test_revoke_spend(vault_factory):
    """Test that unvaults that aren't authorized are revoked."""
    wallets = vault_factory.get_wallets()
    create_new_vaults(wallets, 1)
    trader_A, trader_B = wallets[0], wallets[1]
    bitcoind = trader_A.bitcoind

    # We spend this one!
    vault = trader_A.vaults[0]
    unvault_amount = vault["unvault_tx"].vout[0].nValue
    spend_amount = unvault_amount - 50000
    # Choose an unauthorized address
    addresses = {
        bitcoind.getnewaddress(): spend_amount,
    }

    # The first trader creates the tx, signs it, pass both the tx and sig to B
    trader_A.initiate_spend(vault, addresses)
    # B hands his signature to A
    sigB = trader_B.accept_spend(vault["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    # Then A forms the transaction and tells everyone, we can broadcast it.
    tx, accepted = trader_A.complete_spend(vault, pubkeyB, sigB, addresses)
    assert not accepted
    first_vault_txid = vault["txid"]
    bitcoind.broadcast_and_mine(
        trader_A.get_signed_unvault_tx(vault).serialize().hex())
    # At this point we should have remarked the spend, and have broadcast the
    # cancel_tx. This means still len(vaults) == 1, but a different one !
    wait_for(lambda: all(len(wallet.vaults) == 1 for wallet in wallets))
    wait_for(lambda: all(wallet.vaults[0]["txid"] != first_vault_txid if len(
        wallet.vaults) > 0 else False for wallet in wallets))
    addr = bitcoind.getnewaddress()
    bitcoind.generatetoaddress(5, addr)
    try:
        with pytest.raises(bitcoin.rpc.VerifyError,
                           match="bad-txns-inputs-missingorspent"):
            bitcoind.broadcast_and_mine(b2x(tx.serialize()))
    except AssertionError:
        with pytest.raises(bitcoin.rpc.VerifyError, match="Missing inputs"):
            bitcoind.broadcast_and_mine(b2x(tx.serialize()))
示例#25
0
def sign_unvault_tx(tx, privkey, pubkeys, prev_value):
    """Signs the unvaulting transaction.

    As it's not a revaulting transaction it's signed with SIGHASH_ALL.
    We can imagine updating this transaction as the fees evolve, though (but
    always with SIGHASH_ALL !).

    :param tx: The id of the transaction funding the vault.
    :param privkey: (bytes) The private key to sign the transaction with.
    :param pubkeys: A list containing the public key of each stakeholder.
    :param prev_value: The vault output (previout output) value in satoshis.

    :return: The signatures in the same order as the given privkeys.
    """
    tx_hash = SignatureHash(vault_script(pubkeys),
                            tx,
                            0,
                            SIGHASH_ALL,
                            amount=prev_value,
                            sigversion=SIGVERSION_WITNESS_V0)
    return CKey(privkey).sign(tx_hash) + bytes([SIGHASH_ALL])
示例#26
0
def test_spend_creation(vault_factory):
    """Test that the signature exchange between the traders and cosigner leads
    to a well-formed spend_tx."""
    wallets = vault_factory.get_wallets()
    trader_A, trader_B = wallets[0], wallets[1]
    # FIXME: separate the Bitcoin backends !!
    bitcoind = trader_B.bitcoind
    bitcoind.pay_to(trader_A.getnewaddress(), 10)
    wait_for(lambda: all(len(w.vaults) == 1 for w in wallets))
    wait_for(lambda: all(v["emergency_signed"] for v in trader_A.vaults))
    wait_for(lambda: all(v["unvault_signed"] for v in trader_B.vaults))
    assert all(v["unvault_secure"] for v in trader_A.vaults)

    # Try to spend from the newly created vault
    vault = trader_A.vaults[0]
    # FIXME hardcoded fees..
    spend_amount = 10 * COIN - 50000
    # We choose a valid address..
    addresses = {
        random.choice(trader_A.acked_addresses): spend_amount,
    }
    # The first trader creates the tx, signs it, pass both the tx and sig to B
    trader_A.initiate_spend(vault, addresses)
    # B hands his signature to A
    sigB = trader_B.accept_spend(vault["txid"], addresses)
    pubkeyB = CKey(trader_B.vaults[0]["privkey"]).pub
    # Then A forms the transaction and tells everyone, we can broadcast it.
    tx = trader_A.complete_spend(vault, pubkeyB, sigB, addresses)
    bitcoind.broadcast_and_mine(b2x(vault["unvault_tx"].serialize()))
    # At this point we should have remarked the spend, and have either
    # broadcast the cancel_tx, or removed the vault.
    wait_for(lambda: all(len(trader.vaults) == 0 for trader in [trader_A,
                         trader_B]))
    # Generate 5 blocks for the locktime !
    addr = bitcoind.getnewaddress()
    bitcoind.generatetoaddress(5, addr)
    bitcoind.broadcast_and_mine(b2x(tx.serialize()))
示例#27
0
apikey = ""
secret = ""


def SignECDSA(key, message):
    sig, i = key.sign_compact(message)

    meta = 27 + i
    if key.is_compressed:
        meta += 4

    return base64.b64encode(chr(meta) + sig)


privkey = CKey(base64.b64decode(secret), False)
nonce = str(int(time.time()))
msg = "nonce=" + nonce
sign = SignECDSA(
    privkey,
    hashlib.sha256(hashlib.sha256("Bitmaszyna.pl API:\n" +
                                  msg).digest()).digest())
print(
    requests.post('https://bitmaszyna.pl/api/funds',
                  data={
                      'nonce': nonce
                  },
                  headers={
                      'Rest-Key': apikey,
                      'Rest-Sign': sign
                  }).json())
示例#28
0
def test_unvault_txout(bitcoind):
    """Test that unvault_txout() produces a valid and conform txo.

    Note that we use python-bitcoinlib for this one, as
    signrawtransactionwithkey is (apparently?) not happy dealing with exotic
    scripts.
    Note also that bitcoinlib's API uses sats, while bitcoind's one uses BTC..
    """
    amount = 50 * COIN - 500
    # The stakeholders
    stk_privkeys = [CKey(os.urandom(32)) for i in range(4)]
    stk_pubkeys = [k.pub for k in stk_privkeys]
    # The cosigning server
    serv_privkey = CKey(os.urandom(32))
    # First, pay to the unvault tx script
    txo = unvault_txout(stk_pubkeys,
                        serv_privkey.pub, amount)
    txo_addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey))
    amount_for_bitcoind = float(Decimal(amount) / Decimal(COIN))
    txid = bitcoind.pay_to(txo_addr, amount_for_bitcoind)
    # We can spend it immediately if all stakeholders sign (emergency or cancel
    # tx)
    txin = CTxIn(COutPoint(lx(txid), 0))
    amount_min_fees = amount - 500
    addr = bitcoind.getnewaddress()
    new_txo = CTxOut(amount_min_fees,
                     CBitcoinAddress(addr).to_scriptPubKey())
    tx = CMutableTransaction([txin], [new_txo], nVersion=2)
    # We can't test the signing against bitcoind, but we can at least test the
    # transaction format
    bitcoind_tx = bitcoind.rpc.createrawtransaction([
        {"txid": txid, "vout": 0}
    ], [
        {addr: float(Decimal(amount_min_fees) / Decimal(COIN))}
    ])
    assert b2x(tx.serialize()) == bitcoind_tx
    tx_hash = SignatureHash(unvault_script(*stk_pubkeys, serv_privkey.pub), tx,
                            0, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0)
    sigs = [key.sign(tx_hash) + bytes([SIGHASH_ALL])
            for key in stk_privkeys[::-1]]  # Note the reverse here
    witness_script = [*sigs,
                      unvault_script(*stk_pubkeys, serv_privkey.pub)]
    witness = CTxInWitness(CScriptWitness(witness_script))
    tx.wit = CTxWitness([witness])
    bitcoind.send_tx(b2x(tx.serialize()))
    assert bitcoind.has_utxo(addr)

    # If two out of three stakeholders sign, we need the signature from the
    # cosicosigning server and we can't spend it before 6 blocks (csv).
    # Pay back to the unvault tx script
    txo = unvault_txout(stk_pubkeys,
                        serv_privkey.pub, amount)
    txo_addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey))
    txid = bitcoind.pay_to(txo_addr, amount_for_bitcoind)
    # Reconstruct the transaction but with only two stakeholders signatures
    txin = CTxIn(COutPoint(lx(txid), 0), nSequence=6)
    amount_min_fees = amount - 500
    addr = bitcoind.getnewaddress()
    new_txo = CTxOut(amount_min_fees,
                     CBitcoinAddress(addr).to_scriptPubKey())
    tx = CMutableTransaction([txin], [new_txo], nVersion=2)
    # We can't test the signing against bitcoind, but we can at least test the
    # transaction format
    bitcoind_tx = bitcoind.rpc.createrawtransaction([
        {"txid": txid, "vout": 0, "sequence": 6}
    ], [
        {addr: float(Decimal(amount_min_fees) / Decimal(COIN))}
    ])
    assert b2x(tx.serialize()) == bitcoind_tx
    tx_hash = SignatureHash(unvault_script(*stk_pubkeys, serv_privkey.pub), tx,
                            0, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0)
    # The cosigning server
    sigs = [serv_privkey.sign(tx_hash) + bytes([SIGHASH_ALL])]
    # We fail the third CHECKSIG !!
    sigs += [empty_signature()]
    sigs += [key.sign(tx_hash) + bytes([SIGHASH_ALL])
             for key in stk_privkeys[::-1][2:]]  # Just the first two
    witness_script = [*sigs,
                      unvault_script(*stk_pubkeys, serv_privkey.pub)]
    witness = CTxInWitness(CScriptWitness(witness_script))
    tx.wit = CTxWitness([witness])
    # Relative locktime !
    for i in range(5):
        with pytest.raises(VerifyRejectedError, match="non-BIP68-final"):
            bitcoind.send_tx(b2x(tx.serialize()))
        bitcoind.generate_block(1)
    # It's been 6 blocks now
    bitcoind.send_tx(b2x(tx.serialize()))
    assert bitcoind.has_utxo(addr)
示例#29
0
def test_increase_revault_tx_feerate(bitcoind):
    """This tests that any of the stakeholders can increase the feerate of any
    of the revaulting transactions in a timely manner. Will justice rule?"""
    # The stakeholders, the first two are the traders.
    stk_privkeys = [os.urandom(32) for i in range(4)]
    stk_pubkeys = [CKey(k).pub for k in stk_privkeys]
    # Same, but for the EDV
    emer_privkeys = [os.urandom(32) for i in range(4)]
    emer_pubkeys = [CKey(k).pub for k in emer_privkeys]
    # The co-signing server, required by the spend tx
    serv_privkey = os.urandom(32)
    serv_pubkey = CKey(serv_privkey).pub

    # Test the vault emergency
    amount_vault = 50 * COIN - 500
    txid = send_vault_tx(bitcoind, stk_pubkeys, amount_vault)
    amount_emer = amount_vault - 500
    CTx = create_emergency_vault_tx(lx(txid), 0, amount_emer, emer_pubkeys)
    sigs = [
        sign_emergency_vault_tx(CTx, p, stk_pubkeys, amount_vault)
        for p in stk_privkeys
    ]
    # Sanity checks don't hurt
    assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs)
    CMTx = CMutableTransaction.from_tx(
        form_emergency_vault_tx(CTx, stk_pubkeys, sigs))
    fees_before = tx_fees(bitcoind, CMTx)
    first_txid = creates_add_input(bitcoind, CMTx)
    fees_after = tx_fees(bitcoind, CMTx)
    assert fees_after > fees_before
    bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid])

    # Test the emer unvault
    amount_vault = 50 * COIN - 500
    amount_unvault = amount_vault - 500
    txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey,
                           amount_vault, amount_unvault)
    amount_emer = amount_unvault - 500
    CTx = create_emer_unvault_tx(txid, 0, emer_pubkeys, amount_emer)
    sigs = [
        sign_emer_unvault_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault)
        for p in stk_privkeys
    ]
    # Sanity checks don't hurt
    assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs)
    CMTx = CMutableTransaction.from_tx(
        form_emer_unvault_tx(CTx, sigs, stk_pubkeys, serv_pubkey))
    fees_before = tx_fees(bitcoind, CMTx)
    first_txid = creates_add_input(bitcoind, CMTx)
    fees_after = tx_fees(bitcoind, CMTx)
    assert fees_after > fees_before
    bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid])

    # Test the cancel unvault
    amount_vault = 50 * COIN - 500
    amount_unvault = amount_vault - 500
    txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey,
                           amount_vault, amount_unvault)
    amount_cancel = amount_unvault - 500
    CTx = create_cancel_tx(txid, 0, emer_pubkeys, amount_cancel)
    sigs = [
        sign_cancel_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault)
        for p in stk_privkeys
    ]
    # Sanity checks don't hurt
    assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs)
    CMTx = CMutableTransaction.from_tx(
        form_cancel_tx(CTx, sigs, stk_pubkeys, serv_pubkey))
    fees_before = tx_fees(bitcoind, CMTx)
    first_txid = creates_add_input(bitcoind, CMTx)
    fees_after = tx_fees(bitcoind, CMTx)
    assert fees_after > fees_before
    bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid])
SelectParams('testnet')

MONGOCONNECTION = pymongo.Connection('52.1.141.196', 27017)
MONGODB = MONGOCONNECTION.escrow.demo
escrow = MONGODB.find_one(
    {'buyerurlhash': "906618b107da70ed301d701ce8dbff533f35812d"})

phrase = "sample core fitness wrong unusual inch hurry chaos myself credit welcome margin"
seed = mnemonic.Mnemonic.to_seed(phrase)
wallet = BIP32Node.from_master_secret(seed, 'XTN')
toddkeys = []
keys = []
for k in escrow['keys']:
    print k['subkey']
    hdkey = wallet.subkey_for_path(k['subkey'])
    print b2x(CKey(hdkey.sec()).pub)
    print hdkey.sec_as_hex()
    print k['publickey']
    print ""
    toddkeys.append(CKey(hdkey.sec()))
    keys.append(CPubKey(hdkey.address()))
"""
keys = []
for pubkey in escrow['keys']:
    print "PUBLIC KEY", pubkey['publickey']
    keys.append(CPubKey(pubkey['publickey']))
"""

# Create a redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
redeemScript = CScript(keys)