Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 54 additions & 19 deletions src/script/interface_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ pub fn op_cat(stack: &mut Stack) -> bool {
error_item_size(op);
return false;
}
let cat = [s1, s2].join("");
let cat = [s1, s2].concat();
stack.push(StackEntry::Bytes(cat))
}

Expand Down Expand Up @@ -648,6 +648,10 @@ pub fn op_substr(stack: &mut Stack) -> bool {
return false;
}
};
// TODO: As this was previously a hex string, the indices don't exactly correspond to what
// they did originally. However, I don't think there are any existing transactions
// on the chain which actually use this opcode, so I'm fairly confident it won't
// matter. Double-check that this is the case before merging!
if n1 >= s.len() {
error_item_index(op);
return false;
Expand All @@ -660,7 +664,7 @@ pub fn op_substr(stack: &mut Stack) -> bool {
error_item_index(op);
return false;
}
let substr = s[n1..n1 + n2].to_string();
let substr = s[n1..n1 + n2].to_vec();
stack.push(StackEntry::Bytes(substr))
}

Expand Down Expand Up @@ -700,7 +704,11 @@ pub fn op_left(stack: &mut Stack) -> bool {
if n >= s.len() {
stack.push(StackEntry::Bytes(s))
} else {
let left = s[..n].to_string();
// TODO: As this was previously a hex string, the indices don't exactly correspond to what
// they did originally. However, I don't think there are any existing transactions
// on the chain which actually use this opcode, so I'm fairly confident it won't
// matter. Double-check that this is the case before merging!
let left = s[..n].to_vec();
stack.push(StackEntry::Bytes(left))
}
}
Expand Down Expand Up @@ -739,9 +747,13 @@ pub fn op_right(stack: &mut Stack) -> bool {
}
};
if n >= s.len() {
stack.push(StackEntry::Bytes("".to_string()))
stack.push(StackEntry::Bytes(Vec::new()))
} else {
let right = s[n..].to_string();
// TODO: As this was previously a hex string, the indices don't exactly correspond to what
// they did originally. However, I don't think there are any existing transactions
// on the chain which actually use this opcode, so I'm fairly confident it won't
// matter. Double-check that this is the case before merging!
let right = s[n..].to_vec();
stack.push(StackEntry::Bytes(right))
}
}
Expand All @@ -756,8 +768,8 @@ pub fn op_right(stack: &mut Stack) -> bool {
pub fn op_size(stack: &mut Stack) -> bool {
let (op, desc) = (OPSIZE, OPSIZE_DESC);
trace(op, desc);
let s = match stack.last() {
Some(StackEntry::Bytes(s)) => s,
let len = match stack.last() {
Some(StackEntry::Bytes(s)) => s.len(),
Some(_) => {
error_item_type(op);
return false;
Expand All @@ -767,7 +779,11 @@ pub fn op_size(stack: &mut Stack) -> bool {
return false;
}
};
stack.push(StackEntry::Num(s.len()))
// TODO: As this was previously a hex string, the length doesn't exactly correspond to what
// it did originally. However, I don't think there are any existing transactions
// on the chain which actually use this opcode, so I'm fairly confident it won't
// matter. Double-check that this is the case before merging!
stack.push(StackEntry::Num(len))
}

/*---- BITWISE LOGIC OPS ----*/
Expand Down Expand Up @@ -1924,7 +1940,11 @@ pub fn op_sha3(stack: &mut Stack) -> bool {
let data = match stack.pop() {
Some(StackEntry::Signature(sig)) => sig.as_ref().to_owned(),
Some(StackEntry::PubKey(pk)) => pk.as_ref().to_owned(),
Some(StackEntry::Bytes(s)) => s.as_bytes().to_owned(),
Some(StackEntry::Bytes(s)) => {
// For legacy reasons, the hashed data is the hex representation of the data rather than
// the data itself.
hex::encode(&s).as_bytes().to_owned()
},
Some(_) => {
error_item_type(op);
return false;
Expand All @@ -1934,7 +1954,8 @@ pub fn op_sha3(stack: &mut Stack) -> bool {
return false;
}
};
let hash = hex::encode(sha3_256::digest(&data));
let hash = sha3_256::digest(&data).to_vec();
// TODO: Originally, the hash was converted back to hex!
stack.push(StackEntry::Bytes(hash))
}

Expand All @@ -1960,7 +1981,7 @@ pub fn op_hash256(stack: &mut Stack) -> bool {
}
};
let addr = construct_address(&pk);
stack.push(StackEntry::Bytes(addr))
stack.push(StackEntry::Bytes(hex::decode(addr).unwrap()))
}

/// OP_HASH256_V0: Creates v0 address from public key and pushes it onto the stack
Expand Down Expand Up @@ -1989,7 +2010,7 @@ pub fn op_hash256_v0(stack: &mut Stack) -> bool {
}
};
let addr_v0 = construct_address_v0(&pk);
stack.push(StackEntry::Bytes(addr_v0))
stack.push(StackEntry::Bytes(hex::decode(addr_v0).unwrap()))
}

