Solidityダイナミックアレイアセンブリ開発チュートリアル

Solidityスマートコントラクトを開発するときは、アセンブリを使用しないことを常にお勧めします。ただし、他に選択肢がない場合もあるため、Solidityアセンブリの開発に関する知識を学ぶ必要があります。このチュートリアルでは、Solidityアセンブリ開発で動的バイト配列を使用する方法を学習します。

独自の使い慣れた言語学習イーサネットスクエアDApp開発を使用してください
Java | Php | Python | .Net / C# | golang | Node.JS | Flutter / Dart

1.リミックスエディタを使用します

まず、この単純な契約をリミックスエディタに貼り付けましょう

pragma solidity ^0.5.10;

contract AssemblyArrays {
  
  bytes testArray;
  
  function getLength() public view returns (uint256) {
      return testArray.length;
  }
  
  function getElement(uint256 index) public view returns (bytes1) {
      return testArray[index];
  }
  
  function pushElement(bytes1 value) public {
      testArray.push(value);
  }
  
  function updateElement(bytes1 value, uint256 index) public {
      testArray[index] = value;
  }
}

まず、Remixエディターについて理解します。最初にコンパイラのバージョンを選択し、次にコントラクトをコンパイルし、コントラクトをデプロイし、いくつかの機能を実行してから、デバッグする必要があります。

2.アセンブリコードの最初の行

次に、getLength関数を変更して、アセンブリコードの最初の行を記述します。

function getLength() public view returns (uint256) {
  bytes memory memoryTestArray = testArray;
  uint256 result;
  assembly {
    result := mload(memoryTestArray)
  }
  return result;
}

上記の数行のコードで多くのことが起こりました。アセンブラーはそのようなもので、非常に単純な機能を実現するには、多くのコードも必要です。我々はされますtestArrayこの問題をこの資料の焦点であるので、メモリに記憶装置からコピー。将来的にはストレージスロットについて話すことができます。

アセンブリ言語ブロックを詳しく調べる前に、アセンブリ命令は32バイトのワードで動作することに注意してください。したがって、mload命令は、memoryTestArrayが指す
32バイトのメモリ位置をスタックにプッシュします。

3.ブレークポイント設定とSolidityアセンブリコードのシングルステップ実行

今それをデバッグします。Remixでは、行番号をクリックしてブレークポイントを設定できます。11行目にブレークポイントを設定して、次のようにします。
ここに写真の説明を挿入

getLength関数を更新した後、必ずコントラクトをコンパイルして再デプロイしてください。ここで、pushElement関数を呼び出してバイト0x05を配列に挿入してから、を呼び出すgetLengthと、関数は1を返すはずです。

getLength呼び出した後、デバッグできます。下部パネルの最後の呼び出しで[デバッグ]ボタンをクリックすると、左側のサイドバーにデバッガーが開きます。次のブレークポイントにジャンプする早送り用のボタン(fast_forward:など)があります。それをクリックしてみましょう。別のコンパイラまたは別の設定を使用する場合、それは完全に同じではない可能性がありますが、コアは同じになります。基本的な考え方はmload、実行前にデバッガーを取得することです。私の環境では、#0871命令です。
ここに写真の説明を挿入

4.Solidityアセンブリコードがスタックに与える影響を表示します

次に、デバッガーのサイドバーにあるスタック/スタックの内容を見てみましょう。

ここに写真の説明を挿入

スタックの最上位では、位置0は0x0…80です。これは、mload命令のパラメーターとして使用される、メモリー内のmemoryTestArrayの場所です。

5.Solidityアセンブリコードがメモリに与える影響を表示します

それでは、アドレス0x0 ... 80から始まる、デバッガーサイドバーの「メモリ」セクションを見てみましょう。

ここに写真の説明を挿入

31バイトの0x00、1バイトの0x01、1バイトの0x05、31バイトの0x00があります。これは少し混乱する可能性があるので、一歩下がって、1バイト(8ビット)が2つの16進数で表されていることに注意してください(1つの16進数は4桁を表します)。同様に、0x10の16進数の10進数は16に等しくなります。したがって、メモリ内では、場所0x80は16バイトを保持し、場所0x90(0x80 + 0x10)は次の16バイトを保持し、場所0xa0(0x90 + 0x10)は次の16バイトを保持し、場所0xb0は最後の16を保持します。バイト。アセンブリ内の命令は32バイト単位で動作するため、mload(0x80)を呼び出すと、メモリ位置0x80から32バイトを取得して、スタックに配置します。

6.mload命令をステップスルーします

実際の実装を見てみましょう。デバッガーの「シングルステップイン」ボタン(つまり下向き矢印)をクリックして、mload命令を実行してみましょう。次に、スタックの一番上を見てください。

ここに写真の説明を挿入

mloadこの命令は、スタックの最上位の内容(0x0…80)をフェッチしてから、メモリ内の位置0x0…1に32バイトをプッシュします。これは、メモリ内のバイト配列を理解するための最も重要なポイントです。最初の32バイトは配列の長さを格納します。

pushElement関数を呼び出して、要素0x06を配列に挿入してみてください次に、呼び出しgetLengthて再度デバッグします。同様に、mload32バイトがメモリ位置0x80からロードされますが、今回はメモリの内容は0x0 ... 2です。新しい要素を追加すると、Solidityは配列のサイズを更新します。

メモリ内で変更されたもう1つの点は、位置0xa0が0x050600 ... 00になったことです。したがって、メモリ内では、バイト配列変数はその長さを最初の32バイトに格納してから、特定のメンバーの格納を開始します。最初に0x05を押し、次に0x06を押します。

ここに写真の説明を挿入

7.Solidityアセンブリを使用してgetLengthメソッドを書き直します

さらにいくつかの要素をプッシュし、呼び出しgetLengthてデバッグし、メモリ内の新しいバイトを確認してください。我々は場合getElementアセンブリに変換し、プロセスが明確になるだろう。

function getElement(uint256 index) public view returns (bytes1) {
    uint256 length = getLength();
    require(index < length);
    bytes memory memoryTestArray = testArray;
    bytes1 result;
    assembly {
      let wordIndex := div(index, 32)
      let initialElement := add(memoryTestArray, 32)
      let resultWord := mload(add(initialElement, mul(wordIndex, 32)))
      let indexInWord := mod(index, 32)
      result := shl(mul(indexInWord, 8), resultWord)
    }
    return result;
}

まあ、これはあなたを少し怖がらせるかもしれません!ゆっくりと撫でましょう。

最初の非常に重要なことはrequireindexそれが範囲外ではないことを確認するためのステートメントを追加したことです。これは、mloadを呼び出すときに非常に重要です。ロードするメモリの場所が正しいことを確認する必要があります。そうしないと、呼び出し元がアクセスしてはならない情報が漏洩し、契約が攻撃の重大なリスクにさらされる可能性があります。 。

次に、アセンブリコードブロックを見てみましょう。以来mload32のバイトを一度に読み込み、それだけで1バイトを読み取ることは容易ではありません。インデックスを32で割って切り上げると、検出されるメンバーの32バイトのシーケンス番号が取得されます。例えば:

div(0, 32) = 0
div(18, 32) = 0
div(32, 32) = 1
div(65, 32) = 2

かなり良さそうです。ただし、me​​moryTestArrayが指す組み込みメモリの最初のワード(32バイト)は、ストレージアレイの長さであることを忘れないでください。したがって、最初の配列メンバーを見つけるには、32バイトを追加する必要があります。これらすべての要素を考慮した後、必要なものを含む1バイトのワード(32バイト)をロードできます。

memoryTestArray各ワードには32バイトがあるため、メモリ位置に32バイトを追加して配列の長さをスキップし、wordIndexに32を掛けたものを追加します。

しかし、それはまだ終わっていません。次に、このワードから正確に1バイトを抽出する必要があります。これを行うには、ワード内のバイトのインデックスを見つける必要があります。これは、単語インデックスの残りを32で割ったものであり、mod指示によって取得できます例えば:

mod(0, 32) = 0
mod(18, 32) = 18
mod(32, 32) = 0
mod(65, 32) = 1

はい、最後のステップを完了して、バイトを抽出しましょう。最前面のバイトを作成するには、必要なビット数を左に移動します。shl命令は一度に1ビット移動するため、指定されたビット数を移動するには、indexInWordに8を掛ける必要があります。

このバイトで始まる32バイトのワードを結果変数に割り当てると、
そのタイプをとして宣言したため、他のすべてのバイトが削除さbytes1ます。


元のリンク:Solidityコンパイルによって開発された動的アレイ— Huizhi.com

おすすめ

転載: blog.csdn.net/shebao3333/article/details/107812063