print!() is failing in execute_query whereas it works out of it - elrond

I am doing some rust tests in the elrond blockchain.
When I print a token identifier outside of execute_query, my token is well printed. Whereas, it throws an error when I try to print it in the execute_query.
#[test]
fn test_foo() {
let mut setup = utils::setup(equip_penguin::contract_obj);
let token = TokenIdentifier::<DebugApi>::from_esdt_bytes(b"ITEM-a1a1a1");
// works
println!("{:x?}", token);
let b_wrapper = &mut setup.blockchain_wrapper;
let _ = b_wrapper.execute_query(&setup.cf_wrapper, |sc| {
// throw errors
println!("{:x?}", token);
});
}
The error is
thread 'build_url_with_one_item' panicked at 'called `Option::unwrap()` on a `None` value', /home/username/.cargo/registry/src/github.com-1ecc6299db9ec823/elrond-wasm-debug-0.27.4/src/tx_mock/tx_managed_types.rs:38:31
The utils::setup used in the above snippet code from this doc https://docs.elrond.com/developers/developer-reference/rust-testing-framework/
How does this error happens?

Okay, managed types must be declared inside execute_query.
The above code works:
#[test]
fn test_foo() {
let mut setup = utils::setup(equip_penguin::contract_obj);
let b_wrapper = &mut setup.blockchain_wrapper;
let _ = b_wrapper.execute_query(&setup.cf_wrapper, |sc| {
let token = TokenIdentifier::<DebugApi>::from_esdt_bytes(b"ITEM-a1a1a1");
println!("{:x?}", token);
});
}

Related

Cannot set name of NFT in Elrond Rust testing framework

I am writing tests for my smart contract using elrond-wasm 0.32.0.
My issue is that my smart contract returns the identifier of the ESDT token when I am getting the name.
Detailed steps
I am setting my ESDT token's name like so:
blockchain_wrapper.set_nft_balance_all_properties::<Empty>(
&cf_wrapper.address_ref(),
&ITEM_TO_EQUIP_ID,
ITEM_TO_EQUIP_NONCE,
&rust_biguint!(2u64),
&Empty,
0,
Option::Some(&owner_address),
Option::Some(ITEM_TO_EQUIP_NAME),
Option::Some(b""),
&[],
);
Then, I get my ESDT token's name :
blockchain_wrapper
.execute_query(&cf_wrapper, |sc| {
let data = sc.blockchain().get_esdt_token_data(
&ManagedAddress::from_address(&cf_wrapper.address_ref()),
&TokenIdentifier::from_esdt_bytes(ITEM_TO_EQUIP_ID),
ITEM_TO_EQUIP_NONCE,
);
// data.name equals ITEM_TO_EQUIP_ID, so it fail
assert_eq!(data.name, ManagedBuffer::new_from_bytes(ITEM_TO_EQUIP_NAME));
})
.assert_ok();
Did I forget something, or is it a bug?
Complete snippet
#[test]
fn set_name_test() {
const WASM_PATH: &'static str = "sc-customize-nft/output/customize_nft.wasm";
const ITEM_TO_EQUIP_ID: &[u8] = b"ITEM-a1a1a1";
const ITEM_TO_EQUIP_NAME: &[u8] = b"item name";
const ITEM_TO_EQUIP_NONCE: u64 = 1;
let rust_zero = rust_biguint!(0u64);
let mut blockchain_wrapper = BlockchainStateWrapper::new();
let owner_address = blockchain_wrapper.create_user_account(&rust_zero);
let cf_wrapper = blockchain_wrapper.create_sc_account(
&rust_zero,
Some(&owner_address),
customize_nft::contract_obj,
WASM_PATH,
);
// deploy contract
blockchain_wrapper
.execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| {
let result = sc.init(managed_token_id!(PENGUIN_TOKEN_ID));
assert_eq!(result, SCResult::Ok(()));
})
.assert_ok();
blockchain_wrapper.add_mandos_set_account(cf_wrapper.address_ref());
blockchain_wrapper.set_nft_balance_all_properties::<Empty>(
&cf_wrapper.address_ref(),
&ITEM_TO_EQUIP_ID,
ITEM_TO_EQUIP_NONCE,
&rust_biguint!(2u64),
&Empty,
0,
Option::Some(&owner_address),
Option::Some(ITEM_TO_EQUIP_NAME),
Option::Some(b""),
&[],
);
blockchain_wrapper
.execute_query(&cf_wrapper, |sc| {
let data = sc.blockchain().get_esdt_token_data(
&ManagedAddress::from_address(&cf_wrapper.address_ref()),
&TokenIdentifier::from_esdt_bytes(ITEM_TO_EQUIP_ID),
ITEM_TO_EQUIP_NONCE,
);
println!("Name is {:?}", data.name);
assert_eq!(data.name, ManagedBuffer::new_from_bytes(ITEM_TO_EQUIP_NAME));
})
.assert_ok();
}
I got an answer from an Elrond Developer:
It's a bug in the mock. It's being set properly, but the get_esdt_token_data mock just returns the token ID instead of the name from metadata. Thanks for reporting!

