Ensure InInstruction data is properly limited

Bitcoin didn't check, assuming data was <= 80 bytes thanks to being in
OP_RETURN. An additional global check has been added.
This commit is contained in:
Luke Parker 2023-03-25 01:36:28 -04:00
parent 6a981dae6e
commit d954e67238
No known key found for this signature in database
4 changed files with 20 additions and 8 deletions

View file

@ -36,7 +36,7 @@ use bitcoin_serai::bitcoin::{
PackedLockTime, Sequence, Script, Witness, TxIn, TxOut, Address as BAddress, PackedLockTime, Sequence, Script, Witness, TxIn, TxOut, Address as BAddress,
}; };
use serai_client::coins::bitcoin::Address; use serai_client::{primitives::MAX_DATA_LEN, coins::bitcoin::Address};
use crate::{ use crate::{
coins::{ coins::{
@ -357,6 +357,7 @@ impl Coin for Bitcoin {
} else { } else {
vec![] vec![]
}; };
data.truncate(MAX_DATA_LEN.try_into().unwrap());
outputs.push(Output { kind, output, data }) outputs.push(Output { kind, output, data })
} }

View file

@ -19,7 +19,7 @@ use tokio::time::sleep;
use scale::Decode; use scale::Decode;
use serai_client::{ use serai_client::{
primitives::{Amount, WithAmount}, primitives::{MAX_DATA_LEN, Amount, WithAmount},
tokens::primitives::OutInstruction, tokens::primitives::OutInstruction,
in_instructions::primitives::{Shorthand, RefundableInInstruction}, in_instructions::primitives::{Shorthand, RefundableInInstruction},
}; };
@ -425,7 +425,17 @@ async fn run<C: Coin, D: Db, Co: Coordinator>(raw_db: D, coin: C, mut coordinato
return None; return None;
} }
let shorthand = Shorthand::decode(&mut output.data()).ok()?; let data = output.data();
if data.len() > MAX_DATA_LEN {
error!(
"data in output {} exceeded MAX_DATA_LEN ({MAX_DATA_LEN}): {}",
hex::encode(output.id()),
data.len(),
);
data = data[.. MAX_DATA_LEN];
}
let shorthand = Shorthand::decode(&mut data).ok()?;
let instruction = RefundableInInstruction::try_from(shorthand).ok()?; let instruction = RefundableInInstruction::try_from(shorthand).ok()?;
// TODO2: Set instruction.origin if not set (and handle refunds in general) // TODO2: Set instruction.origin if not set (and handle refunds in general)
Some(WithAmount { data: instruction.instruction, amount: Amount(output.amount()) }) Some(WithAmount { data: instruction.instruction, amount: Amount(output.amount()) })

View file

@ -53,8 +53,8 @@ impl TryFrom<Vec<u8>> for Address {
EncodedAddress::P2WSH(hash) => { EncodedAddress::P2WSH(hash) => {
Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() } Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() }
} }
EncodedAddress::P2TR(hash) => { EncodedAddress::P2TR(key) => {
Payload::WitnessProgram { version: WitnessVersion::V1, program: hash.to_vec() } Payload::WitnessProgram { version: WitnessVersion::V1, program: key.to_vec() }
} }
}, },
})) }))

View file

@ -27,9 +27,10 @@ pub use balance::*;
mod account; mod account;
pub use account::*; pub use account::*;
// Monero, our current longest address candidate, has a longest address of featured with payment ID // Monero, our current longest address candidate, has a longest address of featured
// 1 (enum) + 1 (flags) + 64 (two keys) + 8 (payment ID) = 74 // 1 (enum) + 1 (flags) + 64 (two keys) = 66
pub const MAX_ADDRESS_LEN: u32 = 74; // When JAMTIS arrives, it'll become 114 bytes
pub const MAX_ADDRESS_LEN: u32 = 128;
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)] #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]