Solidity

錯誤的跳轉陣列穩定性和數據位置

  • May 15, 2017

我對結構內的數組有一個奇怪的問題。我將範常式式碼複製給您,以便您理解:

 Foo[] public foos;

 struct Foo {
   uint fooX;
   uint[] fooArray;
 }

 function insertionSort(uint[] a)internal {
  for (uint i = 1;i < a.length;i++){
   uint temp = a[i];
   uint j;
   for (j = i -1; j >= 0 && temp < a[j]; j--)
     a[j+1] = a[j];
   a[j + 1] = temp;
  }
 }

 function doesntWork(uint num,uint _id){
   Foo f = foos[_id];
   f.fooArray[f.fooArray.length++] = nun;
   instertionSort(f.fooArray);
 }

 function work1(uit _id){
   Foo f = foos[_id];
   instertionSort(f.fooArray);
 }

 function work2(uint price,uint pair_id){
   Foo p = pairs[pair_id];
   p.boPrices[p.boPrices.length++] = price;
 }

每當我嘗試在數組上呼叫 insertSort 函式時,它都不起作用#方法 1。

當我事先不修改數組時(如範例 2,3 所示),相同的功能也有效。

因為我的目標是在添加每個新值後保持我的數組排序,所以這是一個問題。

我認為它必須與我的數組的儲存有關。但我無法掌握Data location的那些概念,以及如何解決它。

那裡是否存在解決方法?

編輯:也對這個快速儲存做同樣的事情

工作解決方案

contract InsertionSort {

   struct Record {
       uint recId;
       uint[] data;
   }

   mapping(uint => Record) records;

   function insertionSortMemory(uint[5] a) internal {
     for (uint i = 0; i < a.length; i++) {
       uint j = i;
       while (j > 0 && a[j] < a[j-1]) {
         uint temp = a[j];
         a[j] = a[j-1];
         a[j-1] = temp;
         j--;
       }
     }
   }

   function testInsertionSort() public constant returns (uint[5]) {
     uint[5] memory data;
     data[0] = 1235;
     data[1] = 1234;
     data[2] = 1233;
     data[3] = 1232;
     data[4] = 1231;
     insertionSortMemory(data);
     return data;
   }

   function insertionSort(uint[] a, uint length) internal returns (uint[]) {
     for (uint i = 0; i < length; i++) {
       uint j = i;
       while (j > 0 && a[j] < a[j-1]) {
         uint temp = a[j];
         a[j] = a[j-1];
         a[j-1] = temp;
         j--;
       }
     }
     return a;
   }

   function addRecordWithoutInsertionSort(uint _recId, uint _num) public {
       Record record = records[_recId];
       record.recId = _recId;
       record.data.length++;
       record.data[record.data.length-1] = _num;
   }

   function addRecordWithInsertionSort(uint _recId, uint _num) public {
       Record record = records[_recId];
       record.recId = _recId;
       record.data.length++;
       record.data[record.data.length-1] = _num;
       record.data = insertionSort(record.data, record.data.length);
   }



   function getRecord(uint _recId) public constant returns(uint, uint[]) {
       return (records[_recId].recId, records[_recId].data);
   }
}

測試結果

測試插入排序算法

並且算法工作正常。

> insertionSort.testInsertionSort()
[1231, 1232, 1233, 1234, 1235]

測試程式碼的其餘部分

讓我們先插入一些記錄,而不呼叫insertionSort()函式:

> insertionSort.addRecordWithoutInsertionSort(123, 1233, eth.accounts[0], {
   from:web3.eth.accounts[0], 
   data: insertionSortCompiled.InsertionSort.code,
   gas: 1000000
});
> insertionSort.addRecordWithoutInsertionSort(123, 1235, eth.accounts[0], {
   from:web3.eth.accounts[0], 
   data: insertionSortCompiled.InsertionSort.code,
   gas: 1000000
});
> insertionSort.addRecordWithoutInsertionSort(123, 1231, eth.accounts[0], {
   from:web3.eth.accounts[0], 
   data: insertionSortCompiled.InsertionSort.code,
   gas: 1000000
});
> insertionSort.addRecordWithoutInsertionSort(123, 1232, eth.accounts[0], {
   from:web3.eth.accounts[0], 
   data: insertionSortCompiled.InsertionSort.code,
   gas: 1000000
});

等待交易被探勘,因為我們還沒有呼叫插入排序,所以記錄將是未排序的

> insertionSort.getRecord(123);
[123, [1233, 1235, 1231, 1232]]

再插入一條記錄,addRecordWithInsertionSort()函式呼叫該insertionSort()函式:

> insertionSort.addRecordWithInsertionSort(123, 1234, eth.accounts[0], {
   from:web3.eth.accounts[0], 
   data: insertionSortCompiled.InsertionSort.code,
   gas: 2000000
});

等待此交易被探勘,排序按預期工作!

> insertionSort.getRecord(123);
[123, [1231, 1232, 1233, 1234, 1235]]

