diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/opcodes.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/opcodes.go index 505d536cf..5ebd48072 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/opcodes.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/opcodes.go @@ -166,6 +166,7 @@ const ( CALL CALLCODE RETURN + DELEGATECALL // 0x70 range - other SUICIDE = 0xff @@ -316,10 +317,11 @@ var opCodeToString = map[OpCode]string{ LOG4: "LOG4", // 0xf0 range - CREATE: "CREATE", - CALL: "CALL", - RETURN: "RETURN", - CALLCODE: "CALLCODE", + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + CALLCODE: "CALLCODE", + DELEGATECALL: "DELEGATECALL", // 0x70 range - other SUICIDE: "SUICIDE", diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go index f3d02f215..f79a3d287 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go @@ -147,6 +147,30 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas return } +// DelegateCall is executed by the DELEGATECALL opcode, introduced as off Ethereum Homestead. +// The intent of delegate call is to run the code of the callee in the storage context of the caller; +// while preserving the original caller to the previous callee. +// Different to the normal CALL or CALLCODE, the value does not need to be transferred to the callee. +func (vm *VM) DelegateCall(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) { + + exception := new(string) + // fire the post call event (including exception if applicable) + defer vm.fireCallEvent(exception, &output, caller, callee, input, value, gas) + + // DelegateCall does not transfer the value to the callee. + + if len(code) > 0 { + vm.callDepth += 1 + output, err = vm.call(caller, callee, code, input, value, gas) + vm.callDepth -= 1 + if err != nil { + *exception = err.Error() + } + } + + return +} + // Try to deduct gasToUse from gasLeft. If ok return false, otherwise // set err and return true. func useGasNegative(gasLeft *int64, gasToUse int64, err *error) bool { @@ -745,12 +769,21 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas stack.Push(newAccount.Address) } - case CALL, CALLCODE: // 0xF1, 0xF2 + case CALL, CALLCODE, DELEGATECALL: // 0xF1, 0xF2, 0xF4 if !HasPermission(vm.appState, callee, ptypes.Call) { return nil, ErrPermission{"call"} } gasLimit := stack.Pop64() - addr, value := stack.Pop(), stack.Pop64() + addr := stack.Pop() + // NOTE: for DELEGATECALL value is preserved from the original + // caller, as such it is not stored on stack for the address + // to be called; for CALL and CALLCODE value is stored on stack + // and needs to be overwritten from the given value. + // TODO: [ben] assert that malicious code cannot produce + // new value. + if op != DELEGATECALL { + value = stack.Pop64() + } inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs dbg.Printf(" => %X\n", addr) @@ -799,6 +832,11 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas return nil, firstErr(err, ErrUnknownAddress) } ret, err = vm.Call(callee, callee, acc.Code, args, value, gas) + } else if op == DELEGATECALL { + if acc == nil { + return nil, firstErr(err, ErrUnknownAddress) + } + ret, err = vm.DelegateCall(caller, callee, acc.Code, args, value, gas) } else { // nil account means we're sending funds to a new account if acc == nil {