Exemplo n.º 1
0
 def open_channel(self, my_money, their_money, fees, their_coins,
                  their_change, their_pubkey, their_addr):
     """Respond to a requested open."""
     assert self.state == 'begin'
     # Get inputs and change output
     coins, change = self.select_coins(my_money + 2 * fees)
     # Make the anchor script
     anchor_output_script = self.anchor_script(self.private_key.pub,
                                               their_pubkey)
     # Construct the anchor utxo
     payment = CMutableTxOut(my_money + their_money + 2 * fees,
                             anchor_output_script.to_p2sh_scriptPubKey())
     # Anchor tx
     transaction = CMutableTransaction(their_coins + coins,
                                       [payment, change, their_change])
     # Half-sign
     transaction = self.bitcoind.signrawtransaction(transaction)['tx']
     # Create channel in DB
     self.anchor.prevout = CMutableOutPoint(transaction.GetHash(), 0)
     self.anchor.scriptSig = AnchorScriptSig(0, b'', anchor_output_script)
     self.our.nValue = my_money
     self.our.scriptPubKey = self.bitcoind.getnewaddress()
     self.their.nValue = their_money
     self.their.scriptPubKey = their_addr
     # Event: channel opened
     channel_opened.send(self.cmd_id, address=self.address)
     self.bob.open_accept(transaction, anchor_output_script,
                          self.our.scriptPubKey)
     self.state = 'open_wait_1.5'
Exemplo n.º 2
0
def open_channel(address, mymoney, theirmoney, fees, their_coins, their_change, their_pubkey, their_out_addr): # pylint: disable=too-many-arguments
    """Open a payment channel."""
    # Get inputs and change output
    coins, change = select_coins(mymoney + 2 * fees)
    # Make the anchor script
    anchor_output_script = anchor_script(get_pubkey(), their_pubkey)
    anchor_output_address = anchor_output_script.to_p2sh_scriptPubKey()
    # Construct the anchor utxo
    payment = CMutableTxOut(mymoney + theirmoney + 2 * fees, anchor_output_address)
    # Anchor tx
    transaction = CMutableTransaction(
        their_coins + coins,
        [payment, change, their_change])
    # Half-sign
    transaction = g.bit.signrawtransaction(transaction)['tx']
    # Create channel in DB
    channel = Channel(address,
                      CMutableTxIn(CMutableOutPoint(transaction.GetHash(), 0),
                                   AnchorScriptSig(0, b'', anchor_output_script)),
                      CMutableTxOut(mymoney, g.bit.getnewaddress()),
                      CMutableTxOut(theirmoney, their_out_addr))
    channel.put()
    # Event: channel opened
    CHANNEL_OPENED.send('channel', address=address)
    return (transaction, anchor_output_script, channel.our.scriptPubKey)
Exemplo n.º 3
0
def open_channel(address, mymoney, theirmoney, 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)
    # Make the anchor script
    anchor_output_script = anchor_script(get_pubkey(), their_pubkey)
    # Construct the anchor utxo
    payment = CMutableTxOut(mymoney + theirmoney + 2 * fees,
                            anchor_output_script.to_p2sh_scriptPubKey())
    # Anchor tx
    transaction = CMutableTransaction(their_coins + coins,
                                      [payment, change, their_change])
    # Half-sign
    transaction = g.bit.signrawtransaction(transaction)['tx']
    # Create channel in DB
    our_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_addr=our_addr,
        their_balance=theirmoney,
        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)
 def test_sendrawtransaction(self):
     num_outputs = 5
     given_transaction = CMutableTransaction([], [make_txout() for x in range(0, num_outputs)])
     expected_txid = b2lx(given_transaction.GetHash())
     given_transaction_hex = b2x(given_transaction.serialize())
     proxy = FakeBitcoinProxy()
     resulting_txid = proxy.sendrawtransaction(given_transaction_hex)
     self.assertEqual(resulting_txid, expected_txid)