和解釋

  • Bad Array Jump DestinationPC 00000651: JUMPI GAS: 1946706 COST: 10 ERROR: invalid jump destination (PUSH1) 2可能是由於算法不正確並定址數組中的錯誤索引。我只是查找了插入排序算法,並根據我找到的算法重寫了程式碼。
  • 我遇到的主要問題是我最初的insertionSort()函式編碼如下:
function insertionSort(uint[] a, uint length) internal {
 for (uint i = 0; i < length; i++) {
   uint j = i;
   while (j > 0 && a[j] < a[j-1]) {
     uint temp = a[j];
     a[j] = a[j-1];
     a[j-1] = temp;
     j--;
   }
 }
}

我把它addRecordWithInsertionSort()稱為:

function addRecordWithInsertionSort(uint _recId, uint _num) public {
   Record record = records[_recId];
   record.recId = _recId;
   record.data.length++;
   record.data[record.data.length-1] = _num;
   insertionSort(record.data, record.data.length);
}

數據沒有被排序,這很可能是因為record.data變數被insertionSort()作為記憶體變數傳遞。數組在函式中被排序insertionSort(),但修改後的數組沒有被傳回呼叫函式。

根據Solidity - 常見問題

儲存位置有預設值,具體取決於它涉及的變數類型:

 * function arguments are always in memory.

我必須更改程式碼,以便insertionSort()函式返回修改後的數組。

insertionSort()功能是:

function insertionSort(uint[] a, uint length) internal returns (uint[]) {
 for (uint i = 0; i < length; i++) {
   uint j = i;
   while (j > 0 && a[j] < a[j-1]) {
     uint temp = a[j];
     a[j] = a[j-1];
     a[j-1] = temp;
     j--;
   }
 }
 return a;
}

並且呼叫insertionSort()修改後的程式碼從函式的返回值更新數據,insertionSort()結果現在是正確的:

function addRecordWithInsertionSort(uint _recId, uint _num) public { 
 Record record = records[_recId];
 record.recId = _recId;
 record.data.length++;
 record.data[record.data.length-1] = _num;
 record.data = insertionSort(record.data, record.data.length);
}
  • 最後,我使用映射而不是數組,因此我不必跟踪數組索引。

編輯:使用儲存測試函式參數

我通過將排序後的數據作為函式結果返回並將此數據重新分配給地圖,從而使上述測試程式碼正常工作。

以下程式碼顯示,您還可以將儲存修飾符與被呼叫函式的參數一起使用,這將允許函式對傳遞給它的數據進行排序,並將更改保留在地圖資料結構中。

   function insertionSortStorage(uint[] storage a) internal {
     for (uint i = 0; i < a.length; i++) {
       uint j = i;
       while (j > 0 && a[j] < a[j-1]) {
         uint temp = a[j];
         a[j] = a[j-1];
         a[j-1] = temp;
         j--;
       }
     }
   }

   function addRecordWithInsertionSortStorage(uint _recId, uint _num) public {
       Record record = records[_recId];
       record.recId = _recId;
       record.data.length++;
       record.data[record.data.length-1] = _num;
       insertionSortStorage(record.data);
   }

測試結果顯示成功:

insertionSort.addRecordWithoutInsertionSort(123, 1235, eth.accounts[0], {
 from:web3.eth.accounts[0], 
 data: insertionSortCompiled.InsertionSort.code,
 gas: 1000000
});
insertionSort.addRecordWithoutInsertionSort(123, 1231, eth.accounts[0], {
 from:web3.eth.accounts[0], 
 data: insertionSortCompiled.InsertionSort.code, 
 gas: 1000000
});
insertionSort.addRecordWithoutInsertionSort(123, 1232, eth.accounts[0], {
 from:web3.eth.accounts[0], 
 data: insertionSortCompiled.InsertionSort.code,
 gas: 1000000
});
> insertionSort.getRecord(123);
[123, [1235, 1231, 1232]]

insertionSort.addRecordWithInsertionSortStorage(123, 1220, eth.accounts[0], {
 from:web3.eth.accounts[0], 
 data: insertionSortCompiled.InsertionSort.code,
 gas: 2000000
});
> insertionSort.getRecord(123);
[123, [1220, 1231, 1232, 1235]]

複製錯誤:無效的跳轉目標 (PUSH1) 2

invalid jump destination (PUSH1) 2我可以通過將以下函式添加到類來複製錯誤:

function testInvalidJumpLocation() public constant returns (uint[5]) {
 uint[5] memory data;
 uint j = 1;
 j = j - 2;
 data[j] = 1235;
 return data;
}

並執行函式得到以下結果:

insertionSort.testInvalidJumpLocation()
...
PC 00000304: JUMP GAS: 49978004 COST: 8 ERROR: invalid jump destination (PUSH1) 2
...
[0, 0, 0, 0, 0]

此錯誤可能是由程式碼的其他部分引起的,但無效的數組索引肯定會生成此消息。

引用自:https://ethereum.stackexchange.com/questions/1573