mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-11 21:56:07 +00:00
feat(legacy): Implement SLIP-0019 proofs of ownership.
This commit is contained in:
parent
9732f6524a
commit
fa2d618f7d
1
legacy/firmware/.changelog.d/2718.added.1
Normal file
1
legacy/firmware/.changelog.d/2718.added.1
Normal file
@ -0,0 +1 @@
|
||||
Implement SLIP-0019 proofs of ownership for native SegWit.
|
@ -371,6 +371,14 @@ bool fsm_layoutVerifyMessage(const uint8_t *msg, uint32_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
bool fsm_layoutCommitmentData(const uint8_t *msg, uint32_t len) {
|
||||
if (is_valid_ascii(msg, len)) {
|
||||
return fsm_layoutPaginated(_("Commitment data"), msg, len, true);
|
||||
} else {
|
||||
return fsm_layoutPaginated(_("Binary commitment data"), msg, len, false);
|
||||
}
|
||||
}
|
||||
|
||||
void fsm_msgRebootToBootloader(void) {
|
||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||
_("Do you want to"), _("restart device in"),
|
||||
|
@ -80,6 +80,8 @@ void fsm_msgTxAck(
|
||||
void fsm_msgGetAddress(const GetAddress *msg);
|
||||
void fsm_msgSignMessage(const SignMessage *msg);
|
||||
void fsm_msgVerifyMessage(const VerifyMessage *msg);
|
||||
void fsm_msgGetOwnershipId(const GetOwnershipId *msg);
|
||||
void fsm_msgGetOwnershipProof(const GetOwnershipProof *msg);
|
||||
|
||||
// crypto
|
||||
void fsm_msgCipherKeyValue(const CipherKeyValue *msg);
|
||||
|
@ -183,6 +183,27 @@ bool fsm_checkCoinPath(const CoinInfo *coin, InputScriptType script_type,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fsm_checkScriptType(const CoinInfo *coin, InputScriptType script_type) {
|
||||
if (!is_internal_input_script_type(script_type)) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid script type"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_segwit_input_script_type(script_type) && !coin->has_segwit) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Segwit not enabled on this coin"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (script_type == InputScriptType_SPENDTAPROOT && !coin->has_taproot) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Taproot not enabled on this coin"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fsm_msgGetAddress(const GetAddress *msg) {
|
||||
RESP_INIT(Address);
|
||||
|
||||
@ -373,3 +394,166 @@ void fsm_msgVerifyMessage(const VerifyMessage *msg) {
|
||||
}
|
||||
layoutHome();
|
||||
}
|
||||
|
||||
bool fsm_getOwnershipId(uint8_t *script_pubkey, size_t script_pubkey_size,
|
||||
uint8_t ownership_id[OWNERSHIP_ID_SIZE]) {
|
||||
const char *OWNERSHIP_ID_KEY_PATH[] = {"SLIP-0019",
|
||||
"Ownership identification key"};
|
||||
|
||||
uint8_t ownership_id_key[32] = {0};
|
||||
if (!fsm_getSlip21Key(OWNERSHIP_ID_KEY_PATH, 2, ownership_id_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hmac_sha256(ownership_id_key, sizeof(ownership_id_key), script_pubkey,
|
||||
script_pubkey_size, ownership_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fsm_msgGetOwnershipId(const GetOwnershipId *msg) {
|
||||
RESP_INIT(OwnershipId);
|
||||
|
||||
CHECK_INITIALIZED
|
||||
|
||||
CHECK_PIN
|
||||
|
||||
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
|
||||
if (!coin) return;
|
||||
|
||||
if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count,
|
||||
msg->address_n, msg->has_multisig, false)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fsm_checkScriptType(coin, msg->script_type)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n,
|
||||
msg->address_n_count, NULL);
|
||||
if (!node) return;
|
||||
|
||||
uint8_t script_pubkey[520] = {0};
|
||||
pb_size_t script_pubkey_size = 0;
|
||||
if (!get_script_pubkey(coin, node, msg->has_multisig, &msg->multisig,
|
||||
msg->script_type, script_pubkey,
|
||||
&script_pubkey_size)) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Failed to derive scriptPubKey"));
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fsm_getOwnershipId(script_pubkey, script_pubkey_size,
|
||||
resp->ownership_id.bytes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
resp->ownership_id.size = 32;
|
||||
|
||||
msg_write(MessageType_MessageType_OwnershipId, resp);
|
||||
layoutHome();
|
||||
}
|
||||
|
||||
void fsm_msgGetOwnershipProof(const GetOwnershipProof *msg) {
|
||||
RESP_INIT(OwnershipProof);
|
||||
|
||||
CHECK_INITIALIZED
|
||||
|
||||
CHECK_PIN
|
||||
|
||||
if (msg->has_multisig) {
|
||||
// The legacy implementation currently only supports singlesig native segwit
|
||||
// v0 and v1, the bare minimum for CoinJoin.
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Multisig not supported."));
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
|
||||
if (!coin) return;
|
||||
|
||||
if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count,
|
||||
msg->address_n, msg->has_multisig, false)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fsm_checkScriptType(coin, msg->script_type)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n,
|
||||
msg->address_n_count, NULL);
|
||||
if (!node) return;
|
||||
|
||||
uint8_t script_pubkey[520] = {0};
|
||||
pb_size_t script_pubkey_size = 0;
|
||||
if (!get_script_pubkey(coin, node, msg->has_multisig, &msg->multisig,
|
||||
msg->script_type, script_pubkey,
|
||||
&script_pubkey_size)) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Failed to derive scriptPubKey"));
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ownership_id[OWNERSHIP_ID_SIZE] = {0};
|
||||
if (!fsm_getOwnershipId(script_pubkey, script_pubkey_size, ownership_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Providing an ownership ID is optional in case of singlesig, but if one is
|
||||
// provided, then it should match.
|
||||
if (msg->ownership_ids_count) {
|
||||
if (msg->ownership_ids_count != 1 ||
|
||||
msg->ownership_ids[0].size != sizeof(ownership_id) ||
|
||||
memcmp(ownership_id, msg->ownership_ids[0].bytes,
|
||||
sizeof(ownership_id)) != 0) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Invalid ownership identifier"));
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In order to set the "user confirmation" bit in the proof, the user must
|
||||
// actually confirm.
|
||||
uint8_t flags = 0;
|
||||
if (msg->user_confirmation) {
|
||||
flags |= 1;
|
||||
layoutConfirmOwnershipProof();
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
|
||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->has_commitment_data) {
|
||||
if (!fsm_layoutCommitmentData(msg->commitment_data.bytes,
|
||||
msg->commitment_data.size)) {
|
||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_ownership_proof(coin, msg->script_type, node, flags, ownership_id,
|
||||
script_pubkey, script_pubkey_size,
|
||||
msg->commitment_data.bytes,
|
||||
msg->commitment_data.size, resp)) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed"));
|
||||
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
msg_write(MessageType_MessageType_OwnershipProof, resp);
|
||||
layoutHome();
|
||||
}
|
||||
|
@ -1315,3 +1315,9 @@ void layoutConfirmHash(const BITMAP *icon, const char *description,
|
||||
layoutButtonYes(_("Confirm"), &bmp_btn_confirm);
|
||||
oledRefresh();
|
||||
}
|
||||
|
||||
void layoutConfirmOwnershipProof(void) {
|
||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||
_("Do you want to"), _("create a proof of"),
|
||||
_("ownership?"), NULL, NULL, NULL);
|
||||
}
|
||||
|
@ -115,6 +115,8 @@ void layoutConfirmSafetyChecks(SafetyCheckLevel safety_checks_level);
|
||||
void layoutConfirmHash(const BITMAP *icon, const char *description,
|
||||
const uint8_t *hash, uint32_t len);
|
||||
|
||||
void layoutConfirmOwnershipProof(void);
|
||||
|
||||
const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen);
|
||||
const char **split_message_hex(const uint8_t *msg, uint32_t len);
|
||||
|
||||
|
@ -4,7 +4,7 @@ endif
|
||||
|
||||
SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \
|
||||
DebugLinkRecordScreen DebugLinkEraseSdCard DebugLinkWatchLayout \
|
||||
GetOwnershipProof OwnershipProof GetOwnershipId OwnershipId AuthorizeCoinJoin DoPreauthorized \
|
||||
AuthorizeCoinJoin DoPreauthorized \
|
||||
CancelAuthorization DebugLinkLayout GetNonce SetBusy UnlockPath \
|
||||
TxAckInput TxAckOutput TxAckPrev TxAckPaymentRequest \
|
||||
EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \
|
||||
|
@ -34,8 +34,8 @@ TxInputType.address_n max_count:8
|
||||
TxInputType.prev_hash max_size:32
|
||||
TxInputType.script_sig max_size:1650
|
||||
TxInputType.witness max_size:109
|
||||
TxInputType.ownership_proof max_size:171
|
||||
TxInputType.commitment_data max_size:32
|
||||
TxInputType.ownership_proof max_size:147
|
||||
TxInputType.commitment_data max_size:70
|
||||
TxInputType.orig_hash max_size:32
|
||||
TxInputType.script_pubkey max_size:520
|
||||
|
||||
@ -62,8 +62,8 @@ TxInput.address_n max_count:8
|
||||
TxInput.prev_hash max_size:32
|
||||
TxInput.script_sig max_size:1650
|
||||
TxInput.witness max_size:109
|
||||
TxInput.ownership_proof max_size:171
|
||||
TxInput.commitment_data max_size:32
|
||||
TxInput.ownership_proof max_size:147
|
||||
TxInput.commitment_data max_size:70
|
||||
TxInput.orig_hash max_size:32
|
||||
TxInput.script_pubkey max_size:520
|
||||
|
||||
@ -79,12 +79,21 @@ PrevOutput.script_pubkey max_size:520
|
||||
|
||||
TxAckPrevExtraDataWrapper.extra_data_chunk type:FT_IGNORE
|
||||
|
||||
GetOwnershipId.address_n max_count:8
|
||||
GetOwnershipId.coin_name max_size:21
|
||||
|
||||
OwnershipId.ownership_id max_size:32
|
||||
|
||||
GetOwnershipProof.address_n max_count:8
|
||||
GetOwnershipProof.coin_name max_size:21
|
||||
GetOwnershipProof.ownership_ids max_count:15 max_size:32
|
||||
GetOwnershipProof.commitment_data max_size:70
|
||||
|
||||
OwnershipProof.ownership_proof max_size:147
|
||||
OwnershipProof.signature max_size:71
|
||||
|
||||
# Unused messages.
|
||||
AuthorizeCoinJoin skip_message:true
|
||||
GetOwnershipId skip_message:true
|
||||
OwnershipId skip_message:true
|
||||
GetOwnershipProof skip_message:true
|
||||
OwnershipProof skip_message:true
|
||||
TxAckPaymentRequest skip_message:true
|
||||
PaymentRequestMemo skip_message:true
|
||||
CoinJoinRequest skip_message:true
|
||||
|
@ -79,6 +79,8 @@
|
||||
|
||||
static const uint8_t segwit_header[2] = {0, 1};
|
||||
|
||||
static const uint8_t SLIP19_VERSION_MAGIC[] = {0x53, 0x4c, 0x00, 0x19};
|
||||
|
||||
static inline uint32_t op_push_size(uint32_t i) {
|
||||
if (i < 0x4C) {
|
||||
return 1;
|
||||
@ -413,6 +415,20 @@ int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
|
||||
return out->script_pubkey.size;
|
||||
}
|
||||
|
||||
int get_script_pubkey(const CoinInfo *coin, HDNode *node, bool has_multisig,
|
||||
const MultisigRedeemScriptType *multisig,
|
||||
InputScriptType script_type, uint8_t *script_pubkey,
|
||||
pb_size_t *script_pubkey_size) {
|
||||
char address[MAX_ADDR_SIZE] = {0};
|
||||
bool res = true;
|
||||
res = res && (hdnode_fill_public_key(node) == 0);
|
||||
res = res && compute_address(coin, script_type, node, has_multisig, multisig,
|
||||
address);
|
||||
res = res && address_to_script_pubkey(coin, address, script_pubkey,
|
||||
script_pubkey_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fill_input_script_pubkey(const CoinInfo *coin, const HDNode *root,
|
||||
TxInputType *in) {
|
||||
if (in->script_type == InputScriptType_EXTERNAL) {
|
||||
@ -422,16 +438,13 @@ int fill_input_script_pubkey(const CoinInfo *coin, const HDNode *root,
|
||||
|
||||
static CONFIDENTIAL HDNode node;
|
||||
memcpy(&node, root, sizeof(HDNode));
|
||||
char address[MAX_ADDR_SIZE] = {0};
|
||||
bool res = true;
|
||||
int res = true;
|
||||
res = res && hdnode_private_ckd_cached(&node, in->address_n,
|
||||
in->address_n_count, NULL);
|
||||
res = res && (hdnode_fill_public_key(&node) == 0);
|
||||
res = res && compute_address(coin, in->script_type, &node, in->has_multisig,
|
||||
&in->multisig, address);
|
||||
res = res && get_script_pubkey(coin, &node, in->has_multisig, &in->multisig,
|
||||
in->script_type, in->script_pubkey.bytes,
|
||||
&in->script_pubkey.size);
|
||||
memzero(&node, sizeof(node));
|
||||
res = res && address_to_script_pubkey(coin, address, in->script_pubkey.bytes,
|
||||
&in->script_pubkey.size);
|
||||
in->has_script_pubkey = res;
|
||||
return res;
|
||||
}
|
||||
@ -1222,3 +1235,78 @@ uint32_t tx_decred_witness_weight(const TxInputType *txinput) {
|
||||
return 4 * size;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool get_ownership_proof(const CoinInfo *coin, InputScriptType script_type,
|
||||
const HDNode *node, uint8_t flags,
|
||||
const uint8_t ownership_id[OWNERSHIP_ID_SIZE],
|
||||
const uint8_t *script_pubkey,
|
||||
size_t script_pubkey_size,
|
||||
const uint8_t *commitment_data,
|
||||
size_t commitment_data_size, OwnershipProof *out) {
|
||||
size_t r = 0;
|
||||
|
||||
// Write versionMagic (4 bytes).
|
||||
memcpy(out->ownership_proof.bytes + r, SLIP19_VERSION_MAGIC,
|
||||
sizeof(SLIP19_VERSION_MAGIC));
|
||||
r += sizeof(SLIP19_VERSION_MAGIC);
|
||||
|
||||
// Write flags (1 byte).
|
||||
out->ownership_proof.bytes[r] = flags;
|
||||
r += 1;
|
||||
|
||||
// Write number of ownership IDs (1 byte).
|
||||
r += ser_length(1, out->ownership_proof.bytes + r);
|
||||
|
||||
// Write ownership ID (32 bytes).
|
||||
memcpy(out->ownership_proof.bytes + r, ownership_id, OWNERSHIP_ID_SIZE);
|
||||
r += OWNERSHIP_ID_SIZE;
|
||||
|
||||
// Compute sighash = SHA-256(proofBody || proofFooter).
|
||||
Hasher hasher = {0};
|
||||
uint8_t sighash[SHA256_DIGEST_LENGTH] = {0};
|
||||
hasher_InitParam(&hasher, HASHER_SHA2, NULL, 0);
|
||||
hasher_Update(&hasher, out->ownership_proof.bytes, r);
|
||||
tx_script_hash(&hasher, script_pubkey_size, script_pubkey);
|
||||
tx_script_hash(&hasher, commitment_data_size, commitment_data);
|
||||
hasher_Final(&hasher, sighash);
|
||||
|
||||
// Write proofSignature.
|
||||
if (script_type == InputScriptType_SPENDWITNESS) {
|
||||
if (!tx_sign_ecdsa(coin->curve->params, node->private_key, sighash,
|
||||
out->signature.bytes, &out->signature.size)) {
|
||||
return false;
|
||||
}
|
||||
// Write length-prefixed empty scriptSig (1 byte).
|
||||
r += ser_length(0, out->ownership_proof.bytes + r);
|
||||
|
||||
// Write
|
||||
// 1. number of stack items (1 byte)
|
||||
// 2. signature + sighash type length (1 byte)
|
||||
// 3. DER-encoded signature (max. 71 bytes)
|
||||
// 4. sighash type (1 byte)
|
||||
// 5. public key length (1 byte)
|
||||
// 6. public key (33 bytes)
|
||||
r += serialize_p2wpkh_witness(out->signature.bytes, out->signature.size,
|
||||
node->public_key, 33, SIGHASH_ALL,
|
||||
out->ownership_proof.bytes + r);
|
||||
} else if (script_type == InputScriptType_SPENDTAPROOT) {
|
||||
if (!tx_sign_bip340(node->private_key, sighash, out->signature.bytes,
|
||||
&out->signature.size)) {
|
||||
return false;
|
||||
}
|
||||
// Write length-prefixed empty scriptSig (1 byte).
|
||||
r += ser_length(0, out->ownership_proof.bytes + r);
|
||||
|
||||
// Write
|
||||
// 1. number of stack items (1 byte)
|
||||
// 2. signature length (1 byte)
|
||||
// 3. signature (64 bytes)
|
||||
r += serialize_p2tr_witness(out->signature.bytes, out->signature.size, 0,
|
||||
out->ownership_proof.bytes + r);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
out->ownership_proof.size = r;
|
||||
return true;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#define TX_OVERWINTERED 0x80000000
|
||||
|
||||
#define OWNERSHIP_ID_SIZE 32
|
||||
|
||||
enum {
|
||||
// Signature hash type with the same semantics as SIGHASH_ALL, but instead of
|
||||
// having to include the byte in the signature, it is implied.
|
||||
@ -102,6 +104,10 @@ bool tx_sign_bip340(const uint8_t *private_key, const uint8_t *hash,
|
||||
int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
|
||||
const HDNode *root, TxOutputType *in, TxOutputBinType *out,
|
||||
bool needs_confirm);
|
||||
int get_script_pubkey(const CoinInfo *coin, HDNode *node, bool has_multisig,
|
||||
const MultisigRedeemScriptType *multisig,
|
||||
InputScriptType script_type, uint8_t *script_pubkey,
|
||||
pb_size_t *script_pubkey_size);
|
||||
int fill_input_script_pubkey(const CoinInfo *coin, const HDNode *root,
|
||||
TxInputType *in);
|
||||
|
||||
@ -139,5 +145,12 @@ void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse);
|
||||
uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput);
|
||||
uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput);
|
||||
uint32_t tx_decred_witness_weight(const TxInputType *txinput);
|
||||
bool get_ownership_proof(const CoinInfo *coin, InputScriptType script_type,
|
||||
const HDNode *node, uint8_t flags,
|
||||
const uint8_t ownership_id[OWNERSHIP_ID_SIZE],
|
||||
const uint8_t *script_pubkey,
|
||||
size_t script_pubkey_size,
|
||||
const uint8_t *commitment_data,
|
||||
size_t commitment_data_size, OwnershipProof *out);
|
||||
|
||||
#endif
|
||||
|
@ -21,8 +21,6 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
pytestmark = pytest.mark.skip_t1
|
||||
|
||||
|
||||
def test_p2wpkh_ownership_id(client: Client):
|
||||
ownership_id = btc.get_ownership_id(
|
||||
|
@ -101,6 +101,14 @@
|
||||
"T1_bitcoin-test_getaddress_show.py::test_show_multisig_15": "40f652d0e899d528605f472f47ec6cb727ced8fe90588a8904b46ed39c2088e8",
|
||||
"T1_bitcoin-test_getaddress_show.py::test_show_multisig_3": "05e4e5cd014bf96373788e4563a48f5cdb4c54d358a88e8a29247c2825c5669e",
|
||||
"T1_bitcoin-test_getaddress_show.py::test_show_unrecognized_path": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_attack_ownership_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_confirm_ownership_proof": "7e6fcf4df33bf876103f0f8b547c0bd7e5babd34cfe5be43d62c08fbc67f525b",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_confirm_ownership_proof_with_data": "c0c6509ae54e7199cb457ababbc71cdb0ef1c53d535ba4b9fbf59db2cb5171cd",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_fake_ownership_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_p2tr_ownership_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_p2tr_ownership_proof": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_p2wpkh_ownership_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getownershipproof.py::test_p2wpkh_ownership_proof": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getpublickey.py::test_get_public_node[Bitcoin-76067358-path0-xpub6BiVtCpG9fQPx-40a56ca3": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getpublickey.py::test_get_public_node[Bitcoin-76067358-path1-xpub6BiVtCpG9fQQR-1abafc98": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"T1_bitcoin-test_getpublickey.py::test_get_public_node[Bitcoin-76067358-path2-xpub6FVDRC1jiWNTu-47a67414": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
|
Loading…
Reference in New Issue
Block a user