Skip to content

Commit

Permalink
add marf proof to contract src, rename marfProof to proof
Browse files Browse the repository at this point in the history
  • Loading branch information
kantai committed Apr 5, 2020
1 parent 5f46bab commit 297de0c
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 44 deletions.
19 changes: 16 additions & 3 deletions docs/rpc-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Returns JSON data in the form:
```
{
"data": "01ce...",
"marfProof": "01ab...",
"proof": "01ab...",
}
```

Expand All @@ -54,7 +54,7 @@ for non-existent values, this is a serialized `none`, and for all other response
object.

This endpoint also accepts a querystring parameter `?proof=` which when supplied `0`, will return the
JSON object _without_ the `marfProof` field.
JSON object _without_ the `proof` field.

### GET /v2/fees/transfer

Expand Down Expand Up @@ -213,7 +213,20 @@ This returns a JSON object of the form:

### GET /v2/contracts/source/[Stacks Address]/[Contract Name]

Fetch the source for a smart contract. Returned as a JSON string.
Fetch the source for a smart contract, along with the block height it was
published in, and the MARF proof for the data.

```
{
"source": "(define-private ...",
"publishHeight": 1,
"proof": "00213..."
}
```

This endpoint also accepts a querystring parameter `?proof=` which
when supplied `0`, will return the JSON object _without_ the `proof`
field.

### POST /v2/contracts/call-read/[Stacks Address]/[Contract Name]/[Function Name]

Expand Down
43 changes: 22 additions & 21 deletions src/net/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,14 +1213,10 @@ impl HttpRequestType {
Ok(HttpRequestType::GetTransferCost(HttpRequestMetadata::from_preamble(preamble)))
}

