diff options
78 files changed, 2121 insertions, 1013 deletions
@@ -1 +1 @@ -1.5.8 +1.5.9 diff --git a/build/ci.go b/build/ci.go index 319691e8a..51540f9b6 100644 --- a/build/ci.go +++ b/build/ci.go @@ -309,7 +309,7 @@ func spellcheck(packages []string) { // Ensure the spellchecker is available build.MustRun(goTool("get", "github.com/client9/misspell/cmd/misspell")) - // Windows chokes on long argument lists, check packages individualy + // Windows chokes on long argument lists, check packages individually for _, pkg := range packages { // The spell checker doesn't work on packages, gather all .go files for it out, err := goTool("list", "-f", "{{.Dir}}{{range .GoFiles}}\n{{.}}{{end}}{{range .CgoFiles}}\n{{.}}{{end}}{{range .TestGoFiles}}\n{{.}}{{end}}", pkg).CombinedOutput() diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 9f67e6628..8af0cebbb 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -156,7 +156,7 @@ func run(ctx *cli.Context) error { ret, _, err = runtime.Create(input, &runtime.Config{ Origin: sender.Address(), State: statedb, - GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)), + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(), GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), Value: common.Big(ctx.GlobalString(ValueFlag.Name)), EVMConfig: vm.Config{ @@ -172,7 +172,7 @@ func run(ctx *cli.Context) error { ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{ Origin: sender.Address(), State: statedb, - GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)), + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(), GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), Value: common.Big(ctx.GlobalString(ValueFlag.Name)), EVMConfig: vm.Config{ diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index c77bd554c..f38ee046f 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -123,6 +123,7 @@ func initGenesis(ctx *cli.Context) error { if err != nil { utils.Fatalf("failed to read genesis file: %v", err) } + defer genesisFile.Close() block, err := core.WriteGenesisBlock(chaindb, genesisFile) if err != nil { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index ff9d34b3c..d7e4cc7b5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -202,7 +202,7 @@ func makeFullNode(ctx *cli.Context) *node.Node { if err != nil { glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) } - if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { + if uint64(len(extra)) > params.MaximumExtraDataSize { glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) glog.V(logger.Debug).Infof("extra: %x\n", extra) extra = nil diff --git a/cmd/swarm/hash.go b/cmd/swarm/hash.go index 0a20bea82..bcba77a2a 100644 --- a/cmd/swarm/hash.go +++ b/cmd/swarm/hash.go @@ -36,6 +36,7 @@ func hash(ctx *cli.Context) { fmt.Println("Error opening file " + args[1]) os.Exit(1) } + defer f.Close() stat, _ := f.Stat() chunker := storage.NewTreeChunker(storage.NewChunkerParams()) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 87e21fb7f..34b3f71c4 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -39,7 +39,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/swarm" bzzapi "github.com/ethereum/go-ethereum/swarm/api" - "github.com/ethereum/go-ethereum/swarm/network" "gopkg.in/urfave/cli.v1" ) @@ -76,7 +75,6 @@ var ( SwarmNetworkIdFlag = cli.IntFlag{ Name: "bzznetworkid", Usage: "Network identifier (integer, default 3=swarm testnet)", - Value: network.NetworkId, } SwarmConfigPathFlag = cli.StringFlag{ Name: "bzzconfig", @@ -242,6 +240,7 @@ func bzzd(ctx *cli.Context) error { } func registerBzzService(ctx *cli.Context, stack *node.Node) { + prvkey := getAccount(ctx, stack) chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) @@ -249,6 +248,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { if bzzdir == "" { bzzdir = stack.InstanceDir() } + bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name)) if err != nil { utils.Fatalf("unable to configure swarm: %v", err) @@ -280,6 +280,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { keyid := ctx.GlobalString(SwarmAccountFlag.Name) + if keyid == "" { utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 4b76b8334..9ba33df80 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -654,6 +654,10 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { vsn += "-" + gitCommit[:8] } + // if we're running a light client or server, force enable the v5 peer discovery unless it is explicitly disabled with --nodiscover + // note that explicitly specifying --v5disc overrides --nodiscover, in which case the later only disables v4 discovery + forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) + config := &node.Config{ DataDir: MakeDataDir(ctx), KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name), @@ -662,8 +666,8 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { Name: name, Version: vsn, UserIdent: makeNodeUserIdent(ctx), - NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), - DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0, + NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // always disable v4 discovery in light client mode + DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || forceV5Discovery, DiscoveryV5Addr: MakeDiscoveryV5Address(ctx), BootstrapNodes: MakeBootstrapNodes(ctx), BootstrapNodesV5: MakeBootstrapNodesV5(ctx), diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go new file mode 100644 index 000000000..cbf093aa7 --- /dev/null +++ b/cmd/wnode/main.go @@ -0,0 +1,537 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +// This is a simple Whisper node. It could be used as a stand-alone bootstrap node. +// Also, could be used for different test and diagnostics purposes. + +package main + +import ( + "bufio" + "crypto/ecdsa" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/whisper/mailserver" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" + "golang.org/x/crypto/pbkdf2" +) + +const quitCommand = "~Q" + +// singletons +var ( + server *p2p.Server + shh *whisper.Whisper + done chan struct{} + mailServer mailserver.WMailServer + + input = bufio.NewReader(os.Stdin) +) + +// encryption +var ( + symKey []byte + pub *ecdsa.PublicKey + asymKey *ecdsa.PrivateKey + nodeid *ecdsa.PrivateKey + topic whisper.TopicType + filterID uint32 + msPassword string +) + +// cmd arguments +var ( + echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics") + bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections") + forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages") + mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand") + requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server") + asymmetricMode = flag.Bool("a", false, "use asymmetric encryption") + testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") + generateKey = flag.Bool("k", false, "generate and show the private key") + + argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") + argWorkTime = flag.Uint("work", 5, "work time in seconds") + argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") + argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request") + + argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") + argSalt = flag.String("salt", "", "salt (for topic and key derivation)") + argPub = flag.String("pub", "", "public key for asymmetric encryption") + argDBPath = flag.String("dbpath", "", "path to the server's DB directory") + argIDFile = flag.String("idfile", "", "file name with node id (private key)") + argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") + argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") +) + +func main() { + processArgs() + initialize() + run() +} + +func processArgs() { + flag.Parse() + + if len(*argIDFile) > 0 { + var err error + nodeid, err = crypto.LoadECDSA(*argIDFile) + if err != nil { + utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) + } + } + + const enodePrefix = "enode://" + if len(*argEnode) > 0 { + if (*argEnode)[:len(enodePrefix)] != enodePrefix { + *argEnode = enodePrefix + *argEnode + } + } + + if len(*argTopic) > 0 { + x, err := hex.DecodeString(*argTopic) + if err != nil { + utils.Fatalf("Failed to parse the topic: %s", err) + } + topic = whisper.BytesToTopic(x) + } + + if *asymmetricMode && len(*argPub) > 0 { + pub = crypto.ToECDSAPub(common.FromHex(*argPub)) + if !isKeyValid(pub) { + utils.Fatalf("invalid public key") + } + } + + if *echoMode { + echo() + } +} + +func echo() { + fmt.Printf("ttl = %d \n", *argTTL) + fmt.Printf("workTime = %d \n", *argWorkTime) + fmt.Printf("pow = %f \n", *argPoW) + fmt.Printf("mspow = %f \n", *argServerPoW) + fmt.Printf("ip = %s \n", *argIP) + fmt.Printf("salt = %s \n", *argSalt) + fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) + fmt.Printf("idfile = %s \n", *argIDFile) + fmt.Printf("dbpath = %s \n", *argDBPath) + fmt.Printf("boot = %s \n", *argEnode) +} + +func initialize() { + glog.SetV(logger.Warn) + glog.SetToStderr(true) + + done = make(chan struct{}) + var peers []*discover.Node + var err error + + if *generateKey { + key, err := crypto.GenerateKey() + if err != nil { + utils.Fatalf("Failed to generate private key: %s", err) + } + k := hex.EncodeToString(crypto.FromECDSA(key)) + fmt.Printf("Random private key: %s \n", k) + os.Exit(0) + } + + if *testMode { + password := []byte("test password for symmetric encryption") + salt := []byte("test salt for symmetric encryption") + symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New) + topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF} + msPassword = "mail server test password" + } + + if *bootstrapMode { + if len(*argIP) == 0 { + argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") + } + } else { + if len(*argEnode) == 0 { + argEnode = scanLineA("Please enter the peer's enode: ") + } + peer := discover.MustParseNode(*argEnode) + peers = append(peers, peer) + } + + if *mailServerMode { + if len(msPassword) == 0 { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + } + shh = whisper.NewWhisper(&mailServer) + mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) + } else { + shh = whisper.NewWhisper(nil) + } + + asymKey = shh.NewIdentity() + if nodeid == nil { + nodeid = shh.NewIdentity() + } + + server = &p2p.Server{ + Config: p2p.Config{ + PrivateKey: nodeid, + MaxPeers: 128, + Name: common.MakeName("whisper-go", "5.0"), + Protocols: shh.Protocols(), + ListenAddr: *argIP, + NAT: nat.Any(), + BootstrapNodes: peers, + StaticNodes: peers, + TrustedNodes: peers, + }, + } +} + +func startServer() { + err := server.Start() + if err != nil { + utils.Fatalf("Failed to start Whisper peer: %s.", err) + } + + fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) + fmt.Println(server.NodeInfo().Enode) + + if *bootstrapMode { + configureNode() + fmt.Println("Bootstrap Whisper node started") + } else { + fmt.Println("Whisper node started") + // first see if we can establish connection, then ask for user input + waitForConnection(true) + configureNode() + } + + if !*forwarderMode { + fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) + } +} + +func isKeyValid(k *ecdsa.PublicKey) bool { + return k.X != nil && k.Y != nil +} + +func configureNode() { + var err error + var p2pAccept bool + + if *forwarderMode { + return + } + + if *asymmetricMode { + if len(*argPub) == 0 { + s := scanLine("Please enter the peer's public key: ") + pub = crypto.ToECDSAPub(common.FromHex(s)) + if !isKeyValid(pub) { + utils.Fatalf("Error: invalid public key") + } + } + } + + if *requestMail { + p2pAccept = true + if len(msPassword) == 0 { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + } + } + + if !*asymmetricMode && !*forwarderMode && !*testMode { + pass, err := console.Stdin.PromptPassword("Please enter the password: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + + if len(*argSalt) == 0 { + argSalt = scanLineA("Please enter the salt: ") + } + + symKey = pbkdf2.Key([]byte(pass), []byte(*argSalt), 65356, 32, sha256.New) + + if len(*argTopic) == 0 { + generateTopic([]byte(pass), []byte(*argSalt)) + } + } + + if *mailServerMode { + if len(*argDBPath) == 0 { + argDBPath = scanLineA("Please enter the path to DB file: ") + } + } + + filter := whisper.Filter{ + KeySym: symKey, + KeyAsym: asymKey, + Topics: []whisper.TopicType{topic}, + AcceptP2P: p2pAccept, + } + filterID = shh.Watch(&filter) + fmt.Printf("Filter is configured for the topic: %x \n", topic) +} + +func generateTopic(password, salt []byte) { + const rounds = 4000 + const size = 128 + x1 := pbkdf2.Key(password, salt, rounds, size, sha512.New) + x2 := pbkdf2.Key(password, salt, rounds, size, sha1.New) + x3 := pbkdf2.Key(x1, x2, rounds, size, sha256.New) + + for i := 0; i < size; i++ { + topic[i%whisper.TopicLength] ^= x3[i] + } +} + +func waitForConnection(timeout bool) { + var cnt int + var connected bool + for !connected { + time.Sleep(time.Millisecond * 50) + connected = server.PeerCount() > 0 + if timeout { + cnt++ + if cnt > 1000 { + utils.Fatalf("Timeout expired, failed to connect") + } + } + } + + fmt.Println("Connected to peer.") +} + +func run() { + defer mailServer.Close() + startServer() + defer server.Stop() + shh.Start(nil) + defer shh.Stop() + + if !*forwarderMode { + go messageLoop() + } + + if *requestMail { + requestExpiredMessagesLoop() + } else { + sendLoop() + } +} + +func sendLoop() { + for { + s := scanLine("") + if s == quitCommand { + fmt.Println("Quit command received") + close(done) + break + } + sendMsg([]byte(s)) + + if *asymmetricMode { + // print your own message for convenience, + // because in asymmetric mode it is impossible to decrypt it + hour, min, sec := time.Now().Clock() + from := crypto.PubkeyToAddress(asymKey.PublicKey) + fmt.Printf("\n%02d:%02d:%02d <%x>: %s\n", hour, min, sec, from, s) + } + } +} + +func scanLine(prompt string) string { + if len(prompt) > 0 { + fmt.Print(prompt) + } + txt, err := input.ReadString('\n') + if err != nil { + utils.Fatalf("input error: %s", err) + } + txt = strings.TrimRight(txt, "\n\r") + return txt +} + +func scanLineA(prompt string) *string { + s := scanLine(prompt) + return &s +} + +func scanUint(prompt string) uint32 { + s := scanLine(prompt) + i, err := strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the lower time limit: %s", err) + } + return uint32(i) +} + +func sendMsg(payload []byte) { + params := whisper.MessageParams{ + Src: asymKey, + Dst: pub, + KeySym: symKey, + Payload: payload, + Topic: topic, + TTL: uint32(*argTTL), + PoW: *argPoW, + WorkTime: uint32(*argWorkTime), + } + + msg := whisper.NewSentMessage(¶ms) + envelope, err := msg.Wrap(¶ms) + if err != nil { + fmt.Printf("failed to seal message: %v \n", err) + return + } + + err = shh.Send(envelope) + if err != nil { + fmt.Printf("failed to send message: %v \n", err) + } +} + +func messageLoop() { + f := shh.GetFilter(filterID) + if f == nil { + utils.Fatalf("filter is not installed") + } + + ticker := time.NewTicker(time.Millisecond * 50) + + for { + select { + case <-ticker.C: + messages := f.Retrieve() + for _, msg := range messages { + printMessageInfo(msg) + } + case <-done: + return + } + } +} + +func printMessageInfo(msg *whisper.ReceivedMessage) { + timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics + text := string(msg.Payload) + + var address common.Address + if msg.Src != nil { + address = crypto.PubkeyToAddress(*msg.Src) + } + + if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { + fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself + } else { + fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer + } +} + +func requestExpiredMessagesLoop() { + var key, peerID []byte + var timeLow, timeUpp uint32 + var t string + var xt, empty whisper.TopicType + + err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword)) + if err != nil { + utils.Fatalf("Failed to create symmetric key for mail request: %s", err) + } + key = shh.GetSymKey(mailserver.MailServerKeyName) + peerID = extractIdFromEnode(*argEnode) + shh.MarkPeerTrusted(peerID) + + for { + timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") + timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") + t = scanLine("Please enter the topic (hexadecimal): ") + if len(t) >= whisper.TopicLength*2 { + x, err := hex.DecodeString(t) + if err != nil { + utils.Fatalf("Failed to parse the topic: %s", err) + } + xt = whisper.BytesToTopic(x) + } + if timeUpp == 0 { + timeUpp = 0xFFFFFFFF + } + + data := make([]byte, 8+whisper.TopicLength) + binary.BigEndian.PutUint32(data, timeLow) + binary.BigEndian.PutUint32(data[4:], timeUpp) + copy(data[8:], xt[:]) + if xt == empty { + data = data[:8] + } + + var params whisper.MessageParams + params.PoW = *argServerPoW + params.Payload = data + params.KeySym = key + params.Src = nodeid + params.WorkTime = 5 + + msg := whisper.NewSentMessage(¶ms) + env, err := msg.Wrap(¶ms) + if err != nil { + utils.Fatalf("Wrap failed: %s", err) + } + + err = shh.RequestHistoricMessages(peerID, env) + if err != nil { + utils.Fatalf("Failed to send P2P message: %s", err) + } + + time.Sleep(time.Second * 5) + } +} + +func extractIdFromEnode(s string) []byte { + n, err := discover.ParseNode(s) + if err != nil { + utils.Fatalf("Failed to parse enode: %s", err) + return nil + } + return n.ID[:] +} diff --git a/common/math/integer.go b/common/math/integer.go new file mode 100644 index 000000000..8162b1985 --- /dev/null +++ b/common/math/integer.go @@ -0,0 +1,25 @@ +package math + +import gmath "math" + +/* + * NOTE: The following methods need to be optimised using either bit checking or asm + */ + +// SafeSub returns subtraction result and whether overflow occurred. +func SafeSub(x, y uint64) (uint64, bool) { + return x - y, x < y +} + +// SafeAdd returns the result and whether overflow occurred. +func SafeAdd(x, y uint64) (uint64, bool) { + return x + y, y > gmath.MaxUint64-x +} + +// SafeMul returns multiplication result and whether overflow occurred. +func SafeMul(x, y uint64) (uint64, bool) { + if x == 0 { + return 0, false + } + return x * y, x != 0 && y != 0 && y > gmath.MaxUint64/x +} diff --git a/common/math/integer_test.go b/common/math/integer_test.go new file mode 100644 index 000000000..198114e5e --- /dev/null +++ b/common/math/integer_test.go @@ -0,0 +1,50 @@ +package math + +import ( + gmath "math" + "testing" +) + +type operation byte + +const ( + sub operation = iota + add + mul +) + +func TestOverflow(t *testing.T) { + for i, test := range []struct { + x uint64 + y uint64 + overflow bool + op operation + }{ + // add operations + {gmath.MaxUint64, 1, true, add}, + {gmath.MaxUint64 - 1, 1, false, add}, + + // sub operations + {0, 1, true, sub}, + {0, 0, false, sub}, + + // mul operations + {10, 10, false, mul}, + {gmath.MaxUint64, 2, true, mul}, + {gmath.MaxUint64, 1, false, mul}, + } { + var overflows bool + switch test.op { + case sub: + _, overflows = SafeSub(test.x, test.y) + case add: + _, overflows = SafeAdd(test.x, test.y) + case mul: + _, overflows = SafeMul(test.x, test.y) + } + + if test.overflow != overflows { + t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows) + } + } +} diff --git a/console/console.go b/console/console.go index 9bb3df926..389d52858 100644 --- a/console/console.go +++ b/console/console.go @@ -137,10 +137,14 @@ func (c *Console) init(preload []string) error { continue // manually mapped or ignore } if file, ok := web3ext.Modules[api]; ok { + // Load our extension for the module. if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil { return fmt.Errorf("%s.js: %v", api, err) } flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) + } else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() { + // Enable web3.js built-in extension if available. + flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) } } if _, err = c.jsre.Run(flatten); err != nil { diff --git a/core/bench_test.go b/core/bench_test.go index 353d217fd..59b5ad758 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -92,6 +92,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { var ( ringKeys = make([]*ecdsa.PrivateKey, 1000) ringAddrs = make([]common.Address, len(ringKeys)) + bigTxGas = new(big.Int).SetUint64(params.TxGas) ) func init() { @@ -111,8 +112,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { gas := CalcGasLimit(gen.PrevBlock(i - 1)) for { - gas.Sub(gas, params.TxGas) - if gas.Cmp(params.TxGas) < 0 { + gas.Sub(gas, bigTxGas) + if gas.Cmp(bigTxGas) < 0 { break } to := (from + 1) % naccounts @@ -120,7 +121,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) { gen.TxNonce(ringAddrs[from]), ringAddrs[to], benchRootFunds, - params.TxGas, + bigTxGas, nil, nil, ) diff --git a/core/block_validator.go b/core/block_validator.go index 65f975345..ee524b61f 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -204,7 +204,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b // // See YP section 4.3.4. "Block Header Validity" func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { - if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { + if uint64(len(header.Extra)) > params.MaximumExtraDataSize { return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) } diff --git a/core/blockchain.go b/core/blockchain.go index 90bb0b5a8..281f28f36 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -853,7 +853,7 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err return } -// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned +// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. If an error is returned // it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go). func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8f1383acd..cdf9b5cc6 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -719,7 +719,7 @@ func TestFastVsFullChains(t *testing.T) { // If the block number is multiple of 3, send a few bonus transactions to the miner if i%3 == 2 { for j := 0; j < i%4+1; j++ { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key) if err != nil { panic(err) } @@ -883,8 +883,8 @@ func TestChainTxReorgs(t *testing.T) { // Create two transactions shared between the chains: // - postponed: transaction included at a later block in the forked chain // - swapped: transaction included at the same block number in the forked chain - postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) + swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) // Create two transactions that will be dropped by the forked chain: // - pastDrop: transaction dropped retroactively from a past block @@ -900,13 +900,13 @@ func TestChainTxReorgs(t *testing.T) { chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) { switch i { case 0: - pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork case 2: - freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point gen.AddTx(swapped) // This transaction will be swapped out at the exact height @@ -925,18 +925,18 @@ func TestChainTxReorgs(t *testing.T) { chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) { switch i { case 0: - pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) gen.AddTx(pastAdd) // This transaction needs to be injected during reorg case 2: gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain - freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time case 3: - futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) gen.AddTx(futureAdd) // This transaction will be added after a full reorg } }) diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 2796817c0..5c563de46 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -56,13 +56,13 @@ func ExampleGenerateChain() { switch i { case 0: // In block 1, addr1 sends addr2 some ether. - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1) gen.AddTx(tx) case 1: // In block 2, addr1 sends some more ether to addr2. // addr2 passes it on to addr3. - tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) + tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) gen.AddTx(tx1) gen.AddTx(tx2) case 2: diff --git a/core/state/managed_state.go b/core/state/managed_state.go index ad73dc0dc..0d8f9dd28 100644 --- a/core/state/managed_state.go +++ b/core/state/managed_state.go @@ -82,10 +82,12 @@ func (ms *ManagedState) NewNonce(addr common.Address) uint64 { return uint64(len(account.nonces)-1) + account.nstart } -// GetNonce returns the canonical nonce for the managed or unmanaged account +// GetNonce returns the canonical nonce for the managed or unmanaged account. +// +// Because GetNonce mutates the DB, we must take a write lock. func (ms *ManagedState) GetNonce(addr common.Address) uint64 { - ms.mu.RLock() - defer ms.mu.RUnlock() + ms.mu.Lock() + defer ms.mu.Unlock() if ms.hasAccount(addr) { account := ms.getAccount(addr) diff --git a/core/state_transition.go b/core/state_transition.go index 3cb7e500c..98dc8d995 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -49,15 +49,16 @@ The state transitioning model does all all the necessary work to work out a vali 6) Derive new state root */ type StateTransition struct { - gp *GasPool - msg Message - gas, gasPrice *big.Int - initialGas *big.Int - value *big.Int - data []byte - state vm.StateDB - - env *vm.EVM + gp *GasPool + msg Message + gas uint64 + gasPrice *big.Int + initialGas *big.Int + value *big.Int + data []byte + state vm.StateDB + + evm *vm.EVM } // Message represents a message sent to a contract. @@ -81,12 +82,14 @@ func MessageCreatesContract(msg Message) bool { // IntrinsicGas computes the 'intrinsic gas' for a message // with the given data. +// +// TODO convert to uint64 func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { igas := new(big.Int) if contractCreation && homestead { - igas.Set(params.TxGasContractCreation) + igas.SetUint64(params.TxGasContractCreation) } else { - igas.Set(params.TxGas) + igas.SetUint64(params.TxGas) } if len(data) > 0 { var nz int64 @@ -96,27 +99,26 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { } } m := big.NewInt(nz) - m.Mul(m, params.TxDataNonZeroGas) + m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas)) igas.Add(igas, m) m.SetInt64(int64(len(data)) - nz) - m.Mul(m, params.TxDataZeroGas) + m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas)) igas.Add(igas, m) } return igas } // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition { +func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ gp: gp, - env: env, + evm: evm, msg: msg, - gas: new(big.Int), gasPrice: msg.GasPrice(), initialGas: new(big.Int), value: msg.Value(), data: msg.Data(), - state: env.StateDB, + state: evm.StateDB, } } @@ -127,8 +129,8 @@ func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) { - st := NewStateTransition(env, msg, gp) +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) { + st := NewStateTransition(evm, msg, gp) ret, _, gasUsed, err := st.TransitionDb() return ret, gasUsed, err @@ -157,21 +159,21 @@ func (self *StateTransition) to() vm.Account { return self.state.GetAccount(*to) } -func (self *StateTransition) useGas(amount *big.Int) error { - if self.gas.Cmp(amount) < 0 { +func (self *StateTransition) useGas(amount uint64) error { + if self.gas < amount { return vm.ErrOutOfGas } - self.gas.Sub(self.gas, amount) + self.gas -= amount return nil } -func (self *StateTransition) addGas(amount *big.Int) { - self.gas.Add(self.gas, amount) -} - func (self *StateTransition) buyGas() error { mgas := self.msg.Gas() + if mgas.BitLen() > 64 { + return vm.ErrOutOfGas + } + mgval := new(big.Int).Mul(mgas, self.gasPrice) sender := self.from() @@ -181,7 +183,8 @@ func (self *StateTransition) buyGas() error { if err := self.gp.SubGas(mgas); err != nil { return err } - self.addGas(mgas) + self.gas += mgas.Uint64() + self.initialGas.Set(mgas) sender.SubBalance(mgval) return nil @@ -209,7 +212,9 @@ func (self *StateTransition) preCheck() (err error) { return nil } -// TransitionDb will move the state by applying the message against the given environment. +// TransitionDb will transition the state by applying the current message and returning the result +// including the required gas for the operation as well as the used gas. It returns an error if it +// failed. An error indicates a consensus issue. func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) { if err = self.preCheck(); err != nil { return @@ -217,26 +222,32 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b msg := self.msg sender := self.from() // err checked in preCheck - homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber) + homestead := self.evm.ChainConfig().IsHomestead(self.evm.BlockNumber) contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas - if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { + // TODO convert to uint64 + intrinsicGas := IntrinsicGas(self.data, contractCreation, homestead) + if intrinsicGas.BitLen() > 64 { + return nil, nil, nil, InvalidTxError(vm.ErrOutOfGas) + } + + if err = self.useGas(intrinsicGas.Uint64()); err != nil { return nil, nil, nil, InvalidTxError(err) } var ( - vmenv = self.env + evm = self.evm // vm errors do not effect consensus and are therefor // not assigned to err, except for insufficient balance // error. vmerr error ) if contractCreation { - ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value) + ret, _, self.gas, vmerr = evm.Create(sender, self.data, self.gas, self.value) } else { // Increment the nonce for the next transaction self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) - ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value) + ret, self.gas, vmerr = evm.Call(sender, self.to().Address(), self.data, self.gas, self.value) } if vmerr != nil { glog.V(logger.Core).Infoln("vm returned with error:", err) @@ -251,7 +262,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b requiredGas = new(big.Int).Set(self.gasUsed()) self.refundGas() - self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + self.state.AddBalance(self.evm.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) return ret, requiredGas, self.gasUsed(), err } @@ -260,20 +271,21 @@ func (self *StateTransition) refundGas() { // Return eth for remaining gas to the sender account, // exchanged at the original rate. sender := self.from() // err already checked - remaining := new(big.Int).Mul(self.gas, self.gasPrice) + remaining := new(big.Int).Mul(new(big.Int).SetUint64(self.gas), self.gasPrice) sender.AddBalance(remaining) // Apply refund counter, capped to half of the used gas. uhalf := remaining.Div(self.gasUsed(), common.Big2) refund := common.BigMin(uhalf, self.state.GetRefund()) - self.gas.Add(self.gas, refund) + self.gas += refund.Uint64() + self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) // Also return remaining gas to the block gas counter so it is // available for the next transaction. - self.gp.AddGas(self.gas) + self.gp.AddGas(new(big.Int).SetUint64(self.gas)) } func (self *StateTransition) gasUsed() *big.Int { - return new(big.Int).Sub(self.initialGas, self.gas) + return new(big.Int).Sub(self.initialGas, new(big.Int).SetUint64(self.gas)) } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index f52f80d34..6e4519c04 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -19,6 +19,7 @@ package types import ( "bytes" "crypto/ecdsa" + "encoding/json" "math/big" "testing" @@ -29,7 +30,6 @@ import ( // The values in those tests are from the Transaction Tests // at github.com/ethereum/tests. - var ( emptyTx = NewTransaction( 0, @@ -190,3 +190,45 @@ func TestTransactionPriceNonceSort(t *testing.T) { } } } + +// TestTransactionJSON tests serializing/de-serializing to/from JSON. +func TestTransactionJSON(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("could not generate key: %v", err) + } + signer := NewEIP155Signer(common.Big1) + + for i := uint64(0); i < 25; i++ { + var tx *Transaction + switch i % 2 { + case 0: + tx = NewTransaction(i, common.Address{1}, common.Big0, common.Big1, common.Big2, []byte("abcdef")) + case 1: + tx = NewContractCreation(i, common.Big0, common.Big1, common.Big2, []byte("abcdef")) + } + + tx, err := SignTx(tx, signer, key) + if err != nil { + t.Fatalf("could not sign transaction: %v", err) + } + + data, err := json.Marshal(tx) + if err != nil { + t.Errorf("json.Marshal failed: %v", err) + } + + var parsedTx *Transaction + if err := json.Unmarshal(data, &parsedTx); err != nil { + t.Errorf("json.Unmarshal failed: %v", err) + } + + // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S + if tx.Hash() != parsedTx.Hash() { + t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx) + } + if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 { + t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId()) + } + } +} diff --git a/core/vm/common.go b/core/vm/common.go index 2878b92d2..b7b9a6a9c 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -21,28 +21,11 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" -) - -// Type is the VM type accepted by **NewVm** -type Type byte - -const ( - StdVmTy Type = iota // Default standard VM - JitVmTy // LLVM JIT VM - MaxVmTy ) var ( - Pow256 = common.BigPow(2, 256) // Pow256 is 2**256 - U256 = common.U256 // Shortcut to common.U256 S256 = common.S256 // Shortcut to common.S256 - - Zero = common.Big0 // Shortcut to common.Big0 - One = common.Big1 // Shortcut to common.Big1 - - max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer ) // calculates the memory size required for a step @@ -54,48 +37,6 @@ func calcMemSize(off, l *big.Int) *big.Int { return new(big.Int).Add(off, l) } -// calculates the quadratic gas -func quadMemGas(mem *Memory, newMemSize, gas *big.Int) { - if newMemSize.Cmp(common.Big0) > 0 { - newMemSizeWords := toWordSize(newMemSize) - newMemSize.Mul(newMemSizeWords, u256(32)) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - // be careful reusing variables here when changing. - // The order has been optimised to reduce allocation - oldSize := toWordSize(big.NewInt(int64(mem.Len()))) - pow := new(big.Int).Exp(oldSize, common.Big2, Zero) - linCoef := oldSize.Mul(oldSize, params.MemoryGas) - quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) - oldTotalFee := new(big.Int).Add(linCoef, quadCoef) - - pow.Exp(newMemSizeWords, common.Big2, Zero) - linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) - quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) - newTotalFee := linCoef.Add(linCoef, quadCoef) - - fee := newTotalFee.Sub(newTotalFee, oldTotalFee) - gas.Add(gas, fee) - } - } -} - -// Simple helper -func u256(n int64) *big.Int { - return big.NewInt(n) -} - -// Mainly used for print variables and passing to Print* -func toValue(val *big.Int) interface{} { - // Let's assume a string on right padded zero's - b := val.Bytes() - if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 { - return string(b) - } - - return val -} - // getData returns a slice from the data based on the start and size and pads // up to size with zero's. This function is overflow safe. func getData(data []byte, start, size *big.Int) []byte { @@ -106,14 +47,17 @@ func getData(data []byte, start, size *big.Int) []byte { return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) } -// useGas attempts to subtract the amount of gas and returns whether it was -// successful -func useGas(gas, amount *big.Int) bool { - if gas.Cmp(amount) < 0 { - return false +// bigUint64 returns the integer casted to a uint64 and returns whether it +// overflowed in the process. +func bigUint64(v *big.Int) (uint64, bool) { + return v.Uint64(), v.BitLen() > 64 +} + +// toWordSize returns the ceiled word size required for memory expansion. +func toWordSize(size uint64) uint64 { + if size > math.MaxUint64-31 { + return math.MaxUint64/32 + 1 } - // Sub the amount of gas from the remaining - gas.Sub(gas, amount) - return true + return (size + 31) / 32 } diff --git a/core/vm/contract.go b/core/vm/contract.go index dfa93ab18..091106d84 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -24,7 +24,6 @@ import ( // ContractRef is a reference to the contract's backing object type ContractRef interface { - ReturnGas(*big.Int) Address() common.Address Value() *big.Int SetCode(common.Hash, []byte) @@ -48,7 +47,8 @@ type Contract struct { CodeAddr *common.Address Input []byte - value, Gas, UsedGas *big.Int + Gas uint64 + value *big.Int Args []byte @@ -56,7 +56,7 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract { +func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} if parent, ok := caller.(*Contract); ok { @@ -68,9 +68,8 @@ func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *C // Gas should be a pointer so it can safely be reduced through the run // This pointer will be off the state transition - c.Gas = gas //new(big.Int).Set(gas) + c.Gas = gas c.value = new(big.Int).Set(value) - c.UsedGas = new(big.Int) return c } @@ -107,27 +106,13 @@ func (c *Contract) Caller() common.Address { return c.CallerAddress } -// Finalise finalises the contract and returning any remaining gas to the original -// caller. -func (c *Contract) Finalise() { - // Return the remaining gas to the caller - c.caller.ReturnGas(c.Gas) -} - // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas *big.Int) (ok bool) { - ok = useGas(c.Gas, gas) - if ok { - c.UsedGas.Add(c.UsedGas, gas) +func (c *Contract) UseGas(gas uint64) (ok bool) { + if c.Gas < gas { + return false } - return -} - -// ReturnGas adds the given gas back to itself. -func (c *Contract) ReturnGas(gas *big.Int) { - // Return the gas to the context - c.Gas.Add(c.Gas, gas) - c.UsedGas.Sub(c.UsedGas, gas) + c.Gas -= gas + return true } // Address returns the contracts address diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9645d268f..593b6ca55 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -17,8 +17,6 @@ package vm import ( - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" @@ -30,8 +28,8 @@ import ( // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use - Run(input []byte) []byte // Run runs the precompiled contract + RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte) []byte // Run runs the precompiled contract } // Precompiled contains the default set of ethereum contracts @@ -57,7 +55,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr // ECRECOVER implemented as a native contract type ecrecover struct{} -func (c *ecrecover) RequiredGas(inputSize int) *big.Int { +func (c *ecrecover) RequiredGas(inputSize int) uint64 { return params.EcrecoverGas } @@ -92,10 +90,12 @@ func (c *ecrecover) Run(in []byte) []byte { // SHA256 implemented as a native contract type sha256 struct{} -func (c *sha256) RequiredGas(inputSize int) *big.Int { - n := big.NewInt(int64(inputSize+31) / 32) - n.Mul(n, params.Sha256WordGas) - return n.Add(n, params.Sha256Gas) +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *sha256) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.Sha256WordGas + params.Sha256Gas } func (c *sha256) Run(in []byte) []byte { return crypto.Sha256(in) @@ -104,10 +104,12 @@ func (c *sha256) Run(in []byte) []byte { // RIPMED160 implemented as a native contract type ripemd160 struct{} -func (c *ripemd160) RequiredGas(inputSize int) *big.Int { - n := big.NewInt(int64(inputSize+31) / 32) - n.Mul(n, params.Ripemd160WordGas) - return n.Add(n, params.Ripemd160Gas) +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *ripemd160) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas } func (c *ripemd160) Run(in []byte) []byte { return common.LeftPadBytes(crypto.Ripemd160(in), 32) @@ -116,11 +118,12 @@ func (c *ripemd160) Run(in []byte) []byte { // data copy implemented as a native contract type dataCopy struct{} -func (c *dataCopy) RequiredGas(inputSize int) *big.Int { - n := big.NewInt(int64(inputSize+31) / 32) - n.Mul(n, params.IdentityWordGas) - - return n.Add(n, params.IdentityGas) +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *dataCopy) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.IdentityWordGas + params.IdentityGas } func (c *dataCopy) Run(in []byte) []byte { return in diff --git a/core/vm/environment.go b/core/vm/evm.go index c19ef464b..0c5d998c2 100644 --- a/core/vm/environment.go +++ b/core/vm/evm.go @@ -17,7 +17,6 @@ package vm import ( - "fmt" "math/big" "sync/atomic" @@ -102,24 +101,18 @@ func (evm *EVM) Cancel() { // Call executes the contract associated with the addr with the given input as parameters. It also handles any // necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // case of an execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - caller.ReturnGas(gas) - - return nil, nil + return nil, gas, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas) - - return nil, ErrDepth + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth } if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { - caller.ReturnGas(gas) - - return nil, ErrInsufficientBalance + return nil, gas, ErrInsufficientBalance } var ( @@ -128,8 +121,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, ) if !evm.StateDB.Exist(addr) { if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 { - caller.ReturnGas(gas) - return nil, nil + return nil, gas, nil } to = evm.StateDB.CreateAccount(addr) @@ -143,7 +135,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) // When an error was returned by the EVM or when setting the creation code @@ -154,7 +145,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, evm.StateDB.RevertToSnapshot(snapshot) } - return ret, err + return ret, contract.Gas, err } // CallCode executes the contract associated with the addr with the given input as parameters. It also handles any @@ -162,24 +153,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, // case of an execution error or failed value transfer. // // CallCode differs from Call in the sense that it executes the given address' code with the caller as context. -func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - caller.ReturnGas(gas) - - return nil, nil + return nil, gas, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas) - - return nil, ErrDepth + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { - caller.ReturnGas(gas) - - return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address())) + return nil, gas, ErrInsufficientBalance } var ( @@ -191,7 +176,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) if err != nil { @@ -200,7 +184,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) } - return ret, err + return ret, contract.Gas, err } // DelegateCall executes the contract associated with the addr with the given input as parameters. @@ -208,18 +192,15 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // // DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context // and the caller is set to the caller of the caller. -func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { +func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - caller.ReturnGas(gas) - - return nil, nil + return nil, gas, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas) - return nil, ErrDepth + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth } var ( @@ -230,7 +211,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Iinitialise a new contract and make initialise the delegate values contract := NewContract(caller, to, caller.Value(), gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) if err != nil { @@ -239,28 +219,22 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) } - return ret, err + return ret, contract.Gas, err } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - caller.ReturnGas(gas) - - return nil, common.Address{}, nil + return nil, common.Address{}, gas, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas) - - return nil, common.Address{}, ErrDepth + if evm.depth > int(params.CallCreateDepth) { + return nil, common.Address{}, gas, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { - caller.ReturnGas(gas) - - return nil, common.Address{}, ErrInsufficientBalance + return nil, common.Address{}, gas, ErrInsufficientBalance } // Create a new account on the state @@ -280,7 +254,6 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) - defer contract.Finalise() ret, err = evm.interpreter.Run(contract, nil) @@ -291,9 +264,8 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. if err == nil && !maxCodeSizeExceeded { - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if contract.UseGas(dataGas) { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if contract.UseGas(createDataGas) { evm.StateDB.SetCode(contractAddr, ret) } else { err = ErrCodeStoreOutOfGas @@ -305,11 +277,10 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re // when we're in homestead this also counts for code storage gas errors. if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { - contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) // Nothing should be returned when an error is thrown. - return nil, contractAddr, err + return nil, contractAddr, 0, err } // If the vm returned with an error the return value should be set to nil. // This isn't consensus critical but merely to for behaviour reasons such as @@ -318,7 +289,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re ret = nil } - return ret, contractAddr, err + return ret, contractAddr, contract.Gas, err } // ChainConfig returns the evmironment's chain configuration diff --git a/core/vm/gas.go b/core/vm/gas.go index fdbc0df7f..dd64d5f17 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -17,149 +17,42 @@ package vm import ( - "fmt" "math/big" "github.com/ethereum/go-ethereum/params" ) -var ( - GasQuickStep = big.NewInt(2) - GasFastestStep = big.NewInt(3) - GasFastStep = big.NewInt(5) - GasMidStep = big.NewInt(8) - GasSlowStep = big.NewInt(10) - GasExtStep = big.NewInt(20) - - GasReturn = big.NewInt(0) - GasStop = big.NewInt(0) - - GasContractByte = big.NewInt(200) - - n64 = big.NewInt(64) +const ( + GasQuickStep uint64 = 2 + GasFastestStep uint64 = 3 + GasFastStep uint64 = 5 + GasMidStep uint64 = 8 + GasSlowStep uint64 = 10 + GasExtStep uint64 = 20 + + GasReturn uint64 = 0 + GasStop uint64 = 0 + GasContractByte uint64 = 200 ) // calcGas returns the actual gas cost of the call. // // The cost of gas was changed during the homestead price change HF. To allow for EIP150 // to be implemented. The returned gas is gas - base * 63 / 64. -func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int { - if gasTable.CreateBySuicide != nil { - availableGas = new(big.Int).Sub(availableGas, base) - g := new(big.Int).Div(availableGas, n64) - g.Sub(availableGas, g) - - if g.Cmp(callCost) < 0 { - return g +func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) { + if gasTable.CreateBySuicide > 0 { + availableGas = availableGas - base + gas := availableGas - availableGas/64 + // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 + // is smaller than the requested amount. Therefor we return the new gas instead + // of returning an error. + if callCost.BitLen() > 64 || gas < callCost.Uint64() { + return gas, nil } } - return callCost -} - -// baseCheck checks for any stack error underflows -func baseCheck(op OpCode, stack *Stack, gas *big.Int) error { - // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit - // PUSH is also allowed to calculate the same price for all PUSHes - // DUP requirements are handled elsewhere (except for the stack limit check) - if op >= PUSH1 && op <= PUSH32 { - op = PUSH1 - } - if op >= DUP1 && op <= DUP16 { - op = DUP1 - } - - if r, ok := _baseCheck[op]; ok { - err := stack.require(r.stackPop) - if err != nil { - return err - } - - if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) { - return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64()) - } - - gas.Add(gas, r.gas) + if callCost.BitLen() > 64 { + return 0, errGasUintOverflow } - return nil -} - -// casts a arbitrary number to the amount of words (sets of 32 bytes) -func toWordSize(size *big.Int) *big.Int { - tmp := new(big.Int) - tmp.Add(size, u256(31)) - tmp.Div(tmp, u256(32)) - return tmp -} - -type req struct { - stackPop int - gas *big.Int - stackPush int -} -var _baseCheck = map[OpCode]req{ - // opcode | stack pop | gas price | stack push - ADD: {2, GasFastestStep, 1}, - LT: {2, GasFastestStep, 1}, - GT: {2, GasFastestStep, 1}, - SLT: {2, GasFastestStep, 1}, - SGT: {2, GasFastestStep, 1}, - EQ: {2, GasFastestStep, 1}, - ISZERO: {1, GasFastestStep, 1}, - SUB: {2, GasFastestStep, 1}, - AND: {2, GasFastestStep, 1}, - OR: {2, GasFastestStep, 1}, - XOR: {2, GasFastestStep, 1}, - NOT: {1, GasFastestStep, 1}, - BYTE: {2, GasFastestStep, 1}, - CALLDATALOAD: {1, GasFastestStep, 1}, - CALLDATACOPY: {3, GasFastestStep, 1}, - MLOAD: {1, GasFastestStep, 1}, - MSTORE: {2, GasFastestStep, 0}, - MSTORE8: {2, GasFastestStep, 0}, - CODECOPY: {3, GasFastestStep, 0}, - MUL: {2, GasFastStep, 1}, - DIV: {2, GasFastStep, 1}, - SDIV: {2, GasFastStep, 1}, - MOD: {2, GasFastStep, 1}, - SMOD: {2, GasFastStep, 1}, - SIGNEXTEND: {2, GasFastStep, 1}, - ADDMOD: {3, GasMidStep, 1}, - MULMOD: {3, GasMidStep, 1}, - JUMP: {1, GasMidStep, 0}, - JUMPI: {2, GasSlowStep, 0}, - EXP: {2, GasSlowStep, 1}, - ADDRESS: {0, GasQuickStep, 1}, - ORIGIN: {0, GasQuickStep, 1}, - CALLER: {0, GasQuickStep, 1}, - CALLVALUE: {0, GasQuickStep, 1}, - CODESIZE: {0, GasQuickStep, 1}, - GASPRICE: {0, GasQuickStep, 1}, - COINBASE: {0, GasQuickStep, 1}, - TIMESTAMP: {0, GasQuickStep, 1}, - NUMBER: {0, GasQuickStep, 1}, - CALLDATASIZE: {0, GasQuickStep, 1}, - DIFFICULTY: {0, GasQuickStep, 1}, - GASLIMIT: {0, GasQuickStep, 1}, - POP: {1, GasQuickStep, 0}, - PC: {0, GasQuickStep, 1}, - MSIZE: {0, GasQuickStep, 1}, - GAS: {0, GasQuickStep, 1}, - BLOCKHASH: {1, GasExtStep, 1}, - BALANCE: {1, Zero, 1}, - EXTCODESIZE: {1, Zero, 1}, - EXTCODECOPY: {4, Zero, 0}, - SLOAD: {1, params.SloadGas, 1}, - SSTORE: {2, Zero, 0}, - SHA3: {2, params.Sha3Gas, 1}, - CREATE: {3, params.CreateGas, 1}, - // Zero is calculated in the gasSwitch - CALL: {7, Zero, 1}, - CALLCODE: {7, Zero, 1}, - DELEGATECALL: {6, Zero, 1}, - SUICIDE: {1, Zero, 0}, - JUMPDEST: {0, params.JumpdestGas, 0}, - RETURN: {2, Zero, 0}, - PUSH1: {0, GasFastestStep, 1}, - DUP1: {0, Zero, 1}, + return callCost.Uint64(), nil } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 4d2c4e94a..fba1eb066 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -1,56 +1,80 @@ package vm import ( + gmath "math" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" ) -func memoryGasCost(mem *Memory, newMemSize *big.Int) *big.Int { - gas := new(big.Int) - if newMemSize.Cmp(common.Big0) > 0 { - newMemSizeWords := toWordSize(newMemSize) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - // be careful reusing variables here when changing. - // The order has been optimised to reduce allocation - oldSize := toWordSize(big.NewInt(int64(mem.Len()))) - pow := new(big.Int).Exp(oldSize, common.Big2, Zero) - linCoef := oldSize.Mul(oldSize, params.MemoryGas) - quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) - oldTotalFee := new(big.Int).Add(linCoef, quadCoef) - - pow.Exp(newMemSizeWords, common.Big2, Zero) - linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) - quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) - newTotalFee := linCoef.Add(linCoef, quadCoef) - - fee := newTotalFee.Sub(newTotalFee, oldTotalFee) - gas.Add(gas, fee) - } +// memoryGasCosts calculates the quadratic gas for memory expansion. It does so +// only for the memory region that is expanded, not the total memory. +func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { + // The maximum that will fit in a uint64 is max_word_count - 1 + // anything above that will result in an overflow. + if newMemSize > gmath.MaxUint64-32 { + return 0, errGasUintOverflow + } + + if newMemSize == 0 { + return 0, nil } - return gas + + newMemSizeWords := toWordSize(newMemSize) + newMemSize = newMemSizeWords * 32 + + if newMemSize > uint64(mem.Len()) { + square := newMemSizeWords * newMemSizeWords + linCoef := newMemSizeWords * params.MemoryGas + quadCoef := square / params.QuadCoeffDiv + newTotalFee := linCoef + quadCoef + + fee := newTotalFee - mem.lastGasCost + mem.lastGasCost = newTotalFee + + return fee, nil + } + return 0, nil } -func constGasFunc(gas *big.Int) gasFunc { - return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return gas +func constGasFunc(gas uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gas, nil } } -func gasCalldataCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := memoryGasCost(mem, memorySize) - gas.Add(gas, GasFastestStep) - words := toWordSize(stack.Back(2)) +func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } - return gas.Add(gas, words.Mul(words, params.CopyGas)) + words, overflow := bigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + + if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, words); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { +func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( y, x = stack.Back(1), stack.Back(0) - val = env.StateDB.GetState(contract.Address(), common.BigToHash(x)) + val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) ) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -58,189 +82,335 @@ func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, m // 3. From a non-zero to a non-zero (CHANGE) if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { // 0 => non 0 - return new(big.Int).Set(params.SstoreSetGas) + return params.SstoreSetGas, nil } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - env.StateDB.AddRefund(params.SstoreRefundGas) + evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas)) - return new(big.Int).Set(params.SstoreClearGas) + return params.SstoreClearGas, nil } else { // non 0 => non 0 (or 0 => 0) - return new(big.Int).Set(params.SstoreResetGas) + return params.SstoreResetGas, nil } } -func makeGasLog(n uint) gasFunc { - return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - mSize := stack.Back(1) +func makeGasLog(n uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + requestedSize, overflow := bigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } - gas := new(big.Int).Add(memoryGasCost(mem, memorySize), params.LogGas) - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas)) - gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) - return gas + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow { + return 0, errGasUintOverflow + } + + var memorySizeGas uint64 + if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil } } -func gasSha3(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := memoryGasCost(mem, memorySize) - gas.Add(gas, params.Sha3Gas) - words := toWordSize(stack.Back(1)) - return gas.Add(gas, words.Mul(words, params.Sha3WordGas)) -} +func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } -func gasCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := memoryGasCost(mem, memorySize) - gas.Add(gas, GasFastestStep) - words := toWordSize(stack.Back(2)) + if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow { + return 0, errGasUintOverflow + } - return gas.Add(gas, words.Mul(words, params.CopyGas)) + wordGas, overflow := bigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasExtCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := memoryGasCost(mem, memorySize) - gas.Add(gas, gt.ExtcodeCopy) - words := toWordSize(stack.Back(3)) +func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } - return gas.Add(gas, words.Mul(words, params.CopyGas)) + wordGas, overflow := bigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasMLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) +func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := bigUint64(stack.Back(3)) + if overflow { + return 0, errGasUintOverflow + } + + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasMStore8(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) +func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasMStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) +func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasCreate(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return new(big.Int).Add(params.CreateGas, memoryGasCost(mem, memorySize)) +func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return gt.Balance +func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return gt.ExtcodeSize +func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.Balance, nil } -func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return gt.SLoad +func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.ExtcodeSize, nil } -func gasExp(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8) - gas := big.NewInt(expByteLen) - gas.Mul(gas, gt.ExpByte) - return gas.Add(gas, GasSlowStep) +func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.SLoad, nil } -func gasCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := new(big.Int).Set(gt.Calls) +func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) - transfersValue := stack.Back(2).BitLen() > 0 var ( - address = common.BigToAddress(stack.Back(1)) - eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) + gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas + overflow bool + ) + if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + gas = gt.Calls + transfersValue = stack.Back(2).BitLen() > 0 + address = common.BigToAddress(stack.Back(1)) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) ) if eip158 { - if env.StateDB.Empty(address) && transfersValue { - gas.Add(gas, params.CallNewAccountGas) + if evm.StateDB.Empty(address) && transfersValue { + gas += params.CallNewAccountGas } - } else if !env.StateDB.Exist(address) { - gas.Add(gas, params.CallNewAccountGas) + } else if !evm.StateDB.Exist(address) { + gas += params.CallNewAccountGas } if transfersValue { - gas.Add(gas, params.CallValueTransferGas) + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow } - gas.Add(gas, memoryGasCost(mem, memorySize)) - cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) + cg, 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] = cg + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - return gas.Add(gas, cg) + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasCallCode(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := new(big.Int).Set(gt.Calls) +func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas := gt.Calls if stack.Back(2).BitLen() > 0 { - gas.Add(gas, params.CallValueTransferGas) + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow } - gas.Add(gas, memoryGasCost(mem, memorySize)) - cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) + cg, 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] = cg + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - return gas.Add(gas, cg) + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { +func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return memoryGasCost(mem, memorySize) } -func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := new(big.Int) +func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var gas uint64 // EIP150 homestead gas reprice fork: - if env.ChainConfig().IsEIP150(env.BlockNumber) { - gas.Set(gt.Suicide) + if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + gas = gt.Suicide var ( address = common.BigToAddress(stack.Back(0)) - eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) ) if eip158 { // if empty and transfers value - if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 { - gas.Add(gas, gt.CreateBySuicide) + if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 { + gas += gt.CreateBySuicide } - } else if !env.StateDB.Exist(address) { - gas.Add(gas, gt.CreateBySuicide) + } else if !evm.StateDB.Exist(address) { + gas += gt.CreateBySuicide } } - if !env.StateDB.HasSuicided(contract.Address()) { - env.StateDB.AddRefund(params.SuicideRefundGas) + if !evm.StateDB.HasSuicided(contract.Address()) { + evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas)) } - return gas + return gas, nil } -func gasDelegateCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - gas := new(big.Int).Add(gt.Calls, memoryGasCost(mem, memorySize)) +func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { + return 0, errGasUintOverflow + } - cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) + cg, 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] = cg + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - return gas.Add(gas, cg) + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil } -func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return GasFastestStep +func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil } -func gasSwap(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return GasFastestStep +func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil } -func gasDup(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { - return GasFastestStep +func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go new file mode 100644 index 000000000..cceb89285 --- /dev/null +++ b/core/vm/gas_table_test.go @@ -0,0 +1,24 @@ +package vm + +import ( + "math" + "testing" +) + +func TestMemoryGasCost(t *testing.T) { + size := uint64(math.MaxUint64 - 64) + _, err := memoryGasCost(&Memory{}, size) + if err != nil { + t.Error("didn't expect error:", err) + } + + _, err = memoryGasCost(&Memory{}, size+32) + if err != nil { + t.Error("didn't expect error:", err) + } + + _, err = memoryGasCost(&Memory{}, size+33) + if err == nil { + t.Error("expected error") + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3b1b06cca..39e5c0587 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,42 +27,56 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +var bigZero = new(big.Int) + +func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Add(x, y))) + + evm.interpreter.intPool.put(y) + return nil, nil } -func opSub(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSub(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Sub(x, y))) + + evm.interpreter.intPool.put(y) + return nil, nil } -func opMul(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMul(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Mul(x, y))) + + evm.interpreter.intPool.put(y) + return nil, nil } -func opDiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opDiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) != 0 { stack.push(U256(x.Div(x, y))) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(y) + return nil, nil } -func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) return nil, nil } else { n := new(big.Int) - if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 { + if evm.interpreter.intPool.get().Mul(x, y).Cmp(common.Big0) < 0 { n.SetInt64(-1) } else { n.SetInt64(1) @@ -73,20 +87,22 @@ func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta stack.push(U256(res)) } + evm.interpreter.intPool.put(y) return nil, nil } -func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) } else { stack.push(U256(x.Mod(x, y))) } + evm.interpreter.intPool.put(y) return nil, nil } -func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { @@ -104,16 +120,20 @@ func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta stack.push(U256(res)) } + evm.interpreter.intPool.put(y) return nil, nil } -func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { base, exponent := stack.pop(), stack.pop() stack.push(math.Exp(base, exponent)) + + evm.interpreter.intPool.put(base, exponent) + return nil, nil } -func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -128,198 +148,231 @@ func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stac stack.push(U256(num)) } + + evm.interpreter.intPool.put(back) return nil, nil } -func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opNot(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.pop() stack.push(U256(x.Not(x))) return nil, nil } -func opLt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) < 0 { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(x, y) return nil, nil } -func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) > 0 { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(x, y) return nil, nil } -func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(S256(y)) < 0 { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(x, y) return nil, nil } -func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(y) > 0 { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(x, y) return nil, nil } -func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) == 0 { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(x, y) return nil, nil } -func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.pop() if x.Cmp(common.Big0) > 0 { stack.push(new(big.Int)) } else { - stack.push(big.NewInt(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(1)) } + + evm.interpreter.intPool.put(x) return nil, nil } -func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) + + evm.interpreter.intPool.put(y) return nil, nil } -func opOr(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.Or(x, y)) + + evm.interpreter.intPool.put(y) return nil, nil } -func opXor(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.Xor(x, y)) + + evm.interpreter.intPool.put(y) return nil, nil } -func opByte(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { - byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) + byte := evm.interpreter.intPool.get().SetInt64(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) stack.push(byte) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(th, val) return nil, nil } -func opAddmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(Zero) > 0 { + if z.Cmp(bigZero) > 0 { add := x.Add(x, y) add.Mod(add, z) stack.push(U256(add)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(y, z) return nil, nil } -func opMulmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(Zero) > 0 { + if z.Cmp(bigZero) > 0 { mul := x.Mul(x, y) mul.Mod(mul, z) stack.push(U256(mul)) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(y, z) return nil, nil } -func opSha3(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() data := memory.Get(offset.Int64(), size.Int64()) hash := crypto.Keccak256(data) - if env.vmConfig.EnablePreimageRecording { - env.StateDB.AddPreimage(common.BytesToHash(hash), data) + if evm.vmConfig.EnablePreimageRecording { + evm.StateDB.AddPreimage(common.BytesToHash(hash), data) } stack.push(common.BytesToBig(hash)) + + evm.interpreter.intPool.put(offset, size) return nil, nil } -func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(common.Bytes2Big(contract.Address().Bytes())) return nil, nil } -func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { addr := common.BigToAddress(stack.pop()) - balance := env.StateDB.GetBalance(addr) + balance := evm.StateDB.GetBalance(addr) stack.push(new(big.Int).Set(balance)) return nil, nil } -func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(env.Origin.Big()) +func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.Origin.Big()) return nil, nil } -func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(contract.Caller().Big()) return nil, nil } -func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(new(big.Int).Set(contract.value)) +func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().Set(contract.value)) return nil, nil } -func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCalldataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) return nil, nil } -func opCalldataSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(big.NewInt(int64(len(contract.Input)))) +func opCalldataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) return nil, nil } -func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) + + evm.interpreter.intPool.put(mOff, cOff, l) return nil, nil } -func opExtCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - addr := common.BigToAddress(stack.pop()) - l := big.NewInt(int64(env.StateDB.GetCodeSize(addr))) - stack.push(l) +func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + a := stack.pop() + + addr := common.BigToAddress(a) + a.SetInt64(int64(evm.StateDB.GetCodeSize(addr))) + stack.push(a) + return nil, nil } -func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - l := big.NewInt(int64(len(contract.Code))) +func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code))) stack.push(l) return nil, nil } -func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( mOff = stack.pop() cOff = stack.pop() @@ -328,113 +381,129 @@ func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack codeCopy := getData(contract.Code, cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + + evm.interpreter.intPool.put(mOff, cOff, l) return nil, nil } -func opExtCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( addr = common.BigToAddress(stack.pop()) mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - codeCopy := getData(env.StateDB.GetCode(addr), cOff, l) + codeCopy := getData(evm.StateDB.GetCode(addr), cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + + evm.interpreter.intPool.put(mOff, cOff, l) + return nil, nil } -func opGasprice(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(new(big.Int).Set(env.GasPrice)) +func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice)) return nil, nil } -func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { num := stack.pop() - n := new(big.Int).Sub(env.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 { - stack.push(env.GetHash(num.Uint64()).Big()) + n := evm.interpreter.intPool.get().Sub(evm.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(evm.BlockNumber) < 0 { + stack.push(evm.GetHash(num.Uint64()).Big()) } else { stack.push(new(big.Int)) } + + evm.interpreter.intPool.put(num, n) return nil, nil } -func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(env.Coinbase.Big()) +func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.Coinbase.Big()) return nil, nil } -func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(env.Time))) +func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.Time))) return nil, nil } -func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(env.BlockNumber))) +func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.BlockNumber))) return nil, nil } -func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(env.Difficulty))) +func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.Difficulty))) return nil, nil } -func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(env.GasLimit))) +func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.GasLimit))) return nil, nil } -func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.pop() +func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + evm.interpreter.intPool.put(stack.pop()) return nil, nil } -func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset := stack.pop() val := common.BigD(memory.Get(offset.Int64(), 32)) stack.push(val) + + evm.interpreter.intPool.put(offset) return nil, nil } -func opMstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // pop value of the stack mStart, val := stack.pop(), stack.pop() memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) + + evm.interpreter.intPool.put(mStart, val) return nil, nil } -func opMstore8(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) + return nil, nil } -func opSload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) - val := env.StateDB.GetState(contract.Address(), loc).Big() + val := evm.StateDB.GetState(contract.Address(), loc).Big() stack.push(val) return nil, nil } -func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) val := stack.pop() - env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + + evm.interpreter.intPool.put(val) return nil, nil } -func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos := stack.pop() if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { nop := contract.GetOp(pos.Uint64()) return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) } *pc = pos.Uint64() + + evm.interpreter.intPool.put(pos) return nil, nil } -func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos, cond := stack.pop(), stack.pop() if cond.Cmp(common.BigTrue) >= 0 { if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { @@ -445,57 +514,62 @@ func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *St } else { *pc++ } + + evm.interpreter.intPool.put(pos, cond) return nil, nil } -func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(new(big.Int).SetUint64(*pc)) +func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetUint64(*pc)) return nil, nil } -func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(big.NewInt(int64(memory.Len()))) +func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len()))) return nil, nil } -func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(new(big.Int).Set(contract.Gas)) +func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas)) return nil, nil } -func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(contract.Gas) + gas = contract.Gas ) - if env.ChainConfig().IsEIP150(env.BlockNumber) { - gas.Div(gas, n64) - gas = gas.Sub(contract.Gas, gas) + if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + gas -= gas / 64 } contract.UseGas(gas) - _, addr, suberr := env.Create(contract, input, gas, value) + _, addr, returnGas, suberr := evm.Create(contract, input, gas, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas { + if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas { stack.push(new(big.Int)) } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { stack.push(new(big.Int)) } else { stack.push(addr.Big()) } + contract.Gas += returnGas + + evm.interpreter.intPool.put(value, offset, size) + return nil, nil } -func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop() +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() value = U256(value) @@ -509,25 +583,26 @@ func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta // Get the arguments from the memory args := memory.Get(inOffset.Int64(), inSize.Int64()) - if len(value.Bytes()) > 0 { - gas.Add(gas, params.CallStipend) + if value.BitLen() > 0 { + gas += params.CallStipend } - ret, err := env.Call(contract, address, args, gas, value) - + ret, returnGas, err := evm.Call(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) - } else { stack.push(big.NewInt(1)) memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + contract.Gas += returnGas + + evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return nil, nil } -func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop() +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() value = U256(value) @@ -541,12 +616,11 @@ func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack // Get the arguments from the memory args := memory.Get(inOffset.Int64(), inSize.Int64()) - if len(value.Bytes()) > 0 { - gas.Add(gas, params.CallStipend) + if value.BitLen() > 0 { + gas += params.CallStipend } - ret, err := env.CallCode(contract, address, args, gas, value) - + ret, returnGas, err := evm.CallCode(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) @@ -555,46 +629,54 @@ func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + contract.Gas += returnGas + + evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return nil, nil } -func opDelegateCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // if not homestead return an error. DELEGATECALL is not supported // during pre-homestead. - if !env.ChainConfig().IsHomestead(env.BlockNumber) { + if !evm.ChainConfig().IsHomestead(evm.BlockNumber) { return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) } - gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, err := env.DelegateCall(contract, toAddr, args, gas) + + ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { stack.push(big.NewInt(1)) memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) } + contract.Gas += returnGas + + evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) return nil, nil } -func opReturn(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := memory.GetPtr(offset.Int64(), size.Int64()) + evm.interpreter.intPool.put(offset, size) return ret, nil } -func opStop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - balance := env.StateDB.GetBalance(contract.Address()) - env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) +func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + balance := evm.StateDB.GetBalance(contract.Address()) + evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) - env.StateDB.Suicide(contract.Address()) + evm.StateDB.Suicide(contract.Address()) return nil, nil } @@ -603,7 +685,7 @@ func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack * // make log instruction function func makeLog(size int) executionFunc { - return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { @@ -611,22 +693,24 @@ func makeLog(size int) executionFunc { } d := memory.Get(mStart.Int64(), mSize.Int64()) - env.StateDB.AddLog(&types.Log{ + evm.StateDB.AddLog(&types.Log{ Address: contract.Address(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: env.BlockNumber.Uint64(), + BlockNumber: evm.BlockNumber.Uint64(), }) + + evm.interpreter.intPool.put(mStart, mSize) return nil, nil } } // make push instruction function func makePush(size uint64, bsize *big.Int) executionFunc { - return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize) + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + byts := getData(contract.Code, evm.interpreter.intPool.get().SetUint64(*pc+1), bsize) stack.push(common.Bytes2Big(byts)) *pc += size return nil, nil @@ -635,7 +719,7 @@ func makePush(size uint64, bsize *big.Int) executionFunc { // make push instruction function func makeDup(size int64) executionFunc { - return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.dup(int(size)) return nil, nil } @@ -645,7 +729,7 @@ func makeDup(size int64) executionFunc { func makeSwap(size int64) executionFunc { // switch n + 1 otherwise n would be swapped with n size += 1 - return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.swap(int(size)) return nil, nil } diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go new file mode 100644 index 000000000..61c83ba7e --- /dev/null +++ b/core/vm/int_pool_verifier.go @@ -0,0 +1,15 @@ +// +build VERIFY_EVM_INTEGER_POOL + +package vm + +import "fmt" + +const verifyPool = true + +func verifyIntegerPool(ip *intPool) { + for i, item := range ip.pool.data { + if item.Cmp(checkVal) != 0 { + panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i)) + } + } +} diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go new file mode 100644 index 000000000..982f8c6dd --- /dev/null +++ b/core/vm/int_pool_verifier_empty.go @@ -0,0 +1,7 @@ +// +build !VERIFY_EVM_INTEGER_POOL + +package vm + +const verifyPool = false + +func verifyIntegerPool(ip *intPool) {} diff --git a/core/vm/vm.go b/core/vm/interpreter.go index a5f48750d..ad41e3602 100644 --- a/core/vm/vm.go +++ b/core/vm/interpreter.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -60,6 +61,7 @@ type Interpreter struct { env *EVM cfg Config gasTable params.GasTable + intPool *intPool } // NewInterpreter returns a new instance of the Interpreter. @@ -75,6 +77,7 @@ func NewInterpreter(env *EVM, cfg Config) *Interpreter { env: env, cfg: cfg, gasTable: env.ChainConfig().GasTable(env.BlockNumber), + intPool: newIntPool(), } } @@ -106,14 +109,18 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible. pc = uint64(0) // program counter - cost *big.Int + cost uint64 ) contract.Input = input // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil && evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) + // XXX For debugging + //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err) + // TODO update the tracer + g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) } }() @@ -126,7 +133,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e } // The Interpreter main run loop (contextual). This loop runs until either an - // explicit STOP, RETURN or SUICIDE is executed, an error accured during + // explicit STOP, RETURN or SUICIDE is executed, an error occurred during // the execution of one of the operations or until the evm.done is set by // the parent context.Context. for atomic.LoadInt32(&evm.env.abort) == 0 { @@ -147,34 +154,47 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e return nil, err } - var memorySize *big.Int + var memorySize uint64 // calculate the new memory size and expand the memory to fit // the operation if operation.memorySize != nil { - memorySize = operation.memorySize(stack) + memSize, overflow := bigUint64(operation.memorySize(stack)) + if overflow { + return nil, errGasUintOverflow + } // memory is expanded in words of 32 bytes. Gas // is also calculated in words. - memorySize.Mul(toWordSize(memorySize), big.NewInt(32)) + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, errGasUintOverflow + } } if !evm.cfg.DisableGasMetering { // consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method cas get the proper cost - cost = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) - if !contract.UseGas(cost) { + cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) + if err != nil || !contract.UseGas(cost) { return nil, ErrOutOfGas } } - if memorySize != nil { - mem.Resize(memorySize.Uint64()) + if memorySize > 0 { + mem.Resize(memorySize) } if evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) + g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) } + // XXX For debugging + //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len()) // execute the operation res, err := operation.execute(&pc, evm.env, contract, mem, stack) + // verifyPool is a build flag. Pool verification makes sure the integrity + // of the integer pool by comparing values to a default value. + if verifyPool { + verifyIntegerPool(evm.intPool) + } switch { case err != nil: return nil, err diff --git a/core/vm/virtual_machine.go b/core/vm/intpool.go index 629108884..4f1228e14 100644 --- a/core/vm/virtual_machine.go +++ b/core/vm/intpool.go @@ -1,4 +1,4 @@ -// Copyright 2014 The go-ethereum Authors +// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -16,7 +16,34 @@ package vm -// VirtualMachine is an EVM interface -type VirtualMachine interface { - Run(*Contract, []byte) ([]byte, error) +import "math/big" + +var checkVal = big.NewInt(-42) + +// intPool is a pool of big integers that +// can be reused for all big.Int operations. +type intPool struct { + pool *Stack +} + +func newIntPool() *intPool { + return &intPool{pool: newstack()} +} + +func (p *intPool) get() *big.Int { + if p.pool.len() > 0 { + return p.pool.pop() + } + return new(big.Int) +} +func (p *intPool) put(is ...*big.Int) { + for _, i := range is { + // verifyPool is a build flag. Pool verification makes sure the integrity + // of the integer pool by comparing values to a default value. + if verifyPool { + i.Set(checkVal) + } + + p.pool.push(i) + } } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index f4ce81883..80e12c10b 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -17,6 +17,7 @@ package vm import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/params" @@ -24,11 +25,13 @@ import ( type ( executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) - gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int + gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 stackValidationFunc func(*Stack) error memorySizeFunc func(*Stack) *big.Int ) +var errGasUintOverflow = errors.New("gas uint64 overflow") + type operation struct { // op is the operation function execute executionFunc @@ -431,7 +434,7 @@ func NewJumpTable() [256]operation { }, STOP: { execute: opStop, - gasCost: constGasFunc(Zero), + gasCost: constGasFunc(0), validateStack: makeStackFunc(0, 0), halts: true, valid: true, diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 1d0bd96fa..ca60cba43 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -56,7 +56,7 @@ func TestStoreCapture(t *testing.T) { logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() - contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int)) + contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) ) stack.push(big.NewInt(1)) stack.push(big.NewInt(0)) @@ -78,7 +78,7 @@ func TestStorageCapture(t *testing.T) { t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it") var ( ref = &dummyContractRef{} - contract = NewContract(ref, ref, new(big.Int), new(big.Int)) + contract = NewContract(ref, ref, new(big.Int), 0) env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) logger = NewStructLogger(nil) mem = NewMemory() diff --git a/core/vm/memory.go b/core/vm/memory.go index d01188417..99a84d227 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -20,11 +20,12 @@ import "fmt" // Memory implements a simple memory model for the ethereum virtual machine. type Memory struct { - store []byte + store []byte + lastGasCost uint64 } func NewMemory() *Memory { - return &Memory{nil} + return &Memory{} } // Set sets offset + size to value diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index a25c6d71c..9aa88e669 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -36,7 +36,7 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM { BlockNumber: cfg.BlockNumber, Time: cfg.Time, Difficulty: cfg.Difficulty, - GasLimit: cfg.GasLimit, + GasLimit: new(big.Int).SetUint64(cfg.GasLimit), GasPrice: new(big.Int), } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index b5adb982c..cf46603db 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -17,6 +17,7 @@ package runtime import ( + "math" "math/big" "time" @@ -37,7 +38,7 @@ type Config struct { Coinbase common.Address BlockNumber *big.Int Time *big.Int - GasLimit *big.Int + GasLimit uint64 GasPrice *big.Int Value *big.Int DisableJit bool // "disable" so it's enabled by default @@ -68,8 +69,8 @@ func setDefaults(cfg *Config) { if cfg.Time == nil { cfg.Time = big.NewInt(time.Now().Unix()) } - if cfg.GasLimit == nil { - cfg.GasLimit = new(big.Int).Set(common.MaxBig) + if cfg.GasLimit == 0 { + cfg.GasLimit = math.MaxUint64 } if cfg.GasPrice == nil { cfg.GasPrice = new(big.Int) @@ -112,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver.SetCode(crypto.Keccak256Hash(code), code) // Call the code with the given configuration. - ret, err := vmenv.Call( + ret, _, err := vmenv.Call( sender, receiver.Address(), input, @@ -140,12 +141,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, error) { ) // Call the code with the given configuration. - return vmenv.Create( + code, address, _, err := vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) + return code, address, err } // Call executes the code given by the contract's address. It will return the @@ -160,7 +162,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. - ret, err := vmenv.Call( + ret, _, err := vmenv.Call( sender, address, input, diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 1e618b688..8ad74a89a 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) { if cfg.Time == nil { t.Error("expected time to be non nil") } - if cfg.GasLimit == nil { - t.Error("expected time to be non nil") + if cfg.GasLimit == 0 { + t.Error("didn't expect gaslimit to be zero") } if cfg.GasPrice == nil { t.Error("expected time to be non nil") diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go index ce4727a71..0936ef06f 100644 --- a/core/vm/stack_table.go +++ b/core/vm/stack_table.go @@ -12,8 +12,8 @@ func makeStackFunc(pop, push int) stackValidationFunc { return err } - if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() { - return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64()) + if push > 0 && stack.len()-pop+push > int(params.StackLimit) { + return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit) } return nil } diff --git a/eth/api_backend.go b/eth/api_backend.go index 1174588ea..72ed76cc4 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -106,14 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) { +func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) { statedb := state.(EthApiState).state from := statedb.GetOrNewStateObject(msg.From()) from.SetBalance(common.MaxBig) vmError := func() error { return nil } context := core.NewEVMContext(msg, header, b.eth.BlockChain()) - return vm.NewEVM(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil + return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/eth/backend.go b/eth/backend.go index e0233db36..af120cbad 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -105,7 +105,6 @@ type Config struct { type LesServer interface { Start(srvr *p2p.Server) - Synced() Stop() Protocols() []p2p.Protocol } diff --git a/eth/bind.go b/eth/bind.go index a9864b367..2ee9f2bf7 100644 --- a/eth/bind.go +++ b/eth/bind.go @@ -69,7 +69,7 @@ func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Add // against the pending block, not the stable head of the chain. func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) - return common.FromHex(out), err + return out, err } // ContractCall implements bind.ContractCaller executing an Ethereum contract @@ -77,7 +77,7 @@ func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg // against the pending block, not the stable head of the chain. func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) - return common.FromHex(out), err + return out, err } func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9be4bd87d..7e2952439 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -49,12 +49,12 @@ var ( MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request MaxStateFetch = 384 // Amount of node state values to allow fetching per request - MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation - rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests - rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests - rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value - ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion - ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts + MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation + rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests + rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests + rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value + ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion + ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts qosTuningPeers = 5 // Number of peers to tune based on (best peers) qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index b156c471d..a9ea797ea 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -119,7 +119,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren // If the block number is multiple of 3, send a bonus transaction to the miner if parent == dl.genesis && i%3 == 0 { signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey) if err != nil { panic(err) } diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index 2e28541ab..7a94241c6 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -51,7 +51,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common // If the block number is multiple of 3, send a bonus transaction to the miner if parent == genesis && i%3 == 0 { signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey) if err != nil { panic(err) } diff --git a/eth/handler.go b/eth/handler.go index 691fc0677..0e7eed352 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -173,7 +173,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int return blockchain.CurrentBlock().NumberU64() } inserter := func(blocks types.Blocks) (int, error) { - manager.setSynced() // Mark initial sync done on any fetcher import + atomic.StoreUint32(&manager.synced, 1) // Mark initial sync done on any fetcher import return manager.insertChain(blocks) } manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) diff --git a/eth/handler_test.go b/eth/handler_test.go index 8a5d7173b..03d5404a4 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -36,6 +36,8 @@ import ( "github.com/ethereum/go-ethereum/params" ) +var bigTxGas = new(big.Int).SetUint64(params.TxGas) + // Tests that protocol versions and modes of operations are matched up properly. func TestProtocolCompatibility(t *testing.T) { // Define the compatibility chart @@ -312,13 +314,13 @@ func testGetNodeData(t *testing.T, protocol int) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) case 2: @@ -404,13 +406,13 @@ func testGetReceipt(t *testing.T, protocol int) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) case 2: diff --git a/eth/sync.go b/eth/sync.go index 234534b4f..373cc2054 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -181,7 +181,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) { if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { return } - pm.setSynced() // Mark initial sync done + atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done // If fast sync was enabled, and we synced up, disable it if atomic.LoadUint32(&pm.fastSync) == 1 { @@ -192,10 +192,3 @@ func (pm *ProtocolManager) synchronise(peer *peer) { } } } - -// setSynced sets the synced flag and notifies the light server if present -func (pm *ProtocolManager) setSynced() { - if atomic.SwapUint32(&pm.synced, 1) == 0 && pm.lesServer != nil { - pm.lesServer.Synced() - } -} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 925f547b6..4c8e784c5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -47,6 +47,8 @@ import ( const defaultGas = 90000 +var emptyHex = "0x" + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -503,58 +505,76 @@ type CallArgs struct { Data hexutil.Bytes `json:"data"` } -func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) { defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { - return "0x", common.Big0, err + return nil, common.Big0, err } - // Set the account address to interact with - var addr common.Address - if args.From == (common.Address{}) { + // Set sender address or use a default if none specified + addr := args.From + if addr == (common.Address{}) { accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { + if len(accounts) > 0 { addr = accounts[0].Address } - } else { - addr = args.From } - // Assemble the CALL invocation + // Set default gas & gas price if none were set gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt() - if gas.Cmp(common.Big0) == 0 { + if gas.BitLen() == 0 { gas = big.NewInt(50000000) } - if gasPrice.Cmp(common.Big0) == 0 { + if gasPrice.BitLen() == 0 { gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) } + + // Create new call message msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false) - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return "0x", common.Big0, err + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if vmCfg.DisableGasMetering { + ctx, cancel = context.WithTimeout(ctx, time.Second*5) + } else { + ctx, cancel = context.WithCancel(ctx) } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer func() { cancel() }() + + // Get a new instance of the EVM. + evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg) + if err != nil { + return nil, common.Big0, err + } + // Wait for the context to be done and cancel the evm. Even if the + // EVM has finished, cancelling may be done (repeatedly) + go func() { + select { + case <-ctx.Done(): + evm.Cancel() + } + }() + + // Setup the gas pool (also for unmetered requests) + // and apply the message. gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) + res, gas, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { - return "0x", common.Big0, err - } - if len(res) == 0 { // backwards compatibility - return "0x", gas, err + return nil, common.Big0, err } - return common.ToHex(res), gas, err + return res, gas, err } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. -func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) { - result, _, err := s.doCall(ctx, args, blockNr) - return result, err +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { + result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) + return (hexutil.Bytes)(result), err } // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. @@ -576,7 +596,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (* mid := (hi + lo) / 2 (*big.Int)(&args.Gas).SetUint64(mid) - _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber) + _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) // If the transaction became invalid or used all the gas (failed), raise the gas limit if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index ebb14a5b5..214214f51 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -51,7 +51,7 @@ type Backend interface { GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) GetTd(blockHash common.Hash) *big.Int - GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.EVM, func() error, error) + GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error RemoveTx(txHash common.Hash) diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index 65a23f55e..693afe802 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -45,7 +45,7 @@ func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func runTrace(tracer *JavascriptTracer) (interface{}, error) { env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - contract := vm.NewContract(account{}, account{}, big.NewInt(0), big.NewInt(10000)) + contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} _, err := env.Interpreter().Run(contract, []byte{}) @@ -134,7 +134,7 @@ func TestHaltBetweenSteps(t *testing.T) { } env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0)) + contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) timeout := errors.New("stahp") diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index e10e66045..edbe45fa3 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -19,10 +19,8 @@ package web3ext var Modules = map[string]string{ "admin": Admin_JS, - "bzz": Bzz_JS, "chequebook": Chequebook_JS, "debug": Debug_JS, - "ens": ENS_JS, "eth": Eth_JS, "miner": Miner_JS, "net": Net_JS, @@ -32,101 +30,6 @@ var Modules = map[string]string{ "txpool": TxPool_JS, } -const Bzz_JS = ` -web3._extend({ - property: 'bzz', - methods: - [ - new web3._extend.Method({ - name: 'syncEnabled', - call: 'bzz_syncEnabled', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'swapEnabled', - call: 'bzz_swapEnabled', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'download', - call: 'bzz_download', - params: 2, - inputFormatter: [null, null] - }), - new web3._extend.Method({ - name: 'upload', - call: 'bzz_upload', - params: 2, - inputFormatter: [null, null] - }), - new web3._extend.Method({ - name: 'resolve', - call: 'bzz_resolve', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'get', - call: 'bzz_get', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'put', - call: 'bzz_put', - params: 2, - inputFormatter: [null, null] - }), - new web3._extend.Method({ - name: 'modify', - call: 'bzz_modify', - params: 4, - inputFormatter: [null, null, null, null] - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'hive', - getter: 'bzz_hive' - }), - new web3._extend.Property({ - name: 'info', - getter: 'bzz_info', - }), - ] -}); -` - -const ENS_JS = ` -web3._extend({ - property: 'ens', - methods: - [ - new web3._extend.Method({ - name: 'register', - call: 'ens_register', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'setContentHash', - call: 'ens_setContentHash', - params: 2, - inputFormatter: [null, null] - }), - new web3._extend.Method({ - name: 'resolve', - call: 'ens_resolve', - params: 1, - inputFormatter: [null] - }), - ] -}) -` - const Chequebook_JS = ` web3._extend({ property: 'chequebook', diff --git a/les/api_backend.go b/les/api_backend.go index 3a71ac4e0..ed2a7cd13 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) { +func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) { stateDb := state.(*light.LightState).Copy() addr := msg.From() from, err := stateDb.GetOrNewStateObject(ctx, addr) @@ -99,7 +99,7 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et vmstate := light.NewVMState(ctx, stateDb) context := core.NewEVMContext(msg, header, b.eth.blockchain) - return vm.NewEVM(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil + return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil } func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/les/handler.go b/les/handler.go index 603ce9ad4..42a45845d 100644 --- a/les/handler.go +++ b/les/handler.go @@ -160,9 +160,6 @@ func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, network if manager.serverPool != nil { addr := p.RemoteAddr().(*net.TCPAddr) entry = manager.serverPool.connect(peer, addr.IP, uint16(addr.Port)) - if entry == nil { - return fmt.Errorf("unwanted connection") - } } peer.poolEntry = entry select { diff --git a/les/helper_test.go b/les/helper_test.go index e0b7558ee..2c6f34a92 100644 --- a/les/helper_test.go +++ b/les/helper_test.go @@ -57,6 +57,8 @@ var ( testContractDeployed = uint64(2) testBufLimit = uint64(100) + + bigTxGas = new(big.Int).SetUint64(params.TxGas) ) /* @@ -80,15 +82,15 @@ func testChainGen(i int, block *core.BlockGen) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. // acc1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) nonce := block.TxNonce(acc1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) nonce++ tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(200000), big.NewInt(0), testContractCode), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) diff --git a/les/server.go b/les/server.go index e55616a44..c4c6fcab5 100644 --- a/les/server.go +++ b/les/server.go @@ -42,9 +42,7 @@ type LesServer struct { fcManager *flowcontrol.ClientManager // nil if our node is client only fcCostStats *requestCostStats defParams *flowcontrol.ServerParams - srvr *p2p.Server - synced, stopped bool - lock sync.Mutex + stopped bool } func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { @@ -70,35 +68,13 @@ func (s *LesServer) Protocols() []p2p.Protocol { return s.protocolManager.SubProtocols } -// Start only starts the actual service if the ETH protocol has already been synced, -// otherwise it will be started by Synced() +// Start starts the LES server func (s *LesServer) Start(srvr *p2p.Server) { - s.lock.Lock() - defer s.lock.Unlock() - - s.srvr = srvr - if s.synced { - s.protocolManager.Start(s.srvr) - } -} - -// Synced notifies the server that the ETH protocol has been synced and LES service can be started -func (s *LesServer) Synced() { - s.lock.Lock() - defer s.lock.Unlock() - - s.synced = true - if s.srvr != nil && !s.stopped { - s.protocolManager.Start(s.srvr) - } + s.protocolManager.Start(srvr) } // Stop stops the LES service func (s *LesServer) Stop() { - s.lock.Lock() - defer s.lock.Unlock() - - s.stopped = true s.fcCostStats.store() s.fcManager.Stop() go func() { diff --git a/les/serverpool.go b/les/serverpool.go index e3b7cf620..9735a718e 100644 --- a/les/serverpool.go +++ b/les/serverpool.go @@ -160,10 +160,10 @@ func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry { defer pool.lock.Unlock() entry := pool.entries[p.ID()] if entry == nil { - return nil + entry = pool.findOrNewNode(p.ID(), ip, port) } glog.V(logger.Debug).Infof("connecting to %v, state: %v", p.id, entry.state) - if entry.state != psDialed { + if entry.state == psConnected || entry.state == psRegistered { return nil } pool.connWg.Add(1) @@ -250,11 +250,17 @@ type poolStatAdjust struct { // adjustBlockDelay adjusts the block announce delay statistics of a node func (pool *serverPool) adjustBlockDelay(entry *poolEntry, time time.Duration) { + if entry == nil { + return + } pool.adjustStats <- poolStatAdjust{pseBlockDelay, entry, time} } // adjustResponseTime adjusts the request response time statistics of a node func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, timeout bool) { + if entry == nil { + return + } if timeout { pool.adjustStats <- poolStatAdjust{pseResponseTimeout, entry, time} } else { @@ -342,7 +348,9 @@ func (pool *serverPool) selectPeerWait(reqID uint64, canSend func(*peer) (bool, func (pool *serverPool) eventLoop() { lookupCnt := 0 var convTime mclock.AbsTime - pool.discSetPeriod <- time.Millisecond * 100 + if pool.discSetPeriod != nil { + pool.discSetPeriod <- time.Millisecond * 100 + } for { select { case entry := <-pool.timeout: @@ -375,39 +383,7 @@ func (pool *serverPool) eventLoop() { case node := <-pool.discNodes: pool.lock.Lock() - now := mclock.Now() - id := discover.NodeID(node.ID) - entry := pool.entries[id] - if entry == nil { - glog.V(logger.Debug).Infof("discovered %v", node.String()) - entry = &poolEntry{ - id: id, - addr: make(map[string]*poolEntryAddress), - addrSelect: *newWeightedRandomSelect(), - shortRetry: shortRetryCnt, - } - pool.entries[id] = entry - // initialize previously unknown peers with good statistics to give a chance to prove themselves - entry.connectStats.add(1, initStatsWeight) - entry.delayStats.add(0, initStatsWeight) - entry.responseStats.add(0, initStatsWeight) - entry.timeoutStats.add(0, initStatsWeight) - } - entry.lastDiscovered = now - addr := &poolEntryAddress{ - ip: node.IP, - port: node.TCP, - } - if a, ok := entry.addr[addr.strKey()]; ok { - addr = a - } else { - entry.addr[addr.strKey()] = addr - } - addr.lastSeen = now - entry.addrSelect.update(addr) - if !entry.known { - pool.newQueue.setLatest(entry) - } + entry := pool.findOrNewNode(discover.NodeID(node.ID), node.IP, node.TCP) pool.updateCheckDial(entry) pool.lock.Unlock() @@ -419,12 +395,16 @@ func (pool *serverPool) eventLoop() { lookupCnt++ if pool.fastDiscover && (lookupCnt == 50 || time.Duration(mclock.Now()-convTime) > time.Minute) { pool.fastDiscover = false - pool.discSetPeriod <- time.Minute + if pool.discSetPeriod != nil { + pool.discSetPeriod <- time.Minute + } } } case <-pool.quit: - close(pool.discSetPeriod) + if pool.discSetPeriod != nil { + close(pool.discSetPeriod) + } pool.connWg.Wait() pool.saveNodes() pool.wg.Done() @@ -434,6 +414,42 @@ func (pool *serverPool) eventLoop() { } } +func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16) *poolEntry { + now := mclock.Now() + entry := pool.entries[id] + if entry == nil { + glog.V(logger.Debug).Infof("discovered %v", id.String()) + entry = &poolEntry{ + id: id, + addr: make(map[string]*poolEntryAddress), + addrSelect: *newWeightedRandomSelect(), + shortRetry: shortRetryCnt, + } + pool.entries[id] = entry + // initialize previously unknown peers with good statistics to give a chance to prove themselves + entry.connectStats.add(1, initStatsWeight) + entry.delayStats.add(0, initStatsWeight) + entry.responseStats.add(0, initStatsWeight) + entry.timeoutStats.add(0, initStatsWeight) + } + entry.lastDiscovered = now + addr := &poolEntryAddress{ + ip: ip, + port: port, + } + if a, ok := entry.addr[addr.strKey()]; ok { + addr = a + } else { + entry.addr[addr.strKey()] = addr + } + addr.lastSeen = now + entry.addrSelect.update(addr) + if !entry.known { + pool.newQueue.setLatest(entry) + } + return entry +} + // loadNodes loads known nodes and their statistics from the database func (pool *serverPool) loadNodes() { enc, err := pool.db.Get(pool.dbKey) diff --git a/light/odr_test.go b/light/odr_test.go index a2f969acb..a76050a29 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -49,6 +49,8 @@ var ( testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") testContractAddr common.Address + + bigTxGas = new(big.Int).SetUint64(params.TxGas) ) type testOdr struct { @@ -205,15 +207,15 @@ func testChainGen(i int, block *core.BlockGen) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. // acc1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) nonce := block.TxNonce(acc1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) nonce++ tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(1000000), big.NewInt(0), testContractCode), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) diff --git a/light/txpool_test.go b/light/txpool_test.go index d8f04e617..af2dcbbef 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -77,7 +77,7 @@ func txPoolTestChainGen(i int, block *core.BlockGen) { func TestTxPool(t *testing.T) { for i := range testTx { - testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), types.HomesteadSigner{}, testBankKey) } var ( diff --git a/miner/miner.go b/miner/miner.go index 61cd3e049..83059f4b1 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -171,7 +171,7 @@ func (self *Miner) HashRate() (tot int64) { } func (self *Miner) SetExtra(extra []byte) error { - if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { + if uint64(len(extra)) > params.MaximumExtraDataSize { return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) } self.worker.setExtra(extra) diff --git a/params/gas_table.go b/params/gas_table.go index 093dacc8b..a06053904 100644 --- a/params/gas_table.go +++ b/params/gas_table.go @@ -16,41 +16,35 @@ package params -import "math/big" - type GasTable struct { - ExtcodeSize *big.Int - ExtcodeCopy *big.Int - Balance *big.Int - SLoad *big.Int - Calls *big.Int - Suicide *big.Int + ExtcodeSize uint64 + ExtcodeCopy uint64 + Balance uint64 + SLoad uint64 + Calls uint64 + Suicide uint64 - ExpByte *big.Int + ExpByte uint64 // CreateBySuicide occurs when the // refunded account is one that does // not exist. This logic is similar // to call. May be left nil. Nil means // not charged. - CreateBySuicide *big.Int + CreateBySuicide uint64 } var ( // GasTableHomestead contain the gas prices for // the homestead phase. GasTableHomestead = GasTable{ - ExtcodeSize: big.NewInt(20), - ExtcodeCopy: big.NewInt(20), - Balance: big.NewInt(20), - SLoad: big.NewInt(50), - Calls: big.NewInt(40), - Suicide: big.NewInt(0), - ExpByte: big.NewInt(10), - - // explicitly set to nil to indicate - // this rule does not apply to homestead. - CreateBySuicide: nil, + ExtcodeSize: 20, + ExtcodeCopy: 20, + Balance: 20, + SLoad: 50, + Calls: 40, + Suicide: 0, + ExpByte: 10, } // GasTableHomestead contain the gas re-prices for @@ -58,26 +52,26 @@ var ( // // TODO rename to GasTableEIP150 GasTableHomesteadGasRepriceFork = GasTable{ - ExtcodeSize: big.NewInt(700), - ExtcodeCopy: big.NewInt(700), - Balance: big.NewInt(400), - SLoad: big.NewInt(200), - Calls: big.NewInt(700), - Suicide: big.NewInt(5000), - ExpByte: big.NewInt(10), + ExtcodeSize: 700, + ExtcodeCopy: 700, + Balance: 400, + SLoad: 200, + Calls: 700, + Suicide: 5000, + ExpByte: 10, - CreateBySuicide: big.NewInt(25000), + CreateBySuicide: 25000, } GasTableEIP158 = GasTable{ - ExtcodeSize: big.NewInt(700), - ExtcodeCopy: big.NewInt(700), - Balance: big.NewInt(400), - SLoad: big.NewInt(200), - Calls: big.NewInt(700), - Suicide: big.NewInt(5000), - ExpByte: big.NewInt(50), + ExtcodeSize: 700, + ExtcodeCopy: 700, + Balance: 400, + SLoad: 200, + Calls: 700, + Suicide: 5000, + ExpByte: 50, - CreateBySuicide: big.NewInt(25000), + CreateBySuicide: 25000, } ) diff --git a/params/protocol_params.go b/params/protocol_params.go index f5b6bedeb..f48bf4992 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -18,56 +18,58 @@ package params import "math/big" -var ( - MaximumExtraDataSize = big.NewInt(32) // Maximum size extra data may be after Genesis. - ExpByteGas = big.NewInt(10) // Times ceil(log256(exponent)) for the EXP instruction. - SloadGas = big.NewInt(50) // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. - CallValueTransferGas = big.NewInt(9000) // Paid for CALL when the value transfer is non-zero. - CallNewAccountGas = big.NewInt(25000) // Paid for CALL when the destination address didn't exist prior. - TxGas = big.NewInt(21000) // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. - TxGasContractCreation = big.NewInt(53000) // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. - TxDataZeroGas = big.NewInt(4) // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. - DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. - QuadCoeffDiv = big.NewInt(512) // Divisor for the quadratic particle of the memory cost equation. - GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. - DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. - SstoreSetGas = big.NewInt(20000) // Once per SLOAD operation. - LogDataGas = big.NewInt(8) // Per byte in a LOG* operation's data. - CallStipend = big.NewInt(2300) // Free gas given at beginning of call. - EcrecoverGas = big.NewInt(3000) // - Sha256WordGas = big.NewInt(12) // - - MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. - GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. - TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target +const ( + MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. + ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. + SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. + CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. + CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. + TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. + TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. + TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. + QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. + SstoreSetGas uint64 = 20000 // Once per SLOAD operation. + LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. + CallStipend uint64 = 2300 // Free gas given at beginning of call. + EcrecoverGas uint64 = 3000 // + Sha256WordGas uint64 = 12 // - Sha3Gas = big.NewInt(30) // Once per SHA3 operation. - Sha256Gas = big.NewInt(60) // - IdentityWordGas = big.NewInt(3) // - Sha3WordGas = big.NewInt(6) // Once per word of the SHA3 operation's data. - SstoreResetGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness changes from zero. - SstoreClearGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas = big.NewInt(15000) // Once per SSTORE operation if the zeroness changes to zero. - JumpdestGas = big.NewInt(1) // Refunded gas, once per SSTORE operation if the zeroness changes to zero. - IdentityGas = big.NewInt(15) // - GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations. - EpochDuration = big.NewInt(30000) // Duration between proof-of-work epochs. - CallGas = big.NewInt(40) // Once per CALL operation & message call transaction. - CreateDataGas = big.NewInt(200) // - Ripemd160Gas = big.NewInt(600) // - Ripemd160WordGas = big.NewInt(120) // - MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. - CallCreateDepth = big.NewInt(1024) // Maximum depth of call/create stack. - ExpGas = big.NewInt(10) // Once per EXP instruction. - LogGas = big.NewInt(375) // Per LOG* operation. - CopyGas = big.NewInt(3) // - StackLimit = big.NewInt(1024) // Maximum size of VM stack allowed. - TierStepGas = big.NewInt(0) // Once per operation, for a selection of them. - LogTopicGas = big.NewInt(375) // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas = big.NewInt(32000) // Once per CREATE operation & contract-creation transaction. - SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation. - MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + Sha3Gas uint64 = 30 // Once per SHA3 operation. + Sha256Gas uint64 = 60 // + IdentityWordGas uint64 = 3 // + Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. + SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. + SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. + JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. + IdentityGas uint64 = 15 // + EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. + CallGas uint64 = 40 // Once per CALL operation & message call transaction. + CreateDataGas uint64 = 200 // + Ripemd160Gas uint64 = 600 // + Ripemd160WordGas uint64 = 120 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + ExpGas uint64 = 10 // Once per EXP instruction + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + TierStepGas uint64 = 0 // Once per operation, for a selection of them. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. MaxCodeSize = 24576 ) + +var ( + GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations. + MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. + GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. + TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target + DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. + GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. + MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. + DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. +) diff --git a/params/version.go b/params/version.go index b60e105d5..72ec2d239 100644 --- a/params/version.go +++ b/params/version.go @@ -21,7 +21,7 @@ import "fmt" const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 5 // Minor version component of the current release - VersionPatch = 8 // Patch version component of the current release + VersionPatch = 9 // Patch version component of the current release VersionMeta = "unstable" // Version metadata to append to the version string ) diff --git a/swarm/api/api.go b/swarm/api/api.go index 3f48437a5..f92a14653 100644 --- a/swarm/api/api.go +++ b/swarm/api/api.go @@ -19,6 +19,7 @@ package api import ( "fmt" "io" + "net/http" "regexp" "strings" "sync" @@ -71,6 +72,7 @@ type ErrResolve error // DNS Resolver func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { + glog.V(logger.Detail).Infof("Resolving : %v", hostPort) if hashMatcher.MatchString(hostPort) || self.dns == nil { glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort) return storage.Key(common.Hex2Bytes(hostPort)), nil @@ -86,8 +88,10 @@ func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error glog.V(logger.Detail).Infof("host lookup: %v -> %v", err) return contentHash[:], err } - -func parse(uri string) (hostPort, path string) { +func Parse(uri string) (hostPort, path string) { + if uri == "" { + return + } parts := slashes.Split(uri, 3) var i int if len(parts) == 0 { @@ -111,7 +115,7 @@ func parse(uri string) (hostPort, path string) { } func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { - hostPort, path = parse(uri) + hostPort, path = Parse(uri) //resolving host and port contentHash, err := self.Resolve(hostPort, nameresolver) glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path) @@ -153,7 +157,9 @@ func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionR } glog.V(logger.Detail).Infof("getEntry(%s)", path) + entry, _ := trie.getEntry(path) + if entry != nil { key = common.Hex2Bytes(entry.Hash) status = entry.Status @@ -161,6 +167,7 @@ func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionR glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType) reader = self.dpa.Retrieve(key) } else { + status = http.StatusNotFound err = fmt.Errorf("manifest entry for '%s' not found", path) glog.V(logger.Warn).Infof("%v", err) } diff --git a/swarm/api/config.go b/swarm/api/config.go index b4c6e3d4a..23a855500 100644 --- a/swarm/api/config.go +++ b/swarm/api/config.go @@ -85,10 +85,17 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n NetworkId: networkId, } data, err = ioutil.ReadFile(confpath) + + // if not set in function param, then set default for swarm network, will be overwritten by config file if present + if networkId == 0 { + self.NetworkId = network.NetworkId + } + if err != nil { if !os.IsNotExist(err) { return } + // file does not exist // write out config file err = self.Save() @@ -97,6 +104,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n } return } + // file exists, deserialise err = json.Unmarshal(data, self) if err != nil { @@ -109,6 +117,12 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n if keyhex != self.BzzKey { return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey) } + + // if set in function param, replace id set from config file + if networkId != 0 { + self.NetworkId = networkId + } + self.Swap.SetKey(prvKey) if (self.EnsRoot == common.Address{}) { diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go index c8e79ab4e..afd867efc 100644 --- a/swarm/api/http/server.go +++ b/swarm/api/http/server.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/storage" "github.com/rs/cors" ) @@ -194,17 +195,34 @@ func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { } case r.Method == "GET" || r.Method == "HEAD": path = trailingSlashes.ReplaceAllString(path, "") + if path == "" { + http.Error(w, "Empty path not allowed", http.StatusBadRequest) + return + } if raw { - // resolving host - key, err := a.Resolve(path, nameresolver) - if err != nil { - glog.V(logger.Error).Infof("%v", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return + var reader storage.LazySectionReader + parsedurl, _ := api.Parse(path) + + if parsedurl == path { + key, err := a.Resolve(parsedurl, nameresolver) + if err != nil { + glog.V(logger.Error).Infof("%v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + reader = a.Retrieve(key) + } else { + var status int + readertmp, _, status, err := a.Get(path, nameresolver) + if err != nil { + http.Error(w, err.Error(), status) + return + } + reader = readertmp } // retrieving content - reader := a.Retrieve(key) + quitC := make(chan bool) size, err := reader.Size(quitC) if err != nil { diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go new file mode 100644 index 000000000..078589097 --- /dev/null +++ b/swarm/api/http/server_test.go @@ -0,0 +1,133 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package http + +import ( + "bytes" + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func TestBzzrGetPath(t *testing.T) { + + var err error + + maxproxyattempts := 3 + + testmanifest := []string{ + `{"entries":[{"path":"a/","hash":"674af7073604ebfc0282a4ab21e5ef1a3c22913866879ebc0816f8a89896b2ed","contentType":"application/bzz-manifest+json","status":0}]}`, + `{"entries":[{"path":"a","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"b/","hash":"0a87b1c3e4bf013686cdf107ec58590f2004610ee58cc2240f26939f691215f5","contentType":"application/bzz-manifest+json","status":0}]}`, + `{"entries":[{"path":"b","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"c","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0}]}`, + } + + testrequests := make(map[string]int) + testrequests["/"] = 0 + testrequests["/a"] = 1 + testrequests["/a/b"] = 2 + testrequests["/x"] = 0 + testrequests[""] = 0 + + expectedfailrequests := []string{"", "/x"} + + reader := [3]*bytes.Reader{} + + key := [3]storage.Key{} + + dir, _ := ioutil.TempDir("", "bzz-storage-test") + + storeparams := &storage.StoreParams{ + ChunkDbPath: dir, + DbCapacity: 5000000, + CacheCapacity: 5000, + Radius: 0, + } + + localStore, err := storage.NewLocalStore(storage.MakeHashFunc("SHA3"), storeparams) + if err != nil { + t.Fatal(err) + } + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + dpa := &storage.DPA{ + Chunker: chunker, + ChunkStore: localStore, + } + dpa.Start() + defer dpa.Stop() + + wg := &sync.WaitGroup{} + + for i, mf := range testmanifest { + reader[i] = bytes.NewReader([]byte(mf)) + key[i], err = dpa.Store(reader[i], int64(len(mf)), wg, nil) + if err != nil { + t.Fatal(err) + } + wg.Wait() + } + + a := api.NewApi(dpa, nil) + + /// \todo iterate port numbers up if fail + StartHttpServer(a, &Server{Addr: "127.0.0.1:8504", CorsString: ""}) + // how to wait for ListenAndServe to have initialized? This is pretty cruuuude + // if we fix it we don't need maxproxyattempts anymore either + time.Sleep(1000 * time.Millisecond) + for i := 0; i <= maxproxyattempts; i++ { + _, err := http.Get("http://127.0.0.1:8504/bzzr:/" + common.ToHex(key[0])[2:] + "/a") + if i == maxproxyattempts { + t.Fatalf("Failed to connect to proxy after %v attempts: %v", i, err) + } else if err != nil { + time.Sleep(100 * time.Millisecond) + continue + } + break + } + + for k, v := range testrequests { + var resp *http.Response + var respbody []byte + + url := "http://127.0.0.1:8504/bzzr:/" + if k[:] != "" { + url += common.ToHex(key[0])[2:] + "/" + k[1:] + "?content_type=text/plain" + } + resp, err = http.Get(url) + defer resp.Body.Close() + respbody, err = ioutil.ReadAll(resp.Body) + + if string(respbody) != testmanifest[v] { + isexpectedfailrequest := false + + for _, r := range expectedfailrequests { + if k[:] == r { + isexpectedfailrequest = true + } + } + if isexpectedfailrequest == false { + t.Fatalf("Response body does not match, expected: %v, got %v", testmanifest[v], string(respbody)) + } + } + } + +} diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go index d6dc24c48..36c0b0436 100644 --- a/swarm/api/manifest.go +++ b/swarm/api/manifest.go @@ -302,7 +302,8 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man if (len(path) >= epl) && (path[:epl] == entry.Path) { glog.V(logger.Detail).Infof("entry.ContentType = %v", entry.ContentType) if entry.ContentType == manifestType { - if self.loadSubTrie(entry, quitC) != nil { + err := self.loadSubTrie(entry, quitC) + if err != nil { return nil, 0 } entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC) @@ -312,8 +313,6 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man } else { pos = epl } - } else { - entry = nil } return } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 470eb7cb7..01539de03 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -146,7 +146,7 @@ func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[s } for name, test := range bt { - if skipTest[name] { + if skipTest[name] /*|| name != "CallingCanonicalContractFromFork_CALLCODE"*/ { glog.Infoln("Skipping block test", name) continue } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 5469a4c71..e8ab29d14 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -108,7 +108,7 @@ func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, ski } for name, test := range tests { - if skipTest[name] { + if skipTest[name] /*|| name != "JUMPDEST_Attack"*/ { glog.Infoln("Skipping state test", name) continue } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 25e55886f..1edf0e425 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -129,7 +129,7 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error { } for name, test := range tests { - if skipTest[name] /*|| name != "loop_stacklimit_1021"*/ { + if skipTest[name] /*|| name != "exp0"*/ { glog.Infoln("Skipping VM test", name) continue } @@ -229,6 +229,6 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec) - ret, err := environment.Call(caller, to, data, gas, value) - return ret, statedb.Logs(), gas, err + ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value) + return ret, statedb.Logs(), new(big.Int).SetUint64(g), err } diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go new file mode 100644 index 000000000..f7d6c3e5c --- /dev/null +++ b/whisper/mailserver/mailserver.go @@ -0,0 +1,170 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package mailserver + +import ( + "bytes" + "encoding/binary" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" +) + +const MailServerKeyName = "958e04ab302fb36ad2616a352cbac79d" + +type WMailServer struct { + db *leveldb.DB + w *whisper.Whisper + pow float64 + key []byte +} + +type DBKey struct { + timestamp uint32 + hash common.Hash + raw []byte +} + +func NewDbKey(t uint32, h common.Hash) *DBKey { + const sz = common.HashLength + 4 + var k DBKey + k.timestamp = t + k.hash = h + k.raw = make([]byte, sz) + binary.BigEndian.PutUint32(k.raw, k.timestamp) + copy(k.raw[4:], k.hash[:]) + return &k +} + +func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) { + var err error + if len(path) == 0 { + utils.Fatalf("DB file is not specified") + } + + if len(password) == 0 { + utils.Fatalf("Password is not specified for MailServer") + } + + s.db, err = leveldb.OpenFile(path, nil) + if err != nil { + utils.Fatalf("Failed to open DB file: %s", err) + } + + s.w = shh + s.pow = pow + + err = s.w.AddSymKey(MailServerKeyName, []byte(password)) + if err != nil { + utils.Fatalf("Failed to create symmetric key for MailServer: %s", err) + } + s.key = s.w.GetSymKey(MailServerKeyName) +} + +func (s *WMailServer) Close() { + if s.db != nil { + s.db.Close() + } +} + +func (s *WMailServer) Archive(env *whisper.Envelope) { + key := NewDbKey(env.Expiry-env.TTL, env.Hash()) + rawEnvelope, err := rlp.EncodeToBytes(env) + if err != nil { + glog.V(logger.Error).Infof("rlp.EncodeToBytes failed: %s", err) + } else { + err = s.db.Put(key.raw, rawEnvelope, nil) + if err != nil { + glog.V(logger.Error).Infof("Writing to DB failed: %s", err) + } + } +} + +func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) { + ok, lower, upper, topic := s.validate(peer, request) + if !ok { + return + } + + var err error + var zero common.Hash + var empty whisper.TopicType + kl := NewDbKey(lower, zero) + ku := NewDbKey(upper, zero) + i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) + defer i.Release() + + for i.Next() { + var envelope whisper.Envelope + err = rlp.DecodeBytes(i.Value(), &envelope) + if err != nil { + glog.V(logger.Error).Infof("RLP decoding failed: %s", err) + } + + if topic == empty || envelope.Topic == topic { + err = s.w.SendP2PDirect(peer, &envelope) + if err != nil { + glog.V(logger.Error).Infof("Failed to send direct message to peer: %s", err) + return + } + } + } + + err = i.Error() + if err != nil { + glog.V(logger.Error).Infof("Level DB iterator error: %s", err) + } +} + +func (s *WMailServer) validate(peer *whisper.Peer, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) { + var topic whisper.TopicType + if s.pow > 0.0 && request.PoW() < s.pow { + return false, 0, 0, topic + } + + f := whisper.Filter{KeySym: s.key} + decrypted := request.Open(&f) + if decrypted == nil { + glog.V(logger.Warn).Infof("Failed to decrypt p2p request") + return false, 0, 0, topic + } + + if len(decrypted.Payload) < 8 { + glog.V(logger.Warn).Infof("Undersized p2p request") + return false, 0, 0, topic + } + + if bytes.Equal(peer.ID(), decrypted.Signature) { + glog.V(logger.Warn).Infof("Wrong signature of p2p request") + return false, 0, 0, topic + } + + lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) + upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) + + if len(decrypted.Payload) >= 8+whisper.TopicLength { + topic = whisper.BytesToTopic(decrypted.Payload[8:]) + } + + return true, lower, upper, topic +} diff --git a/whisper/shhapi/api.go b/whisper/shhapi/api.go index 379bb90d3..b273053ec 100644 --- a/whisper/shhapi/api.go +++ b/whisper/shhapi/api.go @@ -93,12 +93,12 @@ func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { // data contains parameters (time frame, payment details, etc.), required // by the remote email-like server. Whisper is not aware about the data format, // it will just forward the raw data to the server. -func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { - if api.whisper == nil { - return whisperOffLineErr - } - return api.whisper.RequestHistoricMessages(peerID, data) -} +//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { +// if api.whisper == nil { +// return whisperOffLineErr +// } +// return api.whisper.RequestHistoricMessages(peerID, data) +//} // HasIdentity checks if the whisper node is configured with the private key // of the specified public pair. diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go index 8ec81b180..b82a82468 100644 --- a/whisper/whisperv5/doc.go +++ b/whisper/whisperv5/doc.go @@ -83,5 +83,5 @@ func (e unknownVersionError) Error() string { // in order to bypass the expiry checks. type MailServer interface { Archive(env *Envelope) - DeliverMail(whisperPeer *Peer, data []byte) + DeliverMail(whisperPeer *Peer, request *Envelope) } diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go index 7c90f59c0..c6f1ca2ca 100644 --- a/whisper/whisperv5/message_test.go +++ b/whisper/whisperv5/message_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" ) func copyFromBuf(dst []byte, src []byte, beg int) int { @@ -311,3 +312,35 @@ func TestEncryptWithZeroKey(t *testing.T) { t.Fatalf("wrapped with nil key, seed: %d.", seed) } } + +func TestRlpEncode(t *testing.T) { + InitSingleTest() + + params, err := generateMessageParams() + if err != nil { + t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) + } + msg := NewSentMessage(params) + env, err := msg.Wrap(params) + if err != nil { + t.Fatalf("wrapped with zero key, seed: %d.", seed) + } + + raw, err := rlp.EncodeToBytes(env) + if err != nil { + t.Fatalf("RLP encode failed: %s.", err) + } + + var decoded Envelope + rlp.DecodeBytes(raw, &decoded) + if err != nil { + t.Fatalf("RLP decode failed: %s.", err) + } + + he := env.Hash() + hd := decoded.Hash() + + if he != hd { + t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) + } +} diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go index 634045504..42394a0a3 100644 --- a/whisper/whisperv5/peer.go +++ b/whisper/whisperv5/peer.go @@ -175,3 +175,8 @@ func (p *Peer) broadcast() error { glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)") return nil } + +func (p *Peer) ID() []byte { + id := p.peer.ID() + return id[:] +} diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index b514c022e..ff31aab2d 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/pbkdf2" set "gopkg.in/fatih/set.v0" ) @@ -125,13 +124,13 @@ func (w *Whisper) MarkPeerTrusted(peerID []byte) error { return nil } -func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error { +func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { p, err := w.getPeer(peerID) if err != nil { return err } p.trusted = true - return p2p.Send(p.ws, p2pRequestCode, data) + return p2p.Send(p.ws, p2pRequestCode, envelope) } func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { @@ -142,6 +141,10 @@ func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { return p2p.Send(p.ws, p2pCode, envelope) } +func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { + return p2p.Send(peer.ws, p2pCode, envelope) +} + // NewIdentity generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { @@ -347,9 +350,6 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { return fmt.Errorf("invalid envelope") } p.mark(envelope) - if wh.mailServer != nil { - wh.mailServer.Archive(envelope) - } } case p2pCode: // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. @@ -357,25 +357,22 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { // therefore might not satisfy the PoW, expiry and other requirements. // these messages are only accepted from the trusted peer. if p.trusted { - var envelopes []*Envelope - if err := packet.Decode(&envelopes); err != nil { + var envelope Envelope + if err := packet.Decode(&envelope); err != nil { glog.V(logger.Warn).Infof("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err) return fmt.Errorf("garbage received (directMessage)") } - for _, envelope := range envelopes { - wh.postEvent(envelope, true) - } + wh.postEvent(&envelope, true) } case p2pRequestCode: // Must be processed if mail server is implemented. Otherwise ignore. if wh.mailServer != nil { - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - data, err := s.Bytes() - if err == nil { - wh.mailServer.DeliverMail(p, data) - } else { - glog.V(logger.Error).Infof("%v: bad requestHistoricMessages received: [%v]", p.peer, err) + var request Envelope + if err := packet.Decode(&request); err != nil { + glog.V(logger.Warn).Infof("%v: failed to decode p2p request message: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("garbage received (p2p request)") } + wh.mailServer.DeliverMail(p, &request) } default: // New message types might be implemented in the future versions of Whisper. @@ -454,6 +451,9 @@ func (wh *Whisper) add(envelope *Envelope) error { } else { glog.V(logger.Detail).Infof("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope) wh.postEvent(envelope, false) // notify the local node about the new message + if wh.mailServer != nil { + wh.mailServer.Archive(envelope) + } } return nil } diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go index b9cd9b1a0..a3ded7b37 100644 --- a/whisper/whisperv5/whisper_test.go +++ b/whisper/whisperv5/whisper_test.go @@ -57,12 +57,6 @@ func TestWhisperBasic(t *testing.T) { if err := w.MarkPeerTrusted(peerID); err == nil { t.Fatalf("failed MarkPeerTrusted.") } - if err := w.RequestHistoricMessages(peerID, peerID); err == nil { - t.Fatalf("failed RequestHistoricMessages.") - } - if err := w.SendP2PMessage(peerID, nil); err == nil { - t.Fatalf("failed SendP2PMessage.") - } exist := w.HasSymKey("non-existing") if exist { t.Fatalf("failed HasSymKey.") |