How can I speed up the process of parsing a 40Gb txt file and inserting a row, per line, into an sqlite db

Background
I'm currently trying to parse a .txt com zone file. It is structured like so
blahblah.com xx xx examplens.com
Currently my code is structured like so
extern crate regex;
use regex::Regex;
use rusqlite::{params, Connection, Result};
#[derive(Debug)]
struct Domain {
id: i32,
domain: String,
}
use std::io::stdin;
fn main() -> std::io::Result<()> {
let mut reader = my_reader::BufReader::open("data/com_practice.txt")?;
let mut buffer = String::new();
let conn: Connection = Connection::open("/Users/alex/Projects/domain_randomizer/data/domains.db").unwrap();
while let Some(line) = reader.read_line(&mut buffer) {
let regexed_i_think = rexeginton_the_domain_only(line?.trim());
println!("{:?}", regexed_i_think);
sqliting(regexed_i_think, &conn).unwrap();
}
let mut stmt = conn.prepare("SELECT id, domain FROM domains").unwrap();
let domain_iter = stmt.query_map(params![], |row| {
Ok(Domain {
id: row.get(0)?,
domain: row.get(1)?,
})
}).unwrap();
for domain in domain_iter {
println!("Found person {:?}", domain.unwrap());
}
Ok(())
}
pub fn sqliting(domain_name: &str, conn: &Connection) -> Result<()> {
let yeah = Domain {
id: 0,
domain: domain_name.to_string()
};
conn.execute(
"INSERT INTO domains (domain) VALUES (?1)",
params![yeah.domain],
)?;
Ok(())
}
mod my_reader {
use std::{
fs::File,
io::{self, prelude::*},
};
pub struct BufReader {
reader: io::BufReader<File>,
}
impl BufReader {
pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
let file = File::open(path)?;
let reader = io::BufReader::new(file);
Ok(Self { reader })
}
pub fn read_line<'buf>(
&mut self,
buffer: &'buf mut String,
) -> Option<io::Result<&'buf mut String>> {
buffer.clear();
self.reader
.read_line(buffer)
.map(|u| if u == 0 { None } else { Some(buffer) })
.transpose()
}
}
}
pub fn rexeginton_the_domain_only(full_line: &str) -> &str {
let regex_str = Regex::new(r"(?m).*?.com").unwrap();
let final_str = regex_str.captures(full_line).unwrap().get(0).unwrap().as_str();
return final_str;
}
Issue
So I am Parsing a single domain at a time, each time making an Insert. As I've gathered, Inserting would be far more efficient if I was making thousands of Inserts in a single transaction. However, I'm not quite sure what an efficient approach is to refactoring my parsing around this.
Question
How should I reorient my parsing process around my Insertion process? Also how can I actively gauge the speed and efficiency of the process in the first place so I can compare and contrast in an articulate manner?

"The collection is inconsistent state" for multiple tests using Ubuntu 20.04.1 LTS but not while using Windows 10

