Jelajahi Sumber

docs: Update tutorials for new init constraint (#670)

Armani Ferrante 4 tahun lalu
induk
melakukan
2cc82bc11a

+ 10 - 36
docs/src/tutorials/tutorial-1.md

@@ -31,27 +31,16 @@ Some new syntax elements are introduced here.
 First, let's start with the initialize instruction. Notice the `data` argument passed into the program. This argument and any other valid
 First, let's start with the initialize instruction. Notice the `data` argument passed into the program. This argument and any other valid
 Rust types can be passed to the instruction to define inputs to the program.
 Rust types can be passed to the instruction to define inputs to the program.
 
 
-::: tip
-If you'd like to pass in your own type as an input to an instruction handler, then it must be
-defined in the same `src/lib.rs` file as the `#[program]` module, so that the IDL parser can
-pick it up.
-:::
-
 Additionally,
 Additionally,
 notice how we take a mutable reference to `my_account` and assign the `data` to it. This leads us to
 notice how we take a mutable reference to `my_account` and assign the `data` to it. This leads us to
-the `Initialize` struct, deriving `Accounts`. There are three things to notice about `Initialize`.
+the `Initialize` struct, deriving `Accounts`. There are two things to notice about `Initialize`.
 
 
 1. The `my_account` field is of type `ProgramAccount<'info, MyAccount>`, telling the program it *must*
 1. The `my_account` field is of type `ProgramAccount<'info, MyAccount>`, telling the program it *must*
 be **owned** by the currently executing program, and the deserialized data structure is `MyAccount`.
 be **owned** by the currently executing program, and the deserialized data structure is `MyAccount`.
-2. The `my_account` field is marked with the `#[account(init)]` attribute. This should be used
-in one situation: when a given `ProgramAccount` is newly created and is being used by the program
-for the first time (and thus its data field is all zero). If `#[account(init)]` is not used
-when account data is zero initialized, the transaction will be rejected.
-3. The `Rent` **sysvar** is required for the rent exemption check, which the framework enforces
-by default for any account marked with `#[account(init)]`. To be more explicit about the check,
-one can specify `#[account(init, rent_exempt = enforce)]`. To skip this check, (and thus
-allowing you to omit the `Rent` acccount), you can specify
-`#[account(init, rent_exempt = skip)]` on the account being initialized (here, `my_account`).
+2. The `my_account` field is marked with the `init` attribute. This will create a new
+account owned by the current program, zero initialized. When using `init`, one must also provider
+`payer`, which will fund the account creation, `space`, which defines how large the account should be,
+and the `system_program`, which is required by the runtime for creating the account.
 
 
 ::: details
 ::: details
 All accounts created with Anchor are laid out as follows: `8-byte-discriminator || borsh
 All accounts created with Anchor are laid out as follows: `8-byte-discriminator || borsh
@@ -81,15 +70,16 @@ for persisting changes.
 
 
 ## Creating and Initializing Accounts
 ## Creating and Initializing Accounts
 
 
-For a moment, assume an account of type `MyAccount` was created on Solana, in which case,
-we can invoke the above `initialize` instruction as follows.
+We can interact with the program as follows.
 
 
-<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code-separated
+<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code-simplified
 
 
 The last element passed into the method is common amongst all dynamically generated
 The last element passed into the method is common amongst all dynamically generated
 methods on the `rpc` namespace, containing several options for a transaction. Here,
 methods on the `rpc` namespace, containing several options for a transaction. Here,
 we specify the `accounts` field, an object of all the addresses the transaction
 we specify the `accounts` field, an object of all the addresses the transaction
-needs to touch.
+needs to touch, and the `signers` array of all `Signer` objects needed to sign the
+transaction. Because `myAccount` is being created, the Solana runtime requries it
+to sign the transaction.
 
 
 ::: details
 ::: details
 If you've developed on Solana before, you might notice two things 1) the ordering of the accounts doesn't
 If you've developed on Solana before, you might notice two things 1) the ordering of the accounts doesn't
@@ -98,22 +88,6 @@ options are not specified on the account anywhere. In both cases, the framework
 of these details for you, by reading the IDL.
 of these details for you, by reading the IDL.
 :::
 :::
 
 
-However it's common--and sometimes necessary for security purposes--to batch
-instructions together. We can extend the example above to both create an account
-and initialize it in one atomic transaction.
-
-<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code
-
-Here, notice the **two** fields introduced: `signers` and `instructions`. `signers`
-is an array of all `Account` objects to sign the transaction and `instructions` is an
-array of all instructions to run **before** the explicitly specified program instruction,
-which in this case is `initialize`. Because we are creating `myAccount`, it needs to
-sign the transaction, as required by the Solana runtime.
-
-We can simplify this further.
-
-<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code-simplified
-
 As before, we can run the example tests.
 As before, we can run the example tests.
 
 
 ```
 ```

+ 1 - 2
docs/src/tutorials/tutorial-3.md

@@ -71,9 +71,8 @@ retrieve the return value. In future work, Anchor should do this transparently.
 
 
 ## Conclusion
 ## Conclusion
 
 
-Now that you can have your programs call other programs, you should be able to access all the work being done by other developers in your own applications! 
+Now that you can have your programs call other programs, you should be able to access all the work being done by other developers in your own applications!
 
 
 ## Next Steps
 ## Next Steps
 
 
 Up until now, we've treated programs on Solana as stateless. In the next tutorial we will learn how to add a global state to our program.
 Up until now, we've treated programs on Solana as stateless. In the next tutorial we will learn how to add a global state to our program.
-

+ 3 - 1
examples/tutorial/basic-1/programs/basic-1/src/lib.rs

@@ -19,8 +19,10 @@ mod basic_1 {
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]
 pub struct Initialize<'info> {
 pub struct Initialize<'info> {
-    #[account(zero)]
+    #[account(init, payer = user, space = 8 + 8)]
     pub my_account: ProgramAccount<'info, MyAccount>,
     pub my_account: ProgramAccount<'info, MyAccount>,
+    pub user: AccountInfo<'info>,
+    pub system_program: AccountInfo<'info>,
 }
 }
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]

