Skip to content
This repository has been archived by the owner on Nov 16, 2019. It is now read-only.

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
bartekn committed Mar 23, 2015
0 parents commit 74ff507
Show file tree
Hide file tree
Showing 18 changed files with 741 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
/node_modules
58 changes: 58 additions & 0 deletions README.md
@@ -0,0 +1,58 @@
# `stellard` Module

The `stellard` module provides the lower level mechanisms to communicate with the stellar network.

This module is intended to be a cover over `stellar-lib`, integrating well with an modular client environment.

This is meant primarily for backend code; `services` and `lib` scripts.

## Notable Services

The two major services provided by this module are [stellard.Network](services/network.service.es6) and [stellard.Sessions](services/sessions.service.ed6).

### stellard.Network

The Network service manages low level connections (i.e. stellar-lib Remote objects) and wraps them in a Promise-based interface. Additionally, this service will broadcast events on the root scope that other components can use to get notifications regarding network state.


#### Example Usage

```javascript

//Load someone's balance

let network = $get('stellard.Network');
// connect to the live stellar network
network.get("live")
// wait until we are connected
.ensureConnected()
// issue a request
.then(nc => nc.sendRequest("account_info", {account: "gM4Fpv2QuHY4knJsQyYGKEHFGw3eMBwc1U"}))
// process results
.then(result => {
console.log(result.account_data.Balance);
});

```

### stellard.Sessions

The Network service has no concept of a user. NetworkConnection objects do not have an associated address or secret. The Sessions service is where we introduce addresses and secrets.

A session object (obtained through either `Sessions.get` or `Sessions.default`) present a similar interface to a `NetworkConnection` object. The difference is that for helper methods, it will inject the sessions address/secret automatically on the higher level operations.

In addition, a session object has a `withSecret` method that allows you to upgrade to a new session that can, for example, sign requests as needed.

#### Example Usage

```javascript

// watch an account on both live and testnet
let sessions = $get('stellard.Sessions');
let live = sessions.create("account_live", { address: "gM4Fpv2QuHY4knJsQyYGKEHFGw3eMBwc1U", connection: 'live' });
let test = sessions.create("account_test", { address: "gM4Fpv2QuHY4knJsQyYGKEHFGw3eMBwc1U", connection: 'test' });

live.listenForPayments();
test.listenForPayments();

```
13 changes: 13 additions & 0 deletions errors.es6
@@ -0,0 +1,13 @@


export class MismatchedAddressError extends Error { }
export class SessionAlreadyDefinedError extends Error { }
export class SessionNotFoundError extends Error { }
export class ConnectionNotFoundError extends Error { }

export class TransactionFailedError extends Error {
constructor(result) {
super(`Transaction Failed: ${result.engine_result}`);
this.result = result;
}
}
15 changes: 15 additions & 0 deletions index.es6
@@ -0,0 +1,15 @@
import { Module, mod as mcsCore } from "mcs-core";

export const mod = new Module('mcs-stellard');

mod.use(mcsCore.name);

mod.services = require.context("./services", true);
mod.setupBlocks = require.context("./setup-blocks", true);

mod.define();

export * from "./errors";
export { AddressSecretPair } from "./lib/address-secret-pair";

export let lib = require("stellar-lib");
27 changes: 27 additions & 0 deletions lib/address-secret-pair.es6
@@ -0,0 +1,27 @@
let stellarLib = require('stellar-lib');

export class AddressSecretPair {
constructor(seed) {
/*jshint camelcase: false */

this.stellarSeed = new stellar.Seed();

if (seed) {
this.stellarSeed.parse_json(seed);
} else {
this.stellarSeed.random();
}
}

get secret() {
return this.stellarSeed.to_json();
}

get address() {
return this.stellarSeed
.get_key()
.get_address()
.to_json()
;
}
}
87 changes: 87 additions & 0 deletions lib/network-connection.es6
@@ -0,0 +1,87 @@
let _ = require("lodash");
let stellarLib = require('stellar-lib');

window.stellar = stellarLib;

