diff --git a/build/ci.go b/build/ci.go index c603f68e7ad7..2fa13be280ba 100644 --- a/build/ci.go +++ b/build/ci.go @@ -323,7 +323,15 @@ func doLint(cmdline []string) { build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v1"), "--install") // Run fast linters batched together - configs := []string{"--vendor", "--disable-all", "--enable=vet", "--enable=gofmt", "--enable=misspell"} + configs := []string{ + "--vendor", + "--disable-all", + "--enable=vet", + "--enable=gofmt", + "--enable=misspell", + "--enable=goconst", + "--min-occurrences=6", // for goconst + } build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v1"), append(configs, packages...)...) // Run slow linters one by one diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go index 7c4d6052c1a0..aa276e0f9b47 100644 --- a/cmd/swarm/manifest.go +++ b/cmd/swarm/manifest.go @@ -30,6 +30,8 @@ import ( "gopkg.in/urfave/cli.v1" ) +const bzzManifestJSON = "application/bzz-manifest+json" + func add(ctx *cli.Context) { args := ctx.Args() if len(args) < 3 { @@ -145,7 +147,7 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin if path == entry.Path { utils.Fatalf("Path %s already present, not adding anything", path) } else { - if entry.ContentType == "application/bzz-manifest+json" { + if entry.ContentType == bzzManifestJSON { prfxlen := strings.HasPrefix(path, entry.Path) if prfxlen && len(path) > len(longestPathEntry.Path) { longestPathEntry = entry @@ -207,7 +209,7 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st if path == entry.Path { newEntry = entry } else { - if entry.ContentType == "application/bzz-manifest+json" { + if entry.ContentType == bzzManifestJSON { prfxlen := strings.HasPrefix(path, entry.Path) if prfxlen && len(path) > len(longestPathEntry.Path) { longestPathEntry = entry @@ -281,7 +283,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string { if path == entry.Path { entryToRemove = entry } else { - if entry.ContentType == "application/bzz-manifest+json" { + if entry.ContentType == bzzManifestJSON { prfxlen := strings.HasPrefix(path, entry.Path) if prfxlen && len(path) > len(longestPathEntry.Path) { longestPathEntry = entry diff --git a/common/bytes.go b/common/bytes.go index 66577bbfd0d0..bb40ac1d70ff 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -35,12 +35,11 @@ func FromHex(s string) []byte { if s[0:2] == "0x" || s[0:2] == "0X" { s = s[2:] } - if len(s)%2 == 1 { - s = "0" + s - } - return Hex2Bytes(s) } - return nil + if len(s)%2 == 1 { + s = "0" + s + } + return Hex2Bytes(s) } // Copy bytes diff --git a/common/bytes_test.go b/common/bytes_test.go index fc164b13d6d6..71631e6dd72c 100644 --- a/common/bytes_test.go +++ b/common/bytes_test.go @@ -74,7 +74,7 @@ func TestFromHex(t *testing.T) { expected := []byte{1} result := FromHex(input) if !bytes.Equal(expected, result) { - t.Errorf("Expected % x got % x", expected, result) + t.Errorf("Expected %x got %x", expected, result) } } @@ -83,6 +83,15 @@ func TestFromHexOddLength(t *testing.T) { expected := []byte{1} result := FromHex(input) if !bytes.Equal(expected, result) { - t.Errorf("Expected % x got % x", expected, result) + t.Errorf("Expected %x got %x", expected, result) + } +} + +func TestNoPrefixShortHexOddLength(t *testing.T) { + input := "1" + expected := []byte{1} + result := FromHex(input) + if !bytes.Equal(expected, result) { + t.Errorf("Expected %x got %x", expected, result) } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 093c7d4c148c..344435f73cc9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -104,6 +104,10 @@ type EVM struct { // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 + // callGasTemp holds the gas available for the current call. This is needed because the + // available gas is calculated in gasCall* according to the 63/64 rule and later + // applied in opCall*. + callGasTemp uint64 } // NewEVM retutrns a new EVM . The returned EVM is not thread safe and should diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 0d8e295a52ca..ff109af57b96 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -342,19 +342,11 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. This information is otherwise lost due to the dependency on *current* - // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -374,19 +366,11 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. This information is otherwise lost due to the dependency on *current* - // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -436,18 +420,11 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -463,18 +440,11 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b6d6e22c4c45..1d1585fcac06 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -603,24 +603,20 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() - // pop gas and value of the stack. - addr, value := stack.pop(), stack.pop() + // 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) - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // 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, address, args, gas, value) + ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value) if err != nil { stack.push(new(big.Int)) } else { @@ -636,25 +632,20 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() - // pop gas and value of the stack. - addr, value := stack.pop(), stack.pop() + // Pop gas. The actual gas is 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) - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend } - - ret, returnGas, err := evm.CallCode(contract, address, args, gas, value) + ret, returnGas, err := evm.CallCode(contract, toAddr, args, gas, value) if err != nil { stack.push(new(big.Int)) } else { @@ -670,9 +661,13 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack } func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - - toAddr := common.BigToAddress(to) + // Pop gas. The actual gas is in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas) @@ -682,30 +677,25 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st stack.push(big.NewInt(1)) } if err == nil || err == errExecutionReverted { - memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) + evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // pop gas - gas := stack.pop().Uint64() - // pop address - addr := stack.pop() - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // Pop gas. The actual gas is in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, returnGas, err := evm.StaticCall(contract, address, args, gas) + ret, returnGas, err := evm.StaticCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ea5468f901b7..ac6000f97d61 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -138,16 +138,15 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret pc = uint64(0) // program counter cost uint64 // copies used by tracer - stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule - pcCopy uint64 // needed for the deferred Tracer - gasCopy uint64 // for Tracer to log gas remaining before execution - logged bool // deferred Tracer should ignore already logged steps + pcCopy uint64 // needed for the deferred Tracer + gasCopy uint64 // for Tracer to log gas remaining before execution + logged bool // deferred Tracer should ignore already logged steps ) contract.Input = input defer func() { if err != nil && !logged && in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) } }() @@ -156,21 +155,14 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret // the execution of one of the operations or until the done flag is set by the // parent context. for atomic.LoadInt32(&in.evm.abort) == 0 { - // Get the memory location of pc - op = contract.GetOp(pc) - if in.cfg.Debug { - logged = false - pcCopy = pc - gasCopy = contract.Gas - stackCopy = newstack() - for _, val := range stack.data { - stackCopy.push(val) - } + // Capture pre-execution values for tracing. + logged, pcCopy, gasCopy = false, pc, contract.Gas } - // Get the operation from the jump table matching the opcode and validate the - // stack and make sure there enough stack items available to perform the operation + // Get the operation from the jump table and validate the stack to ensure there are + // enough stack items available to perform the operation. + op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] if !operation.valid { return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) @@ -211,7 +203,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) logged = true }