Merge branch 'fix/restore-and-gate-pox-sunset' of https://github.com/stacks-network/stacks-blockchain into fix/restore-and-gate-pox-sunset

This commit is contained in:
Jude Nelson
2022-10-31 10:17:32 -04:00
17 changed files with 841 additions and 22 deletions

View File

@@ -68,6 +68,8 @@ jobs:
- tests::neon_integrations::fuzzed_median_fee_rate_estimation_test_window10
- tests::neon_integrations::use_latest_tip_integration_test
- tests::neon_integrations::test_flash_block_skip_tenure
- tests::neon_integrations::test_chainwork_first_intervals
- tests::neon_integrations::test_chainwork_partial_interval
- tests::epoch_205::test_dynamic_db_method_costs
- tests::epoch_205::transition_empty_blocks
- tests::epoch_205::test_cost_limit_switch_version205

View File

@@ -185,7 +185,7 @@ impl<'a> ArithmeticOnlyChecker<'a> {
return Err(Error::FunctionNotPermitted(function));
}
Append | Concat | AsMaxLen | ContractOf | PrincipalOf | ListCons | Print
| AsContract | ElementAt | IndexOf | Map | Filter | Fold | Slice => {
| AsContract | ElementAt | IndexOf | Map | Filter | Fold | Slice | ReplaceAt => {
return Err(Error::FunctionNotPermitted(function));
}
BuffToIntLe | BuffToUIntLe | BuffToIntBe | BuffToUIntBe => {

View File

@@ -290,6 +290,9 @@ fn test_functions_clarity1() {
("(define-private (foo (a (list 3 uint)))
(slice a u2 u3))",
Ok(())),
("(define-private (foo (a (list 3 uint)) (b uint))
(replace-at a u1 b))",
Ok(())),
("(buff-to-int-le 0x0001)",
Ok(())),
("(buff-to-uint-le 0x0001)",
@@ -350,6 +353,9 @@ fn test_functions_clarity2() {
("(define-private (foo (a (list 3 uint)))
(slice a u2 u3))",
Err(FunctionNotPermitted(NativeFunctions::Slice))),
("(define-private (foo (a (list 3 uint)))
(replace-at a u2 (list u3)))",
Err(FunctionNotPermitted(NativeFunctions::ReplaceAt))),
("(buff-to-int-le 0x0001)",
Err(FunctionNotPermitted(NativeFunctions::BuffToIntLe))),
("(buff-to-uint-le 0x0001)",

View File

@@ -286,7 +286,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
| AsMaxLen | ContractOf | PrincipalOf | ListCons | GetBlockInfo | GetBurnBlockInfo
| TupleGet | TupleMerge | Len | Print | AsContract | Begin | FetchVar
| GetStxBalance | StxGetAccount | GetTokenBalance | GetAssetOwner | GetTokenSupply
| ElementAt | IndexOf | Slice => {
| ElementAt | IndexOf | Slice | ReplaceAt => {
// Check all arguments.
self.check_each_expression_is_read_only(args)
}

View File

@@ -158,6 +158,10 @@ fn test_simple_read_only_violations() {
(define-private (func1) (begin (map-set tokens (tuple (account tx-sender)) (tuple (balance 10))) (list 1 2)))
(define-read-only (not-reading-only)
(concat (func1) (func1)))",
"(define-map tokens { account: principal } { balance: int })
(define-private (func1) (begin (map-set tokens (tuple (account tx-sender)) (tuple (balance 10))) (list 1 2)))
(define-read-only (not-reading-only)
(replace-at (func1) u0 3))",
"(define-map tokens { account: principal } { balance: int })
(define-private (func1) (begin (map-set tokens (tuple (account tx-sender)) (tuple (balance 10))) (list 1 2)))
(define-read-only (not-reading-only)

View File

@@ -850,6 +850,7 @@ impl TypedNativeFunction {
ElementAt => Special(SpecialNativeFunction(&sequences::check_special_element_at)),
IndexOf => Special(SpecialNativeFunction(&sequences::check_special_index_of)),
Slice => Special(SpecialNativeFunction(&sequences::check_special_slice)),
ReplaceAt => Special(SpecialNativeFunction(&sequences::check_special_replace_at)),
ListCons => Special(SpecialNativeFunction(&check_special_list_cons)),
FetchEntry => Special(SpecialNativeFunction(&maps::check_special_fetch_entry)),
SetEntry => Special(SpecialNativeFunction(&maps::check_special_set_entry)),

View File

@@ -423,3 +423,28 @@ pub fn check_special_slice(
Ok(seq)
}
/// This function type checks the Clarity2 function `replace-at`.
pub fn check_special_replace_at(
checker: &mut TypeChecker,
args: &[SymbolicExpression],
context: &TypingContext,
) -> TypeResult {
check_argument_count(3, args)?;
runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?;
// Check sequence
let input_type = checker.type_check(&args[0], context)?;
let seq_type = match &input_type {
TypeSignature::SequenceType(seq) => seq,
_ => return Err(CheckErrors::ExpectedSequence(input_type).into()),
};
let unit_seq = seq_type.unit_type();
// Check index argument
checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?;
// Check element argument
checker.type_check_expects(&args[2], context, &unit_seq)?;
let final_type = TypeSignature::new_option(input_type)?;
Ok(final_type)
}

View File

@@ -1455,6 +1455,229 @@ fn test_slice_utf8() {
}
}
#[test]
fn test_replace_at_list() {
let good = [
"(replace-at (list 2 3 4 5 6 7 8) u0 10)",
"(replace-at (list u0 u1 u2 u3 u4) u3 u10)",
"(replace-at (list true) u0 false)",
"(replace-at (list 2 3 4 5 6 7 8) u6 10)",
"(replace-at (list (list 1) (list 2)) u0 (list 33))",
"(replace-at (list (list 1 2) (list 3 4)) u0 (list 0))",
"(replace-at (list (list 1 2 3)) u0 (list 0))",
];
let expected = [
"(optional (list 7 int))",
"(optional (list 5 uint))",
"(optional (list 1 bool))",
"(optional (list 7 int))",
"(optional (list 2 (list 1 int)))",
"(optional (list 2 (list 2 int)))",
"(optional (list 1 (list 3 int)))",
];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(
expected,
&format!("{}", type_check_helper(&good_test).unwrap())
);
}
let bad = [
"(replace-at (list 2 3) u0 (list 4))",
"(replace-at (list 2 3) u0 true)",
"(replace-at (list 2 3) 0 4)",
"(replace-at (list 2 3) u0 4 5)",
"(replace-at (list u0) u0)",
"(replace-at (list (list 1) (list 2)) u0 (list 33 44))",
];
let bad_expected = [
CheckErrors::TypeError(
IntType,
SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())),
),
CheckErrors::TypeError(IntType, BoolType),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::IncorrectArgumentCount(3, 4),
CheckErrors::IncorrectArgumentCount(3, 2),
CheckErrors::TypeError(
SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())),
SequenceType(ListType(ListTypeData::new_list(IntType, 2).unwrap())),
),
];
for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) {
assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err);
}
}
#[test]
fn test_replace_at_buff() {
let good = [
"(replace-at 0x00112233 u0 0x44)",
"(replace-at 0x00112233 u3 0x66)",
"(replace-at 0x00 u0 0x22)",
"(replace-at 0x001122334455 u2 0x66)",
];
let expected = [
"(optional (buff 4))",
"(optional (buff 4))",
"(optional (buff 1))",
"(optional (buff 6))",
];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(
expected,
&format!("{}", type_check_helper(&good_test).unwrap())
);
}
let bad = [
"(replace-at 0x0011 u0 (list 0))",
"(replace-at 0x0011 u0 \"a\")",
"(replace-at 0x0011 0 0x22)",
"(replace-at 0x0011 u0 0x44 0x55)",
"(replace-at 0x11 u0)",
"(replace-at 0x001122334455 u2 0x6677)",
];
let buff_len = BufferLength::try_from(1u32).unwrap();
let buff_len_two = BufferLength::try_from(2u32).unwrap();
let bad_expected = [
CheckErrors::TypeError(
SequenceType(BufferType(buff_len.clone())),
SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())),
),
CheckErrors::TypeError(
SequenceType(BufferType(buff_len.clone())),
SequenceType(StringType(ASCII(buff_len.clone()))),
),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::IncorrectArgumentCount(3, 4),
CheckErrors::IncorrectArgumentCount(3, 2),
CheckErrors::TypeError(
SequenceType(BufferType(buff_len.clone())),
SequenceType(BufferType(buff_len_two.clone())),
),
];
for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) {
assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err);
}
}
#[test]
fn test_replace_at_ascii() {
let good = [
"(replace-at \"abcd\" u0 \"f\")",
"(replace-at \"abcd\" u3 \"f\")",
"(replace-at \"a\" u0 \"f\")",
"(replace-at \"abcdefg\" u2 \"h\")",
];
let expected = [
"(optional (string-ascii 4))",
"(optional (string-ascii 4))",
"(optional (string-ascii 1))",
"(optional (string-ascii 7))",
"(optional (string-ascii 7))",
];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(
expected,
&format!("{}", type_check_helper(&good_test).unwrap())
);
}
let bad = [
"(replace-at \"abcd\" u0 (list 0))",
"(replace-at \"abcd\" u0 0x00)",
"(replace-at \"abcd\" 0 \"e\")",
"(replace-at \"abcd\" u0 \"a\" \"d\")",
"(replace-at \"abcd\" u0)",
"(replace-at \"abcdefg\" u2 \"hi\")",
];
let buff_len = BufferLength::try_from(1u32).unwrap();
let buff_len_two = BufferLength::try_from(2u32).unwrap();
let bad_expected = [
CheckErrors::TypeError(
SequenceType(StringType(ASCII(buff_len.clone()))),
SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())),
),
CheckErrors::TypeError(
SequenceType(StringType(ASCII(buff_len.clone()))),
SequenceType(BufferType(buff_len.clone())),
),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::IncorrectArgumentCount(3, 4),
CheckErrors::IncorrectArgumentCount(3, 2),
CheckErrors::TypeError(
SequenceType(StringType(ASCII(buff_len.clone()))),
SequenceType(StringType(ASCII(buff_len_two.clone()))),
),
];
for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) {
assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err);
}
}
#[test]
fn test_replace_at_utf8() {
let good = [
"(replace-at u\"abcd\" u0 u\"f\")",
"(replace-at u\"abcd\" u3 u\"f\")",
"(replace-at u\"a\" u0 u\"f\")",
"(replace-at u\"abcdefg\" u2 u\"h\")",
];
let expected = [
"(optional (string-utf8 4))",
"(optional (string-utf8 4))",
"(optional (string-utf8 1))",
"(optional (string-utf8 7))",
];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(
expected,
&format!("{}", type_check_helper(&good_test).unwrap())
);
}
let bad = [
"(replace-at u\"abcd\" u0 (list 0))",
"(replace-at u\"abcd\" u0 0x00)",
"(replace-at u\"abcd\" 0 u\"a\")",
"(replace-at u\"abcd\" u0 u\"a\" u\"d\")",
"(replace-at u\"abcd\" u0)",
"(replace-at u\"abcdefg\" u2 u\"hi\")",
];
let buff_len = BufferLength::try_from(1u32).unwrap();
let str_len = StringUTF8Length::try_from(1u32).unwrap();
let str_len_two = StringUTF8Length::try_from(2u32).unwrap();
let bad_expected = [
CheckErrors::TypeError(
SequenceType(StringType(UTF8(str_len.clone()))),
SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())),
),
CheckErrors::TypeError(
SequenceType(StringType(UTF8(str_len.clone()))),
SequenceType(BufferType(buff_len.clone())),
),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::IncorrectArgumentCount(3, 4),
CheckErrors::IncorrectArgumentCount(3, 2),
CheckErrors::TypeError(
SequenceType(StringType(UTF8(str_len.clone()))),
SequenceType(StringType(UTF8(str_len_two.clone()))),
),
];
for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) {
assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err);
}
}
#[test]
fn test_native_concat() {
let good = ["(concat (list 2 3) (list 4 5))"];

View File

@@ -2204,6 +2204,28 @@ to deserialize the type, the method returns `none`.
"#,
};
const REPLACE_AT: SpecialAPI = SpecialAPI {
input_type: "sequence_A, uint, A",
output_type: "(optional sequence_A)",
snippet: "replace-at ${1:sequence} ${2:index} ${3:element}",
signature: "(replace-at sequence index element)",
description: "The `replace-at` function takes in a sequence, an index, and an element,
and returns a new sequence with the data at the index position replaced with the given element.
The given element's type must match the type of the sequence, and must correspond to a single
index of the input sequence. The return type on success is the same type as the input sequence.
If the provided index is out of bounds, this functions returns `none`.
",
example: r#"
(replace-at u"ab" u1 u"c") ;; Returns (some u"ac")
(replace-at 0x00112233 u2 0x44) ;; Returns (some 0x00114433)
(replace-at "abcd" u3 "e") ;; Returns (some "abce")
(replace-at (list 1) u0 10) ;; Returns (some (10))
(replace-at (list (list 1) (list 2)) u0 (list 33)) ;; Returns (some ((33) (2)))
(replace-at (list 1 2) u3 4) ;; Returns none
"#,
};
pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
use crate::vm::functions::NativeFunctions::*;
let name = function.get_name();
@@ -2307,6 +2329,7 @@ pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
StxBurn => make_for_simple_native(&STX_BURN, &StxBurn, name),
ToConsensusBuff => make_for_special(&TO_CONSENSUS_BUFF, function),
FromConsensusBuff => make_for_special(&FROM_CONSENSUS_BUFF, function),
ReplaceAt => make_for_special(&REPLACE_AT, function),
}
}

