Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Discount CT #120

Merged
merged 1 commit into from
Oct 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/transaction.d.ts
Original file line number Diff line number Diff line change
@@ -45,8 +45,8 @@ export declare class Transaction {
addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer, issuance?: Issuance): number;
addOutput(scriptPubKey: Buffer, value: Buffer, asset: Buffer, nonce: Buffer, rangeProof?: Buffer, surjectionProof?: Buffer): number;
hasWitnesses(): boolean;
weight(): number;
virtualSize(): number;
weight(discountCT?: boolean): number;
virtualSize(discountCT?: boolean): number;
byteLength(_ALLOW_WITNESS?: boolean): number;
clone(): Transaction;
/**
42 changes: 36 additions & 6 deletions src/transaction.js
Original file line number Diff line number Diff line change
@@ -280,14 +280,44 @@ class Transaction {
})
);
}
weight() {
const base = this.__byteLength(false);
const total = this.__byteLength(true);
return base * (WITNESS_SCALE_FACTOR - 1) + total;
weight(discountCT = true) {
const base = this.__byteLength(false, false);
const total = this.__byteLength(true, false);
let weight = base * (WITNESS_SCALE_FACTOR - 1) + total;
if (!discountCT) {
return weight;
}
for (const txOut of this.outs) {
let witnessWeight = 0;
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
witnessWeight += txOut.rangeProof.length;
witnessWeight += bufferutils_1.varuint.encodingLength(
txOut.rangeProof.length,
);
}
if (
txOut.surjectionProof !== undefined &&
txOut.surjectionProof.length > 0
) {
witnessWeight += txOut.surjectionProof.length;
witnessWeight += bufferutils_1.varuint.encodingLength(
txOut.surjectionProof.length,
);
}
// Explicit transactions have 1 byte for each empty proof
weight -= Math.max(witnessWeight - 2, 0);
// When the output has a range proof, it is safe to assume that the nonce and value are confidential
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
weight -= (33 - 9) * 4;
weight -= (33 - 1) * 4;
}
}
return weight;
}
virtualSize() {
virtualSize(discountCT = true) {
const vsize =
(this.weight() + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
(this.weight(discountCT) + WITNESS_SCALE_FACTOR - 1) /
WITNESS_SCALE_FACTOR;
return Math.floor(vsize);
}
byteLength(_ALLOW_WITNESS) {
52 changes: 51 additions & 1 deletion test/fixtures/transaction.json

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions test/transaction.spec.ts
Original file line number Diff line number Diff line change
@@ -26,4 +26,22 @@ describe('Transaction', () => {
});
}
});

describe('calculates weight and virtualSize', () => {
const fixtures = jsonFixture.discountCT;
for (const test of fixtures) {
it(test.description, () => {
const tx = Transaction.fromHex(test.txHex);
assert.strictEqual(tx.weight(false), test.weight);
assert.strictEqual(tx.virtualSize(false), test.vSize);

assert.strictEqual(tx.weight(true), test.discountWeight);
assert.strictEqual(tx.virtualSize(true), test.discountVSize);

// Check that the default behavior is discounted CT
assert.strictEqual(tx.weight(), test.discountWeight);
assert.strictEqual(tx.virtualSize(), test.discountVSize);
});
}
});
});
44 changes: 38 additions & 6 deletions ts_src/transaction.ts
Original file line number Diff line number Diff line change
@@ -314,15 +314,47 @@ export class Transaction {
);
}

weight(): number {
const base = this.__byteLength(false);
const total = this.__byteLength(true);
return base * (WITNESS_SCALE_FACTOR - 1) + total;
weight(discountCT = true): number {
const base = this.__byteLength(false, false);
const total = this.__byteLength(true, false);
let weight = base * (WITNESS_SCALE_FACTOR - 1) + total;

if (!discountCT) {
return weight;
}

for (const txOut of this.outs) {
let witnessWeight = 0;

if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
witnessWeight += txOut.rangeProof.length;
witnessWeight += varuint.encodingLength(txOut.rangeProof.length);
}
if (
txOut.surjectionProof !== undefined &&
txOut.surjectionProof.length > 0
) {
witnessWeight += txOut.surjectionProof.length;
witnessWeight += varuint.encodingLength(txOut.surjectionProof.length);
}

// Explicit transactions have 1 byte for each empty proof
weight -= Math.max(witnessWeight - 2, 0);

// When the output has a range proof, it is safe to assume that the nonce and value are confidential
if (txOut.rangeProof !== undefined && txOut.rangeProof.length > 0) {
weight -= (33 - 9) * 4;
weight -= (33 - 1) * 4;
}
}

return weight;
}

virtualSize(): number {
virtualSize(discountCT = true): number {
const vsize =
(this.weight() + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
(this.weight(discountCT) + WITNESS_SCALE_FACTOR - 1) /
WITNESS_SCALE_FACTOR;
return Math.floor(vsize);
}