Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethereum-compatible wallet (WIP) #56

Closed
wants to merge 63 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
109c005
protos: previewPrivateNames added in rchain/rchain 001589c
dckc Nov 16, 2018
cdd7d21
previewPrivateNames: rnodeAPI, integration test
dckc Nov 16, 2018
7d9e32f
RHOCore: convert `g_int` from string to number
dckc Nov 18, 2018
f2a0ca5
RHOCore.toJSData: accept map as well as sends
dckc Nov 18, 2018
f21424f
proxy javascript method calls
dckc Nov 18, 2018
93378fb
testRNode: unstructured phloLimit, phloPrice
dckc Nov 18, 2018
8536cbd
data from node: Par -> JS data
dckc Nov 18, 2018
6d7f98d
type annotation, lint
dckc Nov 18, 2018
86a2c8d
previewPrivateNames: document; align with doDeploy
dckc Nov 18, 2018
528ace5
Merge branch 'unforgeable' into proxy
dckc Nov 19, 2018
ac79d2c
proxy: lint
dckc Nov 19, 2018
ad8b901
proxy: update preview API
dckc Nov 19, 2018
065d841
previewPrivateNames -> previewPrivateIds, previewPrivateChannels
dckc Nov 19, 2018
562e2d9
liveRNode test: exit(1) on error
dckc Nov 19, 2018
a35b0b7
proxy: never mind `returnChannel`
dckc Nov 19, 2018
b90c581
lint
dckc Nov 19, 2018
a19aeac
testProxy: deploy target1.rho and get URI
dckc Nov 19, 2018
45a9352
testProxy: previewPrivateNames -> previewPrivateChannels
dckc Nov 19, 2018
2b17413
proxy: never mind makePeer()
dckc Nov 19, 2018
3b8e2a0
testPay: deployWalletContracts
dckc Nov 19, 2018
e91a5d7
proxy: handle 0 args, omitted method name
dckc Nov 19, 2018
3ebcd11
testPay: MakeMint
dckc Nov 19, 2018
c328af6
proxy: call clock() on each method call
dckc Nov 22, 2018
9cddb93
testPay: get ballance of alicePurse
dckc Nov 22, 2018
2d38eec
testPay: contract to make genesis wallets
dckc Nov 22, 2018
24326b4
extend RHOCore conversion to support URIs
dckc Nov 23, 2018
e529a66
RHOCore: add bytes, private names
dckc Nov 24, 2018
e89b82f
signing: add signData() and signDataHex()
dckc Nov 24, 2018
87e6094
RHOCore: test signing unforgeable names
dckc Nov 24, 2018
1b179ca
testPay: use faucet from system
dckc Nov 24, 2018
a9d5caf
testPay: BasicWallet.transfer accepts signature
dckc Nov 24, 2018
8758357
testPay: finish the paymen to bob by making a new wallet
dckc Nov 24, 2018
d1c578a
testPay: lint
dckc Nov 24, 2018
9e715d0
testPay: log Alice's balance after
dckc Nov 24, 2018
4a99fda
proxy sendCall: support extra pre-declared names (IOU tests)
dckc Nov 26, 2018
cdf9c02
testPay: factor out sending.rho
dckc Nov 26, 2018
20d67ec
alicePaysBob: caller loads sending.rho
dckc Nov 26, 2018
a10d4cd
proxy: static typing => API tweaks
dckc Nov 26, 2018
e8446e4
test/loading: lint
dckc Nov 26, 2018
e238f9a
Toward decoding ethereum signed transactions: RLP
dckc Dec 7, 2018
1f9fe3f
rlp.rho: all examples from wiki/RLP pass
dckc Dec 8, 2018
8d12a56
rlp.rho: fix long list decoding; add EIP155 test
dckc Dec 8, 2018
9f22674
ethSig WIP
dckc Dec 8, 2018
2e26c6a
DER encoding for signature works in 1 case
dckc Dec 8, 2018
d09b079
ethSig: note on DER Integer 0x0 prefix
dckc Dec 8, 2018
bb08e15
ethSig using interpolation
dckc Dec 10, 2018
377c6fe
ethSig: factor out validateTransaction
dckc Dec 11, 2018
31e7285
ethSig: add another test case; fix sigDER
dckc Dec 11, 2018
9851000
ethSig: sigDER using contract hexBytes + hexDigit.get()
dckc Dec 12, 2018
e7bddf2
rlp: encode works in 1 case
dckc Dec 12, 2018
1e03d46
rlp: refine logging, testing
dckc Dec 12, 2018
f43eb32
rlp: fix concat; test encode + decode
dckc Dec 12, 2018
10bae4e
rlp: handle long ByteArrays
dckc Dec 14, 2018
2e5384b
rlp: encode passes all tests (as well as decode)
dckc Dec 14, 2018
222e10c
rlp: encapsulate encode, decode in bundles
dckc Dec 14, 2018
7c49d0b
ethSig: verifySignature works in 2 cases
dckc Dec 14, 2018
9631495
ethSig: sendTransaction testing (WIP)
dckc Dec 14, 2018
e571eae
rlp: export int encode / decode, oneByte
dckc Dec 14, 2018
c0e8e6e
rlp: rever to real ListOps registry URI
dckc Dec 14, 2018
fb8397d
ethSig: integrate rev wallet (untested)
dckc Dec 14, 2018
db7d52c
rlp: bug fixes (how did the tests pass???)
dckc Dec 14, 2018
950fe26
ethSig: fix syntax errors
dckc Dec 14, 2018
5c172fa
rlp: add docs; refactor for readability
dckc Dec 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const RNode = require('./src/rnodeAPI.js');

