Simple JS login example

Here we demonstrate how you can implement a simple JavaScript web/Node.js application which logs in to GreenAddress wallet using your mnemonic, and checks balance.

Requirements

To implement this example, we use autobahn, bigi, bitcoinjs-lib, bip39, when, and (optionally) browserify, bufferutil, and utf-8-validate, so make sure you install them first in your project directory:

$ npm init  # if you haven't initialised the Node.js project yet
$ npm install --save autobahn@^0.9.9 bigi@^1.4.0 bitcoinjs-lib@^2.2.0 bip39@^2.2.0 when@^3.7.7
$ npm install --save-dev browserify@^13.0.0 bufferutil@^1.2.1 utf-8-validate@^1.2.1  # Browserify dependencies

Overview

To log in to a GreenAddress wallet, you need a private key derived from a mnemonic. The address corresponding to the key is passed to get_challenge() call to retrieve a ‘challenge’ string to be signed by the private key, which is then verified by the authenticate() call.

This example implements a simple GreenAddress login with mnemonic as the input, using the get_challenge() and authenticate() calls.

Deriving the private key from mnemonic

Because GreenAddress uses BIP39 for deriving keys from the mnemonic, you can use any BIP39 implementation to do this. We’ve chosen weilu’s implementation here because it’s first result in Google for ‘bip39 javascript’ at the time of writing this.

NOTE: We’ve simplified this implementation here by not using ‘random BIP 32 subpath’ which GreenAddress uses to avoid signing twice using the same key. If you want to use it, you need to pass appropriate random path_hex argument to authenticate() and then use corresponding derived private key.

To derive the private key, you can use the following code:

var bip39 = require('bip39');
var bitcoin = require('bitcoinjs-lib')

var derive_hd = function() {
    var mnemonic = '[your mnemonic]';
    var cur_net = bitcoin.networks.bitcoin;  // 'testnet' for testnet
    var seed = bip39.mnemonicToSeedHex(mnemonic);  // this is slow, perhaps move to a webworker
    return bitcoin.HDNode.fromSeedHex(seed, cur_net);
    // NOTE: master priv key shouldn't be used for signing because repeated signing using the
    // same key is dangerous, so in production a random BIP32 subpath should be used.
    // See https://github.com/greenaddress/GreenAddressWebFiles/blob/c675736a0839d109df65c3555a9c22829b9ef4cd/static/js/greenwallet/services.js#L2173
    // for example implementation
};

Logging in GreenAddress WAMP server

Connecting via WAMP

You can connect to the server using a simple new autobahn.Connection call:

var autobahn = require('autobahn');

var init_wamp = function() {
    return when.promise(function(resolve, reject) {
        connection = new autobahn.Connection({
            url: "wss://prodwss.greenaddress.it/v2/ws/",
            realm: "realm1"
        });
        connection.onopen = resolve;
        connection.onclose = function(reason, details) {
            reject(reason + ': ' + JSON.stringify(details));
        };
        connection.open();
    })
}

Logging in to the wallet and retrieving balance

We can use the previously derived private key to log in and retrieve the balance:

var BigInteger = require('bigi');

var get_balance = function(session) {
    var hd = derive_hd();
    // caller_disclose_me is required for authentication to work:
    session.caller_disclose_me = true;
    return session.call('com.greenaddress.login.get_challenge',
            [hd.keyPair.getAddress().toString()]).then(function(challenge) {
        var challenge_buf = new BigInteger(challenge).toBuffer();
        return hd.keyPair.sign(challenge_buf);
    }).then(function(signature) {
        return session.call('com.greenaddress.login.authenticate',
                [[signature.r.toString(), signature.s.toString()], false]);
    }).then(function(data) {
        if (data === false) {
            return when.reject('Login failed');
        } else {
            // data contains some wallet configuration data
            return session.call('com.greenaddress.txs.get_balance');
        }
    }).then(function(balance) {
        return balance.satoshi;
    });
};

Putting it all together

You can now connect all the above functions together:

init_wamp()
    // pass the session to allow further calls to get the balance
    .then(get_balance)
    // and finally print the balance
    .then(console.log)
    .then(function() {
        if (!process.browser) {
            process.exit(0);
        }
    })
    // or print any errors
    .catch(function(error) {
        console.log(error);
        if (!process.browser) {
            process.exit(1);
        }
    });

Preparing a Browserify bundle (optional)

To prepare your JavaScript code for running it in a browser, we will use a tool called Browserify. If you just want to run the code using Node.js, you can skip this step.

In some file, called for example gaexample.js, put the JavaScript including all the functions from above, then simply run ./node_modules/.bin/browserify gaexample.js to get browser-ready code.