/// OP_HASH256_TEMP: Creates temporary address from public key and pushes it onto the stack
Expand Down Expand Up @@ -2018,7 +2039,7 @@ pub fn op_hash256_temp(stack: &mut Stack) -> bool {
}
};
let addr_temp = construct_address_temp(&pk);
stack.push(StackEntry::Bytes(addr_temp))
stack.push(StackEntry::Bytes(hex::decode(addr_temp).unwrap()))
}

/// OP_CHECKSIG: Pushes ONE onto the stack if the signature is valid, ZERO otherwise
Expand Down Expand Up @@ -2067,8 +2088,13 @@ pub fn op_checksig(stack: &mut Stack) -> bool {
return false;
}
};
trace!("Signature: {:?}", hex::encode(sig));
if (!sign::verify_detached(&sig, msg.as_bytes(), &pk)) {

// For legacy reasons, the signed message is the hex representation of the message rather than
// the message itself.
let msg_hex = hex::encode(msg);

trace!("Signature: {:?}", msg_hex);
if (!sign::verify_detached(&sig, msg_hex.as_bytes(), &pk)) {
trace!("Signature verification failed");
stack.push(StackEntry::Num(ZERO))
} else {
Expand Down Expand Up @@ -2121,8 +2147,13 @@ pub fn op_checksigverify(stack: &mut Stack) -> bool {
return false;
}
};
trace!("Signature: {:?}", hex::encode(sig));
if (!sign::verify_detached(&sig, msg.as_bytes(), &pk)) {

// For legacy reasons, the signed message is the hex representation of the message rather than
// the message itself.
let msg_hex = hex::encode(msg);

trace!("Signature: {:?}", msg_hex);
if (!sign::verify_detached(&sig, msg_hex.as_bytes(), &pk)) {
trace!("Signature verification failed");
error_invalid_signature(op);
return false;
Expand Down Expand Up @@ -2298,13 +2329,17 @@ pub fn op_checkmultisigverify(stack: &mut Stack) -> bool {
/// * `sigs` - signatures to verify
/// * `msg` - data to verify against
/// * `pks` - public keys to match against
fn verify_multisig(sigs: &[Signature], msg: &String, pks: &mut Vec<PublicKey>) -> bool {
fn verify_multisig(sigs: &[Signature], msg: &[u8], pks: &mut Vec<PublicKey>) -> bool {
// For legacy reasons, the signed message is the hex representation of the message rather than
// the message itself.
let msg_hex = hex::encode(msg);

let mut num_valid_sigs = ZERO;
for sig in sigs {
if let Some((index, _)) = pks
.iter()
.enumerate()
.find(|(_, pk)| sign::verify_detached(sig, msg.as_bytes(), pk))
.find(|(_, pk)| sign::verify_detached(sig, msg_hex.as_bytes(), pk))
{
num_valid_sigs += ONE;
pks.remove(index);
Expand Down
16 changes: 8 additions & 8 deletions src/script/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::utils::error_utils::*;
use crate::utils::transaction_utils::{construct_address, construct_address_for};
use bincode::serialize;
use bytes::Bytes;
use hex::encode;
use serde::{Deserialize, Serialize};
use tracing::{error, warn};

Expand Down Expand Up @@ -375,7 +374,7 @@ impl Script {
StackEntry::Op(OpCodes::OP_CREATE),
StackEntry::Num(block_number as usize),
StackEntry::Op(OpCodes::OP_DROP),
StackEntry::Bytes(asset_hash),
StackEntry::Bytes(hex::decode(asset_hash).expect("asset_hash contains non-hex characters")),
StackEntry::Signature(signature),
StackEntry::PubKey(pub_key),
StackEntry::Op(OpCodes::OP_CHECKSIG),
Expand All @@ -391,7 +390,7 @@ impl Script {
/// * `signature` - Signature of check data
/// * `pub_key` - Public key of the payer
pub fn pay2pkh(
check_data: String,
check_data: Vec<u8>,
signature: Signature,
pub_key: PublicKey,
address_version: Option<u64>,
Expand All @@ -407,7 +406,8 @@ impl Script {
StackEntry::PubKey(pub_key),
StackEntry::Op(OpCodes::OP_DUP),
StackEntry::Op(op_hash_256),
StackEntry::Bytes(construct_address_for(&pub_key, address_version)),
StackEntry::Bytes(hex::decode(construct_address_for(&pub_key, address_version))
.expect("address contains non-hex characters?")),
StackEntry::Op(OpCodes::OP_EQUALVERIFY),
StackEntry::Op(OpCodes::OP_CHECKSIG),
];
Expand All @@ -421,7 +421,7 @@ impl Script {
/// * `check_data` - Data to be signed for verification
/// * `pub_key` - Public key of this party
/// * `signature` - Signature of this party
pub fn member_multisig(check_data: String, pub_key: PublicKey, signature: Signature) -> Self {
pub fn member_multisig(check_data: Vec<u8>, pub_key: PublicKey, signature: Signature) -> Self {
let stack = vec![
StackEntry::Bytes(check_data),
StackEntry::Signature(signature),
Expand All @@ -439,7 +439,7 @@ impl Script {
/// * `n` - Number of valid signatures total
/// * `check_data` - Data to have checked against signatures
/// * `pub_keys` - The constituent public keys
pub fn multisig_lock(m: usize, n: usize, check_data: String, pub_keys: Vec<PublicKey>) -> Self {
pub fn multisig_lock(m: usize, n: usize, check_data: Vec<u8>, pub_keys: Vec<PublicKey>) -> Self {
let mut stack = vec![StackEntry::Bytes(check_data), StackEntry::Num(m)];
stack.append(&mut pub_keys.iter().map(|e| StackEntry::PubKey(*e)).collect());
stack.push(StackEntry::Num(n));
Expand All @@ -453,7 +453,7 @@ impl Script {
///
/// * `check_data` - Data to have signed
/// * `signatures` - Signatures to unlock with
pub fn multisig_unlock(check_data: String, signatures: Vec<Signature>) -> Self {
pub fn multisig_unlock(check_data: Vec<u8>, signatures: Vec<Signature>) -> Self {
let mut stack = vec![StackEntry::Bytes(check_data)];
stack.append(
&mut signatures
Expand All @@ -475,7 +475,7 @@ impl Script {
pub fn multisig_validation(
m: usize,
n: usize,
check_data: String,
check_data: Vec<u8>,
signatures: Vec<Signature>,
pub_keys: Vec<PublicKey>,
) -> Self {
Expand Down
3 changes: 2 additions & 1 deletion src/script/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ pub enum StackEntry {
Op(OpCodes),
Signature(Signature),
PubKey(PublicKey),
// TODO: This should probably be u64, as usize doesn't have a consistent range on all platforms
Num(usize),
Bytes(String),
Bytes(Vec<u8>),
}

/// Opcodes enum
Expand Down
102 changes: 102 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,105 @@ pub fn add_btreemap<E: Ord, T: Copy + std::ops::AddAssign>(
});
m1
}

/// A trait which indicates that it is possible to acquire a "placeholder" value
/// of a type, which can be used for test purposes.
#[cfg(test)]
pub trait Placeholder : Sized {
/// Gets a placeholder value of this type which can be used for test purposes.
fn placeholder() -> Self;

/// Gets an array of placeholder values of this type which can be used for test purposes.
fn placeholder_array<const N: usize>() -> [Self; N] {
core::array::from_fn(|_| Self::placeholder())
}
}

/// A trait which indicates that it is possible to acquire a "placeholder" value
/// of a type, which can be used for test purposes. These placeholder values are consistent
/// across program runs.
#[cfg(test)]
pub trait PlaceholderSeed: Sized + PartialEq {
/// Gets a dummy valid of this type which can be used for test purposes.
///
/// This allows acquiring multiple distinct placeholder values which are still consistent
/// between runs.
///
/// ### Arguments
///
/// * `seed_parts` - the parts of the seed for the placeholder value to obtain. Two placeholder
/// values generated from the same seed are guaranteed to be equal (even
/// across multiple test runs, so long as the value format doesn't change).
fn placeholder_seed_parts<'a>(seed_parts: impl IntoIterator<Item = &'a [u8]>) -> Self;

/// Gets a dummy valid of this type which can be used for test purposes.
///
/// This allows acquiring multiple distinct placeholder values which are still consistent
/// between runs.
///
/// ### Arguments
///
/// * `seed` - the seed for the placeholder value to obtain. Two placeholder
/// values generated from the same seed are guaranteed to be equal (even
/// across multiple test runs, so long as the value format doesn't change).
fn placeholder_seed(seed: impl AsRef<[u8]>) -> Self {
Self::placeholder_seed_parts([ seed.as_ref() ])
}

/// Gets a dummy valid of this type which can be used for test purposes.
///
/// This allows acquiring multiple distinct placeholder values which are still consistent
/// between runs.
///
/// ### Arguments
///
/// * `index` - the index of the placeholder value to obtain. Two placeholder values generated
/// from the same index are guaranteed to be equal (even across multiple test runs,
/// so long as the value format doesn't change).
fn placeholder_indexed(index: u64) -> Self {
Self::placeholder_seed_parts([ index.to_le_bytes().as_slice() ])
}

/// Gets an array of placeholder values of this type which can be used for test purposes.
fn placeholder_array_seed<const N: usize>(seed: impl AsRef<[u8]>) -> [Self; N] {
core::array::from_fn(|n| Self::placeholder_seed_parts(
[ seed.as_ref(), &(n as u64).to_le_bytes() ]
))
}

/// Gets an array of placeholder values of this type which can be used for test purposes.
fn placeholder_array_indexed<const N: usize>(base_index: u64) -> [Self; N] {
Self::placeholder_array_seed(base_index.to_le_bytes())
}
}

#[cfg(test)]
impl<T: PlaceholderSeed> Placeholder for T {
fn placeholder() -> Self {
<Self as PlaceholderSeed>::placeholder_seed_parts([])
}
}

/// Generates the given number of pseudorandom bytes based on the given seed.
///
/// This is intended to be used in tests, where random but reproducible placeholder values are often
/// required.
///
/// ### Arguments
///
/// * `seed_parts` - the parts of the seed, which will be concatenated to form the RNG seed
#[cfg(test)]
pub fn placeholder_bytes<'a, const N: usize>(
seed_parts: impl IntoIterator<Item = &'a [u8]>
) -> [u8; N] {
// Use Shake-256 to generate an arbitrarily large number of random bytes based on the given seed.
let mut shake256 = sha3::Shake256::default();
for slice in seed_parts {
sha3::digest::Update::update(&mut shake256, slice);
}
let mut reader = sha3::digest::ExtendableOutput::finalize_xof(shake256);

let mut res = [0u8; N];
sha3::digest::XofReader::read(&mut reader, &mut res);
res
}
Loading