I'm new to Solana and Rust. I've done some modifications with solana js hello world example and the error with borsh serialize and deserialize occurs.
Here is my rust program:
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub id: u32,
pub name: String
}
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
// Program entrypoint's implementation
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
_instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
// Iterating accounts is safer than indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::deserialize(&mut &account.data.borrow()[..])?;
let msg = GreetingAccount::deserialize(&mut &_instruction_data[..])?;
greeting_account.id = msg.id;
greeting_account.name = msg.name;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.id);
Ok(())
}
The typescript client code is here for sending transaction:
export async function sayHello(): Promise<void> {
console.log('Saying hello to', greetedPubkey.toBase58());
const instruction = new TransactionInstruction({
keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
programId,
data: Buffer.from(borsh.serialize(
GreetingSchema,
new GreetingAccount({id: 126, name: 'anas'}),
)),
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
);
}
The error logs from the console:
logs: [
'Program 7X4jotvCZgDyEPHtAGCZeYXeJb4A8mjZFUhx9two37Vp invoke [1]',
'Program log: Hello World Rust program entrypoint',
'Program 7X4jotvCZgDyEPHtAGCZeYXeJb4A8mjZFUhx9two37Vp consumed 1412 of 1400000 compute units',
'Program 7X4jotvCZgDyEPHtAGCZeYXeJb4A8mjZFUhx9two37Vp failed: Failed to serialize or deserialize account data: Unknown'
]
The error is due to this line of code:
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
Can someone help me identifying the reason of this failure?
Related
I am writing this Solana program without using Anchor. I want the program to know the current timestamp without using any external SystemProgram.
This is the current version of my program:
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
sysvar,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
) -> ProgramResult {
msg!("Hello Solana! (From Rust)");
let nowtime = sysvar::clock::Clock.unix_timestamp;
Ok(())
}
However, I'm getting this error:
expected value, found struct `sysvar::clock::Clock
How am I supposed to use this sysvar variable?
You should be able to get the time within a program with the following:
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct HelloState {
is_initialized: bool,
}
// Accounts required
/// 1. [signer, writable] Payer
/// 2. [writable] Hello state account
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
// Payer account
let _payer_account = next_account_info(accounts_iter)?;
// Hello state account
let hello_state_account = next_account_info(accounts_iter)?;
// Getting clock directly
let clock = Clock::get()?;
let mut hello_state = HelloState::try_from_slice(&hello_state_account.data.borrow())?;
hello_state.is_initialized = true;
hello_state.serialize(&mut &mut hello_state_account.data.borrow_mut()[..])?;
msg!("Account initialized :)");
// Getting timestamp
let current_timestamp = clock.unix_timestamp;
msg!("Current Timestamp: {}", current_timestamp);
Ok(())
}
More specifically
let clock = Clock::get()?;
let current_timestamp = clock.unix_timestamp;
Note: The time on-chain is an estimation. The only reliable clock is slot number on chain. You can read more implementations for getting time here.
You can get the solana time by following function.
use anchor_lang::{prelude::*, solana_program::clock};
use std::convert::TryInto;
pub fn now_ts() -> Result<u64> {
Ok(clock::Clock::get()?.unix_timestamp.try_into().unwrap())
}
I am trying to dynamically allocate a PDA with system_program's create_account instruction, but I feel like I am missing some key piece of information. I keep getting an error saying:
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account
I managed to deduce that the create_account call itself is what fails; I am not sure if I passed in the wrong seeds, or if my signers on the frontend/context are configured wrong (previously I was going with a Signer<'info> directly, but then following the Anchor Book article on PDAs, I turned that into an unchecked account, for better or worse.
pub fn init_collection(
_ctx: Context<InitCollection>,
) -> Result<()> {
msg!("checking if account is initialized");
if !_ctx.accounts.col.data_is_empty() {
msg!("account is already initialized");
return Ok(());
}
let acc = _ctx.accounts.borrow_mut();
init_pda(
&_ctx.accounts.col.to_account_info(),
&_ctx.accounts.rent,
14,
&crate::id(),
&_ctx.accounts.payer.to_account_info(),
&_ctx.accounts.system_program,
&[
b"col".as_ref(),
&[255],
])?;
Ok(())
}
#[derive(Accounts)]
pub struct InitCollection<'info> {
/// CHECK: Is checked in the program.
#[account(mut, seeds = [b"col".as_ref()], bump)]
col: UncheckedAccount<'info>,
/// CHECK: Is checked in the CPI.
payer: UncheckedAccount<'info>,
system_program: Program<'info, System>,
rent: Sysvar<'info, Rent>,
}
For creating the account itself, I basically lifted the functionality from the anchor project:
pub fn init_pda<'a>(
new_account: &AccountInfo<'a>,
rent_key: &Sysvar<'a, Rent>,
space: usize,
owner: &Pubkey,
payer: &AccountInfo<'a>,
system_program: &AccountInfo<'a>,
seeds_with_nonce: &[&[u8]],
) -> Result<()>
{
let rent = &Rent::from_account_info(&rent_key.to_account_info())?;
// If the account being initialized already has lamports, then
// return them all back to the payer so that the account has
// zero lamports when the system program's create instruction
// is eventually called.
let __current_lamports = new_account.lamports();
if __current_lamports == 0 {
// Create the token account with right amount of lamports and space, and the correct owner.
let lamports = rent.minimum_balance(space);
let cpi_accounts = anchor_lang::system_program::CreateAccount {
from: payer.to_account_info(),
to: new_account.to_account_info()
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_owned(), cpi_accounts);
anchor_lang::system_program::create_account(cpi_context.with_signer(&[seeds_with_nonce]), lamports, space as u64, owner)?; // <== This seems to be where it's failing
} else {
// Fund the account for rent exemption.
let required_lamports = rent
.minimum_balance(space)
.max(1)
.saturating_sub(__current_lamports);
if required_lamports > 0 {
let cpi_accounts = anchor_lang::system_program::Transfer {
from: payer.to_owned(),
to: new_account.to_owned(),
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_owned(), cpi_accounts);
anchor_lang::system_program::transfer(cpi_context, required_lamports)?;
}
// Allocate space.
let cpi_accounts = anchor_lang::system_program::Allocate {
account_to_allocate: new_account.to_owned(),
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
anchor_lang::system_program::allocate(cpi_context.with_signer(&[seeds_with_nonce]), space as u64)?;
// Assign to the spl token program.
let cpi_accounts = anchor_lang::system_program::Assign {
account_to_assign: new_account.to_owned()
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
anchor_lang::system_program::assign(cpi_context.with_signer(&[seeds_with_nonce]), owner)?;
}
Ok(())
}
Can anyone point me to the right direction? I can't seem to be able to figure out where exactly what I am missing.
Thank you!
I am trying to run the following code in Anchor Solana, with program in rust as follows:
use anchor_lang::prelude::*;
declare_id!("RnbXAWg5mCvmSafjd1CnYaz32qLgZHdeHK6xzHDi1yU");
#[program]
pub mod sol_proj_1 {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
println!("hello there!");
Ok(())
}
pub fn update(ctx: Context<Update>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
}
// #[derive(Accounts)]
// pub struct Initialize {}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub my_account: Account<'info, MyAccount>,
}
#[account]
pub struct MyAccount {
pub data: u64,
}
The test program is as follow:
import * as anchor from '#project-serum/anchor';
import { Program } from '#project-serum/anchor';
import { SolProj1 } from '../target/types/sol_proj_1';
const assert = require("assert");
describe('sol_proj_1', () => {
// Configure the client to use the local cluster.
const provider = anchor.Provider.local();
anchor.setProvider(provider);
// The Account to create.
const myAccount = anchor.web3.Keypair.generate();
const program = anchor.workspace.SolProj1 as Program<SolProj1>;
it('Is initialized!', async () => {
// Add your test here.
const tx = await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: program.programId,
},
signers: [myAccount],
});
/
console.log("Your transaction signature", tx);
});
});
With error when I run the following command
Anchor test
1) sol_proj_1
Is initialized!:
Error: failed to send transaction: invalid transaction: Transaction failed to sanitize accounts offsets correctly
at Connection.sendEncodedTransaction (node_modules/#solana/web3.js/src/connection.ts:3740:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Connection.sendRawTransaction (node_modules/#solana/web3.js/src/connection.ts:3700:20)
at sendAndConfirmRawTransaction (node_modules/#solana/web3.js/src/util/send-and-confirm-raw-transaction.ts:27:21)
at Provider.send (node_modules/#project-serum/anchor/src/provider.ts:118:18)
at Object.rpc [as initialize] (node_modules/#project-serum/anchor/src/program/namespace/rpc.ts:25:23)
I tried the following
change the program ownership as I found that this can cause the issue but that did not work.
I also added entries in Anchor.toml, the tests are running on localhost
I found that empty wallet may also cause this issue but I have airdrop 100 sols in it
The Rust code deploys correctly but the test is failing the 'sanitizationfailure' is listed as follow "Transaction failed to sanitize accounts offsets correctly implies that account locks are not taken for this TX, and should not be unlocked." I couldn't find any info how to take out the locks
source: https://docs.rs/solana-sdk/1.9.2/solana_sdk/transaction/enum.TransactionError.html
Any help is appreciated!
The only thing I can see obvious is maybe the way you're passing in the system program from the frontend. You're passing in your program's id, when you should be passing the in system program id. So instead, try:
const tx = await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [myAccount],
});
I had this error before and have solved.
In my case, I find that the Program_Id in Anchor.toml is different from lib.rs, they should be the same.
Program_Id is generated by running "anchor deploy" in terminal.
Anchor.toml
[programs.localnet]
solana_app = "<Program_ID>"
lib.rs
declare_id!("<Program_ID>");
Running "anchor deploy" in terminal.
And copy the program id in Anchor.toml and lib.rs
I need help in storing data into an account.
Here's what I've done so far.
Below is my 'process_instruction' entry point,
pub fn process_instruction(...) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
// get account where we have to store state
let states_account_info = next_account_info(account_info_iter)?;
let xyz: u64 = 1234567;
let counter_struct = Counter {
data: xyz
};
counter_struct.serialize(&mut &mut states_account_info.data.borrow_mut()[..])?;
Ok(())
}
Here's my Counter Struct
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct Counter {
/// mappings of the keys
pub data: u64,
}
And this is how, I create the account inside the test,
let state_account_pubkey = Pubkey::new_unique();
let mut program_test = ProgramTest::new(
"solana_states_save_program",
program_id,
processor!(process_instruction),
);
program_test.add_account(
state_account_pubkey,
Account {
lamports: 5,
owner: program_id,
..Account::default()
},
);
But after executing the test, I get the following error, inside process_instruction() method (on counter_struct.serialize(...) statement),
thread 'main' panicked at 'called Result::unwrap() on an Err value:
TransactionError(InstructionError(0, BorshIoError("Unknown")))'
Kindly help.
We also needed to add account data space when creating an Account in tests, which I was missing.
program_test.add_account(
state_account_pubkey,
Account {
lamports: 5,
data: vec![0_u8; mem::size_of::<u32>()],
owner: program_id,
..Account::default()
},
);
Hope, it helps someone.
I have been trying to write some data with struct type to solana blockchain but have been stuck at an error which says ->
Transaction simulation failed: Error processing Instruction 0: Program failed to complete
Program 2qCpEJASM553foMRmd4MHRLxEFywKwvaUXRtypXJp4zv invoke [1]
Program consumption: 199505 units remaining
Program log: Instruction_data message object LoveRecord { groom: "a", created_on: "0" }
Program log: libstd rust_begin_panic
Program log: panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:67:98
Program 2qCpEJASM553foMRmd4MHRLxEFywKwvaUXRtypXJp4zv consumed 200000 of 200000 compute units
Program failed to complete: BPF program panicked
Program 2qCpEJASM553foMRmd4MHRLxEFywKwvaUXRtypXJp4zv failed: Program failed to complete
Code for solana program entrypoint in Rust language is ->
use borsh::{ BorshDeserialize, BorshSerialize };
use solana_program::{
account_info::{ next_account_info, AccountInfo },
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
use std::io::ErrorKind::InvalidData;
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct LoveRecord {
pub groom: String,
pub created_on: String
}
const DUMMY_TX_ID: &str = "a";
const DUMMY_CREATED_ON: &str = "0"; // milliseconds, 16 digits
pub fn get_init_love_record() -> LoveRecord {
LoveRecord{ groom: String::from(DUMMY_TX_ID), created_on: String::from(DUMMY_CREATED_ON) }
}
pub fn get_init_love_records() -> Vec<LoveRecord> {
let mut records = Vec::new();
for _ in 0..10 {
records.push(get_init_love_record());
}
return records;
}
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let account = next_account_info(accounts_iter)?;
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
let instruction_data_message = LoveRecord::try_from_slice(instruction_data).map_err(|err| {
msg!("Attempt to deserialize instruction data has failed. {:?}", err);
ProgramError::InvalidInstructionData
})?;
solana_program::msg!("pass");
msg!("pass");
msg!("Instruction_data message object {:?}", instruction_data_message);
let mut existing_data_messages = match <Vec<LoveRecord>>::try_from_slice(&account.data.borrow_mut()) {
Ok(data) => data,
Err(err) => {
if err.kind() == InvalidData {
msg!("InvalidData so initializing account data");
get_init_love_records()
} else {
msg!("Unknown error decoding account data {:?}", err)
}
}
};
let index = existing_data_messages.iter().position(|p| p.groom == String::from(DUMMY_TX_ID)).unwrap(); // find first dummy data entry
msg!("Found index {}", index);
existing_data_messages[index] = instruction_data_message; // set dummy data to new entry
let updated_data = existing_data_messages.try_to_vec().expect("Failed to encode data."); // set records object back to vector data
msg!("Final existing_data_messages[index] {:?}", existing_data_messages[index]);
// data algorithm for storing data into account and then archiving into Arweave
// 1. Each LoveRecord object will be prepopulated for txt field having 1 characters (length of a arweave tx).
// Each LoveRecordContainer will be prepopulated with 10 LoveRecord objects with dummy data.
// 2. Client will submit an arweave tx for each message; get back the tx id; and submit it to our program.
// 3. This tx id will be saved to the Solana program and be used for querying back to arweave to get actual data.
let data = &mut &mut account.data.borrow_mut();
msg!("Attempting save data.");
data[..updated_data.len()].copy_from_slice(&updated_data);
let saved_data = <Vec<LoveRecord>>::try_from_slice(data)?;
msg!("LoveRecord has been saved to account data. {:?}", saved_data[index]);
msg!("End program.");
Ok(())
}
Code to send transaction using Borsch Serialize is ->
static async sayHello(data) {
let config = DappLib.getConfig();
console.log(data);
let DUMMY_TX_ID = "a";
let DUMMY_CREATED_ON = "0";
class LoveRecord {
constructor(fields = undefined) {
this.groom = DUMMY_TX_ID;
this.created_on = DUMMY_CREATED_ON; // max milliseconds in dat
if (fields) {
this.groom = fields.groom;
this.created_on = fields.created_on;
}
}
}
const LoveRecordSchema = new Map([[
LoveRecord,
{
kind: "struct",
fields: [
["groom", "string"],
["created_on", "string"],
],
},
]]);
const loveobj = new LoveRecord();
loveobj.groom = "a";
loveobj.created_on = "0";
const res = borsh.serialize(LoveRecordSchema,loveobj);
let result = await Blockchain.put({ config }, 'greeting', res);
console.log("Pass")
return {
type: DappLib.DAPP_RESULT_OBJECT,
label: 'Transaction Result',
result
}
}
Code to send and sign transaction has been taken care of.
Any help would be really appreciated.
This is quite a specific question around your code, but the error message is probably the best place to look:
Program log: panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:67:98
This seems to correspond with the line:
let index = existing_data_messages.iter().position(|p| p.groom == String::from(DUMMY_TX_ID)).unwrap();
You're calling unwrap() on a None value, which makes sense, considering nothing has been written to this account before. You'll need to handle the case where DUMMY_TX_ID is not found in your vector.
You can find more information about how position() works at https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.position