Armani Ferrante 3 lat temu
rodzic
commit
bb9b35acbc

+ 1 - 0
Cargo.lock

@@ -252,6 +252,7 @@ dependencies = [
  "anyhow",
  "arrayref",
  "bs58 0.3.1",
+ "cargo_toml",
  "heck 0.3.3",
  "proc-macro2 1.0.32",
  "proc-macro2-diagnostics",

+ 5 - 6
cli/src/config.rs

@@ -83,10 +83,10 @@ impl Manifest {
     }
 
     pub fn version(&self) -> String {
-        match &self.package {
-            Some(package) => package.version.to_string(),
-            _ => "0.0.0".to_string(),
-        }
+        self.package
+            .as_ref()
+            .map(|p| p.version.clone())
+            .expect("Cargo.toml must have package version")
     }
 
     // Climbs each parent directory from the current dir until we find a Cargo.toml
@@ -161,10 +161,9 @@ impl WithPath<Config> {
         for path in self.get_program_list()? {
             let cargo = Manifest::from_path(&path.join("Cargo.toml"))?;
             let lib_name = cargo.lib_name()?;
-            let version = cargo.version();
             let idl = anchor_syn::idl::file::parse(
+                &*cargo,
                 path.join("src/lib.rs"),
-                version,
                 self.features.seeds,
                 false,
             )?;

+ 1 - 1
cli/src/lib.rs

@@ -1389,8 +1389,8 @@ fn extract_idl(cfg: &WithPath<Config>, file: &str) -> Result<Option<Idl>> {
     let cargo = Manifest::discover_from_path(manifest_from_path)?
         .ok_or_else(|| anyhow!("Cargo.toml not found"))?;
     anchor_syn::idl::file::parse(
+        &*cargo,
         &*file,
-        cargo.version(),
         cfg.features.seeds,
         cfg.features.safety_checks,
     )

+ 1 - 0
lang/syn/Cargo.toml

@@ -17,6 +17,7 @@ seeds = []
 
 [dependencies]
 arrayref = "0.3.6"
+cargo_toml = "0.9.2"
 proc-macro2 = { version = "1.0", features=["span-locations"]}
 proc-macro2-diagnostics = "0.9"
 quote = "1.0"

+ 25 - 2
lang/syn/src/idl/file.rs

@@ -4,6 +4,7 @@ use crate::parser::{self, accounts, error, program};
 use crate::Ty;
 use crate::{AccountField, AccountsStruct, StateIx};
 use anyhow::Result;
+use cargo_toml::Manifest;
 use heck::MixedCase;
 use quote::ToTokens;
 use std::collections::{HashMap, HashSet};
@@ -15,8 +16,8 @@ const ERROR_CODE_OFFSET: u32 = 6000;
 
 // Parse an entire interface file.
 pub fn parse(
+    cargo: &Manifest,
     filename: impl AsRef<Path>,
-    version: String,
     seeds_feature: bool,
     safety_checks: bool,
 ) -> Result<Option<Idl>> {
@@ -241,8 +242,30 @@ pub fn parse(
         })
         .collect::<Vec<IdlConst>>();
 
+    let version = cargo
+        .package
+        .as_ref()
+        .map(|p| p.version.clone())
+        .expect("Cargo.toml must have package version");
+
+    let layout_version = {
+        let is_deprecated_layout = cargo
+            .dependencies
+            .get("anchor-lang")
+            .unwrap()
+            .req_features()
+            .iter()
+            .find(|f| f.as_str() == "deprecated-layout")
+            .is_some();
+        if is_deprecated_layout {
+            None
+        } else {
+            Some("0.1.0".to_string())
+        }
+    };
+
     Ok(Some(Idl {
-        layout_version: "0.1.0".to_string(),
+        layout_version,
         version,
         name: p.name.to_string(),
         state,

+ 2 - 1
lang/syn/src/idl/mod.rs

@@ -8,7 +8,8 @@ pub mod pda;
 #[serde(rename_all = "camelCase")]
 pub struct Idl {
     // Version of the idl protocol.
-    pub layout_version: String,
+    #[serde(skip_serializing_if = "Option::is_none", default)]
+    pub layout_version: Option<String>,
     // Version of the program.
     pub version: String,
     pub name: String,

+ 1 - 1
tests/deprecated-layout/migrations/deploy.ts

@@ -9,4 +9,4 @@ module.exports = async function (provider) {
   anchor.setProvider(provider);
 
   // Add your deploy script here.
-}
+};

+ 8 - 28
tests/deprecated-layout/tests/deprecated-layout.ts

@@ -1,6 +1,6 @@
 import * as assert from "assert";
-import * as anchor from '@project-serum/anchor';
-import { Program, BorshAccountHeader } from '@project-serum/anchor';
+import * as anchor from "@project-serum/anchor";
+import { Program, BorshAccountHeader } from "@project-serum/anchor";
 import { Keypair } from "@solana/web3.js";
 import { DeprecatedLayout } from "../target/types/deprecated_layout";
 import { NewLayout } from "../target/types/new_layout";
@@ -10,8 +10,6 @@ describe("deprecated-layout", () => {
   anchor.setProvider(anchor.Provider.env());
 
   it("Has an 8 byte discriminator", async () => {
-    anchor.utils.features.set("deprecated-layout");
-
     const program = anchor.workspace
       .DeprecatedLayout as Program<DeprecatedLayout>;
 
@@ -27,7 +25,9 @@ describe("deprecated-layout", () => {
     const data = accountInfo.data;
     const header = data.slice(0, 8);
     const accountData = data.slice(8);
-    const expectedDiscriminator = BorshAccountHeader.discriminator("data");
+    const expectedDiscriminator = new BorshAccountHeader(
+      program.idl
+    ).discriminator("data");
 
     assert.ok(
       "0xce9c3bbc124ff0e8" ===
@@ -39,21 +39,9 @@ describe("deprecated-layout", () => {
 
     const dataAccount = await program.account.data.fetch(dataKeypair.publicKey);
     assert.ok(dataAccount.data.toNumber() === 2);
-
-    assert.rejects(
-      async () => {
-        anchor.utils.features.unset("deprecated-layout");
-        await program.account.data.fetch(dataKeypair.publicKey);
-      },
-      (err) => {
-        return err.toString() === "Error: Invalid account discriminator";
-      }
-    );
   });
 
   it("Has a 4 byte discriminator and 8 byte header", async () => {
-    anchor.utils.features.unset("deprecated-layout");
-
     const program = anchor.workspace.NewLayout as Program<NewLayout>;
 
     const dataKeypair = Keypair.generate();
@@ -69,7 +57,9 @@ describe("deprecated-layout", () => {
     const header = data.slice(0, 8);
     const givenDiscriminator = header.slice(2, 6);
     const accountData = data.slice(8);
-    const expectedDiscriminator = BorshAccountHeader.discriminator("data");
+    const expectedDiscriminator = new BorshAccountHeader(
+      program.idl
+    ).discriminator("data");
 
     assert.ok(
       "0xce9c3bbc" === anchor.utils.bytes.hex.encode(expectedDiscriminator)
@@ -84,15 +74,5 @@ describe("deprecated-layout", () => {
 
     const dataAccount = await program.account.data.fetch(dataKeypair.publicKey);
     assert.ok(dataAccount.data.toNumber() === 2);
-
-    assert.rejects(
-      async () => {
-        anchor.utils.features.set("deprecated-layout");
-        await program.account.data.fetch(dataKeypair.publicKey);
-      },
-      (err) => {
-        return err.toString() === "Error: Invalid account discriminator";
-      }
-    );
   });
 });

+ 12 - 14
ts/src/coder/borsh/accounts.ts

@@ -8,7 +8,6 @@ import { Idl, IdlTypeDef } from "../../idl.js";
 import { IdlCoder } from "./idl.js";
 import { AccountsCoder } from "../index.js";
 import { accountSize } from "../common.js";
-import { FeatureSet } from "../../utils/features";
 
 /**
  * Number of bytes of the account header.
@@ -34,14 +33,14 @@ export class BorshAccountsCoder<A extends string = string>
   /**
    * IDL whose acconts will be coded.
    */
-	private idl: Idl;
+  private idl: Idl;
 
-	/**
-	 * Header configuration.
-	 */
-	readonly header: BorshAccountHeader;
+  /**
+   * Header configuration.
+   */
+  private header: BorshAccountHeader;
 
-	public constructor(idl: Idl) {
+  public constructor(idl: Idl) {
     if (idl.accounts === undefined) {
       this.accountLayouts = new Map();
       return;
@@ -52,7 +51,7 @@ export class BorshAccountsCoder<A extends string = string>
 
     this.accountLayouts = new Map(layouts);
     this.idl = idl;
-		this.header = new BorshAccountHeader(idl);
+    this.header = new BorshAccountHeader(idl);
   }
 
   public async encode<T = any>(accountName: A, account: T): Promise<Buffer> {
@@ -105,14 +104,13 @@ export class BorshAccountsCoder<A extends string = string>
 }
 
 export class BorshAccountHeader {
-
-	constructor(_idl: Idl) {}
+  constructor(private _idl: Idl) {}
 
   /**
    * Returns the default account header for an account with the given name.
    */
   public encode(accountName: string, nameSpace?: string): Buffer {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return this.discriminator(accountName, nameSpace);
     } else {
       return Buffer.concat([
@@ -138,7 +136,7 @@ export class BorshAccountHeader {
   }
 
   public discriminatorSize(): number {
-    return this._features.deprecatedLayout
+    return this._idl.layoutVersion === undefined
       ? DEPRECATED_ACCOUNT_DISCRIMINATOR_SIZE
       : ACCOUNT_DISCRIMINATOR_SIZE;
   }
@@ -147,7 +145,7 @@ export class BorshAccountHeader {
    * Returns the account data index at which the discriminator starts.
    */
   public discriminatorOffset(): number {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return 0;
     } else {
       return 2;
@@ -165,7 +163,7 @@ export class BorshAccountHeader {
    * Returns the discriminator from the given account data.
    */
   public parseDiscriminator(data: Buffer): Buffer {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return data.slice(0, 8);
     } else {
       return data.slice(2, 6);

+ 9 - 10
ts/src/coder/borsh/event.ts

@@ -6,7 +6,6 @@ import { Idl, IdlEvent, IdlTypeDef } from "../../idl.js";
 import { Event, EventData } from "../../program/event.js";
 import { IdlCoder } from "./idl.js";
 import { EventCoder } from "../index.js";
-import { FeatureSet } from "../../utils/features";
 
 export class BorshEventCoder implements EventCoder {
   /**
@@ -19,17 +18,17 @@ export class BorshEventCoder implements EventCoder {
    */
   private discriminators: Map<string, string>;
 
-	/**
-	 * Header configuration.
-	 */
-	private header: EventHeader;
+  /**
+   * Header configuration.
+   */
+  private header: EventHeader;
 
   public constructor(idl: Idl) {
     if (idl.events === undefined) {
       this.layouts = new Map();
       return;
     }
-		this.header = new EventHeader(features);
+    this.header = new EventHeader(idl);
     const layouts: [string, Layout<any>][] = idl.events.map((event) => {
       let eventTypeDef: IdlTypeDef = {
         name: event.name,
@@ -89,10 +88,10 @@ export function eventDiscriminator(name: string): Buffer {
 }
 
 class EventHeader {
-	constructor(private _features: FeatureSet) {}
+  constructor(private _idl: Idl) {}
 
   public parseDiscriminator(data: Buffer): Buffer {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return data.slice(0, 8);
     } else {
       return data.slice(0, 4);
@@ -100,7 +99,7 @@ class EventHeader {
   }
 
   public size(): number {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return 8;
     } else {
       return 4;
@@ -108,7 +107,7 @@ class EventHeader {
   }
 
   public discriminator(name: string): Buffer {
-    if (this._features.deprecatedLayout) {
+    if (this._idl.layoutVersion === undefined) {
       return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 8);
     } else {
       return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 4);

+ 4 - 4
ts/src/coder/borsh/state.ts

@@ -3,19 +3,19 @@ import { Layout } from "buffer-layout";
 import { sha256 } from "js-sha256";
 import { Idl } from "../../idl.js";
 import { IdlCoder } from "./idl.js";
-import * as features from '../../utils/features';
+import * as features from "../../utils/features";
 import { BorshAccountHeader } from "./accounts";
 
 export class BorshStateCoder {
   private layout: Layout;
-	private header: BorshAccountHeader;
+  readonly header: BorshAccountHeader;
 
-  public constructor(idl: Idl, header: BorshAccountHeader) {
+  public constructor(idl: Idl) {
     if (idl.state === undefined) {
       throw new Error("Idl state not defined.");
     }
     this.layout = IdlCoder.typeDefLayout(idl.state.struct, idl.types);
-		this.header = header;
+    this.header = new BorshAccountHeader(idl);
   }
 
   public async encode<T = any>(name: string, account: T): Promise<Buffer> {

+ 3 - 4
ts/src/program/namespace/state.ts

@@ -36,7 +36,7 @@ export default class StateFactory {
     if (idl.state === undefined) {
       return undefined;
     }
-    return new StateClient(idl, programId, provider, coder);
+    return new StateClient(idl, programId, provider, coder as BorshCoder);
   }
 }
 
@@ -74,7 +74,6 @@ export class StateClient<IDL extends Idl> {
   private _programId: PublicKey;
 
   private _address: PublicKey;
-  private _coder: Coder;
   private _idl: IDL;
   private _sub: Subscription | null;
 
@@ -88,7 +87,7 @@ export class StateClient<IDL extends Idl> {
     /**
      * Returns the coder.
      */
-    public readonly coder: Coder = new BorshCoder(idl)
+    public readonly coder: BorshCoder = new BorshCoder(idl)
   ) {
     this._idl = idl;
     this._programId = programId;
@@ -175,7 +174,7 @@ export class StateClient<IDL extends Idl> {
     }
 
     const expectedDiscriminator = await stateDiscriminator(state.struct.name);
-    const discriminator = BorshAccountHeader.parseDiscriminator(
+    const discriminator = this.coder.state.header.parseDiscriminator(
       accountInfo.data
     );
     if (discriminator.compare(expectedDiscriminator)) {

+ 6 - 8
ts/src/utils/features.ts

@@ -1,16 +1,8 @@
 export const Features = {
   DeprecatedState: "anchor-deprecated-state",
   DebugLogs: "debug-logs",
-  DeprecatedLayout: "deprecated-layout",
 };
 
-const _AVAILABLE_FEATURES = new Set([
-  Features.DeprecatedState,
-  Features.DebugLogs,
-  Features.DeprecatedLayout,
-]);
-const _FEATURES = new Map();
-
 export function set(key: string) {
   if (!_AVAILABLE_FEATURES.has(key)) {
     throw new Error("Invalid feature");
@@ -28,3 +20,9 @@ export function unset(key: string) {
 export function isSet(key: string): boolean {
   return _FEATURES.get(key) === true;
 }
+
+const _AVAILABLE_FEATURES = new Set([
+  Features.DeprecatedState,
+  Features.DebugLogs,
+]);
+const _FEATURES = new Map();