def _validate(state: State): from apps.monero.signing import RctType if state.current_output_index + 1 != state.output_count: raise ValueError("Invalid out num") # Test if \sum Alpha == \sum A if state.rct_type == RctType.Simple: utils.ensure(crypto.sc_eq(state.sumout, state.sumpouts_alphas)) # Fee test if state.fee != (state.summary_inputs_money - state.summary_outs_money): raise ValueError( "Fee invalid %s vs %s, out: %s" % ( state.fee, state.summary_inputs_money - state.summary_outs_money, state.summary_outs_money, ) ) if state.summary_outs_money > state.summary_inputs_money: raise ValueError( "Transaction inputs money (%s) less than outputs money (%s)" % (state.summary_inputs_money, state.summary_outs_money) )
def prove_setup(self, sv, gamma, proof_v8=False): utils.ensure(len(sv) == len(gamma), "|sv| != |gamma|") utils.ensure(len(sv) > 0, "sv empty") self.proof_sec = crypto.random_bytes(64) self._det_mask_init() gc.collect() sv = [crypto.encodeint(x) for x in sv] gamma = [crypto.encodeint(x) for x in gamma] M, logM = 1, 0 while M <= _BP_M and M < len(sv): logM += 1 M = 1 << logM MN = M * _BP_N V = _ensure_dst_keyvect(None, len(sv)) for i in range(len(sv)): add_keys2(tmp_bf_0, gamma[i], sv[i], _XMR_H) if not proof_v8: scalarmult_key(tmp_bf_0, tmp_bf_0, _INV_EIGHT) V.read(i, tmp_bf_0) aL, aR = self.aX_vcts(sv, MN) return M, logM, aL, aR, V, gamma
def matches(self, multisig: MultisigRedeemScriptType): fp = multisig_fingerprint(multisig) ensure(fp is not None) if self.mismatch is False and self.fingerprint == fp: return True else: return False
def write_uint32_le(w: bytearray, n: int) -> int: ensure(0 <= n <= 0xFFFFFFFF) w.append(n & 0xFF) w.append((n >> 8) & 0xFF) w.append((n >> 16) & 0xFF) w.append((n >> 24) & 0xFF) return 4
def set_serialized_signature(self, index: int, signature: bytes) -> None: # Only one signature per TxRequest can be serialized. assert self.tx_req.serialized is not None ensure(self.tx_req.serialized.signature is None) self.tx_req.serialized.signature_index = index self.tx_req.serialized.signature = signature
def preimage_hash( self, coin: CoinInfo, tx: SignTx, txi: TxInputType, pubkeyhash: bytes, sighash: int, ) -> bytes: h_preimage = HashWriter(sha256()) ensure(not tx.overwintered) write_uint32(h_preimage, tx.version) # nVersion write_bytes(h_preimage, bytearray(self.get_prevouts_hash(coin))) # hashPrevouts write_bytes(h_preimage, bytearray(self.get_sequence_hash(coin))) # hashSequence write_bytes_reversed(h_preimage, txi.prev_hash) # outpoint write_uint32(h_preimage, txi.prev_index) # outpoint script_code = self.derive_script_code(txi, pubkeyhash) # scriptCode write_varint(h_preimage, len(script_code)) write_bytes(h_preimage, script_code) write_uint64(h_preimage, txi.amount) # amount write_uint32(h_preimage, txi.sequence) # nSequence write_bytes(h_preimage, bytearray(self.get_outputs_hash(coin))) # hashOutputs write_uint32(h_preimage, tx.lock_time) # nLockTime write_uint32(h_preimage, sighash) # nHashType return get_tx_hash(h_preimage, double=coin.sign_hash_double)
def __exit__(self, exc_type, exc_value, tb): if exc_type is None: ensure(False, f"{repr(self.expected)} not raised") if issubclass(exc_type, self.expected): self.value = exc_value return True return False
async def _slip39_show_share_words(ctx, share_index, share_words): first, chunks, last = _slip39_split_share_into_pages(share_words) if share_index is None: header_title = "Recovery seed" else: header_title = "Recovery share #%s" % (share_index + 1) header_icon = ui.ICON_RESET pages = [] # ui page components shares_words_check = [] # check we display correct data # first page text = Text(header_title, header_icon) text.bold("Write down these") text.bold("%s words:" % len(share_words)) text.br_half() for index, word in first: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) pages.append(text) # middle pages for chunk in chunks: text = Text(header_title, header_icon) for index, word in chunk: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) pages.append(text) # last page text = Text(header_title, header_icon) for index, word in last: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) text.br_half() text.bold("I confirm that I wrote") text.bold("down all %s words." % len(share_words)) pages.append(text) # pagination paginated = Paginated(pages) if __debug__: word_pages = [first] + chunks + [last] def export_displayed_words(): # export currently displayed mnemonic words into debuglink debug.reset_current_words = [ w for _, w in word_pages[paginated.page] ] paginated.on_change = export_displayed_words export_displayed_words() # make sure we display correct data utils.ensure(share_words == shares_words_check) # confirm the share await hold_to_confirm(ctx, paginated) # TODO: customize the loader here
def _build_key( secret: bytes, discriminator: bytes, index: int | None = None, out: bytes | None = None, ) -> bytes: """ Creates an unique-purpose key """ key_buff = _BUILD_KEY_BUFFER utils.ensure(len(secret) == _SECRET_LENGTH, "Invalid key length") utils.ensure(len(discriminator) <= _DISCRIMINATOR_LENGTH, "Disc too long") offset = _SECRET_LENGTH utils.memcpy(key_buff, 0, secret, 0, _SECRET_LENGTH) for i in range(_SECRET_LENGTH, len(key_buff)): key_buff[i] = 0 utils.memcpy(key_buff, offset, discriminator, 0, len(discriminator)) offset += _DISCRIMINATOR_LENGTH # fixed domain separator size if index is not None: # dump_uvarint_b_into, saving import shifted = True while shifted: shifted = index >> 7 key_buff[offset] = (index & 0x7F) | (0x80 if shifted else 0x00) offset += 1 index = shifted return crypto_helpers.keccak_2hash(key_buff, out)
def add(self, multisig: MultisigRedeemScriptType): fp = multisig_fingerprint(multisig) ensure(fp is not None) if self.fingerprint is None: self.fingerprint = fp elif self.fingerprint != fp: self.mismatch = True
def write_uint32_be(w: Writer, n: int) -> int: ensure(0 <= n <= 0xFFFFFFFF) w.append((n >> 24) & 0xFF) w.append((n >> 16) & 0xFF) w.append((n >> 8) & 0xFF) w.append(n & 0xFF) return 4
def _build_key(secret, discriminator=None, index: int = None, out: bytes = None) -> bytes: """ Creates an unique-purpose key """ key_buff = BUILD_KEY_BUFFER # bytearray(32 + 12 + 4) # key + disc + index utils.ensure(len(secret) == 32, "Invalid key length") utils.ensure(len(discriminator) <= 12, "Disc too long") offset = 32 utils.memcpy(key_buff, 0, secret, 0, 32) for i in range(32, len(key_buff)): key_buff[i] = 0 if discriminator is not None: utils.memcpy(key_buff, offset, discriminator, 0, len(discriminator)) offset += len(discriminator) if index is not None: # dump_uvarint_b_into, saving import shifted = True while shifted: shifted = index >> 7 key_buff[offset] = (index & 0x7F) | (0x80 if shifted else 0x00) offset += 1 index = shifted return crypto.keccak_2hash(key_buff, out)
def get(self, key: int, default: T | None = None) -> bytes | T | None: # noqa: F811 utils.ensure(key < len(self.fields), f"failed to load key {key}") if self.data[key][0] != 1: return default return bytes(self.data[key][1:])
def __init__(self, tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo) -> None: ensure(coin.decred) super().__init__(tx, keychain, coin) self.write_tx_header(self.serialized_tx, self.tx, witness_marker=True) write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count)
def _rsig_process_bp(state: State, rsig_data: MoneroTransactionRsigData): from apps.monero.xmr import range_signatures from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import ( Bulletproof, BulletproofPlus, ) if state.rsig_is_bp_plus: bp_obj = serialize.parse_msg(rsig_data.rsig, BulletproofPlus) else: bp_obj = serialize.parse_msg(rsig_data.rsig, Bulletproof) rsig_data.rsig = None # BP is hashed with raw=False as hash does not contain L, R # array sizes compared to the serialized bulletproof format # thus direct serialization cannot be used. state.full_message_hasher.rsig_val(bp_obj, raw=False) res = range_signatures.verify_bp(bp_obj, state.output_amounts, state.output_masks) utils.ensure(res, "BP verification fail") state.mem_trace("BP verified" if __debug__ else None, collect=True) del (bp_obj, range_signatures) # State cleanup after verification is finished state.output_amounts = [] state.output_masks = []
def assertListEqual(self, x, y, msg=''): if len(x) != len(y): if not msg: msg = "List lengths not equal" ensure(False, msg) for i in range(len(x)): self.assertEqual(x[i], y[i], msg)
def assertEqual(self, x, y, msg=''): if not msg: msg = f"{repr(x)} vs (expected) {repr(y)}" if x.__class__ == y.__class__ and x.__class__.__name__ == "Msg": self.assertMessageEqual(x, y) else: ensure(x == y, msg)
def output_script_p2sh(scripthash: bytes) -> bytearray: # A9 14 <scripthash> 87 utils.ensure(len(scripthash) == 20) s = bytearray(23) s[0] = 0xA9 # OP_HASH_160 s[1] = 0x14 # pushing 20 bytes s[2:22] = scripthash s[22] = 0x87 # OP_EQUAL return s
def check_script_size(script: bytes) -> None: try: r = BufferReader(script) n = read_uint32_be(r) r.read(n) n = read_uint32_be(r) ensure(r.remaining_count() == n) except (AssertionError, EOFError): raise wire.DataError("Invalid script")
def _check_out_commitment(state: State, amount, mask, C): utils.ensure( crypto.point_eq( C, crypto.point_add(crypto.scalarmult_base(mask), crypto.scalarmult_h(amount)), ), "OutC fail", )
def assertRaises(self, exc, func=None, *args, **kwargs): if func is None: return AssertRaisesContext(exc) try: func(*args, **kwargs) ensure(False, "%r not raised" % exc) except Exception as e: if isinstance(e, exc): return raise
def output_script_p2pkh(pubkeyhash: bytes) -> bytearray: utils.ensure(len(pubkeyhash) == 20) s = bytearray(25) s[0] = 0x76 # OP_DUP s[1] = 0xA9 # OP_HASH_160 s[2] = 0x14 # pushing 20 bytes s[3:23] = pubkeyhash s[23] = 0x88 # OP_EQUALVERIFY s[24] = 0xAC # OP_CHECKSIG return s
def output_script_ssgen(pkh: bytes) -> bytearray: utils.ensure(len(pkh) == 20) s = bytearray(26) s[0] = 0xBB # OP_SSGEN s[1] = 0x76 # OP_DUP s[2] = 0xA9 # OP_HASH160 s[3] = 0x14 # OP_DATA_20 s[4:24] = pkh s[24] = 0x88 # OP_EQUALVERIFY s[25] = 0xAC # OP_CHECKSIG return s
def script_replay_protection_bip115(block_hash: bytes, block_height: bytes) -> bytearray: if block_hash is None or block_height is None: return bytearray() ensure(len(block_hash) == 32) s = bytearray(33) s[0] = 0x20 # 32 bytes for block hash s[1:33] = block_hash # block hash write_scriptnum(s, block_height) s.append(0xB4) # OP_CHECKBLOCKATHEIGHT return s
def write_uint64_be(w: Writer, n: int) -> int: ensure(0 <= n <= 0xFFFFFFFFFFFFFFFF) w.append((n >> 56) & 0xFF) w.append((n >> 48) & 0xFF) w.append((n >> 40) & 0xFF) w.append((n >> 32) & 0xFF) w.append((n >> 24) & 0xFF) w.append((n >> 16) & 0xFF) w.append((n >> 8) & 0xFF) w.append(n & 0xFF) return 8
def input_script_p2wpkh_in_p2sh(pubkeyhash: bytes) -> bytearray: # 16 00 14 <pubkeyhash> # Signature is moved to the witness. utils.ensure(len(pubkeyhash) == 20) w = empty_bytearray(3 + len(pubkeyhash)) w.append(0x16) # length of the data w.append(0x00) # witness version byte w.append(0x14) # P2WPKH witness program (pub key hash length) write_bytes_fixed(w, pubkeyhash, 20) # pub key hash return w
def preimage_hash( self, coin: CoinInfo, tx: SignTx, txi: TxInputType, pubkeyhash: bytes, sighash: int, ) -> bytes: h_preimage = HashWriter( blake2b(outlen=32, personal=b"ZcashSigHash" + struct.pack("<I", self.branch_id))) ensure(coin.overwintered) ensure(tx.version == 4) write_uint32(h_preimage, tx.version | OVERWINTERED) # 1. nVersion | fOverwintered write_uint32(h_preimage, tx.version_group_id) # 2. nVersionGroupId # 3. hashPrevouts write_bytes_fixed(h_preimage, bytearray(self.get_prevouts_hash()), TX_HASH_SIZE) # 4. hashSequence write_bytes_fixed(h_preimage, bytearray(self.get_sequence_hash()), TX_HASH_SIZE) # 5. hashOutputs write_bytes_fixed(h_preimage, bytearray(self.get_outputs_hash()), TX_HASH_SIZE) zero_hash = b"\x00" * TX_HASH_SIZE write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE) # 6. hashJoinSplits write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE) # 7. hashShieldedSpends write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE) # 8. hashShieldedOutputs write_uint32(h_preimage, tx.lock_time) # 9. nLockTime write_uint32(h_preimage, tx.expiry) # 10. expiryHeight write_uint64(h_preimage, 0) # 11. valueBalance write_uint32(h_preimage, sighash) # 12. nHashType write_bytes_reversed(h_preimage, txi.prev_hash, TX_HASH_SIZE) # 13a. outpoint write_uint32(h_preimage, txi.prev_index) script_code = derive_script_code(txi, pubkeyhash) # 13b. scriptCode write_bytes_prefixed(h_preimage, script_code) write_uint64(h_preimage, txi.amount) # 13c. value write_uint32(h_preimage, txi.sequence) # 13d. nSequence return get_tx_hash(h_preimage)
def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray: # Either: # 00 14 <20-byte-key-hash> # 00 20 <32-byte-script-hash> ensure(len(witprog) == 20 or len(witprog) == 32) w = empty_bytearray(3 + len(witprog)) w.append(0x00) # witness version byte w.append(len(witprog)) # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes write_bytes(w, witprog) # pub key hash return w
def __init__( self, tx: SignTx, keychain: Keychain, coin: CoinInfo, approver: approvers.Approver, ) -> None: ensure(coin.overwintered) super().__init__(tx, keychain, coin, approver) if tx.version != 4: raise wire.DataError("Unsupported transaction version.")
def __init__(self, tx: SignTx, keychain: Keychain, coin: CoinInfo) -> None: ensure(coin.overwintered) super().__init__(tx, keychain, coin) if self.tx.version == 3: if not self.tx.branch_id: self.tx.branch_id = 0x5BA81B19 # Overwinter elif self.tx.version == 4: if not self.tx.branch_id: self.tx.branch_id = 0x76B809BB # Sapling else: raise wire.DataError("Unsupported version for overwintered transaction")