coral-xyz / anchor

⚓ Solana Sealevel Framework
https://anchor-lang.com
Apache License 2.0
3.69k stars 1.35k forks source link

Use of instruction args in seeds for PDA init #1641

Closed jjordan-quantum closed 8 months ago

jjordan-quantum commented 2 years ago

When I try to init a pda using the following, all is well:

#[derive(Accounts)]
pub struct CreateBlockRoot<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    #[account(
        init,
        payer = user,
        space = 8 + 4 + 4 + 200 + 1,
        seeds = [b"123456", user.key().as_ref()],
        bump
    )]
    pub block_root: Account<'info, BlockRoot>,
    pub system_program: Program<'info, System>,
}

but if I try to use an instruction argument, which is a string that matches the bytes string in the previous example, I get the follwoing: Program failed to complete: Could not create program address with signer seeds: Length of the seed is too long for address generation

snippet: (note that the block_id_string passed in the instruction is "123456"

#[derive(Accounts)]
#[instruction(block_id_string: String)]
pub struct CreateBlockRoot<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    #[account(
        init,
        payer = user,
        space = 8 + 4 + 4 + 200 + 1,
        seeds = [&block_id_string.as_bytes(), user.key().as_ref()],
        bump
    )]
    pub block_root: Account<'info, BlockRoot>,
    pub system_program: Program<'info, System>,
}

Is there another way that I need to format / convert the string from the arguments so that it will work as a seed?

callensm commented 2 years ago

Its likely a (de)serialization issue. If the block_id_string is going to be of constant length, it would be better to be [u8; LEN] instead of String.

jjordan-quantum commented 2 years ago

I see this example: seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed, &[bump]], where domain is a String

from here: https://github.com/project-serum/anchor/blob/eb65690b121c19f95fb2165ced34e40dd46a9929/examples/misc/programs/misc/src/lib.rs

jjordan-quantum commented 2 years ago

otherwise, if my seed is u32, instead of a String, is there a convenient way to use that in a seed? that would also work for me

callensm commented 2 years ago

this test program has examples for using tons of different rust types as seeds you can look at.

jjordan-quantum commented 2 years ago

this works:

#[instruction(seed: Vec<u8>)]
...
#[account(
        init,
        payer = user,
        space = 8 + 4 + 4 + 200 + 1,
        seeds = [user.key.as_ref(), &seed],
        bump
    )]

although, it requires me to convert the seed (which is just an uint32) into a buffer to get the PDA:

const seed = Buffer.from([0, 0, 34, 30, 62]);
const [blockPda, _] = await PublicKey.findProgramAddress(
    [
        anchor.getProvider().wallet.publicKey.toBuffer(),
        seed,
    ],
    program.programId
);
jjordan-quantum commented 2 years ago

this test program has examples for using tons of different rust types as seeds you can look at.

great I will take a look. thanks!

gpathela commented 2 years ago

Hi Friends. I am trying to use PDA seeds to create an account. Somehow I have tried everything still I am getting error msg: 'The program could not deserialize the given instruction'. Not sure what wrong I am doing. Please advise. Following is my code.


pub mod referal {
    use super::*;

    pub fn initialize(
        ctx: Context<Initialize>,
        bump: u8,
        refree: Pubkey,
        seed: String,
    ) -> ProgramResult {
        msg!("Seed: {}", seed);
        let base_account: &mut Account<BaseAccount> = &mut ctx.accounts.base_account;
        base_account.bump = bump;
        base_account.refree = refree;
        return Ok(());
    }
}

fn seed_bytes(seed: &str) -> &[u8] {
    msg!("Seed: {}", seed);
    seed.as_bytes()
}

#[derive(Accounts)]
#[instruction(bump:u8, seed: String)]
pub struct Initialize<'info> {
    #[account(
        seeds = [seed_bytes(&seed).as_ref()], 
        bump, init, payer = creator, space = 41)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub creator: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
#[derive(Default)]
pub struct BaseAccount {
    pub refree: Pubkey,
    pub bump: u8,
} ```
AUTr3ch commented 2 years ago

this test program has examples for using tons of different rust types as seeds you can look at.

Hey, I used this code and it works for me, but if I wann derive another address from the same "base" account with a different seed i get the following CPI error: Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account

Is it even possible to derive more addresses for one public key just by changing the seed?

acheroncrypto commented 8 months ago

Fixed by https://github.com/coral-xyz/anchor/pull/2824.