View File

@@ -172,6 +172,7 @@ define_versioned_named_enum!(NativeFunctions(ClarityVersion) {
Slice("slice", ClarityVersion::Clarity2),
ToConsensusBuff("to-consensus-buff", ClarityVersion::Clarity2),
FromConsensusBuff("from-consensus-buff", ClarityVersion::Clarity2),
ReplaceAt("replace-at", ClarityVersion::Clarity2),
});
impl NativeFunctions {
@@ -515,6 +516,7 @@ pub fn lookup_reserved_functions(name: &str, version: &ClarityVersion) -> Option
FromConsensusBuff => {
SpecialFunction("from_consensus_buff", &conversions::from_consensus_buff)
}
ReplaceAt => SpecialFunction("replace_at", &sequences::special_replace_at),
};
Some(callable)
} else {

View File

@@ -383,3 +383,48 @@ pub fn special_slice(
}?;
Ok(sliced_seq)
}
pub fn special_replace_at(
args: &[SymbolicExpression],
env: &mut Environment,
context: &LocalContext,
) -> Result<Value> {
check_argument_count(3, args)?;
// Set the input to runtime_cost to 0, since replacing an element at an index is an O(1) operation.
runtime_cost(ClarityCostFunction::Unimplemented, env, 0)?;
let seq = eval(&args[0], env, context)?;
let seq_type = TypeSignature::type_of(&seq);
let expected_elem_type = if let TypeSignature::SequenceType(seq_subtype) = &seq_type {
seq_subtype.unit_type()
} else {
return Err(CheckErrors::ExpectedSequence(seq_type).into());
};
let index_val = eval(&args[1], env, context)?;
let new_element = eval(&args[2], env, context)?;
if expected_elem_type != TypeSignature::NoType && !expected_elem_type.admits(&new_element) {
return Err(CheckErrors::TypeValueError(expected_elem_type, new_element).into());
}
let index = if let Value::UInt(index_u128) = index_val {
if let Ok(index_usize) = usize::try_from(index_u128) {
index_usize
} else {
return Ok(Value::none());
}
} else {
return Err(CheckErrors::TypeValueError(TypeSignature::UIntType, index_val).into());
};
if let Value::Sequence(data) = seq {
let seq_len = data.len();
if index >= seq_len {
return Ok(Value::none());
}
data.replace_at(index, new_element)
} else {
return Err(CheckErrors::ExpectedSequence(seq_type).into());
}
}

