Skip to content

Instantly share code, notes, and snippets.

@j-chimienti
Last active October 7, 2020 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save j-chimienti/c43a234fc4de9b866e0ccde3f1937ccf to your computer and use it in GitHub Desktop.
Save j-chimienti/c43a234fc4de9b866e0ccde3f1937ccf to your computer and use it in GitHub Desktop.
import scala.io.StdIn
import scala.sys.process._
// CHANGE ME
val bitcoinClient = "bitcoin-cli" // """docker exec btcpayserver_bitcoind bitcoin-cli -datadir="/data""""
def getNewAddress = s"$bitcoinClient getnewaddress".!!.trim
def getAddressInfo(address: String) = s"$bitcoinClient -named getaddressinfo address=$address".!!.trim
def publicKey(address: String): String = {
val info = getAddressInfo(address)
val json = ujson.read(info)
json("pubkey").str
}
def listUnspent = s"$bitcoinClient listunspent".!!.trim
def getBalance = BigDecimal(s"$bitcoinClient getbalance".!!.trim)
def listTransaction(transaction: String) = s"$bitcoinClient gettransaction $transaction".!!.trim
def sendToAddress(address: String, amount: Double) = s"$bitcoinClient sendtoaddress $address $amount".!!
def importAddress(address: String, rescan: Boolean = false) =
s"""$bitcoinClient -named importaddress address="$address" rescan=$rescan""".!!.trim
case class PsbtInput(txid: String, vout: Int)
implicit val formatPsbtInput : upickle.default.ReadWriter[PsbtInput] = upickle.default.macroRW
// machine1$ psbt=$(bitcoin-cli -named createpsbt inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$split1'": 0.009998,"'$split2'": 0.009998 }''')
def createPtsd(inputs: Seq[PsbtInput], outputs: Map[String, Double]) = {
val inputJson = upickle.default.write(inputs)
val outputJson = upickle.default.write(outputs)
val command = s"""$bitcoinClient -named createpsbt inputs='''$inputJson''' outputs='''$outputJson'''"""
pprint.log(command)
command.!!.trim
}
def addMultiSigAddress(n: Int, keys: String*) : MultiSignatureAddressInfo = {
val keysJson = upickle.default.write(keys)
val result = s"$bitcoinClient -named addmultisigaddress nrequired=$n keys='''$keysJson'''".!!.trim
upickle.default.read[MultiSignatureAddressInfo](result)
}
def decodePsbt(psbt: String) =
s"$bitcoinClient decodepsbt $psbt".!!.trim
def analyzePsbt(psbt: String) =
s"$bitcoinClient analyzepsbt $psbt".!!.trim
def combinePsbt(psbts: String*) = {
val s = upickle.default.write(psbts)
s"$bitcoinClient combinepsbt '''$s''''".!!.trim
}
def sendRawTransaction(transaction: String) = s"$bitcoinClient -named sendrawtransaction hexstring=$transaction".!!.trim
def finalizePsbt(psbt: String) : String = {
val command = s"$bitcoinClient finalizepsbt $psbt"
val result = command.!!.trim
val j = ujson.read(result)
pprint.log(j)
j("hex").str
}
def processPsbt(psbt: String): String = {
val r = s"$bitcoinClient walletprocesspsbt $psbt".!!.trim
val j = ujson.read(r)
j("psbt").str
}
case class MultiSignatureAddressInfo(address: String, redeemScript: String, descriptor: String)
implicit val readMultiSigResult : upickle.default.ReadWriter[MultiSignatureAddressInfo] = upickle.default.macroRW
// todo: address_type: p2sh-segwit, legacy, or bech32
def createMultiSig(nRequired: Int, publicKeys: String*): MultiSignatureAddressInfo = {
val keyString = upickle.default.write(publicKeys)
// todo check scientific notation ok
val result = s"""$bitcoinClient -named createmultisig nrequired=$nRequired keys='''$keyString''' address_type=bech32""".!!.trim
pprint.log(result)
upickle.default.read[MultiSignatureAddressInfo](result)
}
// todo: ensure can spend the funds sent to address
def _testCreateMultiSig() = {
val address1 = getNewAddress
val address2 = getNewAddress
val pk1 = publicKey(address1)
val pk2 = publicKey(address2)
pprint.log(s"address1 $address1")
pprint.log(s"address2 = $address2")
pprint.log(s"Creating 2-2 multisig")
val ms = createMultiSig(2, pk1, pk2)
pprint.log(s"multi sig = ${ms.address}")
ms.address
}
def _testAddMultiSig() = {
val address1 = getNewAddress
val address2 = getNewAddress
val pk1 = publicKey(address1)
val pk2 = publicKey(address2)
pprint.log(s"address1 $address1")
pprint.log(s"address2 = $address2")
pprint.log(s"Creating 2-2 multisig")
val ms = addMultiSigAddress(2, pk1, pk2)
pprint.log(s"multi sig = ${ms.address}")
pprint.log(s"Send 0.001 btc to $ms???")
val shouldSend = StdIn.readBoolean()
if (shouldSend) {
val tx = sendToAddress(ms.address, 0.001)
pprint.log(s"tx = $tx")
}
else pprint.log("Done.")
}
def spendMultiSigWithPsbt(txid: String, vOut: Int, sendToAddress: String) = {
val input = PsbtInput(txid, vOut)
val outputs = Map(sendToAddress -> 0.00099000)
val psbt = createPtsd(Seq(input), outputs)
pprint.log(s"Created psbt $psbt")
pprint.log(analyzePsbt(psbt))
val p1 = processPsbt(psbt)
pprint.log(s"Processed psbt $p1")
pprint.log(analyzePsbt(p1))
val p2 = processPsbt(p1)
pprint.log(s"Processed psbt $p2")
pprint.log(analyzePsbt(p2))
val txId = finalizePsbt(p2)
pprint.log(s"Finalized psbt $txId")
pprint.log(s"Broadcast transaction to network?")
val shouldSend = StdIn.readBoolean()
if (shouldSend) {
pprint.log(s"Broadcasting tx $txId")
val r = sendRawTransaction(txId)
pprint.log(s"Done $r")
}
}
//_testAddMultiSig()
//val ms = _testCreateMultiSig()
//val txId = sendToAddress(ms, 0.001)
// wait for confirmation...
//val a = getNewAddress
//pprint.log(s"Send to $a")
//spendMultiSigWithPsbt(txId, 1, a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment