99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

存儲中狀態(tài)變量的布局

2022-05-24 11:38 更新

合約的狀態(tài)變量以緊湊的方式存儲在存儲中,以便多個值有時使用相同的存儲槽。除了動態(tài)大小的數(shù)組和映射(見下文)之外,數(shù)據(jù)以連續(xù)的方式存儲在以第一個狀態(tài)變量開頭的項接一項地存儲,該狀態(tài)變量存儲在插槽中。對于每個變量,大小(以字節(jié)為單位)根據(jù)其類型確定。如果可能,需要少于 32 個字節(jié)的多個連續(xù)項目將打包到單個存儲槽中,具體取決于以下規(guī)則:0

  • 存儲槽中的第一個項目以較低的順序?qū)R存儲。
  • 值類型僅使用存儲它們所需的字節(jié)數(shù)。
  • 如果值類型不適合存儲槽的剩余部分,則該值類型將存儲在下一個存儲槽中。
  • 結(jié)構(gòu)和陣列數(shù)據(jù)總是啟動一個新的槽,并且根據(jù)這些規(guī)則,它們的物品被緊密地包裝起來。
  • 結(jié)構(gòu)或數(shù)組數(shù)據(jù)后面的項始終啟動新的存儲槽。

對于使用繼承的協(xié)定,狀態(tài)變量的順序由從最基本守合同開始的協(xié)定的 C3 線性化順序確定。如果上述規(guī)則允許,來自不同合約的狀態(tài)變量確實共享相同的存儲槽。

結(jié)構(gòu)和數(shù)組的元素彼此相鄰存儲,就像它們作為單個值給出一樣。

警告

當使用小于 32 字節(jié)的元素時,合約的氣體使用量可能會更高。這是因為 EVM 一次在 32 個字節(jié)上運行。因此,如果元素小于此值,則 EVM 必須使用更多操作,以便將元素的大小從 32 個字節(jié)減少到所需的大小。

如果要處理存儲值,則使用減小大小的類型可能會有所幫助,因為編譯器會將多個元素打包到一個存儲槽中,從而將多個讀取或?qū)懭牒喜⒌絾蝹€操作中。但是,如果不同時讀取或?qū)懭氩壑械乃兄?,則可能會產(chǎn)生相反的效果:當將一個值寫入多值存儲槽時,必須首先讀取該存儲槽,然后將其與新值組合,以便不會破壞同一槽中的其他數(shù)據(jù)。

在處理函數(shù)參數(shù)或內(nèi)存值時,沒有固有的好處,因為編譯器不打包這些值。

最后,為了允許 EVM 對此進行優(yōu)化,請確保嘗試對存儲變量和成員進行排序,以便它們可以緊密地打包。例如,按 的順序聲明存儲變量,而不是 ,因為前者僅占用兩個存儲槽,而后者將占用三個。structuint128, uint128, uint256uint128, uint256, uint128

注意

存儲中狀態(tài)變量的布局被認為是 Solidity 外部接口的一部分,因為存儲指針可以傳遞到庫。這意味著對本節(jié)中概述的規(guī)則的任何更改都被視為語言的重大更改,并且由于其關(guān)鍵性,在執(zhí)行之前應(yīng)非常仔細地考慮。如果發(fā)生這樣的重大更改,我們希望發(fā)布一種兼容模式,在該模式下,編譯器將生成支持舊布局的字節(jié)碼。

映射和動態(tài)數(shù)組

由于映射和動態(tài)大小的數(shù)組類型不可預(yù)測,因此不能存儲在它們之前和之后的狀態(tài)變量“之間”。相反,根據(jù)上述規(guī)則,它們被認為僅占用32個字節(jié),并且它們包含的元素從使用Keccak-256哈希計算的不同存儲槽開始存儲。

假設(shè)映射或陣列的存儲位置在應(yīng)用存儲布局規(guī)則后最終成為插槽。對于動態(tài)數(shù)組,此槽存儲數(shù)組中的元素數(shù)(字節(jié)數(shù)組和字符串是例外,請參閱下文)。對于映射,該槽保持為空,但仍需要確保即使有兩個映射彼此相鄰,其內(nèi)容最終也會位于不同的存儲位置。p

數(shù)組數(shù)據(jù)從 開始,其布局方式與靜態(tài)大小的數(shù)組數(shù)據(jù)相同:一個元素接一個,如果元素的長度不超過 16 個字節(jié),則可能共享存儲槽。動態(tài)數(shù)組的動態(tài)數(shù)組以遞歸方式應(yīng)用此規(guī)則。元素的位置,其中類型為 ,計算如下(再次假設(shè)其本身存儲在插槽中):插槽是,并且可以使用從插槽數(shù)據(jù)中獲取元素。keccak256(p)x[i][j]xuint24[][]xpkeccak256(keccak256(p) + i) + floor(j / floor(256 / 24))v(v >> ((j % floor(256 / 24)) * 24)) & type(uint24).max