Exemplo n.º 5
0
class BitcoinTransaction(object):
    '''Bitcoin transaction object.'''
    @auto_switch_params(1)
    def __init__(self,
                 network,
                 recipient_address: str,
                 value: float,
                 solvable_utxo: list,
                 tx_locktime: int = 0):
        self.recipient_address = recipient_address
        self.value = value
        self.network = network
        self.symbol = network.default_symbol

        self.validate_address()

        self.solvable_utxo = solvable_utxo
        self.utxo_value = sum(utxo.value for utxo in self.solvable_utxo)
        self.tx_in_list = [utxo.tx_in for utxo in self.solvable_utxo]
        self.tx_out_list = []

        self.tx = None
        self.tx_locktime = tx_locktime
        self.fee = 0.0
        self.fee_per_kb = 0.0
        self.signed = False

    def validate_address(self):
        if not self.network.is_valid_address(self.recipient_address):
            raise ValueError('Given recipient address is invalid.')

    def build_outputs(self):
        self.tx_out_list = [
            CMutableTxOut(
                to_base_units(self.value),
                CBitcoinAddress(self.recipient_address).to_scriptPubKey())
        ]

    def add_fee_and_sign(self, default_wallet=None):
        """Signing transaction and adding fee under the hood."""

        # signing the transaction for the first time to get the right transaction size
        self.sign(default_wallet)

        # adding fee based on transaction size (this will modify the transaction)
        self.add_fee()

        # signing the modified transaction
        self.sign(default_wallet)

    def sign(self, default_wallet: BitcoinWallet = None):
        """Signing transaction using the wallet object."""

        for tx_index, tx_in in enumerate(self.tx.vin):
            utxo = self.solvable_utxo[tx_index]
            wallet = utxo.wallet or default_wallet

            if wallet is None:
                raise RuntimeError('Cannot sign transaction without a wallet.')

            tx_script = utxo.parsed_script
            if utxo.contract:
                sig_hash = script.SignatureHash(
                    script.CScript.fromhex(utxo.contract), self.tx, tx_index,
                    script.SIGHASH_ALL)
            else:
                sig_hash = script.SignatureHash(tx_script, self.tx, tx_index,
                                                script.SIGHASH_ALL)
            sig = wallet.private_key.sign(sig_hash) + struct.pack(
                '<B', script.SIGHASH_ALL)
            script_sig = [sig, wallet.private_key.pub
                          ] + utxo.unsigned_script_sig
            tx_in.scriptSig = script.CScript(script_sig)

            VerifyScript(tx_in.scriptSig, tx_script, self.tx, tx_index,
                         (SCRIPT_VERIFY_P2SH, ))
        self.signed = True

    def create_unsigned_transaction(self):
        assert self.utxo_value >= self.value, 'You want to spend more than you\'ve got. Add more UTXO\'s.'
        self.build_outputs()
        self.tx = CMutableTransaction(self.tx_in_list,
                                      self.tx_out_list,
                                      nLockTime=self.tx_locktime)

    def publish(self):
        return self.network.publish(self.raw_transaction)

    @property
    def size(self) -> int:
        """Returns the size of a transaction represented in bytes."""
        return len(self.tx.serialize())

    def calculate_fee(self, add_sig_size=False):
        """Calculating fee for given transaction based on transaction size and estimated fee per kb."""
        if not self.fee_per_kb:
            self.fee_per_kb = self.network.get_current_fee_per_kb()
        size = self.size
        if add_sig_size:
            size += len(self.tx_in_list) * SIGNATURE_SIZE
        self.fee = round((self.fee_per_kb / 1000) * size, 8)

    def add_fee(self):
        """Adding fee to the transaction by decreasing 'change' transaction."""
        if not self.fee:
            self.calculate_fee()
        fee_in_satoshi = to_base_units(self.fee)
        if self.tx.vout[0].nValue < fee_in_satoshi:
            raise RuntimeError(
                'Cannot subtract fee from transaction. You need to add more input transactions.'
            )
        self.tx.vout[0].nValue -= fee_in_satoshi

    @property
    def raw_transaction(self):
        return b2x(self.tx.serialize())

    @property
    def address(self):
        return b2lx(self.tx.GetHash())

    def show_details(self):
        details = {
            'transaction': self.raw_transaction,
            'transaction_address': self.address,
            'fee': self.fee,
            'fee_per_kb': self.fee_per_kb,
            'fee_per_kb_text': f'{self.fee_per_kb:.8f} {self.symbol} / 1 kB',
            'fee_text': f'{self.fee:.8f} {self.symbol}',
            'recipient_address': self.recipient_address,
            'size': self.size,
            'size_text': f'{self.size} bytes',
            'value': self.value,
            'value_text': f'{self.value:.8f} {self.symbol}',
        }
        if self.signed:
            details['transaction_link'] = self.network.get_transaction_url(
                self.address)
        return details

    def get_transaction_url(self) -> Optional[str]:
        '''Wrapper around the `get_transaction_url` method from base network.'''
        if not self.tx:
            return
        return self.network.get_transaction_url(self.address)