Opcode
CALL 指令將 GAS 推入堆棧?
根據https://ethervm.io,執行 CALL 指令時,堆棧佈局如下:
[address, value, argOffset, argLen, retOffset, retLen]
所以 CALL 會從棧底彈出 6 個單詞來檢索必要的資訊。
但是當我查看這個 Solidity 程式碼的操作碼時:
contract Test { function withdraw() public { uint x = 123; msg.sender.call.value(x)(); } }
我可以看到相關的操作碼如下:
33 caller 90 swap1 82 dup3 90 swap1 60 00 push1 00 81 dup2 81 dup2 81 dup2 85 dup6 87 dup8 5a gas f1 call
我們可以看到,在 CALL 之前,GAS 將剩餘的氣體推入堆棧底部。但這改變了堆棧佈局,底部單詞不再是呼叫者地址。
這是否意味著來自https://ethervm.io的資訊不正確,或者我在這裡錯過了什麼?
謝謝!
是的,我認為這裡的https://ethervm.io/不正確。
來自黃皮書:
因此操作數順序為:gas, to, value, in offset, in size, out offset, out size
有七個參數,氣體是堆棧中的最頂部。
JULIA還用七個參數對其進行了定義:
呼叫(g:u256,a:u256,v:u256,in:u256,insize:u256,out:u256,outsize:u256)-> r:u256
它執行 7
pop
秒,但在正常 EVM 跟踪期間這些彈出視窗不作為 OPCODES 可見,它們在Golang
自己看(
core/vm/instructions.go
)func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Pop gas. The actual gas in in evm.callGasTemp. evm.interpreter.intPool.put(stack.pop()) gas := evm.callGasTemp // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(addr) value = math.U256(value) // Get the arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend } ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value) if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { stack.push(evm.interpreter.intPool.get().SetUint64(1)) } if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil }
pop
定義為:func (st *Stack) pop() (ret *big.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return }