View File

@@ -16,7 +16,7 @@
use crate::vm::types::signatures::{ListTypeData, SequenceSubtype};
use crate::vm::types::TypeSignature::{BoolType, IntType, SequenceType, UIntType};
use crate::vm::types::{TypeSignature, Value};
use crate::vm::types::{StringSubtype, StringUTF8Length, TypeSignature, Value};
#[cfg(test)]
use rstest::rstest;
#[cfg(test)]
@@ -24,9 +24,13 @@ use rstest_reuse::{self, *};
use crate::vm::analysis::errors::CheckError;
use crate::vm::errors::{CheckErrors, Error, RuntimeErrorType};
use crate::vm::types::signatures::SequenceSubtype::{BufferType, ListType, StringType};
use crate::vm::types::signatures::StringSubtype::ASCII;
use crate::vm::types::BufferLength;
use crate::vm::types::CharType::UTF8;
use crate::vm::{execute, execute_v2, ClarityVersion};
use stacks_common::types::StacksEpochId;
use std::convert::TryInto;
use std::convert::{TryFrom, TryInto};
#[template]
#[rstest]
@@ -637,6 +641,338 @@ fn test_simple_buff_concat() {
);
}
#[test]
fn test_simple_list_replace_at() {
let tests = [
"(replace-at (list 1 2) u1 4)",
"(replace-at (list 1) u0 10)",
"(replace-at (list 1 9 0 5) u3 6)",
"(replace-at (list 4 5 6 7 8) u2 11)",
"(replace-at (list (list 1) (list 2)) u0 (list 33))",
"(replace-at (list (list 1 2) (list 3 4)) u0 (list 0))",
"(replace-at (list (list 1 2 3)) u0 (list 0))",
];
let expected = [
Value::some(Value::list_from(vec![Value::Int(1), Value::Int(4)]).unwrap()).unwrap(),
Value::some(Value::list_from(vec![Value::Int(10)]).unwrap()).unwrap(),
Value::some(
Value::list_from(vec![
Value::Int(1),
Value::Int(9),
Value::Int(0),
Value::Int(6),
])
.unwrap(),
)
.unwrap(),
Value::some(
Value::list_from(vec![
Value::Int(4),
Value::Int(5),
Value::Int(11),
Value::Int(7),
Value::Int(8),
])
.unwrap(),
)
.unwrap(),
Value::some(
Value::list_from(vec![
Value::list_from(vec![Value::Int(33)]).unwrap(),
Value::list_from(vec![Value::Int(2)]).unwrap(),
])
.unwrap(),
)
.unwrap(),
Value::some(
Value::list_from(vec![
Value::list_from(vec![Value::Int(0)]).unwrap(),
Value::list_from(vec![Value::Int(3), Value::Int(4)]).unwrap(),
])
.unwrap(),
)
.unwrap(),
Value::some(
Value::list_from(vec![Value::list_from(vec![Value::Int(0)]).unwrap()]).unwrap(),
)
.unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute_v2(test).unwrap().unwrap());
}
let bad_tests = [
// index is out of bounds
"(replace-at (list 1 2) u3 4)",
// the sequence is length 0, so the index is out of bounds
"(replace-at (list) u0 6)",
];
let bad_expected = [Value::none(), Value::none()];
for (bad_test, bad_expected) in bad_tests.iter().zip(bad_expected.iter()) {
assert_eq!(bad_expected.clone(), execute_v2(bad_test).unwrap().unwrap());
}
// The sequence input has the wrong type
assert_eq!(
execute_v2("(replace-at 0 u0 (list 0))").unwrap_err(),
CheckErrors::ExpectedSequence(IntType).into()
);
// The type of the index should be uint.
assert_eq!(
execute_v2("(replace-at (list 1) 0 0)").unwrap_err(),
CheckErrors::TypeValueError(UIntType, Value::Int(0)).into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at (list 2 3) u0 true)").unwrap_err(),
CheckErrors::TypeValueError(IntType, Value::Bool(true)).into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at (list 2 3) u0 0x00)").unwrap_err(),
CheckErrors::TypeValueError(IntType, Value::buff_from_byte(0)).into()
);
}
#[test]
fn test_simple_buff_replace_at() {
let tests = [
"(replace-at 0x3031 u1 0x44)",
"(replace-at 0x00 u0 0x11)",
"(replace-at 0x00112233 u3 0x44)",
"(replace-at 0x00112233 u1 0x44)",
];
let expected = [
Value::some(Value::buff_from(vec![48, 68]).unwrap()).unwrap(),
Value::some(Value::buff_from(vec![17]).unwrap()).unwrap(),
Value::some(Value::buff_from(vec![0, 17, 34, 68]).unwrap()).unwrap(),
Value::some(Value::buff_from(vec![0, 68, 34, 51]).unwrap()).unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute_v2(test).unwrap().unwrap());
}
let bad_tests = [
// index is out of bounds
"(replace-at 0x0022 u3 0x44)",
// the sequence is length 0, so the index is out of bounds
"(replace-at 0x u0 0x11)",
];
let bad_expected = [Value::none(), Value::none()];
for (bad_test, bad_expected) in bad_tests.iter().zip(bad_expected.iter()) {
assert_eq!(bad_expected.clone(), execute_v2(bad_test).unwrap().unwrap());
}
// The sequence input has the wrong type
assert_eq!(
execute_v2("(replace-at 33 u0 0x00)").unwrap_err(),
CheckErrors::ExpectedSequence(IntType).into()
);
// The type of the index should be uint.
assert_eq!(
execute_v2("(replace-at 0x002244 0 0x99)").unwrap_err(),
CheckErrors::TypeValueError(UIntType, Value::Int(0)).into()
);
// The element input has the wrong type
let buff_len = BufferLength::try_from(1u32).unwrap();
assert_eq!(
execute_v2("(replace-at 0x445522 u0 55)").unwrap_err(),
CheckErrors::TypeValueError(SequenceType(BufferType(buff_len.clone())), Value::Int(55))
.into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at 0x445522 u0 (list 5))").unwrap_err(),
CheckErrors::TypeValueError(
SequenceType(BufferType(buff_len.clone())),
Value::list_from(vec![Value::Int(5)]).unwrap()
)
.into()
);
// The element input has the wrong type (not length 1)
assert_eq!(
execute_v2("(replace-at 0x445522 u0 0x0044)").unwrap_err(),
CheckErrors::TypeValueError(
SequenceType(BufferType(buff_len.clone())),
Value::buff_from(vec![0, 68]).unwrap()
)
.into()
);
}
#[test]
fn test_simple_string_ascii_replace_at() {
let tests = [
"(replace-at \"ab\" u1 \"c\")",
"(replace-at \"a\" u0 \"c\")",
"(replace-at \"abcd\" u3 \"e\")",
"(replace-at \"abcd\" u1 \"e\")",
];
let expected = [
Value::some(Value::string_ascii_from_bytes("ac".into()).unwrap()).unwrap(),
Value::some(Value::string_ascii_from_bytes("c".into()).unwrap()).unwrap(),
Value::some(Value::string_ascii_from_bytes("abce".into()).unwrap()).unwrap(),
Value::some(Value::string_ascii_from_bytes("aecd".into()).unwrap()).unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute_v2(test).unwrap().unwrap());
}
let bad_tests = [
// index is out of bounds
"(replace-at \"ab\" u3 \"c\")",
// the sequence is length 0, so the index is out of bounds
"(replace-at \"\" u0 \"a\")",
];
let bad_expected = [Value::none(), Value::none()];
for (bad_test, bad_expected) in bad_tests.iter().zip(bad_expected.iter()) {
assert_eq!(bad_expected.clone(), execute_v2(bad_test).unwrap().unwrap());
}
// The sequence input has the wrong type
assert_eq!(
execute_v2("(replace-at 33 u0 \"c\")").unwrap_err(),
CheckErrors::ExpectedSequence(IntType).into()
);
// The type of the index should be uint.
assert_eq!(
execute_v2("(replace-at \"abc\" 0 \"c\")").unwrap_err(),
CheckErrors::TypeValueError(UIntType, Value::Int(0)).into()
);
// The element input has the wrong type
let buff_len = BufferLength::try_from(1u32).unwrap();
assert_eq!(
execute_v2("(replace-at \"abc\" u0 55)").unwrap_err(),
CheckErrors::TypeValueError(
SequenceType(StringType(ASCII(buff_len.clone()))),
Value::Int(55)
)
.into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at \"abc\" u0 0x00)").unwrap_err(),
CheckErrors::TypeValueError(
SequenceType(StringType(ASCII(buff_len.clone()))),
Value::buff_from_byte(0)
)
.into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at \"abc\" u0 \"de\")").unwrap_err(),
CheckErrors::TypeValueError(
SequenceType(StringType(ASCII(buff_len.clone()))),
Value::string_ascii_from_bytes("de".into()).unwrap()
)
.into()
);
}
#[test]
fn test_simple_string_utf8_replace_at() {
let tests = [
"(replace-at u\"ab\" u1 u\"c\")",
"(replace-at u\"a\" u0 u\"c\")",
"(replace-at u\"abcd\" u3 u\"e\")",
"(replace-at u\"abcd\" u1 u\"e\")",
"(replace-at u\"hello\\u{1F98A}\" u5 u\"e\")",
"(replace-at u\"hello\\u{1F98A}\" u2 u\"e\")",
];
let expected = [
Value::some(Value::string_utf8_from_bytes("ac".into()).unwrap()).unwrap(),
Value::some(Value::string_utf8_from_bytes("c".into()).unwrap()).unwrap(),
Value::some(Value::string_utf8_from_bytes("abce".into()).unwrap()).unwrap(),
Value::some(Value::string_utf8_from_bytes("aecd".into()).unwrap()).unwrap(),
Value::some(Value::string_utf8_from_bytes("helloe".into()).unwrap()).unwrap(),
Value::some(Value::string_utf8_from_bytes("heelo🦊".into()).unwrap()).unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute_v2(test).unwrap().unwrap());
}
let bad_tests = [
// index is out of bounds
"(replace-at u\"ab\" u3 u\"c\")",
// the sequence is length 0, so the index is out of bounds
"(replace-at u\"\" u0 u\"a\")",
];
let bad_expected = [Value::none(), Value::none()];
for (bad_test, bad_expected) in bad_tests.iter().zip(bad_expected.iter()) {
assert_eq!(bad_expected.clone(), execute_v2(bad_test).unwrap().unwrap());
}
// The sequence input has the wrong type
assert_eq!(
execute_v2("(replace-at 33 u0 u\"c\")").unwrap_err(),
CheckErrors::ExpectedSequence(IntType).into()
);
// The type of the index should be uint.
assert_eq!(
execute_v2("(replace-at u\"abc\" 0 u\"c\")").unwrap_err(),
CheckErrors::TypeValueError(UIntType, Value::Int(0)).into()
);
// The element input has the wrong type
let str_len = StringUTF8Length::try_from(1u32).unwrap();
assert_eq!(
execute_v2("(replace-at u\"abc\" u0 55)").unwrap_err(),
CheckErrors::TypeValueError(
TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len.clone()))),
Value::Int(55)
)
.into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at u\"abc\" u0 0x00)").unwrap_err(),
CheckErrors::TypeValueError(
TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len.clone()))),
Value::buff_from_byte(0)
)
.into()
);
// The element input has the wrong type
assert_eq!(
execute_v2("(replace-at u\"abc\" u0 u\"de\")").unwrap_err(),
CheckErrors::TypeValueError(
TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len.clone()))),
Value::string_utf8_from_string_utf8_literal("de".to_string()).unwrap()
)
.into()
);
}
#[test]
fn test_simple_buff_assert_max_len() {
let tests = [

View File

@@ -282,6 +282,58 @@ impl SequenceData {
Some(result)
}
pub fn replace_at(self, index: usize, element: Value) -> Result<Value> {
let seq_length = self.len();
// Check that the length of the provided element is 1. In the case that SequenceData
// is a list, we check that the provided element is the right type below.
if !self.is_list() {
if let Value::Sequence(data) = &element {
let elem_length = data.len();
if elem_length != 1 {
return Err(RuntimeErrorType::BadTypeConstruction.into());
}
} else {
return Err(RuntimeErrorType::BadTypeConstruction.into());
}
}
if index >= seq_length {
return Err(CheckErrors::ValueOutOfBounds.into());
}
let new_seq_data = match (self, element) {
(SequenceData::Buffer(mut data), Value::Sequence(SequenceData::Buffer(elem))) => {
data.data[index] = elem.data[0];
SequenceData::Buffer(data)
}
(SequenceData::List(mut data), elem) => {
let entry_type = data.type_signature.get_list_item_type();
if !entry_type.admits(&elem) {
return Err(CheckErrors::ListTypesMustMatch.into());
}
data.data[index] = elem;
SequenceData::List(data)
}
(
SequenceData::String(CharType::ASCII(mut data)),
Value::Sequence(SequenceData::String(CharType::ASCII(elem))),
) => {
data.data[index] = elem.data[0];
SequenceData::String(CharType::ASCII(data))
}
(
SequenceData::String(CharType::UTF8(mut data)),
Value::Sequence(SequenceData::String(CharType::UTF8(mut elem))),
) => {
data.data[index] = elem.data.swap_remove(0);
SequenceData::String(CharType::UTF8(data))
}
_ => return Err(CheckErrors::ListTypesMustMatch.into()),
};
Ok(Value::some(Value::Sequence(new_seq_data))?)
}
pub fn contains(&self, to_find: Value) -> Result<Option<usize>> {
match self {
SequenceData::Buffer(ref data) => {
@@ -459,6 +511,14 @@ impl SequenceData {
Ok(result)
}
pub fn is_list(&self) -> bool {
if let SequenceData::List(x) = self {
true
} else {
false
}
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]

View File

@@ -118,6 +118,13 @@ impl SequenceSubtype {
SequenceSubtype::StringType(StringSubtype::UTF8(_)) => TypeSignature::min_string_utf8(),
}
}
pub fn is_list_type(&self) -> bool {
match &self {
SequenceSubtype::ListType(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@@ -472,22 +472,36 @@ impl BitcoinIndexer {
let interval_start_block =
(start_block / BLOCK_DIFFICULTY_CHUNK_SIZE).saturating_sub(2);
let base_block = interval_start_block * BLOCK_DIFFICULTY_CHUNK_SIZE;
let interval_headers =
canonical_spv_client.read_block_headers(base_block, start_block + 1)?;
assert!(
interval_headers.len() >= (start_block - base_block) as usize,
"BUG: missing headers for {}-{}",
base_block,
start_block
);
test_debug!(
"Copy headers {}-{}",
base_block,
base_block + interval_headers.len() as u64
);
reorg_spv_client
.insert_block_headers_before(base_block - 1, interval_headers)?;
if base_block > 0 {
let interval_headers =
canonical_spv_client.read_block_headers(base_block, start_block + 1)?;
assert!(
interval_headers.len() >= (start_block - base_block) as usize,
"BUG: missing headers for {}-{}",
base_block,
start_block
);
debug!(
"Copy headers {}-{}",
base_block,
base_block + interval_headers.len() as u64
);
reorg_spv_client
.insert_block_headers_before(base_block - 1, interval_headers)?;
} else {
let interval_headers =
canonical_spv_client.read_block_headers(1, start_block + 1)?;
assert!(
interval_headers.len() >= start_block as usize,
"BUG: missing headers for 1-{}",
start_block
);
debug!("Copy headers 1-{}", interval_headers.len() as u64);
reorg_spv_client.insert_block_headers_before(0, interval_headers)?;
}
let last_interval = canonical_spv_client.find_highest_work_score_interval()?;
@@ -545,12 +559,12 @@ impl BitcoinIndexer {
let mut new_tip = 0;
let mut found_common_ancestor = false;
let orig_spv_client = SpvClient::new(
let mut orig_spv_client = SpvClient::new(
canonical_headers_path,
0,
None,
self.runtime.network_id,
false,
true,
false,
)?;
@@ -718,7 +732,7 @@ impl BitcoinIndexer {
if check_chain_work {
let reorg_total_work = reorg_spv_client.update_chain_work()?;
let orig_total_work = orig_spv_client.get_chain_work()?;
let orig_total_work = orig_spv_client.update_chain_work()?;
debug!("Bitcoin headers history is consistent up to {}", new_tip;
"Orig chainwork" => %orig_total_work,

View File

@@ -159,6 +159,7 @@ pub fn get_simple_test(function: &NativeFunctions) -> &'static str {
Slice => "(slice str-foo u1 u1)",
ToConsensusBuff => "(to-consensus-buff u1)",
FromConsensusBuff => "(from-consensus-buff bool 0x03)",
ReplaceAt => "(replace-at list-bar u0 5)",
}
}

View File

@@ -7367,3 +7367,73 @@ fn test_flash_block_skip_tenure() {
channel.stop_chains_coordinator();
}
#[test]
#[ignore]
fn test_chainwork_first_intervals() {
if env::var("BITCOIND_TEST") != Ok("1".into()) {
return;
}
let (mut conf, miner_account) = neon_integration_test_conf();
let mut btcd_controller = BitcoinCoreController::new(conf.clone());
btcd_controller
.start_bitcoind()
.map_err(|_e| ())
.expect("Failed starting bitcoind");
let mut btc_regtest_controller = BitcoinRegtestController::new(conf.clone(), None);
let http_origin = format!("http://{}", &conf.node.rpc_bind);
btc_regtest_controller.bootstrap_chain(2016 * 2 - 1);
eprintln!("Chain bootstrapped...");
let mut run_loop = neon::RunLoop::new(conf);
let blocks_processed = run_loop.get_blocks_processed_arc();
let missed_tenures = run_loop.get_missed_tenures_arc();
let channel = run_loop.get_coordinator_channel().unwrap();
thread::spawn(move || run_loop.start(None, 0));
// give the run loop some time to start up!
wait_for_runloop(&blocks_processed);
channel.stop_chains_coordinator();
}
#[test]
#[ignore]
fn test_chainwork_partial_interval() {
if env::var("BITCOIND_TEST") != Ok("1".into()) {
return;
}
let (mut conf, miner_account) = neon_integration_test_conf();
let mut btcd_controller = BitcoinCoreController::new(conf.clone());
btcd_controller
.start_bitcoind()
.map_err(|_e| ())
.expect("Failed starting bitcoind");
let mut btc_regtest_controller = BitcoinRegtestController::new(conf.clone(), None);
let http_origin = format!("http://{}", &conf.node.rpc_bind);
btc_regtest_controller.bootstrap_chain(2016 - 1);
eprintln!("Chain bootstrapped...");
let mut run_loop = neon::RunLoop::new(conf);
let blocks_processed = run_loop.get_blocks_processed_arc();
let missed_tenures = run_loop.get_missed_tenures_arc();
let channel = run_loop.get_coordinator_channel().unwrap();
thread::spawn(move || run_loop.start(None, 0));
// give the run loop some time to start up!
wait_for_runloop(&blocks_processed);
channel.stop_chains_coordinator();
}