Skip to content

Commit f8e817c

Browse files
authored
Merge pull request #6195 from fdefelici/feat/cli-contract-call-from-file
feat: add `--hex-file <file_path>` option to blockstack-cli contract-call
2 parents f6583be + 9ba26bc commit f8e817c

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1414
- `stacks_node_miner_stop_reason_total`: Counts the number of times the miner stopped mining due to various reasons.
1515
- Always report the number of transactions mined in the last attempt, even if there were 0
1616

17+
- Added a new option `--hex-file <file_path>` to `blockstack-cli contract-call` command, that allows to pass a serialized Clarity value by file.
1718
- Added a new option `--postcondition-mode [allow, deny]` to `blockstack-cli publish` command, to set the post-condition mode to allow or deny on the transaction (default is deny)
1819

1920
### Changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stackslib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ rstest_reuse = "0.5.0"
9292
mutants = "0.0.3"
9393
rlimit = "0.10.2"
9494
chrono = "0.4.19"
95+
tempfile = "3.3"
9596

9697
[features]
9798
default = []

stackslib/src/blockstack_cli.rs

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,18 @@ Arguments are supplied in one of two ways: through script evaluation or via hex
112112
of the value serialization format. The method for supplying arguments is chosen by
113113
prefacing each argument with a flag:
114114
115-
-e indicates the argument should be _evaluated_
116-
-x indicates the argument that a serialized Clarity value is being passed (hex-serialized)
115+
-e indicates the argument should be _evaluated_
116+
-x indicates the argument that a serialized Clarity value is being passed (hex-serialized)
117+
--hex-file <file_path> same as `-x`, but reads the serialized Clarity value from a file
117118
118119
e.g.,
119120
120121
blockstack-cli contract-call $secret_key 10 0 SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4 foo-contract \\
121122
transfer-fookens -e \\'SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4 \\
122123
-e \"(+ 1 2)\" \\
123124
-x 0000000000000000000000000000000001 \\
124-
-x 050011deadbeef11ababffff11deadbeef11ababffff
125+
-x 050011deadbeef11ababffff11deadbeef11ababffff \\
126+
--hex-file /path/to/value.hex
125127
";
126128

127129
const TOKEN_TRANSFER_USAGE: &str = "blockstack-cli (options) token-transfer [origin-secret-key-hex] [fee-rate] [nonce] [recipient-address] [amount] [memo] [args...]
@@ -606,7 +608,7 @@ fn handle_contract_call(
606608

607609
if val_args.len() % 2 != 0 {
608610
return Err(
609-
"contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` pairs"
611+
"contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` or `--hex-file <file_path>` pairs"
610612
.into(),
611613
);
612614
}
@@ -624,8 +626,16 @@ fn handle_contract_call(
624626
vm_execute(input, clarity_version)?
625627
.ok_or("Supplied argument did not evaluate to a Value")?
626628
},
629+
"--hex-file" => {
630+
let content = fs::read_to_string(input)
631+
.map_err(|e| {
632+
let err_msg = format!("Cannot read file: {input}. Reason: {e}");
633+
CliError::Message(err_msg)
634+
})?;
635+
Value::try_deserialize_hex_untyped(&content)?
636+
}
627637
_ => {
628-
return Err("contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` pairs".into())
638+
return Err("contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` or `--hex-file <file_path>` pairs".into())
629639
}
630640
};
631641

@@ -1053,6 +1063,7 @@ mod test {
10531063

10541064
use blockstack_lib::chainstate::stacks::TransactionPostCondition;
10551065
use stacks_common::util::cargo_workspace;
1066+
use tempfile::NamedTempFile;
10561067

10571068
use super::*;
10581069

@@ -1574,6 +1585,106 @@ mod test {
15741585
);
15751586
}
15761587

