Skip to content

Commit

Permalink
feat: modify proof of reserve calculation (#15)
Browse files Browse the repository at this point in the history
* feat: modify por calculation, add tests

* feat: add unit tests for bitcoin functions related to por calculations
  • Loading branch information
Polybius93 authored Jul 12, 2024
1 parent 04bdc9d commit a20b638
Show file tree
Hide file tree
Showing 13 changed files with 592 additions and 173 deletions.
22 changes: 13 additions & 9 deletions src/functions/bitcoin/bitcoin-functions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { hexToBytes } from '@noble/hashes/utils';
import {
Address,
OutScript,
Expand Down Expand Up @@ -420,17 +421,20 @@ export function getInputByPaymentTypeArray(
});
}

export function getValueMatchingOutputFromTransaction(
export function getScriptMatchingOutputFromTransaction(
bitcoinTransaction: BitcoinTransaction,
bitcoinValue: number
): BitcoinTransactionVectorOutput {
const valueMatchingTransactionOutput = bitcoinTransaction.vout.find(
output => output.value === bitcoinValue
script: Uint8Array
): BitcoinTransactionVectorOutput | undefined {
return bitcoinTransaction.vout.find(output =>
validateScript(script, hexToBytes(output.scriptpubkey))
);
}

export function validateScript(script: Uint8Array, outputScript: Uint8Array): boolean {
return (
outputScript.length === script.length &&
outputScript.every((value, index) => value === script[index])
);
if (!valueMatchingTransactionOutput) {
throw new Error('Could not find Value matching Input in Transaction');
}
return valueMatchingTransactionOutput;
}

export function getInputIndicesByScript(script: Uint8Array, transaction: Transaction): number[] {
Expand Down
67 changes: 67 additions & 0 deletions src/functions/proof-of-reserve/proof-of-reserve-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Network } from 'bitcoinjs-lib';
import { RawVault } from 'src/models/ethereum-models.js';

import {
createTaprootMultisigPayment,
deriveUnhardenedPublicKey,
getScriptMatchingOutputFromTransaction,
getUnspendableKeyCommittedToUUID,
} from '../bitcoin/bitcoin-functions.js';
import {
checkBitcoinTransactionConfirmations,
fetchBitcoinTransaction,
} from '../bitcoin/bitcoin-request-functions.js';

export async function verifyVaultDeposit(
vault: RawVault,
attestorGroupPublicKey: Buffer,
bitcoinBlockchainBlockHeight: number,
bitcoinBlockchainAPI: string,
bitcoinNetwork: Network
): Promise<boolean> {
try {
const fundingTransaction = await fetchBitcoinTransaction(
vault.fundingTxId,
bitcoinBlockchainAPI
);

const isFundingTransactionConfirmed = await checkBitcoinTransactionConfirmations(
fundingTransaction,
bitcoinBlockchainBlockHeight
);

if (!isFundingTransactionConfirmed) {
return false;
}

const unspendableKeyCommittedToUUID = deriveUnhardenedPublicKey(
getUnspendableKeyCommittedToUUID(vault.uuid, bitcoinNetwork),
bitcoinNetwork
);

const taprootMultisigPayment = createTaprootMultisigPayment(
unspendableKeyCommittedToUUID,
attestorGroupPublicKey,
Buffer.from(vault.taprootPubKey, 'hex'),
bitcoinNetwork
);

const vaultTransactionOutput = getScriptMatchingOutputFromTransaction(
fundingTransaction,
taprootMultisigPayment.script
);

if (!vaultTransactionOutput) {
return false;
}

if (vaultTransactionOutput.value !== vault.valueLocked.toNumber()) {
return false;
}

return true;
} catch (error) {
console.log(`Error verifying Vault Deposit: ${error}`);
return false;
}
}
68 changes: 8 additions & 60 deletions src/proof-of-reserve-handlers/proof-of-reserve-handler.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { hex } from '@scure/base';
import { Network } from 'bitcoinjs-lib';

import {
createTaprootMultisigPayment,
deriveUnhardenedPublicKey,
getUnspendableKeyCommittedToUUID,
getValueMatchingOutputFromTransaction,
} from '../functions/bitcoin/bitcoin-functions.js';
import {
checkBitcoinTransactionConfirmations,
fetchBitcoinBlockchainBlockHeight,
fetchBitcoinTransaction,
} from '../functions/bitcoin/bitcoin-request-functions.js';
import { deriveUnhardenedPublicKey } from '../functions/bitcoin/bitcoin-functions.js';
import { fetchBitcoinBlockchainBlockHeight } from '../functions/bitcoin/bitcoin-request-functions.js';
import { verifyVaultDeposit } from '../functions/proof-of-reserve/proof-of-reserve-functions.js';
import { RawVault } from '../models/ethereum-models.js';
import { compareUint8Arrays } from '../utilities/index.js';

export class ProofOfReserveHandler {
private bitcoinBlockchainAPI: string;
Expand All @@ -30,51 +20,6 @@ export class ProofOfReserveHandler {
this.attestorGroupPublicKey = attestorGroupPublicKey;
}

async verifyVaultDeposit(
vault: RawVault,
attestorGroupPublicKey: Buffer,
bitcoinBlockchainBlockHeight: number
): Promise<boolean> {
try {
const fundingTransaction = await fetchBitcoinTransaction(
vault.fundingTxId,
this.bitcoinBlockchainAPI
);
const isFundingTransactionConfirmed = await checkBitcoinTransactionConfirmations(
fundingTransaction,
bitcoinBlockchainBlockHeight
);

if (!isFundingTransactionConfirmed) {
return false;
}

const vaultTransactionOutput = getValueMatchingOutputFromTransaction(
fundingTransaction,
vault.valueLocked.toNumber()
);

const unspendableKeyCommittedToUUID = deriveUnhardenedPublicKey(
getUnspendableKeyCommittedToUUID(vault.uuid, this.bitcoinNetwork),
this.bitcoinNetwork
);
const taprootMultisigPayment = createTaprootMultisigPayment(
unspendableKeyCommittedToUUID,
attestorGroupPublicKey,
Buffer.from(vault.taprootPubKey, 'hex'),
this.bitcoinNetwork
);

return compareUint8Arrays(
taprootMultisigPayment.script,
hex.decode(vaultTransactionOutput.scriptpubkey)
);
} catch (error) {
console.error(`Error verifying Vault Deposit: ${error}`);
return false;
}
}

async calculateProofOfReserve(vaults: RawVault[]): Promise<number> {
const bitcoinBlockchainBlockHeight = await fetchBitcoinBlockchainBlockHeight(
this.bitcoinBlockchainAPI
Expand All @@ -84,12 +29,15 @@ export class ProofOfReserveHandler {
this.attestorGroupPublicKey,
this.bitcoinNetwork
);

const verifiedDeposits = await Promise.all(
vaults.map(async vault => {
return (await this.verifyVaultDeposit(
return (await verifyVaultDeposit(
vault,
derivedAttestorGroupPublicKey,
bitcoinBlockchainBlockHeight
bitcoinBlockchainBlockHeight,
this.bitcoinBlockchainAPI,
this.bitcoinNetwork
)) === true
? vault.valueLocked.toNumber()
: 0;
Expand Down
17 changes: 17 additions & 0 deletions tests/mocks/api.test.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const TEST_REGTEST_BITCOIN_BLOCKCHAIN_API = 'https://devnet.dlc.link/electrs';
export const TEST_TESTNET_BITCOIN_BLOCKCHAIN_API = 'https://testnet.dlc.link/electrs';

export const TEST_BITCOIN_BLOCKCHAIN_FEE_RECOMMENDATION_API =
'https://devnet.dlc.link/electrs/fee-estimates';

export const TEST_REGTEST_ATTESTOR_APIS = [
'https://devnet.dlc.link/attestor-1',
'https://devnet.dlc.link/attestor-2',
'https://devnet.dlc.link/attestor-3',
];

export const TEST_ETHEREUM_NODE_API = 'https://sepolia-rollup.arbitrum.io/rpc';
export const TEST_ETHEREUM_READ_ONLY_NODE_API = 'https://sepolia-rollup.arbitrum.io/rpc';

export const TEST_ETHEREUM_GITHUB_DEPLOYMENT_PLAN_ROOT_URL =
'https://raw.githubusercontent.com/DLC-link/dlc-solidity';
8 changes: 8 additions & 0 deletions tests/mocks/attestor.test.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const TEST_REGTEST_ATTESTOR_EXTENDED_GROUP_PUBLIC_KEY_1 =
'tpubDDqN2CmTDKaGeqXMayfCZEvjZqntifi4r1ztmRWsGuE1VE4bosR3mBKQwVaCxZcmg8R1nHDMDzDmzjoccBMgwZV1hhz51tAXVnhjABCQcwA';

export const TEST_TESTNET_ATTESTOR_EXTENDED_GROUP_PUBLIC_KEY_1 =
'tpubDDRekL64eJJav32TLhNhG59qra7wAMaei8YMGXNiJE8ksdYrKgvaFM1XG6JrSt31W97XryScrX37RUEujjZT4qScNf8Zu1JxWj4VYkwz4rU';

export const TEST_TESTNET_ATTESTOR_UNHARDENED_DERIVED_PUBLIC_KEY_1 =
'027eda4d625f781dcc98bf68901360fdaaacce8ed466096c1dfe4865209b28c058';
Loading

0 comments on commit a20b638

Please sign in to comment.