Example #1
0
    def unserialize(data, as_coinbase=False):
        k = len(data)

        tr = Transaction()
        tr.version = struct.unpack('<L', data[:4])[0]
        num_inputs, data = Bitcoin.unserialize_variable_int(data[4:])
        for i in range(num_inputs):
            txn_input, data = TransactionInput.unserialize(data, as_coinbase=as_coinbase)
            tr.inputs.append(txn_input)

        num_outputs, data = Bitcoin.unserialize_variable_int(data)
        for i in range(num_outputs):
            try:
                txn_output, data = TransactionOutput.unserialize(data)
            except:
                raise
            tr.outputs.append(txn_output)

        tr.lock_time = struct.unpack("<L", data[:4])[0]

        tr.real_size = k - len(data) + 4

        if any(i.signed and (i.signed_hash_type & SIGHASH_ANYONECANPAY) != 0 for i in tr.inputs):
            tr.type = Transaction.TRANSACTION_TYPE_PAYMENT_ANYONECANPAY

        return tr, data[4:]
Example #2
0
    def key_format_changed(self):
        is_utf8 = self.radioButton_utf8.isChecked()
        is_hex  = self.radioButton_hex.isChecked()
        assert is_utf8 ^ is_hex

        if self.selected_key_format == 'utf8':
            if is_utf8:
                return
            # change to hex from utf8
            text = self.textEdit_key.toPlainText()
            text = Bitcoin.bytes_to_hexstring(str(text).encode('utf8'), reverse=False)
            self.selected_key_format = 'hex'
            self.textEdit_key.setText(text)
        elif self.selected_key_format == 'hex':
            if is_hex:
                return
            # change to utf8 from hex
            try:
                text = self.textEdit_key.toPlainText()
                text = Bitcoin.hexstring_to_bytes(str(text), reverse=False).decode('utf8')
                self.selected_key_format = 'utf8'
                self.textEdit_key.setText(text)
            except UnicodeDecodeError:
                gui.QMessageBox.information(self, "Unicode Error", "The hex content cannot be converted to UTF-8")
                self.radioButton_utf8.setChecked(False)
                self.radioButton_hex.setChecked(True)
Example #3
0
    def send_version(self):
        version = self.bitcoin_network.client_version
        services = self.bitcoin_network.available_services
        now = int(time.time())

        recipient_address = Bitcoin.serialize_network_address(
            None,
            self.bitcoin_network.available_services,
            with_timestamp=False)
        sender_address = Bitcoin.serialize_network_address(
            None,
            self.bitcoin_network.available_services,
            with_timestamp=False)

        nonce = random.randrange(0, 1 << 64)
        user_agent = Bitcoin.serialize_string(self.bitcoin_network.user_agent)
        lastBlock = random.randrange(
            1024,
            102400 * 2)  # TODO - is it OK to just say '0' on every connect?

        payload = struct.pack(
            "<LQQ", version, services,
            now) + recipient_address + sender_address + struct.pack(
                "<Q", nonce) + user_agent + struct.pack("<L", lastBlock)
        message = Bitcoin.wrap_message("version", payload,
                                       Bitcoin.NETWORK_DELIVERY)

        self.send(message)
Example #4
0
    def key_format_changed(self):
        is_utf8 = self.radioButton_utf8.isChecked()
        is_hex = self.radioButton_hex.isChecked()
        assert is_utf8 ^ is_hex

        if self.selected_key_format == 'utf8':
            if is_utf8:
                return
            # change to hex from utf8
            text = self.textEdit_key.toPlainText()
            text = Bitcoin.bytes_to_hexstring(str(text).encode('utf8'),
                                              reverse=False)
            self.selected_key_format = 'hex'
            self.textEdit_key.setText(text)
        elif self.selected_key_format == 'hex':
            if is_hex:
                return
            # change to utf8 from hex
            try:
                text = self.textEdit_key.toPlainText()
                text = Bitcoin.hexstring_to_bytes(str(text),
                                                  reverse=False).decode('utf8')
                self.selected_key_format = 'utf8'
                self.textEdit_key.setText(text)
            except UnicodeDecodeError:
                gui.QMessageBox.information(
                    self, "Unicode Error",
                    "The hex content cannot be converted to UTF-8")
                self.radioButton_utf8.setChecked(False)
                self.radioButton_hex.setChecked(True)
