diff --git a/packages/pointers/src/pointer.test.ts b/packages/pointers/src/pointer.test.ts index 42f9b873..0b12c7e3 100644 --- a/packages/pointers/src/pointer.test.ts +++ b/packages/pointers/src/pointer.test.ts @@ -178,9 +178,9 @@ describe("type guards", () => { }, { schema: { - id: "schema:ethdebug/format/pointer/templates" + id: "schema:ethdebug/format/pointer/template" }, - guard: Pointer.isTemplates + guard: Pointer.isTemplate }, ] as const; diff --git a/packages/pointers/src/test-cases.ts b/packages/pointers/src/test-cases.ts index 0e60ed85..91830a5b 100644 --- a/packages/pointers/src/test-cases.ts +++ b/packages/pointers/src/test-cases.ts @@ -1,7 +1,6 @@ import { singleSourceCompilation, findExamplePointer, - findExampleTemplates, type ObserveTraceOptions } from "../test/index.js"; import { type Cursor, Data } from "./index.js"; @@ -81,8 +80,7 @@ const structStorageTest: ObserveTraceTest<{ }; const stringStorageTest: ObserveTraceTest = { - pointer: findExamplePointer("solidity-string-storage"), - templates: findExampleTemplates(), + pointer: findExamplePointer("string-storage-contract-variable-slot"), compileOptions: singleSourceCompilation({ path: "StringStorage.sol", diff --git a/packages/pointers/test/examples.ts b/packages/pointers/test/examples.ts index 1e7eed81..1c4d7663 100644 --- a/packages/pointers/test/examples.ts +++ b/packages/pointers/test/examples.ts @@ -16,15 +16,3 @@ export const findExamplePointer = (() => { examplePointers .find(pointer => JSON.stringify(pointer).includes(text))!; })(); - -export const findExampleTemplates = (() => { - const { - schema: { - examples: [exampleTemplates] - } - } = describeSchema({ - schema: { id: "schema:ethdebug/format/pointer/templates" } - }) as { schema: { examples: Pointer.Templates[] } }; - - return (): Pointer.Templates => exampleTemplates; -})(); diff --git a/packages/pointers/test/index.ts b/packages/pointers/test/index.ts index 6ac60131..96114464 100644 --- a/packages/pointers/test/index.ts +++ b/packages/pointers/test/index.ts @@ -11,7 +11,7 @@ export { export { deployContract, } from "./deploy.js"; -export { findExamplePointer, findExampleTemplates } from "./examples.js"; +export { findExamplePointer } from "./examples.js"; export { observeTrace, diff --git a/packages/web/docs/implementation-guides/pointers/testing/test-cases/string-storage.mdx b/packages/web/docs/implementation-guides/pointers/testing/test-cases/string-storage.mdx index 9f0f959a..bc52f59a 100644 --- a/packages/web/docs/implementation-guides/pointers/testing/test-cases/string-storage.mdx +++ b/packages/web/docs/implementation-guides/pointers/testing/test-cases/string-storage.mdx @@ -25,5 +25,5 @@ value. ## Tested pointer diff --git a/packages/web/spec/pointer/templates.mdx b/packages/web/spec/pointer/templates.mdx index 26d4931c..6f7109e9 100644 --- a/packages/web/spec/pointer/templates.mdx +++ b/packages/web/spec/pointer/templates.mdx @@ -6,28 +6,13 @@ import SchemaViewer from "@site/src/components/SchemaViewer"; # Pointer templates -This format provides the concept of **pointer templates** to allow +This format provides the concept of a **pointer template** to allow deduplicating representations. Pointer templates are defined to specify the variables they expect in scope and the pointer definition that uses those variables. -## Template schema {#template-schema} - -The following schema defines how to represent a single pointer template. -See [below](#templates-schema) for how to organize pointer templates by name. - - -## Organizing templates {#templates-schema} - -The **ethdebug/format/pointer/templates** schema specifies how to represent -a collection of pointer templates by name. - - diff --git a/packages/web/src/schemas.ts b/packages/web/src/schemas.ts index f2b4ec47..65a2a87b 100644 --- a/packages/web/src/schemas.ts +++ b/packages/web/src/schemas.ts @@ -116,13 +116,8 @@ export const schemaIndex: SchemaIndex = { href: "/spec/pointer/expression" }, - "schema:ethdebug/format/pointer/templates": { - href: "/spec/pointer/templates" - }, - - "schema:ethdebug/format/pointer/templates#/$defs/Template": { - title: "Pointer template schema", - href: "/spec/pointer/templates#template-schema" + "schema:ethdebug/format/pointer/template": { + href: "/spec/pointer/template" }, ...Object.entries({ diff --git a/schemas/pointer.schema.yaml b/schemas/pointer.schema.yaml index d8adf1d1..9346ff36 100644 --- a/schemas/pointer.schema.yaml +++ b/schemas/pointer.schema.yaml @@ -153,6 +153,94 @@ examples: - # example template reference define: - "contract-variable-slot": 0 + "string-storage-contract-variable-slot": 0 in: - template: "solidity-string-storage" + group: + # for short strings, the length is stored as 2n in the last byte of slot + - name: "length-flag" + location: storage + slot: "string-storage-contract-variable-slot" + offset: + $difference: [$wordsize, 1] + length: 1 + + # define the region representing the string data itself conditionally + # based on odd or even length data + - if: + $remainder: + - $sum: + - $read: "length-flag" + - 1 + - 2 + + # short string case (flag is even) + then: + define: + "string-length": + $quotient: [{ $read: "length-flag" }, 2] + in: + name: "string" + location: storage + slot: "string-storage-contract-variable-slot" + offset: 0 + length: "string-length" + + # long string case (flag is odd) + else: + group: + # long strings may use full word to describe length as 2n+1 + - name: "long-string-length-data" + location: storage + slot: "string-storage-contract-variable-slot" + offset: 0 + length: $wordsize + + - define: + "string-length": + $quotient: + - $difference: + - $read: "long-string-length-data" + - 1 + - 2 + + "start-slot": + $keccak256: + - $wordsized: "string-storage-contract-variable-slot" + + "total-slots": + # account for both zero and nonzero slot remainders by adding + # $wordsize-1 to the length before dividing + $quotient: + - $sum: ["string-length", { $difference: [$wordsize, 1] }] + - $wordsize + in: + list: + count: "total-slots" + each: "i" + is: + define: + "current-slot": + $sum: ["start-slot", "i"] + "previous-length": + $product: ["i", $wordsize] + in: + # conditional based on whether this is the last slot: + # is the string length longer than the previous length + # plus this whole slot? + if: + $difference: + - "string-length" + - $sum: ["previous-length", "$wordsize"] + then: + # include the whole slot + name: "string" + location: storage + slot: "current-slot" + else: + # include only what's left in the string + name: "string" + location: storage + slot: "current-slot" + offset: 0 + length: + $difference: ["string-length", "previous-length"] diff --git a/schemas/pointer/template.schema.yaml b/schemas/pointer/template.schema.yaml new file mode 100644 index 00000000..5324685a --- /dev/null +++ b/schemas/pointer/template.schema.yaml @@ -0,0 +1,34 @@ +$schema: "https://json-schema.org/draft/2020-12/schema" +$id: "schema:ethdebug/format/pointer/template" + +title: ethdebug/format/pointer/template +description: | + A schema for representing a pointer defined in terms of some variables whose + values are to be provided when invoking the template. + +type: object +properties: + expect: + title: Template variables + description: | + An array of variable identifiers used in the definition of the + pointer template. + type: array + items: + $ref: "schema:ethdebug/format/pointer/identifier" + additionalItems: false + + for: + $ref: "schema:ethdebug/format/pointer" + +required: + - expect + - for + +additionalProperties: false + +examples: + - expect: ["slot"] + for: + location: storage + slot: "slot" diff --git a/schemas/pointer/templates.schema.yaml b/schemas/pointer/templates.schema.yaml deleted file mode 100644 index e6bca853..00000000 --- a/schemas/pointer/templates.schema.yaml +++ /dev/null @@ -1,141 +0,0 @@ -$schema: "https://json-schema.org/draft/2020-12/schema" -$id: "schema:ethdebug/format/pointer/templates" - -title: ethdebug/format/pointer/templates -description: | - A schema for representing a named set of pointer templates. - -type: object - -patternProperties: - "^[a-zA-Z_\\-]+[a-zA-Z0-9$_\\-]*$": - $ref: "#/$defs/Template" - -additionalProperties: false - -$defs: - Template: - title: Pointer template schema - type: object - properties: - expect: - title: Template variables - description: | - An array of variable identifiers used in the definition of the - pointer template. - type: array - items: - $ref: "schema:ethdebug/format/pointer/identifier" - additionalItems: false - - for: - $ref: "schema:ethdebug/format/pointer" - - required: - - expect - - for - - additionalProperties: false - - examples: - - expect: ["slot"] - for: - location: storage - slot: "slot" - -examples: - - "solidity-string-storage": - expect: - - "contract-variable-slot" - for: - group: - # for short strings, the length is stored as 2n in the last byte of slot - - name: "length-flag" - location: storage - slot: "contract-variable-slot" - offset: - $difference: [$wordsize, 1] - length: 1 - - # define the region representing the string data itself conditionally - # based on odd or even length data - - if: - $remainder: - - $sum: - - $read: "length-flag" - - 1 - - 2 - - # short string case (flag is even) - then: - define: - "string-length": - $quotient: [{ $read: "length-flag" }, 2] - in: - name: "string" - location: storage - slot: "contract-variable-slot" - offset: 0 - length: "string-length" - - # long string case (flag is odd) - else: - group: - # long strings may use full word to describe length as 2n+1 - - name: "long-string-length-data" - location: storage - slot: "contract-variable-slot" - offset: 0 - length: $wordsize - - - define: - "string-length": - $quotient: - - $difference: - - $read: "long-string-length-data" - - 1 - - 2 - - "start-slot": - $keccak256: - - $wordsized: "contract-variable-slot" - - "total-slots": - # account for both zero and nonzero slot remainders by adding - # $wordsize-1 to the length before dividing - $quotient: - - $sum: ["string-length", { $difference: [$wordsize, 1] }] - - $wordsize - in: - list: - count: "total-slots" - each: "i" - is: - define: - "current-slot": - $sum: ["start-slot", "i"] - "previous-length": - $product: ["i", $wordsize] - in: - # conditional based on whether this is the last slot: - # is the string length longer than the previous length - # plus this whole slot? - if: - $difference: - - "string-length" - - $sum: ["previous-length", "$wordsize"] - then: - # include the whole slot - name: "string" - location: storage - slot: "current-slot" - else: - # include only what's left in the string - name: "string" - location: storage - slot: "current-slot" - offset: 0 - length: - $difference: ["string-length", "previous-length"] - -