Last active
October 7, 2020 14:35
-
-
Save j-chimienti/c43a234fc4de9b866e0ccde3f1937ccf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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