fn parse_get_account<R: Read>(_protocol: &mut StacksHttp, preamble: &HttpRequestPreamble, captures: &Captures, query: Option<&str>, _fd: &mut R) -> Result<HttpRequestType, net_error> {
if preamble.get_content_length() != 0 {
return Err(net_error::DeserializeError("Invalid Http request: expected 0-length body for GetAccount".to_string()));
}

let principal = PrincipalData::parse(&captures["principal"])
.map_err(|_e| net_error::DeserializeError("Failed to parse account principal".into()))?;

/// check whether the given option query string
/// sets proof=0 (setting proof to false).
/// Defaults to _true_
fn get_proof_query(query: Option<&str>) -> bool {
let no_proof = if let Some(query_string) = query {
form_urlencoded::parse(query_string.as_bytes())
.find(|(key, _v)| key == "proof")
Expand All @@ -1229,7 +1225,19 @@ impl HttpRequestType {
} else {
false
};
let with_proof = !no_proof;

!no_proof
}

fn parse_get_account<R: Read>(_protocol: &mut StacksHttp, preamble: &HttpRequestPreamble, captures: &Captures, query: Option<&str>, _fd: &mut R) -> Result<HttpRequestType, net_error> {
if preamble.get_content_length() != 0 {
return Err(net_error::DeserializeError("Invalid Http request: expected 0-length body for GetAccount".to_string()));
}

let principal = PrincipalData::parse(&captures["principal"])
.map_err(|_e| net_error::DeserializeError("Failed to parse account principal".into()))?;

let with_proof = HttpRequestType::get_proof_query(query);

Ok(HttpRequestType::GetAccount(HttpRequestMetadata::from_preamble(preamble), principal, with_proof))
}
Expand Down Expand Up @@ -1258,15 +1266,7 @@ impl HttpRequestType {
let value = Value::try_deserialize_hex_untyped(&value_hex)
.map_err(|_e| net_error::DeserializeError("Failed to deserialize key value".into()))?;

let no_proof = if let Some(query_string) = query {
form_urlencoded::parse(query_string.as_bytes())
.find(|(key, _v)| key == "proof")
.map(|(_k, value)| value == "0")
.unwrap_or(false)
} else {
false
};
let with_proof = !no_proof;
let with_proof = HttpRequestType::get_proof_query(query);

Ok(HttpRequestType::GetMapEntry(HttpRequestMetadata::from_preamble(preamble), contract_addr, contract_name, map_name, value, with_proof))
}
Expand Down Expand Up @@ -1322,9 +1322,10 @@ impl HttpRequestType {
.map(|(preamble, addr, name)| HttpRequestType::GetContractABI(preamble, addr, name))
}

fn parse_get_contract_source<R: Read>(_protocol: &mut StacksHttp, preamble: &HttpRequestPreamble, captures: &Captures, _query: Option<&str>, _fd: &mut R) -> Result<HttpRequestType, net_error> {
fn parse_get_contract_source<R: Read>(_protocol: &mut StacksHttp, preamble: &HttpRequestPreamble, captures: &Captures, query: Option<&str>, _fd: &mut R) -> Result<HttpRequestType, net_error> {
let with_proof = HttpRequestType::get_proof_query(query);
HttpRequestType::parse_get_contract_arguments(preamble, captures)
.map(|(preamble, addr, name)| HttpRequestType::GetContractSrc(preamble, addr, name))
.map(|(preamble, addr, name)| HttpRequestType::GetContractSrc(preamble, addr, name, with_proof))
}

fn parse_getblock<R: Read>(_protocol: &mut StacksHttp, preamble: &HttpRequestPreamble, captures: &Captures, _query: Option<&str>, _fd: &mut R) -> Result<HttpRequestType, net_error> {
Expand Down Expand Up @@ -1472,7 +1473,7 @@ impl HttpRequestType {
HttpRequestType::GetTransferCost(_md) => "/v2/fees/transfer".into(),
HttpRequestType::GetContractABI(_, contract_addr, contract_name) =>
format!("/v2/contracts/interface/{}/{}", contract_addr, contract_name.as_str()),
HttpRequestType::GetContractSrc(_, contract_addr, contract_name) =>
HttpRequestType::GetContractSrc(_, contract_addr, contract_name, _with_proof) =>
format!("/v2/contracts/source/{}/{}", contract_addr, contract_name.as_str()),
HttpRequestType::CallReadOnlyFunction(_, contract_addr, contract_name, _, func_name, ..) => {
format!("/v2/contracts/call-read/{}/{}/{}", contract_addr, contract_name.as_str(), func_name.as_str())
Expand Down
16 changes: 13 additions & 3 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,12 +828,22 @@ pub struct HttpRequestMetadata {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MapEntryResponse {
pub data: String,
#[serde(rename = "marfProof")]
#[serde(rename = "proof")]
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub marf_proof: Option<String>
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContractSrcResponse {
pub source: String,
#[serde(rename = "publishHeight")]
pub publish_height: u32,
#[serde(rename = "proof")]
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub marf_proof: Option<String>
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CallReadOnlyResponse {
Expand Down Expand Up @@ -913,7 +923,7 @@ pub enum HttpRequestType {
CallReadOnlyFunction(HttpRequestMetadata, StacksAddress, ContractName,
PrincipalData, ClarityName, Vec<Value>),
GetTransferCost(HttpRequestMetadata),
GetContractSrc(HttpRequestMetadata, StacksAddress, ContractName),
GetContractSrc(HttpRequestMetadata, StacksAddress, ContractName, bool),
GetContractABI(HttpRequestMetadata, StacksAddress, ContractName),
}

Expand Down Expand Up @@ -977,7 +987,7 @@ pub enum HttpResponseType {
CallReadOnlyFunction(HttpResponseMetadata, CallReadOnlyResponse),
GetAccount(HttpResponseMetadata, AccountEntryResponse),
GetContractABI(HttpResponseMetadata, ContractInterface),
GetContractSrc(HttpResponseMetadata, String),
GetContractSrc(HttpResponseMetadata, ContractSrcResponse),
// peer-given error responses
BadRequest(HttpResponseMetadata, String),
BadRequestJSON(HttpResponseMetadata, HashMap<String, String>),
Expand Down
24 changes: 18 additions & 6 deletions src/net/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ use net::HttpRequestType;
use net::HttpResponseType;
use net::HttpRequestMetadata;
use net::HttpResponseMetadata;
use net::{ MapEntryResponse, AccountEntryResponse, CallReadOnlyResponse };
use net::PeerAddress;
use net::PeerInfoData;
use net::NeighborAddress;
Expand All @@ -48,6 +47,7 @@ use net::connection::ConnectionHttp;
use net::connection::ReplyHandleHttp;
use net::connection::ConnectionOptions;
use net::db::PeerDB;
use net::{ MapEntryResponse, AccountEntryResponse, CallReadOnlyResponse, ContractSrcResponse };

use burnchains::Burnchain;
use burnchains::BurnchainView;
Expand Down Expand Up @@ -81,7 +81,9 @@ use vm::{
types::{ PrincipalData,
QualifiedContractIdentifier },
database::{ ClarityDatabase,
ClaritySerializable },
MarfedKV,
ClaritySerializable,
marf::ContractCommitment },
};

use rand::prelude::*;
Expand Down Expand Up @@ -461,13 +463,23 @@ impl ConversationHttp {

fn handle_get_contract_src<W: Write>(http: &mut StacksHttp, fd: &mut W, req: &HttpRequestType,
chainstate: &mut StacksChainState, cur_burn: &BurnchainHeaderHash, cur_block: &BlockHeaderHash,
contract_addr: &StacksAddress, contract_name: &ContractName) -> Result<(), net_error> {
contract_addr: &StacksAddress, contract_name: &ContractName, with_proof: bool) -> Result<(), net_error> {
let response_metadata = HttpResponseMetadata::from(req);
let contract_identifier = QualifiedContractIdentifier::new(contract_addr.clone().into(), contract_name.clone());

let data = chainstate.with_read_only_clarity_tx(cur_burn, cur_block, |clarity_tx| {
clarity_tx.with_clarity_db_readonly(|db| {
db.get_contract_src(&contract_identifier)
let source = db.get_contract_src(&contract_identifier)?;
let contract_commit_key = MarfedKV::make_contract_hash_key(&contract_identifier);
let (contract_commit, proof) = db.get_with_proof::<ContractCommitment>(&contract_commit_key)
.expect("BUG: obtained source, but couldn't get MARF proof.");
let marf_proof = if with_proof {
Some(proof.to_hex())
} else {
None
};
let publish_height = contract_commit.block_height;
Some(ContractSrcResponse { source, publish_height, marf_proof })
})
});

Expand Down Expand Up @@ -606,10 +618,10 @@ impl ConversationHttp {
}
None
},
HttpRequestType::GetContractSrc(ref _md, ref contract_addr, ref contract_name) => {
HttpRequestType::GetContractSrc(ref _md, ref contract_addr, ref contract_name, ref with_proof) => {
if let Some((burn_block, block)) = ConversationHttp::handle_load_stacks_chain_tip(&mut self.connection.protocol, &mut reply, &req, burndb)? {
ConversationHttp::handle_get_contract_src(&mut self.connection.protocol, &mut reply, &req, chainstate, &burn_block, &block,
contract_addr, contract_name)?;
contract_addr, contract_name, *with_proof)?;
}
None
},
Expand Down
14 changes: 9 additions & 5 deletions src/vm/database/marf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::path::PathBuf;

use vm::types::{QualifiedContractIdentifier};
use vm::errors::{InterpreterError, CheckErrors, InterpreterResult as Result, IncomparableError, RuntimeErrorType};
use vm::database::{SqliteConnection, ClarityDatabase, HeadersDB, NULL_HEADER_DB};
use vm::database::{SqliteConnection, ClarityDatabase, HeadersDB, NULL_HEADER_DB,
ClaritySerializable, ClarityDeserializable};
use vm::analysis::{AnalysisDatabase};
use chainstate::stacks::index::marf::MARF;
use chainstate::stacks::index::{MARFValue, Error as MarfError, TrieHash};
Expand Down Expand Up @@ -99,16 +100,19 @@ pub trait ClarityBackingStore {
}
}

struct ContractCommitment {
pub struct ContractCommitment {
pub hash: Sha512Trunc256Sum,
pub block_height: u32
}

impl ContractCommitment {
pub fn serialize(&self) -> String {
impl ClaritySerializable for ContractCommitment {
fn serialize(&self) -> String {
format!("{}{}", self.hash, to_hex(&self.block_height.to_be_bytes()))
}
pub fn deserialize(input: &str) -> ContractCommitment {
}

impl ClarityDeserializable<ContractCommitment> for ContractCommitment {
fn deserialize(input: &str) -> ContractCommitment {
assert_eq!(input.len(), 72);
let hash = Sha512Trunc256Sum::from_hex(&input[0..64]).expect("Hex decode fail.");
let height_bytes = hex_bytes(&input[64..72]).expect("Hex decode fail.");
Expand Down
23 changes: 17 additions & 6 deletions src/vm/tests/integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use chainstate::stacks::{
use chainstate::burn::VRFSeed;
use burnchains::Address;
use address::AddressHashMode;
use net::{Error as NetError, StacksMessageCodec, AccountEntryResponse, CallReadOnlyRequestBody};
use net::{Error as NetError, StacksMessageCodec, AccountEntryResponse, ContractSrcResponse, CallReadOnlyRequestBody};
use util::{log, strings::StacksString, hash::hex_bytes, hash::to_hex};
use std::collections::HashMap;
use util::db::{DBConn, FromRow};
Expand Down Expand Up @@ -359,7 +359,7 @@ fn integration_test_get_info() {
let result_data = Value::try_deserialize_hex_untyped(&res["data"]).unwrap();
let expected_data = chain_state.clarity_eval_read_only(bhh, &contract_identifier,
"(some (get-exotic-data-info u1))");
assert!(res.get("marfProof").is_some());
assert!(res.get("proof").is_some());

assert_eq!(result_data, expected_data);

Expand Down Expand Up @@ -389,7 +389,7 @@ fn integration_test_get_info() {
.send()
.unwrap().json::<HashMap<String, String>>().unwrap();

assert!(res.get("marfProof").is_none());
assert!(res.get("proof").is_none());
let result_data = Value::try_deserialize_hex_untyped(&res["data"]).unwrap();
let expected_data = chain_state.clarity_eval_read_only(bhh, &contract_identifier,
"(some (get-exotic-data-info u1))");
Expand All @@ -410,7 +410,7 @@ fn integration_test_get_info() {
.send()
.unwrap().json::<HashMap<String, String>>().unwrap();

assert!(res.get("marfProof").is_some());
assert!(res.get("proof").is_some());
let result_data = Value::try_deserialize_hex_untyped(&res["data"]).unwrap();
let expected_data = chain_state.clarity_eval_read_only(bhh, &contract_identifier,
"(some (get-exotic-data-info u1))");
Expand Down Expand Up @@ -505,9 +505,20 @@ fn integration_test_get_info() {

let path = format!("{}/v2/contracts/source/{}/{}", &http_origin, &contract_addr, "get-info");
eprintln!("Test: GET {}", path);
let res = client.get(&path).send().unwrap().json::<String>().unwrap();
let res = client.get(&path).send().unwrap().json::<ContractSrcResponse>().unwrap();

assert_eq!(res, GET_INFO_CONTRACT);
assert_eq!(res.source, GET_INFO_CONTRACT);
assert_eq!(res.publish_height, 2);
assert!(res.marf_proof.is_some());


let path = format!("{}/v2/contracts/source/{}/{}?proof=0", &http_origin, &contract_addr, "get-info");
eprintln!("Test: GET {}", path);
let res = client.get(&path).send().unwrap().json::<ContractSrcResponse>().unwrap();

assert_eq!(res.source, GET_INFO_CONTRACT);
assert_eq!(res.publish_height, 2);
assert!(res.marf_proof.is_none());

// a missing one?

Expand Down

0 comments on commit 297de0c

Please sign in to comment.