Example #5
0
    def serializeForSignature(self, inputIndex, hashType):
        data_list = []
        data_list.append(struct.pack("<L", self.version))

        if (hashType & SIGHASH_ANYONECANPAY) != 0:
            data_list.append(Bitcoin.serialize_variable_int(1))
            data_list.append(self.inputs[inputIndex].serializeForSignature(hashType, True))
        else:
            data_list.append(Bitcoin.serialize_variable_int(len(self.inputs)))
            for i, input in enumerate(self.inputs):
                data_list.append(input.serializeForSignature(hashType, i == inputIndex))

        # outputs
        if (hashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_NONE:
            data_list.append(Bitcoin.serialize_variable_int(0))
        elif (hashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_ALL:
            data_list.append(Bitcoin.serialize_variable_int(len(self.outputs)))
            for i, output in enumerate(self.outputs):
                data_list.append(output.serializeForSignature(hashType))
        elif (hashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE:
            data_list.append(Bitcoin.serialize_variable_int(1))
            assert inputIndex < len(self.outputs)
            data_list.append(self.outputs[inputIndex].serializeForSignature(hashType))

        data_list.append(struct.pack("<L", self.lock_time))

        data_list.append(struct.pack("<L", hashType))

        return b''.join(data_list)
Example #6
0
    def handle_addr(self, payload):
        count, payload = Bitcoin.unserialize_variable_int(payload)

        for i in range(min(count, 100)):
            addr, services, when, payload = Bitcoin.unserialize_network_address(
                payload, with_timestamp=self.peer_version >= 31402)
            self.bitcoin_network.discovered_peer_address(
                '{}:{}'.format(*addr), when, services)
Example #7
0
    def sendGetData(self, items):
        data = []
        for item in items:
            data.append(Bitcoin.serialize_inv(item[0], item[1]))

        payload = Bitcoin.serialize_variable_int(len(data)) + b''.join(data)
        message = Bitcoin.wrap_message("getdata", payload, Bitcoin.NETWORK_DELIVERY)
        self.send(message)
Example #8
0
    def sendGetData(self, items):
        data = []
        for item in items:
            data.append(Bitcoin.serialize_inv(item[0], item[1]))

        payload = Bitcoin.serialize_variable_int(len(data)) + b''.join(data)
        message = Bitcoin.wrap_message("getdata", payload,
                                       Bitcoin.NETWORK_DELIVERY)
        self.send(message)
Example #9
0
    def serialize_size(self):
        data_size = 0
        data_size += 4

        data_size += Bitcoin.serialize_variable_int_size(len(self.inputs))
        for i, input in enumerate(self.inputs):
            data_size += input.serialize_size()

        data_size += Bitcoin.serialize_variable_int_size(len(self.outputs))
        for i, output in enumerate(self.outputs):
            data_size += output.serialize_size()

        data_size += 4
        return data_size
Example #10
0
    def serialize(self):
        data_list = []
        data_list.append(struct.pack("<L", self.version))

        data_list.append(Bitcoin.serialize_variable_int(len(self.inputs)))
        for i, input in enumerate(self.inputs):
            data_list.append(input.serialize())

        data_list.append(Bitcoin.serialize_variable_int(len(self.outputs)))
        for i, output in enumerate(self.outputs):
            data_list.append(output.serialize())

        data_list.append(struct.pack("<L", self.lock_time))

        return b''.join(data_list)
Example #11
0
    def handle_inv(self, payload):
        count, payload = Bitcoin.unserialize_variable_int(payload)

        for i in range(count):
            inv, payload = Bitcoin.unserialize_inv(payload)

            if inv['type'] == BitcoinNetwork.MSG_ERROR:
                continue

            elif inv['type'] == BitcoinNetwork.MSG_TX:
                #print('got inv for tx {}'.format(Bitcoin.bytes_to_hexstring(inv['hash'])))
                self.known_transactions.add((inv['hash'], time.time()))

            elif inv['type'] == BitcoinNetwork.MSG_BLOCK:
                #print('got inv for block {}'.format(Bitcoin.bytes_to_hexstring(inv['hash'])))
                pass
Example #12
0
def lookup_unspent_outputs(bitcoin_addresses):
    conn = HTTPConnection('blockchain.info')
    conn.request('GET',
                 '/unspent?active={}'.format('|'.join(bitcoin_addresses)))
    result = json.loads(conn.getresponse().read().decode('utf8'))
    unspent = defaultdict(list)

    for u in result['unspent_outputs']:
        program_bytes = Bitcoin.hexstring_to_bytes(u['script'], reverse=False)
        scriptPubKey, _ = script.Script.unserialize(program_bytes,
                                                    len(program_bytes))
        address = None

        # Try to extract the address from the scriptpubkey program
        if len(scriptPubKey.program) == 6 and \
           scriptPubKey.program[0][0] == script.OP_DUP and scriptPubKey.program[1][0] == script.OP_HASH160 and \
           scriptPubKey.program[2][0] == 20 and \
           scriptPubKey.program[4][0] == script.OP_EQUALVERIFY and scriptPubKey.program[5][0] == script.OP_CHECKSIG:
            address = scriptPubKey.program[3]
        elif len(scriptPubKey.program
                 ) == 3 and scriptPubKey.program[2][0] == script.OP_CHECKSIG:
            if scriptPubKey.program[2][0] in (0x04, 0x03, 0x02):
                address = base58.decode_to_bytes(
                    addressgen.generate_address(scriptPubKey.program[1],
                                                version=0))[-24:-4]

        if address is not None:
            i = 0
            while address[i] == 0:
                i += 1
            address = '1' + ('1' * i) + addressgen.base58_check(address,
                                                                version=0)
            unspent[address].append(u)
    return unspent
Example #13
0
def lookup_unspent_outputs(bitcoin_addresses):
    conn = HTTPConnection('blockchain.info')
    conn.request('GET', '/unspent?active={}'.format('|'.join(bitcoin_addresses)))
    result = json.loads(conn.getresponse().read().decode('utf8'))
    unspent = defaultdict(list)

    for u in result['unspent_outputs']:
        program_bytes = Bitcoin.hexstring_to_bytes(u['script'], reverse=False)
        scriptPubKey, _ = script.Script.unserialize(program_bytes, len(program_bytes))
        address = None

        # Try to extract the address from the scriptpubkey program
        if len(scriptPubKey.program) == 6 and \
           scriptPubKey.program[0][0] == script.OP_DUP and scriptPubKey.program[1][0] == script.OP_HASH160 and \
           scriptPubKey.program[2][0] == 20 and \
           scriptPubKey.program[4][0] == script.OP_EQUALVERIFY and scriptPubKey.program[5][0] == script.OP_CHECKSIG:
                address = scriptPubKey.program[3]
        elif len(scriptPubKey.program) == 3 and scriptPubKey.program[2][0] == script.OP_CHECKSIG:
            if scriptPubKey.program[2][0] in (0x04, 0x03, 0x02):
                address = base58.decode_to_bytes(addressgen.generate_address(scriptPubKey.program[1], version=0))[-24:-4]

        if address is not None:
            i = 0
            while address[i] == 0:
                i += 1
            address = '1' + ('1' * i) + addressgen.base58_check(address, version=0)
            unspent[address].append(u)
    return unspent
Example #14
0
    def send_version(self):
        version  = self.bitcoin_network.client_version
        services = self.bitcoin_network.available_services
        now      = int(time.time())

        recipient_address = Bitcoin.serialize_network_address(None, self.bitcoin_network.available_services, with_timestamp=False)
        sender_address    = Bitcoin.serialize_network_address(None, self.bitcoin_network.available_services, with_timestamp=False)
        
        nonce      = random.randrange(0, 1 << 64)
        user_agent = Bitcoin.serialize_string(self.bitcoin_network.user_agent)
        lastBlock  = random.randrange(1024, 102400*2) # TODO - is it OK to just say '0' on every connect?

        payload = struct.pack("<LQQ", version, services, now) + recipient_address + sender_address + struct.pack("<Q", nonce) + user_agent + struct.pack("<L", lastBlock)
        message = Bitcoin.wrap_message("version", payload, Bitcoin.NETWORK_DELIVERY)

        self.send(message)
Example #15
0
    def handle_inv(self, payload):
        count, payload = Bitcoin.unserialize_variable_int(payload)

        for i in range(count):
            inv, payload = Bitcoin.unserialize_inv(payload)

            if inv['type'] == BitcoinNetwork.MSG_ERROR:
                continue

            elif inv['type'] == BitcoinNetwork.MSG_TX:
                #print('got inv for tx {}'.format(Bitcoin.bytes_to_hexstring(inv['hash'])))
                self.known_transactions.add((inv['hash'], time.time()))

            elif inv['type'] == BitcoinNetwork.MSG_BLOCK:
                #print('got inv for block {}'.format(Bitcoin.bytes_to_hexstring(inv['hash'])))
                pass
Example #16
0
 def unserialize(data):
     txn_output = TransactionOutput()
     txn_output.amount = struct.unpack("<Q", data[:8])[0]
     script_size, data = Bitcoin.unserialize_variable_int(data[8:])
     txn_output.scriptPubKey, data = Script.unserialize(data, script_size)
     txn_output.extractAddressFromOutputScript()
     return txn_output, data
Example #17
0
    def check_for_messages(self):
        try:
            data = self.socket.recv(4096)
        except ConnectionResetError:
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return
        except socket.timeout:
            return

        if len(data) == 0:  # Have we lost connection?
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return

        # TODO - unwrap_message should return a "required" length for the message to be successful
        # and we should wait until that length becomes available before calling unwrap_message again
        current_buffer = self.data_buffer + data
        self.recv_bytes += len(data)

        while True:
            command, payload, leftover_data = Bitcoin.unwrap_message(current_buffer, Bitcoin.NETWORK_DELIVERY)
            self.data_buffer = leftover_data

            if command is None:
                break

            self.got_peer_message(command, payload)
            current_buffer = self.data_buffer
Example #18
0
    def check_for_messages(self):
        try:
            data = self.socket.recv(4096)
        except ConnectionResetError:
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return
        except socket.timeout:
            return

        if len(data) == 0:  # Have we lost connection?
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return

        # TODO - unwrap_message should return a "required" length for the message to be successful
        # and we should wait until that length becomes available before calling unwrap_message again
        current_buffer = self.data_buffer + data
        self.recv_bytes += len(data)

        while True:
            command, payload, leftover_data = Bitcoin.unwrap_message(
                current_buffer, Bitcoin.NETWORK_DELIVERY)
            self.data_buffer = leftover_data

            if command is None:
                break

            self.got_peer_message(command, payload)
            current_buffer = self.data_buffer
Example #19
0
 def get_key_as_bytes(self):
     if self.selected_key_format == 'utf8':
         text = self.textEdit_key.toPlainText()
         return str(text).encode('utf8')
     elif self.selected_key_format == 'hex':
         text = self.textEdit_key.toPlainText()
         text = text.replace(':', '').replace(' ', '').replace('\t','').replace('\n', '').replace('\r', '').replace(',','').lower()
         return Bitcoin.hexstring_to_bytes(str(text), reverse=False)
Example #20
0
    def handle_version(self, payload):
        if len(payload) < 20:
            raise BadCommand()

        try:
            self.peer_version, self.peer_services, when = struct.unpack("<LQQ", payload[:20])
            recipient_address, services, payload = Bitcoin.unserialize_network_address(payload[20:], with_timestamp=False)
            sender_address, services, payload = Bitcoin.unserialize_network_address(payload, with_timestamp=False)
            nonce = struct.unpack("<Q", payload[:8])[0]
            self.peer_user_agent, payload = Bitcoin.unserialize_string(payload[8:])
            self.peer_last_block = struct.unpack("<L", payload)[0]
        except:
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return

        #print("({}) PEER: version {} (User-agent {}, last block {})".format(self.peer_address, self.peer_version, self.peer_user_agent, self.peer_last_block))
        
        self.send_verack()
Example #21
0
    def serialize(self):
        self.createOutputScript()

        data_list = []

        data_list.append(struct.pack("<Q", self.amount))
        script_bytes = self.scriptPubKey.serialize()
        data_list.append(Bitcoin.serialize_variable_int(len(script_bytes)))
        data_list.append(script_bytes)

        return b''.join(data_list)
Example #22
0
    def serialize_size(self):
        self.createOutputScript()

        data_size = 0

        data_size += 8
        script_bytes = self.scriptPubKey.serialize_size()
        data_size += Bitcoin.serialize_variable_int_size(script_bytes)
        data_size += script_bytes

        return data_size
Example #23
0
    def serialize_size(self):
        data_size = 0
        data_size += 32
        data_size += 4

        script_bytes = self.scriptSig.serialize_size()
        data_size += Bitcoin.serialize_variable_int_size(script_bytes)
        data_size += script_bytes

        data_size += 4
        return data_size
Example #24
0
    def serialize(self):
        data_list = []
        data_list.append(self.tx_hash)
        data_list.append(struct.pack("<L", self.n))

        script_bytes = self.scriptSig.serialize()
        data_list.append(Bitcoin.serialize_variable_int(len(script_bytes)))
        data_list.append(script_bytes)

        data_list.append(struct.pack("<L", self.sequence))

        return b''.join(data_list)
Example #25
0
    def unserialize(data, as_coinbase=False):
        txn_input = TransactionInput()
        txn_input.tx_hash = data[:32]
        txn_input.n = struct.unpack("<L", data[32:36])[0]

        script_size, data = Bitcoin.unserialize_variable_int(data[36:])
        txn_input.scriptSig, data = Script.unserialize(data, script_size, as_coinbase=as_coinbase)
        txn_input.extractAddressFromInputScript()

        txn_input.sequence = struct.unpack("<L", data[:4])[0]

        return txn_input, data[4:]
Example #26
0
 def get_key_as_bytes(self):
     if self.selected_key_format == 'utf8':
         text = self.textEdit_key.toPlainText()
         return str(text).encode('utf8')
     elif self.selected_key_format == 'hex':
         text = self.textEdit_key.toPlainText()
         text = text.replace(':',
                             '').replace(' ', '').replace('\t', '').replace(
                                 '\n', '').replace('\r',
                                                   '').replace(',',
                                                               '').lower()
         return Bitcoin.hexstring_to_bytes(str(text), reverse=False)
def main():
    cb = Callbacks()

    # Handle some simple command-line arguments
    # -w Key : watch an RC4-encrypted channel
    # -p     : watch the Public unencrypted channel
    # -t tx  : try decoding and processing transaction 'tx' (hex)
    i = 1
    done = False
    while i < len(sys.argv):
        c = sys.argv[i]
        if c == '-w':
            i += 1
            cb.watch_rc4(sys.argv[i].encode('utf8'))
        elif c == '-a':
            i += 1
            cb.watch_aes128(sys.argv[i].encode('utf8'))
        elif c == '-b':
            i += 1
            cb.watch_aes256(sys.argv[i].encode('utf8'))
        elif c == '-p':
            cb.watch_public()
        elif c == '-r':
            i += 1
            private_key = load_private_key(
                open(sys.argv[i], 'rb').read().decode('ascii'))
            cb.watch_rsa(private_key)
        elif c == '-t':
            i += 1
            cb.got_transaction(
                Transaction.unserialize(
                    Bitcoin.hexstring_to_bytes(sys.argv[i], reverse=False))[0])
            done = True
        else:
            print('invalid command line argument: {}'.format(c))
            return
        i += 1

    if done:
        return

    # start network thread
    bitcoin_network = BitcoinNetwork(cb)
    bitcoin_network.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        bitcoin_network.stop()
        bitcoin_network.join()
        raise
Example #28
0
    def handle_version(self, payload):
        if len(payload) < 20:
            raise BadCommand()

        try:
            self.peer_version, self.peer_services, when = struct.unpack(
                "<LQQ", payload[:20])
            recipient_address, services, payload = Bitcoin.unserialize_network_address(
                payload[20:], with_timestamp=False)
            sender_address, services, payload = Bitcoin.unserialize_network_address(
                payload, with_timestamp=False)
            nonce = struct.unpack("<Q", payload[:8])[0]
            self.peer_user_agent, payload = Bitcoin.unserialize_string(
                payload[8:])
            self.peer_last_block = struct.unpack("<L", payload)[0]
        except:
            self.state = BitcoinNetworkPeer.STATE_DEAD
            return

        #print("({}) PEER: version {} (User-agent {}, last block {})".format(self.peer_address, self.peer_version, self.peer_user_agent, self.peer_last_block))

        self.send_verack()
Example #29
0
def push_transaction(serialized_tx):
    conn = HTTPConnection('blockchain.info')
    body = 'tx=' + ''.join(Bitcoin.bytes_to_hexstring(serialized_tx, reverse=False))
    body = body.encode('ascii')
    conn.request('POST', '/pushtx', body=body, headers={'Content-Length': len(body), 'Content-type': 'application/x-www-form-urlencoded'})
    result = conn.getresponse().read()
    if b'Submitted' in result:
        return True
    else:
        try:
            return result.decode('utf8')
        except:
            return "Unknown error"
Example #30
0
def main():
    cb = Callbacks()

    # Handle some simple command-line arguments
    # -w Key : watch an RC4-encrypted channel
    # -p     : watch the Public unencrypted channel
    # -t tx  : try decoding and processing transaction 'tx' (hex)
    i = 1
    done = False
    while i < len(sys.argv):
        c = sys.argv[i]
        if c == '-w':
            i += 1
            cb.watch_rc4(sys.argv[i].encode('utf8'))
        elif c == '-a':
            i += 1
            cb.watch_aes128(sys.argv[i].encode('utf8'))
        elif c == '-b':
            i += 1
            cb.watch_aes256(sys.argv[i].encode('utf8'))
        elif c == '-p':
            cb.watch_public()
        elif c == '-r':
            i += 1
            private_key = load_private_key(open(sys.argv[i], 'rb').read().decode('ascii'))
            cb.watch_rsa(private_key)
        elif c == '-t':
            i += 1
            cb.got_transaction(Transaction.unserialize(Bitcoin.hexstring_to_bytes(sys.argv[i], reverse=False))[0])
            done = True
        else:
            print('invalid command line argument: {}'.format(c))
            return
        i += 1

    if done:
        return

    # start network thread
    bitcoin_network = BitcoinNetwork(cb)
    bitcoin_network.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        bitcoin_network.stop()
        bitcoin_network.join()
        raise
def verify_p2pkh():
    """
    Unlocking P2PKH script
    """
    b = Bitcoin(True)
    # From the Redeemer
    sig = input("Input sig: ")
    pubkey = input("Input pubkey: ")
    b.push(sig)
    b.push(pubkey)

    # Unlocking Script
    locking_script = input("Enter locking script:")
    for word in locking_script.split():
        if word[:2] == "OP":
            res = b.interpreter(word)()
            if res == False:
                break
        else:
            print("pushing {}".format(word))
            b.push(word)
Example #32
0
    def serializeForSignature(self, hashType, selfSig):
        data_list = []
        data_list.append(self.tx_hash)
        data_list.append(struct.pack("<L", self.n))

        if selfSig:
            script_bytes = self.scriptPubKey
        else:
            script_bytes = b''

        data_list.append(Bitcoin.serialize_variable_int(len(script_bytes)))
        data_list.append(script_bytes)

        if selfSig or (hashType & ~SIGHASH_ANYONECANPAY) != SIGHASH_NONE:
            data_list.append(struct.pack("<L", self.sequence))
        else:
            data_list.append(struct.pack("<L", 0))

        return b''.join(data_list)
    def watch_and_add_key(self, name, encryption_algorithm, key):
        if encryption_algorithm == ENCRYPT_RC4:
            self.watch_rc4(key)
        elif encryption_algorithm == ENCRYPT_AES128:
            self.watch_aes128(key)
        elif encryption_algorithm == ENCRYPT_AES256:
            self.watch_aes256(key)
        elif encryption_algorithm == ENCRYPT_RSA:
            self.watch_rsa(key)

        self.keys_table.insertRow(0)

        w = gui.QTableWidgetItem('name')
        w.setToolTip('name')
        w.setFlags(core.Qt.ItemIsSelectable | core.Qt.ItemIsEnabled | core.Qt.ItemIsEditable)
        #w.setData(Qt.UserRole, 'name')
        self.keys_table.setItem(0, 0, w)

        name_map = {
            ENCRYPT_RC4   : 'RC4',
            ENCRYPT_AES128: 'AES-128',
            ENCRYPT_AES256: 'AES-256',
            ENCRYPT_RSA   : 'RSA',
        }

        w = gui.QTableWidgetItem(name_map[encryption_algorithm])
        w.setToolTip(name_map[encryption_algorithm])
        w.setFlags(core.Qt.ItemIsSelectable | core.Qt.ItemIsEnabled)
        self.keys_table.setItem(0, 1, w)

        if encryption_algorithm == ENCRYPT_RSA:
            der = key.der()
            hasher = hashlib.md5()
            hasher.update(der)
            display_key = hasher.hexdigest().upper()
        else:
            display_key = Bitcoin.bytes_to_hexstring(key, reverse=False)

        w = gui.QTableWidgetItem(display_key)
        w.setToolTip(display_key)
        w.setFlags(core.Qt.ItemIsSelectable | core.Qt.ItemIsEnabled)
        self.keys_table.setItem(0, 2, w)
Example #34
0
def push_transaction(serialized_tx):
    conn = HTTPConnection('blockchain.info')
    body = 'tx=' + ''.join(
        Bitcoin.bytes_to_hexstring(serialized_tx, reverse=False))
    body = body.encode('ascii')
    conn.request('POST',
                 '/pushtx',
                 body=body,
                 headers={
                     'Content-Length': len(body),
                     'Content-type': 'application/x-www-form-urlencoded'
                 })
    result = conn.getresponse().read()
    if b'Submitted' in result:
        return True
    else:
        try:
            return result.decode('utf8')
        except:
            return "Unknown error"
Example #35
0
    def as_dict(self):
        t = {
            "hash": ''.join(['{:02x}'.format(v) for v in self.hash()][::-1]),
            "ver": self.version,
            "vin_sz": len(self.inputs),
            "vout_sz": len(self.outputs),
            "size": self.size(),
            "in": [ {
                "prev_out": {
                    "hash": Bitcoin.bytes_to_hexstring(input.tx_hash),
                    "n"   : input.n,
                },
                "scriptSig":
                    ''.join(["{:02x}".format(b) for b in input.scriptSig.serialize()]),
                #"program": [len(x) for x in input.scriptSig.program],
            } for input in self.inputs ],
            "out": [ {
                "value": output.amount,
                "scriptPubKey": 
                    ''.join(["{:02x}".format(b) for b in output.scriptPubKey.serialize()])
            } for output in self.outputs ],
        }

        return t
Example #36
0
    def handle_addr(self, payload):
        count, payload = Bitcoin.unserialize_variable_int(payload)

        for i in range(min(count, 100)):
            addr, services, when, payload = Bitcoin.unserialize_network_address(payload, with_timestamp=self.peer_version >= 31402)
            self.bitcoin_network.discovered_peer_address('{}:{}'.format(*addr), when, services)
Example #37
0
import pytest
from time import sleep
from bitcoin import Bitcoin

b = Bitcoin()


class TestBTCFeeEstimates:
    def test_btc_fee_estimate(self):
        sleep(1)
        output = b.fee_estimates()
        print(output['data'])
        assert output['status'] == 'successful'
Example #38
0
 def send_verack(self):
     self.send(Bitcoin.wrap_message("verack", b'', Bitcoin.NETWORK_DELIVERY))
    def got_transaction(self, tx):
        # Remember that we got this transaction for a little while
        now = time.time()
        tx_hash = tx.hash()
        self.seen_transactions.add(tx_hash)
        self.seen_transactions_timeout.append((tx_hash, now))

        # Check first output to see if it's delivered to the encryption address,
        # then check second and third address to see if it's something bound for
        # us. (If it's the third one, then the 2nd address is change).  The rest
        # of the addresses are part of the payload.
        if len(tx.outputs) < 3:
            return

        delivery = tx.outputs[0]
        delivery_address = delivery.getBitcoinAddress()
        msg_start_n = 1
        if delivery_address not in self.watched_addresses:
            delivery = tx.outputs[1]
            delivery_address = delivery.getBitcoinAddress()
            if delivery_address not in self.watched_addresses:
                if len(self.rsa_private_keys) != 0:
                    r = self.check_tx_for_rsa(tx)
                    if r is None:
                        return
                    rsa_private_key, key, header, encrypted_message = r
                    encryption_algorithm = header[1]

                    # TODO - use some kind of hash/id of the private key?
                    delivery_address = rsa_private_key
                else:
                    return
            else:
                msg_start_n = 2
                encryption_algorithm, key = self.watched_addresses[
                    delivery_address]
        else:
            encryption_algorithm, key = self.watched_addresses[
                delivery_address]

        print('tx {} is for bitmsg'.format(
            Bitcoin.bytes_to_hexstring(tx_hash)))

        if encryption_algorithm in (ENCRYPT_NONE, ENCRYPT_RC4, ENCRYPT_AES128,
                                    ENCRYPT_AES256):
            # build the msg content
            header = None
            msg = []

            for k in range(msg_start_n, len(tx.outputs)):
                output = tx.outputs[k]
                if output.multisig is None or output.multisig[1] != 1:
                    return

                # Multisignature tx required here..
                assert all(120 >= len(pubkey) >= 33
                           for pubkey in output.multisig[0])
                payload = b''.join(k[1:] for k in output.multisig[0])

                if k == msg_start_n:
                    header, payload = payload[:5], payload[5:]

                    version = header[0]

                    if (header[1] & 0x7f) != encryption_algorithm:
                        # We can't decrypt this, says the header. The encryption algorithm doesn't match.
                        return

                    if header[4] != 0xff:
                        # TODO - handle reserved bits
                        return

                if k == len(tx.outputs) - 1:
                    if header[3] != 0:
                        if header[3] >= PIECE_SIZE[version]:
                            # Invalid padding
                            return
                        payload = payload[:-header[3]]

                msg.append(payload)

            if header is None:
                return

            encrypted_message = b''.join(msg)

        # Determine the IV based on the first input
        input0 = tx.inputs[0]
        if (encryption_algorithm & 0x7f) == ENCRYPT_AES128:
            iv = (int.from_bytes(input0.tx_hash, 'big') % (1 << 128)).to_bytes(
                16, 'big')
        elif (encryption_algorithm & 0x7f) in (ENCRYPT_AES256, ENCRYPT_RSA):
            iv = (int.from_bytes(input0.tx_hash, 'big') % (1 << 256)).to_bytes(
                32, 'big')
        else:
            iv = None

        if (encryption_algorithm & 0x7f) == ENCRYPT_RSA:
            decrypted_message = decrypt(key,
                                        encrypted_message,
                                        ENCRYPT_AES256,
                                        iv=iv)
        else:
            decrypted_message = decrypt(key,
                                        encrypted_message,
                                        encryption_algorithm & 0x7f,
                                        iv=iv)

        if header[1] & 0x80:
            # Message is compressed
            decrypted_message = gzip.decompress(decrypted_message)

        print('-----Begin message to {}-----'.format(delivery_address))
        try:
            sys.stdout.write(decrypted_message.decode('utf8'))
        except UnicodeDecodeError:
            sys.stdout.write(repr(decrypted_message))
        print('\n-----End message-----')
Example #40
0
    def got_transaction(self, tx):
        # Remember that we got this transaction for a little while
        now = time.time()
        tx_hash = tx.hash()
        self.seen_transactions.add(tx_hash)
        self.seen_transactions_timeout.append((tx_hash, now))

        # Check first output to see if it's delivered to the encryption address,
        # then check second and third address to see if it's something bound for
        # us. (If it's the third one, then the 2nd address is change).  The rest
        # of the addresses are part of the payload.
        if len(tx.outputs) < 3:
            return

        delivery = tx.outputs[0]
        delivery_address = delivery.getBitcoinAddress()
        msg_start_n = 1
        if delivery_address not in self.watched_addresses:
            delivery = tx.outputs[1]
            delivery_address = delivery.getBitcoinAddress()
            if delivery_address not in self.watched_addresses:
                if len(self.rsa_private_keys) != 0:
                    r = self.check_tx_for_rsa(tx)
                    if r is None:
                        return
                    rsa_private_key, key, header, encrypted_message = r
                    encryption_algorithm = header[1]

                    # TODO - use some kind of hash/id of the private key?
                    delivery_address = rsa_private_key
                else:
                    return
            else:
                msg_start_n = 2
                encryption_algorithm, key = self.watched_addresses[delivery_address]
        else:
            encryption_algorithm, key = self.watched_addresses[delivery_address]

        print('tx {} is for bitmsg'.format(Bitcoin.bytes_to_hexstring(tx_hash)))

        if encryption_algorithm in (ENCRYPT_NONE, ENCRYPT_RC4, ENCRYPT_AES128, ENCRYPT_AES256):
            # build the msg content
            header = None
            msg = []

            for k in range(msg_start_n, len(tx.outputs)):
                output = tx.outputs[k]
                if output.multisig is None or output.multisig[1] != 1:
                    return

                # Multisignature tx required here..
                assert all(120 >= len(pubkey) >= 33 for pubkey in output.multisig[0])
                payload = b''.join(k[1:] for k in output.multisig[0])

                if k == msg_start_n:
                    header, payload = payload[:5], payload[5:]

                    version = header[0]

                    if (header[1] & 0x7f) != encryption_algorithm:
                        # We can't decrypt this, says the header. The encryption algorithm doesn't match.
                        return

                    if header[4] != 0xff:
                        # TODO - handle reserved bits
                        return
                            
                if k == len(tx.outputs) - 1:
                    if header[3] != 0:
                        if header[3] >= PIECE_SIZE[version]:
                            # Invalid padding
                            return
                        payload = payload[:-header[3]]

                msg.append(payload)

            if header is None:
                return

            encrypted_message = b''.join(msg)

        # Determine the IV based on the first input
        input0 = tx.inputs[0]
        if (encryption_algorithm & 0x7f) == ENCRYPT_AES128:
            iv = (int.from_bytes(input0.tx_hash, 'big') % (1 << 128)).to_bytes(16, 'big')
        elif (encryption_algorithm & 0x7f) in (ENCRYPT_AES256, ENCRYPT_RSA):
            iv = (int.from_bytes(input0.tx_hash, 'big') % (1 << 256)).to_bytes(32, 'big')
        else:
            iv = None

        if (encryption_algorithm & 0x7f) == ENCRYPT_RSA:
            decrypted_message = decrypt(key, encrypted_message, ENCRYPT_AES256, iv=iv)
        else:
            decrypted_message = decrypt(key, encrypted_message, encryption_algorithm & 0x7f, iv=iv)

        if header[1] & 0x80:
            # Message is compressed
            decrypted_message = gzip.decompress(decrypted_message)

        print('-----Begin message to {}-----'.format(delivery_address))
        try:
            sys.stdout.write(decrypted_message.decode('utf8'))
        except UnicodeDecodeError:
            sys.stdout.write(repr(decrypted_message))
        print('\n-----End message-----')
Example #41
0
def main():
    message = None

    # Parse arguments first
    i = 1
    while i < len(sys.argv):
        v = sys.argv[1]
        if v == '-f':
            assert message is None, "you can only specify -f once"
            i += 1
            data = open(sys.argv[i], 'rb').read()
            mime_type, encoding = mimetypes.guess_type(sys.argv[i])

            if mime_type is not None:
                filename = os.path.basename(sys.argv[i])
                message = '\n'.join([
                    'Content-type: {}{}'.format(
                        mime_type, '; charset={}'.format(encoding)
                        if encoding is not None else ''),
                    'Content-length: {}'.format(len(data)),
                    'Content-disposition: attachment; filename={}'.format(
                        filename),
                ])
                message = message.encode('utf8') + b'\n\n' + data

        i += 1

    # Get coins for input
    print(
        '*** Step 1. We need Bitcoins in order to send a message. Give me a Bitcoin private key (it starts with a 5...) to use as an input for the message transaction.'
    )
    bitcoin_private_key = input('...Enter Bitcoin private key: ')

    # Decode private key, show bitcoin address associated with key
    private_key = base58.decode_to_bytes(bitcoin_private_key)[-36:-4]
    public_key = addressgen.get_public_key(private_key)
    bitcoin_input_address = addressgen.generate_address(public_key, version=0)
    print('...The Bitcoin address associated with that private key is: {}'.
          format(bitcoin_input_address))

    # Lookup the unspent outputs associated with the given input...
    print('...Looking up unspent outputs on blockchain.info...')
    unspent_outputs = filter_unspent_outputs(
        lookup_unspent_outputs([bitcoin_input_address])[bitcoin_input_address])

    # Show the inputs to the user, and ask him which he'd like to use as input.
    print('\n*** Step 2. You need to select an input:')
    for k, u in enumerate(unspent_outputs):
        print('...{}: txid={} n={} value={} confirmations={}'.format(
            k + 1, u['tx_hash'], u['tx_output_n'],
            Bitcoin.format_money(u['value']), u['confirmations']))
    selected_inputs = [
        int(x.strip()) - 1
        for x in input('Enter inputs (if more than one, separated by commas): '
                       ).split(',') if len(x) > 0
    ]
    if not all(x >= 0 and x < len(unspent_outputs) for x in selected_inputs):
        raise Exception("Invalid input provided")
    total_input_amount = sum(unspent_outputs[k]['value']
                             for k in selected_inputs)
    print('...{} BTC selected from {} input{}'.format(
        Bitcoin.format_money(total_input_amount), len(selected_inputs),
        's' if len(selected_inputs) != 1 else ''))

    # Ask the user for the change address, defaulting to the same input address
    print(
        '\n*** Step 3. Provide a change address (this will not be used if a change address isn\'t necessary)'
    )
    bitcoin_change_address = input(
        '...Enter change address (leave blank to use the input address as the change address): '
    ).strip()
    if len(bitcoin_change_address) == 0:
        bitcoin_change_address = bitcoin_input_address
        print('...Change address: {}'.format(bitcoin_change_address))

    # Select an encryption method
    while True:
        print('\n*** Step 4a. Select an encryption method:')
        print('...1. None (public message)')
        print('...2. RC4')
        print('...3. AES-128')
        print('...4. AES-256 (best)')
        print('...5. RSA (public-key)')
        try:
            i = int(input('? '))
            if i == 1:
                encryption_key = b'\x00'
                encryption_algorithm = ENCRYPT_NONE
            elif i == 2:
                required_key_length_message = "RC4 allows for variable-length keys, but longer is better"
                encryption_algorithm = ENCRYPT_RC4
            elif i == 3:
                required_key_length_message = "AES-128 requires a key length of 16 bytes"
                encryption_algorithm = ENCRYPT_AES128
            elif i == 4:
                required_key_length_message = "AES-256 requires a key length of 32 bytes"
                encryption_algorithm = ENCRYPT_AES256
            elif i == 5:
                required_key_length_message = "An RSA public-key is required"
                encryption_algorithm = ENCRYPT_RSA
            else:
                continue
            break
        except ValueError:
            continue

    if encryption_algorithm in (ENCRYPT_AES128, ENCRYPT_AES256, ENCRYPT_RC4):
        print(
            '\n*** Step 4b. Provide an encryption key. The encryption key will be hashed and used as a Bitcoin address to designate the recipient.'
        )
        encryption_key = input('...Enter an encryption key ({}): '.format(
            required_key_length_message)).encode('utf8')
        if encryption_algorithm == ENCRYPT_AES128 and len(
                encryption_key) != 16:
            print('...ERROR: key must have a length of 16 bytes.')
            return
        elif encryption_algorithm == ENCRYPT_AES256 and len(
                encryption_key) != 32:
            print('...ERROR: key must have a length of 32 bytes.')
            return
        elif encryption_algorithm == ENCRYPT_RC4 and len(encryption_key) == 0:
            print('...ERROR: key must not be empty')
            return
    elif encryption_algorithm == ENCRYPT_RSA:
        encryption_key = get_random_bytes(32)
        encrypted_rsa_encryption_keys = []
        while True:
            print(
                '\n*** Step 4b. Provide the public-key for a recipient (in PEM form):\n'
            )
            lines = []
            while True:
                line = input('')
                lines.append(line)
                if '-----END PUBLIC KEY-----' in line:
                    break
            public_key = load_public_key('\n'.join(lines))

            # encrypt encryption key
            encrypted_rsa_encryption_key = encrypt(public_key,
                                                   encryption_key,
                                                   algorithm=ENCRYPT_RSA)
            encrypted_rsa_encryption_keys.append(
                struct.pack('<H', len(encrypted_rsa_encryption_key)) +
                encrypted_rsa_encryption_key)

            answer = False
            while True:
                answer = input(
                    '...would you like to add another recipient? (y/N) '
                ).strip().lower()
                if answer in ('y', 'yes'):
                    answer = True
                    break
                if answer in ('n', 'no', ''):
                    answer = False
                    break

            if not answer:
                break

    if encryption_algorithm == ENCRYPT_RSA:
        bitcoin_delivery_addresses = []
    else:
        bitcoin_delivery_addresses = [
            addressgen.generate_address_from_data(encryption_key, version=0)
        ]

    print('...Message delivery to:')
    for bitcoin_delivery_address in bitcoin_delivery_addresses:
        print('... {}'.format(bitcoin_delivery_address))

    # Now we ask the user to enter a message
    if message is None:
        print(
            '\n*** Step 5. Enter your message. End your message by entering \'.\' by itself on a new line.\n...Enter your message:\n'
        )
        lines = []
        while True:
            line = input()
            if line == '.':
                break
            lines.append(line)
        message = '\n'.join(lines).encode('utf8')

        # Add some temporary headers
        message = 'Content-type: text/plain; charset=utf-8\nContent-length: {}\n\n'.format(
            len(message)).encode('ascii') + message

    # Try compressing the message before encryption, this may waste CPU but it's in the interest
    # of saving some outputs in the transaction.
    print('...Trying to compress the message...', end='')
    compressed_message = gzip.compress(message)
    if len(compressed_message) < len(message):
        print('good!')
        message = compressed_message
        encryption_algorithm |= 0x80
    else:
        print('not using because compressed version is larger.')

    # Setup the initialization vector using the first input's transaction id
    if (encryption_algorithm & 0x7f) == ENCRYPT_AES128:
        first_input = unspent_outputs[selected_inputs[0]]
        first_input_tx_hash = Bitcoin.hexstring_to_bytes(
            first_input['tx_hash'], reverse=False)
        iv = int.from_bytes(first_input_tx_hash, 'big')
        iv = (iv % (1 << 128)).to_bytes(16, 'big')
    elif (encryption_algorithm & 0x7f) in (ENCRYPT_AES256, ENCRYPT_RSA):
        # Bitcoin tx hashes are 32-bytes, so this is still OK for AES-256
        first_input = unspent_outputs[selected_inputs[0]]
        first_input_tx_hash = Bitcoin.hexstring_to_bytes(
            first_input['tx_hash'], reverse=False)
        iv = int.from_bytes(first_input_tx_hash, 'big')
        iv = (iv % (1 << 256)).to_bytes(32, 'big')
    else:
        iv = None

    # Encrypt the message
    if (encryption_algorithm & 0x7f) == ENCRYPT_RSA:
        encrypted_message = encrypt(encryption_key,
                                    message,
                                    algorithm=ENCRYPT_AES256,
                                    iv=iv)
    else:
        encrypted_message = encrypt(encryption_key,
                                    message,
                                    algorithm=(encryption_algorithm & 0x7f),
                                    iv=iv)

    # With RSA, the encrypted keys are compressed separately
    if (encryption_algorithm & 0x7f) == ENCRYPT_RSA:
        print('...Trying to compress the key block...', end='')
        encrypted_key_block = b''.join(encrypted_rsa_encryption_keys)
        compressed_encrypted_key_block = gzip.compress(encrypted_key_block)
        if len(compressed_encrypted_key_block) < len(encrypted_key_block):
            print('good!')
            encrypted_key_block = b'\x80' + struct.pack(
                '<L', len(compressed_encrypted_key_block)
            ) + compressed_encrypted_key_block
        else:
            print('not using because compressed version is larger.')
            encrypted_key_block = b'\x00' + struct.pack(
                '<L', len(encrypted_key_block)) + encrypted_key_block

        print('...Encrypted Key Block is {} bytes'.format(
            len(encrypted_key_block)))
        encrypted_message = encrypted_key_block + encrypted_message

    print('...Encrypted message is {} bytes'.format(len(encrypted_message)))

    # Build the message header. Prefix the encrypted message with the header.
    checksum = sum(encrypted_message) % 256
    reserved = 0xff
    padding = 0
    header = bytes(
        [VERSION, encryption_algorithm, checksum, padding, reserved])
    encrypted_message = header + encrypted_message

    # Split the encrypted message into pubkeys. We get 119 bytes per pubkey (we reserve
    # the first byte in case any future changes to bitcoind require a valid pubkey)
    bitcoin_message_pieces = []
    for i in range(0, len(encrypted_message), PIECE_SIZE[VERSION]):
        piece = encrypted_message[i:i + PIECE_SIZE[VERSION]]
        bitcoin_message_pieces.append(b'\xff' + piece)

    if len(bitcoin_message_pieces) == 1 and len(
            bitcoin_message_pieces[0]) < 33:
        # This is the only case where we need padding in version 2 messages.
        padding = 33 - len(bitcoin_message_pieces[0])
        bitcoin_message_pieces[0] = bitcoin_message_pieces[0] + bytes(
            [padding] * padding)

        # We have to adjust the header 'padding' value
        bitcoin_message_pieces[0] = bitcoin_message_pieces[0][:4] + bytes(
            [padding]) + bitcoin_message_pieces[0][5:]
    elif len(bitcoin_message_pieces) > 1 and len(
            bitcoin_message_pieces[-1]) < 33:
        # We shift however many bytes out of the 2nd to last block and into the last one
        # to make sure it's at least 33 bytes long
        req = 33 - len(bitcoin_message_pieces[-1])
        bitcoin_message_pieces[-1] = bytes([
            bitcoin_message_pieces[-1][0]
        ]) + bitcoin_message_pieces[-2][-req:] + bitcoin_message_pieces[-1][1:]
        bitcoin_message_pieces[-2] = bitcoin_message_pieces[-2][:-req]
        assert 120 >= len(bitcoin_message_pieces[-2]) >= 33 and 120 >= len(
            bitcoin_message_pieces[-1]) >= 33

    # start building the transaction
    tx = Transaction()

    # setup the inputs
    for n, k in enumerate(selected_inputs):
        unspent = unspent_outputs[k]
        tx_input = TransactionInput(
            tx_hash=Bitcoin.hexstring_to_bytes(unspent['tx_hash'],
                                               reverse=False),
            tx_output_n=unspent['tx_output_n'],
            scriptPubKey=Bitcoin.hexstring_to_bytes(unspent['script'],
                                                    reverse=False),
            amount=unspent['value'],
            signing_key=private_key)
        print('...input {} is {} BTC from {}'.format(
            n, Bitcoin.format_money(unspent['value']), bitcoin_input_address))
        tx.addInput(tx_input)

    # setup the outputs. a trigger address isn't really needed, since the encryption
    # key can actually be used as the trigger (only those interested will be able to find
    # the message, anyway)

    # cost of the transaction is (targets + pieces/3 + sacrifice) * SPECIAL_SATOSHI
    # peices/3 because we include 3 pieces per output
    outputs_count = (len(bitcoin_delivery_addresses) +
                     math.ceil(len(bitcoin_message_pieces) / 3))
    approx_tx_cost = MINIMUM_SACRIFICE + outputs_count * SPECIAL_SATOSHI
    if approx_tx_cost > total_input_amount:
        raise Exception("not enough inputs provided")

    tx_change_output = None
    tx_change_output_n = None
    if total_input_amount > approx_tx_cost:
        print('...output (change) to {}'.format(bitcoin_change_address))
        tx_change_output = TransactionOutput(bitcoin_change_address,
                                             amount=total_input_amount -
                                             approx_tx_cost)
        tx_change_output_n = tx.addOutput(tx_change_output)

    # The recipient will know how to handle this if they see their key...
    for i, bitcoin_delivery_address in enumerate(bitcoin_delivery_addresses):
        print('...output (target) to {}'.format(bitcoin_delivery_address))
        tx_output = TransactionOutput(bitcoin_delivery_address,
                                      amount=SPECIAL_SATOSHI)
        tx.addOutput(tx_output)

    for i in range(0, len(bitcoin_message_pieces), 3):
        pieces = bitcoin_message_pieces[i:i + 3]

        d = b''.join([p[1:] for p in pieces])
        header = None
        if i == 0:
            header = d[:5]
            d = d[5:]
        if (i + 3) >= len(bitcoin_message_pieces):
            if padding > 0:
                d = d[:-padding]

        print('...output (message) to multisig 1-of-{}{}'.format(
            len(pieces),
            ' (header={})'.format(header) if header is not None else ''))
        tx_output = TransactionOutput(amount=SPECIAL_SATOSHI)
        tx_output.setMultisig(pieces, 1)
        tx.addOutput(tx_output)

    # we should now be able to figure out how much in fees is required now that the tx is built
    recommended_fee = max(
        MINIMUM_SACRIFICE * SPECIAL_SATOSHI,
        tx.getRecommendedTransactionFee(per_kb=SACRIFICE_PER_KB))
    recommended_tx_cost = recommended_fee + outputs_count * SPECIAL_SATOSHI
    if recommended_tx_cost > total_input_amount:
        raise Exception("not enough inputs provided ({} BTC required)".format(
            Bitcoin.format_money(recommended_tx_cost)))

    if tx_change_output is not None and recommended_tx_cost == total_input_amount:
        # We can remove the output
        tx.removeOutput(tx_change_output_n)
        tx_change_output = None

    if recommended_tx_cost < total_input_amount:
        if tx_change_output is None:
            print('...output (change) to {}'.format(bitcoin_change_address))
            tx_change_output = TransactionOutput(bitcoin_change_address)
            tx_change_output_n = tx.addOutput(tx_change_output)
        tx_change_output.amount = total_input_amount - recommended_tx_cost

    print('...the fee for this transaction is {} BTC'.format(
        Bitcoin.format_money(tx.totalInput() - tx.totalOutput())))
    print('...the total sent is {} BTC (change = {})'.format(
        Bitcoin.format_money(tx.totalInput()),
        Bitcoin.format_money(0 if tx_change_output is None else tx.
                             outputs[tx_change_output_n].amount)))

    # sign all inputs
    tx.sign()

    print('...the transaction is {} bytes.'.format(tx.size()))

    # Finally, do something with the transaction
    print(
        '\n*** Step 6. The transaction is built. What would you like to do with it?'
    )
    while True:
        print('...1. Show JSON')
        print('...2. Show HEX')
        print('...3. Push (via blockchain.info/pushtx)')
        print('...4. Quit')
        try:
            command = int(input('? ')) - 1
            assert command >= 0 and command < 4

            if command == 0:
                print(json.dumps(tx.as_dict()))
            elif command == 1:
                print(Bitcoin.bytes_to_hexstring(tx.serialize(),
                                                 reverse=False))
            elif command == 2:
                err = push_transaction(tx.serialize())
                if isinstance(err, bool):
                    print("...pushed {}".format(
                        Bitcoin.bytes_to_hexstring(tx.hash())))
                else:
                    print("...error pushing:", err)
            elif command == 3:
                break
        except EOFError:
            break
        except:
            print('Try again.')
            pass

    print("...exiting")
Example #42
0
def main():
    message = None

    # Parse arguments first
    i = 1
    while i < len(sys.argv):
        v = sys.argv[1]
        if v == '-f':
            assert message is None, "you can only specify -f once"
            i += 1
            data = open(sys.argv[i], 'rb').read()
            mime_type, encoding = mimetypes.guess_type(sys.argv[i])

            if mime_type is not None:
                filename = os.path.basename(sys.argv[i])
                message = '\n'.join([
                    'Content-type: {}{}'.format(mime_type, '; charset={}'.format(encoding) if encoding is not None else ''),
                    'Content-length: {}'.format(len(data)),
                    'Content-disposition: attachment; filename={}'.format(filename),
                ])
                message = message.encode('utf8') + b'\n\n' + data

        i += 1

    # Get coins for input
    print('*** Step 1. We need Bitcoins in order to send a message. Give me a Bitcoin private key (it starts with a 5...) to use as an input for the message transaction.')
    bitcoin_private_key = input('...Enter Bitcoin private key: ')

    # Decode private key, show bitcoin address associated with key
    private_key = base58.decode_to_bytes(bitcoin_private_key)[-36:-4]
    public_key = addressgen.get_public_key(private_key)
    bitcoin_input_address = addressgen.generate_address(public_key, version=0)
    print('...The Bitcoin address associated with that private key is: {}'.format(bitcoin_input_address))

    # Lookup the unspent outputs associated with the given input...
    print('...Looking up unspent outputs on blockchain.info...')
    unspent_outputs = filter_unspent_outputs(lookup_unspent_outputs([bitcoin_input_address])[bitcoin_input_address])

    # Show the inputs to the user, and ask him which he'd like to use as input.
    print('\n*** Step 2. You need to select an input:')
    for k, u in enumerate(unspent_outputs):
        print('...{}: txid={} n={} value={} confirmations={}'.format(k+1, u['tx_hash'], u['tx_output_n'], Bitcoin.format_money(u['value']), u['confirmations']))
    selected_inputs = [int(x.strip())-1 for x in input('Enter inputs (if more than one, separated by commas): ').split(',') if len(x) > 0]
    if not all(x >= 0 and x < len(unspent_outputs) for x in selected_inputs):
        raise Exception("Invalid input provided")
    total_input_amount = sum(unspent_outputs[k]['value'] for k in selected_inputs)
    print('...{} BTC selected from {} input{}'.format(Bitcoin.format_money(total_input_amount), len(selected_inputs), 's' if len(selected_inputs) != 1 else ''))

    # Ask the user for the change address, defaulting to the same input address
    print('\n*** Step 3. Provide a change address (this will not be used if a change address isn\'t necessary)')
    bitcoin_change_address = input('...Enter change address (leave blank to use the input address as the change address): ').strip()
    if len(bitcoin_change_address) == 0:
        bitcoin_change_address = bitcoin_input_address
        print('...Change address: {}'.format(bitcoin_change_address))

    # Select an encryption method
    while True:
        print('\n*** Step 4a. Select an encryption method:')
        print('...1. None (public message)')
        print('...2. RC4')
        print('...3. AES-128')
        print('...4. AES-256 (best)')
        print('...5. RSA (public-key)')
        try:
            i = int(input('? '))
            if i == 1:
                encryption_key = b'\x00'
                encryption_algorithm = ENCRYPT_NONE
            elif i == 2:
                required_key_length_message = "RC4 allows for variable-length keys, but longer is better"
                encryption_algorithm = ENCRYPT_RC4
            elif i == 3:
                required_key_length_message = "AES-128 requires a key length of 16 bytes"
                encryption_algorithm = ENCRYPT_AES128
            elif i == 4:
                required_key_length_message = "AES-256 requires a key length of 32 bytes"
                encryption_algorithm = ENCRYPT_AES256
            elif i == 5:
                required_key_length_message = "An RSA public-key is required"
                encryption_algorithm = ENCRYPT_RSA
            else:
                continue
            break
        except ValueError:
            continue

    if encryption_algorithm in (ENCRYPT_AES128, ENCRYPT_AES256, ENCRYPT_RC4):
        print('\n*** Step 4b. Provide an encryption key. The encryption key will be hashed and used as a Bitcoin address to designate the recipient.')
        encryption_key = input('...Enter an encryption key ({}): '.format(required_key_length_message)).encode('utf8')
        if encryption_algorithm == ENCRYPT_AES128 and len(encryption_key) != 16:
            print('...ERROR: key must have a length of 16 bytes.')
            return
        elif encryption_algorithm == ENCRYPT_AES256 and len(encryption_key) != 32:
            print('...ERROR: key must have a length of 32 bytes.')
            return
        elif encryption_algorithm == ENCRYPT_RC4 and len(encryption_key) == 0:
            print('...ERROR: key must not be empty')
            return
    elif encryption_algorithm == ENCRYPT_RSA:
        encryption_key = get_random_bytes(32)
        encrypted_rsa_encryption_keys = []
        while True:
            print('\n*** Step 4b. Provide the public-key for a recipient (in PEM form):\n')
            lines = []
            while True:
                line = input('')
                lines.append(line)
                if '-----END PUBLIC KEY-----' in line:
                    break
            public_key = load_public_key('\n'.join(lines))

            # encrypt encryption key
            encrypted_rsa_encryption_key = encrypt(public_key, encryption_key, algorithm=ENCRYPT_RSA)
            encrypted_rsa_encryption_keys.append(struct.pack('<H', len(encrypted_rsa_encryption_key)) + encrypted_rsa_encryption_key)

            answer = False
            while True:
                answer = input('...would you like to add another recipient? (y/N) ').strip().lower()
                if answer in ('y', 'yes'):
                    answer = True
                    break
                if answer in ('n', 'no', ''):
                    answer = False
                    break

            if not answer:
                break

    if encryption_algorithm == ENCRYPT_RSA:
        bitcoin_delivery_addresses = []
    else:
        bitcoin_delivery_addresses = [addressgen.generate_address_from_data(encryption_key, version=0)]

    print('...Message delivery to:')
    for bitcoin_delivery_address in bitcoin_delivery_addresses:
        print('... {}'.format(bitcoin_delivery_address))

    # Now we ask the user to enter a message
    if message is None:
        print('\n*** Step 5. Enter your message. End your message by entering \'.\' by itself on a new line.\n...Enter your message:\n')
        lines = []
        while True:
            line = input()
            if line == '.':
                break
            lines.append(line)
        message = '\n'.join(lines).encode('utf8')

        # Add some temporary headers
        message = 'Content-type: text/plain; charset=utf-8\nContent-length: {}\n\n'.format(len(message)).encode('ascii') + message

    # Try compressing the message before encryption, this may waste CPU but it's in the interest
    # of saving some outputs in the transaction.
    print('...Trying to compress the message...', end='')
    compressed_message = gzip.compress(message)
    if len(compressed_message) < len(message):
        print('good!')
        message = compressed_message
        encryption_algorithm |= 0x80
    else:
        print('not using because compressed version is larger.')

    # Setup the initialization vector using the first input's transaction id
    if (encryption_algorithm & 0x7f) == ENCRYPT_AES128:
        first_input = unspent_outputs[selected_inputs[0]]
        first_input_tx_hash = Bitcoin.hexstring_to_bytes(first_input['tx_hash'], reverse=False)
        iv = int.from_bytes(first_input_tx_hash, 'big')
        iv = (iv % (1 << 128)).to_bytes(16, 'big')
    elif (encryption_algorithm & 0x7f) in (ENCRYPT_AES256, ENCRYPT_RSA):
        # Bitcoin tx hashes are 32-bytes, so this is still OK for AES-256
        first_input = unspent_outputs[selected_inputs[0]]
        first_input_tx_hash = Bitcoin.hexstring_to_bytes(first_input['tx_hash'], reverse=False)
        iv = int.from_bytes(first_input_tx_hash, 'big')
        iv = (iv % (1 << 256)).to_bytes(32, 'big')
    else:
        iv = None

    # Encrypt the message
    if (encryption_algorithm & 0x7f) == ENCRYPT_RSA:
        encrypted_message = encrypt(encryption_key, message, algorithm=ENCRYPT_AES256, iv=iv)
    else:
        encrypted_message = encrypt(encryption_key, message, algorithm=(encryption_algorithm & 0x7f), iv=iv)

    # With RSA, the encrypted keys are compressed separately
    if (encryption_algorithm& 0x7f)  == ENCRYPT_RSA:
        print('...Trying to compress the key block...', end='')
        encrypted_key_block = b''.join(encrypted_rsa_encryption_keys)
        compressed_encrypted_key_block = gzip.compress(encrypted_key_block)
        if len(compressed_encrypted_key_block) < len(encrypted_key_block):
            print('good!')
            encrypted_key_block = b'\x80' + struct.pack('<L', len(compressed_encrypted_key_block)) + compressed_encrypted_key_block
        else:
            print('not using because compressed version is larger.')
            encrypted_key_block = b'\x00' + struct.pack('<L', len(encrypted_key_block)) + encrypted_key_block

        print('...Encrypted Key Block is {} bytes'.format(len(encrypted_key_block)))
        encrypted_message = encrypted_key_block + encrypted_message

    print('...Encrypted message is {} bytes'.format(len(encrypted_message)))

    # Build the message header. Prefix the encrypted message with the header.
    checksum             = sum(encrypted_message) % 256
    reserved             = 0xff
    padding              = 0
    header               = bytes([VERSION, encryption_algorithm, checksum, padding, reserved])
    encrypted_message    = header + encrypted_message

    # Split the encrypted message into pubkeys. We get 119 bytes per pubkey (we reserve
    # the first byte in case any future changes to bitcoind require a valid pubkey)
    bitcoin_message_pieces = []
    for i in range(0, len(encrypted_message), PIECE_SIZE[VERSION]):
        piece = encrypted_message[i:i+PIECE_SIZE[VERSION]]
        bitcoin_message_pieces.append(b'\xff' + piece)

    if len(bitcoin_message_pieces) == 1 and len(bitcoin_message_pieces[0]) < 33:
        # This is the only case where we need padding in version 2 messages.
        padding = 33 - len(bitcoin_message_pieces[0])
        bitcoin_message_pieces[0] = bitcoin_message_pieces[0] + bytes([padding] * padding)

        # We have to adjust the header 'padding' value
        bitcoin_message_pieces[0] = bitcoin_message_pieces[0][:4] + bytes([padding]) + bitcoin_message_pieces[0][5:]
    elif len(bitcoin_message_pieces) > 1 and len(bitcoin_message_pieces[-1]) < 33:
        # We shift however many bytes out of the 2nd to last block and into the last one
        # to make sure it's at least 33 bytes long
        req = 33 - len(bitcoin_message_pieces[-1])
        bitcoin_message_pieces[-1] = bytes([bitcoin_message_pieces[-1][0]]) + bitcoin_message_pieces[-2][-req:] + bitcoin_message_pieces[-1][1:]
        bitcoin_message_pieces[-2] = bitcoin_message_pieces[-2][:-req]
        assert 120 >= len(bitcoin_message_pieces[-2]) >= 33 and 120 >= len(bitcoin_message_pieces[-1]) >= 33

    # start building the transaction
    tx = Transaction()

    # setup the inputs
    for n, k in enumerate(selected_inputs):
        unspent = unspent_outputs[k]
        tx_input = TransactionInput(tx_hash=Bitcoin.hexstring_to_bytes(unspent['tx_hash'], reverse=False), 
                                    tx_output_n=unspent['tx_output_n'],
                                    scriptPubKey=Bitcoin.hexstring_to_bytes(unspent['script'], reverse=False),
                                    amount=unspent['value'],
                                    signing_key=private_key)
        print('...input {} is {} BTC from {}'.format(n, Bitcoin.format_money(unspent['value']), bitcoin_input_address))
        tx.addInput(tx_input)

    # setup the outputs. a trigger address isn't really needed, since the encryption
    # key can actually be used as the trigger (only those interested will be able to find
    # the message, anyway)

    # cost of the transaction is (targets + pieces/3 + sacrifice) * SPECIAL_SATOSHI
    # peices/3 because we include 3 pieces per output
    outputs_count = (len(bitcoin_delivery_addresses) + math.ceil(len(bitcoin_message_pieces) / 3))
    approx_tx_cost = MINIMUM_SACRIFICE + outputs_count * SPECIAL_SATOSHI
    if approx_tx_cost > total_input_amount:
        raise Exception("not enough inputs provided")

    tx_change_output = None
    tx_change_output_n = None
    if total_input_amount > approx_tx_cost:
        print('...output (change) to {}'.format(bitcoin_change_address))
        tx_change_output = TransactionOutput(bitcoin_change_address, amount=total_input_amount - approx_tx_cost)
        tx_change_output_n = tx.addOutput(tx_change_output)

    # The recipient will know how to handle this if they see their key...
    for i, bitcoin_delivery_address in enumerate(bitcoin_delivery_addresses):
        print('...output (target) to {}'.format(bitcoin_delivery_address))
        tx_output = TransactionOutput(bitcoin_delivery_address, amount=SPECIAL_SATOSHI)
        tx.addOutput(tx_output)

    for i in range(0, len(bitcoin_message_pieces), 3):
        pieces = bitcoin_message_pieces[i:i+3]

        d = b''.join([p[1:] for p in pieces])
        header = None
        if i == 0:
            header = d[:5]
            d = d[5:]
        if (i + 3) >= len(bitcoin_message_pieces):
            if padding > 0:
                d = d[:-padding]

        print('...output (message) to multisig 1-of-{}{}'.format(len(pieces), ' (header={})'.format(header) if header is not None else ''))
        tx_output = TransactionOutput(amount=SPECIAL_SATOSHI)
        tx_output.setMultisig(pieces, 1)
        tx.addOutput(tx_output)

    # we should now be able to figure out how much in fees is required now that the tx is built
    recommended_fee = max(MINIMUM_SACRIFICE * SPECIAL_SATOSHI, tx.getRecommendedTransactionFee(per_kb=SACRIFICE_PER_KB))
    recommended_tx_cost = recommended_fee + outputs_count * SPECIAL_SATOSHI
    if recommended_tx_cost > total_input_amount:
        raise Exception("not enough inputs provided ({} BTC required)".format(Bitcoin.format_money(recommended_tx_cost)))
    
    if tx_change_output is not None and recommended_tx_cost == total_input_amount:
        # We can remove the output
        tx.removeOutput(tx_change_output_n)
        tx_change_output = None
        
    if recommended_tx_cost < total_input_amount:
        if tx_change_output is None:
            print('...output (change) to {}'.format(bitcoin_change_address))
            tx_change_output = TransactionOutput(bitcoin_change_address)
            tx_change_output_n = tx.addOutput(tx_change_output)
        tx_change_output.amount = total_input_amount - recommended_tx_cost

    print('...the fee for this transaction is {} BTC'.format(Bitcoin.format_money(tx.totalInput() - tx.totalOutput())))
    print('...the total sent is {} BTC (change = {})'.format(Bitcoin.format_money(tx.totalInput()), Bitcoin.format_money(0 if tx_change_output is None else tx.outputs[tx_change_output_n].amount)))
    
    # sign all inputs
    tx.sign()

    print('...the transaction is {} bytes.'.format(tx.size()))

    # Finally, do something with the transaction
    print('\n*** Step 6. The transaction is built. What would you like to do with it?')
    while True:
        print('...1. Show JSON')
        print('...2. Show HEX')
        print('...3. Push (via blockchain.info/pushtx)')
        print('...4. Quit')
        try:
            command = int(input('? ')) - 1
            assert command >= 0 and command < 4

            if command == 0:
                print(json.dumps(tx.as_dict()))
            elif command == 1:
                print(Bitcoin.bytes_to_hexstring(tx.serialize(), reverse=False))
            elif command == 2:
                err = push_transaction(tx.serialize())
                if isinstance(err, bool):
                    print("...pushed {}".format(Bitcoin.bytes_to_hexstring(tx.hash())))
                else:
                    print("...error pushing:", err)
            elif command == 3:
                break
        except EOFError:
            break
        except:
            print('Try again.')
            pass

    print("...exiting")
Example #43
0
 def hash(self):
     return Bitcoin.hash(self.serialize())
Example #44
0
 def send_verack(self):
     self.send(Bitcoin.wrap_message("verack", b'',
                                    Bitcoin.NETWORK_DELIVERY))