mirror of
https://github.com/mempool/mempool.git
synced 2024-11-20 10:21:52 +01:00
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
@ -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.');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
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()
|
||||
);
|
||||
}
|
@ -9,33 +9,33 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
<div>
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, txId]">
|
||||
<span class="d-inline d-lg-none">{{ txId | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ txId }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="txId"></app-clipboard>
|
||||
</div>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, txId]">
|
||||
<span class="d-inline d-lg-none">{{ txId | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ txId }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="txId"></app-clipboard>
|
||||
</div>
|
||||
|
||||
<div class="container-buttons">
|
||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success">
|
||||
<ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container>
|
||||
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="tx && !tx?.status.confirmed">
|
||||
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
|
||||
</ng-template>
|
||||
<div class="container-buttons">
|
||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success">
|
||||
<ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container>
|
||||
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="tx && !tx?.status.confirmed">
|
||||
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<ng-template [ngIf]="!isLoadingTx && !error">
|
||||
@ -198,7 +198,7 @@
|
||||
|
||||
<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>
|
||||
<div class="box">
|
||||
|
@ -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<string>();
|
||||
commitments: Map<any, any>;
|
||||
|
||||
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<Transaction>;
|
||||
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<Transaction>;
|
||||
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<any> {
|
||||
@ -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:
|
||||
// <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 class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<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="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>
|
||||
<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">
|
||||
<ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout">
|
||||
<i class="arrow grey"></i>
|
||||
@ -66,7 +69,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="displayDetails">
|
||||
<td colspan="3">
|
||||
<td colspan="3" class="details-container" >
|
||||
<table class="table table-striped table-borderless details-table mb-3">
|
||||
<tbody>
|
||||
<ng-template [ngIf]="vin.scriptsig">
|
||||
@ -114,10 +117,10 @@
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@ -145,7 +148,7 @@
|
||||
<ng-template [ngIf]="vout.asset && vout.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
|
||||
<div *ngIf="assetsMinimal && assetsMinimal[vout.asset]">
|
||||
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vout }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #defaultOutput>
|
||||
<app-amount [satoshis]="vout.value"></app-amount>
|
||||
@ -162,7 +165,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="displayDetails">
|
||||
<td colspan="3">
|
||||
<td colspan="3" class=" details-container" >
|
||||
<table class="table table-striped table-borderless details-table mb-3">
|
||||
<tbody>
|
||||
<tr *ngIf="vout.scriptpubkey_type">
|
||||
@ -235,5 +238,4 @@
|
||||
{{ assetsMinimal[item.asset][0] }}
|
||||
<br />
|
||||
<a [routerLink]="['/asset/' | relativeUrl, item.asset]">{{ item.asset | shortenString : 13 }}</a>
|
||||
<br /><br />
|
||||
</ng-template>
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -17,6 +17,8 @@ export interface Transaction {
|
||||
bestDescendant?: BestDescendant | null;
|
||||
cpfpChecked?: boolean;
|
||||
deleteAfter?: number;
|
||||
_unblinded?: any;
|
||||
_deduced?: boolean;
|
||||
}
|
||||
|
||||
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…
Reference in New Issue
Block a user