I have an issue running tests for the payments-contract demo https://github.com/near-apps/payments-contract running on ubuntu the error messages are as follows:
running 3 tests
test tests::make_deposit ... ok
test tests::make_deposit_and_payment ... thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: HostError(GuestPanic { panic_msg: "The collection is an inconsistent state. Did previous smart contract execution terminate unexpectedly?" })', /home/simon/.cargo/registry/src/github.com-1ecc6299db9ec823/near-sdk-2.0.1/src/environment/mocked_blockchain.rs:165:54
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
FAILED
test tests::make_deposit_and_withdrawal ... thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: HostError(GuestPanic { panic_msg: "The collection is an inconsistent state. Did previous smart contract execution terminate unexpectedly?" })', /home/simon/.cargo/registry/src/github.com-1ecc6299db9ec823/near-sdk-2.0.1/src/environment/mocked_blockchain.rs:165:54
FAILED
No matter which one is first it will successfully complete the test however any subsequent test/s will always fail.
Whereas when I run the same repo on Windows 10 all 3 tests work fine. I am using the same lib.rs file. Do tests need to be cleared after each one completes?
I noticed a similar question was answered by revising how the TreeMap is given an id in the contract. The only map I have is
#[derive(BorshDeserialize, BorshSerialize)]
pub struct PaymentContract {
pub owner_id: AccountId,
pub deposits: UnorderedMap<AccountId, Vec<Deposit>>,
}
impl Default for PaymentContract {
fn default() -> Self {
panic!("Should be initialized before usage")
}
}
#[near_bindgen]
impl PaymentContract {
#[init]
pub fn new(owner_id: AccountId) -> Self {
assert!(env::is_valid_account_id(owner_id.as_bytes()), "Invalid owner account");
assert!(!env::state_exists(), "Already initialized");
Self {
owner_id,
deposits: UnorderedMap::new(b"deposits".to_vec()),
}
}
Testing looks like below
// use the attribute below for unit tests
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
use near_sdk::MockedBlockchain;
use near_sdk::{testing_env, VMContext};
fn ntoy(near_amount: u128) -> U128 {
U128(near_amount * 10u128.pow(24))
}
fn get_context() -> VMContext {
VMContext {
predecessor_account_id: "alice.testnet".to_string(),
current_account_id: "alice.testnet".to_string(),
signer_account_id: "bob.testnet".to_string(),
signer_account_pk: vec![0],
input: vec![],
block_index: 0,
block_timestamp: 0,
account_balance: 0,
account_locked_balance: 0,
attached_deposit: 0,
prepaid_gas: 10u64.pow(18),
random_seed: vec![0, 1, 2],
is_view: false,
output_data_receivers: vec![],
epoch_height: 19,
storage_usage: 1000
}
}
#[test]
fn make_deposit() {
let mut context = get_context();
context.attached_deposit = ntoy(1).into();
testing_env!(context.clone());
let mut contract = PaymentContract::new(context.current_account_id.clone());
contract.deposit("take my money".to_string());
let deposits = contract.get_deposits(context.signer_account_id.clone());
assert_eq!(deposits.get(0).unwrap().memo, "take my money".to_string());
}
#[test]
fn make_deposit_and_payment() {
let mut context = get_context();
context.attached_deposit = ntoy(1).into();
testing_env!(context.clone());
let mut contract = PaymentContract::new(context.current_account_id.clone());
contract.deposit("take my money".to_string());
contract.make_payment(0);
let deposits = contract.get_deposits(context.signer_account_id.clone());
assert_eq!(deposits[0].paid, true);
}
#[test]
fn make_deposit_and_withdrawal() {
let mut context = get_context();
context.attached_deposit = ntoy(1).into();
testing_env!(context.clone());
let mut contract = PaymentContract::new(context.current_account_id.clone());
contract.deposit("take my money".to_string());
contract.withdraw(0);
let deposits = contract.get_deposits(context.signer_account_id.clone());
assert_eq!(deposits.len(), 0);
}
}
Any help would be appreciated

Are cross contract call atomic?

