Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kimdhamilton committed Jul 17, 2017
0 parents commit d9dedf2
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.idea/*

node_modules/*

bundle*.js
.babel*

.DS_Store*
.DS_Store*
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Create BTCR DID

This is a prototype-only script to create BTCR DIDs.


## Install
```
git clone [email protected]:WebOfTrustInfo/btcr-did-tools-js.git
npm install
```

## Preparation

We recommend starting in testnet mode. You will need testnet addresses for the input address and the change address.


### How to generate testnet addresses
If you don't have a testnet address yet, and don't know how to create one, you can start with this client-side address generator: [https://www.bitaddress.org/?testnet=true](https://www.bitaddress.org/?testnet=true).

As this is a client-side address generator, and will be passing private keys, go ahead and disconnect your internet connection. Even though this is testnet mode, it's a good practice to start following.

Once you've disconnected from the internet and generated a testnet address, record the Bitcoin address (what you can share with the public) and the Private Key (always keep this private). The private key is in Wallet Import Format (WIF).

![](img/bitaddress.png)


### How to get testnet funds

Lastly, you'll need some funds in your input (funding) address before creating the Bitcoin testnet transaction in the following steps.

To get some testnet funds, search for a testnet faucet and send to your input address.

Be sure to hold onto your private keys. Even in testnet mode, you'll want to send your testnet coins back to the faucet provider when you are finished. This tool only uses a small amount of money, and many testnet faucets will send you much more than you need.


## Ensure your private key WIF is available as an environment variable

```
export WIF=<privateKeyWif>
```

## Create BTCR DID with DDO/1 ref

```
node createBtcrDid.js -i <inputAddress> -c <changeAddress> -d <link-to-ddo.jsonld>
```

## Create BTCR DID without DDO/1 ref

```
node createBtcrDid.js -i <inputAddress> -c <changeAddress>
```

## Usage

The default node is testnet; call `--help` for more options

```
node createBtcrDid.js --help
```
176 changes: 176 additions & 0 deletions createBtcrDid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
var bitcoin = require('bitcoinjs-lib');
var program = require('commander');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;


const MAINNET_BLOCKR_IO = "https://btc.blockr.io/api/v1";
const TESTNET_BLOCKR_IO = "https://tbtc.blockr.io/api/v1";
const SATOSHIS_PER_BTC = 100000000;


let request = obj => {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest();

request.addEventListener('load', () => {
if (request.status >= 200 && request.status < 300) {
resolve(request.responseText);
} else {
reject(new Error(request.responseText));
}
});
request.addEventListener('error', () => {
console.error(request.status);
reject(new Error(request.status));
});

request.open(obj.method || "GET", obj.url);
request.responseType = "json";
if (obj.body !== null) {
request.send(JSON.stringify(obj.body));
} else {
request.send();
}
});
};


class UnspentOut {
/**
*
* @param address
* @param txid
* @param amount in BTC
* @param numConfirmations
* @param script
*/
constructor(address, txid, amount, numConfirmations, script) {
this.address = address;
this.txid = txid;
this.amount = amount;
this.numConfirmations = numConfirmations;
this.script = script;
}
}


class BlockrIOBroadcaster {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}

getUnspentOutputs(address) {
let url = this.baseUrl + "/address/unspent/" + address;
let obj = {
"url": url
};
return request(obj)
.then(result => {
let resultJson = JSON.parse(result);
if (resultJson.data.unspent == null || resultJson.data.unspent.length == 0) {
throw Error("no unspent outputs for address " + address);
}
let firstUnspent = resultJson.data.unspent[0];
return new UnspentOut(address,
firstUnspent.tx,
firstUnspent.amount,
firstUnspent.confirmations,
firstUnspent.script);
}, error => {
console.error(error);
throw error;
});
}

broadcast(hextx) {
let url = this.baseUrl + '/tx/push';
let obj = {
"url": url,
"method": "POST",
"body": {"hex": hextx}
};
return request(obj)
.then(result => {
return result;
}, error => {
console.error(error);
throw error;
});
}
}


var createDidTx = function (network, wif, inputTxid, outputAddress, ddo1Ref, changeAmount) {
let tx = new bitcoin.TransactionBuilder(network);
tx.addInput(inputTxid, 0);
tx.addOutput(outputAddress, changeAmount);

if (ddo1Ref !== null) {
let data = new Buffer(ddo1Ref);
ret = bitcoin.script.compile(
[
bitcoin.opcodes.OP_RETURN,
data
]);
tx.addOutput(ret, 0);
}
key = bitcoin.ECPair.fromWIF(wif, network);
tx.sign(0, key);
return tx.build().toHex();
}



var inputAddress = null;
var changeAddress = null;
var ddo1Ref = null;
var fee = 0;
var chain = null;


program
.version('1.0.0')
.usage('[options]')
.option('-i, --inputAddress <inputAddress>', 'input (funding) address; required')
.option('-c, --changeAddress <changeAddress>', 'change address; required')
.option('-n, --network <network>', 'testnet or mainnet; default is testnet', 'testnet')
.option('-d, --ddo1Ref <ddo1Ref>', 'DDO/1 reference; will be added to the OP_RETURN field. Can be null')
.option('-f, --fee <fee>', 'Transaction fee in BTC. Default is 0.001 BTC', 0.001);

program.parse(process.argv);

inputAddress = program.inputAddress;
changeAddress = program.changeAddress;
ddo1Ref = program.ddo1Ref;
fee = program.fee;
chain = program.network === "mainnet" ? bitcoin.networks.mainnet : bitcoin.networks.testnet;

if (inputAddress === null || changeAddress === null) {
program.help();
process.exit(1);
}

let baseUrl = chain === bitcoin.networks.bitcoin ? MAINNET_BLOCKR_IO : TESTNET_BLOCKR_IO;
let connector = new BlockrIOBroadcaster(baseUrl);

let wif = process.env.WIF;

connector.getUnspentOutputs(inputAddress)
.then(unspentOutput => {
let change = unspentOutput.amount - fee; // BTC
let changeSatoshi = change * SATOSHIS_PER_BTC; // SATOSHI
let signedHexTx = createDidTx(chain, wif, unspentOutput.txid, changeAddress, ddo1Ref, changeSatoshi);
connector.broadcast(signedHexTx)
.then(result => {
console.log(result);
return result;
}, error => {
console.error(error);
throw error;
});

}, error => {
console.error(error);
throw error;
});

Binary file added img/bitaddress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "btcr-did-js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "npm run test"
},
"author": "Kim Hamilton Duffy",
"license": "MIT",
"dependencies": {
"bitcoinjs-lib": "^3.1.0",
"commander": "^2.11.0",
"xmlhttprequest": "^1.8.0"
}
}

0 comments on commit d9dedf2

Please sign in to comment.