Cannot set name of NFT in Elrond Rust testing framework - elrond

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!

Related

How to get drive letter returned as a string?

I'm struggling to get a string of the prefix of a path. All I want to do is get the "D:" from the string "D:\Household\Internet\September_2022_Statement.pdf".
If I follow the example for std:path:Component in here, I can see the value I want but don't know how to get to it.
The code I am running is:
let filepath = "D:\\Household\\Internet\\September_2022_Statement.pdf";
let path = Path::new(filepath);
let components = path.components().collect::<Vec<_>>();
for value in &components {
println!("{:?}", value);
}
The output I get is:
Prefix(PrefixComponent { raw: "D:", parsed: Disk(68) })
RootDir
Normal("Household")
Normal("Internet")
Normal("September_2022_Statement.pdf")
How do I get the raw value "D:" from Prefix(PrefixComponent { raw: "D:", parsed: Disk(68) })?
Looks like components is an iterator of instances of the Component enum, which is declared as
pub enum Component<'a> {
Prefix(PrefixComponent<'a>),
RootDir,
CurDir,
ParentDir,
Normal(&'a OsStr),
}
Since you know that the drive is a Prefix, you can test for that.
let filepath = "D:\\Household\\Internet\\September_2022_Statement.pdf";
let path = Path::new(filepath);
let components = path.components().collect::<Vec<_>>();
for value in &components {
if let std::path::Component::Prefix(prefixComponent) = value {
return Some(value.as_os_str());
// instead of returning you could set this to a mutable variable
// or you could just check the first element of `components`
}
}
None // if this were a function that returned Option<String>
The example from the Rust docs is
use std::path::{Component, Path, Prefix};
use std::ffi::OsStr;
let path = Path::new(r"c:\you\later\");
match path.components().next().unwrap() {
Component::Prefix(prefix_component) => {
assert_eq!(Prefix::Disk(b'C'), prefix_component.kind());
assert_eq!(OsStr::new("c:"), prefix_component.as_os_str());
}
_ => unreachable!(),
}
Thanks to #Samathingamajig and #PitaJ, I was able to achieve what I needed. Obviously, I am a Rust newbie so I'm sure there is a cleaner way of doing this, but combining the help suggested, this works for me:
let filepath = "D:\\Household\\Internet\\September_2022_Statement.pdf";
let path = Path::new(fileName);
let components = path.components().collect::<Vec<_>>();
let mut os_string = OsString::new();
match path.components().next().unwrap() {
Component::Prefix(prefix_component) => {
os_string = prefix_component.as_os_str().into();
}
_ => todo!()
}
println!("{}", os_string.to_str().unwrap());
Resulting output:
D:

Adding writer function to tunnel program

How do I create a writer function for the tunnel program below? The code below is a sample program to create windows tunnel interface. I want to write a function that writes (or sends) packets to another server IP address. Github link for full code and its dependencies given below.
https://github.com/nulldotblack/wintun/blob/main/examples/basic.rs
use log::*;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
static RUNNING: AtomicBool = AtomicBool::new(true);
fn main() {
env_logger::init();
let wintun = unsafe { wintun::load_from_path("examples/wintun/bin/amd64/wintun.dll") }
.expect("Failed to load wintun dll");
let version = wintun::get_running_driver_version(&wintun);
info!("Using wintun version: {:?}", version);
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
Ok(a) => a,
Err(_) => wintun::Adapter::create(&wintun, "Example", "Demo", None)
.expect("Failed to create wintun adapter!"),
};
let version = wintun::get_running_driver_version(&wintun).unwrap();
info!("Using wintun version: {:?}", version);
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
let reader_session = session.clone();
let reader = std::thread::spawn(move || {
while RUNNING.load(Ordering::Relaxed) {
match reader_session.receive_blocking() {
Ok(packet) => {
let bytes = packet.bytes();
println!(
"Read packet size {} bytes. Header data: {:?}",
bytes.len(),
&bytes[0..(20.min(bytes.len()))]
);
}
Err(_) => println!("Got error while reading packet"),
}
}
});
println!("Press enter to stop session");
let mut line = String::new();
let _ = std::io::stdin().read_line(&mut line);
println!("Shutting down session");
RUNNING.store(false, Ordering::Relaxed);
session.shutdown();
let _ = reader.join();
println!("Shutdown complete");
}

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

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);
});
}

How to verify SECP256K1 signed message in smart contract?

I have signed the message, using ed25519
const msg = Buffer.from("hi");
const signerKeyPair = await keyStore.getKey(config.networkId, signerAccount);
const { signature } = signerKeyPair.sign(msg);
How to verify the signature in the smart contract if I have a signature and signer PublicKey?
I've found that near-core uses a near-crypto crate to do it, but I'm not sure that I can use it for the smart contracts.
an example using near-core
use near_crypto::{PublicKey, Signature, KeyType};
...
pub fn verify(&self, data: &[u8], public_key: String) -> bool {
let public_key = PublicKey::from_str(&public_key).unwrap();
let signature = Signature::empty(KeyType::SECP256K1);
signature.verify(data, &public_key)
}
...
Here is a solution
using ed25519-dalek = "1.0.1" crate
pub fn gimme_my_present(&mut self, signature: Vec<u8>) -> Promise {
let signature = ed25519_dalek::Signature::try_from(signature.as_ref())
.expect("Signature should be a valid array of 64 bytes [13, 254, 123, ...]");
let account_id = near_sdk::env::signer_account_id();
// Someone will give you the corresponding private key...
let public_key = ed25519_dalek::PublicKey::from_bytes(
&bs58::decode(
"H5ANpdUoXVwhYBgAgEi1ieMQZKJbwxjPJtHX4vkVcSnF",
)
.into_vec()
.unwrap(),
)
.unwrap();
near_sdk::env::log(
format!(
"Verifiying validity of signature ('{:?}') for string '{}'...",
signature, account_id
)
.as_bytes(),
);
if let Ok(_) = public_key.verify(account_id.as_bytes(), &signature) {
return Promise::new(account_id).transfer(16 * 16 * ONE_NEAR);
}
panic!("Ima no gonna give-ya the present without a signature! :-P");
}
source

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.

Resources