I am using Anchor for developing my Solana programs, and I'm wondering how should I make CPI calls that require previously created accounts, for instance, I wasn't able to find how to calculate rent-exemption amount. Should I init these accounts like this?
#[derive(Accounts)]
pub struct CreateNft<'info> {
#[account(init, payer = user, space = 967)]
pub candy_machine: AccountInfo<'info>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
You can just pass in the AccountInfo if it already exists (no need for init) but you do want constraints to check and make sure the account(s) passed in are correct and not some malicious account pretending to be the real thing. For some things like token accounts, Anchor has built in primitives for this.
Related
I have done CPI instrucion calls before, every time the Solana program had a github repo, which had a function to create an instruction. But recently I've bumped into a situation where there is a repo, but they provide nothing to do a CPI call, on their discord server they said that they don't expect any call from on-chain programs, only from clients. General question, is it possible to call any instruction from my on-chain program? For example by duplicating all the structs, and serializing them to binary form before invoking it?
You can absolutely call any program from a CPI. You'll need to reverse-engineer the accounts and data expected by the program, and then call it like any other program, ie:
let encoded_instruction_data = [1, 0, 0, 0, 10]; // you'll need to reverse-engineer these
let account_metas = vec![
AccountMeta::new(account1_info.key, false),
AccountMeta::new_readonly(account2_info.key, false),
]; // you'll also need to reverse-engineer these based on working transactions
let instruction = Instruction::new_with_bytes(program_id, &encoded_instruction_data, account_metas);
invoke(&instruction, [account1_info.clone(), account2_info.clone()])
The only exception is if the program does instruction introspection to make sure that it isn't in a CPI context, but very few programs do this.
I am trying to build a simple game for testing where you enter sol amount and the program just sends double the amount (not on mainnet, ofc) but I just don't get some stuff about solana.
For now i don't have the code cause I am trying to understand the workflow
Here i brainstormed how my program will look like
I don't know how to create this treasury wallet account? Will it be owned by my program?
Could you also show me a piece of code, like this play function that will allow me to interact with it?
My guess rn is that i will just write the public address, and then write a from/to function in the program to do the transaction. Is it correct?
I will be using anchor btw. Thanks for the help :)
Your treasury wallet, since it's holding SOL, can simply be a PDA derived using your program id, with no data in it. It will still be owned by the system program, but since it's derived from your program, only your program can sign for it. In your program, you'll do something like:
fn transfer_one_token_from_escrow(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
// User supplies the destination
let alice_pubkey = accounts[1].key;
// Iteratively derive the escrow pubkey
let (escrow_pubkey, bump_seed) = Pubkey::find_program_address(&[&["escrow"]], program_id);
// Create the transfer instruction
let instruction = token_instruction::transfer(&escrow_pubkey, alice_pubkey, 1);
// Include the generated bump seed to the list of all seeds
invoke_signed(&instruction, accounts, &[&["escrow", &[bump_seed]]])
}
You'll likely need to do some more research to understand exactly how to implement a lot of these bits. Here are some resources:
Solana Account Model: https://solanacookbook.com/core-concepts/accounts.html#account-model
Using PDAs: https://docs.solana.com/developing/programming-model/calling-between-programs#using-program-addresses
PDAs with Anchor: https://book.anchor-lang.com/chapter_3/PDAs.html
use std::os::windows::fs;
// https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
use std::io::*;
use std::io::{self, Read, Stdin};
fn input_from() -> io::Result<()> {
let mut buffer = String::new();
let mut stdin = io::stdin(); // We get `Stdin` here.
stdin.read_to_string(&mut buffer)?;
Ok(())
}
fn main() -> std::io::Result<()> {
let mut shortcut_dir = String::from("D:\\winr\\");
println!("Your script path:");
let mut buffer1 = String::new();
let mut stdin = io::stdin();
stdin
.read_line(&mut buffer1)
.expect("error: unable to read user input");
buffer1.pop();
println!("Your shortcut name:");
let mut buffer2 = String::new();
let mut stdin: Stdin = io::stdin();
stdin
.read_line(&mut buffer2)
.expect("error: unable to read user input");
println!("buffer1 is:{:?}", buffer1);
buffer2.pop();
let mut buffer2 = &buffer2[..];
println!("buffer2 is:{:?}", buffer2);
shortcut_dir.push_str(&buffer2);
let mut buffer3 = shortcut_dir;
println!("buffer3 is:{:?}", buffer3);
fs::symlink_file(buffer1, buffer3)?;
Ok(())
}
It complains:
Error: Os { code: 1314, kind: Other, message: "A required privilege is not held by the client" }
error: process didn't exit successfully: `target\debug\ssdkqn.exe` (exit code: 1)
I use Windows. When I run this script in Administrator mode, the problem goes away. Why does it need the Administrator privilege to create a symlink?
This is a requirement of Windows, unless you are running in Developer Mode on Windows 10.
Unix has had symbolic links since 4.2 BSD. Their behavior is well known and well understood and they are widely used. Any Unix programmer knows about symbolic links.
However, when they're available, there are certain sets of security problems that a developer must be aware of. For example, there are time-of-check/time-of-use race conditions where the destination of a symlink can be changed, so it isn't safe to assume that just because you've checked the path that it means the destination won't change. Experienced Unix developers know about these and consider them carefully.
Windows added symlinks relatively late on (Windows Vista for userspace), and as such, developers were not used to using them. There was concern that enabling them would cause older programs or programs written by developers unfamiliar with symbolic links to have security issues, some of which could not be fixed. As such, it was decided to restrict the use of symbolic links to the administrator unless the user has the Create Symbolic Links privilege.
If you are on Windows 10, you can enable Developer Mode and restart, and you will be able to create symbolic links. You can also create symbolic links under WSL without privileges.
If a workaround is acceptable for you, Windows supports hard links and directory junctions, both of which can be created without administrative privileges. Unfortunately symlink_dir() and symlink_file() do not seem to allow junction or hard links.
if source.is_dir() {
std::process::Command::new("cmd")
.arg("/C")
.arg("mklink")
.arg("/J")
.arg(destination)
.arg(source)
.output()?;
} else {
std::process::Command::new("cmd")
.arg("/C")
.arg("mklink")
.arg("/H")
.arg(destination)
.arg(source)
.output()?;
}
Here is my benchmark program:
extern crate zip;
use std::fs::File;
use std::io::copy;
use zip::write::FileOptions;
use zip::ZipWriter;
fn main() {
let mut src = File::open("/tmp/src.mxf").unwrap(); // 624 Mb file.
let dest = File::create("/tmp/test.zip").unwrap();
let mut zip_writer = ZipWriter::new(dest);
zip_writer
.start_file("src.mxf", FileOptions::default())
.unwrap();
copy(&mut src, &mut zip_writer).unwrap();
zip_writer.finish().unwrap();
}
With the program compiled in release mode:
time ./zip_bench
./zip_bench 62,68s user 146,21s system 99% cpu 3:28,91 total
The same file compressed using the system zip binary:
time zip /tmp/test2.zip /tmp/src.mxf
zip /tmp/test2.zip /tmp/src.mxf 13,77s user 0,19s system 99% cpu 13,965 total
The time factor between the system and Rust zip is around 14x (for a similar output file, with insignificant size difference).
Am I doing something wrong in the code that could explain Rust performance? How can I improve it to approach system performance?
I don't have your test data, so I'm operating on a 3.7 GB Debian DVD ISO. I'm also assuming whatever you're calling the "system zip" is roughly the same as the Arch zip package.
Starting with your original code, updates to the zip crate such as moving to flate2 over deflate help:
time ./zipbench
real 2m29.285s
user 2m23.396s
sys 0m4.066s
time zip test2.zip debian-10.4.0-amd64-DVD-1.iso
adding: debian-10.4.0-amd64-DVD-1.iso (deflated 1%)
real 1m42.709s
user 1m38.066s
sys 0m3.386s
The zip utility is only about twice as fast, and we haven't even changed our code yet, just updated our crates and Rust proper by about a year.
We can add buffered IO to our Rust with BufReader and BufWriter:
fn main() -> io::Result<()> {
let mut src = BufReader::new(File::open("./debian-10.4.0-amd64-DVD-1.iso")?);
dest = BufWriter::new(File::create("./test.zip")?);
let mut zip_writer = ZipWriter::new(dest);
zip_writer.start_file("src.mxf", FileOptions::default())?;
// This is only workable because we're only writing one file to our ZIP.
let mut zip_writer = BufWriter::new(zip_writer);
io::copy(&mut src, &mut zip_writer)?;
Ok(())
}
This earns us a small performance bump, but not a huge amount:
time ./zipbench
real 2m25.348s
user 2m20.105s
sys 0m3.894s
You might get a bit more speed by using flate2 directly, especially if you can use CloudFlare's Zlib fork. However, I haven't tested this.
How can i programatically get the Enforce Password History group policy setting?
Research Effort
You can find the Group Policy option under:
**Computer Configuration\Windows Settings\Security Settings\Account Policies\Password Policy**
Enforce password history
This security setting determines the number of unique new passwords that have to be associated with a user account before an old password can be reused. The value must be between 0 and 24 passwords.
This policy enables administrators to enhance security by ensuring that old passwords are not reused continually.
Like all group policy options, it is stored in the registry. Unfortunately it is stored in an undocumented registry location:
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\F
In an undocumented binary blob format.
There is a WMI class RSOP_SecuritySettingBoolean, but without any context, it's just a name hanging out there - which i have no idea how to read through COM/native.
NetValidatePasswordPolicy
Windows does provide an NetValidatePasswordPolicy API that will allow it to validate your password for things like:
too many bad attempts
account lockout
lockout automatic reset
minimum password age
maximum password age
password reuse
and it will do all these things following the group policy in effect on the computer. And it all works great, except for password history.
The function requires you to pass a list of password hashes, e.g.:
$2a$14$mACnM5lzNigHMaf7O1py1OLCBgGL4tYUF0N/4rS9CwDsI7ytwL4D6
$2a$14$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
$2a$12$.TtQJ4Jr6isd4Hp.mVfZeuh6Gws4rOQ/vdBczhDx.19NFK0Y84Dle
$2a$12$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
as well as the hash of the new candidate password, e.g.:
$2a$10$1JsBs47iuMNsV166PKV.u.56hlT5/tRe9V5t5FIdfA0axpDSQuNN
This will cause NetValidatePasswordPolicy to check if your current hash matches any of the existing hashes. Unfortunately, the entire concept of checking previous password hashes only works when you use a weak password algorithm (such as PBKDF2 that Windows uses).
Because i use a modern, expensive, salted, password hashing algorithm (BCrypt/SCrypt), a hash cannot be deterministically generated from a password alone - i can't simply check the old list. I would have to rehash the user's new password against every previously stored salt.
not only is this an expensive operation; potentially taking 12 seconds to check all 24 stored hashes
i don't know when to stop, because i don't know the Password History group policy value (e.g. i would be checking all 24 stored hashes, when i only need to check zero).
I have considered calling NetValidatePasswordPolicy with a dummy list of 24 stored password hashes, e.g.:
a
b
c
d
...
v
w
x
The API will then tell me that it password has did not match any in the n history. But the API is also designed to return to you what you have to keep. It might then return:
$2a$10$1JsBs47iuMNsV166PKV.u.56hlT5/tRe9V5t5FIdfA0axpDSQuNN
a
b
c
And from that i might be able to infer that the password history length is four.
But i've not gotten there yet.
I'm three days into this, and losing patience.
why did Microsoft obfuscate the group policy?
why did Microsoft not allow people to read it?
why is it undocumented?
how do i get it all the same?
Turns out that using RSOP_SecuritySettingBoolean (resultant set of policy) is a bad idea; as it only works on domain-joined machines.
Querying Active Directory likewise only works for computers joined to a domain; and on works for users who have the ability to query the domain controller (which is something that can be un-granted).
The real solution is to use NetUserModalsGet, which can return you structures like:
struct USER_MODALS_INFO_0
{
DWORD usrmod0_min_passwd_len;
DWORD usrmod0_max_passwd_age;
DWORD usrmod0_min_passwd_age
DWORD usrmod0_force_logoff;
DWORD usrmod0_password_hist_len; //Specifies the length of password history maintained.
//A new password cannot match any of the previous usrmod0_password_hist_len passwords.
//Valid values for this element are zero through DEF_MAX_PWHIST.
}
and
struct USER_MODALS_INFO_3
{
DWORD usrmod3_lockout_duration;
DWORD usrmod3_lockout_observation_window;
DWORD usrmod3_lockout_threshold;
}
The sample code would be:
Int32 GetPasswordHistoryLength()
{
PUSER_MODALS_INFO_0 info0;
NET_API_STATUS res = NetUserModalsGet(nil, 0, out info0);
if (res <> NERR_Success)
RaiseWin32Error(res);
try
{
return info0.usrmod0_password_hist_len;
}
finally
{
NetApiBufferFree(info0);
}
}
The documentation says the max value is DEF_MAX_PWHIST, which is defined in Lmaccess.h as:
//
// user modals related defaults
//
#define MAX_PASSWD_LEN PWLEN
#define DEF_MIN_PWLEN 6
#define DEF_PWUNIQUENESS 5
#define DEF_MAX_PWHIST 8
#define DEF_MAX_PWAGE TIMEQ_FOREVER // forever
#define DEF_MIN_PWAGE (unsigned long) 0L // 0 days
#define DEF_FORCE_LOGOFF (unsigned long) 0xffffffff // never
#define DEF_MAX_BADPW 0 // no limit
#define ONE_DAY (unsigned long) 01*24*3600 // 01 day
But that's not true. The policy allows a user to set a maximum of 24.
Note: Any code released into public domain. No attribution required.