Unable to push script bytes into a Script builder
Unable to push script bytes into a Script builder
Loading saved threads...
jiamijiang · External communityPost link
External question — Bitcoin Stack Exchange
Author: jiamijiang
Original post: https://bitcoin.stackexchange.com/questions/130649
License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/
Adaptation: HTML converted to plain text; contact email addresses removed.
I am using rust-bitcoin to spend a P2SH. I want to construct the scriptSig by pushing a signature and the redeem script. The redeem script is just a 1-of-1 P2MS script.
let redeem_script = Script::builder()
.push_opcode(OP_PUSHNUM_1)
.push_key(&pubkey1)
.push_opcode(OP_PUSHNUM_1)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
...
let mut scriptSig = ScriptBuf::builder();
scriptSig.push_opcode(OP_0);
scriptSig.push_slice(&signature1.serialize());
scriptSig.push_slice(&redeem_script.as_bytes()); // type error here
There is a type error that i get on the
.push_slice(&redeem_script.as_bytes())
segment because the
push_slice
method expects a type that implements
AsRef<PushBytes>
, but the provided type
&[u8]
does not satisfy this trait bound.
How would I convert the
redeem_script
to satisfy the
AsRef<PushBytes>
param type?
Quote
Report
vincenzopalazzo · External communityPost link
External answer — Bitcoin Stack Exchange
Author: vincenzopalazzo
Original post: https://bitcoin.stackexchange.com/a/130650
License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/
Adaptation: HTML converted to plain text; contact email addresses removed.
The issue is with the push_slice API in recent versions of rust-bitcoin (≥ 0.30).
ScriptBuf::builder().push_slice
(and
Builder::push_slice
) now requires
T: AsRef<PushBytes>
for safety—it enforces Bitcoin’s push-data limits at compile time where possible and prevents oversized pushes.
&[u8]
(what
redeem_script.as_bytes()
returns) no longer satisfies that bound directly.
Fix
Convert the redeem script bytes to
PushBytesBuf
(the owned version) using
TryFrom
:
use bitcoin::script::{Builder, PushBytesBuf, ScriptBuf}; // or the full path: bitcoin::blockdata::script::*
// ... your redeem_script construction (this part is already correct) ...
let redeem_script = Script::builder()
.push_opcode(OP_PUSHNUM_1)
.push_key(&pubkey1)
.push_opcode(OP_PUSHNUM_1)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
// Build the scriptSig for the P2SH spend (1-of-1 multisig redeem script)
let mut script_sig = Builder::new()
.push_opcode(OP_0) // dummy 0 for CHECKMULTISIG
.push_slice(&signature1.serialize()) // signature (already a valid push)
.push_slice(
PushBytesBuf::try_from(redeem_script.as_bytes())
.expect("redeem script too large to push") // will never fail for normal multisig
)
.into_script();
tx.input[0].script_sig = script_sig; // or however you're attaching it
Why this works
PushBytesBuf::try_from(&[u8])
(or
&PushBytes::try_from(&[u8])
if you prefer a reference) validates the length and gives you a type that implements
AsRef<PushBytes>
.
For a 1-of-1 P2MS redeem script the size is tiny (~36 bytes), so the
expect
/
unwrap
is safe. In production you can handle the
PushBytesError
if you want to be extra defensive.
The resulting
script_sig
will be a valid P2SH unlocking script:
<0> <sig> <redeemScript>
(all pushes).
Alternative one-liners (if you prefer)
.push_slice(PushBytesBuf::from(redeem_script.as_bytes())) // panics on >4 GiB (impossible)
or
.push_slice(redeem_script.as_bytes().try_into().unwrap())
(using the
TryInto
impl that
PushBytesBuf
provides).
This is the idiomatic way in current
rust-bitcoin
. Your redeem script builder and overall P2SH flow look correct—only the final push needed the type adjustment.
Quote
Report
Post Reply
Checking account access…