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>
This commit is contained in:
parent
3ae3df6722
commit
9dae7020c8
@ -78,4 +78,43 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
184
frontend/src/app/components/transaction/libwally.js
Normal file
184
frontend/src/app/components/transaction/libwally.js
Normal file
@ -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()
|
||||||
|
);
|
||||||
|
}
|
@ -198,7 +198,7 @@
|
|||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<app-transactions-list #txList [transactions]="[tx]" [transactionPage]="true"></app-transactions-list>
|
<app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [transactionPage]="true"></app-transactions-list>
|
||||||
|
|
||||||
<h2 class="text-left" i18n="transaction.details">Details</h2>
|
<h2 class="text-left" i18n="transaction.details">Details</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
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 { Transaction, Block } from '../../interfaces/electrs.interface';
|
||||||
import { of, merge, Subscription, Observable, Subject } from 'rxjs';
|
import { of, merge, Subscription, Observable, Subject } from 'rxjs';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
@ -14,7 +20,7 @@ import { CpfpInfo } from 'src/app/interfaces/node-api.interface';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-transaction',
|
selector: 'app-transaction',
|
||||||
templateUrl: './transaction.component.html',
|
templateUrl: './transaction.component.html',
|
||||||
styleUrls: ['./transaction.component.scss']
|
styleUrls: ['./transaction.component.scss'],
|
||||||
})
|
})
|
||||||
export class TransactionComponent implements OnInit, OnDestroy {
|
export class TransactionComponent implements OnInit, OnDestroy {
|
||||||
network = '';
|
network = '';
|
||||||
@ -23,6 +29,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
txInBlockIndex: number;
|
txInBlockIndex: number;
|
||||||
isLoadingTx = true;
|
isLoadingTx = true;
|
||||||
error: any = undefined;
|
error: any = undefined;
|
||||||
|
errorUnblinded: any = undefined;
|
||||||
waitingForTransaction = false;
|
waitingForTransaction = false;
|
||||||
latestBlock: Block;
|
latestBlock: Block;
|
||||||
transactionTime = -1;
|
transactionTime = -1;
|
||||||
@ -32,6 +39,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
cpfpInfo: CpfpInfo | null;
|
cpfpInfo: CpfpInfo | null;
|
||||||
showCpfpDetails = false;
|
showCpfpDetails = false;
|
||||||
fetchCpfp$ = new Subject<string>();
|
fetchCpfp$ = new Subject<string>();
|
||||||
|
commitments: Map<any, any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -40,28 +48,36 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private audioService: AudioService,
|
private audioService: AudioService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
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$
|
this.fetchCpfpSubscription = this.fetchCpfp$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((txId) => this.apiService.getCpfpinfo$(txId)
|
switchMap((txId) =>
|
||||||
.pipe(
|
this.apiService
|
||||||
retryWhen((errors) => errors.pipe(delay(2000)))
|
.getCpfpinfo$(txId)
|
||||||
|
.pipe(retryWhen((errors) => errors.pipe(delay(2000))))
|
||||||
)
|
)
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.subscribe((cpfpInfo) => {
|
.subscribe((cpfpInfo) => {
|
||||||
if (!this.tx) {
|
if (!this.tx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const lowerFeeParents = cpfpInfo.ancestors.filter((parent) => (parent.fee / (parent.weight / 4)) < this.tx.feePerVsize);
|
const lowerFeeParents = cpfpInfo.ancestors.filter(
|
||||||
let totalWeight = this.tx.weight + lowerFeeParents.reduce((prev, val) => prev + val.weight, 0);
|
(parent) => parent.fee / (parent.weight / 4) < this.tx.feePerVsize
|
||||||
let totalFees = this.tx.fee + lowerFeeParents.reduce((prev, val) => prev + val.fee, 0);
|
);
|
||||||
|
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) {
|
if (cpfpInfo.bestDescendant) {
|
||||||
totalWeight += cpfpInfo.bestDescendant.weight;
|
totalWeight += cpfpInfo.bestDescendant.weight;
|
||||||
@ -69,20 +85,29 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tx.effectiveFeePerVsize = totalFees / (totalWeight / 4);
|
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.cpfpInfo = cpfpInfo;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subscription = this.route.paramMap.pipe(
|
this.subscription = this.route.paramMap
|
||||||
switchMap((params: ParamMap) => {
|
.pipe(
|
||||||
|
switchMap(async (params: ParamMap) => {
|
||||||
this.txId = params.get('id') || '';
|
this.txId = params.get('id') || '';
|
||||||
this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`);
|
|
||||||
|
await this.checkUnblindedTx();
|
||||||
|
this.seoService.setTitle(
|
||||||
|
$localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
|
||||||
|
);
|
||||||
this.resetTransaction();
|
this.resetTransaction();
|
||||||
return merge(
|
return merge(
|
||||||
of(true),
|
of(true),
|
||||||
this.stateService.connectionState$.pipe(
|
this.stateService.connectionState$.pipe(
|
||||||
filter((state) => state === 2 && this.tx && !this.tx.status.confirmed)
|
filter(
|
||||||
),
|
(state) => state === 2 && this.tx && !this.tx.status.confirmed
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
@ -90,7 +115,9 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
if (history.state.data) {
|
if (history.state.data) {
|
||||||
transactionObservable$ = of(history.state.data);
|
transactionObservable$ = of(history.state.data);
|
||||||
} else {
|
} else {
|
||||||
transactionObservable$ = this.electrsApiService.getTransaction$(this.txId).pipe(
|
transactionObservable$ = this.electrsApiService
|
||||||
|
.getTransaction$(this.txId)
|
||||||
|
.pipe(
|
||||||
catchError(this.handleLoadElectrsTransactionError.bind(this))
|
catchError(this.handleLoadElectrsTransactionError.bind(this))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -100,7 +127,8 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe((tx: Transaction) => {
|
.subscribe(
|
||||||
|
async (tx: Transaction) => {
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -125,10 +153,14 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.tx.status.confirmed) {
|
if (this.tx.status.confirmed) {
|
||||||
this.stateService.markBlock$.next({ blockHeight: tx.status.block_height });
|
this.stateService.markBlock$.next({
|
||||||
|
blockHeight: tx.status.block_height,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (tx.cpfpChecked) {
|
if (tx.cpfpChecked) {
|
||||||
this.stateService.markBlock$.next({ txFeePerVSize: tx.effectiveFeePerVsize });
|
this.stateService.markBlock$.next({
|
||||||
|
txFeePerVSize: tx.effectiveFeePerVsize,
|
||||||
|
});
|
||||||
this.cpfpInfo = {
|
this.cpfpInfo = {
|
||||||
ancestors: tx.ancestors,
|
ancestors: tx.ancestors,
|
||||||
bestDescendant: tx.bestDescendant,
|
bestDescendant: tx.bestDescendant,
|
||||||
@ -137,14 +169,15 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
this.fetchCpfp$.next(this.tx.txid);
|
this.fetchCpfp$.next(this.tx.txid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await this.checkUnblindedTx();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.isLoadingTx = false;
|
this.isLoadingTx = false;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.stateService.blocks$
|
this.stateService.blocks$.subscribe(([block, txConfirmed]) => {
|
||||||
.subscribe(([block, txConfirmed]) => {
|
|
||||||
this.latestBlock = block;
|
this.latestBlock = block;
|
||||||
|
|
||||||
if (txConfirmed && this.tx) {
|
if (txConfirmed && this.tx) {
|
||||||
@ -159,8 +192,9 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stateService.txReplaced$
|
this.stateService.txReplaced$.subscribe(
|
||||||
.subscribe((rbfTransaction) => this.rbfTransaction = rbfTransaction);
|
(rbfTransaction) => (this.rbfTransaction = rbfTransaction)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadElectrsTransactionError(error: any): Observable<any> {
|
handleLoadElectrsTransactionError(error: any): Observable<any> {
|
||||||
@ -174,17 +208,20 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMempoolBlocksSubscription() {
|
setMempoolBlocksSubscription() {
|
||||||
this.stateService.mempoolBlocks$
|
this.stateService.mempoolBlocks$.subscribe((mempoolBlocks) => {
|
||||||
.subscribe((mempoolBlocks) => {
|
|
||||||
if (!this.tx) {
|
if (!this.tx) {
|
||||||
return;
|
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 (const block of mempoolBlocks) {
|
||||||
for (let i = 0; i < block.feeRange.length - 1; i++) {
|
for (let i = 0; i < block.feeRange.length - 1; i++) {
|
||||||
if (txFeePerVSize <= block.feeRange[i + 1] && txFeePerVSize >= block.feeRange[i]) {
|
if (
|
||||||
|
txFeePerVSize <= block.feeRange[i + 1] &&
|
||||||
|
txFeePerVSize >= block.feeRange[i]
|
||||||
|
) {
|
||||||
this.txInBlockIndex = mempoolBlocks.indexOf(block);
|
this.txInBlockIndex = mempoolBlocks.indexOf(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +230,8 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTransactionTime() {
|
getTransactionTime() {
|
||||||
this.apiService.getTransactionTimes$([this.tx.txid])
|
this.apiService
|
||||||
|
.getTransactionTimes$([this.tx.txid])
|
||||||
.subscribe((transactionTimes) => {
|
.subscribe((transactionTimes) => {
|
||||||
this.transactionTime = transactionTimes[0];
|
this.transactionTime = transactionTimes[0];
|
||||||
});
|
});
|
||||||
@ -226,4 +264,145 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
this.fetchCpfpSubscription.unsubscribe();
|
this.fetchCpfpSubscription.unsubscribe();
|
||||||
this.leaveTransaction();
|
this.leaveTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the blinders data from a string encoded as a comma separated list, in the following format:
|
||||||
|
// <value_in_satoshis>,<asset_tag_hex>,<amount_blinder_hex>,<asset_blinder_hex>
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
|
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
|
||||||
|
|
||||||
|
<div *ngIf="errorUnblinded" class="error-unblinded">{{ errorUnblinded }}</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
<table class="table table-borderless smaller-text table-sm" style="margin: 0;" id="table-tx-vin">
|
||||||
<tbody>
|
<tbody>
|
||||||
<ng-template ngFor let-vin [ngForOf]="tx['@vinLimit'] ? tx.vin.slice(0, 10) : tx.vin" [ngForTrackBy]="trackByIndexFn">
|
<ng-template ngFor let-vin [ngForOf]="tx['@vinLimit'] ? tx.vin.slice(0, 10) : tx.vin" [ngForTrackBy]="trackByIndexFn">
|
||||||
<tr>
|
<tr [ngClass]="assetsMinimal && assetsMinimal[vin.prevout.asset] && vin.prevout.scriptpubkey_address ? 'assetBox' : ''">
|
||||||
<td class="arrow-td">
|
<td class="arrow-td">
|
||||||
<ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout">
|
<ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout">
|
||||||
<i class="arrow grey"></i>
|
<i class="arrow grey"></i>
|
||||||
@ -66,7 +69,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="displayDetails">
|
<tr *ngIf="displayDetails">
|
||||||
<td colspan="3">
|
<td colspan="3" class="details-container" >
|
||||||
<table class="table table-striped table-borderless details-table mb-3">
|
<table class="table table-striped table-borderless details-table mb-3">
|
||||||
<tbody>
|
<tbody>
|
||||||
<ng-template [ngIf]="vin.scriptsig">
|
<ng-template [ngIf]="vin.scriptsig">
|
||||||
@ -114,10 +117,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-100 d-block d-md-none"></div>
|
<div class="w-100 d-block d-md-none"></div>
|
||||||
<div class="col mobile-bottomcol">
|
<div class="col mobile-bottomcol">
|
||||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
<table class="table table-borderless smaller-text table-sm" style="margin: 0;" id="table-tx-vout">
|
||||||
<tbody>
|
<tbody>
|
||||||
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] ? tx.vout.slice(0, 10) : tx.vout" [ngForTrackBy]="trackByIndexFn">
|
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] ? tx.vout.slice(0, 10) : tx.vout" [ngForTrackBy]="trackByIndexFn">
|
||||||
<tr>
|
<tr [ngClass]="assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address ? 'assetBox' : ''">
|
||||||
<td>
|
<td>
|
||||||
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
|
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
|
||||||
<span class="d-block d-lg-none">{{ vout.scriptpubkey_address | shortenString : 16 }}</span>
|
<span class="d-block d-lg-none">{{ vout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||||
@ -162,7 +165,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="displayDetails">
|
<tr *ngIf="displayDetails">
|
||||||
<td colspan="3">
|
<td colspan="3" class=" details-container" >
|
||||||
<table class="table table-striped table-borderless details-table mb-3">
|
<table class="table table-striped table-borderless details-table mb-3">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngIf="vout.scriptpubkey_type">
|
<tr *ngIf="vout.scriptpubkey_type">
|
||||||
@ -235,5 +238,4 @@
|
|||||||
{{ assetsMinimal[item.asset][0] }}
|
{{ assetsMinimal[item.asset][0] }}
|
||||||
<br />
|
<br />
|
||||||
<a [routerLink]="['/asset/' | relativeUrl, item.asset]">{{ item.asset | shortenString : 13 }}</a>
|
<a [routerLink]="['/asset/' | relativeUrl, item.asset]">{{ item.asset | shortenString : 13 }}</a>
|
||||||
<br /><br />
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
display: inline-block!important;
|
display: inline-block!important;
|
||||||
position: relative;
|
position: absolute;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 22px;
|
margin-top: 10px;
|
||||||
|
margin-left: -5px;
|
||||||
box-sizing: content-box
|
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 {
|
.smaller-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
@ -138,3 +121,35 @@
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
margin-top: 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;
|
||||||
|
}
|
@ -21,6 +21,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
|||||||
@Input() transactions: Transaction[];
|
@Input() transactions: Transaction[];
|
||||||
@Input() showConfirmations = false;
|
@Input() showConfirmations = false;
|
||||||
@Input() transactionPage = false;
|
@Input() transactionPage = false;
|
||||||
|
@Input() errorUnblinded = false;
|
||||||
|
|
||||||
@Output() loadMore = new EventEmitter();
|
@Output() loadMore = new EventEmitter();
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ export interface Transaction {
|
|||||||
bestDescendant?: BestDescendant | null;
|
bestDescendant?: BestDescendant | null;
|
||||||
cpfpChecked?: boolean;
|
cpfpChecked?: boolean;
|
||||||
deleteAfter?: number;
|
deleteAfter?: number;
|
||||||
|
_unblinded?: any;
|
||||||
|
_deduced?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Ancestor {
|
interface Ancestor {
|
||||||
|
802
frontend/src/resources/wallycore/wallycore.js
Normal file
802
frontend/src/resources/wallycore/wallycore.js
Normal file
@ -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;
|
BIN
frontend/src/resources/wallycore/wallycore.wasm
Executable file
BIN
frontend/src/resources/wallycore/wallycore.wasm
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user