與映射鍵對應(yīng)的值位于串聯(lián)位置,并且是根據(jù)其類型應(yīng)用于鍵的函數(shù):kkeccak256(h(k) . p).h

  • 對于值類型,將值填充到 32 個字節(jié),其方式與將值存儲在內(nèi)存中時相同。h

  • 對于字符串和字節(jié)數(shù)組,只是未填充的數(shù)據(jù)。h(k)

如果映射值是非值類型,則計算的槽將標記數(shù)據(jù)的開始。例如,如果值為結(jié)構(gòu)類型,則必須添加與結(jié)構(gòu)成員對應(yīng)的偏移量才能到達該成員。

例如,請考慮以下合同:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;


contract C {
    struct S { uint16 a; uint16 b; uint256 c; }
    uint x;
    mapping(uint => mapping(uint => S)) data;
}

我們計算 的存儲位置。映射本身的位置是(前面有 32 個字節(jié)的變量)。這意味著 存儲在 。的類型再次是映射,并且 的數(shù)據(jù)從插槽 開始。結(jié)構(gòu)內(nèi)成員的槽偏移量是因為 并且被打包在單個槽中。這意味著 的插槽是 。該值的類型是 ,因此它使用單個插槽。data[4][9].c1xdata[4]keccak256(uint256(4) . uint256(1))data[4]data[4][9]keccak256(uint256(9) . keccak256(uint256(4) . uint256(1)))cS1abdata[4][9].ckeccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1uint256

bytesstring

bytes并且編碼相同。通常,編碼類似于 ,因為數(shù)組本身有一個插槽,而數(shù)據(jù)區(qū)域是使用該插槽位置的哈希值計算的。但是,對于短值(短于 32 個字節(jié)),數(shù)組元素將與長度一起存儲在同一個插槽中。stringbytes1[]keccak256

特別是:如果數(shù)據(jù)最多是字節(jié)長,則元素存儲在高階字節(jié)(左對齊)中,最低階字節(jié)存儲值。對于存儲長度為或更多字節(jié)的數(shù)據(jù)的字節(jié)數(shù)組,主槽將存儲,并且數(shù)據(jù)將照常存儲在 中。這意味著您可以通過檢查是否設(shè)置了最低位來區(qū)分短數(shù)組和長數(shù)組:短位(未設(shè)置)和長位(設(shè)置)。31length * 232plength * 2 + 1keccak256(p)

注意

目前不支持處理無效編碼的插槽,但將來可能會添加。如果通過 IR 進行編譯,則讀取無效編碼的插槽會導(dǎo)致錯誤。Panic(0x22)

JSON 輸出

可以通過標準 JSON 接口請求合約的存儲布局。輸出是一個 JSON 對象,其中包含兩個鍵和 。該對象是一個數(shù)組,其中每個元素具有以下形式:storagetypesstorage

{
    "astId": 2,
    "contract": "fileA:A",
    "label": "x",
    "offset": 0,
    "slot": "0",
    "type": "t_uint256"
}

上面的示例是源單元的存儲布局,并且contract A { uint x; }fileA

  • astId是狀態(tài)變量聲明的 AST 節(jié)點的 ID

  • contract是合約的名稱,包括其路徑作為前綴

  • label是狀態(tài)變量的名稱

  • offset是存儲槽內(nèi)根據(jù)編碼的偏移量(以字節(jié)為單位)

  • slot是狀態(tài)變量駐留或啟動的存儲槽。此數(shù)字可能非常大,因此其 JSON 值表示為字符串。

  • type是用作變量類型信息鍵的標識符(如下所述)

給定的 ,在本例中表示 中的一個元素,其形式為:typet_uint256types

{
    "encoding": "inplace",
    "label": "uint256",
    "numberOfBytes": "32",
}

哪里

  • encoding數(shù)據(jù)在存儲中的編碼方式,其中可能的值為:

    • inplace:數(shù)據(jù)在存儲中連續(xù)排列(見上文)。

    • mapping:基于Keccak-256哈希的方法(見上文)。

    • dynamic_array:基于Keccak-256哈希的方法(見上文)。

    • bytes:單插槽或基于Keccak-256哈希,具體取決于數(shù)據(jù)大?。ㄒ?a rel="external nofollow" target="_blank" >上文)。

  • label是規(guī)范類型名稱。

  • numberOfBytes是已用字節(jié)數(shù)(作為十進制字符串)。請注意,如果這意味著使用了多個插槽。numberOfBytes > 32

