diff --git a/electrum/electrumabc/bitcoin.py b/electrum/electrumabc/bitcoin.py --- a/electrum/electrumabc/bitcoin.py +++ b/electrum/electrumabc/bitcoin.py @@ -353,25 +353,26 @@ return bytes.fromhex(s)[::-1].hex() -def int_to_hex(i: int, length: int = 1) -> str: +def int_to_le_hex(i: int, length: int = 1) -> str: """Return a little-endian hexadecimal representation of an integer. :: - >>> int_to_hex(8, 1) + >>> int_to_le_hex(8, 1) '08' - >>> int_to_hex(8, 2) + >>> int_to_le_hex(8, 2) '0800' - >>> int_to_hex(32001, 3) + >>> int_to_le_hex(32001, 3) '017d00' + >>> int_to_le_hex(0xdeadbeefc0ffee11, 8) + '11eeffc0efbeadde' :param i: Integer to be represented. :param length: Length in bytes of the hexadecimal number to be represented. Each byte is represented as two characters. """ - s = hex(i)[2:].rstrip("L") - s = "0" * (2 * length - len(s)) + s - return rev_hex(s) + le_bytes = i.to_bytes(length, "little") + return le_bytes.hex() def var_int(i: int) -> str: @@ -381,13 +382,13 @@ https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer """ if i < 0xFD: - return int_to_hex(i) + return int_to_le_hex(i) elif i <= 0xFFFF: - return "fd" + int_to_hex(i, 2) + return "fd" + int_to_le_hex(i, 2) elif i <= 0xFFFFFFFF: - return "fe" + int_to_hex(i, 4) + return "fe" + int_to_le_hex(i, 4) else: - return "ff" + int_to_hex(i, 8) + return "ff" + int_to_le_hex(i, 8) def op_push_bytes(data_len: int) -> bytes: @@ -1046,7 +1047,7 @@ # public key can be determined without the master private key. def CKD_priv(k, c, n): is_prime = n & BIP32_PRIME - return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n, 4))), is_prime) + return _CKD_priv(k, c, n.to_bytes(4, "big"), is_prime) def _CKD_priv(k, c, s, is_prime): @@ -1071,7 +1072,7 @@ def CKD_pub(cK, c, n): if n & BIP32_PRIME: raise - return _CKD_pub(cK, c, bfh(rev_hex(int_to_hex(n, 4)))) + return _CKD_pub(cK, c, n.to_bytes(4, "big")) # helper function, callable with arbitrary string @@ -1935,3 +1936,9 @@ def __str__(self): return self.enc + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/electrum/electrumabc/blockchain.py b/electrum/electrumabc/blockchain.py --- a/electrum/electrumabc/blockchain.py +++ b/electrum/electrumabc/blockchain.py @@ -132,12 +132,12 @@ def serialize_header(res): s = ( - bitcoin.int_to_hex(res.get("version"), 4) + bitcoin.int_to_le_hex(res.get("version"), 4) + bitcoin.rev_hex(res.get("prev_block_hash")) + bitcoin.rev_hex(res.get("merkle_root")) - + bitcoin.int_to_hex(int(res.get("timestamp")), 4) - + bitcoin.int_to_hex(int(res.get("bits")), 4) - + bitcoin.int_to_hex(int(res.get("nonce")), 4) + + bitcoin.int_to_le_hex(int(res.get("timestamp")), 4) + + bitcoin.int_to_le_hex(int(res.get("bits")), 4) + + bitcoin.int_to_le_hex(int(res.get("nonce")), 4) ) return s diff --git a/electrum/electrumabc/keystore.py b/electrum/electrumabc/keystore.py --- a/electrum/electrumabc/keystore.py +++ b/electrum/electrumabc/keystore.py @@ -318,9 +318,9 @@ def encode_path_int(path_int) -> str: if path_int < 0xFFFF: - hexstr = bitcoin.int_to_hex(path_int, 2) + hexstr = bitcoin.int_to_le_hex(path_int, 2) else: - hexstr = "ffff" + bitcoin.int_to_hex(path_int, 4) + hexstr = "ffff" + bitcoin.int_to_le_hex(path_int, 4) return hexstr s = "".join(map(encode_path_int, (c, i))) @@ -551,7 +551,7 @@ return self.mpk def get_xpubkey(self, for_change, n): - s = bitcoin.int_to_hex(for_change, 2) + bitcoin.int_to_hex(n, 2) + s = bitcoin.int_to_le_hex(for_change, 2) + bitcoin.int_to_le_hex(n, 2) return "fe" + self.mpk + s @classmethod diff --git a/electrum/electrumabc/transaction.py b/electrum/electrumabc/transaction.py --- a/electrum/electrumabc/transaction.py +++ b/electrum/electrumabc/transaction.py @@ -878,7 +878,7 @@ @classmethod def serialize_outpoint(self, txin): - return bh2u(bfh(txin["prevout_hash"])[::-1]) + bitcoin.int_to_hex( + return bh2u(bfh(txin["prevout_hash"])[::-1]) + bitcoin.int_to_le_hex( txin["prevout_n"], 4 ) @@ -889,14 +889,14 @@ # Script length, script, sequence s += bitcoin.var_int(len(script) // 2) s += script - s += bitcoin.int_to_hex(txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4) + s += bitcoin.int_to_le_hex(txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4) # offline signing needs to know the input value if ( "value" in txin and txin.get("scriptSig") is None and not (estimate_size or self.is_txin_complete(txin)) ): - s += bitcoin.int_to_hex(txin["value"], 8) + s += bitcoin.int_to_le_hex(txin["value"], 8) return s def shuffle_inputs(self): @@ -918,7 +918,7 @@ def serialize_output(self, output): output_type, addr, amount = output - s = bitcoin.int_to_hex(amount, 8) + s = bitcoin.int_to_le_hex(amount, 8) script = self.pay_script(addr) s += bitcoin.var_int(len(script) // 2) s += script @@ -980,7 +980,9 @@ hashSequence = bitcoin.Hash( bfh( "".join( - bitcoin.int_to_hex(txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4) + bitcoin.int_to_le_hex( + txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4 + ) for txin in inputs ) ) @@ -1000,19 +1002,21 @@ if (nHashType & 0xFF) != 0x41: raise ValueError("other hashtypes not supported; submit a PR to fix this!") - nVersion = bitcoin.int_to_hex(self.version, 4) - nHashType = bitcoin.int_to_hex(nHashType, 4) - nLocktime = bitcoin.int_to_hex(self.locktime, 4) + nVersion = bitcoin.int_to_le_hex(self.version, 4) + nHashType = bitcoin.int_to_le_hex(nHashType, 4) + nLocktime = bitcoin.int_to_le_hex(self.locktime, 4) txin = self.inputs()[i] outpoint = self.serialize_outpoint(txin) preimage_script = self.get_preimage_script(txin) scriptCode = bitcoin.var_int(len(preimage_script) // 2) + preimage_script try: - amount = bitcoin.int_to_hex(txin["value"], 8) + amount = bitcoin.int_to_le_hex(txin["value"], 8) except KeyError: raise InputValueMissing - nSequence = bitcoin.int_to_hex(txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4) + nSequence = bitcoin.int_to_le_hex( + txin.get("sequence", DEFAULT_TXIN_SEQUENCE), 4 + ) hashPrevouts, hashSequence, hashOutputs = self.calc_common_sighash( use_cache=use_cache @@ -1033,8 +1037,8 @@ return preimage def serialize(self, estimate_size=False): - nVersion = bitcoin.int_to_hex(self.version, 4) - nLocktime = bitcoin.int_to_hex(self.locktime, 4) + nVersion = bitcoin.int_to_le_hex(self.version, 4) + nLocktime = bitcoin.int_to_le_hex(self.locktime, 4) inputs = self.inputs() outputs = self.outputs() txins = bitcoin.var_int(len(inputs)) + "".join( diff --git a/electrum/electrumabc_plugins/ledger/ledger.py b/electrum/electrumabc_plugins/ledger/ledger.py --- a/electrum/electrumabc_plugins/ledger/ledger.py +++ b/electrum/electrumabc_plugins/ledger/ledger.py @@ -13,7 +13,7 @@ TYPE_ADDRESS, TYPE_SCRIPT, SignatureType, - int_to_hex, + int_to_le_hex, var_int, ) from electrumabc.constants import DEFAULT_TXIN_SEQUENCE @@ -516,7 +516,7 @@ txOutput = var_int(len(tx.outputs())) for txout in tx.outputs(): - txOutput += int_to_hex(txout.value, 8) + txOutput += int_to_le_hex(txout.value, 8) script = tx.pay_script(txout.destination) txOutput += var_int(len(script) // 2) txOutput += script @@ -594,11 +594,11 @@ _("Preparing transaction inputs...") + f" (phase1, {input_idx}/{len(inputs)})" ) - sequence = int_to_hex(utxo[5], 4) + sequence = int_to_le_hex(utxo[5], 4) if not client_electrum.requires_trusted_inputs(): txtmp = bitcoinTransaction(bfh(utxo[0])) tmp = bfh(utxo[3])[::-1] - tmp += bfh(int_to_hex(utxo[1], 4)) + tmp += bfh(int_to_le_hex(utxo[1], 4)) tmp += txtmp.outputs[utxo[1]].amount chipInputs.append( {"value": tmp, "witness": True, "sequence": sequence}