Skip to content

Commit 5b70def

Browse files
authored
Merge branch 'develop' into feat/events-for-microblocks
2 parents 1caf53d + f6697f3 commit 5b70def

File tree

3 files changed

+128
-27
lines changed

3 files changed

+128
-27
lines changed

src/net/inv.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,7 +1467,7 @@ impl PeerNetwork {
14671467
};
14681468

14691469
debug!(
1470-
"{:?}: Send GetPoxInv to {:?} for {} rewad cycles starting at {} ({})",
1470+
"{:?}: Send GetPoxInv to {:?} for {} reward cycles starting at {} ({})",
14711471
&self.local_peer,
14721472
nk,
14731473
num_reward_cycles,
@@ -1999,7 +1999,7 @@ impl PeerNetwork {
19991999
true,
20002000
);
20012001

2002-
debug!("{:?}: {:?} has {} new blocks and {} new microblocks (total {} blocks, {} microblocks, {} sortitions): {:?}",
2002+
debug!("{:?}: {:?} has {} new blocks and {} new microblocks (total {} blocks, {} microblocks, {} sortitions): {:?}",
20032003
&self.local_peer, &nk, new_blocks, new_microblocks, stats.inv.num_blocks(), stats.inv.num_microblock_streams(), stats.inv.num_sortitions, &stats.inv);
20042004

20052005
if new_blocks > 0 || new_microblocks > 0 {

src/net/rpc.rs

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,13 @@ use util::hash::{hex_bytes, to_hex};
8989
use vm::database::clarity_store::make_contract_hash_key;
9090
use vm::types::TraitIdentifier;
9191
use vm::{
92+
analysis::errors::CheckErrors,
9293
costs::{ExecutionCost, LimitedCostTracker},
9394
database::{
9495
clarity_store::ContractCommitment, ClarityDatabase, ClaritySerializable, STXBalance,
9596
},
9697
errors::Error as ClarityRuntimeError,
98+
errors::Error::Unchecked,
9799
errors::InterpreterError,
98100
types::{PrincipalData, QualifiedContractIdentifier, StandardPrincipalData},
99101
ClarityName, ContractName, SymbolicExpression, Value,
@@ -1200,22 +1202,28 @@ impl ConversationHttp {
12001202
.map(|x| SymbolicExpression::atom_value(x.clone()))
12011203
.collect();
12021204
let mainnet = chainstate.mainnet;
1205+
let mut cost_limit = options.read_only_call_limit.clone();
1206+
cost_limit.write_length = 0;
1207+
cost_limit.write_count = 0;
1208+
12031209
let data_opt_res =
12041210
chainstate.maybe_read_only_clarity_tx(&sortdb.index_conn(), tip, |clarity_tx| {
12051211
let cost_track = clarity_tx
12061212
.with_clarity_db_readonly(|clarity_db| {
1207-
LimitedCostTracker::new_mid_block(
1208-
mainnet,
1209-
options.read_only_call_limit.clone(),
1210-
clarity_db,
1211-
)
1213+
LimitedCostTracker::new_mid_block(mainnet, cost_limit, clarity_db)
12121214
})
12131215
.map_err(|_| {
12141216
ClarityRuntimeError::from(InterpreterError::CostContractLoadFailure)
12151217
})?;
12161218

12171219
clarity_tx.with_readonly_clarity_env(mainnet, sender.clone(), cost_track, |env| {
1218-
env.execute_contract(&contract_identifier, function.as_str(), &args, true)
1220+
// we want to execute any function as long as no actual writes are made as
1221+
// opposed to be limited to purely calling `define-read-only` functions,
1222+
// so use `read_only = false`. This broadens the number of functions that
1223+
// can be called, and also circumvents limitations on `define-read-only`
1224+
// functions that can not use `contrac-call?`, even when calling other
1225+
// read-only functions
1226+
env.execute_contract(&contract_identifier, function.as_str(), &args, false)
12191227
})
12201228
});
12211229

@@ -1228,14 +1236,28 @@ impl ConversationHttp {
12281236
cause: None,
12291237
},
12301238
),
1231-
Ok(Some(Err(e))) => HttpResponseType::CallReadOnlyFunction(
1232-
response_metadata,
1233-
CallReadOnlyResponse {
1234-
okay: false,
1235-
result: None,
1236-
cause: Some(e.to_string()),
1237-
},
1238-
),
1239+
Ok(Some(Err(e))) => match e {
1240+
Unchecked(CheckErrors::CostBalanceExceeded(actual_cost, _))
1241+
if actual_cost.write_count > 0 =>
1242+
{
1243+
HttpResponseType::CallReadOnlyFunction(
1244+
response_metadata,
1245+
CallReadOnlyResponse {
1246+
okay: false,
1247+
result: None,
1248+
cause: Some("NotReadOnly".to_string()),
1249+
},
1250+
)
1251+
}
1252+
_ => HttpResponseType::CallReadOnlyFunction(
1253+
response_metadata,
1254+
CallReadOnlyResponse {
1255+
okay: false,
1256+
result: None,
1257+
cause: Some(e.to_string()),
1258+
},
1259+
),
1260+
},
12391261
Ok(None) | Err(_) => {
12401262
HttpResponseType::NotFound(response_metadata, "Chain tip not found".into())
12411263
}
@@ -2867,7 +2889,7 @@ mod test {
28672889
(define-public (set-bar (x int) (y int))
28682890
(begin (var-set bar (/ x y)) (ok (var-get bar))))
28692891
(define-public (add-unit)
2870-
(begin
2892+
(begin
28712893
(map-set unit-map { account: tx-sender } { units: 1 } )
28722894
(ok 1)))
28732895
(begin

testnet/stacks-node/src/tests/integrations.rs

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use stacks::vm::{
2020
mem_type_check,
2121
},
2222
database::ClaritySerializable,
23-
types::{QualifiedContractIdentifier, TupleData},
23+
types::{QualifiedContractIdentifier, ResponseData, TupleData},
2424
Value,
2525
};
2626

@@ -33,6 +33,21 @@ use super::{
3333
SK_3,
3434
};
3535

36+
const OTHER_CONTRACT: &'static str = "
37+
(define-data-var x uint u0)
38+
(define-public (f1)
39+
(ok (var-get x)))
40+
(define-public (f2 (val uint))
41+
(ok (var-set x val)))
42+
";
43+
44+
const CALL_READ_CONTRACT: &'static str = "
45+
(define-public (public-no-write)
46+
(ok (contract-call? .other f1)))
47+
(define-public (public-write)
48+
(ok (contract-call? .other f2 u5)))
49+
";
50+
3651
const GET_INFO_CONTRACT: &'static str = "
3752
(define-map block-data
3853
{ height: uint }
@@ -105,14 +120,14 @@ const GET_INFO_CONTRACT: &'static str = "
105120
(begin
106121
(unwrap-panic (inner-update-info (- block-height u2)))
107122
(inner-update-info (- block-height u1))))
108-
123+
109124
(define-trait trait-1 (
110125
(foo-exec (int) (response int int))))
111126
112127
(define-trait trait-2 (
113128
(get-1 (uint) (response uint uint))
114129
(get-2 (uint) (response uint uint))))
115-
130+
116131
(define-trait trait-3 (
117132
(fn-1 (uint) (response uint uint))
118133
(fn-2 (uint) (response uint uint))))
@@ -123,7 +138,7 @@ const IMPL_TRAIT_CONTRACT: &'static str = "
123138
(impl-trait .get-info.trait-1)
124139
(define-private (test-height) burn-block-height)
125140
(define-public (foo-exec (a int)) (ok 1))
126-
141+
127142
;; implicit trait compliance for trait-2
128143
(define-public (get-1 (x uint)) (ok u1))
129144
(define-public (get-2 (x uint)) (ok u1))
@@ -172,9 +187,30 @@ fn integration_test_get_info() {
172187

173188
if round == 1 {
174189
// block-height = 2
190+
eprintln!("Tenure in 1 started!");
175191
let publish_tx =
176192
make_contract_publish(&contract_sk, 0, 0, "get-info", GET_INFO_CONTRACT);
177-
eprintln!("Tenure in 1 started!");
193+
tenure
194+
.mem_pool
195+
.submit_raw(
196+
&mut chainstate_copy,
197+
&consensus_hash,
198+
&header_hash,
199+
publish_tx,
200+
)
201+
.unwrap();
202+
let publish_tx = make_contract_publish(&contract_sk, 1, 0, "other", OTHER_CONTRACT);
203+
tenure
204+
.mem_pool
205+
.submit_raw(
206+
&mut chainstate_copy,
207+
&consensus_hash,
208+
&header_hash,
209+
publish_tx,
210+
)
211+
.unwrap();
212+
let publish_tx =
213+
make_contract_publish(&contract_sk, 2, 0, "main", CALL_READ_CONTRACT);
178214
tenure
179215
.mem_pool
180216
.submit_raw(
@@ -188,7 +224,7 @@ fn integration_test_get_info() {
188224
// block-height = 3
189225
let publish_tx = make_contract_publish(
190226
&contract_sk,
191-
1,
227+
3,
192228
0,
193229
"impl-trait-contract",
194230
IMPL_TRAIT_CONTRACT,
@@ -255,8 +291,8 @@ fn integration_test_get_info() {
255291
let blocks = StacksChainState::list_blocks(&chain_state.db()).unwrap();
256292
assert!(chain_tip.metadata.block_height == 2);
257293

258-
// Block #1 should have 3 txs
259-
assert!(chain_tip.block.txs.len() == 3);
294+
// Block #1 should have 5 txs
295+
assert!(chain_tip.block.txs.len() == 5);
260296

261297
let parent = chain_tip.block.header.parent_block;
262298
let bhh = &chain_tip.metadata.index_block_hash();
@@ -467,7 +503,7 @@ fn integration_test_get_info() {
467503
eprintln!("Test: GET {}", path);
468504
let res = client.get(&path).send().unwrap().json::<AccountEntryResponse>().unwrap();
469505
assert_eq!(u128::from_str_radix(&res.balance[2..], 16).unwrap(), 0);
470-
assert_eq!(res.nonce, 2);
506+
assert_eq!(res.nonce, 4);
471507
assert!(res.nonce_proof.is_some());
472508
assert!(res.balance_proof.is_some());
473509

@@ -581,6 +617,49 @@ fn integration_test_get_info() {
581617
"(get-exotic-data-info u3)");
582618
assert_eq!(result_data, expected_data);
583619

620+
// how about a non read-only function call which does not modify anything
621+
let path = format!("{}/v2/contracts/call-read/{}/{}/{}", &http_origin, &contract_addr, "main", "public-no-write");
622+
eprintln!("Test: POST {}", path);
623+
624+
let body = CallReadOnlyRequestBody {
625+
sender: "'SP139Q3N9RXCJCD1XVA4N5RYWQ5K9XQ0T9PKQ8EE5".into(),
626+
arguments: vec![]
627+
};
628+
629+
let res = client.post(&path)
630+
.json(&body)
631+
.send()
632+
.unwrap().json::<serde_json::Value>().unwrap();
633+
assert!(res.get("cause").is_none());
634+
assert!(res["okay"].as_bool().unwrap());
635+
636+
let result_data = Value::try_deserialize_hex_untyped(&res["result"].as_str().unwrap()[2..]).unwrap();
637+
let expected_data = Value::Response(ResponseData {
638+
committed: true,
639+
data: Box::new(Value::Response(ResponseData {
640+
committed: true,
641+
data: Box::new(Value::UInt(0))
642+
}))
643+
});
644+
assert_eq!(result_data, expected_data);
645+
646+
// how about a non read-only function call which does modify something and should fail
647+
let path = format!("{}/v2/contracts/call-read/{}/{}/{}", &http_origin, &contract_addr, "main", "public-write");
648+
eprintln!("Test: POST {}", path);
649+
650+
let body = CallReadOnlyRequestBody {
651+
sender: "'SP139Q3N9RXCJCD1XVA4N5RYWQ5K9XQ0T9PKQ8EE5".into(),
652+
arguments: vec![]
653+
};
654+
655+
let res = client.post(&path)
656+
.json(&body)
657+
.send()
658+
.unwrap().json::<serde_json::Value>().unwrap();
659+
assert!(res.get("cause").is_some());
660+
assert!(!res["okay"].as_bool().unwrap());
661+
assert!(res["cause"].as_str().unwrap().contains("NotReadOnly"));
662+
584663
// let's try a call with a url-encoded string.
585664
let path = format!("{}/v2/contracts/call-read/{}/{}/{}", &http_origin, &contract_addr, "get-info",
586665
"get-exotic-data-info%3F");
@@ -636,7 +715,7 @@ fn integration_test_get_info() {
636715
.send()
637716
.unwrap().json::<serde_json::Value>().unwrap();
638717

639-
eprintln!("{}", res["cause"].as_str().unwrap());
718+
eprintln!("{:#?}", res["cause"].as_str().unwrap());
640719
assert!(res.get("result").is_none());
641720
assert!(!res["okay"].as_bool().unwrap());
642721
assert!(res["cause"].as_str().unwrap().contains("NotReadOnly"));

0 commit comments

Comments
 (0)