除了上述四種類型之外,某些類型還有額外的信息。映射包含其和類型(再次引用此類型映射中的條目),數(shù)組具有其類型,結(jié)構(gòu)以與頂級相同的格式列出它們(見上文)。keyvaluebasemembersstorage

注意

合約存儲布局的 JSON 輸出格式仍被視為實驗性格式,并且可能會在 Solidity 的非中斷版本中發(fā)生變化。

下面的示例演示協(xié)定及其存儲布局,其中包含值和引用類型、編碼打包的類型以及嵌套類型。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract A {
    struct S {
        uint128 a;
        uint128 b;
        uint[2] staticArray;
        uint[] dynArray;
    }

    uint x;
    uint y;
    S s;
    address addr;
    mapping (uint => mapping (address => bool)) map;
    uint[] array;
    string s1;
    bytes b1;
}
{
  "storage": [
    {
      "astId": 15,
      "contract": "fileA:A",
      "label": "x",
      "offset": 0,
      "slot": "0",
      "type": "t_uint256"
    },
    {
      "astId": 17,
      "contract": "fileA:A",
      "label": "y",
      "offset": 0,
      "slot": "1",
      "type": "t_uint256"
    },
    {
      "astId": 20,
      "contract": "fileA:A",
      "label": "s",
      "offset": 0,
      "slot": "2",
      "type": "t_struct(S)13_storage"
    },
    {
      "astId": 22,
      "contract": "fileA:A",
      "label": "addr",
      "offset": 0,
      "slot": "6",
      "type": "t_address"
    },
    {
      "astId": 28,
      "contract": "fileA:A",
      "label": "map",
      "offset": 0,
      "slot": "7",
      "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
    },
    {
      "astId": 31,
      "contract": "fileA:A",
      "label": "array",
      "offset": 0,
      "slot": "8",
      "type": "t_array(t_uint256)dyn_storage"
    },
    {
      "astId": 33,
      "contract": "fileA:A",
      "label": "s1",
      "offset": 0,
      "slot": "9",
      "type": "t_string_storage"
    },
    {
      "astId": 35,
      "contract": "fileA:A",
      "label": "b1",
      "offset": 0,
      "slot": "10",
      "type": "t_bytes_storage"
    }
  ],
  "types": {
    "t_address": {
      "encoding": "inplace",
      "label": "address",
      "numberOfBytes": "20"
    },
    "t_array(t_uint256)2_storage": {
      "base": "t_uint256",
      "encoding": "inplace",
      "label": "uint256[2]",
      "numberOfBytes": "64"
    },
    "t_array(t_uint256)dyn_storage": {
      "base": "t_uint256",
      "encoding": "dynamic_array",
      "label": "uint256[]",
      "numberOfBytes": "32"
    },
    "t_bool": {
      "encoding": "inplace",
      "label": "bool",
      "numberOfBytes": "1"
    },
    "t_bytes_storage": {
      "encoding": "bytes",
      "label": "bytes",
      "numberOfBytes": "32"
    },
    "t_mapping(t_address,t_bool)": {
      "encoding": "mapping",
      "key": "t_address",
      "label": "mapping(address => bool)",
      "numberOfBytes": "32",
      "value": "t_bool"
    },
    "t_mapping(t_uint256,t_mapping(t_address,t_bool))": {
      "encoding": "mapping",
      "key": "t_uint256",
      "label": "mapping(uint256 => mapping(address => bool))",
      "numberOfBytes": "32",
      "value": "t_mapping(t_address,t_bool)"
    },
    "t_string_storage": {
      "encoding": "bytes",
      "label": "string",
      "numberOfBytes": "32"
    },
    "t_struct(S)13_storage": {
      "encoding": "inplace",
      "label": "struct A.S",
      "members": [
        {
          "astId": 3,
          "contract": "fileA:A",
          "label": "a",
          "offset": 0,
          "slot": "0",
          "type": "t_uint128"
        },
        {
          "astId": 5,
          "contract": "fileA:A",
          "label": "b",
          "offset": 16,
          "slot": "0",
          "type": "t_uint128"
        },
        {
          "astId": 9,
          "contract": "fileA:A",
          "label": "staticArray",
          "offset": 0,
          "slot": "1",
          "type": "t_array(t_uint256)2_storage"
        },
        {
          "astId": 12,
          "contract": "fileA:A",
          "label": "dynArray",
          "offset": 0,
          "slot": "3",
          "type": "t_array(t_uint256)dyn_storage"
        }
      ],
      "numberOfBytes": "128"
    },
    "t_uint128": {
      "encoding": "inplace",
      "label": "uint128",
      "numberOfBytes": "16"
    },
    "t_uint256": {
      "encoding": "inplace",
      "label": "uint256",
      "numberOfBytes": "32"
    }
  }
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號