fix(Clarity2):Factor out 'stx-transfer-memo' into a separate function (#2732)

* began refactor of memo

* fixed some of the tests

* added a test for memo's

* added tranfer-memo to the docs

* fixed test::events

* fixed a costs test

* rust fmt

* change version in arithemtic checker

* fix test_stx_ops

* cleaning up some imports

* rearranged the vm tests

* copied tests in test_stx_ops to create memo tests

* added some memo tests to simple_apply_eval

* revert change to ast

* new test cases for test_stx_ops

* began refactor of memo

* fixed some of the tests

* added a test for memo's

* added tranfer-memo to the docs

* fixed test::events

* fixed a costs test

* rust fmt

* change version in arithemtic checker

* fix test_stx_ops

* cleaning up some imports

* rearranged the vm tests

* copied tests in test_stx_ops to create memo tests

* added some memo tests to simple_apply_eval

* revert change to ast

* new test cases for test_stx_ops

* using Unimplemented as the cost

* arithmetic_checker separates Clarity1 vs Clarity2 functions

* remove some prinln's

Co-authored-by: Greg Coppola <greg@hiro.so>
This commit is contained in:
Gregory Coppola
2021-07-13 09:30:34 -05:00
committed by GitHub
parent e7b0e6a29a
commit a83f45ed2d
12 changed files with 260 additions and 93 deletions

View File

@@ -175,8 +175,8 @@ impl<'a> ArithmeticOnlyChecker<'a> {
match function {
FetchVar | GetBlockInfo | GetTokenBalance | GetAssetOwner | FetchEntry | SetEntry
| DeleteEntry | InsertEntry | SetVar | MintAsset | MintToken | TransferAsset
| TransferToken | ContractCall | StxTransfer | StxBurn | AtBlock | GetStxBalance
| GetTokenSupply | BurnToken | BurnAsset | StxGetAccount => {
| TransferToken | ContractCall | StxTransfer | StxTransferMemo | StxBurn | AtBlock
| GetStxBalance | GetTokenSupply | BurnToken | BurnAsset | StxGetAccount => {
return Err(Error::FunctionNotPermitted(function));
}
Append | Concat | AsMaxLen | ContractOf | PrincipalOf | ListCons | Print

View File

@@ -183,95 +183,123 @@ fn test_variables_fail_arithmetic_check_clarity2() {
}
#[test]
fn test_functions() {
let bad_tests = [
fn test_functions_clarity1() {
// Tests all functions against Clarity1 VM. Results should be different for Clarity1 vs Clarity2 functions.
let tests = [
// Clarity1 functions.
("(define-private (foo) (at-block 0x0202020202020202020202020202020202020202020202020202020202020202 (+ 1 2)))",
FunctionNotPermitted(NativeFunctions::AtBlock)),
Err(FunctionNotPermitted(NativeFunctions::AtBlock))),
("(define-private (foo) (map-get? foo-map {a: u1}))",
FunctionNotPermitted(NativeFunctions::FetchEntry)),
Err(FunctionNotPermitted(NativeFunctions::FetchEntry))),
("(define-private (foo) (map-delete foo-map {a: u1}))",
FunctionNotPermitted(NativeFunctions::DeleteEntry)),
Err(FunctionNotPermitted(NativeFunctions::DeleteEntry))),
("(define-private (foo) (map-set foo-map {a: u1} {b: u2}))",
FunctionNotPermitted(NativeFunctions::SetEntry)),
Err(FunctionNotPermitted(NativeFunctions::SetEntry))),
("(define-private (foo) (map-insert foo-map {a: u1} {b: u2}))",
FunctionNotPermitted(NativeFunctions::InsertEntry)),
Err(FunctionNotPermitted(NativeFunctions::InsertEntry))),
("(define-private (foo) (var-get foo-var))",
FunctionNotPermitted(NativeFunctions::FetchVar)),
Err(FunctionNotPermitted(NativeFunctions::FetchVar))),
("(define-private (foo) (var-set foo-var u2))",
FunctionNotPermitted(NativeFunctions::SetVar)),
Err(FunctionNotPermitted(NativeFunctions::SetVar))),
("(define-private (foo (a principal)) (ft-get-balance tokaroos a))",
FunctionNotPermitted(NativeFunctions::GetTokenBalance)),
Err(FunctionNotPermitted(NativeFunctions::GetTokenBalance))),
("(define-private (foo (a principal))
(ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF))",
FunctionNotPermitted(NativeFunctions::TransferToken)),
Err(FunctionNotPermitted(NativeFunctions::TransferToken))),
("(define-private (foo (a principal))
(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
FunctionNotPermitted(NativeFunctions::MintToken)),
Err(FunctionNotPermitted(NativeFunctions::MintToken))),
("(define-private (foo (a principal))
(nft-mint? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
FunctionNotPermitted(NativeFunctions::MintAsset)),
Err(FunctionNotPermitted(NativeFunctions::MintAsset))),
("(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
FunctionNotPermitted(NativeFunctions::TransferAsset)),
Err(FunctionNotPermitted(NativeFunctions::TransferAsset))),
("(nft-get-owner? stackaroo \"Roo\")",
FunctionNotPermitted(NativeFunctions::GetAssetOwner)),
Err(FunctionNotPermitted(NativeFunctions::GetAssetOwner))),
("(get-block-info? id-header-hash 0)",
FunctionNotPermitted(NativeFunctions::GetBlockInfo)),
Err(FunctionNotPermitted(NativeFunctions::GetBlockInfo))),
("(define-private (foo) (contract-call? .bar outer-call))",
FunctionNotPermitted(NativeFunctions::ContractCall)),
Err(FunctionNotPermitted(NativeFunctions::ContractCall))),
("(stx-get-balance 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
FunctionNotPermitted(NativeFunctions::GetStxBalance)),
Err(FunctionNotPermitted(NativeFunctions::GetStxBalance))),
("(stx-burn? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
FunctionNotPermitted(NativeFunctions::StxBurn)),
(r#"(stx-transfer? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 0x00)"#,
FunctionNotPermitted(NativeFunctions::StxTransfer)),
Err(FunctionNotPermitted(NativeFunctions::StxBurn))),
(r#"(stx-transfer? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)"#,
Err(FunctionNotPermitted(NativeFunctions::StxTransfer))),
("(define-private (foo (a (list 3 uint)))
(map log2 a))",
FunctionNotPermitted(NativeFunctions::Map)),
Err(FunctionNotPermitted(NativeFunctions::Map))),
("(define-private (foo (a (list 3 (optional uint))))
(filter is-none a))",
FunctionNotPermitted(NativeFunctions::Filter)),
Err(FunctionNotPermitted(NativeFunctions::Filter))),
("(define-private (foo (a (list 3 uint)))
(append a u4))",
FunctionNotPermitted(NativeFunctions::Append)),
Err(FunctionNotPermitted(NativeFunctions::Append))),
("(define-private (foo (a (list 3 uint)))
(concat a a))",
FunctionNotPermitted(NativeFunctions::Concat)),
Err(FunctionNotPermitted(NativeFunctions::Concat))),
("(define-private (foo (a (list 3 uint)))
(as-max-len? a u4))",
FunctionNotPermitted(NativeFunctions::AsMaxLen)),
Err(FunctionNotPermitted(NativeFunctions::AsMaxLen))),
("(define-private (foo) (print 10))",
FunctionNotPermitted(NativeFunctions::Print)),
Err(FunctionNotPermitted(NativeFunctions::Print))),
("(define-private (foo) (list 3 4 10))",
FunctionNotPermitted(NativeFunctions::ListCons)),
Err(FunctionNotPermitted(NativeFunctions::ListCons))),
("(define-private (foo) (keccak256 0))",
FunctionNotPermitted(NativeFunctions::Keccak256)),
Err(FunctionNotPermitted(NativeFunctions::Keccak256))),
("(define-private (foo) (hash160 0))",
FunctionNotPermitted(NativeFunctions::Hash160)),
Err(FunctionNotPermitted(NativeFunctions::Hash160))),
("(define-private (foo) (secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301))",
FunctionNotPermitted(NativeFunctions::Secp256k1Recover)),
Err(FunctionNotPermitted(NativeFunctions::Secp256k1Recover))),
("(define-private (foo) (secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04
0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a13
0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110))",
FunctionNotPermitted(NativeFunctions::Secp256k1Verify)),
Err(FunctionNotPermitted(NativeFunctions::Secp256k1Verify))),
("(define-private (foo) (sha256 0))",
FunctionNotPermitted(NativeFunctions::Sha256)),
Err(FunctionNotPermitted(NativeFunctions::Sha256))),
("(define-private (foo) (sha512 0))",
FunctionNotPermitted(NativeFunctions::Sha512)),
Err(FunctionNotPermitted(NativeFunctions::Sha512))),
("(define-private (foo) (sha512/256 0))",
FunctionNotPermitted(NativeFunctions::Sha512Trunc256)),
Err(FunctionNotPermitted(NativeFunctions::Sha512Trunc256))),
// Clarity2 functions.
(r#"(stx-transfer-memo? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 0x010203)"#,
Ok(())),
];
for (contract, error) in bad_tests.iter() {
eprintln!("{}", contract);
for (contract, result) in tests.iter() {
assert_eq!(
arithmetic_check(contract, ClarityVersion::Clarity1),
Err(error.clone()),
result.clone(),
"Check contract:\n {}",
contract
);
}
}
#[test]
fn test_functions_clarity2() {
// Tests Clarity2 functions against Clarity2 VM.
let tests = [
// Clarity2 functions.
(
r#"(stx-transfer-memo? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 0x010203)"#,
Err(FunctionNotPermitted(NativeFunctions::StxTransferMemo)),
),
];
for (contract, result) in tests.iter() {
assert_eq!(
arithmetic_check(contract, ClarityVersion::Clarity2),
result.clone(),
"Check contract:\n {}",
contract
);
}
}
#[test]
fn test_functions_contract() {
let good_tests = [
"(match (if (is-eq 0 1) (ok 1) (err 2))
ok-val (+ 1 ok-val)
@@ -306,7 +334,6 @@ fn test_functions() {
];
for contract in good_tests.iter() {
eprintln!("{}", contract);
check_good(contract);
}
}

View File

@@ -203,8 +203,9 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
check_argument_count(2, args)?;
self.check_all_read_only(args)
}
StxTransfer | StxBurn | SetEntry | DeleteEntry | InsertEntry | SetVar | MintAsset
| MintToken | TransferAsset | TransferToken | BurnAsset | BurnToken => {
StxTransfer | StxTransferMemo | StxBurn | SetEntry | DeleteEntry | InsertEntry
| SetVar | MintAsset | MintToken | TransferAsset | TransferToken | BurnAsset
| BurnToken => {
self.check_all_read_only(args)?;
Ok(false)
}

View File

@@ -195,12 +195,30 @@ pub fn check_special_stx_transfer(
args: &[SymbolicExpression],
context: &TypingContext,
) -> TypeResult {
let memo_passed = if let Ok(()) = check_argument_count(4, args) {
true
} else {
check_argument_count(3, args)?;
false
};
check_argument_count(3, args)?;
let amount_type: TypeSignature = TypeSignature::UIntType;
let from_type: TypeSignature = TypeSignature::PrincipalType;
let to_type: TypeSignature = TypeSignature::PrincipalType;
runtime_cost(ClarityCostFunction::AnalysisTypeLookup, checker, 0)?;
checker.type_check_expects(&args[0], context, &amount_type)?;
checker.type_check_expects(&args[1], context, &from_type)?;
checker.type_check_expects(&args[2], context, &to_type)?;
Ok(
TypeSignature::ResponseType(Box::new((TypeSignature::BoolType, TypeSignature::UIntType)))
.into(),
)
}
pub fn check_special_stx_transfer_memo(
checker: &mut TypeChecker,
args: &[SymbolicExpression],
context: &TypingContext,
) -> TypeResult {
check_argument_count(4, args)?;
let amount_type: TypeSignature = TypeSignature::UIntType;
let from_type: TypeSignature = TypeSignature::PrincipalType;
@@ -214,9 +232,7 @@ pub fn check_special_stx_transfer(
checker.type_check_expects(&args[0], context, &amount_type)?;
checker.type_check_expects(&args[1], context, &from_type)?;
checker.type_check_expects(&args[2], context, &to_type)?;
if memo_passed {
checker.type_check_expects(&args[3], context, &memo_type)?;
}
checker.type_check_expects(&args[3], context, &memo_type)?;
Ok(
TypeSignature::ResponseType(Box::new((TypeSignature::BoolType, TypeSignature::UIntType)))

View File

@@ -715,6 +715,9 @@ impl TypedNativeFunction {
.unwrap(),
}))),
StxTransfer => Special(SpecialNativeFunction(&assets::check_special_stx_transfer)),
StxTransferMemo => Special(SpecialNativeFunction(
&assets::check_special_stx_transfer_memo,
)),
GetTokenBalance => Special(SpecialNativeFunction(&assets::check_special_get_balance)),
GetAssetOwner => Special(SpecialNativeFunction(&assets::check_special_get_owner)),
TransferToken => Special(SpecialNativeFunction(&assets::check_special_transfer_token)),

View File

@@ -188,26 +188,31 @@ fn test_stx_ops() {
let good = [
"(stx-burn? u10 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)",
r#"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"#,
r#"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 0x00)"#,
r#"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 0x935699)"#,
r#"(stx-transfer-memo? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 0x0102)"#,
"(stx-get-balance 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)",
];
let expected = [
"(response bool uint)",
"(response bool uint)",
"(response bool uint)",
"(response bool uint)",
"uint",
];
let bad = [
r#"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x7759)"#,
r#"(stx-transfer? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x000000)"#,
r#"(stx-transfer? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
r#"(stx-transfer? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true 0x00)"#,
r#"(stx-transfer? u4 u3 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x00)"#,
r#"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true 0x00)"#,
r#"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true)"#,
"(stx-transfer? u10 tx-sponsor? 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)",
r#"(stx-transfer? u4 u3 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
r#"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true)"#,
r#"(stx-transfer? u10 tx-sponsor? 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"#,
r#"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 0x0102)"#, // valid arguments for stx-transfer-memo
r#"(stx-transfer-memo? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x7759 0x0102)"#,
r#"(stx-transfer-memo? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x0102)"#,
r#"(stx-transfer-memo? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true 0x00) 0x0102"#,
r#"(stx-transfer-memo? u4 u3 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x0102)"#,
r#"(stx-transfer-memo? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true 0x0102)"#,
r#"(stx-transfer-memo? u10 tx-sponsor? 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 0x0102)"#,
r#"(stx-transfer-memo? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"#, // valid arguments for stx-transfer
"(stx-burn? u4)",
"(stx-burn? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
"(stx-burn? u4 true)",
@@ -221,8 +226,15 @@ fn test_stx_ops() {
CheckErrors::IncorrectArgumentCount(3, 5),
CheckErrors::TypeError(PrincipalType, UIntType),
CheckErrors::TypeError(PrincipalType, BoolType),
CheckErrors::TypeError(SequenceType(BufferType(BufferLength(34))), BoolType),
CheckErrors::TypeError(PrincipalType, OptionalType(Box::from(PrincipalType))),
CheckErrors::IncorrectArgumentCount(3, 4),
CheckErrors::TypeError(PrincipalType, SequenceType(BufferType(BufferLength(2)))),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::IncorrectArgumentCount(4, 5),
CheckErrors::TypeError(PrincipalType, UIntType),
CheckErrors::TypeError(PrincipalType, BoolType),
CheckErrors::TypeError(PrincipalType, OptionalType(Box::from(PrincipalType))),
CheckErrors::IncorrectArgumentCount(4, 3),
CheckErrors::IncorrectArgumentCount(2, 1),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::TypeError(PrincipalType, BoolType),

View File

@@ -1728,10 +1728,9 @@ principal isn't materialized, it returns 0.
const STX_TRANSFER: SpecialAPI = SpecialAPI {
input_type: "uint, principal, principal, buff",
output_type: "(response bool uint)",
signature: "(stx-transfer? amount sender recipient memo)",
signature: "(stx-transfer? amount sender recipient)",
description: "`stx-transfer?` is used to increase the STX balance for the `recipient` principal
by debiting the `sender` principal. The `sender` principal _must_ be equal to the current context's `tx-sender`.
The `memo` field is optional, and can be omitted.
This function returns (ok true) if the transfer is successful. In the event of an unsuccessful transfer it returns
one of the following error codes:
@@ -1745,9 +1744,23 @@ one of the following error codes:
(as-contract
(stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)) ;; Returns (ok true)
(as-contract
(stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x00)) ;; Returns (ok true)
(stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)) ;; Returns (ok true)
(as-contract
(stx-transfer? u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR tx-sender 0x00)) ;; Returns (err u4)
(stx-transfer? u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR tx-sender)) ;; Returns (err u4)
"#
};
const STX_TRANSFER_MEMO: SpecialAPI = SpecialAPI {
input_type: "uint, principal, principal, buff",
output_type: "(response bool uint)",
signature: "(stx-transfer-memo? amount sender recipient memo)",
description: "`stx-transfer-memo?` is similar to `stx-transfer?`, except that it adds a `memo` field.
This function returns (ok true) if the transfer is successful, or, on an error, returns the same codes as `stx-transfer?`.
",
example: r#"
(as-contract
(stx-transfer-memo? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x010203)) ;; Returns (ok true)
"#
};
@@ -1867,6 +1880,7 @@ fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
GetStxBalance => make_for_simple_native(&STX_GET_BALANCE, &GetStxBalance, name),
StxGetAccount => make_for_simple_native(&STX_GET_BALANCE, &StxGetAccount, name),
StxTransfer => make_for_special(&STX_TRANSFER, name),
StxTransferMemo => make_for_special(&STX_TRANSFER_MEMO, name),
StxBurn => make_for_simple_native(&STX_BURN, &StxBurn, name),
}
}

