mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-06-18 03:38:57 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -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)",
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user