aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--VERSION2
-rw-r--r--build/ci.go2
-rw-r--r--cmd/evm/main.go4
-rw-r--r--cmd/geth/chaincmd.go1
-rw-r--r--cmd/geth/main.go2
-rw-r--r--cmd/swarm/hash.go1
-rw-r--r--cmd/swarm/main.go5
-rw-r--r--cmd/utils/flags.go8
-rw-r--r--cmd/wnode/main.go537
-rw-r--r--common/math/integer.go25
-rw-r--r--common/math/integer_test.go50
-rw-r--r--console/console.go4
-rw-r--r--core/bench_test.go7
-rw-r--r--core/block_validator.go2
-rw-r--r--core/blockchain.go2
-rw-r--r--core/blockchain_test.go16
-rw-r--r--core/chain_makers_test.go6
-rw-r--r--core/state/managed_state.go8
-rw-r--r--core/state_transition.go88
-rw-r--r--core/types/transaction_test.go44
-rw-r--r--core/vm/common.go78
-rw-r--r--core/vm/contract.go33
-rw-r--r--core/vm/contracts.go39
-rw-r--r--core/vm/evm.go (renamed from core/vm/environment.go)83
-rw-r--r--core/vm/gas.go153
-rw-r--r--core/vm/gas_table.go418
-rw-r--r--core/vm/gas_table_test.go24
-rw-r--r--core/vm/instructions.go338
-rw-r--r--core/vm/int_pool_verifier.go15
-rw-r--r--core/vm/int_pool_verifier_empty.go7
-rw-r--r--core/vm/interpreter.go (renamed from core/vm/vm.go)42
-rw-r--r--core/vm/intpool.go (renamed from core/vm/virtual_machine.go)35
-rw-r--r--core/vm/jump_table.go7
-rw-r--r--core/vm/logger_test.go4
-rw-r--r--core/vm/memory.go5
-rw-r--r--core/vm/runtime/env.go2
-rw-r--r--core/vm/runtime/runtime.go14
-rw-r--r--core/vm/runtime/runtime_test.go4
-rw-r--r--core/vm/stack_table.go4
-rw-r--r--eth/api_backend.go4
-rw-r--r--eth/backend.go1
-rw-r--r--eth/bind.go4
-rw-r--r--eth/downloader/downloader.go12
-rw-r--r--eth/downloader/downloader_test.go2
-rw-r--r--eth/fetcher/fetcher_test.go2
-rw-r--r--eth/handler.go2
-rw-r--r--eth/handler_test.go14
-rw-r--r--eth/sync.go9
-rw-r--r--internal/ethapi/api.go74
-rw-r--r--internal/ethapi/backend.go2
-rw-r--r--internal/ethapi/tracer_test.go4
-rw-r--r--internal/web3ext/web3ext.go97
-rw-r--r--les/api_backend.go4
-rw-r--r--les/handler.go3
-rw-r--r--les/helper_test.go8
-rw-r--r--les/server.go30
-rw-r--r--les/serverpool.go92
-rw-r--r--light/odr_test.go8
-rw-r--r--light/txpool_test.go2
-rw-r--r--miner/miner.go2
-rw-r--r--params/gas_table.go68
-rw-r--r--params/protocol_params.go100
-rw-r--r--params/version.go2
-rw-r--r--swarm/api/api.go13
-rw-r--r--swarm/api/config.go14
-rw-r--r--swarm/api/http/server.go32
-rw-r--r--swarm/api/http/server_test.go133
-rw-r--r--swarm/api/manifest.go5
-rw-r--r--tests/block_test_util.go2
-rw-r--r--tests/state_test_util.go2
-rw-r--r--tests/vm_test_util.go6
-rw-r--r--whisper/mailserver/mailserver.go170
-rw-r--r--whisper/shhapi/api.go12
-rw-r--r--whisper/whisperv5/doc.go2
-rw-r--r--whisper/whisperv5/message_test.go33
-rw-r--r--whisper/whisperv5/peer.go5
-rw-r--r--whisper/whisperv5/whisper.go34
-rw-r--r--whisper/whisperv5/whisper_test.go6
78 files changed, 2121 insertions, 1013 deletions
diff --git a/VERSION b/VERSION
index 1cc9c180e..2b26b8d21 100644
--- a/VERSION
+++ b/VERSION
@@ -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(&params)
+ envelope, err := msg.Wrap(&params)
+ 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(&params)
+ env, err := msg.Wrap(&params)
+ 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.")