Skip to content

Commit

Permalink
refactor scanning, pub key compression, update test
Browse files Browse the repository at this point in the history
  • Loading branch information
notTanveer committed Jan 25, 2025
1 parent 5c02789 commit d858932
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 106 deletions.
29 changes: 3 additions & 26 deletions packages/core/src/scanning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,11 @@ export const scanOutputsWithTweak = (
const matches = new Map<string, Buffer>();
let n = 0;
let counterIncrement = 0;
const typedScanTweak = new Uint8Array(scanTweak);

if (
typedScanTweak.length === 33 &&
(typedScanTweak[0] === 0x02 || typedScanTweak[0] === 0x03)
) {
if (scanTweak.length === 33) {
// Use publicKeyTweakMul for compressed pubkey
const ecdhSecret = secp256k1.publicKeyTweakMul(
typedScanTweak,
scanTweak,
scanPrivateKey,
true,
);
Expand All @@ -146,28 +142,9 @@ export const scanOutputsWithTweak = (
);
n += counterIncrement;
} while (counterIncrement > 0 && outputs.length > 0);
} else if (typedScanTweak.length === 32) {
const ecdhSecret = secp256k1.privateKeyTweakMul(
scanPrivateKey,
typedScanTweak,
);
do {
const tweak = createTaggedHash(
'BIP0352/SharedSecret',
Buffer.concat([ecdhSecret, serialiseUint32(n)]),
);
counterIncrement = processTweak(
spendPublicKey,
tweak,
outputs,
matches,
labels,
);
n += counterIncrement;
} while (counterIncrement > 0 && outputs.length > 0);
} else {
throw new Error(
`Expected scanTweak to be either 32 bytes or a 33-byte compressed public key, got ${typedScanTweak.length}`,
`Expected scanTweak to be either 33-byte compressed public key, got ${scanTweak.length}`,
);
}

Expand Down
39 changes: 38 additions & 1 deletion packages/core/test/scanning.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LabelMap, scanOutputs } from '../src';
import { LabelMap, scanOutputs, scanOutputsWithTweak } from '../src';
import { Buffer } from 'buffer';
import { testData } from './fixtures/scanning';

Expand Down Expand Up @@ -33,4 +33,41 @@ describe('Scanning', () => {
);
},
);