export class NetworkConnection {
constructor($q, network, name, connectionSpec) {
this.$q = $q;
this.network = network;
this.name = name;
this.connectionSpec = connectionSpec;
this.connected = false;
this._remote = new stellarLib.Remote(connectionSpec, true);

this._remote.on('connected', () => {
/*jshint camelcase: false */
// TODO: need to figure out why this isn't being set when we connect to the stellard
this._remote._reserve_base=50*1000000;
this._remote._reserve_inc=10*1000000;
this.connected = true;
this.network.onConnected(this);

if(this.waitingForConnection) {
this.waitingForConnection.resolve(this);
}
});

this._remote.on('connecting', () => this.network.onConnecting(this));
this._remote.on('disconnected', () => this.network.onDisconnected(this));
this._remote.on('reconnecting', (t) => this.network.onReconnecting(this, t));
this._remote.on('transaction', (tx) => this.network.onTransaction(this, tx));
}

disconnect() {
this._remote.disconnect();
this.connected = false;

this._remote.removeAllListeners('connected');
this._remote.removeAllListeners('disconnected');
this._remote.removeAllListeners('reconnecting');
this._remote.removeAllListeners('connecting');
this._remote.removeAllListeners('transaction');

}

forceReconnect() {
/* jshint camelcase:false */
this._remote.force_reconnect();
}

ensureConnected() {
if (this.connected) {
return this.$q.when(this);
} else if (this.waitingForConnection) {
return this.waitingForConnection.promise;
} else {
this.waitingForConnection = this.$q.defer();
this._remote.connect();
return this.waitingForConnection.promise;
}
}

sendRequest(method, params) {
let req = new stellarLib.Request(this._remote, method);

// fold the params into the message object
_.extend(req.message, params);

var deferred = this.$q.defer();

req.on('success', deferred.resolve);
req.on('error', deferred.reject);
req.request();

return deferred.promise;
}

sendTransaction(tx) {
let deferred = this.$q.defer();

tx.on('success', deferred.resolve);
tx.on('error', deferred.reject);
tx.submit();

return deferred.promise;
}
}
38 changes: 38 additions & 0 deletions lib/session.es6
@@ -0,0 +1,38 @@
let { AddressSecretPair } = require("../lib/address-secret-pair");
let { MismatchedSecretError } = require("../errors");

export class Session {
constructor(address, secret, connection) {
this.address = address;
this.secret = secret;
this.connection = connection;
}

withSecret(secret) {
let source = new AddressSecretPair(secret);

if (source.address !== this.address) {
throw new MismatchedSecretError();
}

return new Session(source.address, source.secret, this.connection);
}

sendRequest(...args) {
return this.connection.sendRequest(...args);
}

sendTransaction(...args) {
return this.connection.sendTransaction(...args);
}

ensureConnected() {
return this.connection.ensureConnected().then(() => this);
}

destroy() {
//TODO: any cleanup needed
}

//TODO: add helper methods here. getBalance, etc.
}
19 changes: 19 additions & 0 deletions package.json
@@ -0,0 +1,19 @@
{
"name": "mcs-stellard",
"version": "0.0.1",
"description": "The mcs-stellard modules provides the communication layer with stellard and session containers.",
"keywords": ["stellar", "mcs"],
"scripts": {
"test": "gulp test"
},
"author": "Stellar Development Foundation <stellar@stellar.org>",
"license": "ISC",
"repository": {
"type": "git",
"url": "http://github.com/stellar/mcs-stellard.git"
},
"dependencies": {
"mcs-core": "git+https://github.com/stellar/mcs-core.git#master",
"stellar-lib": "git+https://github.com/stellar/stellar-lib.git#master"
}
}
95 changes: 95 additions & 0 deletions services/network.service.es6
@@ -0,0 +1,95 @@
let _ = require('lodash');
let { NetworkConnection } = require("../lib/network-connection");

/**
The Network service is used to communicate with the Stellar network.
@namespace stellard.Network
*/
import { ConnectionNotFoundError } from "../errors";

class Network {
constructor($rootScope, $timeout, $q, config) {
this.$rootScope = $rootScope;
this.$timeout = $timeout;
this.$q = $q;
this.config = config;
this.connections = {};
}

shutdown() {
this.connections.forEach(r => r.disconnect());
this.connections.clear();
}

ensureConnected(name) {
try {
return this._getConnection(name).ensureConnected();
} catch(e) {
return this.$q.reject(e);
}
}

get(name) {
return this._getConnection(name);
}

forceReconnect(name) {
let connection = this.connections[name];
connection.forceReconnect();
}

// NetworkConnection callbacks

onConnecting(connection) {
this._safeBroadcast('stellar-network:connecting', connection.name);
}

onConnected(connection) {
this._safeBroadcast('stellar-network:connected', connection.name);
}

onDisconnected(connection) {
this._safeBroadcast('stellar-network:disconnected', connection.name);
}

onReconnecting(connection, timeout) {
this._safeBroadcast('stellar-network:reconnecting', connection.name, timeout);
}

onTransaction(connection, tx) {
this._safeBroadcast('stellar-network:transaction', connection.name, tx);
}

_getConnection(name) {
if(!this.connections[name]) {
let config = this._getConnectionSpec(name);
this.connections[name] = new NetworkConnection(this.$q, this, name, config);
}

return this.connections[name];
}

_getConnectionSpec(name) {
let configKey = `stellard/connections/${name}`;
let connectionSpec = this.config.get(configKey);

if (!connectionSpec) {
throw new ConnectionNotFoundError(`No connection config found at key: ${configKey}`);
}

return connectionSpec;
}

_safeBroadcast(event, ...args) {
this.$timeout(() => {
this.$rootScope.$broadcast(event, ...args);
});
}
}

Network.$inject = ["$rootScope", "$timeout", "$q", 'mcs-core.Config'];

module.exports = function(mod) {
mod.service("Network", Network);
};

0 comments on commit 74ff507

Please sign in to comment.