From 9dae7020c8b8a4b3cf9676a073cbb0de5cbd21bc Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Tue, 6 Jul 2021 13:56:32 -0300 Subject: [PATCH] Liquid support for unblinding transactions. (#588) * Add docker file to generate wallycore wasm js lib. * Add unblinded liquid transactions. * Add background to unblided transactions. * Check liquid network to try to unblind tx. Ww don't want to try to unblind transactions in other networks. Co-authored-by: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> * Delete libwally-core dockerfile. * Delete wallycore.html. * Fix validation unblind tx. Fix lint. Add errorUnblinded. Add vin.prevout unblinded tx. * Add e2e testing to liquids unblinded tx. * Load libwally.js dynamically. * Fix table size. * Add Blockstream License to libwally and wallycore. Co-authored-by: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> --- .../cypress/integration/liquid/liquid.spec.ts | 89 +- .../app/components/transaction/libwally.js | 184 ++++ .../transaction/transaction.component.html | 48 +- .../transaction/transaction.component.ts | 393 ++++++--- .../transactions-list.component.html | 18 +- .../transactions-list.component.scss | 55 +- .../transactions-list.component.ts | 1 + .../src/app/interfaces/electrs.interface.ts | 2 + frontend/src/resources/wallycore/wallycore.js | 802 ++++++++++++++++++ .../src/resources/wallycore/wallycore.wasm | Bin 0 -> 135961 bytes 10 files changed, 1408 insertions(+), 184 deletions(-) create mode 100644 frontend/src/app/components/transaction/libwally.js create mode 100644 frontend/src/resources/wallycore/wallycore.js create mode 100755 frontend/src/resources/wallycore/wallycore.wasm diff --git a/frontend/cypress/integration/liquid/liquid.spec.ts b/frontend/cypress/integration/liquid/liquid.spec.ts index c1d1c1a02..38abe6bb4 100644 --- a/frontend/cypress/integration/liquid/liquid.spec.ts +++ b/frontend/cypress/integration/liquid/liquid.spec.ts @@ -44,38 +44,77 @@ describe('Liquid', () => { }); describe('assets', () => { - it('shows the assets screen', () => { - cy.visit('/liquid'); - cy.get('li:nth-of-type(5) > a').click().then(() => { - cy.get('table tr').should('have.length', 5); - }); - }); - - it('allows searching assets', () => { - cy.visit('/liquid'); - cy.get('li:nth-of-type(5) > a').click().then(() => { - cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => { - cy.get('table tr').should('have.length', 1); + it('shows the assets screen', () => { + cy.visit('/liquid'); + cy.get('li:nth-of-type(5) > a').click().then(() => { + cy.get('table tr').should('have.length', 5); }); }); - }); - it('shows a specific asset ID', () => { - cy.visit('/liquid'); - cy.get('li:nth-of-type(5) > a').click().then(() => { - cy.get('.container-xl input').click().type('Liquid CAD').then(() => { - cy.get('table tr td:nth-of-type(4) a').click(); + it('allows searching assets', () => { + cy.visit('/liquid'); + cy.get('li:nth-of-type(5) > a').click().then(() => { + cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => { + cy.get('table tr').should('have.length', 1); + }); }); }); - }); - it('shows a specific asset issuance TX', () => { - cy.visit('/liquid'); - cy.get('li:nth-of-type(5) > a').click().then(() => { - cy.get('.container-xl input').click().type('Liquid CAD').then(() => { - cy.get('table tr td:nth-of-type(5) a').click(); + it('shows a specific asset ID', () => { + cy.visit('/liquid'); + cy.get('li:nth-of-type(5) > a').click().then(() => { + cy.get('.container-xl input').click().type('Liquid CAD').then(() => { + cy.get('table tr td:nth-of-type(4) a').click(); + }); + }); + }); + + it('shows a specific asset issuance TX', () => { + cy.visit('/liquid'); + cy.get('li:nth-of-type(5) > a').click().then(() => { + cy.get('.container-xl input').click().type('Liquid CAD').then(() => { + cy.get('table tr td:nth-of-type(5) a').click(); + }); }); }); - }); }); + + + describe('unblinded TX', () => { + it('show unblinded TX', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a'); + cy.get('#table-tx-vin tr').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); + }); + + it('show empty unblinded TX', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded='); + cy.get('#table-tx-vin tr').should('have.class', ''); + cy.get('#table-tx-vout tr').should('have.class', ''); + }); + + it('show invalid unblinded TX hex', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123'); + cy.get('#table-tx-vin tr').should('have.class', ''); + cy.get('#table-tx-vout tr').should('have.class', ''); + cy.get('.error-unblinded' ).contains('Error: Invalid blinding data (invalid hex)'); + }); + + it('show first unblinded vout', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc'); + cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox'); + }); + + it('show second unblinded vout', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a'); + cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); + }); + + it('show invalid error unblinded TX', () => { + cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c'); + cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); + cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.'); + }); + }); + }); diff --git a/frontend/src/app/components/transaction/libwally.js b/frontend/src/app/components/transaction/libwally.js new file mode 100644 index 000000000..f72cbfc00 --- /dev/null +++ b/frontend/src/app/components/transaction/libwally.js @@ -0,0 +1,184 @@ +/* +The MIT License (MIT) + +Copyright 2021 Blockstream Corp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +const WALLY_OK = 0, + ASSET_COMMITMENT_LEN = 33, + ASSET_GENERATOR_LEN = 33, + ASSET_TAG_LEN = 32, + BLINDING_FACTOR_LEN = 32; + +const WASM_URL = `./resources/wallycore/wallycore.js`; + +let load_promise, Module; +export function load() { + return ( + load_promise || + (load_promise = new Promise((resolve, reject) => { + const script = document.createElement("script"); + script.src = WASM_URL; + script.addEventListener("error", reject); + script.addEventListener("load", () => + InitWally().then((module) => { + Module = module; + resolve(); + }, reject) + ); + document.body.appendChild(script); + })) + ); +} + +// Simple wrapper to execute both asset_generator_from_bytes and asset_value_commitment, +// with hex conversions +export function generate_commitments( + value, + asset_hex, + value_blinder_hex, + asset_blinder_hex +) { + const asset = parseHex(asset_hex, ASSET_TAG_LEN), + value_blinder = parseHex(value_blinder_hex, BLINDING_FACTOR_LEN), + asset_blinder = parseHex(asset_blinder_hex, BLINDING_FACTOR_LEN); + + const asset_commitment = asset_generator_from_bytes(asset, asset_blinder), + value_commitment = asset_value_commitment( + value, + value_blinder, + asset_commitment + ); + + return { + asset_commitment: encodeHex(asset_commitment), + value_commitment: encodeHex(value_commitment), + }; +} + +export function asset_generator_from_bytes(asset, asset_blinder) { + const asset_commitment_ptr = Module._malloc(ASSET_GENERATOR_LEN); + checkCode( + Module.ccall( + "wally_asset_generator_from_bytes", + "number", + ["array", "number", "array", "number", "number", "number"], + [ + asset, + asset.length, + asset_blinder, + asset_blinder.length, + asset_commitment_ptr, + ASSET_GENERATOR_LEN, + ] + ) + ); + + const asset_commitment = readBytes(asset_commitment_ptr, ASSET_GENERATOR_LEN); + Module._free(asset_commitment_ptr); + return asset_commitment; +} + +export function asset_value_commitment(value, value_blinder, asset_commitment) { + // Emscripten transforms int64 function arguments into two int32 arguments, see: + // https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-pass-int64-t-and-uint64-t-values-from-js-into-wasm-functions + const [value_lo, value_hi] = split_int52_lo_hi(value); + + const value_commitment_ptr = Module._malloc(ASSET_COMMITMENT_LEN); + checkCode( + Module.ccall( + "wally_asset_value_commitment", + "number", + [ + "number", + "number", + "array", + "number", + "array", + "number", + "number", + "number", + ], + [ + value_lo, + value_hi, + value_blinder, + value_blinder.length, + asset_commitment, + asset_commitment.length, + value_commitment_ptr, + ASSET_COMMITMENT_LEN, + ] + ) + ); + + const value_commitment = readBytes( + value_commitment_ptr, + ASSET_COMMITMENT_LEN + ); + Module._free(value_commitment_ptr); + return value_commitment; +} + +function checkCode(code) { + if (code != WALLY_OK) throw new Error(`libwally failed with code ${code}`); +} + +function readBytes(ptr, size) { + const bytes = new Uint8Array(size); + for (let i = 0; i < size; i++) bytes[i] = Module.getValue(ptr + i, "i8"); + return bytes; +} + +// Split a 52-bit JavaScript number into two 32-bits numbers for the low and high bits +// https://stackoverflow.com/a/19274574 +function split_int52_lo_hi(i) { + let lo = i | 0; + if (lo < 0) lo += 4294967296; + + let hi = i - lo; + hi /= 4294967296; + + if (hi < 0 || hi >= 1048576) throw new Error("not an int52: " + i); + + return [lo, hi]; +} + +function encodeHex(bytes) { + // return Buffer.from(bytes).toString("hex"); + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +} + +// Parse hex string encoded in *reverse* +function parseHex(str, expected_size) { + if (!/^([0-9a-f]{2})+$/.test(str)) + throw new Error("Invalid blinders (invalid hex)"); + if (str.length != expected_size * 2) + throw new Error("Invalid blinders (invalid length)"); + return new Uint8Array( + str + .match(/.{2}/g) + .map((hex_byte) => parseInt(hex_byte, 16)) + .reverse() + ); +} diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index ed35889e8..d2080498b 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -9,33 +9,33 @@ -
-
-

Transaction

-
+
+
+

Transaction

+
- + -
- - - - - - +
+ + + + + + +
-
@@ -198,7 +198,7 @@
- +

Details

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 74d181a50..f3f2191a4 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -1,7 +1,13 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ElectrsApiService } from '../../services/electrs-api.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { switchMap, filter, catchError, retryWhen, delay } from 'rxjs/operators'; +import { + switchMap, + filter, + catchError, + retryWhen, + delay, +} from 'rxjs/operators'; import { Transaction, Block } from '../../interfaces/electrs.interface'; import { of, merge, Subscription, Observable, Subject } from 'rxjs'; import { StateService } from '../../services/state.service'; @@ -14,7 +20,7 @@ import { CpfpInfo } from 'src/app/interfaces/node-api.interface'; @Component({ selector: 'app-transaction', templateUrl: './transaction.component.html', - styleUrls: ['./transaction.component.scss'] + styleUrls: ['./transaction.component.scss'], }) export class TransactionComponent implements OnInit, OnDestroy { network = ''; @@ -23,6 +29,7 @@ export class TransactionComponent implements OnInit, OnDestroy { txInBlockIndex: number; isLoadingTx = true; error: any = undefined; + errorUnblinded: any = undefined; waitingForTransaction = false; latestBlock: Block; transactionTime = -1; @@ -32,6 +39,7 @@ export class TransactionComponent implements OnInit, OnDestroy { cpfpInfo: CpfpInfo | null; showCpfpDetails = false; fetchCpfp$ = new Subject(); + commitments: Map; constructor( private route: ActivatedRoute, @@ -40,28 +48,36 @@ export class TransactionComponent implements OnInit, OnDestroy { private websocketService: WebsocketService, private audioService: AudioService, private apiService: ApiService, - private seoService: SeoService, - ) { } + private seoService: SeoService + ) {} ngOnInit() { this.websocketService.want(['blocks', 'mempool-blocks']); - this.stateService.networkChanged$.subscribe((network) => this.network = network); + this.stateService.networkChanged$.subscribe( + (network) => (this.network = network) + ); this.fetchCpfpSubscription = this.fetchCpfp$ .pipe( - switchMap((txId) => this.apiService.getCpfpinfo$(txId) - .pipe( - retryWhen((errors) => errors.pipe(delay(2000))) - ) - ), + switchMap((txId) => + this.apiService + .getCpfpinfo$(txId) + .pipe(retryWhen((errors) => errors.pipe(delay(2000)))) + ) ) .subscribe((cpfpInfo) => { if (!this.tx) { return; } - const lowerFeeParents = cpfpInfo.ancestors.filter((parent) => (parent.fee / (parent.weight / 4)) < this.tx.feePerVsize); - let totalWeight = this.tx.weight + lowerFeeParents.reduce((prev, val) => prev + val.weight, 0); - let totalFees = this.tx.fee + lowerFeeParents.reduce((prev, val) => prev + val.fee, 0); + const lowerFeeParents = cpfpInfo.ancestors.filter( + (parent) => parent.fee / (parent.weight / 4) < this.tx.feePerVsize + ); + let totalWeight = + this.tx.weight + + lowerFeeParents.reduce((prev, val) => prev + val.weight, 0); + let totalFees = + this.tx.fee + + lowerFeeParents.reduce((prev, val) => prev + val.fee, 0); if (cpfpInfo.bestDescendant) { totalWeight += cpfpInfo.bestDescendant.weight; @@ -69,98 +85,116 @@ export class TransactionComponent implements OnInit, OnDestroy { } this.tx.effectiveFeePerVsize = totalFees / (totalWeight / 4); - this.stateService.markBlock$.next({ txFeePerVSize: this.tx.effectiveFeePerVsize }); + this.stateService.markBlock$.next({ + txFeePerVSize: this.tx.effectiveFeePerVsize, + }); this.cpfpInfo = cpfpInfo; }); - this.subscription = this.route.paramMap.pipe( - switchMap((params: ParamMap) => { - this.txId = params.get('id') || ''; - this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`); - this.resetTransaction(); - return merge( - of(true), - this.stateService.connectionState$.pipe( - filter((state) => state === 2 && this.tx && !this.tx.status.confirmed) - ), - ); - }), - switchMap(() => { - let transactionObservable$: Observable; - if (history.state.data) { - transactionObservable$ = of(history.state.data); - } else { - transactionObservable$ = this.electrsApiService.getTransaction$(this.txId).pipe( - catchError(this.handleLoadElectrsTransactionError.bind(this)) + this.subscription = this.route.paramMap + .pipe( + switchMap(async (params: ParamMap) => { + this.txId = params.get('id') || ''; + + await this.checkUnblindedTx(); + this.seoService.setTitle( + $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` ); - } - return merge( - transactionObservable$, - this.stateService.mempoolTransactions$ - ); - }) - ) - .subscribe((tx: Transaction) => { - if (!tx) { - return; - } - this.tx = tx; - if (tx.fee === undefined) { - this.tx.fee = 0; - } - this.tx.feePerVsize = tx.fee / (tx.weight / 4); - this.isLoadingTx = false; - this.error = undefined; - this.waitingForTransaction = false; - this.setMempoolBlocksSubscription(); + this.resetTransaction(); + return merge( + of(true), + this.stateService.connectionState$.pipe( + filter( + (state) => state === 2 && this.tx && !this.tx.status.confirmed + ) + ) + ); + }), + switchMap(() => { + let transactionObservable$: Observable; + if (history.state.data) { + transactionObservable$ = of(history.state.data); + } else { + transactionObservable$ = this.electrsApiService + .getTransaction$(this.txId) + .pipe( + catchError(this.handleLoadElectrsTransactionError.bind(this)) + ); + } + return merge( + transactionObservable$, + this.stateService.mempoolTransactions$ + ); + }) + ) + .subscribe( + async (tx: Transaction) => { + if (!tx) { + return; + } + this.tx = tx; + if (tx.fee === undefined) { + this.tx.fee = 0; + } + this.tx.feePerVsize = tx.fee / (tx.weight / 4); + this.isLoadingTx = false; + this.error = undefined; + this.waitingForTransaction = false; + this.setMempoolBlocksSubscription(); - if (!tx.status.confirmed) { - this.websocketService.startTrackTransaction(tx.txid); + if (!tx.status.confirmed) { + this.websocketService.startTrackTransaction(tx.txid); - if (tx.firstSeen) { - this.transactionTime = tx.firstSeen; - } else { - this.getTransactionTime(); - } - } + if (tx.firstSeen) { + this.transactionTime = tx.firstSeen; + } else { + this.getTransactionTime(); + } + } - if (this.tx.status.confirmed) { - this.stateService.markBlock$.next({ blockHeight: tx.status.block_height }); - } else { - if (tx.cpfpChecked) { - this.stateService.markBlock$.next({ txFeePerVSize: tx.effectiveFeePerVsize }); - this.cpfpInfo = { - ancestors: tx.ancestors, - bestDescendant: tx.bestDescendant, - }; - } else { - this.fetchCpfp$.next(this.tx.txid); + if (this.tx.status.confirmed) { + this.stateService.markBlock$.next({ + blockHeight: tx.status.block_height, + }); + } else { + if (tx.cpfpChecked) { + this.stateService.markBlock$.next({ + txFeePerVSize: tx.effectiveFeePerVsize, + }); + this.cpfpInfo = { + ancestors: tx.ancestors, + bestDescendant: tx.bestDescendant, + }; + } else { + this.fetchCpfp$.next(this.tx.txid); + } + } + await this.checkUnblindedTx(); + }, + (error) => { + this.error = error; + this.isLoadingTx = false; } + ); + + this.stateService.blocks$.subscribe(([block, txConfirmed]) => { + this.latestBlock = block; + + if (txConfirmed && this.tx) { + this.tx.status = { + confirmed: true, + block_height: block.height, + block_hash: block.id, + block_time: block.timestamp, + }; + this.stateService.markBlock$.next({ blockHeight: block.height }); + this.audioService.playSound('magic'); } - }, - (error) => { - this.error = error; - this.isLoadingTx = false; }); - this.stateService.blocks$ - .subscribe(([block, txConfirmed]) => { - this.latestBlock = block; - - if (txConfirmed && this.tx) { - this.tx.status = { - confirmed: true, - block_height: block.height, - block_hash: block.id, - block_time: block.timestamp, - }; - this.stateService.markBlock$.next({ blockHeight: block.height }); - this.audioService.playSound('magic'); - } - }); - - this.stateService.txReplaced$ - .subscribe((rbfTransaction) => this.rbfTransaction = rbfTransaction); + this.stateService.txReplaced$.subscribe( + (rbfTransaction) => (this.rbfTransaction = rbfTransaction) + ); } handleLoadElectrsTransactionError(error: any): Observable { @@ -174,26 +208,30 @@ export class TransactionComponent implements OnInit, OnDestroy { } setMempoolBlocksSubscription() { - this.stateService.mempoolBlocks$ - .subscribe((mempoolBlocks) => { - if (!this.tx) { - return; - } + this.stateService.mempoolBlocks$.subscribe((mempoolBlocks) => { + if (!this.tx) { + return; + } - const txFeePerVSize = this.tx.effectiveFeePerVsize || this.tx.fee / (this.tx.weight / 4); + const txFeePerVSize = + this.tx.effectiveFeePerVsize || this.tx.fee / (this.tx.weight / 4); - for (const block of mempoolBlocks) { - for (let i = 0; i < block.feeRange.length - 1; i++) { - if (txFeePerVSize <= block.feeRange[i + 1] && txFeePerVSize >= block.feeRange[i]) { - this.txInBlockIndex = mempoolBlocks.indexOf(block); - } + for (const block of mempoolBlocks) { + for (let i = 0; i < block.feeRange.length - 1; i++) { + if ( + txFeePerVSize <= block.feeRange[i + 1] && + txFeePerVSize >= block.feeRange[i] + ) { + this.txInBlockIndex = mempoolBlocks.indexOf(block); } } - }); + } + }); } getTransactionTime() { - this.apiService.getTransactionTimes$([this.tx.txid]) + this.apiService + .getTransactionTimes$([this.tx.txid]) .subscribe((transactionTimes) => { this.transactionTime = transactionTimes[0]; }); @@ -226,4 +264,145 @@ export class TransactionComponent implements OnInit, OnDestroy { this.fetchCpfpSubscription.unsubscribe(); this.leaveTransaction(); } + + // Parse the blinders data from a string encoded as a comma separated list, in the following format: + // ,,, + // This can be repeated with a comma separator to specify blinders for multiple outputs. + + parseBlinders(str: string) { + const parts = str.split(','); + const blinders = []; + while (parts.length) { + blinders.push({ + value: this.verifyNum(parts.shift()), + asset: this.verifyHex32(parts.shift()), + value_blinder: this.verifyHex32(parts.shift()), + asset_blinder: this.verifyHex32(parts.shift()), + }); + } + return blinders; + } + + verifyNum(num: string) { + if (!+num) { + throw new Error('Invalid blinding data (invalid number)'); + } + return +num; + } + verifyHex32(str: string) { + if (!str || !/^[0-9a-f]{64}$/i.test(str)) { + throw new Error('Invalid blinding data (invalid hex)'); + } + return str; + } + + async makeCommitmentMap(blinders: any) { + const libwally = await import('./libwally.js'); + await libwally.load(); + const commitments = new Map(); + blinders.forEach(b => { + const { asset_commitment, value_commitment } = + libwally.generate_commitments(b.value, b.asset, b.value_blinder, b.asset_blinder); + + commitments.set(`${asset_commitment}:${value_commitment}`, { + asset: b.asset, + value: b.value, + }); + }); + return commitments; + } + + // Look for the given output, returning an { value, asset } object + find(vout: any) { + return vout.assetcommitment && vout.valuecommitment && + this.commitments.get(`${vout.assetcommitment}:${vout.valuecommitment}`); + } + + // Lookup all transaction inputs/outputs and attach the unblinded data + tryUnblindTx(tx: any) { + if (tx) { + if (tx._unblinded) { return tx._unblinded; } + let matched = 0; + if (tx.vout !== undefined) { + tx.vout.forEach(vout => matched += +this.tryUnblindOut(vout)); + tx.vin.filter(vin => vin.prevout).forEach(vin => matched += +this.tryUnblindOut(vin.prevout)); + } + if (this.commitments !== undefined) { + tx._unblinded = { matched, total: this.commitments.size }; + this.deduceBlinded(tx); + if (matched < this.commitments.size) { + this.errorUnblinded = `Error: Invalid blinding data.`; + } + tx._deduced = false; // invalidate cache so deduction is attempted again + return tx._unblinded; + } + } + } + + // Look the given output and attach the unblinded data + tryUnblindOut(vout: any) { + const unblinded = this.find(vout); + if (unblinded) { Object.assign(vout, unblinded); } + return !!unblinded; + } + + // Attempt to deduce the blinded input/output based on the available information + deduceBlinded(tx: any) { + if (tx._deduced) { return; } + tx._deduced = true; + + // Find ins/outs with unknown amounts (blinded ant not revealed via the `#blinded` hash fragment) + const unknownIns = tx.vin.filter(vin => vin.prevout && vin.prevout.value == null); + const unknownOuts = tx.vout.filter(vout => vout.value == null); + + // If the transaction has a single unknown input/output, we can deduce its asset/amount + // based on the other known inputs/outputs. + if (unknownIns.length + unknownOuts.length === 1) { + + // Keep a per-asset tally of all known input amounts, minus all known output amounts + const totals = new Map(); + tx.vin.filter(vin => vin.prevout && vin.prevout.value != null) + .forEach(({ prevout }) => + totals.set(prevout.asset, (totals.get(prevout.asset) || 0) + prevout.value)); + tx.vout.filter(vout => vout.value != null) + .forEach(vout => + totals.set(vout.asset, (totals.get(vout.asset) || 0) - vout.value)); + + // There should only be a single asset where the inputs and outputs amounts mismatch, + // which is the asset of the blinded input/output + const remainder = Array.from(totals.entries()).filter(([ asset, value ]) => value !== 0); + if (remainder.length !== 1) { throw new Error('unexpected remainder while deducing blinded tx'); } + const [ blindedAsset, blindedValue ] = remainder[0]; + + // A positive remainder (when known in > known out) is the asset/amount of the unknown blinded output, + // a negative one is the input. + if (blindedValue > 0) { + if (!unknownOuts.length) { throw new Error('expected unknown output'); } + unknownOuts[0].asset = blindedAsset; + unknownOuts[0].value = blindedValue; + } else { + if (!unknownIns.length) { throw new Error('expected unknown input'); } + unknownIns[0].prevout.asset = blindedAsset; + unknownIns[0].prevout.value = blindedValue * -1; + } + } + } + + async checkUnblindedTx() { + try { + if (this.network === 'liquid') { + const windowLocationHash = window.location.hash.substring('#blinded='.length); + if (windowLocationHash.length > 0) { + + const blinders = this.parseBlinders(windowLocationHash); + if (blinders) { + this.commitments = await this.makeCommitmentMap(blinders); + this.tryUnblindTx(this.tx); + } + } + } + } catch (error) { + this.errorUnblinded = error; + } + } } diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 03ab90afd..20dee92e5 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -12,13 +12,16 @@
+
+ +
{{ errorUnblinded }}
- +
- + -
@@ -66,7 +69,7 @@
+ @@ -114,10 +117,10 @@
-
+
- + -
{{ vout.scriptpubkey_address | shortenString : 16 }} @@ -145,7 +148,7 @@
-
+
@@ -162,7 +165,7 @@
+ @@ -235,5 +238,4 @@ {{ assetsMinimal[item.asset][0] }}
{{ item.asset | shortenString : 13 }} -

diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.scss b/frontend/src/app/components/transactions-list/transactions-list.component.scss index fd95e3759..b85069650 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.scss +++ b/frontend/src/app/components/transactions-list/transactions-list.component.scss @@ -4,9 +4,10 @@ .arrow { display: inline-block!important; - position: relative; + position: absolute; width: 14px; - height: 22px; + margin-top: 10px; + margin-left: -5px; box-sizing: content-box } @@ -66,24 +67,6 @@ } } -.details-table { - margin-top: 5px; -} - -.details-table td { - padding: 0.75rem; - &:first-child { - white-space: pre-wrap; - } -} - -.details-table td:nth-child(2) { - word-break: break-all; - white-space: normal; - font-family: "Courier New", Courier, monospace; - font-size: 12px; -} - .smaller-text { font-size: 12px; @media (min-width: 576px) { @@ -137,4 +120,36 @@ padding: 10px; margin-bottom: 10px; margin-top: 10px; +} +.assetBox { + background-color: #653b9c90; +} + +.details-container { + padding: 0px; + tr td { + padding: 0.75rem; + font-size: 12px; + &:first-child { + color: #ffffff66; + white-space: pre-wrap; + @media (min-width: 476px) { + white-space: nowrap; + } + } + &:nth-child(2) { + word-break: break-all; + white-space: normal; + font-family: "Courier New", Courier, monospace; + } + } +} + +.error-unblinded { + display: block; + width: 100%; + color: #d43131; + text-align: right; + margin-top: 0px; + margin-bottom: 10px; } \ No newline at end of file diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index dd7350954..4425317c0 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -21,6 +21,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { @Input() transactions: Transaction[]; @Input() showConfirmations = false; @Input() transactionPage = false; + @Input() errorUnblinded = false; @Output() loadMore = new EventEmitter(); diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index dcf7508ae..ff948551f 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -17,6 +17,8 @@ export interface Transaction { bestDescendant?: BestDescendant | null; cpfpChecked?: boolean; deleteAfter?: number; + _unblinded?: any; + _deduced?: boolean; } interface Ancestor { diff --git a/frontend/src/resources/wallycore/wallycore.js b/frontend/src/resources/wallycore/wallycore.js new file mode 100644 index 000000000..8a61046f2 --- /dev/null +++ b/frontend/src/resources/wallycore/wallycore.js @@ -0,0 +1,802 @@ +/* +The MIT License (MIT) + +Copyright 2021 Blockstream Corp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE +*/ + +var InitWally = (function () { + var _scriptDir = + typeof document !== "undefined" && document.currentScript + ? document.currentScript.src + : undefined; + if (typeof __filename !== "undefined") _scriptDir = _scriptDir || __filename; + return function (InitWally) { + InitWally = InitWally || {}; + + var Module = typeof InitWally !== "undefined" ? InitWally : {}; + var readyPromiseResolve, readyPromiseReject; + Module["ready"] = new Promise(function (resolve, reject) { + readyPromiseResolve = resolve; + readyPromiseReject = reject; + }); + var moduleOverrides = {}; + var key; + for (key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } + } + var arguments_ = []; + var thisProgram = "./this.program"; + var quit_ = function (status, toThrow) { + throw toThrow; + }; + var ENVIRONMENT_IS_WEB = false; + var ENVIRONMENT_IS_WORKER = false; + var ENVIRONMENT_IS_NODE = false; + var ENVIRONMENT_IS_SHELL = false; + ENVIRONMENT_IS_WEB = typeof window === "object"; + ENVIRONMENT_IS_WORKER = typeof importScripts === "function"; + ENVIRONMENT_IS_NODE = + typeof process === "object" && + typeof process.versions === "object" && + typeof process.versions.node === "string"; + ENVIRONMENT_IS_SHELL = + !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + var scriptDirectory = ""; + function locateFile(path) { + if (Module["locateFile"]) { + return Module["locateFile"](path, scriptDirectory); + } + return scriptDirectory + path; + } + var read_, readAsync, readBinary, setWindowTitle; + var nodeFS; + var nodePath; + if (ENVIRONMENT_IS_NODE) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = require("path").dirname(scriptDirectory) + "/"; + } else { + scriptDirectory = __dirname + "/"; + } + read_ = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = require("fs"); + if (!nodePath) nodePath = require("path"); + filename = nodePath["normalize"](filename); + return nodeFS["readFileSync"](filename, binary ? null : "utf8"); + }; + readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; + }; + if (process["argv"].length > 1) { + thisProgram = process["argv"][1].replace(/\\/g, "/"); + } + arguments_ = process["argv"].slice(2); + process["on"]("uncaughtException", function (ex) { + if (!(ex instanceof ExitStatus)) { + throw ex; + } + }); + process["on"]("unhandledRejection", abort); + quit_ = function (status) { + process["exit"](status); + }; + Module["inspect"] = function () { + return "[Emscripten Module object]"; + }; + } else if (ENVIRONMENT_IS_SHELL) { + if (typeof read != "undefined") { + read_ = function shell_read(f) { + return read(f); + }; + } + readBinary = function readBinary(f) { + var data; + if (typeof readbuffer === "function") { + return new Uint8Array(readbuffer(f)); + } + data = read(f, "binary"); + assert(typeof data === "object"); + return data; + }; + if (typeof scriptArgs != "undefined") { + arguments_ = scriptArgs; + } else if (typeof arguments != "undefined") { + arguments_ = arguments; + } + if (typeof quit === "function") { + quit_ = function (status) { + quit(status); + }; + } + if (typeof print !== "undefined") { + if (typeof console === "undefined") console = {}; + console.log = print; + console.warn = console.error = + typeof printErr !== "undefined" ? printErr : print; + } + } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = self.location.href; + } else if (typeof document !== "undefined" && document.currentScript) { + scriptDirectory = document.currentScript.src; + } + if (_scriptDir) { + scriptDirectory = _scriptDir; + } + if (scriptDirectory.indexOf("blob:") !== 0) { + scriptDirectory = scriptDirectory.substr( + 0, + scriptDirectory.lastIndexOf("/") + 1 + ); + } else { + scriptDirectory = ""; + } + { + read_ = function shell_read(url) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.send(null); + return xhr.responseText; + }; + if (ENVIRONMENT_IS_WORKER) { + readBinary = function readBinary(url) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.responseType = "arraybuffer"; + xhr.send(null); + return new Uint8Array(xhr.response); + }; + } + readAsync = function readAsync(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + xhr.onload = function xhr_onload() { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { + onload(xhr.response); + return; + } + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + }; + } + setWindowTitle = function (title) { + document.title = title; + }; + } else { + } + var out = Module["print"] || console.log.bind(console); + var err = Module["printErr"] || console.warn.bind(console); + for (key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } + } + moduleOverrides = null; + if (Module["arguments"]) arguments_ = Module["arguments"]; + if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + if (Module["quit"]) quit_ = Module["quit"]; + var wasmBinary; + if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; + var noExitRuntime; + if (Module["noExitRuntime"]) noExitRuntime = Module["noExitRuntime"]; + if (typeof WebAssembly !== "object") { + abort("no native wasm support detected"); + } + function getValue(ptr, type, noSafe) { + type = type || "i8"; + if (type.charAt(type.length - 1) === "*") type = "i32"; + switch (type) { + case "i1": + return HEAP8[ptr >> 0]; + case "i8": + return HEAP8[ptr >> 0]; + case "i16": + return HEAP16[ptr >> 1]; + case "i32": + return HEAP32[ptr >> 2]; + case "i64": + return HEAP32[ptr >> 2]; + case "float": + return HEAPF32[ptr >> 2]; + case "double": + return HEAPF64[ptr >> 3]; + default: + abort("invalid type for getValue: " + type); + } + return null; + } + var wasmMemory; + var ABORT = false; + var EXITSTATUS = 0; + function assert(condition, text) { + if (!condition) { + abort("Assertion failed: " + text); + } + } + function getCFunc(ident) { + var func = Module["_" + ident]; + assert( + func, + "Cannot call unknown function " + ident + ", make sure it is exported" + ); + return func; + } + function ccall(ident, returnType, argTypes, args, opts) { + var toC = { + string: function (str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + }, + array: function (arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + }, + }; + function convertReturnValue(ret) { + if (returnType === "string") return UTF8ToString(ret); + if (returnType === "boolean") return Boolean(ret); + return ret; + } + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); + ret = convertReturnValue(ret); + if (stack !== 0) stackRestore(stack); + return ret; + } + var UTF8Decoder = + typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined; + function UTF8ArrayToString(heap, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; + if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { + return UTF8Decoder.decode(heap.subarray(idx, endPtr)); + } else { + var str = ""; + while (idx < endPtr) { + var u0 = heap[idx++]; + if (!(u0 & 128)) { + str += String.fromCharCode(u0); + continue; + } + var u1 = heap[idx++] & 63; + if ((u0 & 224) == 192) { + str += String.fromCharCode(((u0 & 31) << 6) | u1); + continue; + } + var u2 = heap[idx++] & 63; + if ((u0 & 240) == 224) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); + } + if (u0 < 65536) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 65536; + str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); + } + } + } + return str; + } + function UTF8ToString(ptr, maxBytesToRead) { + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; + } + function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) return 0; + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; + for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) { + var u1 = str.charCodeAt(++i); + u = (65536 + ((u & 1023) << 10)) | (u1 & 1023); + } + if (u <= 127) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 2047) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 192 | (u >> 6); + heap[outIdx++] = 128 | (u & 63); + } else if (u <= 65535) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 224 | (u >> 12); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 240 | (u >> 18); + heap[outIdx++] = 128 | ((u >> 12) & 63); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } + } + heap[outIdx] = 0; + return outIdx - startIdx; + } + function stringToUTF8(str, outPtr, maxBytesToWrite) { + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + } + function writeArrayToMemory(array, buffer) { + HEAP8.set(array, buffer); + } + var buffer, + HEAP8, + HEAPU8, + HEAP16, + HEAPU16, + HEAP32, + HEAPU32, + HEAPF32, + HEAPF64; + function updateGlobalBufferAndViews(buf) { + buffer = buf; + Module["HEAP8"] = HEAP8 = new Int8Array(buf); + Module["HEAP16"] = HEAP16 = new Int16Array(buf); + Module["HEAP32"] = HEAP32 = new Int32Array(buf); + Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); + Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); + Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); + Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); + Module["HEAPF64"] = HEAPF64 = new Float64Array(buf); + } + var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; + if (Module["wasmMemory"]) { + wasmMemory = Module["wasmMemory"]; + } else { + wasmMemory = new WebAssembly.Memory({ + initial: INITIAL_MEMORY / 65536, + maximum: INITIAL_MEMORY / 65536, + }); + } + if (wasmMemory) { + buffer = wasmMemory.buffer; + } + INITIAL_MEMORY = buffer.byteLength; + updateGlobalBufferAndViews(buffer); + var wasmTable; + var __ATPRERUN__ = []; + var __ATINIT__ = []; + var __ATMAIN__ = []; + var __ATPOSTRUN__ = []; + var runtimeInitialized = false; + function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") + Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); + } + function initRuntime() { + runtimeInitialized = true; + callRuntimeCallbacks(__ATINIT__); + } + function preMain() { + callRuntimeCallbacks(__ATMAIN__); + } + function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") + Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); + } + function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); + } + function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); + } + var runDependencies = 0; + var runDependencyWatcher = null; + var dependenciesFulfilled = null; + function addRunDependency(id) { + runDependencies++; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + } + } + function removeRunDependency(id) { + runDependencies--; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } + } + Module["preloadedImages"] = {}; + Module["preloadedAudios"] = {}; + function abort(what) { + if (Module["onAbort"]) { + Module["onAbort"](what); + } + what += ""; + err(what); + ABORT = true; + EXITSTATUS = 1; + what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info."; + var e = new WebAssembly.RuntimeError(what); + readyPromiseReject(e); + throw e; + } + function hasPrefix(str, prefix) { + return String.prototype.startsWith + ? str.startsWith(prefix) + : str.indexOf(prefix) === 0; + } + var dataURIPrefix = "data:application/octet-stream;base64,"; + function isDataURI(filename) { + return hasPrefix(filename, dataURIPrefix); + } + var fileURIPrefix = "file://"; + function isFileURI(filename) { + return hasPrefix(filename, fileURIPrefix); + } + var wasmBinaryFile = "wallycore.wasm"; + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); + } + function getBinary() { + try { + if (wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(wasmBinaryFile); + } else { + throw "both async and sync fetching of the wasm failed"; + } + } catch (err) { + abort(err); + } + } + function getBinaryPromise() { + if ( + !wasmBinary && + (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && + typeof fetch === "function" && + !isFileURI(wasmBinaryFile) + ) { + return fetch(wasmBinaryFile, { credentials: "same-origin" }) + .then(function (response) { + if (!response["ok"]) { + throw ( + "failed to load wasm binary file at '" + wasmBinaryFile + "'" + ); + } + return response["arrayBuffer"](); + }) + .catch(function () { + return getBinary(); + }); + } + return Promise.resolve().then(getBinary); + } + function createWasm() { + var info = { a: asmLibraryArg }; + function receiveInstance(instance, module) { + var exports = instance.exports; + Module["asm"] = exports; + wasmTable = Module["asm"]["h"]; + removeRunDependency("wasm-instantiate"); + } + addRunDependency("wasm-instantiate"); + function receiveInstantiatedSource(output) { + receiveInstance(output["instance"]); + } + function instantiateArrayBuffer(receiver) { + return getBinaryPromise() + .then(function (binary) { + return WebAssembly.instantiate(binary, info); + }) + .then(receiver, function (reason) { + err("failed to asynchronously prepare wasm: " + reason); + abort(reason); + }); + } + function instantiateAsync() { + if ( + !wasmBinary && + typeof WebAssembly.instantiateStreaming === "function" && + !isDataURI(wasmBinaryFile) && + !isFileURI(wasmBinaryFile) && + typeof fetch === "function" + ) { + return fetch(wasmBinaryFile, { credentials: "same-origin" }).then( + function (response) { + var result = WebAssembly.instantiateStreaming(response, info); + return result.then(receiveInstantiatedSource, function (reason) { + err("wasm streaming compile failed: " + reason); + err("falling back to ArrayBuffer instantiation"); + return instantiateArrayBuffer(receiveInstantiatedSource); + }); + } + ); + } else { + return instantiateArrayBuffer(receiveInstantiatedSource); + } + } + if (Module["instantiateWasm"]) { + try { + var exports = Module["instantiateWasm"](info, receiveInstance); + return exports; + } catch (e) { + err("Module.instantiateWasm callback failed with error: " + e); + return false; + } + } + instantiateAsync().catch(readyPromiseReject); + return {}; + } + function callRuntimeCallbacks(callbacks) { + while (callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == "function") { + callback(Module); + continue; + } + var func = callback.func; + if (typeof func === "number") { + if (callback.arg === undefined) { + wasmTable.get(func)(); + } else { + wasmTable.get(func)(callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } + } + function _abort() { + abort(); + } + function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.copyWithin(dest, src, src + num); + } + function abortOnCannotGrowMemory(requestedSize) { + abort("OOM"); + } + function _emscripten_resize_heap(requestedSize) { + requestedSize = requestedSize >>> 0; + abortOnCannotGrowMemory(requestedSize); + } + var SYSCALLS = { + mappings: {}, + buffers: [null, [], []], + printChar: function (stream, curr) { + var buffer = SYSCALLS.buffers[stream]; + if (curr === 0 || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); + buffer.length = 0; + } else { + buffer.push(curr); + } + }, + varargs: undefined, + get: function () { + SYSCALLS.varargs += 4; + var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2]; + return ret; + }, + getStr: function (ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + get64: function (low, high) { + return low; + }, + }; + function _fd_close(fd) { + return 0; + } + function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {} + function _fd_write(fd, iov, iovcnt, pnum) { + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(iov + i * 8) >> 2]; + var len = HEAP32[(iov + (i * 8 + 4)) >> 2]; + for (var j = 0; j < len; j++) { + SYSCALLS.printChar(fd, HEAPU8[ptr + j]); + } + num += len; + } + HEAP32[pnum >> 2] = num; + return 0; + } + __ATINIT__.push({ + func: function () { + ___wasm_call_ctors(); + }, + }); + var asmLibraryArg = { + b: _abort, + e: _emscripten_memcpy_big, + f: _emscripten_resize_heap, + g: _fd_close, + d: _fd_seek, + c: _fd_write, + a: wasmMemory, + }; + var asm = createWasm(); + var ___wasm_call_ctors = (Module["___wasm_call_ctors"] = function () { + return (___wasm_call_ctors = Module["___wasm_call_ctors"] = + Module["asm"]["i"]).apply(null, arguments); + }); + var _wally_asset_generator_from_bytes = (Module[ + "_wally_asset_generator_from_bytes" + ] = function () { + return (_wally_asset_generator_from_bytes = Module[ + "_wally_asset_generator_from_bytes" + ] = + Module["asm"]["j"]).apply(null, arguments); + }); + var _wally_asset_value_commitment = (Module[ + "_wally_asset_value_commitment" + ] = function () { + return (_wally_asset_value_commitment = Module[ + "_wally_asset_value_commitment" + ] = + Module["asm"]["k"]).apply(null, arguments); + }); + var _wally_init = (Module["_wally_init"] = function () { + return (_wally_init = Module["_wally_init"] = Module["asm"]["l"]).apply( + null, + arguments + ); + }); + var _malloc = (Module["_malloc"] = function () { + return (_malloc = Module["_malloc"] = Module["asm"]["m"]).apply( + null, + arguments + ); + }); + var _free = (Module["_free"] = function () { + return (_free = Module["_free"] = Module["asm"]["n"]).apply( + null, + arguments + ); + }); + var stackSave = (Module["stackSave"] = function () { + return (stackSave = Module["stackSave"] = Module["asm"]["o"]).apply( + null, + arguments + ); + }); + var stackRestore = (Module["stackRestore"] = function () { + return (stackRestore = Module["stackRestore"] = Module["asm"]["p"]).apply( + null, + arguments + ); + }); + var stackAlloc = (Module["stackAlloc"] = function () { + return (stackAlloc = Module["stackAlloc"] = Module["asm"]["q"]).apply( + null, + arguments + ); + }); + Module["ccall"] = ccall; + Module["getValue"] = getValue; + var calledRun; + function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; + } + dependenciesFulfilled = function runCaller() { + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; + }; + function run(args) { + args = args || arguments_; + if (runDependencies > 0) { + return; + } + preRun(); + if (runDependencies > 0) return; + function doRun() { + if (calledRun) return; + calledRun = true; + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + preMain(); + readyPromiseResolve(Module); + if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(function () { + setTimeout(function () { + Module["setStatus"](""); + }, 1); + doRun(); + }, 1); + } else { + doRun(); + } + } + Module["run"] = run; + if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") + Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) { + Module["preInit"].pop()(); + } + } + noExitRuntime = true; + run(); + + return InitWally.ready; + }; +})(); +if (typeof exports === "object" && typeof module === "object") + module.exports = InitWally; +else if (typeof define === "function" && define["amd"]) + define([], function () { + return InitWally; + }); +else if (typeof exports === "object") exports["InitWally"] = InitWally; diff --git a/frontend/src/resources/wallycore/wallycore.wasm b/frontend/src/resources/wallycore/wallycore.wasm new file mode 100755 index 0000000000000000000000000000000000000000..d941b95045f18f4b6ef546d9badfe0ed1fe6e460 GIT binary patch literal 135961 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%0DUL{-qQv4iyJeve@-c3wwb`~rX`Bo!WC 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 literal 0 HcmV?d00001