새로운 가치가 기존 가치와 동일한 SSTORE는 가스 비용이 듭니까?
예 (Vyper에서) :
@external
def foo(bar: uint256):
self.baz = bar
foo(3)
foo(3) # Is the gas cost here still 5000?
대신 이것을하는 것이 더 낫습니까?
def foo(bar: uint256):
if self.baz != bar: # Will this check save gas from unnecessary SSTORE's
self.baz = bar
답변
가스 비용은 200입니다. 실제로 Geth의 소스 코드에는이 정확한 동작을 언급하는 주석이 있습니다. 에서 protocol_params.go
라인 (46)에 우리는 다음 코드 줄을 참조하십시오
NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change.
출처: https://github.com/ethereum/go-ethereum/blob/master/params/protocol_params.go
다음 테스트는 동일한 값을 저장하는 비용이 가스 단위 800 개라는 것을 의미합니다.
견고성 계약 :
pragma solidity 0.6.12;
contract MyContract {
uint256 public gasUsed;
uint256 public storageSlot;
function func(uint256 x) public {
storageSlot = x;
uint256 gasLeft = gasleft();
storageSlot = x;
gasUsed = gasLeft - gasleft();
}
}
Truffle 5.x 테스트 :
const MyContract = artifacts.require("MyContract");
contract("MyContract", accounts => {
it("test", async () => {
const myContract = await MyContract.new();
for (let x = 0; x < 10; x++) {
await myContract.func(x);
const gasUsed = await myContract.gasUsed();
console.log(gasUsed.toString());
}
});
});
출력은 모든 반복에 대해 816입니다.
gasleft()
계약 함수의 마지막 라인에서 작업이 16 개의 가스 단위를 소비 한다고 가정하면 동일한 값을 저장하는 비용은 800 개의 가스 단위로 나타납니다.
@technicallyty 및 @goodvibration의 답변에 감사 드리며 @goodvibration의 지속 및 접근 방식은 정확합니다.
예, 동일한 값을 저장하는 데는 800 가스가 듭니다.
EIP-2200 및 관련 코드는 실제로 다음 기능인 gasSStoreEIP2200입니다.
더 정확하게는 코드 :
if current == value { // noop (1)
return params.SloadGasEIP2200, nil
}
다음 과 같이 주석 이 달린
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
그리고 SloadGasEIP2200는 800 :
SloadGasEIP2200 uint64 = 800 // Cost of SLOAD after EIP 2200 (part of Istanbul)
추가 정보
gasSStoreEIP2200
EIP-2200 을 기반으로 한 전체 의견은 다음과 같습니다 .
// 0. If *gasleft* is less than or equal to 2300, fail the current call.
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
// 2. If current value does not equal new value:
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
// 2.2.1. If original value is not 0:
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset):
// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
gasSStoreEIP2200
현재 기능이며 gasSStore
역사적 기능입니다. (@MrClottom이 그의 대답 에서 말했듯이 : "새 노드가 모든 트랜잭션을 동기화하고 확인할 때 이전 합의 규칙도 이해할 수 있어야합니다 ...")
아래의 원래 답변은 EIP-2200이 누락되었으며 gasSStore
.
Geth 소스 코드 (마스터 지점 2020년 11월) 및 EIP-1283 상태 :
// 새 가스 계량은 순 가스 비용 (EIP-1283)을 기반으로합니다. // //
- 현재 값이 새 값과 같으면 (이는 작동하지 않음) 200 가스가 차감됩니다. // 2. 현재 값이 새 값과 같지 않은 경우 // 2.1. 원래 값이 현재 값과 같은 경우 (이 스토리지 슬롯은 현재 실행 컨텍스트에 의해 변경되지 않음) // 2.1.1. 원래 값이 0이면 20000 가스가 차감됩니다. // 2.1.2. 그렇지 않으면 5000 가스가 공제됩니다. 새 값이 0이면 환불 카운터에 15000 가스를 추가하십시오. // 2.2. 원래 값이 현재 값과 같지 않으면 (이 저장 슬롯이 더러움) 200 가스가 차감됩니다. 다음 절을 모두 적용하십시오. // 2.2.1. 원래 값이 0이 아닌 경우 // 2.2.1.1. 현재 값이 0이면 (새 값이 0이 아님을 의미 함) 환불 카운터에서 15000 가스를 제거합니다. 환불 카운터가 0 미만이되지 않음을 증명할 수 있습니다. // 2.2.1.2. 새 값이 0이면 (현재 값이 0이 아님을 의미 함) 환불 카운터에 15000 가스를 추가합니다. //
2.2.2. 원래 값이 새 값과 같으면 (이 스토리지 슬롯이 재설정 됨) // 2.2.2.1. 원래 값이 0이면 19800 가스를 환불 카운터에 추가하십시오. // 2.2.2.2. 그렇지 않으면 4800 가스를 환불 카운터에 추가하십시오. value : = common.Hash (y.Bytes32 ()) if current == value {// noop (1) return params.NetSstoreNoopGas, nil}