qortal/block-explorer.html
catbref ad9fa9bf9d More work on API plus basic block explorer
Added FATJAR packaging support to pom.xml

Added some "summary" fields to API calls but more need doing.
Corrected path clash from having unnecessary @OpenAPIDefinition annotations.
Added API "tags" to group similar calls (address-based, block-related, etc.)
Fixed addresses/lastreference/{address}
Implemented addresses/lastreference/{address}/unconfirmed
Implemented addresses/assets/{address}
Added /admin/stop and /admin/uptime API calls.
Moved general API info into new src/api/ApiDefinition.java
Added CORS support to ApiService
Added /transactions/address/{address} and /transactions/block/{signature}

Replaced references to test.Common.* to do with repository factory.
 This fixes issues with building FATJAR due to references to test classes
 that are omitted from FATJAR.

Changes to AccountBalanceData, BlockData and TransactionData
 to support JAX-RS rendering to JSON.

Added getUnconfirmedLastReference() to Account.

Added getAllBalances(address) to account repository
 - returns all asset balances for that address.

Added getAllSignaturesInvolvingAddress(address) to account repository
 but currently only uses TransactionRecipients HSQLDB table.
 (And even that wasn't automatically populated).

Included: very basic block explorer to be opened in browser as a file:
block-explorer.html
2018-12-04 16:34:55 +00:00

789 lines
27 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Block Explorer</title>
<style>
body { color: #404040; font-family: sans-serif; }
table { width: 100%; border-collapse: collapse; border: 1px solid #dddddd; margin-bottom: 40px; }
tr:nth-child(odd) { background-color: #eeeeee; }
th { background-color: #eeeeee; }
td { text-align: center; }
.sig { font-size: 60%; color: #405906; }
.ref { font-size: 60%; color: #609040; }
</style>
<script>
// USEFUL CODE STARTS HERE
function publicKeyToAddress(publicKey) {
var ADDRESS_VERSION = 58; // For normal user accounts starting with "Q"
// var ADDRESS_VERSION = 23; // For Automated Transaction accounts starting with "A"
var publicKeyHashSHA256 = SHA256.digest(publicKey);
var ripemd160 = new RIPEMD160(); // 'Grandfathered' broken implementation of MD160
var publicKeyHash = ripemd160.digest(publicKeyHashSHA256);
var addressArray = new Uint8Array();
addressArray = appendBuffer(addressArray, [ADDRESS_VERSION]);
addressArray = appendBuffer(addressArray, publicKeyHash);
var checkSum = SHA256.digest(SHA256.digest(addressArray));
addressArray = appendBuffer(addressArray, checkSum.subarray(0, 4));
return Base58.encode(addressArray);
}
function renderAddressTransactions(e) {
var transactions = e.target.response;
var address = this.responseURL.split("/")[5];
if (transactions.length == 0) {
document.body.innerHTML += 'No transactions involving address';
return;
}
var html = '<table id="transactions"><tr><th>Type</th><th>Timestamp</th><th>Creator</th><th>Fee</th><th>Signature</th><th>Reference</th></tr>';
for (var i=0; i<transactions.length; ++i) {
var tx = transactions[i];
var txTimestamp = new Date(tx.timestamp).toLocaleString();
var txCreatorAddress = publicKeyToAddress(base64ToArray(tx.creatorPublicKey)); // currently base64 but likely to be base58 in the future
var row = '<tr><td>' + tx.type + '</td>' +
'<td>' + txTimestamp + '</td>' +
'<td class="addr">' + addressAsLink(txCreatorAddress) + '</td>' +
'<td>' + tx.fee + ' QORA</td>' +
'<td class="sig">' + Base58.encode(base64ToArray(tx.signature)) + '</td>' +
'<td class="ref">' + Base58.encode(base64ToArray(tx.reference)) + '</td></tr>';
html += row;
}
html += '</table>';
document.body.innerHTML += html;
}
function renderAddressInfo(e) {
var balances = e.target.response;
var address = this.responseURL.split("/")[5];
var html = '<h1>Address ' + address + '</h1>';
html += '<table><tr><th>Asset ID</th><th>Balance</th></tr>';
for (var i=0; i<balances.length; ++i) {
var balanceInfo = balances[i];
html += '<tr><td>' + balanceInfo.assetId + '</td><td>' + balanceInfo.balance + '</td></tr>';
}
html += '</table>';
document.body.innerHTML = html;
XHR({
url: "http://localhost:9085/transactions/address/" + address,
onload: renderAddressTransactions,
responseType: "json"
});
}
function fetchAddressInfo(address) {
XHR({
url: "http://localhost:9085/addresses/assets/" + address,
onload: renderAddressInfo,
responseType: "json"
});
}
function addressAsLink(address) {
return '<a href="#" onclick="fetchAddressInfo(' + "'" + address + "'" + ')">' + address + '</a>';
}
function renderBlockTransactions(e) {
var transactions = e.target.response;
if (transactions.length == 0) {
document.body.innerHTML += 'No transactions in block';
return;
}
var html = '<table id="transactions"><tr><th>Type</th><th>Timestamp</th><th>Creator</th><th>Fee</th><th>Signature</th><th>Reference</th></tr>';
for (var i=0; i<transactions.length; ++i) {
var tx = transactions[i];
var txTimestamp = new Date(tx.timestamp).toLocaleString();
var txCreatorAddress = publicKeyToAddress(base64ToArray(tx.creatorPublicKey)); // currently base64 but likely to be base58 in the future
var row = '<tr><td>' + tx.type + '</td>' +
'<td>' + txTimestamp + '</td>' +
'<td class="addr">' + addressAsLink(txCreatorAddress) + '</td>' +
'<td>' + tx.fee + ' QORA</td>' +
'<td class="sig">' + Base58.encode(base64ToArray(tx.signature)) + '</td>' +
'<td class="ref">' + Base58.encode(base64ToArray(tx.reference)) + '</td></tr>';
html += row;
}
html += '</table>';
document.body.innerHTML += html;
}
function renderBlockInfo(e) {
var blockData = e.target.response;
// These properties are currently emitted as base64 by API but likely to be base58 in the future, so convert them
var props = [ "signature", "reference", "transactionsSignature", "generatorPublicKey", "generatorSignature" ];
for (var i=0; i<props.length; ++i) {
var p = props[i];
blockData[p] = Base58.encode(base64ToArray(blockData[p]));
}
// convert generator public key into address
blockData.generator = publicKeyToAddress(base64ToArray(blockData.generatorPublicKey)); // currently base64 but likely to be base58 in the future
var html = '<h1>Block ' + blockData.height + '</h1>';
html += '<table id="block"><tr><th>Property</th><th>Value</th></tr>';
for (var p in blockData) {
html += '<tr><td>' + p + '</td>';
if (p.indexOf("ignature") != -1) {
html += '<td class="sig">';
} else if (p.indexOf("eference") != -1) {
html += '<td class="ref">';
} else {
html += '<td>';
}
if (p == "generator") {
html += addressAsLink(blockData[p]);
} else {
html += blockData[p];
}
if (p.indexOf("ees") != -1)
html += " QORA";
html += '</td></tr>';
}
html += '</table>';
document.body.innerHTML = html;
// Fetch block's transactions
XHR({
url: "http://localhost:9085/transactions/block/" + blockData.signature,
onload: renderBlockTransactions,
responseType: "json"
});
}
function fetchBlockInfo(height) {
XHR({
url: "http://localhost:9085/blocks/byheight/" + height,
onload: renderBlockInfo,
responseType: "json"
});
}
function listBlock(e) {
var blockData = e.target.response;
var ourHeight = blockData.height;
var blockTimestamp = new Date(blockData.timestamp).toLocaleString();
var blockGeneratorAddress = publicKeyToAddress(base64ToArray(blockData.generatorPublicKey)); // currently base64 but likely to be base58 in the future
var ourRow = document.createElement('TR');
ourRow.innerHTML = '<td><a href="#" onclick="fetchBlockInfo(' + ourHeight + ')">' + ourHeight + '</a></td>' +
'<td>' + blockTimestamp + '</td>' +
'<td class="addr">' + addressAsLink(blockGeneratorAddress) + '</td>' +
'<td>' + blockData.generatingBalance + '</td>' +
'<td>' + blockData.transactionCount + '</td>' +
'<td>' + blockData.totalFees + ' QORA</td>';
var table = document.getElementById('blocks');
var rows = table.getElementsByTagName('TR');
for (var r = 1; r < rows.length; ++r)
if (ourHeight > rows[r].cells[0].innerText) {
table.insertBefore(ourRow, rows[r]);
return;
}
table.appendChild(ourRow);
}
function showShutdown() {
document.body.innerHTML = '<h1>Shutdown</h1>';
}
function shutdownAPI() {
XHR({
url: "http://localhost:9085/admin/stop",
onload: showShutdown,
responseType: "json"
});
}
function listBlocksFrom(height) {
document.body.innerHTML = '<h1>Blocks</h1>';
document.body.innerHTML += '<table id="blocks"><tr><th>Height</th><th>Time</th><th>Generator</th><th>Gen.Balance</th><th>TX</th><th>Fees</th></tr></table>';
if (height > 20)
document.body.innerHTML += '<a href="#" onclick="listBlocksFrom(' + (height - 20) + ')">Previous blocks...</a>';
document.body.innerHTML += '<p><a href="#" onclick="fetchBlockInfo(356928)">Block with lots of transactions</a>';
document.body.innerHTML += '<p><a href="#" onclick="shutdownAPI()">Shutdown</a>';
for (var h = height; h > 0 && h >= height - 20; --h)
XHR({
url: "http://localhost:9085/blocks/byheight/" + h,
onload: listBlock,
responseType: "json"
});
return false;
}
function initialBlocks(e) {
var height = e.target.response;
console.log("initial block height: " + height);
listBlocksFrom(height);
}
function windowOnLoad() {
XHR({
url: "http://localhost:9085/blocks/height",
onload: initialBlocks,
responseType: "json"
});
}
window.addEventListener('load', windowOnLoad, false);
// UTILITY FUNCTIONS BELOW (Base64, Base58, SHA256, etc.)
function base64ToArray(base64) {
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
function appendBuffer (buffer1, buffer2) {
buffer1 = new Uint8Array(buffer1);
buffer2 = new Uint8Array(buffer2);
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(buffer1, 0);
tmp.set(buffer2, buffer1.byteLength);
return tmp;
}
// BASE58
(function() {
var ALPHABET, ALPHABET_MAP, Base58, i;
Base58 = (typeof module !== "undefined" && module !== null ? module.exports : void 0) || (window.Base58 = {});
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
ALPHABET_MAP = {};
i = 0;
while (i < ALPHABET.length) {
ALPHABET_MAP[ALPHABET.charAt(i)] = i;
i++;
}
Base58.encode = function(buffer) {
var carry, digits, j;
if (buffer.length === 0) {
return "";
}
i = void 0;
j = void 0;
digits = [0];
i = 0;
while (i < buffer.length) {
j = 0;
while (j < digits.length) {
digits[j] <<= 8;
j++;
}
digits[0] += buffer[i];
carry = 0;
j = 0;
while (j < digits.length) {
digits[j] += carry;
carry = (digits[j] / 58) | 0;
digits[j] %= 58;
++j;
}
while (carry) {
digits.push(carry % 58);
carry = (carry / 58) | 0;
}
i++;
}
i = 0;
while (buffer[i] === 0 && i < buffer.length - 1) {
digits.push(0);
i++;
}
return digits.reverse().map(function(digit) {
return ALPHABET[digit];
}).join("");
};
Base58.decode = function(string) {
var bytes, c, carry, j;
if (string.length === 0) {
return new (typeof Uint8Array !== "undefined" && Uint8Array !== null ? Uint8Array : Buffer)(0);
}
i = void 0;
j = void 0;
bytes = [0];
i = 0;
while (i < string.length) {
c = string[i];
if (!(c in ALPHABET_MAP)) {
throw "Base58.decode received unacceptable input. Character '" + c + "' is not in the Base58 alphabet.";
}
j = 0;
while (j < bytes.length) {
bytes[j] *= 58;
j++;
}
bytes[0] += ALPHABET_MAP[c];
carry = 0;
j = 0;
while (j < bytes.length) {
bytes[j] += carry;
carry = bytes[j] >> 8;
bytes[j] &= 0xff;
++j;
}
while (carry) {
bytes.push(carry & 0xff);
carry >>= 8;
}
i++;
}
i = 0;
while (string[i] === "1" && i < string.length - 1) {
bytes.push(0);
i++;
}
return new (typeof Uint8Array !== "undefined" && Uint8Array !== null ? Uint8Array : Buffer)(bytes.reverse());
};
}).call(this);
// SHA256
SHA256 = {};
SHA256.K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
// The digest function returns the hash value (digest)
// as a 32 byte (typed) array.
// message: the string or byte array to hash
SHA256.digest = function(message) {
var h0 = 0x6a09e667;
var h1 = 0xbb67ae85;
var h2 = 0x3c6ef372;
var h3 = 0xa54ff53a;
var h4 = 0x510e527f;
var h5 = 0x9b05688c;
var h6 = 0x1f83d9ab;
var h7 = 0x5be0cd19;
var K = SHA256.K;
if (typeof message == 'string') {
var s = unescape(encodeURIComponent(message)); // UTF-8
message = new Uint8Array(s.length);
for (var i = 0; i < s.length; i++) {
message[i] = s.charCodeAt(i) & 0xff;
}
}
var length = message.length;
var byteLength = Math.floor((length + 72) / 64) * 64;
var wordLength = byteLength / 4;
var bitLength = length * 8;
var m = new Uint8Array(byteLength);
m.set(message);
m[length] = 0x80;
m[byteLength - 4] = bitLength >>> 24;
m[byteLength - 3] = (bitLength >>> 16) & 0xff;
m[byteLength - 2] = (bitLength >>> 8) & 0xff;
m[byteLength - 1] = bitLength & 0xff;
var words = new Int32Array(wordLength);
var byteIndex = 0;
for (var i = 0; i < words.length; i++) {
var word = m[byteIndex++] << 24;
word |= m[byteIndex++] << 16;
word |= m[byteIndex++] << 8;
word |= m[byteIndex++];
words[i] = word;
}
var w = new Int32Array(64);
for (var j = 0; j < wordLength; j += 16) {
for (i = 0; i < 16; i++) {
w[i] = words[j + i];
}
for (i = 16; i < 64; i++) {
var v = w[i - 15];
var s0 = (v >>> 7) | (v << 25);
s0 ^= (v >>> 18) | (v << 14);
s0 ^= (v >>> 3);
v = w[i - 2];
var s1 = (v >>> 17) | (v << 15);
s1 ^= (v >>> 19) | (v << 13);
s1 ^= (v >>> 10);
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xffffffff;
}
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
var f = h5;
var g = h6;
var h = h7;
for (i = 0; i < 64; i++) {
s1 = (e >>> 6) | (e << 26);
s1 ^= (e >>> 11) | (e << 21);
s1 ^= (e >>> 25) | (e << 7);
var ch = (e & f) ^ (~e & g);
var temp1 = (h + s1 + ch + K[i] + w[i]) & 0xffffffff;
s0 = (a >>> 2) | (a << 30);
s0 ^= (a >>> 13) | (a << 19);
s0 ^= (a >>> 22) | (a << 10);
var maj = (a & b) ^ (a & c) ^ (b & c);
var temp2 = (s0 + maj) & 0xffffffff;
h = g
g = f
f = e
e = (d + temp1) & 0xffffffff;
d = c;
c = b;
b = a;
a = (temp1 + temp2) & 0xffffffff;
}
h0 = (h0 + a) & 0xffffffff;
h1 = (h1 + b) & 0xffffffff;
h2 = (h2 + c) & 0xffffffff;
h3 = (h3 + d) & 0xffffffff;
h4 = (h4 + e) & 0xffffffff;
h5 = (h5 + f) & 0xffffffff;
h6 = (h6 + g) & 0xffffffff;
h7 = (h7 + h) & 0xffffffff;
}
var hash = new Uint8Array(32);
for (i = 0; i < 4; i++) {
hash[i] = (h0 >>> (8 * (3 - i))) & 0xff;
hash[i + 4] = (h1 >>> (8 * (3 - i))) & 0xff;
hash[i + 8] = (h2 >>> (8 * (3 - i))) & 0xff;
hash[i + 12] = (h3 >>> (8 * (3 - i))) & 0xff;
hash[i + 16] = (h4 >>> (8 * (3 - i))) & 0xff;
hash[i + 20] = (h5 >>> (8 * (3 - i))) & 0xff;
hash[i + 24] = (h6 >>> (8 * (3 - i))) & 0xff;
hash[i + 28] = (h7 >>> (8 * (3 - i))) & 0xff;
}
return hash;
}
// The hash function returns the hash value as a hex string.
// message: the string or byte array to hash
SHA256.hash = function(message) {
var digest = SHA256.digest(message);
var hex = '';
for (i = 0; i < digest.length; i++) {
var s = '0' + digest[i].toString(16);
hex += s.length > 2 ? s.substring(1) : s;
}
return hex;
}
function XHR(options) {
var xhr = new XMLHttpRequest();
if ("form" in options) {
options.data = new FormData(options.form);
if ( !("url" in options) )
options.url = options.form.action;
if ( !("method" in options) && ("method" in options.form) )
options.method = options.form.method;
}
if ("json" in options) {
options.data = JSON.stringify(options.json);
options.method = "POST";
options.responseType = "json";
}
if ( !("method" in options) )
options.method = "GET";
if ("responseType" in options)
try {
xhr.responseType = options.responseType;
} catch(e) {
console.log("XMLHttpRequest doesn't support responseType of '" + options.responseType + "'");
xhr.bodgeJSON = true;
}
if ("onload" in options) {
if (options.responseType == "json" && xhr.bodgeJSON)
xhr.addEventListener("load", function(e) { var e = { target: { response: JSON.parse(e.target.response) } }; options.onload(e) }, false);
else
xhr.addEventListener("load", options.onload, false);
}
xhr.open(options.method, options.url);
if ("json" in options)
xhr.setRequestHeader( "Content-Type", "application/json" );
if ("contentType" in options)
xhr.setRequestHeader( "Content-Type", options.contentType );
xhr.send(options.data);
return xhr;
}
// RIPEMD160
var RIPEMD160 = (function () {
function RIPEMD160() {
this.MDbuf = [];
this.MDbuf[0] = 1732584193;
this.MDbuf[1] = -271733879;
this.MDbuf[2] = -1732584194;
this.MDbuf[3] = 271733878;
this.MDbuf[4] = -1009589776;
this.working = new Int32Array(16);
this.working_ptr = 0;
this.msglen = 0;
}
RIPEMD160.prototype.reset = function () {
this.MDbuf = [];
this.MDbuf[0] = 1732584193;
this.MDbuf[1] = -271733879;
this.MDbuf[2] = -1732584194;
this.MDbuf[3] = 271733878;
this.MDbuf[4] = -1009589776;
this.working = new Int32Array(16);
this.working_ptr = 0;
this.msglen = 0;
};
RIPEMD160.prototype.compress = function (X) {
var index = 0;
var a;
var b;
var c;
var d;
var e;
var A;
var B;
var C;
var D;
var E;
var temp;
var s;
A = a = this.MDbuf[0];
B = b = this.MDbuf[1];
C = c = this.MDbuf[2];
D = d = this.MDbuf[3];
E = e = this.MDbuf[4];
for (; index < 16; index++) {
temp = a + (b ^ c ^ d) + X[RIPEMD160.IndexArray[0][index]];
a = e;
e = d;
d = (c << 10) | (c >>> 22);
c = b;
s = RIPEMD160.ArgArray[0][index];
b = ((temp << s) | (temp >>> (32 - s))) + a;
temp = A + (B ^ (C | ~D)) + X[RIPEMD160.IndexArray[1][index]] + 1352829926;
A = E;
E = D;
D = (C << 10) | (C >>> 22);
C = B;
s = RIPEMD160.ArgArray[1][index];
B = ((temp << s) | (temp >>> (32 - s))) + A;
}
for (; index < 32; index++) {
temp = a + ((b & c) | (~b & d)) + X[RIPEMD160.IndexArray[0][index]] + 1518500249;
a = e;
e = d;
d = (c << 10) | (c >>> 22);
c = b;
s = RIPEMD160.ArgArray[0][index];
b = ((temp << s) | (temp >>> (32 - s))) + a;
temp = A + ((B & D) | (C & ~D)) + X[RIPEMD160.IndexArray[1][index]] + 1548603684;
A = E;
E = D;
D = (C << 10) | (C >>> 22);
C = B;
s = RIPEMD160.ArgArray[1][index];
B = ((temp << s) | (temp >>> (32 - s))) + A;
}
for (; index < 48; index++) {
temp = a + ((b | ~c) ^ d) + X[RIPEMD160.IndexArray[0][index]] + 1859775393;
a = e;
e = d;
d = (c << 10) | (c >>> 22);
c = b;
s = RIPEMD160.ArgArray[0][index];
b = ((temp << s) | (temp >>> (32 - s))) + a;
temp = A + ((B | ~C) ^ D) + X[RIPEMD160.IndexArray[1][index]] + 1836072691;
A = E;
E = D;
D = (C << 10) | (C >>> 22);
C = B;
s = RIPEMD160.ArgArray[1][index];
B = ((temp << s) | (temp >>> (32 - s))) + A;
}
for (; index < 64; index++) {
temp = a + ((b & d) | (c & ~d)) + X[RIPEMD160.IndexArray[0][index]] + -1894007588;
a = e;
e = d;
d = (c << 10) | (c >>> 22);
c = b;
s = RIPEMD160.ArgArray[0][index];
b = ((temp << s) | (temp >>> (32 - s))) + a;
temp = A + ((B & C) | (~B & D)) + X[RIPEMD160.IndexArray[1][index]] + 2053994217;
A = E;
E = D;
D = (C << 10) | (C >>> 22);
C = B;
s = RIPEMD160.ArgArray[1][index];
B = ((temp << s) | (temp >>> (32 - s))) + A;
}
for (; index < 80; index++) {
temp = a + (b ^ (c | ~d)) + X[RIPEMD160.IndexArray[0][index]] + -1454113458;
a = e;
e = d;
d = (c << 10) | (c >>> 22);
c = b;
s = RIPEMD160.ArgArray[0][index];
b = ((temp << s) | (temp >>> (32 - s))) + a;
temp = A + (B ^ C ^ D) + X[RIPEMD160.IndexArray[1][index]];
A = E;
E = D;
D = (C << 10) | (C >>> 22);
C = B;
s = RIPEMD160.ArgArray[1][index];
B = ((temp << s) | (temp >>> (32 - s))) + A;
}
D += c + this.MDbuf[1];
this.MDbuf[1] = this.MDbuf[2] + d + E;
this.MDbuf[2] = this.MDbuf[3] + e + A;
this.MDbuf[3] = this.MDbuf[4] + a + B;
this.MDbuf[4] = this.MDbuf[0] + b + C;
this.MDbuf[0] = D;
};
RIPEMD160.prototype.MDfinish = function (array, lswlen, mswlen) {
var X = array;
X[(lswlen >> 2) & 15] ^= 1 << (((lswlen & 3) << 3) + 7);
if (((lswlen & 63) > 55)) {
this.compress(X);
for (var i = 0; i < 14; i++) {
X[i] = 0;
}
}
X[14] = lswlen << 3;
X[15] = (lswlen >> 29) | (mswlen << 3);
this.compress(X);
};
RIPEMD160.prototype.update = function (input) {
for (var i = 0; i < input.length; i++) {
this.working[this.working_ptr >> 2] ^= input[i] << ((this.working_ptr & 3) << 3);
this.working_ptr++;
if ((this.working_ptr == 64)) {
this.compress(this.working);
for (var j = 0; j < 16; j++) {
this.working[j] = 0;
}
this.working_ptr = 0;
}
}
this.msglen += input.length;
};
RIPEMD160.prototype.digestBin = function () {
this.MDfinish(this.working, this.msglen, 0);
//var res = new Int8Array();
var res = [];
for (var i = 0; i < 20; i++) {
res[i] = ((this.MDbuf[i >> 2] >>> ((i & 3) << 3)) & 255);
}
return new Uint8Array(res);
};
RIPEMD160.prototype.digest = function (input) {
this.update(new Int8Array(input));
return this.digestBin();
};
RIPEMD160.ArgArray = [[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6], [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]];
RIPEMD160.IndexArray = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13], [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]];
return RIPEMD160;
})();
</script>
</head>
<body>
Making initial call to API...
<p>
If nothing happens then check API is running!
</body>
</html>