Skip to content

Commit

Permalink
Ignore errors in invalid scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Mar 28, 2024
1 parent 4b5f573 commit 055da58
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 15 deletions.
5 changes: 4 additions & 1 deletion src/index/updater/rune_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,10 @@ impl<'a, 'tx, 'client> RuneUpdater<'a, 'tx, 'client> {
};

for instruction in tapscript.instructions() {
let instruction = instruction?;
// ignore errors, since the extracted script may not be valid
let Ok(instruction) = instruction else {
break;
};

let Some(pushbytes) = instruction.push_bytes() else {
continue;
Expand Down
35 changes: 35 additions & 0 deletions src/runes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5392,4 +5392,39 @@ mod tests {

context.assert_runes([], []);
}

#[test]
fn tx_commits_to_rune_ignores_invalid_script() {
let context = Context::builder().arg("--index-runes").build();

context.mine_blocks(1);

let runestone = Runestone {
etching: Some(Etching {
rune: Some(Rune(RUNE)),
terms: Some(Terms {
amount: Some(1000),
..default()
}),
..default()
}),
..default()
};

let mut witness = Witness::new();

witness.push(&[opcodes::all::OP_PUSHDATA4.to_u8()]);
witness.push(&[]);

context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, witness)],
op_return: Some(runestone.encipher()),
outputs: 1,
..default()
});

context.mine_blocks(1);

context.assert_runes([], []);
}
}
76 changes: 62 additions & 14 deletions src/runes/runestone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct Message {
fields: HashMap<u128, VecDeque<u128>>,
}

#[derive(Debug, PartialEq)]
enum Payload {
Valid(Vec<u8>),
Invalid,
Expand Down Expand Up @@ -264,16 +265,17 @@ impl Runestone {
continue;
}

// followed by the protocol identifier
if instructions.next().transpose()? != Some(Instruction::Op(MAGIC_NUMBER)) {
// followed by the protocol identifier, ignoring errors, since OP_RETURN
// scripts may be invalid
if instructions.next().transpose().ok().flatten() != Some(Instruction::Op(MAGIC_NUMBER)) {
continue;
}

// construct the payload by concatinating remaining data pushes
let mut payload = Vec::new();

for result in instructions {
if let Instruction::PushBytes(push) = result? {
if let Ok(Instruction::PushBytes(push)) = result {
payload.extend_from_slice(push.as_bytes());
} else {
return Ok(Some(Payload::Invalid));
Expand Down Expand Up @@ -433,7 +435,7 @@ mod tests {
}

#[test]
fn deciphering_valid_runestone_with_invalid_script_postfix_returns_script_error() {
fn deciphering_valid_runestone_with_invalid_script_postfix_returns_invalid_payload() {
let mut script_pubkey = script::Builder::new()
.push_opcode(opcodes::all::OP_RETURN)
.push_opcode(MAGIC_NUMBER)
Expand All @@ -442,16 +444,18 @@ mod tests {

script_pubkey.push(opcodes::all::OP_PUSHBYTES_4.to_u8());

Runestone::decipher(&Transaction {
input: Vec::new(),
output: vec![TxOut {
script_pubkey: ScriptBuf::from_bytes(script_pubkey),
value: 0,
}],
lock_time: LockTime::ZERO,
version: 2,
})
.unwrap_err();
assert_eq!(
Runestone::payload(&Transaction {
input: Vec::new(),
output: vec![TxOut {
script_pubkey: ScriptBuf::from_bytes(script_pubkey),
value: 0,
}],
lock_time: LockTime::ZERO,
version: 2,
}),
Ok(Some(Payload::Invalid))
);
}

#[test]
Expand Down Expand Up @@ -1955,4 +1959,48 @@ mod tests {
.cenotaph
);
}

#[test]
fn invalid_scripts_in_op_returns_are_ignored() {
let transaction = Transaction {
version: 2,
lock_time: LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint::null(),
script_sig: ScriptBuf::new(),
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
witness: Witness::new(),
}],
output: vec![TxOut {
script_pubkey: ScriptBuf::from(vec![
opcodes::all::OP_RETURN.to_u8(),
opcodes::all::OP_PUSHBYTES_4.to_u8(),
]),
value: 0,
}],
};

Runestone::decipher(&transaction).unwrap();

let transaction = Transaction {
version: 2,
lock_time: LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint::null(),
script_sig: ScriptBuf::new(),
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
witness: Witness::new(),
}],
output: vec![TxOut {
script_pubkey: ScriptBuf::from(vec![
opcodes::all::OP_RETURN.to_u8(),
MAGIC_NUMBER.to_u8(),
opcodes::all::OP_PUSHBYTES_4.to_u8(),
]),
value: 0,
}],
};

Runestone::decipher(&transaction).unwrap();
}
}

0 comments on commit 055da58

Please sign in to comment.