View File

@@ -147,24 +147,40 @@ pub fn special_stx_transfer(
env: &mut Environment,
context: &LocalContext,
) -> Result<Value> {
let memo_passed;
if let Ok(()) = check_argument_count(4, args) {
memo_passed = true;
} else {
check_argument_count(3, args)?;
memo_passed = false;
}
check_argument_count(3, args)?;
runtime_cost(ClarityCostFunction::StxTransfer, env, 0)?;
let amount_val = eval(&args[0], env, context)?;
let from_val = eval(&args[1], env, context)?;
let to_val = eval(&args[2], env, context)?;
let memo_val = if memo_passed {
eval(&args[3], env, context)?
let memo_val = Value::Sequence(SequenceData::Buffer(BuffData::empty()));
if let (
Value::Principal(ref from),
Value::Principal(ref to),
Value::UInt(amount),
Value::Sequence(SequenceData::Buffer(ref memo)),
) = (from_val, to_val, amount_val, memo_val)
{
stx_transfer_consolidated(env, from, to, amount, memo)
} else {
Value::Sequence(SequenceData::Buffer(BuffData::empty()))
};
Err(CheckErrors::BadTransferSTXArguments.into())
}
}
pub fn special_stx_transfer_memo(
args: &[SymbolicExpression],
env: &mut Environment,
context: &LocalContext,
) -> Result<Value> {
check_argument_count(4, args)?;
runtime_cost(ClarityCostFunction::Unimplemented, env, 0)?;
let amount_val = eval(&args[0], env, context)?;
let from_val = eval(&args[1], env, context)?;
let to_val = eval(&args[2], env, context)?;
let memo_val = eval(&args[3], env, context)?;
if let (
Value::Principal(ref from),

View File

@@ -144,6 +144,7 @@ define_versioned_named_enum!(NativeFunctions(ClarityVersion) {
BurnAsset("nft-burn?", ClarityVersion::Clarity1),
GetStxBalance("stx-get-balance", ClarityVersion::Clarity1),
StxTransfer("stx-transfer?", ClarityVersion::Clarity1),
StxTransferMemo("stx-transfer-memo?", ClarityVersion::Clarity2),
StxBurn("stx-burn?", ClarityVersion::Clarity1),
StxGetAccount("stx-account", ClarityVersion::Clarity2),
});
@@ -470,6 +471,10 @@ pub fn lookup_reserved_functions(name: &str, version: &ClarityVersion) -> Option
AtBlock => SpecialFunction("special_at_block", &database::special_at_block),
GetStxBalance => SpecialFunction("special_stx_balance", &assets::special_stx_balance),
StxTransfer => SpecialFunction("special_stx_transfer", &assets::special_stx_transfer),
StxTransferMemo => SpecialFunction(
"special_stx_transfer_memo",
&assets::special_stx_transfer_memo,
),
StxBurn => SpecialFunction("special_stx_burn", &assets::special_stx_burn),
StxGetAccount => SpecialFunction("stx_get_account", &assets::special_stx_account),
};

View File

@@ -143,7 +143,8 @@ pub fn get_simple_test(function: &NativeFunctions) -> &'static str {
GetTokenSupply => "(ft-get-supply ft-foo)",
AtBlock => "(at-block 0x55c9861be5cff984a20ce6d99d4aa65941412889bdc665094136429b84f8c2ee 1)", // first stacksblockid
GetStxBalance => "(stx-get-balance 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
StxTransfer => r#"(stx-transfer? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x89995432)"#,
StxTransfer => r#"(stx-transfer? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
StxTransferMemo => r#"(stx-transfer-memo? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x89995432)"#,
StxBurn => "(stx-burn? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
StxGetAccount => "(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
}

