I tried a lot of things, in order to create an account with "mut" data, this is an example:
pub struct Test {
pub correct: u8,
pub seed: u8,
pub signer: Pubkey,
}
....
.......
let testing = Test {
correct: 0,
seed: 2,
signer: anybody.pubkey(),
};
let mut t_data = Vec::new();
my_data.serialize(&mut t_data).unwrap();
.......
.....
let x = solana_program::system_instruction::create_account(
&payer.pubkey(),
&test_ac.pubkey(),
1_0000_0000,
180,
&bfp_loader::ID,
);
.....
.......
...
let y = loader_instruction::write(
&test_ac.pubkey(),
&bpf_loader::ID,
1, // doesn't matter
t_data
);
...
......
.........
Please, show us a code, creating an account with mutable data in solana
Thx
Related
I am struggling to make a simple solana anchor code example to work. All I am trying to do is to initialize a piece of data with only my wallet being allowed to do it.
Here is my Solana anchor code:
impl<'info> Validate<'info> for InitData<'info> {
fn validate(&self) -> Result<()> {
assert_keys_eq!(self.manager, Pubkey::from_str("2jEfqL1wFEHFjtbKEDoRottSBsij3v1j19aZueqrnj7v").unwrap());
Ok(())
}
}
#[program]
mod coinflip_bet {
use super::*;
#[access_control(ctx.accounts.validate())]
pub fn init_data(ctx: Context<InitData>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct InitData<'info> {
#[account(
init,
payer = manager,
space = 8 + 1,
seeds = [b"data-account", manager.key().as_ref()],
bump
)]
pub data_account: Account<'info, DummyData>,
#[account(mut)]
pub manager: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct DummyData {
pub dummy: u8,
}
Here is my client code:
require('dotenv').config();
const PROGRAM_ID = process.env.PROGRAM_ID ?? '';
async function run() {
const connection = new Connection('http://127.0.0.1:8899');
const wallet = NodeWallet.local();
const programId = new PublicKey(PROGRAM_ID);
const [dataAccount,] = await PublicKey.findProgramAddress(
[Buffer.from("data-account"), wallet.publicKey.toBuffer()],
SystemProgram.programId
);
const provider = new anchor.AnchorProvider(
connection,
wallet,
anchor.AnchorProvider.defaultOptions()
);
//doesn't compile without this type checker skip
//#ts-ignore
const program = new anchor.Program(IDL, programId, provider);
await program.methods.initData()
.accounts(
{
dataAccount: dataAccount,
manager: wallet.publicKey,
systemProgram: SystemProgram.programId,
}
)
.signers(
[wallet.payer]
)
.rpc();
}
run().then(
() => process.exit(),
err => {
console.error(err);
process.exit(-1);
},
);
I'm on localhost:8899, launching this with 'solana-test-validator', 'anchor build', 'anchor deploy', 'ts-node src/init-data-account.ts'
I get this error:
SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account
logs: [
'Program 6YQukxVDKejG79RNSddyeEy6YQRVNkXSi4f5HuEyVMd6 invoke [1]',
'Program log: Instruction: InitData',
"7g32AFUNrTqyyuj1zbve6ui1aUzqzEvzpBc2tdNmqBjo's signer privilege escalated",
'Program 6YQukxVDKejG79RNSddyeEy6YQRVNkXSi4f5HuEyVMd6 consumed 6670 of 200000 compute units',
'Program 6YQukxVDKejG79RNSddyeEy6YQRVNkXSi4f5HuEyVMd6 failed: Cross-program invocation with unauthorized signer or writable account'
],
Can you please help me get rid off this error?
Update:
I updated my accounts struct:
#[derive(Accounts)]
pub struct InitData<'info> {
#[account(
init,
payer = manager,
space = 8 + 1,
seeds = [b"data-account", handler.key().as_ref()],
bump
)]
pub data_account: Account<'info, Escrow>,
/// CHECK:
#[account(mut)]
pub handler: AccountInfo<'info>,
#[account(mut)]
pub manager: Signer<'info>,
pub system_program: Program<'info, System>,
}
And I still have this error:
Cross-program invocation with unauthorized signer or writable account
Going by the accepted answer here, you need to use some variable aside from the 'manager' as seed e.g.
#[account(
init,
payer = manager,
space = 8 + 1,
seeds = [b"data-account", system_program.key().as_ref()],
bump
)]
pub data_account: Account<'info, DummyData>,
Don't forget to update your test to use the new key in findProgramAddress
The problem is this code:
const [dataAccount,] = await PublicKey.findProgramAddress(
[Buffer.from("data-account"), wallet.publicKey.toBuffer()],
SystemProgram.programId
);
It should be the id of the program instead of system program
I'm writing a program in Solana, I have to save data on the chain but make the data read only.
Right now I'm using anchor and I've wrote this:
pub fn save_data(ctx: Context<SaveData>, content: String, actor: String) -> Result<()> {
let service_account = &mut ctx.accounts.service_account;
let data_account = &mut ctx.accounts.data_account;
let company_account = &mut ctx.accounts.company_account;
let authority = &mut ctx.accounts.authority;
data_account.content = content;
data_account.actor = actor;
data_account.company = company_account.key();
data_account.authority = authority.key();
data_account.pre_data_key = service_account.current_data_key;
service_account.current_data_key = data_account.key();
Ok(())
}
#[derive(Accounts)]
pub struct SaveData<'info> {
#[account(init, payer = authority, space = 8 + 50 + 32 + 32 + 32 + 32 )]
pub data_account: Account<'info, DataState>,
#[account(mut, has_one = authority)]
pub company_account: Account<'info, CompanyState>,
#[account(mut)]
pub service_account: Account<'info, ServiceState>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct DataState {
content: String,
actor: String,
company: Pubkey,
pub pre_data_key: Pubkey,
pub authority: Pubkey,
}
I didn't implement any update or delete method but I would like to know if it would be possible to make the data content just read only.
Also, right now, if the creator of the dataAccount wants to update the data can update it outside of my smart contract?
Thanks
The account cant be updated outside of your contract since its a PDA(program derived account) and only your program can modify it through the instructions youve written, If you want to make it read only you have to write the program in such a way that it doesnt change the data of the account(just dont make a function that does that)
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?
I have a vector of struct and want to initialize it with all zeros.
struct MyStruct {
v1: u32,
v2: u64,
}
type MyVector = Vec<MyStruct>;
Cause the size of vector is already known, I can specify the capacity.
My first approach is as below,
impl Default for MyStruct {
fn default() -> Self {
Self {
v1: 0,
v2: 0,
}
}
}
fn init_my_vec() {
let size = 1000;
let mut my_vec: MyVector = Vec::with_capacity(size);
(0..size).for_each(|_| my_vec.push(MyStruct::default()))
}
As far as I know, the vector initialization with 0 is faster than using iterator. like this,
let usize_vec: Vec<usize> = vec![0; 1000];
// is faster than
let mut usize_vec: Vec<usize> = Vec::with_capacity(1000);
for i in 0..1000 {
usize_vec.push(0);
}
Question
Am I right about vector initialization speed? As fill with 0 is special instruction, using iterator is slower than using macro.
Is there any method that can initialize the vector of struct with 0 values safely and fast?
Or I should use unsafe code like making empty bytes and casting it to vector?
Speed measurement about Question 1
const VEC_SIZE: usize = 10_000;
fn init_with_iter() -> u128 {
let start = Instant::now();
let mut usize_vec: Vec<usize> = Vec::with_capacity(VEC_SIZE);
for i in 0..VEC_SIZE {
usize_vec.push(0);
}
start.elapsed().as_micros()
}
fn init_with_macro() -> u128 {
let start = Instant::now();
let _: Vec<usize> = vec![0; VEC_SIZE];
start.elapsed().as_micros()
}
Average time taken to generate vector 10,000 times is
using iter(init_with_iter): 514.6805 ms
using macro(init_with_macro): 2.0361 ms
on my machine
Speed measurement about Question 3
I think using unsafe function mem::zeroed is slightly faster than any others
const VEC_SIZE: usize = 10_000;
fn init_with_iter() -> u128 {
let start = Instant::now();
let mut my_vec: MyVector = Vec::with_capacity(VEC_SIZE);
for _ in 0..VEC_SIZE {
my_vec.push(MyStruct::default());
}
start.elapsed().as_micros()
}
fn init_with_macro() -> u128 {
let start = Instant::now();
let _: MyVector = vec![MyStruct::default(); VEC_SIZE];
start.elapsed().as_micros()
}
fn init_with_zeroed() -> u128 {
let start = Instant::now();
let _: MyVector = unsafe { vec![std::mem::zeroed(); VEC_SIZE] };
start.elapsed().as_micros()
}
Average time taken to generate vector 1,000 times is
using iter(init_with_iter): 575.572 ms.
using macro(init_with_macro): 486.958 ms
using unsafe function(init_with_zeroed): 468.885 ms
on my machine
Here is a criterion benchmark of your three approaches:
use criterion::{black_box, criterion_group, criterion_main, Criterion};
criterion_group!(
benches,
init_structs_with_iter,
init_structs_with_macro,
init_structs_with_unsafe
);
criterion_main!(benches);
const N_ITEMS: usize = 1000;
#[allow(unused)]
#[derive(Debug, Clone)]
struct MyStruct {
v1: u32,
v2: u64,
}
impl Default for MyStruct {
fn default() -> Self {
Self { v1: 0, v2: 0 }
}
}
fn init_structs_with_iter(c: &mut Criterion) {
c.bench_function("structs: with_iter", |b| {
b.iter(|| {
let mut my_vec = Vec::with_capacity(N_ITEMS);
(0..my_vec.capacity()).for_each(|_| my_vec.push(MyStruct::default()));
black_box(my_vec);
})
});
}
fn init_structs_with_macro(c: &mut Criterion) {
c.bench_function("structs: with_macro", |b| {
b.iter(|| {
let my_vec = vec![MyStruct::default(); N_ITEMS];
black_box(my_vec);
})
});
}
fn init_structs_with_unsafe(c: &mut Criterion) {
c.bench_function("structs: with_unsafe", |b| {
b.iter(|| {
let my_vec: Vec<MyStruct> = vec![unsafe { std::mem::zeroed() }; N_ITEMS];
black_box(my_vec);
})
});
}
And the results:
structs: with_iter time: [1.3857 us 1.3960 us 1.4073 us]
structs: with_macro time: [563.30 ns 565.30 ns 567.32 ns]
structs: with_unsafe time: [568.84 ns 570.09 ns 571.49 ns]
The vec![] macro seems to be the fastest (and also the cleanest and easiest to read).
As you can see, the time is measured in nanoseconds, so although the iterator version is 2-3x slower, it won't matter in practice. Optimizing the zero-initialization of a struct is the least important thing you can do - you can save at most 1 microsecond ;)
PS: those times include the memory allocation and deallocation times
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