فهرست منبع

Use allocate & assign rather than create account

create account also transfers lamports which is not permitted from
non-empty accounts. From Solana v1.7 onwards this also no longer
works for 0 lamports.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 سال پیش
والد
کامیت
0dc56f4f02
5فایلهای تغییر یافته به همراه100 افزوده شده و 15 حذف شده
  1. 1 1
      .github/workflows/test.yml
  2. 1 1
      integration/solana/create_contract.spec.ts
  3. BIN
      stdlib/bpf/solana.bc
  4. 30 13
      stdlib/solana.c
  5. 68 0
      tests/solana.rs

+ 1 - 1
.github/workflows/test.yml

@@ -118,7 +118,7 @@ jobs:
     needs: linux
     services:
       solana:
-        image: solanalabs/solana:v1.6.9
+        image: solanalabs/solana:v1.7.3
         ports:
           - 8899
           - 8900

+ 1 - 1
integration/solana/create_contract.spec.ts

@@ -15,7 +15,7 @@ describe('Deploy solang contract and test', () => {
 
         let seed = await createProgramAddress(creator.get_program_key());
 
-        console.log("now create child");
+        console.log("now create child: " + seed.address.toString());
 
         let child = await conn.createStorageAccount(creator.get_program_key(), 1024);
 

BIN
stdlib/bpf/solana.bc


+ 30 - 13
stdlib/solana.c

@@ -129,8 +129,8 @@ uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports,
     }
 
     SolAccountMeta create_metas[2] = {
-        {params->account_id, true, true},
         {new_acc->key, true, true},
+        {params->account_id, true, true},
     };
 
     // FIXME we need to add our own seed if we have one in order to fund/approve it
@@ -138,26 +138,30 @@ uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports,
         {seed, 1},
     };
 
-    // create the account
-    struct create_account
+    struct allocate
     {
-        uint32_t instruction;
-        uint64_t lamports;
+        uint32_t instruction_allocate;
         uint64_t space;
-        SolPubkey owner;
-    } __attribute__((__packed__)) create_account = {
-        0,
-        lamports,
+    } __attribute__((__packed__)) allocate = {
+        8,
         space,
+    };
+
+    struct assign
+    {
+        uint32_t instruction_assign;
+        SolPubkey owner;
+    } __attribute__((__packed__)) assign = {
+        1,
         *params->ka[params->ka_cur].owner,
     };
 
-    const SolInstruction create_instruction = {
+    SolInstruction create_instruction = {
         .program_id = (SolPubkey *)&system_address,
         .accounts = create_metas,
         .account_len = SOL_ARRAY_SIZE(create_metas),
-        .data = (uint8_t *)&create_account,
-        .data_len = sizeof(create_account),
+        .data = (uint8_t *)&allocate,
+        .data_len = sizeof(allocate),
     };
 
     uint64_t ret = sol_invoke_signed_c(&create_instruction, params->ka, params->ka_num,
@@ -165,7 +169,20 @@ uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports,
 
     if (ret != 0)
     {
-        sol_log("failed to create new account");
+        sol_log("failed to allocate new account");
+
+        sol_panic();
+    }
+
+    create_instruction.data = (uint8_t *)&assign;
+    create_instruction.data_len = sizeof(assign);
+
+    ret = sol_invoke_signed_c(&create_instruction, params->ka, params->ka_num,
+                              signer_seeds, SOL_ARRAY_SIZE(signer_seeds));
+
+    if (ret != 0)
+    {
+        sol_log("failed to assign new account");
 
         sol_panic();
     }

+ 68 - 0
tests/solana.rs

@@ -94,6 +94,18 @@ struct CreateAccountWithSeed {
     program_id: Account,
 }
 
+#[derive(Deserialize)]
+struct Allocate {
+    instruction: u32,
+    space: u64,
+}
+
+#[derive(Deserialize)]
+struct Assign {
+    instruction: u32,
+    owner: Account,
+}
+
 fn build_solidity(src: &str) -> VirtualMachine {
     let mut cache = FileCache::new();
 
@@ -907,6 +919,29 @@ impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
                             }
                         }
                     }
+                    1 => {
+                        let assign: Assign = bincode::deserialize(&instruction.data).unwrap();
+
+                        let address = &instruction.accounts[0].pubkey;
+
+                        println!("assign address: {}", address.0.to_base58());
+                        for s in &signers {
+                            println!("signer: {}", s.0.to_base58());
+                        }
+                        assert!(signers.contains(&address));
+
+                        assert_eq!(assign.instruction, 1);
+
+                        println!(
+                            "assign account {} owner {}",
+                            address.0.to_base58(),
+                            assign.owner.to_base58(),
+                        );
+
+                        if let Some(entry) = context.account_data.get_mut(&address.0) {
+                            entry.owner = Some(assign.owner);
+                        }
+                    }
                     3 => {
                         let create_account: CreateAccountWithSeed =
                             bincode::deserialize(&instruction.data).unwrap();
@@ -943,6 +978,39 @@ impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
                             data: new_address,
                         });
                     }
+                    8 => {
+                        let allocate: Allocate = bincode::deserialize(&instruction.data).unwrap();
+
+                        let address = &instruction.accounts[0].pubkey;
+
+                        println!("new address: {}", address.0.to_base58());
+                        for s in &signers {
+                            println!("signer: {}", s.0.to_base58());
+                        }
+                        assert!(signers.contains(&address));
+
+                        assert_eq!(allocate.instruction, 8);
+
+                        println!(
+                            "allocate account {} with space {}",
+                            address.0.to_base58(),
+                            allocate.space,
+                        );
+
+                        assert_eq!(context.account_data[&address.0].data.len(), 0);
+
+                        if let Some(entry) = context.account_data.get_mut(&address.0) {
+                            entry.data = vec![0; allocate.space as usize];
+                        }
+
+                        let mut refs = self.refs.try_borrow_mut().unwrap();
+
+                        for r in refs.iter_mut() {
+                            if r.account == address.0 {
+                                r.length = allocate.space as usize;
+                            }
+                        }
+                    }
                     instruction => panic!("instruction {} not supported", instruction),
                 }
             } else {