View File

@@ -14,23 +14,51 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use crate::clarity_vm::database::MemoryBackingStore;
use chainstate::stacks::events::*;
use std::convert::TryInto;
use vm::analysis::errors::CheckError;
use vm::contexts::{Environment, GlobalContext, OwnedEnvironment};
use vm::errors::{CheckErrors, Error, RuntimeErrorType};
use clarity_vm::clarity::ClarityInstance;
use clarity_vm::database::marf::MarfedKV;
use types::chainstate::{StacksBlockHeader, StacksBlockId};
use types::proof::ClarityMarfTrieId;
use vm::contexts::OwnedEnvironment;
use vm::costs::ExecutionCost;
use vm::database::{NULL_BURN_STATE_DB, NULL_BURN_STATE_DB_2_1, NULL_HEADER_DB};
use vm::tests::execute;
use vm::types::TypeSignature::UIntType;
use vm::types::{AssetIdentifier, PrincipalData, QualifiedContractIdentifier, ResponseData, Value};
use vm::types::{AssetIdentifier, BuffData, QualifiedContractIdentifier, Value};
use core::{FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH};
fn helper_execute(contract: &str, method: &str) -> (Value, Vec<StacksTransactionEvent>) {
let contract_id = QualifiedContractIdentifier::local("contract").unwrap();
let address = "'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR";
let sender = execute(address).expect_principal();
let mut marf_kv = MemoryBackingStore::new();
let mut owned_env = OwnedEnvironment::new(marf_kv.as_clarity_db());
let marf_kv = MarfedKV::temporary();
let mut clarity_instance = ClarityInstance::new(false, marf_kv, ExecutionCost::max_value());
clarity_instance
.begin_test_genesis_block(
&StacksBlockId::sentinel(),
&StacksBlockHeader::make_index_block_hash(
&FIRST_BURNCHAIN_CONSENSUS_HASH,
&FIRST_STACKS_BLOCK_HASH,
),
&NULL_HEADER_DB,
&NULL_BURN_STATE_DB,
)
.commit_block();
let mut marf_kv = clarity_instance.destroy();
let mut store = marf_kv.begin(
&StacksBlockHeader::make_index_block_hash(
&FIRST_BURNCHAIN_CONSENSUS_HASH,
&FIRST_STACKS_BLOCK_HASH,
),
&StacksBlockId([1 as u8; 32]),
);
let mut owned_env = OwnedEnvironment::new_max_limit(
store.as_clarity_db(&NULL_HEADER_DB, &NULL_BURN_STATE_DB_2_1),
);
{
let mut env = owned_env.get_exec_environment(None, None);
@@ -85,7 +113,7 @@ fn test_emit_stx_transfer_ok() {
(define-fungible-token token)
(define-public (emit-event-ok)
(begin
(unwrap-panic (stx-transfer? u10 sender recipient 0x67))
(unwrap-panic (stx-transfer? u10 sender recipient))
(ok u1)))"#;
let (value, mut events) = helper_execute(contract, "emit-event-ok");
@@ -102,6 +130,42 @@ fn test_emit_stx_transfer_ok() {
Value::Principal(data.recipient),
execute("'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G")
);
assert_eq!(data.memo, BuffData { data: vec![] });
}
_ => panic!("assertion failed"),
};
}
#[test]
fn test_emit_stx_transfer_memo_ok() {
let contract = r#"(define-constant sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)
(define-constant recipient 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)
(define-fungible-token token)
(define-public (emit-event-ok)
(begin
(unwrap-panic (stx-transfer-memo? u10 sender recipient 0x010203))
(ok u1)))"#;
let (value, mut events) = helper_execute(contract, "emit-event-ok");
assert_eq!(value, Value::okay(Value::UInt(1)).unwrap());
assert_eq!(events.len(), 1);
match events.pop() {
Some(StacksTransactionEvent::STXEvent(STXEventType::STXTransferEvent(data))) => {
assert_eq!(data.amount, 10u128);
assert_eq!(
Value::Principal(data.sender),
execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR")
);
assert_eq!(
Value::Principal(data.recipient),
execute("'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G")
);
assert_eq!(
data.memo,
BuffData {
data: vec![1, 2, 3]
}
);
}
_ => panic!("assertion failed"),
};
@@ -114,7 +178,7 @@ fn test_emit_stx_transfer_nok() {
(define-fungible-token token)
(define-public (emit-event-nok)
(begin
(unwrap-panic (stx-transfer? u10 sender recipient 0x99))
(unwrap-panic (stx-transfer? u10 sender recipient))
(err u1)))"#;
let (value, events) = helper_execute(contract, "emit-event-nok");

View File

@@ -30,7 +30,7 @@ use vm::tests::execute;
use vm::types::signatures::BufferLength;
use vm::types::{BuffData, QualifiedContractIdentifier, TypeSignature};
use vm::types::{PrincipalData, ResponseData, SequenceData, SequenceSubtype};
use vm::{eval, execute as vm_execute};
use vm::{eval, execute as vm_execute, execute_v2 as vm_execute_v2};
use vm::{CallStack, ContractContext, Environment, GlobalContext, LocalContext, Value};
use crate::types::chainstate::StacksAddress;
@@ -680,9 +680,13 @@ fn test_options_errors() {
fn test_stx_ops_errors() {
let tests = [
r#"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
r#"(stx-transfer? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x000000000000000000000000000000000000000000000000000000000000000000)"#,
r#"(stx-transfer? u4 u3 u2 0x00)"#,
r#"(stx-transfer? u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR true)"#,
r#"(stx-transfer? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
r#"(stx-transfer? u4 u3 u2)"#,
r#"(stx-transfer? true 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"#,
r#"(stx-transfer-memo? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x0102)"#,
r#"(stx-transfer-memo? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x0102)"#,
r#"(stx-transfer-memo? u4 u3 u2 0x0102)"#,
r#"(stx-transfer-memo? true 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x0102)"#,
"(stx-burn? u4)",
"(stx-burn? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
];
@@ -692,12 +696,16 @@ fn test_stx_ops_errors() {
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::IncorrectArgumentCount(4, 3).into(),
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::BadTransferSTXArguments.into(),
CheckErrors::IncorrectArgumentCount(2, 1).into(),
CheckErrors::BadTransferSTXArguments.into(),
];
for (program, expectation) in tests.iter().zip(expectations.iter()) {
assert_eq!(*expectation, vm_execute(program).unwrap_err());
assert_eq!(*expectation, vm_execute_v2(program).unwrap_err());
}
}