mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Add Taproot PSBT fields (#4420)
This commit is contained in:
parent
aed92c35af
commit
c4fd7035be
4 changed files with 412 additions and 14 deletions
|
@ -7,19 +7,16 @@ import org.bitcoins.core.number.UInt32
|
|||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.psbt.GlobalPSBTRecord.{UnsignedTransaction, Version}
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.{
|
||||
NonWitnessOrUnknownUTXO,
|
||||
WitnessUTXO
|
||||
}
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord._
|
||||
import org.bitcoins.core.psbt.PSBTGlobalKeyId.XPubKeyKeyId
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.wallet.utxo.{ConditionalPath, InputInfo}
|
||||
import org.bitcoins.crypto.{HashType, _}
|
||||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.bitcoins.testkitcore.util.TransactionTestUtil._
|
||||
import scodec.bits._
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
import scala.util._
|
||||
|
||||
class PSBTUnitTest extends BitcoinSUnitTest {
|
||||
|
||||
|
@ -549,7 +546,6 @@ class PSBTUnitTest extends BitcoinSUnitTest {
|
|||
}
|
||||
|
||||
it must "correctly add a non-witness utxo when there is a witness v0 redeem script" in {
|
||||
|
||||
val witScript = P2PKHScriptPubKey(ECPublicKey.freshPublicKey)
|
||||
val witness = P2WSHWitnessV0(witScript)
|
||||
val redeemScript = P2WSHWitnessSPKV0(witScript)
|
||||
|
@ -571,4 +567,79 @@ class PSBTUnitTest extends BitcoinSUnitTest {
|
|||
.map(_.transactionSpent)
|
||||
.contains(utxoTx))
|
||||
}
|
||||
|
||||
// -- BIP 371 tests --
|
||||
|
||||
it must "PSBT with one P2TR key only input with internal key and its derivation path" in {
|
||||
val bytes =
|
||||
hex"70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
assert(PSBT.fromBytes(psbt.bytes) == psbt)
|
||||
}
|
||||
|
||||
it must "PSBT with one P2TR key only input with internal key, its derivation path, and signature" in {
|
||||
val bytes =
|
||||
hex"70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011340bb53ec917bad9d906af1ba87181c48b86ace5aae2b53605a725ca74625631476fc6f5baedaf4f2ee0f477f36f58f3970d5b8273b7e497b97af2e3f125c97af342116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
assert(PSBT.fromBytes(bytes).bytes == bytes)
|
||||
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
|
||||
val finalizedT = psbt.finalizePSBT
|
||||
assert(finalizedT.isSuccess)
|
||||
// todo can't verify taproot yet
|
||||
// val finalized = finalizedT.get
|
||||
// assert(finalized.extractTransactionAndValidate.isSuccess)
|
||||
}
|
||||
|
||||
it must "PSBT with one P2TR key only output with internal key and its derivation path" in {
|
||||
val bytes =
|
||||
hex"70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
assert(PSBT.fromBytes(psbt.bytes) == psbt)
|
||||
}
|
||||
|
||||
it must "PSBT with one P2TR script path only input with dummy internal key, scripts, derivation paths for keys in the scripts, and merkle root" in {
|
||||
val bytes =
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
assert(PSBT.fromBytes(psbt.bytes) == psbt)
|
||||
}
|
||||
|
||||
it must "PSBT with one P2TR script path only output with dummy internal key, taproot tree, and script key derivation paths" in {
|
||||
val bytes =
|
||||
hex"70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a010000002251200a8cbdc86de1ce1c0f9caeb22d6df7ced3683fe423e05d1e402a879341d6f6f5000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2320001052050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac001066f02c02220736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02ac02c02220631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969ac01c0222044faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c4273ac210744faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c42733901f06b798b92a10ed9a9d0bbfd3af173a53b1617da3a4159ca008216cd856b2e0e772b2da75600008001000080010000800000000003000000210750929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2107631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969390118ace409889785e0ea70ceebb8e1ca892a7a78eaede0f2e296cf435961a8f4ca772b2da756000080010000800200008000000000030000002107736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02390129a5b4915090162d759afd3fe0f93fa3326056d0b4088cb933cae7826cb8d82c772b2da7560000800100008003000080000000000300000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
assert(PSBT.fromBytes(psbt.bytes) == psbt)
|
||||
}
|
||||
|
||||
it must "PSBT with one P2TR script path only input with dummy internal key, scripts, script key derivation paths, merkle root, and script path" in {
|
||||
val bytes =
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b0940bf818d9757d6ffeb538ba057fb4c1fc4e0f5ef186e765beb564791e02af5fd3d5e2551d4e34e33d86f276b82c99c79aed3f0395a081efcd2cc2c65dd7e693d7941144320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f840e1f1ab6fabfa26b236f21833719dc1d428ab768d80f91f9988d8abef47bfb863bb1f2a529f768c15f00ce34ec283cdc07e88f8428be28f6ef64043c32911811a4114fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca96f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae97040ec1f0379206461c83342285423326708ab031f0da4a253ee45aafa5b8c92034d8b605490f8cd13e00f989989b97e215faa36f12dee3693d2daccf3781c1757f66215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000"
|
||||
assert(Try(PSBT.fromBytes(bytes)).isSuccess)
|
||||
val psbt = PSBT.fromBytes(bytes)
|
||||
assert(PSBT.fromBytes(psbt.bytes) == psbt)
|
||||
}
|
||||
|
||||
it must "pass BIP 371 negative tests" in {
|
||||
val vec = Vector(
|
||||
hex"70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701172102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000000",
|
||||
hex"70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701133f173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc3475000000",
|
||||
hex"70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000",
|
||||
hex"70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000",
|
||||
hex"70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200",
|
||||
hex"70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07570000220702fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da7560000800100008000000080010000000000000000",
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000",
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000",
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093f89756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd430000",
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000",
|
||||
hex"70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000"
|
||||
)
|
||||
|
||||
assert(vec.forall(b => Try(PSBT.fromBytes(b)).isFailure))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,12 @@ object PSBTInputKeyId extends PSBTKeyIdFactory[PSBTInputKeyId] {
|
|||
case SHA256PreImageKeyId.byte => SHA256PreImageKeyId
|
||||
case HASH160PreImageKeyId.byte => HASH160PreImageKeyId
|
||||
case HASH256PreImageKeyId.byte => HASH256PreImageKeyId
|
||||
case TRKeySpendSignatureKeyId.byte => TRKeySpendSignatureKeyId
|
||||
case TRScriptSpendSignatureKeyId.byte => TRScriptSpendSignatureKeyId
|
||||
case TRLeafScriptKeyId.byte => TRLeafScriptKeyId
|
||||
case TRBIP32DerivationPathKeyId.byte => TRBIP32DerivationPathKeyId
|
||||
case TRInternalKeyKeyId.byte => TRInternalKeyKeyId
|
||||
case TRMerkelRootKeyId.byte => TRMerkelRootKeyId
|
||||
case _: Byte => UnknownKeyId
|
||||
|
||||
}
|
||||
|
@ -160,6 +166,36 @@ object PSBTInputKeyId extends PSBTKeyIdFactory[PSBTInputKeyId] {
|
|||
type RecordType = InputPSBTRecord.HASH256PreImage
|
||||
}
|
||||
|
||||
final case object TRKeySpendSignatureKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x13.byteValue
|
||||
type RecordType = InputPSBTRecord.TRKeySpendSignature
|
||||
}
|
||||
|
||||
final case object TRScriptSpendSignatureKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x14.byteValue
|
||||
type RecordType = InputPSBTRecord.TRScriptSpendSignature
|
||||
}
|
||||
|
||||
final case object TRLeafScriptKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x15.byteValue
|
||||
type RecordType = InputPSBTRecord.TRLeafScript
|
||||
}
|
||||
|
||||
final case object TRBIP32DerivationPathKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x16.byteValue
|
||||
type RecordType = InputPSBTRecord.TRBIP32DerivationPath
|
||||
}
|
||||
|
||||
final case object TRInternalKeyKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x17.byteValue
|
||||
type RecordType = InputPSBTRecord.TRInternalKey
|
||||
}
|
||||
|
||||
final case object TRMerkelRootKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = 0x18.byteValue
|
||||
type RecordType = InputPSBTRecord.TRMerkelRoot
|
||||
}
|
||||
|
||||
final case object UnknownKeyId extends PSBTInputKeyId {
|
||||
override val byte: Byte = Byte.MaxValue
|
||||
type RecordType = InputPSBTRecord.Unknown
|
||||
|
@ -179,6 +215,9 @@ object PSBTOutputKeyId extends PSBTKeyIdFactory[PSBTOutputKeyId] {
|
|||
case RedeemScriptKeyId.byte => RedeemScriptKeyId
|
||||
case WitnessScriptKeyId.byte => WitnessScriptKeyId
|
||||
case BIP32DerivationPathKeyId.byte => BIP32DerivationPathKeyId
|
||||
case TRInternalKeyKeyId.byte => TRInternalKeyKeyId
|
||||
case TaprootTreeKeyId.byte => TaprootTreeKeyId
|
||||
case TRBIP32DerivationPathKeyId.byte => TRBIP32DerivationPathKeyId
|
||||
case _: Byte => UnknownKeyId
|
||||
}
|
||||
|
||||
|
@ -197,6 +236,21 @@ object PSBTOutputKeyId extends PSBTKeyIdFactory[PSBTOutputKeyId] {
|
|||
type RecordType = OutputPSBTRecord.BIP32DerivationPath
|
||||
}
|
||||
|
||||
final case object TRInternalKeyKeyId extends PSBTOutputKeyId {
|
||||
override val byte: Byte = 0x05.byteValue
|
||||
type RecordType = OutputPSBTRecord.TRInternalKey
|
||||
}
|
||||
|
||||
final case object TaprootTreeKeyId extends PSBTOutputKeyId {
|
||||
override val byte: Byte = 0x06.byteValue
|
||||
type RecordType = OutputPSBTRecord.TRInternalKey
|
||||
}
|
||||
|
||||
final case object TRBIP32DerivationPathKeyId extends PSBTOutputKeyId {
|
||||
override val byte: Byte = 0x07.byteValue
|
||||
type RecordType = OutputPSBTRecord.TRBIP32DerivationPath
|
||||
}
|
||||
|
||||
final case object UnknownKeyId extends PSBTOutputKeyId {
|
||||
override val byte: Byte = Byte.MaxValue
|
||||
type RecordType = OutputPSBTRecord.Unknown
|
||||
|
|
|
@ -195,6 +195,7 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
}
|
||||
}
|
||||
|
||||
// todo maybe rethink return type
|
||||
/** The HASH160 of each public key that could be used to sign the input,
|
||||
* if calculable. [[Sha256Hash160Digest]] is used because we won't know the
|
||||
* raw public key for P2PKH scripts
|
||||
|
@ -205,7 +206,15 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
spk: ScriptPubKey): Vector[Sha256Hash160Digest] = {
|
||||
spk match {
|
||||
case spk: TaprootScriptPubKey =>
|
||||
throw new IllegalArgumentException(s"Taproot not yet supported: $spk")
|
||||
leafScriptOpt match {
|
||||
case Some(script) =>
|
||||
missingSigsFromScript(script.script)
|
||||
case None =>
|
||||
// key spend 1 signature
|
||||
if (partialSignatures.isEmpty) {
|
||||
Vector(CryptoUtil.sha256Hash160(spk.pubKey.bytes))
|
||||
} else Vector.empty
|
||||
}
|
||||
case EmptyScriptPubKey | _: WitnessCommitment |
|
||||
_: NonStandardScriptPubKey | _: UnassignedWitnessScriptPubKey =>
|
||||
Vector.empty
|
||||
|
@ -318,6 +327,26 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
getRecords(ProofOfReservesCommitmentKeyId).headOption
|
||||
}
|
||||
|
||||
def keySpendSignatureOpt: Option[TRKeySpendSignature] = {
|
||||
getRecords(TRKeySpendSignatureKeyId).headOption
|
||||
}
|
||||
|
||||
def scriptSpendSignatureOpt: Option[TRScriptSpendSignature] = {
|
||||
getRecords(TRScriptSpendSignatureKeyId).headOption
|
||||
}
|
||||
|
||||
def leafScriptOpt: Option[TRLeafScript] = {
|
||||
getRecords(TRLeafScriptKeyId).headOption
|
||||
}
|
||||
|
||||
def taprootInternalKey: Option[TRInternalKey] = {
|
||||
getRecords(TRInternalKeyKeyId).headOption
|
||||
}
|
||||
|
||||
def taprootMerkelRoot: Option[TRMerkelRoot] = {
|
||||
getRecords(TRMerkelRootKeyId).headOption
|
||||
}
|
||||
|
||||
def getRecords(key: PSBTInputKeyId): Vector[key.RecordType] = {
|
||||
super.getRecords(key, PSBTInputKeyId)
|
||||
}
|
||||
|
@ -606,8 +635,22 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
case EmptyScriptPubKey =>
|
||||
val scriptSig = TrivialTrueScriptSignature
|
||||
Success(wipeAndAdd(scriptSig))
|
||||
case _: TaprootScriptPubKey =>
|
||||
keySpendSignatureOpt match {
|
||||
case Some(keySpendSignature) =>
|
||||
val sig = keySpendSignature.signature
|
||||
val hashType =
|
||||
sigHashTypeOpt.map(_.hashType).getOrElse(SIGHASH_DEFAULT)
|
||||
|
||||
val witnessScript = TaprootKeyPath(sig, hashType, None)
|
||||
Success(wipeAndAdd(EmptyScriptSignature, Some(witnessScript)))
|
||||
case None =>
|
||||
// todo add script spend support
|
||||
Failure(new UnsupportedOperationException(
|
||||
s"Cannot finalize the following input because no key spend signature was provided: $this"))
|
||||
}
|
||||
case _: NonStandardScriptPubKey | _: UnassignedWitnessScriptPubKey |
|
||||
_: WitnessCommitment | _: TaprootScriptPubKey =>
|
||||
_: WitnessCommitment =>
|
||||
Failure(
|
||||
new UnsupportedOperationException(
|
||||
s"$spkToSatisfy is not yet supported"))
|
||||
|
@ -948,6 +991,10 @@ case class OutputPSBTMap(elements: Vector[OutputPSBTRecord])
|
|||
getRecords(BIP32DerivationPathKeyId)
|
||||
}
|
||||
|
||||
def taprootInternalKey: Option[TRInternalKey] = {
|
||||
getRecords(TRInternalKeyKeyId).headOption
|
||||
}
|
||||
|
||||
def getRecords(key: PSBTOutputKeyId): Vector[key.RecordType] = {
|
||||
super.getRecords(key, PSBTOutputKeyId)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.core.psbt
|
|||
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.hd.BIP32Path
|
||||
import org.bitcoins.core.number.{Int32, UInt32}
|
||||
import org.bitcoins.core.number.{Int32, UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
|
@ -16,6 +16,8 @@ import org.bitcoins.core.util.BytesUtil
|
|||
import org.bitcoins.crypto.{HashType, _}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
sealed trait PSBTRecord extends NetworkElement {
|
||||
|
||||
type KeyId <: PSBTKeyId
|
||||
|
@ -321,6 +323,79 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
|
|||
override val value: ByteVector = preImage
|
||||
}
|
||||
|
||||
case class TRKeySpendSignature(signature: SchnorrDigitalSignature)
|
||||
extends InputPSBTRecord {
|
||||
override type KeyId = TRKeySpendSignatureKeyId.type
|
||||
|
||||
override val key: ByteVector = ByteVector(TRKeySpendSignatureKeyId.byte)
|
||||
override val value: ByteVector = signature.bytes
|
||||
}
|
||||
|
||||
case class TRScriptSpendSignature(
|
||||
xOnlyPubKey: XOnlyPubKey,
|
||||
leafHash: Sha256Digest,
|
||||
signature: SchnorrDigitalSignature)
|
||||
extends InputPSBTRecord {
|
||||
override type KeyId = TRScriptSpendSignatureKeyId.type
|
||||
|
||||
override val key: ByteVector = ByteVector(
|
||||
TRScriptSpendSignatureKeyId.byte) ++ xOnlyPubKey.bytes ++ leafHash.bytes
|
||||
override val value: ByteVector = signature.bytes
|
||||
}
|
||||
|
||||
case class TRLeafScript(
|
||||
controlBlock: ControlBlock,
|
||||
script: RawScriptPubKey,
|
||||
leafVersion: Byte)
|
||||
extends InputPSBTRecord {
|
||||
override type KeyId = TRLeafScriptKeyId.type
|
||||
|
||||
override val key: ByteVector = {
|
||||
ByteVector(TRLeafScriptKeyId.byte) ++ controlBlock.bytes
|
||||
}
|
||||
|
||||
override val value: ByteVector = {
|
||||
script.asmBytes ++ ByteVector.fromByte(leafVersion)
|
||||
}
|
||||
}
|
||||
|
||||
case class TRBIP32DerivationPath(
|
||||
xOnlyPubKey: XOnlyPubKey,
|
||||
hashes: Vector[Sha256Digest],
|
||||
masterFingerprint: ByteVector,
|
||||
path: BIP32Path)
|
||||
extends InputPSBTRecord {
|
||||
|
||||
override type KeyId = TRBIP32DerivationPathKeyId.type
|
||||
|
||||
override val key: ByteVector =
|
||||
ByteVector(TRBIP32DerivationPathKeyId.byte) ++ xOnlyPubKey.bytes
|
||||
|
||||
override val value: ByteVector = {
|
||||
val hashesBytes = if (hashes.isEmpty) {
|
||||
CompactSizeUInt.zero.bytes
|
||||
} else {
|
||||
CompactSizeUInt(UInt64(hashes.length)).bytes ++
|
||||
hashes.map(_.bytes).reduce(_ ++ _)
|
||||
}
|
||||
hashesBytes ++ path.foldLeft(masterFingerprint)(_ ++ _.toUInt32.bytesLE)
|
||||
}
|
||||
}
|
||||
|
||||
case class TRInternalKey(xOnlyPubKey: XOnlyPubKey) extends InputPSBTRecord {
|
||||
override type KeyId = TRInternalKeyKeyId.type
|
||||
|
||||
override val key: ByteVector = ByteVector(TRInternalKeyKeyId.byte)
|
||||
override val value: ByteVector = xOnlyPubKey.bytes
|
||||
}
|
||||
|
||||
case class TRMerkelRoot(hash: Sha256Digest) extends InputPSBTRecord {
|
||||
override type KeyId = TRMerkelRootKeyId.type
|
||||
|
||||
override val key: ByteVector = ByteVector(TRMerkelRootKeyId.byte)
|
||||
override val value: ByteVector = hash.bytes
|
||||
}
|
||||
|
||||
case class Unknown(key: ByteVector, value: ByteVector)
|
||||
extends InputPSBTRecord {
|
||||
override type KeyId = UnknownKeyId.type
|
||||
|
@ -421,6 +496,63 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
|
|||
require(record.hash.bytes == hash,
|
||||
"Received invalid HASH256PreImage, hash does not match")
|
||||
record
|
||||
case TRKeySpendSignatureKeyId =>
|
||||
require(key.size == 1,
|
||||
s"The key must only contain the 1 byte type, got: ${key.size}")
|
||||
|
||||
val sig = SchnorrDigitalSignature.fromBytes(value)
|
||||
TRKeySpendSignature(sig)
|
||||
case TRScriptSpendSignatureKeyId =>
|
||||
require(
|
||||
key.size == 65,
|
||||
s"The key must only contain the 65 bytes type, got: ${key.size}")
|
||||
|
||||
val (xOnlyPubKey, leafHash) = key.tail.splitAt(32)
|
||||
val sig = SchnorrDigitalSignature.fromBytes(value)
|
||||
|
||||
TRScriptSpendSignature(XOnlyPubKey(xOnlyPubKey),
|
||||
Sha256Digest(leafHash),
|
||||
sig)
|
||||
|
||||
case TRLeafScriptKeyId =>
|
||||
val controlBlock = ControlBlock(key.tail)
|
||||
|
||||
val script = RawScriptPubKey.fromAsmBytes(value.init)
|
||||
|
||||
TRLeafScript(controlBlock, script, value.last)
|
||||
case TRBIP32DerivationPathKeyId =>
|
||||
val pubKey = XOnlyPubKey(key.tail)
|
||||
val numHashes = CompactSizeUInt.fromBytes(value)
|
||||
val hashes = value
|
||||
.drop(numHashes.byteSize)
|
||||
.take(numHashes.num.toInt * 32)
|
||||
.grouped(32)
|
||||
.map(Sha256Digest.fromBytes)
|
||||
.toVector
|
||||
val remaining = value.drop(numHashes.byteSize + hashes.size * 32)
|
||||
val fingerprint = remaining.take(4)
|
||||
val path = BIP32Path.fromBytesLE(remaining.drop(4))
|
||||
|
||||
TRBIP32DerivationPath(xOnlyPubKey = pubKey,
|
||||
hashes = hashes,
|
||||
masterFingerprint = fingerprint,
|
||||
path = path)
|
||||
case TRInternalKeyKeyId =>
|
||||
require(key.size == 1,
|
||||
s"The key must only contain the 1 byte type, got: ${key.size}")
|
||||
|
||||
require(
|
||||
value.size == 32,
|
||||
s"The value must contain the 32 byte x-only public key, got: ${value.size}")
|
||||
TRInternalKey(XOnlyPubKey.fromBytes(value))
|
||||
case TRMerkelRootKeyId =>
|
||||
require(key.size == 1,
|
||||
s"The key must only contain the 1 byte type, got: ${key.size}")
|
||||
|
||||
require(
|
||||
value.size == 32,
|
||||
s"The value must contain the 32 byte x-only public key, got: ${value.size}")
|
||||
TRMerkelRoot(Sha256Digest(value))
|
||||
case UnknownKeyId =>
|
||||
InputPSBTRecord.Unknown(key, value)
|
||||
}
|
||||
|
@ -464,6 +596,57 @@ object OutputPSBTRecord extends Factory[OutputPSBTRecord] {
|
|||
path.foldLeft(masterFingerprint)(_ ++ _.toUInt32.bytesLE)
|
||||
}
|
||||
|
||||
case class TaprootTree(
|
||||
leafs: Vector[
|
||||
(Byte, Byte, ByteVector)
|
||||
] // todo change to TapScriptPubKey when we have a TapScriptPubKey type
|
||||
) extends OutputPSBTRecord {
|
||||
require(leafs.nonEmpty)
|
||||
override type KeyId = TaprootTreeKeyId.type
|
||||
|
||||
override val key: ByteVector =
|
||||
ByteVector(TaprootTreeKeyId.byte)
|
||||
|
||||
override val value: ByteVector = {
|
||||
leafs.foldLeft(ByteVector.empty) { (acc, leaf) =>
|
||||
val spk = leaf._3
|
||||
acc ++ ByteVector.fromByte(leaf._1) ++ ByteVector.fromByte(
|
||||
leaf._2) ++ CompactSizeUInt.calc(spk).bytes ++ spk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class TRBIP32DerivationPath(
|
||||
xOnlyPubKey: XOnlyPubKey,
|
||||
hashes: Vector[Sha256Digest],
|
||||
masterFingerprint: ByteVector,
|
||||
path: BIP32Path)
|
||||
extends OutputPSBTRecord {
|
||||
|
||||
override type KeyId = TRBIP32DerivationPathKeyId.type
|
||||
|
||||
override val key: ByteVector =
|
||||
ByteVector(TRBIP32DerivationPathKeyId.byte) ++ xOnlyPubKey.bytes
|
||||
|
||||
override val value: ByteVector = {
|
||||
val hashesBytes = if (hashes.isEmpty) {
|
||||
CompactSizeUInt.zero.bytes
|
||||
} else {
|
||||
CompactSizeUInt(UInt64(hashes.size)).bytes ++
|
||||
hashes.map(_.bytes).reduce(_ ++ _)
|
||||
}
|
||||
hashesBytes ++ path.foldLeft(masterFingerprint)(_ ++ _.toUInt32.bytesLE)
|
||||
}
|
||||
}
|
||||
|
||||
case class TRInternalKey(xOnlyPubKey: XOnlyPubKey) extends OutputPSBTRecord {
|
||||
override type KeyId = TRInternalKeyKeyId.type
|
||||
|
||||
override val key: ByteVector = ByteVector(TRInternalKeyKeyId.byte)
|
||||
|
||||
override val value: ByteVector = xOnlyPubKey.bytes
|
||||
}
|
||||
|
||||
case class Unknown(key: ByteVector, value: ByteVector)
|
||||
extends OutputPSBTRecord {
|
||||
override type KeyId = UnknownKeyId.type
|
||||
|
@ -491,6 +674,49 @@ object OutputPSBTRecord extends Factory[OutputPSBTRecord] {
|
|||
val path = BIP32Path.fromBytesLE(value.drop(4))
|
||||
|
||||
OutputPSBTRecord.BIP32DerivationPath(pubKey, fingerprint, path)
|
||||
case PSBTOutputKeyId.TRInternalKeyKeyId =>
|
||||
require(key.size == 1,
|
||||
s"The key must only contain the 1 byte type, got: ${key.size}")
|
||||
|
||||
val xOnlyPubKey = XOnlyPubKey.fromBytes(value)
|
||||
OutputPSBTRecord.TRInternalKey(xOnlyPubKey)
|
||||
case TaprootTreeKeyId =>
|
||||
@tailrec
|
||||
def loop(
|
||||
bytes: ByteVector,
|
||||
accum: Vector[(Byte, Byte, ByteVector)]): Vector[
|
||||
(Byte, Byte, ByteVector)] = {
|
||||
if (bytes.isEmpty) {
|
||||
accum
|
||||
} else {
|
||||
val depth = bytes.head
|
||||
val version = bytes.tail.head
|
||||
val spkLen = CompactSizeUInt.fromBytes(bytes.drop(2))
|
||||
val spk = bytes.drop(spkLen.byteSize + 2)
|
||||
|
||||
val remaining = bytes.drop(2 + spkLen.byteSize + spk.length)
|
||||
loop(remaining, accum :+ (depth, version, spk))
|
||||
}
|
||||
}
|
||||
val leafs = loop(value, Vector.empty)
|
||||
OutputPSBTRecord.TaprootTree(leafs)
|
||||
case PSBTOutputKeyId.TRBIP32DerivationPathKeyId =>
|
||||
val pubKey = XOnlyPubKey(key.tail)
|
||||
val numHashes = CompactSizeUInt.fromBytes(value)
|
||||
val hashes = value
|
||||
.drop(numHashes.byteSize)
|
||||
.take(numHashes.num.toInt * 32)
|
||||
.grouped(32)
|
||||
.map(Sha256Digest.fromBytes)
|
||||
.toVector
|
||||
val remaining = value.drop(numHashes.byteSize + hashes.size * 32)
|
||||
val fingerprint = remaining.take(4)
|
||||
val path = BIP32Path.fromBytesLE(remaining.drop(4))
|
||||
|
||||
OutputPSBTRecord.TRBIP32DerivationPath(xOnlyPubKey = pubKey,
|
||||
hashes = hashes,
|
||||
masterFingerprint = fingerprint,
|
||||
path = path)
|
||||
case UnknownKeyId =>
|
||||
OutputPSBTRecord.Unknown(key, value)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue