Skip to content

Commit

Permalink
Initial commit 2
Browse files Browse the repository at this point in the history
  • Loading branch information
svk31 committed Jan 19, 2017
1 parent 55407c6 commit fedd4e5
Show file tree
Hide file tree
Showing 58 changed files with 7,633 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"presets": [
"./tools/es2015Preset",
"stage-1"
],
"env": {
"cjs": {
"plugins": ["add-module-exports"]
}
}
}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.npmrc
npm-debug.log
node_modules
dist
es
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib
build
test
tools
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## v0.4.0
- Add AccountLogin class
- Clean up imports
- Use babel for tests
## v0.3.0
- Extract websocket library to separate bitsharesjs-ws lib
100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# BitsharesJS (bitsharesjs)

Pure JavaScript Bitshares library for node.js and browsers. Can be used to construct, sign and broadcast transactions in JavaScript, and to easily obtain data from the blockchain via public apis.

Most of this code was written by [jcalfeee](https://github.com/jcalfee), my work was mostly just repackaging to a discrete npm package.

[![npm version](https://img.shields.io/npm/v/bitsharesjs.svg?style=flat-square)](https://www.npmjs.com/package/bitsharesjs)
[![npm downloads](https://img.shields.io/npm/dm/bitsharesjs.svg?style=flat-square)](https://www.npmjs.com/package/bitsharesjs)


## Setup

This library can be obtained through npm:
```
npm install bitsharesjs
```

## Usage

Three sub-libraries are included: `ECC`, `Chain` and `Serializer`. Generally only the `ECC` and `Chain` libraries need to be used directly.

### Chain
This library provides utility functions to handle blockchain state as well as a login class that can be used for simple login functionality using a specific key seed.

#### Login
The login class uses the following format for keys:

```
keySeed = accountName + role + password
```

Using this seed, private keys are generated for either the default roles `active, owner, memo`, or as specified. A minimum password length of 12 characters is enforced, but an even longer password is recommended. Three methods are provided:

```
generateKeys(account, password, [roles])
checkKeys(account, password, auths)
signTransaction(tr)
```

The auths object should contain the auth arrays from the account object. An example is this:

```
{
active: [
["GPH5Abm5dCdy3hJ1C5ckXkqUH2Me7dXqi9Y7yjn9ACaiSJ9h8r8mL", 1]
]
}
```

If checkKeys is successful, you can use signTransaction to sign a TransactionBuilder transaction using the private keys for that account.

#### State container
The Chain library contains a complete state container called the ChainStore. The ChainStore will automatically configure the `set_subscribe_callback` and handle any incoming state changes appropriately. It uses Immutable.js for storing the state, so all objects are return as immutable objects. It has its own `subscribe` method that can be used to register a callback that will be called whenever a state change happens.

The ChainStore has several useful methods to retrieve, among other things, objects, assets and accounts using either object ids or asset/account names. These methods are synchronous and will return `undefined` to indicate fetching in progress, and `null` to indicate that the object does not exist.

```
import {Apis} from "bitsharesjs-ws";
var {ChainStore} = require("bitsharesjs");
Apis.instance("wss://bitshares.openledger.info/ws", true).init_promise.then((res) => {
console.log("connected to:", res[0].network);
ChainStore.init().then(() => {
ChainStore.subscribe(updateState);
});
});
let dynamicGlobal = null;
function updateState(object) {
dynamicGlobal = ChainStore.getObject("2.1.0");
console.log("ChainStore object update\n", dynamicGlobal ? dynamicGlobal.toJS() : dynamicGlobal);
}
```

### ECC
The ECC library contains all the crypto functions for private and public keys as well as transaction creation/signing.

#### Private keys
As a quick example, here's how to generate a new private key from a seed (a brainkey for example):

```
var {PrivateKey, key} = require("bitsharesjs");
let seed = "THIS IS A TERRIBLE BRAINKEY SEED WORD SEQUENCE";
let pkey = PrivateKey.fromSeed( key.normalize_brainKey(seed) );
console.log("\nPrivate key:", pkey.toWif());
console.log("Public key :", pkey.toPublicKey().toString(), "\n");
```

#### Transactions
TODO transaction signing example

## ESDoc (beta)
```bash
npm i -g esdoc esdoc-es7-plugin
esdoc -c ./esdoc.json
open out/esdoc/index.html
```
9 changes: 9 additions & 0 deletions esdoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"source": ".",
"includes": ["\\.(js|es6)$"],
"excludes": ["node_modules"],
"destination": "./out/esdoc",
"index": "./README.md",
"package": "./package.json",
"title": "BitsharesJS Docs"
}
16 changes: 16 additions & 0 deletions examples/chainStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Apis} from "bitsharesjs-ws";
import {ChainStore} from "../lib";

Apis.instance("wss://bitshares.openledger.info/ws", true).init_promise.then((res) => {
console.log("connected to:", res[0].network);
ChainStore.init().then(() => {
ChainStore.subscribe(updateState);
});
});

let dynamicGlobal = null;
function updateState(object) {
dynamicGlobal = ChainStore.getObject("2.1.0");

console.log("ChainStore object update\n", dynamicGlobal ? dynamicGlobal.toJS() : dynamicGlobal);
}
8 changes: 8 additions & 0 deletions examples/privKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

import {PrivateKey, key} from "../lib";

let seed = "THIS IS A TERRIBLE BRAINKEY SEED WORD SEQUENCE";
let pkey = PrivateKey.fromSeed( key.normalize_brainKey(seed) );

console.log("\nPrivate key:", pkey.toWif());
console.log("Public key :", pkey.toPublicKey().toString(), "\n");
72 changes: 72 additions & 0 deletions examples/transfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {Apis} from "bitsharesjs-ws";
import {ChainStore, FetchChain, PrivateKey, TransactionHelper, Aes, TransactionBuilder} from "../lib";

var privKey = "5KBuq5WmHvgePmB7w3onYsqLM8ESomM2Ae7SigYuuwg8MDHW7NN";
let pKey = PrivateKey.fromWif(privKey);

Apis.instance("wss://testnet.bitshares.eu/ws", true)
.init_promise.then((res) => {
console.log("connected to:", res[0].network_name, "network");

ChainStore.init().then(() => {

let fromAccount = "bitsharesjs";
let memoSender = fromAccount;
let memo = "Testing transfer from node.js";

let toAccount = "faucet";

let sendAmount = {
amount: 10000,
asset: "TEST"
}

Promise.all([
FetchChain("getAccount", fromAccount),
FetchChain("getAccount", toAccount),
FetchChain("getAccount", memoSender),
FetchChain("getAsset", sendAmount.asset),
FetchChain("getAsset", sendAmount.asset)
]).then((res)=> {
// console.log("got data:", res);
let [fromAccount, toAccount, memoSender, sendAsset, feeAsset] = res;

// Memos are optional, but if you have one you need to encrypt it here
let memoFromKey = memoSender.getIn(["options","memo_key"]);
console.log("memo pub key:", memoFromKey);
let memoToKey = toAccount.getIn(["options","memo_key"]);
let nonce = TransactionHelper.unique_nonce_uint64();

let memo_object = {
from: memoFromKey,
to: memoToKey,
nonce,
message: Aes.encrypt_with_checksum(
pKey,
memoToKey,
nonce,
memo
)
}

let tr = new TransactionBuilder()

tr.add_type_operation( "transfer", {
fee: {
amount: 0,
asset_id: feeAsset.get("id")
},
from: fromAccount.get("id"),
to: toAccount.get("id"),
amount: { amount: sendAmount.amount, asset_id: sendAsset.get("id") },
memo: memo_object
} )

tr.set_required_fees().then(() => {
tr.add_signer(pKey, pKey.toPublicKey().toPublicKeyString());
console.log("serialized transaction:", tr.serialize());
tr.broadcast();
})
});
});
});
9 changes: 9 additions & 0 deletions lib/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
PrivateKey: require("./ecc/src/PrivateKey"),
PublicKey: require("./ecc/src/PublicKey"),
Signature: require("./ecc/src/signature"),
key: require("./ecc/src/KeyUtils"),
TransactionBuilder: require("./chain/src/TransactionBuilder"),
Login: require("./chain/src/AccountLogin"),
bitshares_ws: require("bitsharesjs-ws")
}
14 changes: 14 additions & 0 deletions lib/chain/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ChainStore from "./src/ChainStore";
import TransactionBuilder from "./src/TransactionBuilder";
import ChainTypes from "./src/ChainTypes";
import ObjectId from "./src/ObjectId";
import NumberUtils from "./src/NumberUtils";
import TransactionHelper from "./src/TransactionHelper";
import ChainValidation from "./src/ChainValidation";
import EmitterInstance from "./src/EmitterInstance";
import Login from "./src/AccountLogin";

const {FetchChainObjects, FetchChain} = ChainStore;

export {ChainStore, TransactionBuilder, FetchChainObjects, ChainTypes,
ObjectId, NumberUtils, TransactionHelper, ChainValidation, FetchChain, Login }
99 changes: 99 additions & 0 deletions lib/chain/src/AccountLogin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import PrivateKey from "../../ecc/src/PrivateKey";
import key from "../../ecc/src/KeyUtils";

import {get, set} from "./state";

var _keyCachePriv = {};
var _keyCachePub = {};

class AccountLogin {

constructor() {
let state = {loggedIn: false, roles: ["active", "owner", "memo"]};
this.get = get(state);
this.set = set(state);

this.subs = {};
}

addSubscription(cb) {
this.subs[cb] = cb;
}

setRoles(roles) {
this.set("roles", roles);
}

generateKeys(accountName, password, roles, prefix) {
var start = new Date().getTime();
if (!accountName || !password) {
throw new Error("Account name or password required");
}
if (password.length < 12) {
throw new Error("Password must have at least 12 characters");
}

let privKeys = {};
let pubKeys = {};

(roles || this.get("roles")).forEach(role => {
let seed = accountName + role + password;
let pkey = _keyCachePriv[seed] ? _keyCachePriv[seed] : PrivateKey.fromSeed( key.normalize_brainKey(seed) );
_keyCachePriv[seed] = pkey;

privKeys[role] = pkey;
pubKeys[role] = _keyCachePub[seed] ? _keyCachePub[seed] : pkey.toPublicKey().toString(prefix);

_keyCachePub[seed] = pubKeys[role];
});

return {privKeys, pubKeys};
}

checkKeys({accountName, password, auths}) {
if (!accountName || !password || !auths) {
throw new Error("checkKeys: Missing inputs");
}
let hasKey = false;

for (let role in auths) {
let {privKeys, pubKeys} = this.generateKeys(accountName, password, [role]);
auths[role].forEach(key => {
if (key[0] === pubKeys[role]) {
hasKey = true;
this.set(role, {priv: privKeys[role], pub: pubKeys[role]});
}
});
};

if (hasKey) {
this.set("name", accountName);
}

this.set("loggedIn", hasKey);

return hasKey;
}

signTransaction(tr) {
let myKeys = {};
let hasKey = false;

this.get("roles").forEach(role => {
let myKey = this.get(role);
if (myKey) {
hasKey = true;
console.log("adding signer:", myKey.pub);
tr.add_signer(myKey.priv, myKey.pub);
}
});

if (!hasKey) {
throw new Error("You do not have any private keys to sign this transaction");
}
}
}

let accountLogin = new AccountLogin();

export default accountLogin;
Loading

0 comments on commit fedd4e5

Please sign in to comment.