mainnet-cash / mainnet-js

TypeScript/JavaScript interface for interacting with Bitcoin Cash network
MIT License
31 stars 26 forks source link

New wallet, named wallets (+basic REST API) #7

Closed readcash closed 4 years ago

readcash commented 4 years ago

Though, of course, anyone is free to take this task, but I'd prefer @2qx to take it, since he seems to have a lot of understanding how to do this well and this task is the basis of ALL of our future work. Don't worry, as soon as we're done with some basic stuff, there will be plenty of tasks.

This issue calls to implement the creation of a new wallet and its corresponding REST API:

const w = new RegTestWallet();
console.log(w.wif);
// cNfsP... (random every time)

and named wallets:

const w1 = new RegTestWallet('username1');
console.log(w1.wif);
// abcdef.. (random first time)

const w2 = new RegTestWallet('username1');
console.log(w2.wif);
// abcdef.. (same as above)

const w2 = new RegTestWallet('username2');
console.log(w2.wif);
// fghi.. (random)

It should store its private key (in WIF format I guess?) in IndexedDB in the browser by name. The usefulness of this comes when the same browser is used by different people (or one person) logging in to different accounts on the same site. i.e. on read.cash you can have multiple usernames, each one having its own wallet. Without this any person who tries to create a new account on any site will end up with the same wallet on every account (which is incorrect). read.cash doesn't actually store it by username, but rather by a hash of user.id + secret string, so that it's harder to guess. But it will be just a recommendation.

Also, we need to start doing the node.js interface, so similarly we need to implement

POST /api/v1/wallet
JSON request body: {"type":"wif", "network":"regtest"}

returning JSON:

{"wallet": "wif:regtest:abc12345", "wif": "abc12345", "cashaddr": "..."}

This call is stateless, so it doesn't save anything, just returns a random wallet. abc12345 is the actual random WIF, like cNfsPtqN2bMRS7vH5qd8tR8GMvgXyL5BjnGAKgZ8DYEiCrCCQcP6.

....and a named version of the call:

POST /api/v1/wallet
{"type":"wif", "network":"regtest", "name": "username1"}

returning

{"wallet": "wif:regtest:username1", "wif": "abc12345", "cashaddr": "..."}

But this call is stateful, meaning each time user calls for {"type":"wif", "network":"regtest", "name": "username1"} he gets the same wallet back.

wallet in return is the serialization of the wallet data that we would need in future calls - it's important for us to know that this is a WIF wallet (as opposed to HD wallets), that it's for regtest and what its WIF is... for named wallets we don't need to store WIF in the wallet, since we store WIF on server.

So, now a future call of

POST /api/v1/send
{"wallet": "wif:regtest:abcd123", "to" ...}

...we already know what to do.

Some considerations for storage: for browser it's surely IndexedDB (though https://dexie.org/ looks like a much nicer wrapper API and pretty small).

For server-side node.js, I have no idea what we should use. I would probably avoid using sqlite, because some servers could see heavy use, that means having to run multiple workers of node.js, and SQLite won't work well with that. So maybe it's PostgreSQL or mySQL.. This looks interesting though: https://github.com/indexeddbshim/IndexedDBShim

Yeah and I guess if we're talking Postgres/mySQL - then we'll need docker-compose anyway.

Tests are required.

-- $1,000 bounty --

The bounty is paid in BCH from the funds collected via https://github.com/mainnet-cash/mainnet-js . To claim the bounty, please do the following:

1) if this task requires coding in TypeScript, first you need to complete the test task by sending a private message to ReadCash via Slack link here;

2) after you have completed the test task once, tell us that you are working on this task and how long it is going to take, like this: "I'll take it. I expect it to be done in 2 days". If that time close to expire and you haven't finished it yet, you can write something like "It'll take 2 more days", otherwise anyone else is free to take the task);

3) send a Pull Request or publish the solution here as described in the issue. Good luck! If you think that bounty is way too small, please notify me about it and we can discuss. A bounty will be paid via the chaintip bot.

readcash commented 4 years ago

@2qx Maybe we can avoid having to install mySQL/PostgreSQL and use something like https://github.com/Venemo/node-lmdb ? I think that would be pretty neat, since then when BCHD implements SLP token database, we can be zero-dependencies for development again. And it seems that LMDB supports multi-process write, so we're clear in this regard too.

2qx commented 4 years ago

What about https://github.com/nimiq/jungle-db ?

Seems like it was designed exactly for this problem.

readcash commented 4 years ago

Seems like a good choice too.

2qx commented 4 years ago

Breaking this down into sub-tasks:

If the express server is stateless, does it need a database at all, or can we just use fakeindexeddb for testing?

Not in this ticket, for the use case of multiple users on the same computer, should the secret be encrypted client side? : https://github.com/mark43/dexie-encrypted

Looking at jungle-db closer it seems that it hasn't been actively maintained in about two years. I think dexie is the way to go for the browser at least.

readcash commented 4 years ago

Package a version of the full-client targeted at browsers

Just to be clear here. It's not a client for the REST API. Browser version must interact with GRPC (or whatever) directly, without ever touching the REST API. There should be some common TS/JS codebase that both browsers and the REST API use (to avoid code duplication). Maybe that's what you meant, but better we discuss it now, rather than later.

can we just use fakeindexeddb for testing?

IDK for testing. Use for testing whatever is suitable. But I don't think that express is stateless - it still has to store stuff like named wallets (and webhooks, maybe contracts, maybe some other stuff later)

Not in this ticket, for the use case of multiple users on the same computer, should the secret be encrypted client side?

Not sure. That way a user would have to enter his/her password everytime they use the wallet (remember that users go from page to page and we're certainly not asking everyone to implement SPA, read.cash, for one, is not an SPA), so the password will be gone with each link click. People will lose this password too. Maybe it could be an option for later, but I don't think it's wise for a default.

Yep, I like dexie. Seems like a very sane layer on top of indexeddb. I think it could be used for REST API too (by utilizing some indexeddb implementation underneath) to simplify the common code.

Thanks 👍

readcash commented 4 years ago

@2qx @chaintip

chaintip commented 4 years ago

@2qx, you've been sent 3.65070093 BCH| ~ 996.45 USD by @readcash via chaintip.