+ 5 - 83
examples/tutorial/basic-1/tests/basic-1.js

@@ -1,5 +1,6 @@
 const assert = require("assert");
 const assert = require("assert");
 const anchor = require("@project-serum/anchor");
 const anchor = require("@project-serum/anchor");
+const { SystemProgram } = anchor.web3;
 
 
 describe("basic-1", () => {
 describe("basic-1", () => {
   // Use a local provider.
   // Use a local provider.
@@ -8,102 +9,23 @@ describe("basic-1", () => {
   // Configure the client to use the local cluster.
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);
   anchor.setProvider(provider);
 
 
-  it("Creates and initializes an account in two different transactions", async () => {
-    // The program owning the account to create.
-    const program = anchor.workspace.Basic1;
-
-    // The Account to create.
-    const myAccount = anchor.web3.Keypair.generate();
-
-    // Create account transaction.
-    const tx = new anchor.web3.Transaction();
-    tx.add(
-      anchor.web3.SystemProgram.createAccount({
-        fromPubkey: provider.wallet.publicKey,
-        newAccountPubkey: myAccount.publicKey,
-        space: 8 + 8,
-        lamports: await provider.connection.getMinimumBalanceForRentExemption(
-          8 + 8
-        ),
-        programId: program.programId,
-      })
-    );
-
-    // Execute the transaction against the cluster.
-    await provider.send(tx, [myAccount]);
-
-    // Execute the RPC.
-    // #region code-separated
-    await program.rpc.initialize(new anchor.BN(1234), {
-      accounts: {
-        myAccount: myAccount.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-      },
-    });
-    // #endregion code-separated
-
-    // Fetch the newly created account from the cluster.
-    const account = await program.account.myAccount.fetch(myAccount.publicKey);
-
-    // Check it's state was initialized.
-    assert.ok(account.data.eq(new anchor.BN(1234)));
-  });
-
-  // Reference to an account to use between multiple tests.
-  let _myAccount = undefined;
-
-  it("Creates and initializes an account in a single atomic transaction", async () => {
-    // The program to execute.
-    const program = anchor.workspace.Basic1;
-
-    // #region code
-    // The Account to create.
-    const myAccount = anchor.web3.Keypair.generate();
-
-    // Atomically create the new account and initialize it with the program.
-    await program.rpc.initialize(new anchor.BN(1234), {
-      accounts: {
-        myAccount: myAccount.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-      },
-      signers: [myAccount],
-      instructions: [
-        anchor.web3.SystemProgram.createAccount({
-          fromPubkey: provider.wallet.publicKey,
-          newAccountPubkey: myAccount.publicKey,
-          space: 8 + 8, // Add 8 for the account discriminator.
-          lamports: await provider.connection.getMinimumBalanceForRentExemption(
-            8 + 8
-          ),
-          programId: program.programId,
-        }),
-      ],
-    });
-
-    // Fetch the newly created account from the cluster.
-    const account = await program.account.myAccount.fetch(myAccount.publicKey);
-
-    // Check it's state was initialized.
-    assert.ok(account.data.eq(new anchor.BN(1234)));
-    // #endregion code
-  });
-
   it("Creates and initializes an account in a single atomic transaction (simplified)", async () => {
   it("Creates and initializes an account in a single atomic transaction (simplified)", async () => {
+    // #region code-simplified
     // The program to execute.
     // The program to execute.
     const program = anchor.workspace.Basic1;
     const program = anchor.workspace.Basic1;
 
 
     // The Account to create.
     // The Account to create.
     const myAccount = anchor.web3.Keypair.generate();
     const myAccount = anchor.web3.Keypair.generate();
 
 
-    // Atomically create the new account and initialize it with the program.
+    // Create the new account and initialize it with the program.
     // #region code-simplified
     // #region code-simplified
     await program.rpc.initialize(new anchor.BN(1234), {
     await program.rpc.initialize(new anchor.BN(1234), {
       accounts: {
       accounts: {
         myAccount: myAccount.publicKey,
         myAccount: myAccount.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
+        user: provider.wallet.publicKey,
+        systemProgram: SystemProgram.programId,
       },
       },
       signers: [myAccount],
       signers: [myAccount],
-      instructions: [await program.account.myAccount.createInstruction(myAccount)],
     });
     });
     // #endregion code-simplified
     // #endregion code-simplified
 
 

+ 6 - 1
examples/tutorial/basic-2/programs/basic-2/src/lib.rs

@@ -1,4 +1,5 @@
 use anchor_lang::prelude::*;
 use anchor_lang::prelude::*;
+use anchor_lang::solana_program::system_program;
 
 
 // Define the program's instruction handlers.
 // Define the program's instruction handlers.
 
 
@@ -24,8 +25,12 @@ mod basic_2 {
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]
 pub struct Create<'info> {
 pub struct Create<'info> {
-    #[account(zero)]
+    #[account(init, payer = user, space = 8 + 40)]
     pub counter: ProgramAccount<'info, Counter>,
     pub counter: ProgramAccount<'info, Counter>,
+    #[account(signer)]
+    pub user: AccountInfo<'info>,
+    #[account(address = system_program::ID)]
+    pub system_program: AccountInfo<'info>,
 }
 }
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]

+ 3 - 2
examples/tutorial/basic-2/tests/basic-2.js

@@ -1,5 +1,6 @@
 const assert = require('assert');
 const assert = require('assert');
 const anchor = require('@project-serum/anchor');
 const anchor = require('@project-serum/anchor');
+const { SystemProgram } = anchor.web3;
 
 
 describe('basic-2', () => {
 describe('basic-2', () => {
   const provider = anchor.Provider.local()
   const provider = anchor.Provider.local()
@@ -17,10 +18,10 @@ describe('basic-2', () => {
     await program.rpc.create(provider.wallet.publicKey, {
     await program.rpc.create(provider.wallet.publicKey, {
       accounts: {
       accounts: {
         counter: counter.publicKey,
         counter: counter.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
+        user: provider.wallet.publicKey,
+        systemProgram: SystemProgram.programId,
       },
       },
       signers: [counter],
       signers: [counter],
-      instructions: [await program.account.counter.createInstruction(counter)],
     })
     })
 
 
     let counterAccount = await program.account.counter.fetch(counter.publicKey)
     let counterAccount = await program.account.counter.fetch(counter.publicKey)

+ 1 - 1
examples/tutorial/basic-3/programs/puppet-master/src/lib.rs

@@ -17,7 +17,7 @@ mod puppet_master {
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]
 pub struct PullStrings<'info> {
 pub struct PullStrings<'info> {
-    #[account(mut)]
+    #[account(mut, owner = puppet_program)]
     pub puppet: CpiAccount<'info, Puppet>,
     pub puppet: CpiAccount<'info, Puppet>,
     pub puppet_program: AccountInfo<'info>,
     pub puppet_program: AccountInfo<'info>,
 }
 }

+ 6 - 1
examples/tutorial/basic-3/programs/puppet/src/lib.rs

@@ -1,4 +1,5 @@
 use anchor_lang::prelude::*;
 use anchor_lang::prelude::*;
+use anchor_lang::solana_program::system_program;
 
 
 #[program]
 #[program]
 pub mod puppet {
 pub mod puppet {
@@ -16,8 +17,12 @@ pub mod puppet {
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]
 pub struct Initialize<'info> {
 pub struct Initialize<'info> {
-    #[account(zero)]
+    #[account(init, payer = user, space = 8 + 8)]
     pub puppet: ProgramAccount<'info, Puppet>,
     pub puppet: ProgramAccount<'info, Puppet>,
+    #[account(signer)]
+    pub user: AccountInfo<'info>,
+    #[account(address = system_program::ID)]
+    pub system_program: AccountInfo<'info>,
 }
 }
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]

+ 7 - 6
examples/tutorial/basic-3/tests/basic-3.js

@@ -1,5 +1,6 @@
 const assert = require("assert");
 const assert = require("assert");
 const anchor = require("@project-serum/anchor");
 const anchor = require("@project-serum/anchor");
+const { SystemProgram } = anchor.web3;
 
 
 describe("basic-3", () => {
 describe("basic-3", () => {
   const provider = anchor.Provider.local();
   const provider = anchor.Provider.local();
@@ -16,18 +17,18 @@ describe("basic-3", () => {
     const tx = await puppet.rpc.initialize({
     const tx = await puppet.rpc.initialize({
       accounts: {
       accounts: {
         puppet: newPuppetAccount.publicKey,
         puppet: newPuppetAccount.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
+        user: provider.wallet.publicKey,
+        systemProgram: SystemProgram.programId,
       },
       },
       signers: [newPuppetAccount],
       signers: [newPuppetAccount],
-      instructions: [await puppet.account.puppet.createInstruction(newPuppetAccount)],
     });
     });
 
 
     // Invoke the puppet master to perform a CPI to the puppet.
     // Invoke the puppet master to perform a CPI to the puppet.
     await puppetMaster.rpc.pullStrings(new anchor.BN(111), {
     await puppetMaster.rpc.pullStrings(new anchor.BN(111), {
-        accounts: {
-            puppet: newPuppetAccount.publicKey,
-            puppetProgram: puppet.programId,
-        },
+       accounts: {
+          puppet: newPuppetAccount.publicKey,
+          puppetProgram: puppet.programId,
+       },
     });
     });
 
 
     // Check the state updated.
     // Check the state updated.