Skip to content

Output Script Descriptor

shigeyuki azuchi edited this page Jul 9, 2024 · 7 revisions

Bitcoin::Descriptor module

Include Bitcoin::Descriptor module provides methods for working with descriptors. Each descriptor has a to_script method that returns the corresponding output script object (Bitcoin::Script) and a to_hex method that returns its hex value.

Please refer to each BIP for the specifications of each descriptor (BIP-380, BIP-381, BIP-382, BIP-383, BIP-384, BIP-385, BIP-386, BIP-387).

pk()

include Bitcoin::Descriptor

desc = pk("L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1")

puts desc.to_script
=> '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd OP_CHECKSIG'
puts desc.to_hex
=> '2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac'

pkh()

include Bitcoin::Descriptor

desc = pkh("[deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1")

puts desc.to_script
=> 'OP_DUP OP_HASH160 9a1c78a507689f6f54b847ad1cef1e614ee23f1e OP_EQUALVERIFY OP_CHECKSIG'
puts desc.to_hex
=> '76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac'

sh()

include Bitcoin::Descriptor

desc = sh(pk('L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1'))

puts desc.to_script
=> 'OP_HASH160 1857af51a5e516552b3086430fd8ce55f7c1a524 OP_EQUAL'
puts desc.to_hex
=> 'a9141857af51a5e516552b3086430fd8ce55f7c1a52487'

wpkh()

include Bitcoin::Descriptor

desc = wpkh('03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd')

puts desc.to_script
=> '0 9a1c78a507689f6f54b847ad1cef1e614ee23f1e'
puts desc.to_hex
=> '00149a1c78a507689f6f54b847ad1cef1e614ee23f1e'

wsh()

include Bitcoin::Descriptor

desc = wsh(pk('L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1'))

puts desc.to_script
=> '0 2e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa'
puts desc.to_hex
=> '00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa'

multi()

include Bitcoin::Descriptor

desc = multi(1, 'L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1', '5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss')

puts desc.to_script
=> '1 03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd 04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235 2 OP_CHECKMULTISIG'
puts desc.to_hex
=> '512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae'

sortedmulti()

include Bitcoin::Descriptor

desc = sortedmulti(1, '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd', '04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235')

puts desc.to_script
=> '1 03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd 04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235 2 OP_CHECKMULTISIG'
puts desc.to_hex
=> '512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae'

combo()

Only the combo() descriptor returns multiple outputs, so you should use to_scripts method instead of to_script to output an array of Bitcoin::Script.

include Bitcoin::Descriptor

desc = combo('[01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc')

puts desc.to_scripts
=> '02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0 OP_CHECKSIG
OP_DUP OP_HASH160 31a507b815593dfc51ffc7245ae7e5aee304246e OP_EQUALVERIFY OP_CHECKSIG
0 31a507b815593dfc51ffc7245ae7e5aee304246e
OP_HASH160 2aafb926eb247cb18240a7f4c07983ad1f379226 OP_EQUAL'

raw()

include Bitcoin::Descriptor

desc = raw('a9141857af51a5e516552b3086430fd8ce55f7c1a52487')

puts desc.to_script
=> 'OP_HASH160 1857af51a5e516552b3086430fd8ce55f7c1a524 OP_EQUAL'
puts desc.to_hex
=> 'a9141857af51a5e516552b3086430fd8ce55f7c1a52487'

addr()

include Bitcoin::Descriptor

desc = addr('3PUNyaW7M55oKWJ3kDukwk9bsKvryra15j')

puts desc.to_script
=> 'OP_HASH160 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee OP_EQUAL'
puts desc.to_hex
=> 'a914eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee87'

tr()

tr() descriptor takes an optional script tree as a second argument. When processing as a string with the parse method described below, {} is used to represent the tree structure, but when writing in code, a nested array is used instead.

include Bitcoin::Descriptor

# Take only key arg
desc = tr('a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd')

puts desc.to_script
=> '1 77aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11'
puts desc.to_hex
=> '512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11'

# Take key and tree args
desc = desc = tr('a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd',
                [
                  pk('xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0'),
                  [
                    [
                      pk('xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL'),
                      pk('02df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076')
                    ],
                    pk('L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1')
                  ]
                ])

puts desc.to_script
=> '1 71fff39599a7b78bc02623cbe814efebf1a404f5d8ad34ea80f213bd8943f574'
puts desc.to_hex
=> '512071fff39599a7b78bc02623cbe814efebf1a404f5d8ad34ea80f213bd8943f574'

multi_a()

multi_a and sortedmulti_a are the same as multi and sortedmulti, except that the script they generate is a multisig for Tapscript. Therefore, multi_a and sortedmulti_a can only be used in tr() descriptor.

include Bitcoin::Descriptor

desc = desc = tr('L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1',
                multi_a(1, 'KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy'))

puts desc.to_script
=> '1 eb5bd3894327d75093891cc3a62506df7d58ec137fcd104cdd285d67816074f3'
puts desc.to_hex
=> '5120eb5bd3894327d75093891cc3a62506df7d58ec137fcd104cdd285d67816074f3'

sortedmulti_a()

include Bitcoin::Descriptor

desc = tr('50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0', 
                sortedmulti_a(2,
                              "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0", 
                              "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/0"))

puts desc.to_script
=> ''
puts desc.to_hex
=> ''

Parse the descriptor string

The Bitcoin::Descriptor#parse method parses a string into a descriptor and generates the corresponding descriptor. If a checksum is provided, it will also be verified.

desc = Bitcoin::Descriptor.parse("wpkh([00aabbcc/0]033d65a099daf8d973422e75f78c29504e5e53bfb81f3b08d9bb161cdfb3c3ee9a)#g6gm8u7v")
# in this case, desc is Bitcoin::Descriptor::Wpkh

Output descriptor as string

Calling the to_s method on each descriptor class will output the descriptor as a string. If the checksum argument is set to true, a checksum is added.

include Bitcoin::Descriptor

desc = pk("L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1")

puts desc.to_s
=> 'pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)'
puts desc.to_s(checksum: true)
=> 'pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)#tp6f86mw'