Beta 7 with some repo names changed.

This commit is contained in:
Luis Fernando Molina committed 2020-11-15 13:05:19 +01:00
1 parent 0f7b40cca7
commit 963c19493a
3408 files changed
+462217 -15516

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
const Bytes = require("./bytes");
const Nat = require("./nat");
const elliptic = require("elliptic");
const rlp = require("./rlp");
const secp256k1 = new (elliptic.ec)("secp256k1"); // eslint-disable-line
const {keccak256, keccak256s} = require("./hash");
const create = entropy => {
const innerHex = keccak256(Bytes.concat(Bytes.random(32), entropy || Bytes.random(32)));
const middleHex = Bytes.concat(Bytes.concat(Bytes.random(32), innerHex), Bytes.random(32));
const outerHex = keccak256(middleHex);
return fromPrivate(outerHex);
}
const toChecksum = address => {
const addressHash = keccak256s(address.slice(2));
let checksumAddress = "0x";
for (let i = 0; i < 40; i++)
checksumAddress += parseInt(addressHash[i + 2], 16) > 7
? address[i + 2].toUpperCase()
: address[i + 2];
return checksumAddress;
}
const fromPrivate = privateKey => {
const buffer = new Buffer(privateKey.slice(2), "hex");
const ecKey = secp256k1.keyFromPrivate(buffer);
const publicKey = "0x" + ecKey.getPublic(false, 'hex').slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return {
address: address,
privateKey: privateKey
}
}
const encodeSignature = ([v, r, s]) =>
Bytes.flatten([r,s,v]);
const decodeSignature = (hex) =>
[Bytes.slice(64,65,hex), Bytes.slice(0,32,hex), Bytes.slice(32,64,hex)];
const makeSign = addToV => (hash, privateKey) => {
const signature = secp256k1
.keyFromPrivate(new Buffer(privateKey.slice(2), "hex"))
.sign(new Buffer(hash.slice(2), "hex"), {canonical: true});
return encodeSignature([
Bytes.pad(1, Bytes.fromNumber(addToV + signature.recoveryParam)),
Bytes.pad(32, Bytes.fromNat("0x" + signature.r.toString(16))),
Bytes.pad(32, Bytes.fromNat("0x" + signature.s.toString(16)))]);
}
const sign = makeSign(27); // v=27|28 instead of 0|1...
const recover = (hash, signature) => {
const vals = decodeSignature(signature);
const vrs = {v: Bytes.toNumber(vals[0]), r:vals[1].slice(2), s:vals[2].slice(2)};
const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - (vrs.v % 2)); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that
const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return address;
}
const transactionSigningData = tx =>
rlp.encode([
Bytes.fromNat(tx.nonce),
Bytes.fromNat(tx.gasPrice),
Bytes.fromNat(tx.gas),
tx.to.toLowerCase(),
Bytes.fromNat(tx.value),
tx.data,
Bytes.fromNat(tx.chainId || "0x1"),
"0x",
"0x"]);
const signTransaction = (tx, privateKey) => {
const signingData = transactionSigningData(tx);
const signature = makeSign(Nat.toNumber(tx.chainId || "0x1") * 2 + 35)(keccak256(signingData), privateKey);
const rawTransaction = rlp.decode(signingData).slice(0,6).concat(decodeSignature(signature));
return rlp.encode(rawTransaction);
}
const recoverTransaction = (rawTransaction) => {
const values = rlp.decode(rawTransaction);
const signature = encodeSignature(values.slice(6,9));
const recovery = Bytes.toNumber(values[6]);
const extraData = recovery < 35 ? [] : [Bytes.fromNumber((recovery - 35) >> 1), "0x", "0x"]
const signingData = values.slice(0,6).concat(extraData);
const signingDataHex = rlp.encode(signingData);
return recover(keccak256(signingDataHex), signature);
}
module.exports = {
create,
toChecksum,
fromPrivate,
sign,
recover,
signTransaction,
recoverTransaction,
transactionSigningData,
encodeSignature,
decodeSignature
}
+195
View File
@@ -0,0 +1,195 @@
const Api = provider => {
const Nat = require("./nat");
const Map = require("./map");
const Bytes = require("./bytes");
const keccak256s = require("./hash").keccak256s;
const send = method => (...params) =>
new Promise((resolve,reject) =>
provider.send(method, params, (err, result) =>
err ? reject(err) : resolve(result)));
// TODO check inputs
// TODO move to proper file
const encodeABI = (type, value) => {
if (type === "bytes") {
const length = Bytes.length(value);
const nextMul32 = (((length - 1) / 32 | 0) + 1) * 32;
const lengthEncoded = encodeABI("uint256", Nat.fromNumber(length)).data;
const bytesEncoded = Bytes.padRight(nextMul32, value);
return {data: Bytes.concat(lengthEncoded, bytesEncoded), dynamic: true};
} else if (type === "uint256" || type === "bytes32") {
return {data: Bytes.pad(32, value), dynamic: false};
} else {
throw "Eth-lib can't encode ABI type " + type + " yet.";
}
}
const sendTransaction = send("eth_sendTransaction");
const sendRawTransaction = send("eth_sendRawTransaction");
const getTransactionReceipt = send("eth_getTransactionReceipt");
const compileSolidity = send("eth_compileSolidity");
const call = send("eth_call");
const getBalance = send("eth_getBalance");
const accounts = send("eth_accounts");
const removeEmptyTo = tx =>
tx.to === "" || tx.to === "0x" ? Map.remove("to")(tx) : tx;
const waitTransactionReceipt = getTransactionReceipt; // TODO: implement correctly
const addTransactionDefaults = tx =>
// Get basic defaults
Promise.all([
tx.chainId || send("net_version")(),
tx.gasPrice || send("eth_gasPrice")(),
tx.nonce || send("eth_getTransactionCount")(tx.from,"latest"),
tx.value || "0x0",
tx.data || "0x"])
// Add them to tx
.then(([chainId, gasPrice, nonce, value, data]) => {
return Map.merge(tx)({chainId: Nat.fromNumber(chainId), gasPrice, nonce, value, data});
})
.then(tx => {
// Add gas default by estimating
if (!tx.gas) {
let estimateTx = {};
estimateTx.from = tx.from;
if (tx.to !== "" && tx.to !== "0x")
estimateTx.to = tx.to;
estimateTx.value = tx.value;
estimateTx.nonce = tx.nonce;
estimateTx.data = tx.data;
return send("eth_estimateGas")(estimateTx)
.then(usedGas => {
return Map.merge(tx)({gas: Nat.div(Nat.mul(usedGas,"0x6"),"0x5")});
});
} else {
return Promise.resolve(tx);
}
});
const sendTransactionWithDefaults = tx =>
addTransactionDefaults(tx)
.then(tx => sendTransaction(removeEmptyTo(tx)));
const callWithDefaults = (tx, block) =>
addTransactionDefaults(tx)
.then(tx => call(removeEmptyTo(tx), block || "latest"));
const callMethodData = method => (...params) => {
const methodSig = method.name + "(" + method.inputs.map(i => i.type).join(",") + ")";
const methodHash = keccak256s(methodSig).slice(0,10);
let encodedParams = params.map((param,i) => encodeABI(method.inputs[i].type, param));
var headBlock = "0x";
let dataBlock = "0x";
for (var i = 0; i < encodedParams.length; ++i) {
if (encodedParams[i].dynamic) {
var dataLoc = encodedParams.length * 32 + Bytes.length(dataBlock);
headBlock = Bytes.concat(headBlock, Bytes.pad(32, Nat.fromNumber(dataLoc)));
dataBlock = Bytes.concat(dataBlock, encodedParams[i].data);
} else {
headBlock = Bytes.concat(headBlock, encodedParams[i].data);
}
}
return Bytes.flatten([methodHash, headBlock, dataBlock]);
}
// Address, Address, ContractInterface -> Contract
const contract = (from, address, contractInterface) => {
let contract = {};
contract._address = address;
contract._from = from;
contract.broadcast = {};
contractInterface.forEach(method => {
if (method && method.name) {
const call = (type, value) => (...params) => {
const transaction = {
from: from,
to: address,
value: value,
data: callMethodData(method)(...params)
};
return type === "data"
? Promise.resolve(transaction)
: method.constant
? callWithDefaults(transaction)
: sendTransactionWithDefaults(transaction)
.then(type === "receipt" ? waitTransactionReceipt : (x => x));
};
contract[method.name] = call("receipt", "0x0");
if (!method.constant) {
contract[method.name+"_data"] = call("data", "0x0");
contract[method.name+"_pay"] = value => (...params) => call("receipt", value)(...params);
contract[method.name+"_pay_txHash"] = value => (...params) => call("txHash", value)(...params);
contract[method.name+"_txHash"] = call("txHash", "0x0");
}
}
});
return contract;
}
// Address, Bytecode -> TxHash
const deployBytecode_txHash = (from, code) =>
sendTransactionWithDefaults({from: from, data: code, to: ""});
// Address, Bytecode -> Receipt
const deployBytecode = (from, code) =>
deployBytecode_txHash(from,code)
.then(waitTransactionReceipt);
// Address, Bytecode, ContractInterface
const deployBytecodeContract = (from, code, contractInterface) =>
deployBytecode(from, code)
.then(receipt => contract(from, receipt.contractAddress, contractInterface));
// Address, String, Address -> Contract
const solidityContract = (from, source, at) =>
compileSolidity(source)
.then(({info:{abiDefinition}}) => contract(from, at, abiDefinition));
// Address, String -> TxHash
const deploySolidity_txHash = (from, source) =>
compileSolidity(source)
.then(({code}) => deployBytecode_txHash(from, code));
// Address, String -> Receipt
const deploySolidity = (from, source) =>
deploySolidity_txHash(from, source)
.then(waitTransactionReceipt);
// Address, String -> Contract
const deploySolidityContract = (from, source) =>
compileSolidity(source)
.then(({code, info:{abiDefinition}}) =>
deployBytecodeContract(from, code, abiDefinition));
return {
send,
sendTransaction,
sendRawTransaction,
getTransactionReceipt,
call,
getBalance,
accounts,
waitTransactionReceipt,
addTransactionDefaults,
sendTransactionWithDefaults,
callWithDefaults,
callMethodData,
contract,
deployBytecode_txHash,
deployBytecode,
deployBytecodeContract,
compileSolidity,
solidityContract,
deploySolidity_txHash,
deploySolidity,
deploySolidityContract
}
}
module.exports = Api;
+35
View File
@@ -0,0 +1,35 @@
const generate = (num, fn) => {
let a = [];
for (var i = 0; i < num; ++i)
a.push(fn(i));
return a;
};
const replicate = (num, val) =>
generate(num, () => val);
const concat = (a, b) =>
a.concat(b);
const flatten = (a) => {
let r = [];
for (let j = 0, J = a.length; j < J; ++j)
for (let i = 0, I = a[j].length; i < I; ++i)
r.push(a[j][i]);
return r;
}
const chunksOf = (n,a) => {
let b = [];
for (let i = 0, l = a.length; i < l; i += n)
b.push(a.slice(i, i + n));
return b;
}
module.exports = {
generate,
replicate,
concat,
flatten,
chunksOf
}
+179
View File
@@ -0,0 +1,179 @@
const A = require("./array.js");
const at = (bytes, index) =>
parseInt(bytes.slice(index * 2 + 2, index * 2 + 4), 16);
const random = bytes => {
let rnd;
if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues)
rnd = window.crypto.getRandomValues(new Uint8Array(bytes));
else if (typeof require !== "undefined")
rnd = require("c" + "rypto").randomBytes(bytes);
else
throw "Safe random numbers not available.";
let hex = "0x";
for (let i = 0; i < bytes; ++i)
hex += ("00" + rnd[i].toString(16)).slice(-2);
return hex;
};
const length = a =>
(a.length - 2) / 2;
const flatten = (a) =>
"0x" + a.reduce((r,s) => r + s.slice(2), "");
const slice = (i,j,bs) =>
"0x" + bs.slice(i*2+2,j*2+2);
const reverse = hex => {
let rev = "0x";
for (let i = 0, l = length(hex); i < l; ++i) {
rev += hex.slice((l-i)*2, (l-i+1)*2);
}
return rev;
}
const pad = (l,hex) =>
hex.length === l*2+2 ? hex : pad(l,"0x"+"0"+hex.slice(2));
const padRight = (l,hex) =>
hex.length === l*2+2 ? hex : padRight(l,hex+"0");
const toArray = hex => {
let arr = [];
for (let i = 2, l = hex.length; i < l; i += 2)
arr.push(parseInt(hex.slice(i, i + 2), 16));
return arr;
}
const fromArray = arr => {
let hex = "0x";
for (let i = 0, l = arr.length; i < l; ++i) {
let b = arr[i];
hex += (b < 16 ? "0" : "") + b.toString(16);
}
return hex;
}
const toUint8Array = hex =>
new Uint8Array(toArray(hex));
const fromUint8Array = arr =>
fromArray([].slice.call(arr, 0));
const fromNumber = num => {
let hex = num.toString(16);
return hex.length % 2 === 0 ? "0x" + hex : "0x0" + hex ;
};
const toNumber = hex =>
parseInt(hex.slice(2), 16);
const concat = (a, b) =>
a.concat(b.slice(2));
const fromNat = bn =>
bn === "0x0" ? "0x" : bn.length % 2 === 0 ? bn : "0x0" + bn.slice(2);
const toNat = bn =>
bn[2] === "0" ? "0x" + bn.slice(3) : bn;
const fromAscii = ascii => {
let hex = "0x";
for (let i = 0; i < ascii.length; ++i)
hex += ("00" + ascii.charCodeAt(i).toString(16)).slice(-2);
return hex;
};
const toAscii = hex => {
let ascii = "";
for (let i = 2; i < hex.length; i += 2)
ascii += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16));
return ascii;
};
// From https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
const fromString = s => {
const makeByte = uint8 => {
const b = uint8.toString(16);
return b.length < 2 ? "0" + b : b;
};
let bytes = "0x";
for (let ci = 0; ci != s.length; ci++) {
let c = s.charCodeAt(ci);
if (c < 128) {
bytes += makeByte(c);
continue;
}
if (c < 2048) {
bytes += makeByte(c >> 6 | 192);
} else {
if (c > 0xd7ff && c < 0xdc00) {
if (++ci == s.length) return null;
let c2 = s.charCodeAt(ci);
if (c2 < 0xdc00 || c2 > 0xdfff) return null;
c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
bytes += makeByte(c >> 18 | 240);
bytes += makeByte(c>> 12 & 63 | 128);
} else { // c <= 0xffff
bytes += makeByte(c >> 12 | 224);
}
bytes += makeByte(c >> 6 & 63 | 128);
}
bytes += makeByte(c & 63 | 128);
}
return bytes;
};
const toString = (bytes) => {
let s = '';
let i = 0;
let l = length(bytes);
while (i < l) {
let c = at(bytes, i++);
if (c > 127) {
if (c > 191 && c < 224) {
if (i >= l) return null;
c = (c & 31) << 6 | at(bytes, i) & 63;
} else if (c > 223 && c < 240) {
if (i + 1 >= l) return null;
c = (c & 15) << 12 | (at(bytes, i) & 63) << 6 | at(bytes, ++i) & 63;
} else if (c > 239 && c < 248) {
if (i+2 >= l) return null;
c = (c & 7) << 18 | (at(bytes, i) & 63) << 12 | (at(bytes, ++i) & 63) << 6 | at(bytes, ++i) & 63;
} else return null;
++i;
}
if (c <= 0xffff) s += String.fromCharCode(c);
else if (c <= 0x10ffff) {
c -= 0x10000;
s += String.fromCharCode(c >> 10 | 0xd800)
s += String.fromCharCode(c & 0x3FF | 0xdc00)
} else return null;
}
return s;
};
module.exports = {
random,
length,
concat,
flatten,
slice,
reverse,
pad,
padRight,
fromAscii,
toAscii,
fromString,
toString,
fromNumber,
toNumber,
fromNat,
toNat,
fromArray,
toArray,
fromUint8Array,
toUint8Array
}
+62
View File
@@ -0,0 +1,62 @@
const Bytes = require("./bytes");
const inis = "pbtdkgxjfvlrmnsz".split("");
const mids = "auie".split("");
const back = [inis,mids].map(chars => {
let map = {};
for (let i = 0; i < chars.length; ++i)
map[chars[i]] = i;
return map;
});
const syllableFromB64 = b64 => {
const ini = (b64 >>> 2) & 15;
const mid = (b64 >>> 0) & 3;
return inis[ini] + mids[mid];
}
const syllableToB64 = syllable => {
const ini = back[0][syllable[0]];
const mid = back[1][syllable[1]];
return ini * 4 + mid;
}
const b64sFromBytes = bytes => {
//BoooooBoooooBoooooBooooo
//BoooooooBoooooooBooooooo
let b64s = [], b64;
for (let i = 0, l = Math.ceil(bytes.length * 8 / 6); i < l; ++i) {
let j = i / 8 * 6 | 0;
b64s.push
( i % 4 === 0 ? (bytes[j+0] / 4 | 0)
: i % 4 === 1 ? bytes[j+0] % 4 * 16 + (bytes[j+1] / 16 | 0)
: i % 4 === 2 ? bytes[j+0] % 16 * 4 + (bytes[j+1] / 64 | 0)
: bytes[j+0] % 64 * 1);
}
return b64s;
}
const b64sToBytes = b64s => {
let bytes = [];
for (let i = 0, l = Math.floor(b64s.length * 6 / 8); i < l; ++i) {
let j = i / 6 * 8 | 0;
bytes.push
( i % 3 === 0 ? b64s[j+0] % 64 * 4 + (b64s[j+1] / 16 | 0)
: i % 3 === 1 ? b64s[j+0] % 16 * 16 + (b64s[j+1] / 4 | 0)
: b64s[j+0] % 4 * 64 + (b64s[j+1] / 1 | 0));
}
return bytes;
}
const fromBytes = bytes =>
b64sFromBytes(Bytes.toArray(bytes)).map(syllableFromB64).join("");
const toBytes = syllables =>
Bytes.fromArray(b64sToBytes(syllables.match(/\w\w/g).map(syllableToB64)));
module.exports = {
fromBytes,
toBytes
}
+340
View File
@@ -0,0 +1,340 @@
// This was ported from https://github.com/emn178/js-sha3, with some minor
// modifications and pruning. It is licensed under MIT:
//
// Copyright 2015-2016 Chen, Yi-Cyuan
//
// 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 HEX_CHARS = '0123456789abcdef'.split('');
const KECCAK_PADDING = [1, 256, 65536, 16777216];
const SHIFT = [0, 8, 16, 24];
const RC = [
1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
const Keccak = (bits) => ({
blocks: [],
reset: true,
block: 0,
start: 0,
blockCount: (1600 - (bits << 1)) >> 5,
outputBlocks: bits >> 5,
s: (s=>[].concat(s,s,s,s,s))([0,0,0,0,0,0,0,0,0,0])
});
const update = (state, message) => {
var length = message.length,
blocks = state.blocks,
byteCount = state.blockCount << 2,
blockCount = state.blockCount,
outputBlocks = state.outputBlocks,
s = state.s,
index = 0,
i,
code;
// update
while (index < length) {
if (state.reset) {
state.reset = false;
blocks[0] = state.block;
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
if (typeof message !== "string") {
for (i = state.start; index < length && i < byteCount; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
} else {
for (i = state.start; index < length && i < byteCount; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
}
}
}
state.lastByteIndex = i;
if (i >= byteCount) {
state.start = i - byteCount;
state.block = blocks[blockCount];
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
state.reset = true;
} else {
state.start = i;
}
}
// finalize
i = state.lastByteIndex;
blocks[i >> 2] |= KECCAK_PADDING[i & 3];
if (state.lastByteIndex === byteCount) {
blocks[0] = blocks[blockCount];
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
blocks[blockCount - 1] |= 0x80000000;
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
// toString
var hex = '', i = 0, j = 0, block;
while (j < outputBlocks) {
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
block = s[i];
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
}
if (j % blockCount === 0) {
f(s);
i = 0;
}
}
return "0x" + hex;
}
const f = s => {
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
for (n = 0; n < 48; n += 2) {
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
s[0] ^= h;
s[1] ^= l;
s[10] ^= h;
s[11] ^= l;
s[20] ^= h;
s[21] ^= l;
s[30] ^= h;
s[31] ^= l;
s[40] ^= h;
s[41] ^= l;
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
s[2] ^= h;
s[3] ^= l;
s[12] ^= h;
s[13] ^= l;
s[22] ^= h;
s[23] ^= l;
s[32] ^= h;
s[33] ^= l;
s[42] ^= h;
s[43] ^= l;
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
s[4] ^= h;
s[5] ^= l;
s[14] ^= h;
s[15] ^= l;
s[24] ^= h;
s[25] ^= l;
s[34] ^= h;
s[35] ^= l;
s[44] ^= h;
s[45] ^= l;
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
s[6] ^= h;
s[7] ^= l;
s[16] ^= h;
s[17] ^= l;
s[26] ^= h;
s[27] ^= l;
s[36] ^= h;
s[37] ^= l;
s[46] ^= h;
s[47] ^= l;
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
s[8] ^= h;
s[9] ^= l;
s[18] ^= h;
s[19] ^= l;
s[28] ^= h;
s[29] ^= l;
s[38] ^= h;
s[39] ^= l;
s[48] ^= h;
s[49] ^= l;
b0 = s[0];
b1 = s[1];
b32 = (s[11] << 4) | (s[10] >>> 28);
b33 = (s[10] << 4) | (s[11] >>> 28);
b14 = (s[20] << 3) | (s[21] >>> 29);
b15 = (s[21] << 3) | (s[20] >>> 29);
b46 = (s[31] << 9) | (s[30] >>> 23);
b47 = (s[30] << 9) | (s[31] >>> 23);
b28 = (s[40] << 18) | (s[41] >>> 14);
b29 = (s[41] << 18) | (s[40] >>> 14);
b20 = (s[2] << 1) | (s[3] >>> 31);
b21 = (s[3] << 1) | (s[2] >>> 31);
b2 = (s[13] << 12) | (s[12] >>> 20);
b3 = (s[12] << 12) | (s[13] >>> 20);
b34 = (s[22] << 10) | (s[23] >>> 22);
b35 = (s[23] << 10) | (s[22] >>> 22);
b16 = (s[33] << 13) | (s[32] >>> 19);
b17 = (s[32] << 13) | (s[33] >>> 19);
b48 = (s[42] << 2) | (s[43] >>> 30);
b49 = (s[43] << 2) | (s[42] >>> 30);
b40 = (s[5] << 30) | (s[4] >>> 2);
b41 = (s[4] << 30) | (s[5] >>> 2);
b22 = (s[14] << 6) | (s[15] >>> 26);
b23 = (s[15] << 6) | (s[14] >>> 26);
b4 = (s[25] << 11) | (s[24] >>> 21);
b5 = (s[24] << 11) | (s[25] >>> 21);
b36 = (s[34] << 15) | (s[35] >>> 17);
b37 = (s[35] << 15) | (s[34] >>> 17);
b18 = (s[45] << 29) | (s[44] >>> 3);
b19 = (s[44] << 29) | (s[45] >>> 3);
b10 = (s[6] << 28) | (s[7] >>> 4);
b11 = (s[7] << 28) | (s[6] >>> 4);
b42 = (s[17] << 23) | (s[16] >>> 9);
b43 = (s[16] << 23) | (s[17] >>> 9);
b24 = (s[26] << 25) | (s[27] >>> 7);
b25 = (s[27] << 25) | (s[26] >>> 7);
b6 = (s[36] << 21) | (s[37] >>> 11);
b7 = (s[37] << 21) | (s[36] >>> 11);
b38 = (s[47] << 24) | (s[46] >>> 8);
b39 = (s[46] << 24) | (s[47] >>> 8);
b30 = (s[8] << 27) | (s[9] >>> 5);
b31 = (s[9] << 27) | (s[8] >>> 5);
b12 = (s[18] << 20) | (s[19] >>> 12);
b13 = (s[19] << 20) | (s[18] >>> 12);
b44 = (s[29] << 7) | (s[28] >>> 25);
b45 = (s[28] << 7) | (s[29] >>> 25);
b26 = (s[38] << 8) | (s[39] >>> 24);
b27 = (s[39] << 8) | (s[38] >>> 24);
b8 = (s[48] << 14) | (s[49] >>> 18);
b9 = (s[49] << 14) | (s[48] >>> 18);
s[0] = b0 ^ (~b2 & b4);
s[1] = b1 ^ (~b3 & b5);
s[10] = b10 ^ (~b12 & b14);
s[11] = b11 ^ (~b13 & b15);
s[20] = b20 ^ (~b22 & b24);
s[21] = b21 ^ (~b23 & b25);
s[30] = b30 ^ (~b32 & b34);
s[31] = b31 ^ (~b33 & b35);
s[40] = b40 ^ (~b42 & b44);
s[41] = b41 ^ (~b43 & b45);
s[2] = b2 ^ (~b4 & b6);
s[3] = b3 ^ (~b5 & b7);
s[12] = b12 ^ (~b14 & b16);
s[13] = b13 ^ (~b15 & b17);
s[22] = b22 ^ (~b24 & b26);
s[23] = b23 ^ (~b25 & b27);
s[32] = b32 ^ (~b34 & b36);
s[33] = b33 ^ (~b35 & b37);
s[42] = b42 ^ (~b44 & b46);
s[43] = b43 ^ (~b45 & b47);
s[4] = b4 ^ (~b6 & b8);
s[5] = b5 ^ (~b7 & b9);
s[14] = b14 ^ (~b16 & b18);
s[15] = b15 ^ (~b17 & b19);
s[24] = b24 ^ (~b26 & b28);
s[25] = b25 ^ (~b27 & b29);
s[34] = b34 ^ (~b36 & b38);
s[35] = b35 ^ (~b37 & b39);
s[44] = b44 ^ (~b46 & b48);
s[45] = b45 ^ (~b47 & b49);
s[6] = b6 ^ (~b8 & b0);
s[7] = b7 ^ (~b9 & b1);
s[16] = b16 ^ (~b18 & b10);
s[17] = b17 ^ (~b19 & b11);
s[26] = b26 ^ (~b28 & b20);
s[27] = b27 ^ (~b29 & b21);
s[36] = b36 ^ (~b38 & b30);
s[37] = b37 ^ (~b39 & b31);
s[46] = b46 ^ (~b48 & b40);
s[47] = b47 ^ (~b49 & b41);
s[8] = b8 ^ (~b0 & b2);
s[9] = b9 ^ (~b1 & b3);
s[18] = b18 ^ (~b10 & b12);
s[19] = b19 ^ (~b11 & b13);
s[28] = b28 ^ (~b20 & b22);
s[29] = b29 ^ (~b21 & b23);
s[38] = b38 ^ (~b30 & b32);
s[39] = b39 ^ (~b31 & b33);
s[48] = b48 ^ (~b40 & b42);
s[49] = b49 ^ (~b41 & b43);
s[0] ^= RC[n];
s[1] ^= RC[n + 1];
}
}
const keccak = bits => str => {
var msg;
if (str.slice(0,2) === "0x") {
msg = [];
for (var i = 2, l = str.length; i < l; i += 2)
msg.push(parseInt(str.slice(i,i+2), 16));
} else {
msg = str;
}
return update(Keccak(bits, bits), msg);
}
module.exports = {
keccak256: keccak(256),
keccak512: keccak(512),
keccak256s: keccak(256),
keccak512s: keccak(512),
}
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
Account: require("./account.js"),
Nat: require("./nat.js"),
Bytes: require("./bytes.js"),
Hash: require("./hash.js"),
RLP: require("./rlp.js"),
Api: require("./api.js"),
Provider: require("./provider.js"),
Desubits: require("./desubits.js"),
Passphrase: require("./passphrase.js")
}
+21
View File
@@ -0,0 +1,21 @@
const merge = a => b => {
let c = {};
for (let key in a)
c[key] = a[key];
for (let key in b)
c[key] = b[key];
return c;
}
const remove = removeKey => a => {
let b = {};
for (let key in a)
if (key !== removeKey)
b[key] = a[key];
return b;
}
module.exports = {
merge,
remove
}
+57
View File
@@ -0,0 +1,57 @@
const BN = require("bn.js");
const Bytes = require("./bytes");
const fromBN = bn =>
"0x" + bn.toString("hex");
const toBN = str =>
new BN(str.slice(2), 16);
const fromString = str => {
const bn = "0x" + (str.slice(0,2) === "0x"
? new BN(str.slice(2), 16)
: new BN(str, 10)).toString("hex");
return bn === "0x0" ? "0x" : bn;
}
const toEther = wei =>
toNumber(div(wei, fromString("10000000000"))) / 100000000;
const fromEther = eth =>
mul(fromNumber(Math.floor(eth * 100000000)), fromString("10000000000"));
const toString = a =>
toBN(a).toString(10);
const fromNumber = a =>
typeof a === "string"
? (/^0x/.test(a) ? a : "0x" + a)
: "0x" + new BN(a).toString("hex");
const toNumber = a =>
toBN(a).toNumber();
const toUint256 = a =>
Bytes.pad(32, a);
const bin = method => (a, b) =>
fromBN(toBN(a)[method](toBN(b)));
const add = bin("add");
const mul = bin("mul");
const div = bin("div");
const sub = bin("sub");
module.exports = {
toString,
fromString,
toNumber,
fromNumber,
toEther,
fromEther,
toUint256,
add,
mul,
div,
sub
}
+51
View File
@@ -0,0 +1,51 @@
const Hash = require("./hash");
const Bytes = require("./bytes");
const Desubits = require("./desubits");
// Bytes -> Bytes
const bytesAddChecksum = (bytes) => {
const hash = Hash.keccak256(bytes);
const sum = Bytes.slice(0, 1, hash);
return Bytes.concat(bytes, sum);
};
// Bytes -> Bool
const bytesChecksum = (bytes) => {
const length = Bytes.length(bytes);
const prefix = Bytes.slice(0, length - 1, bytes);
return bytesAddChecksum(prefix) === bytes;
};
// () ~> Passphrase
const create = () => {
const bytes = bytesAddChecksum(Bytes.random(11));
const seed = Desubits.fromBytes(bytes);
const passphrase = seed.replace(/([a-z]{8})/g, "$1 ");
return passphrase;
};
// Passphrase -> Bytes
const toBytes = (passphrase) => {
const seed = passphrase.replace(/ /g, "");
const bytes = Desubits.toBytes(passphrase);
return bytes;
};
// Passphrase -> Bool
const checksum = (passphrase) =>
bytesChecksum(toBytes(passphrase));
// Passphrase -> Bytes
const toMasterKey = (passphrase) => {
let hash = Hash.keccak256;
let bytes = toBytes(passphrase);
for (let i = 0, l = Math.pow(2,12); i < l; ++i)
bytes = hash(bytes);
return bytes;
}
module.exports = {
create,
checksum,
toMasterKey
}
+87
View File
@@ -0,0 +1,87 @@
const njsp = require("nano-json-stream-parser");
const request = require("xhr-request-promise");
const EthereumProvider = (url, intercept) => {
intercept = intercept || (() => {});
let api = {};
let onResponse = {};
let callbacks = {};
let nextId = 0;
let send;
const makeSender = send => {
const P = fn => (...args) => new Promise((resolve, reject) =>
fn(...args.concat((err,res) => err ? reject(err) : resolve(res))));
const sender = intercept => (method, params, callback) => {
const intercepted = intercept(method, params, P(sender(() => {})));
if (intercepted) {
intercepted.then(response => callback(null, response));
} else {
send(method, params, callback);
}
}
return sender(intercept);
};
const parseResponse = njsp(json => {
onResponse[json.id] && onResponse[json.id](null, json.result);
});
const genPayload = (method, params) => ({
jsonrpc: "2.0",
id: ++nextId,
method: method,
params: params
});
api.on = (name, callback) => {
callbacks[name] = callback;
}
if (/^ws/.test(url)) {
const WebSocket = require("w"+"s");
const ws = new WebSocket(url);
api.send = makeSender((method, params, callback) => {
const intercepted = intercept(method, params, P(send(() => {})));
if (intercepted) {
intercepted.then(response => callback(null, response));
} else {
const payload = genPayload(method, params);
onResponse[payload.id] = callback;
ws.send(JSON.stringify(payload));
}
});
ws.on("message", parseResponse);
ws.on("open", () => callbacks.connect && callbacks.connect(eth));
ws.on("close", () => callbacks.disconnect && callbacks.disconnect());
} else if (/^http/.test(url)) {
api.send = makeSender((method, params, callback) => {
request(url, {
method: "POST",
contentType: "application/json-rpc",
body: JSON.stringify(genPayload(method,params))})
.then(answer => {
var resp = JSON.parse(answer);
if (resp.error) {
callback(resp.error.message);
} else {
callback(null, resp.result)
}
})
.catch(err => callback("Couldn't connect to Ethereum node."));
});
setTimeout(() => {
callbacks.connect && callbacks.connect();
}, 1);
} else {
throw "IPC not supported yet.";
}
return api;
};
module.exports = EthereumProvider;
+72
View File
@@ -0,0 +1,72 @@
// The RLP format
// Serialization and deserialization for the BytesTree type, under the following grammar:
// | First byte | Meaning |
// | ---------- | -------------------------------------------------------------------------- |
// | 0 to 127 | HEX(leaf) |
// | 128 to 183 | HEX(length_of_leaf + 128) + HEX(leaf) |
// | 184 to 191 | HEX(length_of_length_of_leaf + 128 + 55) + HEX(length_of_leaf) + HEX(leaf) |
// | 192 to 247 | HEX(length_of_node + 192) + HEX(node) |
// | 248 to 255 | HEX(length_of_length_of_node + 128 + 55) + HEX(length_of_node) + HEX(node) |
const encode = tree => {
const padEven = str =>
str.length % 2 === 0 ? str : "0" + str;
const uint = num =>
padEven(num.toString(16))
const length = (len, add) =>
len < 56 ? uint(add + len) : uint(add + uint(len).length / 2 + 55) + uint(len);
const dataTree = tree => {
if (typeof tree === "string") {
const hex = tree.slice(2);
const pre = hex.length != 2 || hex >= "80" ? length(hex.length / 2, 128) : "";
return pre + hex;
} else {
const hex = tree.map(dataTree).join("");
const pre = length(hex.length / 2, 192)
return pre + hex;
}
}
return "0x" + dataTree(tree);
};
const decode = hex => {
let i = 2;
const parseTree = () => {
if (i >= hex.length) throw "";
const head = hex.slice(i,i+2);
return head < "80" ? (i+=2, "0x" + head)
: head < "c0" ? parseHex()
: parseList();
}
const parseLength = () => {
const len = parseInt(hex.slice(i,i+=2), 16) % 64;
return len < 56 ? len : parseInt(hex.slice(i, i += (len - 55) * 2), 16);
}
const parseHex = () => {
const len = parseLength();
return "0x" + hex.slice(i, i += len * 2);
}
const parseList = () => {
const lim = parseLength() * 2 + i;
let list = [];
while (i < lim)
list.push(parseTree());
return list;
}
try {
return parseTree();
} catch (e) {
return [];
}
};
module.exports = {encode, decode};
+83
View File
@@ -0,0 +1,83 @@
const F = require("forall");
const A = require("./array");
const B = require("./bytes");
const Account = require("./account");
F.Bytes = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by an even number of low-case hex chars (i.e., `0123456789abcdef`)",
rand: () => "0x" + A.generate((Math.random() * 16 | 0) * 2, () => (Math.random() * 16 | 0).toString(16)).join(""),
test: value => typeof value === "string" && /^0x([0-9a-f][0-9a-f])*$/.test(value),
}).__name("Bytes").__desc("any arbitrary data");
F.NBytes = bytes => F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by " + (bytes * 2) + " low-case hex chars (i.e., `0123456789abcdef`)",
test: value => F.Bytes.test(value) && value.length === (bytes * 2 + 2),
rand: () => "0x" + A.generate(bytes * 2, () => (Math.random() * 16 | 0).toString(16)).join("")
})
.__name("NBytes(" + bytes + ")")
.__desc("any arbitrary data of exactly " + bytes + "-byte" + (bytes > 1 ? "s" : ""));
F.Nat = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by at least one low-case hex char different from 0, followed by any number of low-case hex chars (i.e., `0123456789abcdef`)",
test: value => typeof value === "string" && /^0x[1-9a-f]([0-9a-f])*$/.test(value),
rand: () => "0x" + (Math.random() * Math.pow(2,50) | 0).toString(16)
})
.__name("Nat")
.__desc("an arbitrarily long non-negative integer number");
F.Address = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by 40 hex chars (i.e., `0123456789abcdefABCDEF`), with the nth hex being uppercase iff the nth hex of the keccak256 of the lowercase address in ASCII is > 7",
test: address => /^(0x)?[0-9a-f]{40}$/i.test(address) && Account.toChecksum(address.toLowerCase()) === address,
rand: () => F.Account.rand().address
})
.__name("Address")
.__desc("an Ethereum public address");
F.Hash = F(F.Type, {
form: F.NBytes(32).form,
test: F.NBytes(32).test,
rand: F.NBytes(32).rand
})
.__name("Hash")
.__desc("a Keccak-256 hash");
F.PrivateKey = F(F.Type, {
form: F.NBytes(32).form,
test: F.NBytes(32).test,
rand: F.NBytes(32).rand
})
.__name("PrivateKey")
.__desc("an Ethereum private key");
F.Account = (() => {
const base = F.Struct({
address: F.Address,
privateKey: F.PrivateKey,
});
return F(F.Type, {
form: base.form,
test: base.test,
rand: () => Account.create("")
});
})()
.__name("Account")
.__desc("an Ethereum account");
F.BytesTree = F(F.Type, {
form: "either " + F.Bytes.form + ", or a tree of nested JavaScript Arrays of BytesTrees",
test: value => F.Bytes.test(value) || value instanceof Array && value.reduce((r,v) => F.BytesTree.test(v) && r, true),
rand: () => {
let list = [];
while (Math.random() < 0.8) {
if (Math.random() < 0.8)
list.push(F.Bytes.rand());
else
list.push(F.BytesTree.rand());
}
return list;
}
})
.__name("BytesTree")
.__desc("a tree of arbitrary binary data");
module.exports = F;