re-enable docs generation, tests

This commit is contained in:
Aaron Blankstein
2022-02-03 14:43:47 -06:00
parent 32bb7203d9
commit 84b4369485
5 changed files with 405 additions and 395 deletions

View File

@@ -1,4 +1,3 @@
//use chainstate::stacks::boot::STACKS_BOOT_CODE_MAINNET;
use vm::analysis::{mem_type_check, ContractAnalysis};
use vm::docs::{get_input_type_string, get_output_type_string, get_signature};
use vm::types::{FunctionType, Value};
@@ -6,17 +5,17 @@ use vm::types::{FunctionType, Value};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::iter::FromIterator;
//use vm::database::MemoryBackingStore;
use crate::types::StacksEpochId;
use crate::vm::contexts::GlobalContext;
use crate::vm::costs::LimitedCostTracker;
use crate::vm::database::MemoryBackingStore;
use crate::vm::types::QualifiedContractIdentifier;
use crate::vm::{self, ContractContext};
const DOCS_GENERATION_EPOCH: StacksEpochId = StacksEpochId::Epoch2_05;
#[derive(Serialize)]
struct ContractRef {
pub struct ContractRef {
public_functions: Vec<FunctionRef>,
read_only_functions: Vec<FunctionRef>,
error_codes: Vec<ErrorCode>,
@@ -39,145 +38,9 @@ struct ErrorCode {
value: String,
}
struct ContractSupportDocs {
descriptions: HashMap<&'static str, &'static str>,
skip_func_display: HashSet<&'static str>,
}
const STACKS_BOOT_CODE_MAINNET: [(&'static str, &'static str); 0] = [];
fn make_contract_support_docs() -> HashMap<&'static str, ContractSupportDocs> {
let pox_descriptions = vec![
("disallow-contract-caller", "Revokes authorization from a contract to invoke stacking methods through contract-calls"),
("allow-contract-caller", "Give a contract-caller authorization to call stacking methods. Normally, stacking methods may
only be invoked by _direct_ transactions (i.e., the `tx-sender` issues a direct `contract-call` to the stacking methods).
By issuing an allowance, the tx-sender may call through the allowed contract."),
("stack-stx", "Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX).
The STX will be locked for the given number of reward cycles (lock-period).
This is the self-service interface. tx-sender will be the Stacker.
* The given stacker cannot currently be stacking.
* You will need the minimum uSTX threshold. This isn't determined until the reward cycle begins, but this
method still requires stacking over the _absolute minimum_ amount, which can be obtained by calling `get-stacking-minimum`.
* The pox-addr argument must represent a valid reward address. Right now, this must be a Bitcoin
p2pkh or p2sh address. It cannot be a native Segwit address, but it may be a p2wpkh-p2sh or p2wsh-p2sh address.
The tokens will unlock and be returned to the Stacker (tx-sender) automatically."),
("revoke-delegate-stx", "Revoke a Stacking delegate relationship. A particular Stacker may only have one delegate,
so this method does not take any parameters, and just revokes the Stacker's current delegate (if one exists)."),
("delegate-stx", "Delegate to `delegate-to` the ability to stack from a given address.
This method _does not_ lock the funds, rather, it allows the delegate to issue the stacking lock.
The caller specifies:
* amount-ustx: the total amount of ustx the delegate may be allowed to lock
* until-burn-ht: an optional burn height at which this delegation expiration
* pox-addr: an optional p2pkh or p2sh address to which any rewards *must* be sent"),
("delegate-stack-stx", "As a delegate, stack the given principal's STX using `partial-stacked-by-cycle`.
Once the delegate has stacked > minimum, the delegate should call `stack-aggregation-commit`."),
("stack-aggregation-commit", "Commit partially stacked STX.
This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions,
so long as:
1. The pox-addr is the same.
2. This \"commit\" transaction is called _before_ the PoX anchor block.
This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold,
but does not require it be all locked up within a single transaction"),
("reject-pox", "Reject Stacking for this reward cycle.
`tx-sender` votes all its uSTX for rejection.
Note that unlike Stacking, rejecting PoX does not lock the tx-sender's tokens: PoX rejection acts like a coin vote."),
("can-stack-stx", "Evaluate if a participant can stack an amount of STX for a given period."),
("get-stacking-minimum", "Returns the absolute minimum amount that could be validly Stacked (the threshold to Stack in
a given reward cycle may be higher than this"),
("get-pox-rejection", "Returns the amount of uSTX that a given principal used to reject a PoX cycle."),
("is-pox-active", "Returns whether or not PoX has been rejected at a given PoX cycle."),
("get-stacker-info", "Returns the _current_ stacking information for `stacker. If the information
is expired, or if there's never been such a stacker, then returns none."),
("get-total-ustx-stacked", "Returns the amount of currently participating uSTX in the given cycle."),
("get-pox-info", "Returns information about PoX status.")
];
let bns_descriptions = vec![
("namespace-preorder", "Registers the salted hash of the namespace with BNS nodes, and burns the requisite amount of cryptocurrency. Additionally, this step proves to the BNS nodes that user has honored the BNS consensus rules by including a recent consensus hash in the transaction. Returns pre-order's expiration date (in blocks)."),
("namespace-reveal", "Reveals the salt and the namespace ID (after a namespace preorder). It reveals how long names last in this namespace before they expire or must be renewed, and it sets a price function for the namespace that determines how cheap or expensive names its will be.\
All of the parameters prefixed by `p` make up the `price function`. These parameters govern the pricing and lifetime of names in the namespace.
The rules for a namespace are as follows:
* a name can fall into one of 16 buckets, measured by length. Bucket 16 incorporates all names at least 16 characters long.
* the pricing structure applies a multiplicative penalty for having numeric characters, or punctuation characters.
* the price of a name in a bucket is `((coeff) * (base) ^ (bucket exponent)) / ((numeric discount multiplier) * (punctuation discount multiplier))`
Example:
* base = 10
* coeff = 2
* nonalpha discount: 10
* no-vowel discount: 10
* buckets 1, 2: 9
* buckets 3, 4, 5, 6: 8
* buckets 7, 8, 9, 10, 11, 12, 13, 14: 7
* buckets 15, 16+:"),
("name-import", "Imports name to a revealed namespace. Each imported name is given both an owner and some off-chain state."),
("namespace-ready", "Launches the namespace and makes it available to the public. Once a namespace is launched, anyone can register a name in it if they pay the appropriate amount of cryptocurrency."),
("name-preorder", "Preorders a name by telling all BNS nodes the salted hash of the BNS name. It pays the registration fee to the namespace owner's designated address."),
("name-register", "Reveals the salt and the name to all BNS nodes, and assigns the name an initial public key hash and zone file hash."),
("name-update", "Changes the name's zone file hash. You would send a name update transaction if you wanted to change the name's zone file contents. For example, you would do this if you want to deploy your own Gaia hub and want other people to read from it."),
("name-transfer", "Changes the name's public key hash. You would send a name transfer transaction if you wanted to:
* Change your private key
* Send the name to someone else or
* Update your zone file
When transferring a name, you have the option to also clear the name's zone file hash (i.e. set it to null). This is useful for when you send the name to someone else, so the recipient's name does not resolve to your zone file."),
("name-revoke", "Makes a name unresolvable. The BNS consensus rules stipulate that once a name is revoked, no one can change its public key hash or its zone file hash. The name's zone file hash is set to null to prevent it from resolving. You should only do this if your private key is compromised, or if you want to render your name unusable for whatever reason."),
("name-renewal", "Depending in the namespace rules, a name can expire. For example, names in the .id namespace expire after 2 years. You need to send a name renewal every so often to keep your name.
You will pay the registration cost of your name to the namespace's designated burn address when you renew it.
When a name expires, it enters a \"grace period\". The period is set to 5000 blocks (a month) but can be configured for each namespace.
It will stop resolving in the grace period, and all of the above operations will cease to be honored by the BNS consensus rules.
You may, however, send a NAME_RENEWAL during this grace period to preserve your name. After the grace period, everybody can register that name again.
If your name is in a namespace where names do not expire, then you never need to use this transaction."),
("get-namespace-price", "Gets the price for a namespace."),
("get-name-price", "Gets the price for a name."),
("can-namespace-be-registered", "Returns true if the provided namespace is available."),
("is-name-lease-expired", "Return true if the provided name lease is expired."),
("can-name-be-registered", "Returns true if the provided name can be registered."),
("name-resolve", "Get name registration details."),
("get-namespace-properties", "Get namespace properties."),
("can-receive-name", "Returns true if the provided name can be received. That is, if it is not curretly owned, a previous lease is expired, and the name wasn't revoked."),
("get-name", "Returns a response with the username that belongs to the user if any, otherwise an error ERROR_NOT_FOUND is returned."),
("resolve-principal", "Returns the registered name that a principal owns if there is one. A principal can only own one name at a time.")
];
let pox_skip_display = vec![
"set-burnchain-parameters",
"minimal-can-stack-stx",
"get-reward-set-size",
"get-reward-set-pox-address",
];
let bns_skip_display = vec![
"namespace-update-function-price",
"namespace-revoke-function-price-edition",
"check-name-ops-preconditions",
"is-name-in-grace-period",
];
HashMap::from_iter(vec![
(
"pox",
ContractSupportDocs {
descriptions: HashMap::from_iter(pox_descriptions.into_iter()),
skip_func_display: HashSet::from_iter(pox_skip_display.into_iter()),
},
),
(
"bns",
ContractSupportDocs {
descriptions: HashMap::from_iter(bns_descriptions.into_iter()),
skip_func_display: HashSet::from_iter(bns_skip_display.into_iter()),
},
),
])
pub struct ContractSupportDocs {
pub descriptions: HashMap<&'static str, &'static str>,
pub skip_func_display: HashSet<&'static str>,
}
fn make_func_ref(func_name: &str, func_type: &FunctionType, description: &str) -> FunctionRef {
@@ -202,8 +65,6 @@ fn get_constant_value(var_name: &str, contract_content: &str) -> Value {
}
fn doc_execute(program: &str) -> Result<Option<Value>, vm::Error> {
use vm::database::clarity_store::NullBackingStore as MemoryBackingStore;
let contract_id = QualifiedContractIdentifier::transient();
let mut contract_context = ContractContext::new(contract_id.clone());
let mut marf = MemoryBackingStore::new();
@@ -220,117 +81,98 @@ fn doc_execute(program: &str) -> Result<Option<Value>, vm::Error> {
})
}
fn produce_docs() -> BTreeMap<String, ContractRef> {
pub fn make_docs(content: &str, support_docs: &ContractSupportDocs) -> ContractRef {
let (_, contract_analysis) =
mem_type_check(content).expect("BUG: failed to type check boot contract");
let ContractAnalysis {
public_function_types,
read_only_function_types,
variable_types,
..
} = contract_analysis;
let public_functions: Vec<_> = public_function_types
.iter()
.filter(|(func_name, _)| !support_docs.skip_func_display.contains(func_name.as_str()))
.map(|(func_name, func_type)| {
let description = support_docs
.descriptions
.get(func_name.as_str())
.expect(&format!("BUG: no description for {}", func_name.as_str()));
make_func_ref(func_name, func_type, description)
})
.collect();
let read_only_functions: Vec<_> = read_only_function_types
.iter()
.filter(|(func_name, _)| !support_docs.skip_func_display.contains(func_name.as_str()))
.map(|(func_name, func_type)| {
let description = support_docs
.descriptions
.get(func_name.as_str())
.expect(&format!("BUG: no description for {}", func_name.as_str()));
make_func_ref(func_name, func_type, description)
})
.collect();
let ecode_names = variable_types
.iter()
.filter_map(|(var_name, _)| {
if var_name.starts_with("ERR_") {
Some(format!("{}: {}", var_name.as_str(), var_name.as_str()))
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ");
let ecode_to_eval = format!("{}\n {{ {} }}", content, ecode_names);
let ecode_result = doc_execute(&ecode_to_eval)
.expect("BUG: failed to evaluate contract for constant value")
.expect("BUG: failed to return constant value")
.expect_tuple();
let error_codes = variable_types
.iter()
.filter_map(|(var_name, type_signature)| {
if var_name.starts_with("ERR_") {
let value = ecode_result
.get(var_name)
.expect("BUG: failed to fetch tuple entry from ecode output")
.to_string();
Some(ErrorCode {
name: var_name.to_string(),
value,
value_type: type_signature.to_string(),
})
} else {
None
}
})
.collect();
ContractRef {
public_functions,
read_only_functions,
error_codes,
}
}
/// Produce a set of documents for multiple contracts, supplied as a list of `(contract_name, contract_content)` pairs,
/// and a map from `contract_name` to corresponding `ContractSupportDocs`
pub fn produce_docs_refs<A: AsRef<str>, B: AsRef<str>>(
contracts: &[(A, B)],
support_docs: &HashMap<&str, ContractSupportDocs>,
) -> BTreeMap<String, ContractRef> {
let mut docs = BTreeMap::new();
let support_docs = make_contract_support_docs();
for (contract_name, content) in STACKS_BOOT_CODE_MAINNET.iter() {
let (_, contract_analysis) =
mem_type_check(content).expect("BUG: failed to type check boot contract");
for (contract_name, content) in contracts.iter() {
if let Some(contract_support) = support_docs.get(contract_name.as_ref()) {
let contract_ref = make_docs(content.as_ref(), contract_support);
if let Some(contract_support) = support_docs.get(*contract_name) {
let ContractAnalysis {
public_function_types,
read_only_function_types,
variable_types,
..
} = contract_analysis;
let public_functions: Vec<_> = public_function_types
.iter()
.filter(|(func_name, _)| {
!contract_support
.skip_func_display
.contains(func_name.as_str())
})
.map(|(func_name, func_type)| {
let description = contract_support
.descriptions
.get(func_name.as_str())
.expect(&format!("BUG: no description for {}", func_name.as_str()));
make_func_ref(func_name, func_type, description)
})
.collect();
let read_only_functions: Vec<_> = read_only_function_types
.iter()
.filter(|(func_name, _)| {
!contract_support
.skip_func_display
.contains(func_name.as_str())
})
.map(|(func_name, func_type)| {
let description = contract_support
.descriptions
.get(func_name.as_str())
.expect(&format!("BUG: no description for {}", func_name.as_str()));
make_func_ref(func_name, func_type, description)
})
.collect();
let ecode_names = variable_types
.iter()
.filter_map(|(var_name, _)| {
if var_name.starts_with("ERR_") {
Some(format!("{}: {}", var_name.as_str(), var_name.as_str()))
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ");
let ecode_to_eval = format!("{}\n {{ {} }}", content, ecode_names);
let ecode_result = doc_execute(&ecode_to_eval)
.expect("BUG: failed to evaluate contract for constant value")
.expect("BUG: failed to return constant value")
.expect_tuple();
let error_codes = variable_types
.iter()
.filter_map(|(var_name, type_signature)| {
if var_name.starts_with("ERR_") {
let value = ecode_result
.get(var_name)
.expect("BUG: failed to fetch tuple entry from ecode output")
.to_string();
Some(ErrorCode {
name: var_name.to_string(),
value,
value_type: type_signature.to_string(),
})
} else {
None
}
})
.collect();
docs.insert(
contract_name.to_string(),
ContractRef {
public_functions,
read_only_functions,
error_codes,
},
);
docs.insert(contract_name.as_ref().to_string(), contract_ref);
}
}
docs
}
pub fn make_json_boot_contracts_reference() -> String {
let api_out = produce_docs();
format!(
"{}",
serde_json::to_string(&api_out).expect("Failed to serialize documentation")
)
}
#[cfg(test)]
mod tests {
use vm::docs::contracts::make_json_boot_contracts_reference;
#[test]
fn test_make_boot_contracts_reference() {
make_json_boot_contracts_reference();
}
}

View File

@@ -1806,9 +1806,12 @@ mod test {
use super::make_all_api_reference;
use super::make_json_api_reference;
use crate::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash};
use crate::types::chainstate::{SortitionId, StacksAddress, StacksBlockId};
use crate::{types::chainstate::VRFSeed, vm::StacksEpoch};
use crate::{
types::chainstate::{BlockHeaderHash, BurnchainHeaderHash},
vm::database::{ClarityDatabase, MemoryBackingStore},
};
use vm::analysis::type_check;
use vm::costs::ExecutionCost;
@@ -1816,6 +1819,12 @@ mod test {
struct DocHeadersDB {}
const DOC_HEADER_DB: DocHeadersDB = DocHeadersDB {};
impl MemoryBackingStore {
pub fn as_docs_clarity_db<'a>(&'a mut self) -> ClarityDatabase<'a> {
ClarityDatabase::new(self, &DOC_HEADER_DB, &DOC_POX_STATE_DB)
}
}
impl HeadersDB for DocHeadersDB {
fn get_burn_header_hash_for_block(
&self,
@@ -1886,79 +1895,73 @@ mod test {
}
}
// fn docs_execute(marf: &mut MarfedKV, program: &str) {
// // start the next block,
// // we never commit it so that we can reuse the initialization
// let mut store = marf.begin(&StacksBlockId([0; 32]), &StacksBlockId([1; 32]));
fn docs_execute(store: &mut MemoryBackingStore, program: &str) {
// execute the program, iterating at each ";; Returns" comment
// there are maybe more rust-y ways of doing this, but this is the simplest.
let mut segments = vec![];
let mut current_segment: String = "".into();
for line in program.lines() {
current_segment.push_str(line);
current_segment.push_str("\n");
if line.contains(";;") && line.contains("Returns ") {
segments.push(current_segment);
current_segment = "".into();
}
}
if current_segment.len() > 0 {
segments.push(current_segment);
}
// // execute the program, iterating at each ";; Returns" comment
// // there are maybe more rust-y ways of doing this, but this is the simplest.
// let mut segments = vec![];
// let mut current_segment: String = "".into();
// for line in program.lines() {
// current_segment.push_str(line);
// current_segment.push_str("\n");
// if line.contains(";;") && line.contains("Returns ") {
// segments.push(current_segment);
// current_segment = "".into();
// }
// }
// if current_segment.len() > 0 {
// segments.push(current_segment);
// }
let contract_id = QualifiedContractIdentifier::local("docs-test").unwrap();
// let contract_id = QualifiedContractIdentifier::local("docs-test").unwrap();
{
let mut analysis_db = store.as_analysis_db();
let whole_contract = segments.join("\n");
eprintln!("{}", whole_contract);
let mut parsed = ast::build_ast(&contract_id, &whole_contract, &mut ())
.unwrap()
.expressions;
// {
// let mut analysis_db = store.as_analysis_db();
// let whole_contract = segments.join("\n");
// eprintln!("{}", whole_contract);
// let mut parsed = ast::build_ast(&contract_id, &whole_contract, &mut ())
// .unwrap()
// .expressions;
type_check(&contract_id, &mut parsed, &mut analysis_db, false)
.expect("Failed to type check");
}
// type_check(&contract_id, &mut parsed, &mut analysis_db, false)
// .expect("Failed to type check");
// }
let conn = store.as_docs_clarity_db();
let mut contract_context = ContractContext::new(contract_id.clone());
let mut global_context = GlobalContext::new(
false,
conn,
LimitedCostTracker::new_free(),
StacksEpochId::Epoch2_05,
);
// let conn = store.as_clarity_db(&DOC_HEADER_DB, &DOC_POX_STATE_DB);
// let mut contract_context = ContractContext::new(contract_id.clone());
// let mut global_context = GlobalContext::new(
// false,
// conn,
// LimitedCostTracker::new_free(),
// StacksEpochId::Epoch2_05,
// );
global_context
.execute(|g| {
for segment in segments.iter() {
let expected = if segment.contains("Returns ") {
let expects_start = segment.rfind("Returns ").unwrap() + "Returns ".len();
Some(segment[expects_start..].trim().to_string())
} else {
None
};
// global_context
// .execute(|g| {
// for segment in segments.iter() {
// let expected = if segment.contains("Returns ") {
// let expects_start = segment.rfind("Returns ").unwrap() + "Returns ".len();
// Some(segment[expects_start..].trim().to_string())
// } else {
// None
// };
eprintln!("{}", segment);
// eprintln!("{}", segment);
let result = {
let parsed = ast::build_ast(&contract_id, segment, &mut ())
.unwrap()
.expressions;
eval_all(&parsed, &mut contract_context, g).unwrap()
};
// let result = {
// let parsed = ast::build_ast(&contract_id, segment, &mut ())
// .unwrap()
// .expressions;
// eval_all(&parsed, &mut contract_context, g).unwrap()
// };
// if let Some(expected) = expected {
// assert_eq!(expected, result.unwrap().to_string());
// }
// }
// Ok(())
// })
// .unwrap();
// store.rollback_block();
// }
if let Some(expected) = expected {
assert_eq!(expected, result.unwrap().to_string());
}
}
Ok(())
})
.unwrap();
}
#[test]
fn ensure_docgen_runs() {
@@ -1967,90 +1970,95 @@ mod test {
make_json_api_reference();
}
// #[test]
// fn test_examples() {
// let apis = make_all_api_reference();
// let mut marf = MarfedKV::temporary();
// // first, load the samples for contract-call
// // and give the doc environment's contract some STX
// {
// let mut store = marf.begin(&StacksBlockId::sentinel(), &StacksBlockId([0; 32]));
// let contract_id = QualifiedContractIdentifier::local("tokens").unwrap();
// let trait_def_id = QualifiedContractIdentifier::parse(
// "SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a",
// )
// .unwrap();
#[test]
fn test_examples() {
let apis = make_all_api_reference();
let token_contract_content = include_str!("../../../../sample-contracts/tokens.clar");
for func_api in apis.functions.iter() {
if func_api.name == "at-block" {
eprintln!("Skipping at-block, because it cannot be evaluated without a MARF");
continue;
}
if func_api.name == "get-block-info?" {
eprintln!(
"Skipping get-block-info?, because it cannot be evaluated without a MARF"
);
continue;
}
// {
// let mut analysis_db = store.as_analysis_db();
// let whole_contract =
// std::fs::read_to_string("sample-contracts/tokens.clar").unwrap();
// let mut parsed = ast::build_ast(&contract_id, &whole_contract, &mut ())
// .unwrap()
// .expressions;
let mut store = MemoryBackingStore::new();
// first, load the samples for contract-call
// and give the doc environment's contract some STX
{
let contract_id = QualifiedContractIdentifier::local("tokens").unwrap();
let trait_def_id = QualifiedContractIdentifier::parse(
"SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a",
)
.unwrap();
// type_check(&contract_id, &mut parsed, &mut analysis_db, true)
// .expect("Failed to type check sample-contracts/tokens");
// }
{
let mut analysis_db = store.as_analysis_db();
let mut parsed = ast::build_ast(&contract_id, &token_contract_content, &mut ())
.unwrap()
.expressions;
// {
// let mut analysis_db = store.as_analysis_db();
// let mut parsed =
// ast::build_ast(&trait_def_id, super::DEFINE_TRAIT_API.example, &mut ())
// .unwrap()
// .expressions;
type_check(&contract_id, &mut parsed, &mut analysis_db, true)
.expect("Failed to type check sample-contracts/tokens");
}
// type_check(&trait_def_id, &mut parsed, &mut analysis_db, true)
// .expect("Failed to type check sample-contracts/tokens");
// }
{
let mut analysis_db = store.as_analysis_db();
let mut parsed =
ast::build_ast(&trait_def_id, super::DEFINE_TRAIT_API.example, &mut ())
.unwrap()
.expressions;
// let conn = store.as_clarity_db(&DOC_HEADER_DB, &DOC_POX_STATE_DB);
// let docs_test_id = QualifiedContractIdentifier::local("docs-test").unwrap();
// let docs_principal_id = PrincipalData::Contract(docs_test_id);
// let mut env = OwnedEnvironment::new(conn);
// let balance = STXBalance::initial(1000);
// env.execute_in_env::<_, _, ()>(
// QualifiedContractIdentifier::local("tokens").unwrap().into(),
// |e| {
// let mut snapshot = e
// .global_context
// .database
// .get_stx_balance_snapshot_genesis(&docs_principal_id);
// snapshot.set_balance(balance);
// snapshot.save();
// e.global_context
// .database
// .increment_ustx_liquid_supply(100000)
// .unwrap();
// Ok(())
// },
// )
// .unwrap();
type_check(&trait_def_id, &mut parsed, &mut analysis_db, true)
.expect("Failed to type check sample-contracts/tokens");
}
// env.initialize_contract(
// contract_id,
// &std::fs::read_to_string("sample-contracts/tokens.clar").unwrap(),
// )
// .unwrap();
let conn = store.as_docs_clarity_db();
let docs_test_id = QualifiedContractIdentifier::local("docs-test").unwrap();
let docs_principal_id = PrincipalData::Contract(docs_test_id);
let mut env = OwnedEnvironment::new(conn);
let balance = STXBalance::initial(1000);
env.execute_in_env::<_, _, ()>(
QualifiedContractIdentifier::local("tokens").unwrap().into(),
|e| {
let mut snapshot = e
.global_context
.database
.get_stx_balance_snapshot_genesis(&docs_principal_id);
snapshot.set_balance(balance);
snapshot.save();
e.global_context
.database
.increment_ustx_liquid_supply(100000)
.unwrap();
Ok(())
},
)
.unwrap();
// env.initialize_contract(trait_def_id, super::DEFINE_TRAIT_API.example)
// .unwrap();
// store.test_commit();
// }
env.initialize_contract(contract_id, &token_contract_content)
.unwrap();
// for func_api in apis.functions.iter() {
// let example = &func_api.example;
// let without_throws: String = example
// .lines()
// .filter(|x| !x.contains(";; Throws"))
// .collect::<Vec<_>>()
// .join("\n");
// let the_throws = example.lines().filter(|x| x.contains(";; Throws"));
// docs_execute(&mut marf, &without_throws);
// for expect_err in the_throws {
// eprintln!("{}", expect_err);
// execute(expect_err).unwrap_err();
// }
// }
// }
env.initialize_contract(trait_def_id, super::DEFINE_TRAIT_API.example)
.unwrap();
}
let example = &func_api.example;
let without_throws: String = example
.lines()
.filter(|x| !x.contains(";; Throws"))
.collect::<Vec<_>>()
.join("\n");
let the_throws = example.lines().filter(|x| x.contains(";; Throws"));
docs_execute(&mut store, &without_throws);
for expect_err in the_throws {
eprintln!("{}", expect_err);
execute(expect_err).unwrap_err();
}
}
}
}

View File

@@ -0,0 +1,158 @@
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
use super::STACKS_BOOT_CODE_MAINNET;
use clarity::vm::docs::contracts::{produce_docs_refs, ContractSupportDocs};
fn make_contract_support_docs() -> HashMap<&'static str, ContractSupportDocs> {
let pox_descriptions = vec![
("disallow-contract-caller", "Revokes authorization from a contract to invoke stacking methods through contract-calls"),
("allow-contract-caller", "Give a contract-caller authorization to call stacking methods. Normally, stacking methods may
only be invoked by _direct_ transactions (i.e., the `tx-sender` issues a direct `contract-call` to the stacking methods).
By issuing an allowance, the tx-sender may call through the allowed contract."),
("stack-stx", "Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX).
The STX will be locked for the given number of reward cycles (lock-period).
This is the self-service interface. tx-sender will be the Stacker.
* The given stacker cannot currently be stacking.
* You will need the minimum uSTX threshold. This isn't determined until the reward cycle begins, but this
method still requires stacking over the _absolute minimum_ amount, which can be obtained by calling `get-stacking-minimum`.
* The pox-addr argument must represent a valid reward address. Right now, this must be a Bitcoin
p2pkh or p2sh address. It cannot be a native Segwit address, but it may be a p2wpkh-p2sh or p2wsh-p2sh address.
The tokens will unlock and be returned to the Stacker (tx-sender) automatically."),
("revoke-delegate-stx", "Revoke a Stacking delegate relationship. A particular Stacker may only have one delegate,
so this method does not take any parameters, and just revokes the Stacker's current delegate (if one exists)."),
("delegate-stx", "Delegate to `delegate-to` the ability to stack from a given address.
This method _does not_ lock the funds, rather, it allows the delegate to issue the stacking lock.
The caller specifies:
* amount-ustx: the total amount of ustx the delegate may be allowed to lock
* until-burn-ht: an optional burn height at which this delegation expiration
* pox-addr: an optional p2pkh or p2sh address to which any rewards *must* be sent"),
("delegate-stack-stx", "As a delegate, stack the given principal's STX using `partial-stacked-by-cycle`.
Once the delegate has stacked > minimum, the delegate should call `stack-aggregation-commit`."),
("stack-aggregation-commit", "Commit partially stacked STX.
This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions,
so long as:
1. The pox-addr is the same.
2. This \"commit\" transaction is called _before_ the PoX anchor block.
This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold,
but does not require it be all locked up within a single transaction"),
("reject-pox", "Reject Stacking for this reward cycle.
`tx-sender` votes all its uSTX for rejection.
Note that unlike Stacking, rejecting PoX does not lock the tx-sender's tokens: PoX rejection acts like a coin vote."),
("can-stack-stx", "Evaluate if a participant can stack an amount of STX for a given period."),
("get-stacking-minimum", "Returns the absolute minimum amount that could be validly Stacked (the threshold to Stack in
a given reward cycle may be higher than this"),
("get-pox-rejection", "Returns the amount of uSTX that a given principal used to reject a PoX cycle."),
("is-pox-active", "Returns whether or not PoX has been rejected at a given PoX cycle."),
("get-stacker-info", "Returns the _current_ stacking information for `stacker. If the information
is expired, or if there's never been such a stacker, then returns none."),
("get-total-ustx-stacked", "Returns the amount of currently participating uSTX in the given cycle."),
("get-pox-info", "Returns information about PoX status.")
];
let bns_descriptions = vec![
("namespace-preorder", "Registers the salted hash of the namespace with BNS nodes, and burns the requisite amount of cryptocurrency. Additionally, this step proves to the BNS nodes that user has honored the BNS consensus rules by including a recent consensus hash in the transaction. Returns pre-order's expiration date (in blocks)."),
("namespace-reveal", "Reveals the salt and the namespace ID (after a namespace preorder). It reveals how long names last in this namespace before they expire or must be renewed, and it sets a price function for the namespace that determines how cheap or expensive names its will be.\
All of the parameters prefixed by `p` make up the `price function`. These parameters govern the pricing and lifetime of names in the namespace.
The rules for a namespace are as follows:
* a name can fall into one of 16 buckets, measured by length. Bucket 16 incorporates all names at least 16 characters long.
* the pricing structure applies a multiplicative penalty for having numeric characters, or punctuation characters.
* the price of a name in a bucket is `((coeff) * (base) ^ (bucket exponent)) / ((numeric discount multiplier) * (punctuation discount multiplier))`
Example:
* base = 10
* coeff = 2
* nonalpha discount: 10
* no-vowel discount: 10
* buckets 1, 2: 9
* buckets 3, 4, 5, 6: 8
* buckets 7, 8, 9, 10, 11, 12, 13, 14: 7
* buckets 15, 16+:"),
("name-import", "Imports name to a revealed namespace. Each imported name is given both an owner and some off-chain state."),
("namespace-ready", "Launches the namespace and makes it available to the public. Once a namespace is launched, anyone can register a name in it if they pay the appropriate amount of cryptocurrency."),
("name-preorder", "Preorders a name by telling all BNS nodes the salted hash of the BNS name. It pays the registration fee to the namespace owner's designated address."),
("name-register", "Reveals the salt and the name to all BNS nodes, and assigns the name an initial public key hash and zone file hash."),
("name-update", "Changes the name's zone file hash. You would send a name update transaction if you wanted to change the name's zone file contents. For example, you would do this if you want to deploy your own Gaia hub and want other people to read from it."),
("name-transfer", "Changes the name's public key hash. You would send a name transfer transaction if you wanted to:
* Change your private key
* Send the name to someone else or
* Update your zone file
When transferring a name, you have the option to also clear the name's zone file hash (i.e. set it to null). This is useful for when you send the name to someone else, so the recipient's name does not resolve to your zone file."),
("name-revoke", "Makes a name unresolvable. The BNS consensus rules stipulate that once a name is revoked, no one can change its public key hash or its zone file hash. The name's zone file hash is set to null to prevent it from resolving. You should only do this if your private key is compromised, or if you want to render your name unusable for whatever reason."),
("name-renewal", "Depending in the namespace rules, a name can expire. For example, names in the .id namespace expire after 2 years. You need to send a name renewal every so often to keep your name.
You will pay the registration cost of your name to the namespace's designated burn address when you renew it.
When a name expires, it enters a \"grace period\". The period is set to 5000 blocks (a month) but can be configured for each namespace.
It will stop resolving in the grace period, and all of the above operations will cease to be honored by the BNS consensus rules.
You may, however, send a NAME_RENEWAL during this grace period to preserve your name. After the grace period, everybody can register that name again.
If your name is in a namespace where names do not expire, then you never need to use this transaction."),
("get-namespace-price", "Gets the price for a namespace."),
("get-name-price", "Gets the price for a name."),
("can-namespace-be-registered", "Returns true if the provided namespace is available."),
("is-name-lease-expired", "Return true if the provided name lease is expired."),
("can-name-be-registered", "Returns true if the provided name can be registered."),
("name-resolve", "Get name registration details."),
("get-namespace-properties", "Get namespace properties."),
("can-receive-name", "Returns true if the provided name can be received. That is, if it is not curretly owned, a previous lease is expired, and the name wasn't revoked."),
("get-name", "Returns a response with the username that belongs to the user if any, otherwise an error ERROR_NOT_FOUND is returned."),
("resolve-principal", "Returns the registered name that a principal owns if there is one. A principal can only own one name at a time.")
];
let pox_skip_display = vec![
"set-burnchain-parameters",
"minimal-can-stack-stx",
"get-reward-set-size",
"get-reward-set-pox-address",
];
let bns_skip_display = vec![
"namespace-update-function-price",
"namespace-revoke-function-price-edition",
"check-name-ops-preconditions",
"is-name-in-grace-period",
];
HashMap::from_iter(vec![
(
"pox",
ContractSupportDocs {
descriptions: HashMap::from_iter(pox_descriptions.into_iter()),
skip_func_display: HashSet::from_iter(pox_skip_display.into_iter()),
},
),
(
"bns",
ContractSupportDocs {
descriptions: HashMap::from_iter(bns_descriptions.into_iter()),
skip_func_display: HashSet::from_iter(bns_skip_display.into_iter()),
},
),
])
}
pub fn make_json_boot_contracts_reference() -> String {
let contract_supporting_docs = make_contract_support_docs();
let api_out = produce_docs_refs(&*STACKS_BOOT_CODE_MAINNET, &contract_supporting_docs);
format!(
"{}",
serde_json::to_string(&api_out).expect("Failed to serialize documentation")
)
}
#[cfg(test)]
mod tests {
use super::make_json_boot_contracts_reference;
#[test]
fn test_make_boot_contracts_reference() {
make_json_boot_contracts_reference();
}
}

View File

@@ -64,6 +64,8 @@ const BOOT_CODE_GENESIS: &'static str = std::include_str!("genesis.clar");
pub const COSTS_1_NAME: &'static str = "costs";
pub const COSTS_2_NAME: &'static str = "costs-2";
pub mod docs;
lazy_static! {
static ref BOOT_CODE_POX_MAINNET: String =
format!("{}\n{}", BOOT_CODE_POX_MAINNET_CONSTS, BOOT_CODE_POX_BODY);

View File

@@ -721,7 +721,7 @@ simulating a miner.
if argv[1] == "docgen_boot" {
println!(
"{}",
blockstack_lib::clarity::vm::docs::contracts::make_json_boot_contracts_reference()
blockstack_lib::chainstate::stacks::boot::docs::make_json_boot_contracts_reference()
);
return;
}