Skip to content

Commit

Permalink
Add support to address reuse
Browse files Browse the repository at this point in the history
  • Loading branch information
ssantos21 committed May 25, 2024
1 parent 504bac9 commit fa96233
Show file tree
Hide file tree
Showing 26 changed files with 550 additions and 368 deletions.
271 changes: 150 additions & 121 deletions clients/nodejs/transfer_receive.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,80 @@ const execute = async (electrumClient, db, wallet_name) => {

const serverInfo = await utils.infoConfig(electrumClient);

let received_statechain_ids = [];
let uniqueAuthPubkeys = new Set();

for (let coin of wallet.coins) {
wallet.coins.forEach(coin => {
uniqueAuthPubkeys.add(coin.auth_pubkey);
});

if (coin.status != CoinStatus.INITIALISED) {
continue;
let encMsgsPerAuthPubkey = new Map();

for (let authPubkey of uniqueAuthPubkeys) {
try {
let encMessages = await getMsgAddr(authPubkey);
if (encMessages.length === 0) {
console.log("No messages");
continue;
}

encMsgsPerAuthPubkey.set(authPubkey, encMessages);
} catch (err) {
console.error(err);
}
}

// console.log("----\nuser_pubkey", coin.user_pubkey);
// console.log("auth_pubkey", coin.auth_pubkey);
// console.log("statechain_id", coin.statechain_id);
// console.log("coin.amount", coin.amount);
// console.log("coin.status", coin.status);
let receivedStatechainIds = [];

let encMessages = await get_msg_addr(coin.auth_pubkey);
let tempCoins = [...wallet.coins];
let tempActivities = [...wallet.activities];

if (encMessages.length == 0) {
continue;
}
for (let [authPubkey, encMessages] of encMsgsPerAuthPubkey.entries()) {

for (let encMessage of encMessages) {

const statechain_ids_added = await process_encrypted_message(electrumClient, db, coin, encMessages, wallet.network, serverInfo, wallet.activities);
received_statechain_ids = [...received_statechain_ids, ...statechain_ids_added];
let coin = tempCoins.find(coin => coin.auth_pubkey === authPubkey && coin.status === 'INITIALISED');

if (coin) {
try {
let statechainIdAdded = await processEncryptedMessage(electrumClient, db, coin, encMessage, wallet.network, serverInfo, tempActivities);

if (statechainIdAdded) {
receivedStatechainIds.push(statechainIdAdded);
}
} catch (error) {
console.error(`Error: ${error.message}`);
continue;
}

} else {
try {
let newCoin = await mercury_wasm.duplicateCoinToInitializedState(wallet, authPubkey);

if (newCoin) {
let statechainIdAdded = await processEncryptedMessage(electrumClient, db, newCoin, encMessage, wallet.network, serverInfo, tempActivities);

if (statechainIdAdded) {
tempCoins.push(newCoin);
receivedStatechainIds.push(statechainIdAdded);
}
}
} catch (error) {
console.error(`Error: ${error.message}`);
continue;
}
}
}
}

wallet.coins = [...tempCoins];
wallet.activities = [...tempActivities];

await sqlite_manager.updateWallet(db, wallet);

return received_statechain_ids;
return receivedStatechainIds;
}

const get_msg_addr = async (auth_pubkey) => {
const getMsgAddr = async (auth_pubkey) => {

const statechain_entity_url = config.get('statechainEntity');
const path = "transfer/get_msg_addr/";
Expand All @@ -75,149 +119,134 @@ const get_msg_addr = async (auth_pubkey) => {
return response.data.list_enc_transfer_msg;
}

const process_encrypted_message = async (electrumClient, db, coin, encMessages, network, serverInfo, activities) => {
const processEncryptedMessage = async (electrumClient, db, coin, encMessage, network, serverInfo, activities) => {
let clientAuthKey = coin.auth_privkey;
let newUserPubkey = coin.user_pubkey;

let statechain_ids_added = [];

for (let encMessage of encMessages) {
let transferMsg = mercury_wasm.decryptTransferMsg(encMessage, clientAuthKey);

let transferMsg = mercury_wasm.decryptTransferMsg(encMessage, clientAuthKey);
let tx0Outpoint = mercury_wasm.getTx0Outpoint(transferMsg.backup_transactions);

let tx0Outpoint = mercury_wasm.getTx0Outpoint(transferMsg.backup_transactions);
const tx0Hex = await getTx0(electrumClient, tx0Outpoint.txid);

const tx0Hex = await getTx0(electrumClient, tx0Outpoint.txid);
const isTransferSignatureValid = mercury_wasm.verifyTransferSignature(newUserPubkey, tx0Outpoint, transferMsg);

const isTransferSignatureValid = mercury_wasm.verifyTransferSignature(newUserPubkey, tx0Outpoint, transferMsg);
if (!isTransferSignatureValid) {
throw new Error("Invalid transfer signature");
}

const statechainInfo = await utils.getStatechainInfo(transferMsg.statechain_id);

if (!isTransferSignatureValid) {
console.error("Invalid transfer signature");
continue;
}

const statechainInfo = await utils.getStatechainInfo(transferMsg.statechain_id);
if (statechainInfo == null) {
throw new Error("Statechain info not found");
}

if (statechainInfo == null) {
console.error("Statechain info not found");
continue;
}
const isTx0OutputPubkeyValid = mercury_wasm.validateTx0OutputPubkey(statechainInfo.enclave_public_key, transferMsg, tx0Outpoint, tx0Hex, network);

const isTx0OutputPubkeyValid = mercury_wasm.validateTx0OutputPubkey(statechainInfo.enclave_public_key, transferMsg, tx0Outpoint, tx0Hex, network);
if (!isTx0OutputPubkeyValid) {
throw new Error("Invalid tx0 output pubkey");
}

if (!isTx0OutputPubkeyValid) {
console.error("Invalid tx0 output pubkey");
continue;
}
let latestBackupTxPaysToUserPubkey = mercury_wasm.verifyLatestBackupTxPaysToUserPubkey(transferMsg, newUserPubkey, network);

let latestBackupTxPaysToUserPubkey = mercury_wasm.verifyLatestBackupTxPaysToUserPubkey(transferMsg, newUserPubkey, network);
if (!latestBackupTxPaysToUserPubkey) {
throw new Error("Latest Backup Tx does not pay to the expected public key");
}

if (!latestBackupTxPaysToUserPubkey) {
console.error("Latest Backup Tx does not pay to the expected public key");
continue;
}
if (statechainInfo.num_sigs != transferMsg.backup_transactions.length) {
throw new Error("num_sigs is not correct");
}

let isTx0OutputUnspent = await verifyTx0OutputIsUnspentAndConfirmed(electrumClient, tx0Outpoint, tx0Hex, network);
if (!isTx0OutputUnspent.result) {
throw new Error("tx0 output is spent or not confirmed");
}

if (statechainInfo.num_sigs != transferMsg.backup_transactions.length) {
console.error("num_sigs is not correct");
continue;
}

let isTx0OutputUnspent = await verifyTx0OutputIsUnspentAndConfirmed(electrumClient, tx0Outpoint, tx0Hex, network);
if (!isTx0OutputUnspent.result) {
console.error("tx0 output is spent or not confirmed");
continue;
}
const currentFeeRateSatsPerByte = serverInfo.fee_rate_sats_per_byte;

const currentFeeRateSatsPerByte = serverInfo.fee_rate_sats_per_byte;
const feeRateTolerance = config.get('feeRateTolerance');

const feeRateTolerance = config.get('feeRateTolerance');
let previousLockTime = null;

let previousLockTime = null;
let sigSchemeValidation = true;

let sigSchemeValidation = true;
for (const [index, backupTx] of transferMsg.backup_transactions.entries()) {

for (const [index, backupTx] of transferMsg.backup_transactions.entries()) {
const isSignatureValid = mercury_wasm.verifyTransactionSignature(backupTx.tx, tx0Hex, feeRateTolerance, currentFeeRateSatsPerByte);

const isSignatureValid = mercury_wasm.verifyTransactionSignature(backupTx.tx, tx0Hex, feeRateTolerance, currentFeeRateSatsPerByte);
if (!isSignatureValid.result) {
console.error(`Invalid signature, ${isSignatureValid.result.msg}`);
sigSchemeValidation = false;
break;
}

if (!isSignatureValid.result) {
console.error(`Invalid signature, ${isSignatureValid.result.msg}`);
sigSchemeValidation = false;
break;
}
const currentStatechainInfo = statechainInfo.statechain_info[index];

const currentStatechainInfo = statechainInfo.statechain_info[index];
const isBlindedMusigSchemeValid = mercury_wasm.verifyBlindedMusigScheme(backupTx, tx0Hex, currentStatechainInfo);

const isBlindedMusigSchemeValid = mercury_wasm.verifyBlindedMusigScheme(backupTx, tx0Hex, currentStatechainInfo);
if (!isBlindedMusigSchemeValid.result) {
console.error(`Invalid musig scheme, ${isBlindedMusigSchemeValid.result.msg}`);
sigSchemeValidation = false;
break;
}

if (!isBlindedMusigSchemeValid.result) {
console.error(`Invalid musig scheme, ${isBlindedMusigSchemeValid.result.msg}`);
if (previousLockTime != null) {
let currentLockTime = mercury_wasm.getBlockheight(backupTx);
if ((previousLockTime - currentLockTime) != serverInfo.interval) {
console.error("interval is not correct");
sigSchemeValidation = false;
break;
}

if (previousLockTime != null) {
let currentLockTime = mercury_wasm.getBlockheight(backupTx);
if ((previousLockTime - currentLockTime) != serverInfo.interval) {
console.error("interval is not correct");
sigSchemeValidation = false;
break;
}
}

previousLockTime = mercury_wasm.getBlockheight(backupTx);
}

if (!sigSchemeValidation) {
console.error("Signature scheme validation failed");
continue;
}

const transferReceiverRequestPayload = mercury_wasm.createTransferReceiverRequestPayload(statechainInfo, transferMsg, coin);

let signedStatechainIdForUnlock = mercury_wasm.signMessage(transferMsg.statechain_id, coin);

await unlockStatecoin(transferMsg.statechain_id, signedStatechainIdForUnlock, coin.auth_pubkey);
previousLockTime = mercury_wasm.getBlockheight(backupTx);
}

let serverPublicKeyHex = "";
if (!sigSchemeValidation) {
throw new Error("Signature scheme validation failed");
}

try {
serverPublicKeyHex = await sendTransferReceiverRequestPayload(transferReceiverRequestPayload);
} catch (error) {
console.error(error);
continue;
}
const transferReceiverRequestPayload = mercury_wasm.createTransferReceiverRequestPayload(statechainInfo, transferMsg, coin);

let newKeyInfo = mercury_wasm.getNewKeyInfo(serverPublicKeyHex, coin, transferMsg.statechain_id, tx0Outpoint, tx0Hex, network);
let signedStatechainIdForUnlock = mercury_wasm.signMessage(transferMsg.statechain_id, coin);

coin.server_pubkey = serverPublicKeyHex;
coin.aggregated_pubkey = newKeyInfo.aggregate_pubkey;
coin.aggregated_address = newKeyInfo.aggregate_address;
coin.statechain_id = transferMsg.statechain_id;
coin.signed_statechain_id = newKeyInfo.signed_statechain_id;
coin.amount = newKeyInfo.amount;
coin.utxo_txid = tx0Outpoint.txid;
coin.utxo_vout = tx0Outpoint.vout;
coin.locktime = previousLockTime;
coin.status = isTx0OutputUnspent.status;
await unlockStatecoin(transferMsg.statechain_id, signedStatechainIdForUnlock, coin.auth_pubkey);

let utxo = `${tx0Outpoint.txid}:${tx0Outpoint.vout}`;
let serverPublicKeyHex = "";

let activity = {
utxo: utxo,
amount: newKeyInfo.amount,
action: "Receive",
date: new Date().toISOString()
};
try {
serverPublicKeyHex = await sendTransferReceiverRequestPayload(transferReceiverRequestPayload);
} catch (error) {
throw new Error(error);
}

activities.push(activity);
let newKeyInfo = mercury_wasm.getNewKeyInfo(serverPublicKeyHex, coin, transferMsg.statechain_id, tx0Outpoint, tx0Hex, network);

coin.server_pubkey = serverPublicKeyHex;
coin.aggregated_pubkey = newKeyInfo.aggregate_pubkey;
coin.aggregated_address = newKeyInfo.aggregate_address;
coin.statechain_id = transferMsg.statechain_id;
coin.signed_statechain_id = newKeyInfo.signed_statechain_id;
coin.amount = newKeyInfo.amount;
coin.utxo_txid = tx0Outpoint.txid;
coin.utxo_vout = tx0Outpoint.vout;
coin.locktime = previousLockTime;
coin.status = isTx0OutputUnspent.status;

let utxo = `${tx0Outpoint.txid}:${tx0Outpoint.vout}`;

let activity = {
utxo: utxo,
amount: newKeyInfo.amount,
action: "Receive",
date: new Date().toISOString()
};

statechain_ids_added.push(transferMsg.statechain_id);
activities.push(activity);

await sqlite_manager.insertOrUpdateBackupTxs(db, transferMsg.statechain_id, transferMsg.backup_transactions);
}
await sqlite_manager.insertOrUpdateBackupTxs(db, transferMsg.statechain_id, transferMsg.backup_transactions);

return statechain_ids_added;
return transferMsg.statechain_id;
}

const getTx0 = async (electrumClient, tx0_txid) => {
Expand Down
2 changes: 1 addition & 1 deletion clients/nodejs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const getNetwork = (wallet_network) => {
return bitcoinjs_lib.networks.testnet;
case "regtest":
return bitcoinjs_lib.networks.regtest;
case "mainnet":
case "bitcoin":
return bitcoinjs_lib.networks.bitcoin;
default:
throw new Error("Unknown network");
Expand Down
Loading

0 comments on commit fa96233

Please sign in to comment.