1588+
#[test]
1589+
fn test_contract_call_with_serialized_arg_from_file_ok() {
1590+
let mut file = NamedTempFile::new().expect("Cannot create tempfile!");
1591+
write!(file, "0000000000000000000000000000000001").expect("Cannot Write to temp file");
1592+
1593+
let file_path = file.path().to_str().unwrap();
1594+
let cc_args = [
1595+
"contract-call",
1596+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1597+
"1",
1598+
"0",
1599+
"SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4",
1600+
"foo-contract",
1601+
"transfer-fookens",
1602+
"--hex-file",
1603+
file_path,
1604+
];
1605+
1606+
let result = main_handler(to_string_vec(&cc_args));
1607+
assert!(result.is_ok(), "Result should be ok!");
1608+
1609+
let expected_tx = "0000000001040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000000000000000000001010011db0868db0cd44c463b3a8a8b3b428ddaad15661e7b7d8c92c814c142c526e30abffe74e1e098f517037a1ee74969f4db27630407f4c958cb0d6e1d7485fe06030200000000021625a2a51cf0712a9d228e2788e2fe7acf8917ec810c666f6f2d636f6e7472616374107472616e736665722d666f6f6b656e73000000010000000000000000000000000000000001";
1610+
assert_eq!(expected_tx, result.unwrap());
1611+
}
1612+
1613+
#[test]
1614+
fn test_contract_call_with_serialized_arg_from_file_fails_due_to_file() {
1615+
let file_path = "/tmp/this-file-not-exists";
1616+
let cc_args = [
1617+
"contract-call",
1618+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1619+
"1",
1620+
"0",
1621+
"SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4",
1622+
"foo-contract",
1623+
"transfer-fookens",
1624+
"--hex-file",
1625+
file_path,
1626+
];
1627+
1628+
let result = main_handler(to_string_vec(&cc_args));
1629+
assert!(result.is_err(), "Result should be err!");
1630+
1631+
let expected_msg = format!("Cannot read file: {}. Reason: ", file_path);
1632+
assert!(result.unwrap_err().to_string().starts_with(&expected_msg));
1633+
}
1634+
1635+
#[test]
1636+
fn test_contract_call_with_serialized_arg_from_file_fails_due_to_bad_hex() {
1637+
let mut file = NamedTempFile::new().expect("Cannot create tempfile!");
1638+
// Bad hex string but (good except for the \n)
1639+
write!(file, "0000000000000000000000000000000001\n").expect("Cannot Write to temp file");
1640+
let file_path = file.path().to_str().unwrap();
1641+
1642+
let cc_args = [
1643+
"contract-call",
1644+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1645+
"1",
1646+
"0",
1647+
"SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4",
1648+
"foo-contract",
1649+
"transfer-fookens",
1650+
"--hex-file",
1651+
&file_path,
1652+
];
1653+
1654+
let result = main_handler(to_string_vec(&cc_args));
1655+
assert!(result.is_err(), "Result should be err!");
1656+
1657+
let expected_msg = "Failed to deserialize: Deserialization error: Bad hex string";
1658+
assert_eq!(expected_msg, result.unwrap_err().to_string());
1659+
}
1660+
1661+
#[test]
1662+
fn test_contract_call_with_serialized_arg_from_file_fails_due_to_short_buffer() {
1663+
let mut file = NamedTempFile::new().expect("Cannot create tempfile!");
1664+
// hex buffer is short
1665+
write!(file, "0101").expect("Cannot Write to temp file");
1666+
let file_path = file.path().to_str().unwrap();
1667+
1668+
let cc_args = [
1669+
"contract-call",
1670+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1671+
"1",
1672+
"0",
1673+
"SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4",
1674+
"foo-contract",
1675+
"transfer-fookens",
1676+
"--hex-file",
1677+
&file_path,
1678+
];
1679+
1680+
let result = main_handler(to_string_vec(&cc_args));
1681+
assert!(result.is_err(), "Result should be err!");
1682+
1683+
let expected_msg =
1684+
"Failed to deserialize: Serialization error caused by IO: failed to fill whole buffer";
1685+
assert_eq!(expected_msg, result.unwrap_err().to_string());
1686+
}
1687+
15771688
#[test]
15781689
fn simple_addresses() {
15791690
let addr_args = [

0 commit comments

Comments
 (0)