module.exports.RNode = RNode.RNode;

const { sendCall, makeProxy, callSource } = require('./src/proxy');

module.exports.sendCall = sendCall;
module.exports.makeProxy = makeProxy;
module.exports.callSource = callSource;

const signing = require('./src/signing');

Expand Down
18 changes: 0 additions & 18 deletions interfaces/JSON.js

This file was deleted.

32 changes: 22 additions & 10 deletions protobuf/CasperMessage.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,8 @@ service DeployService {
rpc showBlocks(BlocksQuery) returns (stream BlockInfoWithoutTuplespace) {}
rpc listenForDataAtName(DataAtNameQuery) returns (ListeningNameDataResponse) {}
rpc listenForContinuationAtName(ContinuationAtNameQuery) returns (ListeningNameContinuationResponse) {}
}

message PhloLimit {
int64 value = 1;
}

message PhloPrice {
int64 value = 1;
rpc findBlockWithDeploy(FindDeployInBlockQuery) returns (BlockQueryResponse) {}
rpc previewPrivateNames(PrivateNamePreviewQuery) returns (PrivateNamePreviewResponse) {}
}

message DeployData {
Expand All @@ -43,8 +37,8 @@ message DeployData {
bytes sig = 4; //signature of (hash(term) + timestamp) using private key
string sigAlgorithm = 5; // name of the algorithm used to sign
string from = 6; //wallet address which will be used to pay for the deployment
PhloPrice phloPrice = 7; //phlo price
PhloLimit phloLimit = 8; //phlo limit for the deployment
int64 phloPrice = 7; //phlo price
int64 phloLimit = 8; //phlo limit for the deployment
int32 nonce = 9; //nonce for transaction made against `from` wallet
}

Expand All @@ -56,6 +50,11 @@ message BlockRequest {
bytes hash = 2;
}

message FindDeployInBlockQuery {
bytes user = 1;
int64 timestamp = 2;
}

message BlockQuery {
string hash = 1;
}
Expand Down Expand Up @@ -146,6 +145,17 @@ message BlockInfo {
string shardId = 13;
}

message PrivateNamePreviewQuery {
bytes user = 1; // public key a la DeployData
int64 timestamp = 2; // millisecond timestamp
int32 nameQty = 3; // how many names to preview? (max: 1024)
}

message PrivateNamePreviewResponse {
repeated bytes ids = 1; // a la GPrivate
}


// --------- End DeployService --------

// ---------- Signing Protocol ---------
Expand Down Expand Up @@ -254,11 +264,13 @@ message Event {
message ProduceEvent {
bytes channelsHash = 1;
bytes hash = 2;
int32 sequenceNumber = 3;
}

message ConsumeEvent {
bytes channelsHash = 1;
bytes hash = 2;
int32 sequenceNumber = 3;
}

message CommEvent {
Expand Down
141 changes: 113 additions & 28 deletions src/RHOCore.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
/* global require, exports, Buffer */
// @flow strict

const { URL } = require('url');

// ISSUE: generated code isn't annotated. $FlowFixMe
const { Par } = require('../protobuf/RhoTypes.js');
const { Par, GPrivate } = require('../protobuf/RhoTypes.js');

// copied from signing.js to avoid circular imports
const b2h = bytes => Buffer.from(bytes).toString('hex');

/**
* "we can detail a direct representation of JSON into a
* fragment of the rholang syntax referred to in the diagram
* below as RHOCore." -- [Mobile process calculi for programming the blockchain[1]
* Build Rholang expression from Javascript data.
*
* [1]: https://github.com/rchain/mobile-process-calculi-for-blockchain/blob/master/enter-the-blockchain.rst
* @param x Any javascript object to be serialized to RHOCore
* @return A rholang term representing the object in RHOCore form.
* This is the inverse of `toJSData`.
*
* @param data: number, string, array, etc.; see toJSData for details.
* @return: A rholang term in Protobuf's JSON representation,
* i.e. `IPar` derived from RhoTypes.proto.
*/
exports.fromJSData = fromJSData;
function fromJSData(data /*: mixed */) /* : IPar */ {
function expr1(kv /*: IPar*/) { return { exprs: [kv] }; }

function recur(x) /*: IPar */{
if (x instanceof URL) {
return expr1({ g_uri: x.href });
}
if (x instanceof GPrivate) {
return { ids: [x] };
}
if (x instanceof Uint8Array) {
return expr1({ g_byte_array: Buffer.from(x) });
}
if (x instanceof Buffer) {
return expr1({ g_byte_array: x });
}
switch (typeof x) {
case 'boolean':
return expr1({ g_bool: x });
Expand All @@ -38,7 +56,7 @@ function fromJSData(data /*: mixed */) /* : IPar */ {
}
}

function toArry(items /*: JsonArray */) {
function toArry(items /*: mixed[] */) {
// [1, 2, 2] is a process with one exprs, which is a list
// The list has one 3 items, each of which is a process
// with one exprs, which is an int.
Expand All @@ -58,7 +76,9 @@ function fromJSData(data /*: mixed */) /* : IPar */ {


/**
* Turns a rholang term into a byte-array compatible with Rholang
* Serialize a rholang term from Protobuf JSON to bytes.
*
* This is compatible with `x.toByteArray()` in Rholang.
*/
exports.toByteArray = toByteArray;
function toByteArray(termObj /*: IPar */) /*: Uint8Array */ {
Expand All @@ -67,15 +87,45 @@ function toByteArray(termObj /*: IPar */) /*: Uint8Array */ {
}


/**
* Converts an RHOCore object back to JavaScript data
*
* @param par A RHOCore representation of a Rholang term
* @return JSON-serializable data
/*:: // Get Javascript data represented by a a RHOCore object.

// "we can detail a direct representation of JSON into a
// fragment of the rholang syntax referred to in the diagram
// below as RHOCore." -- [Mobile process calculi for programming the blockchain[1]
//
// [1]: https://github.com/rchain/mobile-process-calculi-for-blockchain/blob/master/enter-the-blockchain.rst

// @param par: An extended RHOCore object in Protobuf JSON form.
// RHOCore is a subset of Rholang defined in [1].
// We extend it to include URIs.

// @return: Javascript data represented by `par`; that is:
// Any data you might get from JSON.parse()
// as well as URLs.
// The flow type is given below:
export type JsonExt<T> =
| JsonPrimitive<T>
| JsonExtArray<T>
| JsonExtObject<T>
;

export type JsonPrimitive<T> =
| null
| string
| number
| boolean
| T
;

export type JsonExtArray<T> = Array<JsonExt<T>>;

export type JsonExtObject<T> = { [string]: JsonExt<T> };

*/
// ack: https://github.com/facebook/flow/issues/4825#issuecomment-414605109
exports.toJSData = toJSData;
function toJSData(par /*: IPar */) /*: Json */{
function recur(p /*: IPar */) {
function toJSData(par /*: IPar */) /*: JsonExt<URL | GPrivate> */{
function recur(p /*: IPar */) /*: JsonExt<URL | GPrivate> */{
if (p.exprs && p.exprs.length > 0) {
if (p.exprs.length > 1) {
throw new Error(`${p.exprs.length} exprs not part of RHOCore`);
Expand All @@ -85,24 +135,47 @@ function toJSData(par /*: IPar */) /*: Json */{
return ex.g_bool;
}
if (typeof ex.g_int !== 'undefined') {
return ex.g_int;
return parseInt(ex.g_int, 10); // ISSUE: overflow
}
if (typeof ex.g_string !== 'undefined') {
if (typeof ex.g_string !== 'undefined' && ex.g_string !== null) {
return ex.g_string;
}
if (typeof ex.g_byte_array !== 'undefined' && ex.g_string !== null) {
return ex.g_byte_array;
}
if (typeof ex.g_uri !== 'undefined' && ex.g_uri !== null) {
return new URL(ex.g_uri);
}
if (typeof ex.e_list_body !== 'undefined' && ex.e_list_body !== null
&& Array.isArray(ex.e_list_body.ps)) {
return ex.e_list_body.ps.map(recur);
}
throw new Error(`not RHOCore? ${JSON.stringify(ex)}`);
} else if (p.sends) {
if (typeof ex.e_map_body !== 'undefined' && ex.e_map_body !== null
&& Array.isArray(ex.e_map_body.kvs)) {
const props = ex.e_map_body.kvs.map((kv) => {
const key = recur(kv.key || {});
if (typeof key !== 'string') {
throw new Error(`not RHOCore? map key not string: ${JSON.stringify(key)}`);
}
const val = recur(kv.value || {});
return { k: key, v: val };
});
return props.reduce((acc, { k, v }) => ({ [k]: v, ...acc }), {});
}
throw new Error(`not RHOCore? unknown expression type: ${JSON.stringify(ex)}`);
} else if (p.sends && p.sends.length) {
const props = p.sends.map((s) => {
const key = recur(s.chan || {});
if (typeof key !== 'string') { throw new Error(`not RHOCore? ${JSON.stringify(key)}`); }
if (typeof key !== 'string') { throw new Error(`not RHOCore? send key: ${JSON.stringify(key)}`); }
const val = recur((s.data || [{}])[0]);
return { k: key, v: val };
});
return props.reduce((acc, { k, v }) => ({ [k]: v, ...acc }), {});
} else if (p.ids && p.ids.length) {
if (p.ids.length !== 1) {
throw new Error(`not RHOCore? >1 ids ${JSON.stringify(p)}`);
}
return GPrivate.fromObject(p.ids[0]);
} else {
// TODO: check that everything else is empty
return null;
Expand All @@ -126,7 +199,9 @@ function toRholang(par /*: IPar */) /*: string */ {
const src = x => JSON.stringify(x);

function recur(p /*: IPar */) {
if (p.exprs && p.exprs.length > 0) {
if (p.ids && p.ids.length) {
throw new Error('Unforgeable names have no rholang syntax.');
} else if (p.exprs && p.exprs.length > 0) {
if (p.exprs.length > 1) {
throw new Error(`${p.exprs.length} exprs not part of RHOCore`);
}
Expand All @@ -137,19 +212,26 @@ function toRholang(par /*: IPar */) /*: string */ {
if (typeof ex.g_int !== 'undefined') {
return src(ex.g_int);
}
if (typeof ex.g_string !== 'undefined') {
if (typeof ex.g_string !== 'undefined' && ex.g_string !== null) {
return src(ex.g_string);
}
if (typeof ex.g_uri !== 'undefined') {
return src(ex.g_uri);
if (typeof ex.g_byte_array !== 'undefined' && ex.g_byte_array !== null) {
return `"${b2h(ex.g_byte_array)}".hexToBytes()`;
}
if (typeof ex.g_uri !== 'undefined' && ex.g_uri !== null) {
const uri = ex.g_uri;
if (uri.match(/`/g)) {
throw new Error(`not implemented: URIs containing back-tick: ${uri}`);
}
return `\`${uri}\``;
}
if (typeof ex.e_list_body !== 'undefined' && ex.e_list_body !== null
&& Array.isArray(ex.e_list_body.ps)) {
const items /*: string[] */= (ex.e_list_body.ps || []).map(recur);
return `[${items.join(', ')}]`;
}
throw new Error(`not RHOCore? ${JSON.stringify(ex)}`);
} else if (p.sends) {
throw new Error(`not RHOCore? unknown expr ${JSON.stringify(ex)}`);
} else if (p.sends && p.sends.length) {
const ea = s => `@${recur(s.chan || {})}!(${(s.data || []).map(recur).join(', ')})`;
return p.sends.map(ea).join(' | ');
} else {
Expand All @@ -162,8 +244,11 @@ function toRholang(par /*: IPar */) /*: string */ {
}


/**
* Rholang template tag.
*/
exports.rhol = rhol;
function rhol(template /*: string[] */, ...subs /*: Json[] */) {
function rhol(template /*: string[] */, ...subs /*: JsonExt<URL | GPrivate>[] */) {
const encoded = subs.map(it => toRholang(fromJSData(it)));

const out = [];
Expand Down
Loading