Take for example this code:
#[payable]
pub fn add_liquidity(&mut self, min_liquidity: u128, max_tokens: u128) -> U128 {
let deposit = env::attached_deposit();
let contract_near_balance = env::account_balance();
let user_address = env::predecessor_account_id();
let contract_address = env::current_account_id();
let token_amount = max_tokens;
println!("{}", token_amount);
let initial_liquidity = contract_near_balance;
println!("initial liquidity{}", initial_liquidity);
self.uni_totalsupply = initial_liquidity;
let balance_option = self.uni_balances.get(&user_address);
match balance_option {
Some(_) => {
self.uni_balances.insert(&user_address, &initial_liquidity);
}
None => {
self.uni_balances.insert(&user_address, &initial_liquidity);
}
}
Promise::new(self.avrit_token_id.clone()).function_call(
b"transfer_from".to_vec(),
json!({"owner_id":contract_address, "new_owner_id":"avrit.testnet", "amount": U128(token_amount)}).to_string().as_bytes().to_vec(),
DEPOSIT,
env::prepaid_gas() - GAS_FOR_SWAP,
);
initial_liquidity.into()
}
Even if the promise fails, will it set uni_balances in the storage? How can I make the transaction atomic?
Contract calls are not atomic. In order to make the chain of promises atomic is to use a then callback which is called after the initial promise. In the callback function you can check the success of the previous promise like here:
pub fn check_promise(&mut self) {
match env::promise_result(0) {
PromiseResult::Successful(_) => {
env::log(b"Check_promise successful");
self.checked_promise = true;
}
_ => panic!("Promise with index 0 failed"),
};
}
At this point you can make a state change that is final and could only happen if the whole transaction was successful.

OpenGL gl::GetUniformLocation fails to locate uniform only in certain cases

I'm kinda stuck. I have a program abstraction to locate uniforms for me. I have a fragment shader with a float uniform elapsed, and a vertex shader with a mat4 uniform projection.
The helper function is defined as followed:
pub fn find_uniform(&mut self, name: &str) -> Result<(), UniformError> {
if let Some(_) = self.uniforms.get(name) {
return Ok(());
}
let target_location = unsafe {
let location = gl::GetUniformLocation(self.program_id, name.as_bytes().as_ptr() as *const i8);
let error = gl::GetError();
if error != gl::NO_ERROR {
return Err(UniformError::new(true, error));
}
location
};
if target_location < 0 {
return Err(UniformError::new(false, UNIFORM_NOT_FOUND));
}
self.uniforms.insert(name.to_string(), target_location);
return Ok(());
}
If I use the helper function to look for only elapsed or if i look for both with native gl calls it works:
// No error
if let Err(e) = program.find_uniform("elapsed") {
eprint!("Failed to find elapsed, probably loading wrong shader. err: {}", e);
return;
};
// OR
unsafe {
let location1 = gl::GetUniformLocation(program.program_id, b"elapsed".as_ptr() as *const i8);
println!("{}", location1); // 0
let location2 = gl::GetUniformLocation(program.program_id, b"projection".as_ptr() as *const i8);
println!("{}", location2); // 1
}
But if I use my helper function for both it fails to find whatever i look for first:
if let Err(e) = program.find_uniform("elapsed") {
// Enters error branch here
eprint!("Failed to find elapsed, probably loading wrong shader. err: {}", e);
return;
};
if let Err(e) = program.find_uniform("projection") {
eprint!("Failed to find projection, probably loading wrong shader. err: {}", e);
return;
};
Does anyone have an idea of what I'm doing wrong?
Looking at your code, the conversion to name.as_bytes().as_ptr() as *const i8 is unsafe and depends on memory layout. Rust strings are not C-strings and are not null terminated by default. Use std::ffi::CString::new(); to ensure the string null-terminated. So your current program depends purely if you get lucky in having a null byte after your byte strings.
The following code should work:
let target_location = unsafe {
use std::ffi::CString;
let c_name = CString::new(name).expect("Convert to c-string");
let location = gl::GetUniformLocation(self.program_id, c_name.as_ptr());
let error = gl::GetError();
if error != gl::NO_ERROR {
return Err(UniformError::new(true, error));
}
location
};
See also:
How to pass data to openGL functions correctly in Rust

Resources