Solidity

簡單交易合約中的 Gas 成本不加起來

  • March 16, 2018

我有一份簡單的契約,可以幫助向另一個帳戶付款。假設我以價值開始契約V,契約氣體成本G和價值轉換到另一個帳戶X。我假設以下情況為真:

V = G + X.

然而事實並非如此。

按照一個簡單的truffle init步驟,我正在處理以下契約。

Pay.sol

pragma solidity ^0.4.17;

/// @title Pay - Facilitates payments.
contract Pay {
   event Payment(
       address _from,
       address _to,
       uint amount
   );

   /// @dev Makes a payment.
   /// @param _to Address to pay to.
   function pay(address _to) public payable {
       require(msg.value > 0);
       // Does this transfer the right amount of ether (msg.value measured in wei)?
       _to.transfer(msg.value);
       Payment(msg.sender, _to, msg.value);
   }
}

以及下面的測試文件。

pay.js

var Pay = artifacts.require("./Pay.sol");

contract('Pay', function(accounts) {
   it("should put money in the first account", function() {
       return Pay.deployed().then(function(instance) {
           pay = instance;
           return web3.eth.getBalance(accounts[1]);
       }).then(function(balance){
           startingBalance = balance.toNumber();
           gasPrice = 200000;
           payAmount = 2500000;
           return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
       }).then(function() {
           return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
       }).then(function(gasCost) {
           gasSpent = gasCost * gasPrice;
           console.log(gasCost);
           console.log(gasPrice);
           console.log(gasSpent);
           return web3.eth.getBalance(accounts[1]);
       }).then(function(balance) {
           endingBalance = balance.toNumber();
           assert.equal(endingBalance - startingBalance, payAmount);
       })
   });
});

輸出如下:

 Contract: Pay
32001
200000
6400200000
   1) should put money in the first account

   Events emitted during test:
   ---------------------------

   Payment(_from: 0x627306090abab3a6e1400e9345bc60c78a8bef57, _to: 0xf17f52151ebef6c7334fad080c5704d77216b732, amount: 2500000)

   ---------------------------


 0 passing (476ms)
 1 failing

 1) Contract: Pay should put money in the first account:
    AssertionError: expected 2506752 to equal 2500000
     at test/pay.js:23:20
     at <anonymous>
     at process._tickCallback (internal/process/next_tick.js:118:7)

特別是,正如您從輸出中看到的:

V-X=-6752(它甚至可以是負面的嗎?) G=6400200000

更新:正確答案供參考。

pay.js

var Pay = artifacts.require("./Pay.sol");

contract('Pay', function(accounts) {
   it("should put money in the first account", function() {
       return Pay.deployed().then(function(instance) {
           pay = instance;
           return web3.eth.getBalance(accounts[1]);
       }).then(function(balance){
           startingBalance = balance;
           gasPrice = 200000;
           payAmount = 2500000;
           return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
       }).then(function(result) {
           gasUsed = result.receipt.gasUsed;
           return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
       }).then(function(gasCost) {
           gasSpent = gasCost * gasPrice;
           return web3.eth.getBalance(accounts[1]);
       }).then(function(balance) {
           endingBalance = balance;
           assert.equal(endingBalance.sub(startingBalance).toNumber(), payAmount);
       })
   });

   it("should pay gas costs from the second account", function() {
       return Pay.deployed().then(function(instance) {
           pay = instance;
           return web3.eth.getBalance(accounts[0]);
       }).then(function(balance){
           startingBalance = balance;
           gasPrice = 200000;
           payAmount = 2500000;
           return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
       }).then(function(result) {
           gasUsed = result.receipt.gasUsed;
           return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
       }).then(function(gasCost) {
           gasSpent = gasCost * gasPrice;
           return web3.eth.getBalance(accounts[0]);
       }).then(function(balance) {
           endingBalance = balance;
           assert.equal(startingBalance.sub(endingBalance).toNumber(), payAmount + gasSpent);
       })
   });
});

您正在使用 javascript 的數字來獲取差異

startingBalance = balance.toNumber();
...
endingBalance = balance.toNumber();
assert.equal(endingBalance - startingBalance, payAmount);

但是 javascript 數字沒有足夠的精度來處理大值。

您應該將餘額儲存為 BigNums,並將轉換為數字作為最後的操作。

startingBalance = balance;
...
endingBalance = balance;
assert.equal(endingBalance.sub(startingBalance).toNumber(), payAmount);

此外,您正在支付操作費用,account[0]您必須使用該帳戶中的餘額來衡量所花費的氣體。


您正在使用estimateGas來確定使用的氣體,您應該使用交易收據gasUsed中的欄位。

來自eth_estimategas文件

請注意,出於各種原因,包括 EVM 機制和節點性能,估計值可能遠遠超過交易實際使用的 gas 量。

有些操作是負成本的,例如刪除儲存會返回gas,但必須先成功。

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