Skip to content

Commit 08edd24

Browse files
authored
Add support for Discount CT (#120)
1 parent f582872 commit 08edd24

File tree

5 files changed

+145
-15
lines changed

5 files changed

+145
-15
lines changed

src/transaction.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export declare class Transaction {
4545
addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer, issuance?: Issuance): number;
4646
addOutput(scriptPubKey: Buffer, value: Buffer, asset: Buffer, nonce: Buffer, rangeProof?: Buffer, surjectionProof?: Buffer): number;
4747
hasWitnesses(): boolean;
48-
weight(): number;
49-
virtualSize(): number;
48+
weight(discountCT?: boolean): number;
49+
virtualSize(discountCT?: boolean): number;
5050
byteLength(_ALLOW_WITNESS?: boolean): number;
5151
clone(): Transaction;
5252
/**

src/transaction.js

+36-6
Original file line numberDiff line numberDiff line change
@@ -280,14 +280,44 @@ class Transaction {
280280
})
281281
);
282282
}
283-
weight() {
284-
const base = this.__byteLength(false);
285-
const total = this.__byteLength(true);
286-
return base * (WITNESS_SCALE_FACTOR - 1) + total;
283+
weight(discountCT = true) {
284+
const base = this.__byteLength(false, false);
285+
const total = this.__byteLength(true, false);
286+
let weight = base * (WITNESS_SCALE_FACTOR - 1) + total;
287+
if (!discountCT) {
288+
return weight;
289+
}
290+
for (const txOut of this.outs) {
291+
let witnessWeight = 0;
292+
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
293+
witnessWeight += txOut.rangeProof.length;
294+
witnessWeight += bufferutils_1.varuint.encodingLength(
295+
txOut.rangeProof.length,
296+
);
297+
}
298+
if (
299+
txOut.surjectionProof !== undefined &&
300+
txOut.surjectionProof.length > 0
301+
) {
302+
witnessWeight += txOut.surjectionProof.length;
303+
witnessWeight += bufferutils_1.varuint.encodingLength(
304+
txOut.surjectionProof.length,
305+
);
306+
}
307+
// Explicit transactions have 1 byte for each empty proof
308+
weight -= Math.max(witnessWeight - 2, 0);
309+
// When the output has a range proof, it is safe to assume that the nonce and value are confidential
310+
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
311+
weight -= (33 - 9) * 4;
312+
weight -= (33 - 1) * 4;
313+
}
314+
}
315+
return weight;
287316
}
288-
virtualSize() {
317+
virtualSize(discountCT = true) {
289318
const vsize =
290-
(this.weight() + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
319+
(this.weight(discountCT) + WITNESS_SCALE_FACTOR - 1) /
320+
WITNESS_SCALE_FACTOR;
291321
return Math.floor(vsize);
292322
}
293323
byteLength(_ALLOW_WITNESS) {

test/fixtures/transaction.json

+51-1
Large diffs are not rendered by default.

test/transaction.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,22 @@ describe('Transaction', () => {
2626
});
2727
}
2828
});
29+
30+
describe('calculates weight and virtualSize', () => {
31+
const fixtures = jsonFixture.discountCT;
32+
for (const test of fixtures) {
33+
it(test.description, () => {
34+
const tx = Transaction.fromHex(test.txHex);
35+
assert.strictEqual(tx.weight(false), test.weight);
36+
assert.strictEqual(tx.virtualSize(false), test.vSize);
37+
38+
assert.strictEqual(tx.weight(true), test.discountWeight);
39+
assert.strictEqual(tx.virtualSize(true), test.discountVSize);
40+
41+
// Check that the default behavior is discounted CT
42+
assert.strictEqual(tx.weight(), test.discountWeight);
43+
assert.strictEqual(tx.virtualSize(), test.discountVSize);
44+
});
45+
}
46+
});
2947
});

ts_src/transaction.ts

+38-6
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,47 @@ export class Transaction {
314314
);
315315
}
316316

317-
weight(): number {
318-
const base = this.__byteLength(false);
319-
const total = this.__byteLength(true);
320-
return base * (WITNESS_SCALE_FACTOR - 1) + total;
317+
weight(discountCT = true): number {
318+
const base = this.__byteLength(false, false);
319+
const total = this.__byteLength(true, false);
320+
let weight = base * (WITNESS_SCALE_FACTOR - 1) + total;
321+
322+
if (!discountCT) {
323+
return weight;
324+
}
325+
326+
for (const txOut of this.outs) {
327+
let witnessWeight = 0;
328+
329+
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
330+
witnessWeight += txOut.rangeProof.length;
331+
witnessWeight += varuint.encodingLength(txOut.rangeProof.length);
332+
}
333+
if (
334+
txOut.surjectionProof !== undefined &&
335+
txOut.surjectionProof.length > 0
336+
) {
337+
witnessWeight += txOut.surjectionProof.length;
338+
witnessWeight += varuint.encodingLength(txOut.surjectionProof.length);
339+
}
340+
341+
// Explicit transactions have 1 byte for each empty proof
342+
weight -= Math.max(witnessWeight - 2, 0);
343+
344+
// When the output has a range proof, it is safe to assume that the nonce and value are confidential
345+
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
346+
weight -= (33 - 9) * 4;
347+
weight -= (33 - 1) * 4;
348+
}
349+
}
350+
351+
return weight;
321352
}
322353

323-
virtualSize(): number {
354+
virtualSize(discountCT = true): number {
324355
const vsize =
325-
(this.weight() + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
356+
(this.weight(discountCT) + WITNESS_SCALE_FACTOR - 1) /
357+
WITNESS_SCALE_FACTOR;
326358
return Math.floor(vsize);
327359
}
328360

0 commit comments

Comments
 (0)