Solanaは何をするにもアカウントが発行されるので、Anchorを使っていると、以下のように「#[account(init)」でアカウントの初期化を行うことになる。
#[account(init, payer = user, space = 8 + 8)]
pub crud_account: Account<'info, CrudAccount>,
データ格納用の「#[account]」を複数作成して、どちらも同じPublicKeyを使用したらどうなるか検証したときのメモ。以下のようなイメージ。
#[account]
pub struct CrudAccount {
pub data: u64,
}
#[account]
pub struct CrudAccount2 {
pub data: u64,
}
現象
anchor testを実施したところ、「Creates and initializes an account in a single atomic transaction (simplified)」エラーが出力。
CRUD
data: 1234
CwarBz4jUs4Sr7gFz7ZkpLVrR6unaUPybzzXnLpqjQY
4NyFApAgLURknM4xgkRYaQSHcFBG3D3xnfeEw6hVdxhdVGThS56RHc3kmcxJwWHDW9dFFNxaeH71oJnys3oAZyL5
✔ 1: Creates and initializes an account in a single atomic transaction (simplified) (506ms)
Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
Program EXi7tnp3oYjUvpoDm3Bxc63KhYKS1nJW5xa8zyuZBESw invoke [1]
Program log: Instruction: Create2
Program 11111111111111111111111111111111 invoke [2]
Allocate: account Address { address: CwarBz4jUs4Sr7gFz7ZkpLVrR6unaUPybzzXnLpqjQY, base: None } already in use
Program 11111111111111111111111111111111 failed: custom program error: 0x0
Program EXi7tnp3oYjUvpoDm3Bxc63KhYKS1nJW5xa8zyuZBESw consumed 4892 of 1400000 compute units
Program EXi7tnp3oYjUvpoDm3Bxc63KhYKS1nJW5xa8zyuZBESw failed: custom program error: 0x0
1) 2: Creates and initializes an account in a single atomic transaction (simplified)
1 passing (612ms)
1 failing
1) CRUD
2: Creates and initializes an account in a single atomic transaction (simplified):
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
at Connection.sendEncodedTransaction (node_modules/@solana/web3.js/src/connection.ts:3964:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Connection.sendRawTransaction (node_modules/@solana/web3.js/src/connection.ts:3921: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 create2] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:25:23)
error Command failed with exit code 1.
ソースコードは以下。
lib.rs
use anchor_lang::prelude::*;
declare_id!("EXi7tnp3oYjUvpoDm3Bxc63KhYKS1nJW5xa8zyuZBESw");
#[program]
mod crud {
use super::*;
pub fn create(ctx: Context<Initialize>, data: u64) -> Result<()> {
let crud_account = &mut ctx.accounts.crud_account;
crud_account.data = data;
msg!("data: {}", data);
Ok(())
}
pub fn create2(ctx: Context<Initialize2>, data: u64) -> Result<()> {
let crud_account2 = &mut ctx.accounts.crud_account2;
crud_account2.data = data;
msg!("data: {}", data);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub crud_account: Account<'info, CrudAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct CrudAccount {
pub data: u64,
}
#[derive(Accounts)]
pub struct Initialize2<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub crud_account2: Account<'info, CrudAccount2>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct CrudAccount2 {
pub data: u64,
}
myapp.ts
import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import { Crud } from "../target/types/crud";
import { PublicKey, SystemProgram } from '@solana/web3.js';
import { assert } from "chai";
describe("CRUD", () => {
const provider = anchor.Provider.local();
const connection = provider.connection
anchor.setProvider(provider);
const program = anchor.workspace.Crud as Program<Crud>;
const crudAccount = anchor.web3.Keypair.generate();
it("1: Creates and initializes an account in a single atomic transaction (simplified)", async () => {
const tx = await program.rpc.create(
new anchor.BN(1234), // args: data
{
accounts: { // args: ctx
crudAccount: crudAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [crudAccount],
});
const account = await program.account.crudAccount.fetch(crudAccount.publicKey);
assert.ok(account.data.eq(new anchor.BN(1234)));
console.log("data: ", account.data.toString());
console.log(crudAccount.publicKey.toString());
console.log(tx);
});
it("2: Creates and initializes an account in a single atomic transaction (simplified)", async () => {
const tx2 = await program.rpc.create2(
new anchor.BN(1234), // args: data
{
accounts: { // args: ctx
crudAccount2: crudAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [crudAccount],
});
console.log(crudAccount2.publicKey.toString());
console.log(tx2);
// const account = await program.account.crudAccount.fetch(crudAccount.publicKey);
//
// assert.ok(account.data.eq(new anchor.BN(1234)));
// console.log("data: ", account.data.toString());
// Store the account for the next test.
});
});
原因
初期化したアカウントのPublicKeyが重複しているため。
今回の場合は、データ格納用の CrudAccount と CrudAccount2 に以下「crudAccount.publicKey」を使用した。
const crudAccount = anchor.web3.Keypair.generate();
crudAccount.publicKeyをどちらも「#[account(init)」を使用したため。
#[account]
pub struct CrudAccount {
pub data: u64,
}
#[account]
pub struct CrudAccount2 {
pub data: u64,
}
対応
PublicKeyをユニークに変更する(PublicKeyの新規作成)。
const crudAccount2 = anchor.web3.Keypair.generate();
〜〜〜省略〜〜〜
const tx2 = await program.rpc.create2(
new anchor.BN(1234), // args: data
{
accounts: { // args: ctx
crudAccount2: crudAccount2.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [crudAccount],
});