rchain / rchain-api

Obsoleted in favor of https://github.com/rchain-community/rchain-api. Use at your own risk.
Other
8 stars 4 forks source link

Repackage for NPM #5

Closed JoshOrndorff closed 6 years ago

JoshOrndorff commented 6 years ago

From @crypto-coder on October 3, 2018 6:49

Need to reorganize RChain-API for NPM. Package name would be "rchain.js".

Installation using NPM: npm install --save rchain Inclusion in Node.js: const { RNode, RCore } = require('rchain'); Inclusion in Typescript: import { RNode, RCore } from 'rchain';

Initial function set would include:

Copied from original issue: JoshOrndorff/RChain-API#19

JoshOrndorff commented 6 years ago

It looks like your list above can be broken down into a few classes.

  1. Rename to rchain.js I like that Idea

  2. Wrap the rest of the gRPC API such as show blocks, etc I like that Idea too, but don't think we need to block the npm module for that.

  3. Rename and restructure the existing functions. I don't think I like that idea. It helps js programmers communicate with the rest of the RChain devs if we stick to common names like listenForDataAtName instead of readDataByName. How to the commit_block and send_block_to_peers parameters work?

  4. Add crypto (hashing, signing, key management) I don't like that idea. There are already good modules like tweetNaCl, or if you want a nice UX, RSign. I wouldn't say nay if you go ahead and wrap those here anyway, but I'd say it's lower priority than wrapping the rest of the gRPC interface and shouldn't block an npm module.

Just my thoughts.

JoshOrndorff commented 6 years ago

From @crypto-coder on October 3, 2018 15:58

  1. Rename and restructure the existing functions. existing (listenForDataAtName) vs rename (readDataByName). I agree that keeping the same names will allow us to communicate the same way across different RChain client libs. My idea was to provide some very simple functions for specific uses, rather than overload (and parse) all variations through one function. We have 3 variations for ways to read data from a channel: friendly_name, unforgeable_name, and Par def. These 3 variations imply distinctly different tactics for interacting with channels from the dApp developer's perspective (how they will record the channel name in some DB for use later). Can we use the following 3 variations: listenForData(Par def), listenForDataAtName(friendly_name), listenForDataAtUName(unforgeable_name) ?

The gRPC interface provides the functions 'createBlock' and 'addBlock', which are almost always going to be called by the dApp developer after they execute a smart contract, so I provide a way to have them called internally to the function call, as parameters in the function calls: 'commit_block' and 'send_block_to_peers' respectively.

  1. Add crypto (hashing, signing, key management) Hashing: Recently attempted to store a salted hash in Rholang, that would be later verified by passing in the salt. Rholang data hashing is for a byte array with several additional bytes prepended. Want to package up this special hashing process so they can be computed off-chain to match what gets calculated on-chain.
    Signing: rchain.js will be an NPM package for backend applications, so we will be doing transactions signing inside a webserver. These signatures will be verified on-chain, so wanted a single client library for Rholang that could perform this. Key Management: Very crude today, but this is also related to rchain.js being an NPM package for backend applications, so we will need a way to store, create, and retrieve keys that will be applied to transactions on-chain. Definitely leveraging tweetNaCl, but wanted to simplify the interface to just the things a dApp dev will need for interacting with Rholang.
JoshOrndorff commented 6 years ago
  1. Okay, I'm starting to get that and be on board. So how about this idea. We always wrap the direct gRPC service with a function of the exact same name (listenForDataAtName) and then other convenience helpers are also fine keeping name similarity to the extent practicable. So for example, maybe the convenience parameter is called create_block rather than commit_block. I like the idea of having those so the js dev doesn't have to call create block manually every time.

I don't like the name executeContract because not all deploys do that. You could be deploying a send or receive that just sits in the tuplespace waiting for its counterpart to come along and cause actual execution.

  1. @dckc Can you comment on this stuff about locallyFree etc? I don't have a good handle on it.
JoshOrndorff commented 6 years ago

From @dckc on October 3, 2018 21:51

@JoshOrndorff writes:

It looks like your list above can be broken down into a few classes.

I think the package name deserves its own issue: #22

Hashing out the rest of the API design here is fine with me, for the most part...

Let's stick to ocap discipline, please; that is: this call...

var myNode = RNode(hostname, port);

needs another argument for access to the network:

var myNode = RNode(grpc, hostname, port);

IOU a section for CONTRIBUTING.md.

Also, combining { hostname, port } seems to be the node.js norm:

http.get({
  hostname: 'localhost',
  port: 80,
...

-- https://nodejs.org/api/http.html

We could put grpc in the same object like they do with http agent. (But having a default would cross the ocap discipline boundary, and I really want to hold fast to that.)

JoshOrndorff commented 6 years ago

From @dckc on October 3, 2018 22:0

Why involve myNode in hashing?

Hash Data (SHA256): myNode.hashData( data_list, 'sha256');

It's computable locally, without communicating with the node.

Also, why pass the hash algorithm in as a name? Why not just separate functions? Are there any uses for hashing without caring what the algorithm is?

So how about just: sha256( data_list). At which point: is there any particular value in re-exporting what's already provided from tweetnacl? I guess nacl.hash only does SHA-512. Why do we want sha256? why keccak256? why blake2b256?

JoshOrndorff commented 6 years ago

From @dckc on October 3, 2018 22:2

What is myNode.loadKeyStore( keystore_path ) supposed to do? Does rnode have a gRPC API for getting at its keystore that I missed?

JoshOrndorff commented 6 years ago

From @dckc on October 3, 2018 22:8

I'm starting to lean toward separate issues for hashing and key storage.

Create new KeyPair: myNode.createKeyPair( alias, add_to_keystore=true ); Get Public Key by Alias: myNode.getPublicKey( alias ); Sign a transaction: myNode.signTransaction( alias, DeployData );

Is there some reason to change from the current API?

  const seed = randomBytes(32);
  const pair1 = keyPair(seed);

  console.log('public key:', pair1.publicKey());
  console.log('signature:', pair1.signTextHex('hello world'));
JoshOrndorff commented 6 years ago

IOU a section for CONTRIBUTING.md.

23 . We do currently take grpc and endpoint as args just as you suggest. Let's just leave it as is.

I'm starting to lean toward separate issues for hashing and key storage.

@crypto-coder If you wan to discuss this stuff more, please do open an issue. You're working on the most advanced use case so far, so let us know your thoughts. OTOH, if you're convinced, we can just let that convo die.

JoshOrndorff commented 6 years ago

From @crypto-coder on October 4, 2018 5:11

Why involve myNode in hashing?

Rholang can perform hashing, but it only hashes byte arrays. In my coin-faucet project, I was creating a 'race' where I would store a coin in an account identified by a salted hash (keccak256). The first user to send a transaction with the correct salt would receive the coin. The data being hashed is the name of the coin concatenated with a salt. Everytime I ran the "keccak256Hash" function inside Rholang, I would get something different than what I computed in node.js, off-chain. The keccak256Hash function in Rholang wants a byte array, so I wrote the byte array to stdout, and found that it is prepending 4 bytes in front of my concatenated text. I still don't know how to correctly compute those initial 4 bytes, but when I figure it out, I feel that we should have a function that other dApp devs can use to compute sha256, keccak256, and blake2b256 off-chain, the way that Rholang internally will compute them. The alternative (addition?) would be to amend the Rholang tutorial to describe how to compute matching hashes off-chain that include the prepended 4 bytes.

Also, why pass the hash algorithm in as a name? Why not just separate functions? So how about just: sha256( data_list)

We could do a single overloaded function, or different named functions. Ultimately, I think the steps involved in generating a hash will largely be the same for all algorithms. No preference either way.

Why do we want sha256? why keccak256? why blake2b256?

These are the 3 hashing algorithms provided natively inside Rholang.

JoshOrndorff commented 6 years ago

From @crypto-coder on October 4, 2018 6:0

What is myNode.loadKeyStore( keystore_path ) supposed to do? Does rnode have a gRPC API for getting at its keystore that I missed?

Not sure how to get access to RNode's keystore, but if that is a thing (by any means necessary), then that is definitely what I would want to do with these functions.

Otherwise, this is a convenience feature that will do many of the same things that RSign provides, but for backend applications. This keystore is a totally contrived file of aliased pub-priv key pairs. Password-protected even. These keys are used for signing transactions. Specifically, a backend system that is acting as an automated oracle would need a way to load keys on demand. Eventually, this keystore idea will need to be a proper RChain wallet / HSM, so these functions will definitely change. For today, this is just a simple, dirty solution.

JoshOrndorff commented 6 years ago

From @crypto-coder on October 4, 2018 6:23

I'm starting to lean toward separate issues for hashing and key storage.

Created a new issue for key management / signing #26

JoshOrndorff commented 6 years ago

From @dckc on October 4, 2018 16:9

... it is prepending 4 bytes in front of my concatenated text. ...

I bet that's protobuf encoding. the rholang .toByteArray() turns any process into its protobuf serialization. We have tests for this; for example, notice how "Bob" turns into 7 bytes worth of hex:

    string: rtest({
      data: 'Bob',
      rholang: '"Bob"',
      hex: '2a051a03426f62',
      rho: { exprs: [{ g_string: 'Bob', expr_instance: 'g_string' }] },
    }),

-- https://github.com/JoshOrndorff/RChain-API/blob/master/test/testRHOCore.js#L14-L19

The inital 4 bytes tell us that this par consists of one exprs which is a string (and probably its length).

So it's not the hashing function that prepends bytes. It's the business of serializing a process.

So if you want to hash "Bob", i.e. hex 426f62, then write "426f62".hexToBytes() and pass that to the hashing contract.

JoshOrndorff commented 6 years ago

@crypto-coder says:

I feel that we should have a function that other dApp devs can use to compute sha256, keccak256, and blake2b256 off-chain, the way that Rholang internally will compute them.

I've seen a lot of people around the community including myself trip on this encoding thing. I agree the protobuf encoding makes good sense in hindsight, but making it visible is like throwing obstacles in the path to success. Consider this developer story.

I'm a dApp developer who has written a rholang smart contract. I'm now writing the frontend in javacscript. I want to use RChain-API to connect to the blockchain, but I don't want to dig into it's guts or fiddle with bit tweezers. What's my best way to hash the string "Hello World" in such a way that it will verify correctly on chain, and preferably on ethereum or even bitcoin as well?

Ideally the answer is something like call serializeThenKeccak256("Hello World").

I agree that it shouldn't be a method on the node instance.

JoshOrndorff commented 6 years ago

From @dckc on October 5, 2018 2:24

Do ethereum and bitcoins have norms for hashing things beyond byte sequences?

Text encoding can be really tricky: http://intertwingly.net/blog/2008/12/09/Just-use-Unicode

JoshOrndorff commented 6 years ago

Yeah, I guess I don't know about that. Maybe compatibility with ethereum is a different conversation. But do you agree, @dckc, that the developer I described above shouldn't have to see protobuf to make a hash that will verify on-chain?

A helper function that is not a method on the node seems like the right solution to me. @cryptocoder?

dckc commented 6 years ago

We currently have:

const b2h = bytes => Buffer.from(bytes).toString('hex');
const h2b = hex => Buffer.from(hex, 'hex');
const t2b = text => Buffer.from(text);

though we don't export t2b.

I guess convenience functions that compose those with function that hash bytes are OK. We already have precedent:

  return def({
    signBytes,
    signBytesHex: bytes => b2h(signBytes(bytes)),
    signText: text => signBytes(t2b(text)),
    signTextHex: text => b2h(signBytes(t2b(text))),
...
dckc commented 6 years ago

@crypto-coder writes

What do we still need for an npm package?

Nothing, as far as I'm concerned. That is: publishing via npm is orthogonal to adding / changing the feature set.

On the other hand, I'm content with publishing via github for now, so maybe somebody else who is more inclined to do the publishing should weigh in.

dckc commented 6 years ago

@crypto-coder writes:

What do we have that we don't need for an npm package?

Nothing.

All of the stuff we have added has been well motivated, I think.

crypto-coder commented 6 years ago

Final list for first version of the NPM package -

Connect to RNode: var myNode = RNode(grpc {hostname, port}); Deploy SmartContract: myNode.doDeploy( DeployData, commit_block=false ); Commit trans to Block: myNode.createBlock( ); Get Block by hash: myNode.getBlock( hash ); Get all Blocks: myNode.getAllBlocks( block_depth ); Get Data by Par: myNode.listenForDataAtName( par ); Hash Data (SHA256): myNode.sha256Hash( js_data ); Hash Data (Keccak256): myNode.keccak256Hash( js_data ); Hash Data (Blake2b256): myNode.blake2b256Hash( js_data );

dckc commented 6 years ago

Since my time is scarce after today, I went ahead and reviewed https://github.com/crypto-coder/rchain-api/tree/npm in advance of the PR. I did find several critical issues.

dckc commented 6 years ago

We shipped on time! https://www.npmjs.com/package/rchain-api 0.7.1-beta.1 b9a4636

Oh. I was going to close this but I can't.

JoshOrndorff commented 6 years ago

Awesome work everybody, especially @dckc and @crypto-coder . Thanks to you we have our first published package! https://www.npmjs.com/package/rchain-api