it('should scan using tweak', async () => {
const scanPrivateKey = Buffer.from(
'38658693c017c46fd6b8bb94b8766c123cd5baf6026338305b6f59f82b36f9c0',
'hex',
);
const spendPublicKey = Buffer.from(
'02833085c9a716d36b467552c00d6aa8bd42e39adbe98b05bc203110177192f702',
'hex',
);
const tweak = Buffer.from(
'02ccd442a997b40661a9a1e233884986a9c970f5da3b68514c6ea2533b708e2ae1',
'hex',
);
const outputs = [
Buffer.from(
'025a90a5fad7ab4d32e41fd02de786f7af3ecbe85bd18784e51e89a97c8693ca3c',
'hex',
),
];
const expectedTweakHex =
'635aaddb7a1f7f64a6b78ddf47772ae987f6e29b79bb4eebbf1826f21af25e39';

const res = scanOutputsWithTweak(
scanPrivateKey,
spendPublicKey,
tweak,
outputs,
);

expect(res).toBeDefined();
expect(res.size).toBeGreaterThan(0);

const [[output, foundTweak]] = Array.from(res);
expect(output.toString()).toBe(outputs[0].toString());
expect(foundTweak.toString()).toBe(expectedTweakHex);
});
});
17 changes: 6 additions & 11 deletions packages/wallet/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,33 +342,28 @@ export class Wallet {
const matchedUTXOs: Coin[] = [];

for (const transaction of silentBlock.transactions) {
console.log('Processing transaction:', transaction.txid);
console.log('Outputs:', transaction.outputs);

const outputs = transaction.outputs;

if (outputs.length === 0) continue;

const outputPubKeys = outputs.map((output) =>
Buffer.from(output.pubKey, 'hex'),
);
Buffer.from('02' + output.pubKey, 'hex'),
);

const scanTweak = Buffer.from(transaction.scanTweak, 'hex');
console.log('scanTweak:', scanTweak.toString('hex'));

const matches = scanOutputsWithTweak(
scanPrivateKey,
spendPublicKey,
scanTweak,
outputPubKeys,
);
console.log('Matches found:', Array.from(matches.keys()));

if (matches.size === 0) continue;

for (const pubKeyHex of matches.keys()) {
const output = outputs.find(
(output) => output.pubKey === pubKeyHex,
(output) => output.pubKey === pubKeyHex.slice(2),
);
if (output) {
matchedUTXOs.push(
Expand Down
17 changes: 17 additions & 0 deletions packages/wallet/test/helpers/silent-block.fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const parsedSilentBlock = {
type: 0,
transactions: [
{
txid: '7d77c249a6ade81248e1a2ba28e1128e3beee0a3727d6cc0b1bed13e07f12147',
scanTweak:
'02ccd442a997b40661a9a1e233884986a9c970f5da3b68514c6ea2533b708e2ae1',
outputs: [
{
pubKey: '5a90a5fad7ab4d32e41fd02de786f7af3ecbe85bd18784e51e89a97c8693ca3c',
value: 4000000,
vout: 0,
},
],
},
],
};
73 changes: 5 additions & 68 deletions packages/wallet/test/wallet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,7 @@ import { BitcoinRpcClient } from './helpers/bitcoin-rpc-client';
import { Wallet } from '../src';
import { WalletDB } from '@silent-pay/level/src';
import { EsploraClient } from '@silent-pay/esplora/src';
// import { silentBlock } from '@silent-pay/core/test/fixtures/encoding.ts';

const silentBlock = [
{
parsedBlock: {
type: 0,
transactions: [
{
txid: 'd60cdcc1bdb78d44bf83e2ae0efa94bbb1187ba2397e4a40960d853b78e15b4f',
scanTweak:
'0381a4ef11a26108cf10d8c33305b77aec009beed82a77bb47b716a04ae9ae5bf1',
outputs: [
{
pubKey: 'ca80a975d09b84b385e39d779293121b9a7c80942d824d2496f098e3f077ced3',
value: 24771038,
vout: 1,
},
],
},
{
txid: 'cf655a07ed9b672ecf811ce8b3b69257eecaf0518edd025d7014d7c0177050cb',
scanTweak:
'03d87550a4ba8ed7dd37df7739a021d6d212e70e8a8e6e9e70249c13a11fd9f412',
outputs: [
{
pubKey: '5af9436eb515871413a913f24290ba003eab7a64b9c62cb72051599a383de8ff',
value: 26940,
vout: 0,
},
{
pubKey: 'e7a3ab33572f47b074878c749a5f9a5d7e026f10b6ffcab1388e9356c07828fa',
value: 4400,
vout: 1,
},
],
},
],
},
encodedBlockHex:
'0002d60cdcc1bdb78d44bf83e2ae0efa94bbb1187ba2397e4a40960d853b78e15b4f01000000000179f9deca80a975d09b84b385e39d779293121b9a7c80942d824d2496f098e3f077ced3000000010381a4ef11a26108cf10d8c33305b77aec009beed82a77bb47b716a04ae9ae5bf1cf655a07ed9b672ecf811ce8b3b69257eecaf0518edd025d7014d7c0177050cb02000000000000693c5af9436eb515871413a913f24290ba003eab7a64b9c62cb72051599a383de8ff000000000000000000001130e7a3ab33572f47b074878c749a5f9a5d7e026f10b6ffcab1388e9356c07828fa0000000103d87550a4ba8ed7dd37df7739a021d6d212e70e8a8e6e9e70249c13a11fd9f412',
},
{
parsedBlock: {
type: 0,
transactions: [],
},
encodedBlockHex: '0000',
},
];
import { parsedSilentBlock } from './helpers/silent-block.fixtures';

describe('Wallet', () => {
let wallet: Wallet;
Expand Down Expand Up @@ -212,7 +164,7 @@ describe('Wallet', () => {
throw new Error('Scan private key is undefined');

const matchedUTXOs = wallet.matchSilentBlockOutputs(
silentBlock[0].parsedBlock,
parsedSilentBlock,
scanKey.privateKey,
scanKey.publicKey,
spendKey.publicKey,
Expand All @@ -222,28 +174,13 @@ describe('Wallet', () => {
});

it('should scan a silent block and update UTXOs', async () => {
if (!silentBlock || !silentBlock[0]) {
throw new Error('Silent block fixture is not properly defined');
}

const block = silentBlock[0].parsedBlock;

await wallet.scanSilentBlock(block);
await wallet.scanSilentBlock(parsedSilentBlock);
const utxos = await walletDB.getUnspentCoins();
expect(utxos).toContainEqual(
expect.objectContaining({
txid: 'd60cdcc1bdb78d44bf83e2ae0efa94bbb1187ba2397e4a40960d853b78e15b4f',
vout: 1,
value: 24771038,
address: expect.any(String),
status: { isConfirmed: true },
}),
);
expect(utxos).toContainEqual(
expect.objectContaining({
txid: 'cf655a07ed9b672ecf811ce8b3b69257eecaf0518edd025d7014d7c0177050cb',
txid: '7d77c249a6ade81248e1a2ba28e1128e3beee0a3727d6cc0b1bed13e07f12147',
vout: 0,
value: 26940,
value: 4000000,
address: expect.any(String),
status: { isConfirmed: true },
}),
Expand Down

0 comments on commit d858932

Please sign in to comment.