Skip to content

Commit

Permalink
Use bitcoinjs-lib for bitcoin address generation
Browse files Browse the repository at this point in the history
  • Loading branch information
ramontayag committed May 26, 2018
1 parent 420e593 commit 2913a46
Show file tree
Hide file tree
Showing 10 changed files with 442 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,4 @@ DEPENDENCIES
webmock

BUNDLED WITH
1.16.1
1.16.2
17 changes: 12 additions & 5 deletions app/lib/btc/address_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ def address(idx)
private

def multisig_address(idx)
keychain_group = BTC::KeychainGroup.new(extended_keys: xpub)
keychain_group.standard_address({
index: idx,
signatures_required: signatures_required,
})
keychains = xpub.map { |x| BTC::Keychain.new(extended_key: x) }
keys = keychains.map { |keychain| keychain.derived_key(idx) }
public_keys = keys.map { |key| BTC.to_hex(key.public_key) }.sort

command = [
"node",
Rails.root.join("lib", "address_gen.js"),
public_keys.join(","),
signatures_required,
].join(" ")
stdout_str, stderr_str, status = Open3.capture3(command)
stdout_str.chomp
end

def single_address(idx)
Expand Down
10 changes: 10 additions & 0 deletions docs/bitcoin.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ Bitcoin addresses will be generated from the supplied `BTC_MASTER_PUBLIC_KEY`.
## Bitcoind setup

- Set `txindex=1` to [index all transactions](https://bitcore.io/guides/full-node/).

## Tips

If you will be using a wallet like Electrum to manage the funds, the master public key you find there *is not* the one you should place here. The key placed here can be derived the following manner:

```sh
ruby scripts/electrum.rb master_public_key_you_see_in_electrum
```

Of course, test that the addresses generated by this application are the same as the receiving addresses in Electrum.
10 changes: 10 additions & 0 deletions lib/address_gen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let bitcoin = require('bitcoinjs-lib')

let pubKeys = process.argv[2].split(",").sort().map(function (hex) { return Buffer.from(hex, 'hex') })
let signaturesRequired = parseInt(process.argv[3])

var redeemScript = bitcoin.script.multisig.output.encode(signaturesRequired, pubKeys)
var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
var address = bitcoin.address.fromOutputScript(scriptPubKey)

console.log(address)
208 changes: 208 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"name": "crypto_cold_store",
"private": true,
"dependencies": {}
"dependencies": {
"bitcoinjs-lib": "^3.3.2"
}
}
8 changes: 8 additions & 0 deletions scripts/electrum.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require "btcruby"

def receiving_mpk_for(parent_mpk)
keychain = BTC::Keychain.new(extended_key: parent_mpk)
keychain.derived_keychain("0").xpub
end

puts receiving_mpk_for(ARGV[0])
23 changes: 14 additions & 9 deletions spec/lib/btc/address_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,25 @@ module Btc
end

context "multiple extended keys" do
let(:generator) do
described_class.new(xpub: xpub, signatures_required: 2)
end
let(:xpub) do
[
"xpub6DY7Fqdz98GSsFDN96Levia3PwnREqhFER5RtKwiwrDzBJpEtGX5VcZdPrLgJriUfStunLmWYxrHM6XPygEJhrXZGrVh1fVZc2AQkAVPf9n",
"xpub6DfZs3n92pxJ3LCBf7bfgbyrECteT8PWmee5UpZhG1aBurdF5t1Tu2jxdnCBXETztHu6YkJ8Hin8t8qwPsh3YScNX3dLxduNSaevLF3KLpq",
"xpub6FFgmnaotx9GP3XkdK8oS8j3ExWBXXGdJ5LeWYmCHUAppaG85HPm4QRGrtowZJfwzDqjKkp1kc5mzUwa2W1PEicuynzC45myaa4vVG7bNJy",
# These are from Electrum
mpks = [
"xpub6D4K8Hb6sMZwwtbk6p94RnRewTb6PykJc1zZwB3GUozGSNXLnTdL2T8uXRehiD9d5Dis3bDpJCvR3pFZcYUu2xpPj5Z73oFNUUQKwkFTewo",
"xpub661MyMwAqRbcGHY2QrfyiUyvSv93zCQN8Cv3eXFB3ivnq7SQ6TLUJueJnGZKR8r8CQ5cLsGrr9D71uwAW9RLKqFyUybARtbbqg1kiHopBsZ",
]
# but since we want the xpub of the receiving chain and not change
# chain...
mpks.map do |mpk|
BTC::Keychain.new(extended_key: mpk).derived_keychain("0").xpub
end
end
let(:generator) do
described_class.new(xpub: xpub, signatures_required: 1)
end

it "generates an address at the given index for the required number of signatures" do
expect(generator.address(0)).to eq "397Zemr1wRy3hznQBsdQeeu1d8U5M1HrTZ"
expect(generator.address(18)).to eq "3GAGdkU6awG9aiDyiyrmrjvo8tpHbab5Ff"
expect(generator.address(0)).to eq "3GbE5Hn5NNfAbkk33AquoZqtoehkDDBEZn"
expect(generator.address(18)).to eq "3CwgJ7iYBWufLpwgsGKikjEH5YL38CAfz3"
end
end
end
Expand Down
21 changes: 13 additions & 8 deletions spec/services/btc/addresses/creation/gen_address_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@ module Creation
RSpec.describe GenAddress do

let(:master_public_key) do
[
"xpub6DY7Fqdz98GSsFDN96Levia3PwnREqhFER5RtKwiwrDzBJpEtGX5VcZdPrLgJriUfStunLmWYxrHM6XPygEJhrXZGrVh1fVZc2AQkAVPf9n",
"xpub6DfZs3n92pxJ3LCBf7bfgbyrECteT8PWmee5UpZhG1aBurdF5t1Tu2jxdnCBXETztHu6YkJ8Hin8t8qwPsh3YScNX3dLxduNSaevLF3KLpq",
"xpub6FFgmnaotx9GP3XkdK8oS8j3ExWBXXGdJ5LeWYmCHUAppaG85HPm4QRGrtowZJfwzDqjKkp1kc5mzUwa2W1PEicuynzC45myaa4vVG7bNJy",
# These are from Electrum
# chain...
mpks = [
"xpub6D4K8Hb6sMZwwtbk6p94RnRewTb6PykJc1zZwB3GUozGSNXLnTdL2T8uXRehiD9d5Dis3bDpJCvR3pFZcYUu2xpPj5Z73oFNUUQKwkFTewo",
"xpub661MyMwAqRbcGHY2QrfyiUyvSv93zCQN8Cv3eXFB3ivnq7SQ6TLUJueJnGZKR8r8CQ5cLsGrr9D71uwAW9RLKqFyUybARtbbqg1kiHopBsZ",
]
# but since we want the xpub of the receiving chain...
mpks.map do |mpk|
BTC::Keychain.new(extended_key: mpk).derived_keychain("0").xpub
end
end
let(:address_18) { "3GAGdkU6awG9aiDyiyrmrjvo8tpHbab5Ff" }
let(:address_1) { "3Dur7FTopK9jAUNSmLyGggMQc1nsnL2Cv6" }

it "generates an address based on the keychain[address_index]" do
resulting_ctx = described_class.execute(
master_public_key: master_public_key,
signatures_required: 2,
address_index: 18,
signatures_required: 1,
address_index: 1,
)
expect(resulting_ctx.public_address).to eq address_18
expect(resulting_ctx.public_address).to eq address_1
end

end
Expand Down
Loading

0 comments on commit 2913a46

Please sign in to comment.