Skip to content

Commit ab94d29

Browse files
committed
remove dependencies, convert to typescript
1 parent 27a4e43 commit ab94d29

File tree

9 files changed

+979
-4882
lines changed

9 files changed

+979
-4882
lines changed

.gitignore

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,2 @@
1-
# Logs
2-
logs
3-
*.log
4-
5-
# Runtime data
6-
pids
7-
*.pid
8-
*.seed
9-
10-
# Directory for instrumented libs generated by jscoverage/JSCover
11-
lib-cov
12-
13-
# Coverage directory used by tools like istanbul
14-
coverage
15-
16-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17-
.grunt
18-
19-
# Compiled binary addons (http://nodejs.org/api/addons.html)
20-
build/Release
21-
22-
# Dependency directory
23-
# Commenting this out is preferred by some people, see
24-
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25-
node_modules
26-
27-
# Users Environment Variables
28-
.lock-wscript
1+
node_modules/
2+
dist/

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
sudo: false
22
language: node_js
33
node_js:
4-
- "0.10"
5-
- "0.12"
64
- "4"
75
- "5"
86
env:

index.d.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

index.js

Lines changed: 0 additions & 133 deletions
This file was deleted.

index.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/** Produces a Merkle proof for a single tx within a list. */
2+
export async function getProof(txIds: string[], txIndex: number) {
3+
const proof = {
4+
txId: txIds[txIndex],
5+
txIndex: txIndex,
6+
sibling: [],
7+
};
8+
9+
let tree = new Array(txIds.length);
10+
for (let i = 0; i < tree.length; ++i) {
11+
tree[i] = reverse(fromHex(txIds[i]));
12+
}
13+
let target = tree[txIndex];
14+
15+
while (tree.length !== 1) {
16+
const newTree = new Array(~~((tree.length + 1) / 2));
17+
for (let j = 0; j < tree.length; j += 2) {
18+
const hash1 = tree[j];
19+
const hash2 = tree[Math.min(j + 1, tree.length - 1)];
20+
21+
newTree[j / 2] = await sha256x2(hash1, hash2);
22+
23+
if (isEqual(target, hash1)) {
24+
proof.sibling.push(toHex(reverse(hash2)));
25+
target = newTree[j / 2];
26+
} else if (isEqual(target, hash2)) {
27+
proof.sibling.push(toHex(reverse(hash1)));
28+
target = newTree[j / 2];
29+
}
30+
}
31+
32+
tree = newTree;
33+
}
34+
35+
return proof;
36+
}
37+
38+
export interface TxMerkleProof {
39+
txId: string;
40+
txIndex: number;
41+
sibling: string[];
42+
}
43+
44+
/** Evaluates a transaction merkle proof, returning the root hash. */
45+
export async function getTxMerkle(proofObj: TxMerkleProof) {
46+
let target = reverse(fromHex(proofObj.txId));
47+
let txIndex = proofObj.txIndex;
48+
const sibling = proofObj.sibling;
49+
50+
for (let i = 0; i < proofObj.sibling.length; ++i, txIndex = ~~(txIndex / 2)) {
51+
if (txIndex % 2 === 1) {
52+
target = await sha256x2(reverse(fromHex(sibling[i])), target);
53+
} else {
54+
target = await sha256x2(target, reverse(fromHex(sibling[i])));
55+
}
56+
}
57+
58+
return toHex(reverse(target));
59+
}
60+
61+
/** Computes the Merkle root of a list of Bitcoin transaction IDs. */
62+
export async function getMerkleRoot(txIds: string[]) {
63+
let tree = new Array(txIds.length) as Uint8Array[];
64+
for (let i = 0; i < tree.length; ++i) {
65+
tree[i] = reverse(fromHex(txIds[i]));
66+
}
67+
68+
while (tree.length !== 1) {
69+
const newTree = new Array(~~((tree.length + 1) / 2));
70+
for (let j = 0; j < tree.length; j += 2) {
71+
const hash1 = tree[j];
72+
const hash2 = tree[Math.min(j + 1, tree.length - 1)];
73+
74+
newTree[j / 2] = await sha256x2(hash1, hash2);
75+
}
76+
77+
tree = newTree;
78+
}
79+
80+
return toHex(reverse(tree[0]));
81+
}
82+
83+
// This ugly construction is required to make a library that bundles without
84+
// error for browser use, but still uses Node builtins when running in node.
85+
let nodeCrypto = null;
86+
try {
87+
nodeCrypto = require("crypto");
88+
} catch (_) {}
89+
90+
/** Computes a double-SHA256 hash of [a, b]. Async in-browser. */
91+
async function sha256x2(buf1: Uint8Array, buf2: Uint8Array) {
92+
if (nodeCrypto) {
93+
// Synchronous native SHA256 via require("crypto")
94+
const hash1 = nodeCrypto
95+
.createHash("sha256")
96+
.update(buf1)
97+
.update(buf2)
98+
.digest();
99+
const hash2 = nodeCrypto.createHash("sha256").update(hash1).digest();
100+
return hash2;
101+
} else {
102+
// Asynchronous native SHA256 via SubtleCrypto
103+
const comb = new Uint8Array(buf1.length + buf2.length);
104+
comb.set(buf1, 0);
105+
comb.set(buf2, buf1.length);
106+
const hash1 = await crypto.subtle.digest("SHA-256", comb);
107+
const hash2 = await crypto.subtle.digest("SHA-256", hash1);
108+
return hash2;
109+
}
110+
}
111+
112+
/** Reverse a byte array in-place. */
113+
function reverse(buf: Uint8Array) {
114+
return Array.prototype.reverse.call(buf);
115+
}
116+
117+
/** Check deep equality between two byte arrays. */
118+
function isEqual(buf1: Uint8Array, buf2: Uint8Array) {
119+
if (buf1.length !== buf2.length) {
120+
return false;
121+
}
122+
123+
for (let i = 0; i < buf1.length; ++i) {
124+
if (buf1[i] !== buf2[i]) {
125+
return false;
126+
}
127+
}
128+
129+
return true;
130+
}
131+
132+
/** Parses hex to a Uint8Array */
133+
function fromHex(hex: string): Uint8Array {
134+
return new Uint8Array(
135+
hex.match(/[\da-f]{2}/gi).map(function (h) {
136+
return parseInt(h, 16);
137+
})
138+
);
139+
}
140+
141+
/** Print Uint8Array to hex */
142+
function toHex(arr: Uint8Array): string {
143+
return Array.prototype.map
144+
.call(arr, function (byte: number) {
145+
return ("0" + (byte & 0xff).toString(16)).slice(-2);
146+
})
147+
.join("");
148+
}

0 commit comments

Comments
 (0)