bitcoin-kmp
bitcoin-kmp copied to clipboard
How do I use Redeem Script Hex for create PSBT to unlock P2SH?
val xpub = DeterministicWallet.ExtendedPublicKey.decode(
"xpub6CEAyB6zF8bLzi1tNqg4zEfWSBBXtieA4hm3EstWsgSJCgMQS1UrfAFAZHYH1o7tfgFuNWzJhsDHg1EhoF4G5gzXUpQNkt1RzSTyFeAUv4S"
).second
println(xpub)
// สร้างธุรกรรม
val unsignedTx = Transaction(
version = 2,
txIn = listOf(
TxIn(
OutPoint(
TxId("c95039b1ce6152a20ecab1759e924c15e25f4d980673bd64c07a43d2fb501acb"),
0
),
signatureScript = listOf(),
sequence = 0xfdffffff
)
),
txOut = listOf(
TxOut(
12_000.toSatoshi(),
ByteVector("0014d85c2b71d0060b09c9886aeb815e50991dda124d")
)
),
lockTime = 1423787
)
val psbt = Psbt(unsignedTx)
// ดึงข้อมูล public key, fingerprint, และ key path จาก xpub
val publicKey = xpub.publicKey
val fingerprint = xpub.parent
val keyPath = xpub.path
// สร้าง mapping ของ public key และ key path
val pubkeyData = mapOf(
publicKey to KeyPathWithMaster(fingerprint, keyPath)
)
// ระบุ redeem script
val redeemScript = listOf(
)
// อัปเดต PSBT : https://github.com/ACINQ/bitcoin-kmp/blob/master/src%2FcommonMain%2Fkotlin%2Ffr%2Facinq%2Fbitcoin%2Fpsbt%2FPsbt.kt#L172-L172
val firstPSBT = psbt.updateNonWitnessInput(
inputTx = unsignedTx,
outputIndex = 0,
redeemScript = redeemScript,
sighashType = 1,
derivationPaths = pubkeyData
)
import fr.acinq.bitcoin.OP_CHECKLOCKTIMEVERIFY
import fr.acinq.bitcoin.OP_CHECKSIG
import fr.acinq.bitcoin.OP_DROP
import fr.acinq.bitcoin.OP_PUSHDATA
import win.notoshi.genesec.utils.ShiftTo.ByteArrayToHex
import win.notoshi.genesec.utils.ShiftTo.HexToByteArray
import win.notoshi.genesec.utils.ShiftTo.toByteArray
import java.nio.ByteBuffer
import java.nio.ByteOrder
class ScriptBuilder {
// * https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
fun TimeLock(blockNumber: Int, publicKey: ByteArray): String {
if (blockNumber < 0) {
throw IllegalArgumentException("Block number must be non-negative")
}
// กำหนดค่าเริ่มต้นสำหรับ blockNumber ในรูปแบบ LITTLE ENDIAN 8 Bytes
val LITTLE_ENDIAN: ByteBuffer =
ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putInt(blockNumber)
// ตรวจสอบและกำหนดค่าให้กับ nLockTime โดยตัด byte 0x00 ที่อยู่ด้านท้ายออก
var nLockTime: ByteArray = LITTLE_ENDIAN.array()
while (nLockTime.isNotEmpty() && nLockTime.last() == 0x00.toByte()) {
nLockTime = nLockTime.dropLast(1).toByteArray()
}
/*
* องค์ประกอบสคริปต์
*
* [ < ขนาดหมายเลข Block, หมายเลข Block รูปแบบ LITTLE ENDIAN > ]
* OP_CHECKLOCKTIMEVERIFY
* OP_DROP
* [ < ขนาดของ Public key >, < Public key > ]
* OP_CHECKSIG
*/
val stack = listOf(
OP_PUSHDATA(nLockTime).opCode.toByteArray(),
nLockTime,
OP_CHECKLOCKTIMEVERIFY.code.toByteArray(),
OP_DROP.code.toByteArray(),
OP_PUSHDATA(publicKey).opCode.toByteArray(),
publicKey,
OP_CHECKSIG.code.toByteArray()
)
return buildString {
stack.forEach { element ->
append(element.ByteArrayToHex())
}
}
}
}
fun main() {
val script = ScriptBuilder()
val publicKey = "02a1d6c523ea4baa26127a55eee14adcee1b1419407e317654682c0759f431ecaf"
val redeemScript = script.TimeLock(1423787, publicKey.HexToByteArray())
println(redeemScript)
// 03abb915b1752102a1d6c523ea4baa26127a55eee14adcee1b1419407e317654682c0759f431ecafac
// 3JFVsdsib5mrvpZmid1jdTPa2KR1fXXgpz
}