Skip to content

Instantly share code, notes, and snippets.

@justinmoon
Last active February 1, 2020 05:25
Show Gist options
  • Save justinmoon/a7bbf738a75e4435ff1f3484a6acdc9b to your computer and use it in GitHub Desktop.
Save justinmoon/a7bbf738a75e4435ff1f3484a6acdc9b to your computer and use it in GitHub Desktop.
Electrum notes
// Prototype communicating to Electrum server backend
// Not currently used
const jayson = require('jayson/promise')
const bitcoin = require('bitcoinjs-lib')
const bip32 = require('bip32')
const testnet = bitcoin.networks.testnet
function getScriptHash(xpub, index) {
const {address} = bitcoin.payments.p2wpkh({
pubkey: bip32
.fromBase58(xpub, testnet)
.derive(1)
.derive(index).publicKey,
network: testnet,
});
// FIXME: this is roundabout
let script = bitcoin.address.toOutputScript(address, testnet)
let hashBuf = bitcoin.crypto.sha256(script)
let reversedHashString = hashBuf.toString('hex').match(/.{2}/g).reverse().join("")
return reversedHashString
}
function getScriptHashes(xpub) {
let scriptHashes = []
for (let i = 0; i < 20; i += 1) {
const scriptHash = getScriptHash(xpub, i)
scriptHashes.push(scriptHash)
}
return scriptHashes
}
const concatResultReducer = (accumulator, response) => accumulator.concat(response.result)
// This is going to need a connection manager if we're working with random public nodes
export class ElectrumProtocolBackend {
constructor(account) {
this.account = account
// TODO: maybe we should pass the Account in here so that we can look at signers?
this.client = jayson.client.tcp({
host: '68.183.110.103',
port: 50001,
})
}
balances() {
// FIXME: jayson must have a batching mode to facilitate this ...
let requests = []
const scriptHashes = getScriptHashes(this.account.signers[0].xpub)
// each script pubkey
for (var scriptHash of scriptHashes) {
// call blockchain.scripthash.get_balance
let request = this.client.request('blockchain.scripthash.get_balance', [scriptHash])
requests.push(request)
}
const reducer = (accumulator, response) => ({
confirmed: accumulator.confirmed + response.result.confirmed,
unconfirmed: accumulator.unconfirmed + response.result.unconfirmed,
})
return Promise.all(requests).then(responses => {
return responses.reduce(
(accumulator, response) => ({
confirmed: accumulator.confirmed + response.result.confirmed,
unconfirmed: accumulator.unconfirmed + response.result.unconfirmed,
}),
{confirmed: 0, unconfirmed: 0}
)
})
}
utxos() {
let requests = []
const scriptHashes = getScriptHashes(this.account.signers[0].xpub)
for (var scriptHash of scriptHashes) {
let request = this.client.request('blockchain.scripthash.listunspent', [scriptHash])
requests.push(request)
}
const initial = []
return Promise.all(requests).then(responses => {
return responses.reduce(concatResultReducer, initial)
})
}
async transactions() {
// each pubkey
// result in blockchain.scripthash.history
// result in blockchain.transaction.get (verbose=true)
// return Tranasction instance
// Fetch txids
let historyRequests = []
const scriptHashes = getScriptHashes(this.account.signers[0].xpub)
for (var scriptHash of scriptHashes) {
let request = this.client.request('blockchain.scripthash.get_history', [scriptHash])
historyRequests.push(request)
}
const history = await Promise.all(historyRequests).then(responses => {
return responses.reduce(concatResultReducer, [])
})
// Fetch full transactions
const transactionRequests = []
for (var tx of history) {
let request = this.client.request('blockchain.transaction.get', [tx.tx_hash, true])
transactionRequests.push(request)
}
return Promise.all(transactionRequests).then(responses => {
return responses.reduce(concatResultReducer, [])
})
}
broadcast() {}
}
const bitcoin = require('bitcoinjs-lib');
const testnet = bitcoin.networks.testnet;
const mainnet = bitcoin.networks.mainnet;
const bip32 = require('bip32');
const jayson = require('jayson/promise')
const fs = require('fs')
const path = require('path')
// Silly hack: https://github.com/request/request/issues/418#issuecomment-17149236
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
const testnetClient = jayson.client.tcp({
//host: "testnetnode.arihanc.com",
//port: 50002,
//host: "testnet.hsmiths.com",
//port: 53012,
host: '68.183.110.103',
port: 50001,
})
const mainnetClient = jayson.client.tcp({
// mainnet
//host: "electrum.hsmiths.com",
host: "node.arihanc.com",
port: 50001,
})
async function getBalancesForAddress(client, address, network) {
let script = bitcoin.address.toOutputScript(address, network)
console.log(script)
let hashBuf = bitcoin.crypto.sha256(script)
let scriptHash = hashBuf.toString('hex').match(/.{2}/g).reverse().join("")
console.log(scriptHash)
client.request('blockchain.scripthash.get_balance', [scriptHash]).then(console.log).catch(console.error)
}
const mainnetAddress = '17A16QmavnUfCW11DAApiJxp7ARnxN5pGX'
//const testnetAddress = '2Mt9iSWNS6eXPeZ5tGo3uJSkGzobMzqEiKa'
const testnetAddress = 'tb1qgvyn7zyl329m663lpgdw042hqslmfsndugnwnm'
function getMainnetBalance() {
getBalancesForAddress(mainnetClient, mainnetAddress, mainnet)
}
function sendVersion(client) {
client.request('server.version', ["2.7.11", "1.4"])
}
function getTestnetBalance() {
sendVersion(testnetClient)
getBalancesForAddress(testnetClient, testnetAddress, testnet)
}
//getMainnetBalance()
getTestnetBalance()
//
// Everything is testnet for now
//
const bitcoin = require('bitcoinjs-lib');
const testnet = bitcoin.networks.testnet;
const mainnet = bitcoin.networks.mainnet;
const bip32 = require('bip32');
const ElectrumProtocolClient = require('./electrum.js')
const jayson = require('jayson/promise')
const client = jayson.client.tcp({
// mainnet
//host: "electrum.hsmiths.com",
//host: "node.arihanc.com",
// testnet
//host: "testnet.hsmiths.com",
//host: "testnet1.bauerj.eu",
//host: "testnet.qtornado.com",
host: "bitcoin-test.networkingfanatic.com",
port: 50001,
})
async function getBalancesForAddress(address) {
let script = bitcoin.address.toOutputScript(address, mainnet)
console.log(script)
let hashBuf = bitcoin.crypto.sha256(script)
let scriptHash = hashBuf.toString('hex').match(/.{2}/g).reverse().join("")
console.log(scriptHash)
client.request('blockchain.scripthash.get_balance', [scriptHash]).then(console.log).catch(console.error)
}
// should equal 8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161
//console.log(getBalancesForAddress('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'))
// mine
//const myaddress = 'tb1qgvyn7zyl329m663lpgdw042hqslmfsndugnwnm'
// Random mainnet addresses
//const myaddress = '1521WZxZnReQ6H3TCHh5nBjaRu3HPXM2NX'
//const myaddress = 'bc1q62x32j8kkvzdjzy8sulduj7ft4czu5tq6w8f4d'
//const myaddress = '1EYTGtG4LnFfiMvjJdsU7GMGCQvsRSjYhx'
//const myaddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
// Random testnet address
const myaddress = '2Mt9iSWNS6eXPeZ5tGo3uJSkGzobMzqEiKa'
//getBalancesForAddress(myaddress)
// works
//client.request(
//'blockchain.scripthash.get_balance',
//['8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161'],
//).then(console.log).catch(console.error)
function getScriptHash(xpub, index) {
const {address} = bitcoin.payments.p2wpkh({
pubkey: bip32
.fromBase58(xpub, testnet)
.derive(1)
.derive(index).publicKey,
network: testnet,
});
console.log(address)
// FIXME: this is roundabout
let script = bitcoin.address.toOutputScript(address, testnet)
let hashBuf = bitcoin.crypto.sha256(script)
return hashBuf.toString('hex')
}
function getScriptHashes(xpub) {
let scriptHashes = []
for (let i = 0; i < 20; i += 1) {
const scriptHash = getScriptHash(xpub, i)
scriptHashes.push(scriptHash)
}
return scriptHashes
}
// Fetch the first 20 balances for this xpub
function getBalances(xpub) {
const scriptHashes = getScriptHashes(xpub)
let requests = []
for (let i = 0; i < scriptHashes.length; i++) {
const scriptHash = scriptHashes[i]
const request = client.request('blockchain.scripthash.get_balance', [scriptHash])
requests.push(request)
}
const reducer = (accumulator, response) => {
console.log(response)
return ({
confirmed: accumulator.confirmed + response.result.confirmed,
unconfirmed: accumulator.unconfirmed + response.result.unconfirmed,
})
}
const initial = {confirmed: 0, unconfirmed: 0}
return Promise.all(requests).then(responses => {
return responses.reduce(reducer, initial)
})
}
//getBalances().then(console.log)
const XPRV = "tprv8ZgxMBicQKsPeCXRXf7d8ibQiXrei23kAa3tPkH7aKg33LFrtG6JhF3zQCiy8ADzoEr6C9Kj7PCq23Lehq1redvYpYnCSS6Ledt6CwiXTUx"
const XPUB = 'tpubD6NzVbkrYhZ4XfZDRJnDY8FXHZNasMEejsefgGKQzbURspWdWeutsjfraKQqpmVJUKDHdzuXPKzJ2U1gLSsHqhg1qPZhYn4hTyYt5r2dfHp'
const FINGERPRINT = '41cdd223'
function getAddress(index) {
const pubkey = bip32
.fromBase58(XPRV, testnet)
.derive(1)
.derive(index)
const {address} = bitcoin.payments.p2wpkh({
pubkey: pubkey.publicKey,
network: testnet,
})
return address
}
function xprvToXpub(xprv) {
return bip32.fromBase58(XPRV, testnet).neutered().toBase58()
}
function getUtxos(xpub) {
return []
}
function getTransactions(xpub) {
return []
}
//getBalances(XPRV).then(console.log)
module.exports = {
getBalances,
getUtxos,
getTransactions,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment