diff --git a/accounts/abi/bind/ext.go b/accounts/abi/bind/ext.go
new file mode 100644
index 0000000000..3b9ffb3edd
--- /dev/null
+++ b/accounts/abi/bind/ext.go
@@ -0,0 +1,109 @@
+package bind
+
+import (
+ "fmt"
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+)
+
+type DeployOptions struct {
+ ReceiptQueryInterval time.Duration
+ DeployRetryInterval time.Duration
+ ConfirmationInterval time.Duration
+ MaxReceiptQueryAttempts int
+ MaxDeployAttempts int
+}
+
+func DefaultDeployOptions() *DeployOptions {
+ return &DeployOptions{
+ ReceiptQueryInterval: 10000000000, // 10 sec
+ DeployRetryInterval: 5000000000, // 5 sec
+ ConfirmationInterval: 60000000000, // 60 sec
+ MaxReceiptQueryAttempts: 5,
+ MaxDeployAttempts: 100,
+ }
+}
+
+// implemented by eth.APIBackend
+type Backend interface {
+ GetTxReceipt(txhash common.Hash) *types.Receipt
+ CodeAt(address common.Address) string
+ BalanceAt(address common.Address) *big.Int
+ ContractBackend
+}
+
+func Deploy(deployF func(*TransactOpts, ContractBackend) (*types.Transaction, error), contractCode string, deployTransactor *TransactOpts, opt *DeployOptions, backend Backend) (contractAddr common.Address, err error) {
+
+ deployRetryTimer := time.NewTimer(0).C
+ var receiptQueryTimer <-chan time.Time
+ var deployRetries, receiptQueries int
+ var txhash common.Hash
+
+DEPLOY:
+ for {
+ select {
+ case <-deployRetryTimer:
+ deployRetries++
+ if deployRetries == opt.MaxDeployAttempts {
+ return common.Address{}, fmt.Errorf("deployment failed...giving up after %v attempts", opt.MaxDeployAttempts)
+ }
+ tx, err := deployF(deployTransactor, backend)
+ if err != nil {
+ glog.V(logger.Warn).Infof("deployment failed: %v (attempt %v)", err, deployRetries)
+ deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
+ continue DEPLOY
+ }
+
+ txhash = tx.Hash()
+ deployRetryTimer = nil
+ receiptQueryTimer = time.NewTimer(0).C
+
+ case <-receiptQueryTimer:
+ receipt := backend.GetTxReceipt(txhash)
+ receiptQueries++
+ if receipt == nil {
+ if receiptQueries == opt.MaxReceiptQueryAttempts {
+ glog.V(logger.Warn).Infof("attempt %s deployment failed. Given up after %v attempts", opt.MaxReceiptQueryAttempts)
+
+ deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
+ receiptQueryTimer = nil
+ continue DEPLOY
+
+ }
+ glog.V(logger.Detail).Infof("new checkbook contract (txhash: %v) not yet mined... checking in %v", txhash.Hex(), opt.ReceiptQueryInterval)
+ receiptQueryTimer = time.NewTimer(opt.ReceiptQueryInterval).C
+ continue DEPLOY
+ }
+
+ contractAddr = receipt.ContractAddress
+ glog.V(logger.Detail).Infof("new chequebook contract mined at %v (owner: %v)", contractAddr.Hex(), deployTransactor.From.Hex())
+ <-time.NewTimer(opt.ConfirmationInterval).C
+ err = Validate(contractAddr, contractCode, backend)
+ if err != nil {
+ glog.V(logger.Warn).Infof("invalid contract at %v after %v: %v", contractAddr.Hex(), opt.ConfirmationInterval, err)
+ deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
+ receiptQueryTimer = nil
+ continue DEPLOY
+ }
+ break DEPLOY
+
+ } // select
+ } // for
+ return contractAddr, nil
+}
+
+func Validate(contractAddr common.Address, expCode string, backend Backend) (err error) {
+ if (contractAddr == common.Address{}) {
+ return fmt.Errorf("zero address")
+ }
+ code := backend.CodeAt(contractAddr)
+ if len(expCode) > 0 && code != expCode {
+ return fmt.Errorf("incorrect code %v:\n%v\n%v", contractAddr.Hex(), code, expCode)
+ }
+ return nil
+}
diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go
index 7fea16a25e..67225c701d 100644
--- a/cmd/geth/accountcmd.go
+++ b/cmd/geth/accountcmd.go
@@ -63,7 +63,7 @@ import a private key into a new account.
It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
Non-interactive mode is only meant for scripted use on test networks or known
-safe environments.
+safe environ>>>ments.
Make sure you remember the password you gave when creating a new account (with
either new or import). Without it you are not able to unlock your account.
@@ -119,7 +119,7 @@ password to file or expose in any other way.
Description: `
ethereum account update
-
+>>>>>>>>>>>>
Update an existing account.
The account is saved in the newest version in encrypted format, you are prompted
@@ -175,7 +175,7 @@ func accountList(ctx *cli.Context) error {
return nil
}
-// tries unlocking the specified account a few times.
+// tries unlocking the specifiedqqa few times.
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
account, err := utils.MakeAddress(accman, address)
if err != nil {
@@ -203,7 +203,7 @@ func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i
return accounts.Account{}, ""
}
-// getPassPhrase retrieves the passwor associated with an account, either fetched
+// getPassPhrase retrieves the password associated with an account, either fetched
// from a list of preloaded passphrases, or requested interactively from the user.
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
// If a list of passwords was supplied, retrieve from them
@@ -262,7 +262,7 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro
// accountCreate creates a new account into the keystore defined by the CLI flags.
func accountCreate(ctx *cli.Context) error {
accman := utils.MakeAccountManager(ctx)
- password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+ password := utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
account, err := accman.NewAccount(password)
if err != nil {
@@ -280,8 +280,8 @@ func accountUpdate(ctx *cli.Context) error {
}
accman := utils.MakeAccountManager(ctx)
- account, oldPassword := unlockAccount(ctx, accman, ctx.Args().First(), 0, nil)
- newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
+ account, oldPassword := utils.UnlockAccount(accman, ctx.Args().First(), 0, nil)
+ newPassword := utils.GetPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
if err := accman.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
@@ -299,7 +299,7 @@ func importWallet(ctx *cli.Context) error {
}
accman := utils.MakeAccountManager(ctx)
- passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
+ passphrase := utils.GetPassPhrase("", false, 0, utils.MakePasswordList(ctx))
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
if err != nil {
@@ -319,7 +319,7 @@ func accountImport(ctx *cli.Context) error {
utils.Fatalf("Failed to load the private key: %v", err)
}
accman := utils.MakeAccountManager(ctx)
- passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+ passphrase := utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
acct, err := accman.ImportECDSA(key, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
diff --git a/cmd/geth/js.go b/cmd/geth/js.go
new file mode 100644
index 0000000000..178fc76c94
--- /dev/null
+++ b/cmd/geth/js.go
@@ -0,0 +1,424 @@
+// Copyright 2015 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 .
+
+package main
+
+import (
+ "fmt"
+ "math/big"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/internal/web3ext"
+ re "github.com/ethereum/go-ethereum/jsre"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/peterh/liner"
+ "github.com/robertkrimen/otto"
+)
+
+var (
+ passwordRegexp = regexp.MustCompile("personal.[nu]")
+ leadingSpace = regexp.MustCompile("^ ")
+ onlyws = regexp.MustCompile("^\\s*$")
+ exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
+)
+
+type jsre struct {
+ re *re.JSRE
+ stack *node.Node
+ wait chan *big.Int
+ ps1 string
+ atexit func()
+ corsDomain string
+ client rpc.Client
+}
+
+func makeCompleter(re *jsre) liner.WordCompleter {
+ return func(line string, pos int) (head string, completions []string, tail string) {
+ if len(line) == 0 || pos == 0 {
+ return "", nil, ""
+ }
+ // chuck data to relevant part for autocompletion, e.g. in case of nested lines eth.getBalance(eth.coinb
+ i := 0
+ for i = pos - 1; i > 0; i-- {
+ if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
+ continue
+ }
+ if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
+ continue
+ }
+ i += 1
+ break
+ }
+ return line[:i], re.re.CompleteKeywords(line[i:pos]), line[pos:]
+ }
+}
+
+func newLightweightJSRE(docRoot string, client rpc.Client, datadir string, interactive bool) *jsre {
+ js := &jsre{ps1: "> "}
+ js.wait = make(chan *big.Int)
+ js.client = client
+ js.re = re.New(docRoot)
+ if err := js.apiBindings(); err != nil {
+ utils.Fatalf("Unable to initialize console - %v", err)
+ }
+ js.setupInput(datadir)
+ return js
+}
+
+func newJSRE(stack *node.Node, docRoot, corsDomain string, client rpc.Client, interactive bool) *jsre {
+ js := &jsre{stack: stack, ps1: "> "}
+ // set default cors domain used by startRpc from CLI flag
+ js.corsDomain = corsDomain
+ js.wait = make(chan *big.Int)
+ js.client = client
+ js.re = re.New(docRoot)
+ if err := js.apiBindings(); err != nil {
+ utils.Fatalf("Unable to connect - %v", err)
+ }
+ js.setupInput(stack.DataDir())
+ return js
+}
+
+func (self *jsre) setupInput(datadir string) {
+ self.withHistory(datadir, func(hist *os.File) { utils.Stdin.ReadHistory(hist) })
+ utils.Stdin.SetCtrlCAborts(true)
+ utils.Stdin.SetWordCompleter(makeCompleter(self))
+ utils.Stdin.SetTabCompletionStyle(liner.TabPrints)
+ self.atexit = func() {
+ self.withHistory(datadir, func(hist *os.File) {
+ hist.Truncate(0)
+ utils.Stdin.WriteHistory(hist)
+ })
+ utils.Stdin.Close()
+ close(self.wait)
+ }
+}
+
+func (self *jsre) batch(statement string) {
+ err := self.re.EvalAndPrettyPrint(statement)
+
+ if err != nil {
+ fmt.Printf("%v", jsErrorString(err))
+ }
+
+ if self.atexit != nil {
+ self.atexit()
+ }
+
+ self.re.Stop(false)
+}
+
+// show summary of current geth instance
+func (self *jsre) welcome() {
+ self.re.Run(`
+ (function () {
+ console.log('instance: ' + web3.version.node);
+ console.log("coinbase: " + eth.coinbase);
+ var ts = 1000 * eth.getBlock(eth.blockNumber).timestamp;
+ console.log("at block: " + eth.blockNumber + " (" + new Date(ts) + ")");
+ console.log(' datadir: ' + admin.datadir);
+ })();
+ `)
+ if modules, err := self.supportedApis(); err == nil {
+ loadedModules := make([]string, 0)
+ for api, version := range modules {
+ loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
+ }
+ sort.Strings(loadedModules)
+ }
+}
+
+func (self *jsre) supportedApis() (map[string]string, error) {
+ return self.client.SupportedModules()
+}
+
+func (js *jsre) apiBindings() error {
+ apis, err := js.supportedApis()
+ if err != nil {
+ return err
+ }
+
+ apiNames := make([]string, 0, len(apis))
+ for a, _ := range apis {
+ apiNames = append(apiNames, a)
+ }
+
+ jeth := utils.NewJeth(js.re, js.client)
+ js.re.Set("jeth", struct{}{})
+ t, _ := js.re.Get("jeth")
+ jethObj := t.Object()
+
+ jethObj.Set("send", jeth.Send)
+ jethObj.Set("sendAsync", jeth.Send)
+
+ err = js.re.Compile("bignumber.js", re.BigNumber_JS)
+ if err != nil {
+ utils.Fatalf("Error loading bignumber.js: %v", err)
+ }
+
+ err = js.re.Compile("web3.js", re.Web3_JS)
+ if err != nil {
+ utils.Fatalf("Error loading web3.js: %v", err)
+ }
+
+ _, err = js.re.Run("var Web3 = require('web3');")
+ if err != nil {
+ utils.Fatalf("Error requiring web3: %v", err)
+ }
+
+ _, err = js.re.Run("var web3 = new Web3(jeth);")
+ if err != nil {
+ utils.Fatalf("Error setting web3 provider: %v", err)
+ }
+
+ // load only supported API's in javascript runtime
+ shortcuts := "var eth = web3.eth; var personal = web3.personal; "
+ for _, apiName := range apiNames {
+ if apiName == "web3" || apiName == "rpc" {
+ continue // manually mapped or ignore
+ }
+
+ if jsFile, ok := web3ext.Modules[apiName]; ok {
+ if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), jsFile); err == nil {
+ shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
+ } else {
+ utils.Fatalf("Error loading %s.js: %v", apiName, err)
+ }
+ }
+ }
+
+ _, err = js.re.Run(shortcuts)
+ if err != nil {
+ utils.Fatalf("Error setting namespaces: %v", err)
+ }
+
+ // overrule some of the methods that require password as input and ask for it interactively
+ p, err := js.re.Get("personal")
+ if err != nil {
+ fmt.Println("Unable to overrule sensitive methods in personal module")
+ return nil
+ }
+
+ // Override the unlockAccount and newAccount methods on the personal object since these require user interaction.
+ // Assign the jeth.unlockAccount and jeth.newAccount in the jsre the original web3 callbacks. These will be called
+ // by the jeth.* methods after they got the password from the user and send the original web3 request to the backend.
+ if persObj := p.Object(); persObj != nil { // make sure the personal api is enabled over the interface
+ js.re.Run(`jeth.unlockAccount = personal.unlockAccount;`)
+ persObj.Set("unlockAccount", jeth.UnlockAccount)
+ js.re.Run(`jeth.newAccount = personal.newAccount;`)
+ persObj.Set("newAccount", jeth.NewAccount)
+ }
+
+ // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
+ // Bind these if the admin module is available.
+ if a, err := js.re.Get("admin"); err == nil {
+ if adminObj := a.Object(); adminObj != nil {
+ adminObj.Set("sleepBlocks", jeth.SleepBlocks)
+ adminObj.Set("sleep", jeth.Sleep)
+ }
+ }
+
+ return nil
+}
+
+func (self *jsre) AskPassword() (string, bool) {
+ pass, err := utils.Stdin.PasswordPrompt("Passphrase: ")
+ if err != nil {
+ return "", false
+ }
+ return pass, true
+}
+
+func (self *jsre) ConfirmTransaction(tx string) bool {
+ // Retrieve the Ethereum instance from the node
+ var ethereum *eth.Ethereum
+ if err := self.stack.Service(ðereum); err != nil {
+ return false
+ }
+ // If natspec is enabled, ask for permission
+ if ethereum.NatSpec && false /* disabled for now */ {
+ // notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient())
+ // fmt.Println(notice)
+ // answer, _ := self.Prompt("Confirm Transaction [y/n]")
+ // return strings.HasPrefix(strings.Trim(answer, " "), "y")
+ }
+ return true
+}
+
+func (self *jsre) UnlockAccount(addr []byte) bool {
+ fmt.Printf("Please unlock account %x.\n", addr)
+ pass, err := utils.Stdin.PasswordPrompt("Passphrase: ")
+ if err != nil {
+ return false
+ }
+ // TODO: allow retry
+ var ethereum *eth.Ethereum
+ if err := self.stack.Service(ðereum); err != nil {
+ return false
+ }
+ a := accounts.Account{Address: common.BytesToAddress(addr)}
+ if err := ethereum.AccountManager().Unlock(a, pass); err != nil {
+ return false
+ } else {
+ fmt.Println("Account is now unlocked for this session.")
+ return true
+ }
+}
+
+// preloadJSFiles loads JS files that the user has specified with ctx.PreLoadJSFlag into
+// the JSRE. If not all files could be loaded it will return an error describing the error.
+func (self *jsre) preloadJSFiles(ctx *cli.Context) error {
+ if ctx.GlobalString(utils.PreLoadJSFlag.Name) != "" {
+ assetPath := ctx.GlobalString(utils.JSpathFlag.Name)
+ jsFiles := strings.Split(ctx.GlobalString(utils.PreLoadJSFlag.Name), ",")
+ for _, file := range jsFiles {
+ filename := common.AbsolutePath(assetPath, strings.TrimSpace(file))
+ if err := self.re.Exec(filename); err != nil {
+ return fmt.Errorf("%s: %v", file, jsErrorString(err))
+ }
+ }
+ }
+ return nil
+}
+
+// jsErrorString adds a backtrace to errors generated by otto.
+func jsErrorString(err error) string {
+ if ottoErr, ok := err.(*otto.Error); ok {
+ return ottoErr.String()
+ }
+ return err.Error()
+}
+
+func (self *jsre) interactive() {
+ // Read input lines.
+ prompt := make(chan string)
+ inputln := make(chan string)
+ go func() {
+ defer close(inputln)
+ for {
+ line, err := utils.Stdin.Prompt(<-prompt)
+ if err != nil {
+ if err == liner.ErrPromptAborted { // ctrl-C
+ self.resetPrompt()
+ inputln <- ""
+ continue
+ }
+ return
+ }
+ inputln <- line
+ }
+ }()
+ // Wait for Ctrl-C, too.
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt)
+
+ defer func() {
+ if self.atexit != nil {
+ self.atexit()
+ }
+ self.re.Stop(false)
+ }()
+ for {
+ prompt <- self.ps1
+ select {
+ case <-sig:
+ fmt.Println("caught interrupt, exiting")
+ return
+ case input, ok := <-inputln:
+ if !ok || indentCount <= 0 && exit.MatchString(input) {
+ return
+ }
+ if onlyws.MatchString(input) {
+ continue
+ }
+ str += input + "\n"
+ self.setIndent()
+ if indentCount <= 0 {
+ if mustLogInHistory(str) {
+ utils.Stdin.AppendHistory(str[:len(str)-1])
+ }
+ self.parseInput(str)
+ str = ""
+ }
+ }
+ }
+}
+
+func mustLogInHistory(input string) bool {
+ return len(input) == 0 ||
+ passwordRegexp.MatchString(input) ||
+ !leadingSpace.MatchString(input)
+}
+
+func (self *jsre) withHistory(datadir string, op func(*os.File)) {
+ hist, err := os.OpenFile(filepath.Join(datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
+ if err != nil {
+ fmt.Printf("unable to open history file: %v\n", err)
+ return
+ }
+ op(hist)
+ hist.Close()
+}
+
+func (self *jsre) parseInput(code string) {
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Println("[native] error", r)
+ }
+ }()
+ if err := self.re.EvalAndPrettyPrint(code); err != nil {
+ if ottoErr, ok := err.(*otto.Error); ok {
+ fmt.Println(ottoErr.String())
+ } else {
+ fmt.Println(err)
+ }
+ return
+ }
+}
+
+var indentCount = 0
+var str = ""
+
+func (self *jsre) resetPrompt() {
+ indentCount = 0
+ str = ""
+ self.ps1 = "> "
+}
+
+func (self *jsre) setIndent() {
+ open := strings.Count(str, "{")
+ open += strings.Count(str, "(")
+ closed := strings.Count(str, "}")
+ closed += strings.Count(str, ")")
+ indentCount = open - closed
+ if indentCount <= 0 {
+ self.ps1 = "> "
+ } else {
+ self.ps1 = strings.Join(make([]string, indentCount*2), "..")
+ self.ps1 += " "
+ }
+}
diff --git a/cmd/geth/js_bzz_test.go b/cmd/geth/js_bzz_test.go
deleted file mode 100644
index 2d8b70efb9..0000000000
--- a/cmd/geth/js_bzz_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package main
-
-import (
- "io/ioutil"
- "os"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/swarm"
- "github.com/ethereum/go-ethereum/swarm/api"
-)
-
-var port = 8500
-
-func bzzREPL(t *testing.T, configf func(*api.Config)) (string, string, *testjethre, *node.Node) {
- prvKey, err := crypto.GenerateKey()
- if err != nil {
- t.Fatal("unable to generate key")
- }
- bzztmp, err := ioutil.TempDir("", "bzz-js-test")
- config, err := api.NewConfig(bzztmp, common.Address{}, prvKey)
- if err != nil {
- t.Fatal("unable to configure swarm")
- }
- if configf != nil {
- configf(config)
- }
- tmp, repl, stack := testREPL(t, func(n *node.Node) {
- if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- return swarm.NewSwarm(ctx, config, false)
- }); err != nil {
- t.Fatalf("Failed to register the Swarm service: %v", err)
- }
- })
- return bzztmp, tmp, repl, stack
-}
-
-func withREPL(t *testing.T, cf func(*api.Config), f func(repl *testjethre)) {
- bzztmp, tmp, repl, stack := bzzREPL(t, cf)
- defer stack.Stop()
- defer os.RemoveAll(tmp)
- defer os.RemoveAll(bzztmp)
- f(repl)
-}
-
-func TestBzzPutGet(t *testing.T) {
- withREPL(t,
- func(c *api.Config) {
- c.Port = ""
- }, func(repl *testjethre) {
- if checkEvalJSON(t, repl, `hash = bzz.put("console.log(\"hello from console\")", "application/javascript")`, `"97f1b7c7ea12468fd37c262383b9aa862d0cfbc4fc7218652374679fc5cf40cd"`) != nil {
- return
- }
- want := `{"content":"console.log(\"hello from console\")","contentType":"application/javascript","size":"33","status":"0"}`
- if checkEvalJSON(t, repl, `bzz.get(hash)`, want) != nil {
- return
- }
- })
-}
-
-// the server can be initialized only once per test session !
-// until we implement a stoppable http server
-// further http tests will need to make sure the correct server is running
-func TestHTTP(t *testing.T) {
- withREPL(t, nil, func(repl *testjethre) {
- if checkEvalJSON(t, repl, `hash = bzz.put("f42 = function() { return 42 }", "application/javascript")`, `"e6847876f00102441f850b2d438a06d10e3bf24e6a0a76d47b073a86c3c2f9ac"`) != nil {
- return
- }
- if checkEvalJSON(t, repl, `admin.httpGet("bzz://"+hash)`, `"f42 = function() { return 42 }"`) != nil {
- return
- }
-
- // if checkEvalJSON(t, repl, `http.loadScript("bzz://"+hash)`, `true`) != nil {
- // return
- // }
-
- // if checkEvalJSON(t, repl, `f42()`, `42`) != nil {
- // return
- // }
- })
-}
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index af8b96a459..7459f69575 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -29,7 +29,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/httpclient"
@@ -43,18 +42,17 @@ import (
const (
testSolcPath = ""
solcVersion = "0.9.23"
-
- testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
- testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
- testBalance = "10000000000000000000"
+ testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
+ testBalance = "10000000000000000000"
// of empty string
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
)
var (
- versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
- testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
- testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
+ versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
+ testNodeKey, _ = crypto.HexToECDSA("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
+ testAccount, _ = crypto.HexToECDSA("e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674")
+ testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
)
type testjethre struct {
@@ -63,17 +61,6 @@ type testjethre struct {
client *httpclient.HTTPClient
}
-func (self *testjethre) UnlockAccount(acc []byte) bool {
- var ethereum *eth.Ethereum
- self.stack.Service(ðereum)
-
- err := ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
- if err != nil {
- panic("unable to unlock")
- }
- return true
-}
-
// Temporary disabled while natspec hasn't been migrated
//func (self *testjethre) ConfirmTransaction(tx string) bool {
// var ethereum *eth.Ethereum
@@ -95,18 +82,19 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
t.Fatal(err)
}
// Create a networkless protocol stack
- stack, err := node.New(&node.Config{PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
+ stack, err := node.New(&node.Config{DataDir: tmp, PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
if err != nil {
t.Fatalf("failed to create node: %v", err)
}
// Initialize and register the Ethereum protocol
- keystore := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
- accman := accounts.NewManager(keystore)
-
+ accman := accounts.NewPlaintextManager(filepath.Join(tmp, "keystore"))
db, _ := ethdb.NewMemDatabase()
- core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
-
+ core.WriteGenesisBlockForTesting(db, core.GenesisAccount{
+ Address: common.HexToAddress(testAddress),
+ Balance: common.String2Big(testBalance),
+ })
ethConf := ð.Config{
+ ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
TestGenesisState: db,
AccountManager: accman,
DocRoot: "/",
@@ -122,15 +110,11 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
t.Fatalf("failed to register ethereum protocol: %v", err)
}
// Initialize all the keys for testing
- keyb, err := crypto.HexToECDSA(testKey)
+ a, err := accman.ImportECDSA(testAccount, "")
if err != nil {
t.Fatal(err)
}
- key := crypto.NewKeyFromECDSA(keyb)
- if err := keystore.StoreKey(key, ""); err != nil {
- t.Fatal(err)
- }
- if err := accman.Unlock(key.Address, ""); err != nil {
+ if err := accman.Unlock(a, ""); err != nil {
t.Fatal(err)
}
// Start the node and assemble the REPL tester
@@ -141,8 +125,10 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
stack.Service(ðereum)
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
- //client := comms.NewInProcClient(codec.JSON)
- client := utils.NewInProcRPCClient(stack)
+ client, err := stack.Attach()
+ if err != nil {
+ t.Fatalf("failed to attach to node: %v", err)
+ }
tf := &testjethre{client: ethereum.HTTPClient()}
repl := newJSRE(stack, assetPath, "", client, false)
tf.jsre = repl
@@ -152,9 +138,6 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
func TestNodeInfo(t *testing.T) {
t.Skip("broken after p2p update")
tmp, repl, ethereum := testJEthRE(t)
- if err := ethereum.Start(); err != nil {
- t.Fatalf("error starting ethereum: %v", err)
- }
defer ethereum.Stop()
defer os.RemoveAll(tmp)
@@ -398,7 +381,7 @@ multiply7 = Multiply7.at(contractaddress);
if sol != nil && solcVersion != sol.Version() {
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
fmt.Printf("modified contractinfo:\n%s\n", modContractInfo)
- contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"`
+ contentHash = `"` + common.ToHex(crypto.Keccak256([]byte(modContractInfo))) + `"`
}
if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil {
return
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index f66068bde7..3ea47fc297 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -4,7 +4,7 @@
// 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.
+// (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
@@ -190,9 +190,6 @@ participating.
utils.ExecFlag,
utils.PreloadJSFlag,
utils.WhisperEnabledFlag,
- utils.SwarmConfigPathFlag,
- utils.SwarmAccountAddrFlag,
- utils.ChequebookAddrFlag,
utils.DevModeFlag,
utils.TestNetFlag,
utils.VMForceJitFlag,
@@ -210,6 +207,13 @@ participating.
utils.GpobaseStepUpFlag,
utils.GpobaseCorrectionFactorFlag,
utils.ExtraDataFlag,
+ // on top of develop
+ utils.SwarmConfigPathFlag,
+ utils.SwarmSwapDisabled,
+ utils.SwarmSyncDisabled,
+ utils.SwarmPortFlag,
+ utils.SwarmAccountAddrFlag,
+ utils.ChequebookAddrFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 3b521a0e12..3083ea4e8b 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -24,6 +24,7 @@ import (
"os/signal"
"regexp"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
@@ -209,3 +210,57 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
glog.Infoln("Exported blockchain to ", fn)
return nil
}
+
+// tries unlocking the specified account a few times.
+func UnlockAccount(accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
+ account, err := MakeAddress(accman, address)
+ if err != nil {
+ Fatalf("Could not list accounts: %v", err)
+ }
+ for trials := 0; trials < 3; trials++ {
+ prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
+ password := GetPassPhrase(prompt, false, i, passwords)
+ err = accman.Unlock(account, password)
+ if err == nil {
+ glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
+ return account, password
+ }
+ if err, ok := err.(*accounts.AmbiguousAddrError); ok {
+ glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
+ return ambiguousAddrRecovery(accman, err, password), password
+ }
+ if err != accounts.ErrDecrypt {
+ // No need to prompt again if the error is not decryption-related.
+ break
+ }
+ }
+ // All trials expended to unlock account, bail out
+ Fatalf("Failed to unlock account %s (%v)", address, err)
+ return accounts.Account{}, ""
+}
+
+func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
+ fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
+ for _, a := range err.Matches {
+ fmt.Println(" ", a.File)
+ }
+ fmt.Println("Testing your passphrase against all of them...")
+ var match *accounts.Account
+ for _, a := range err.Matches {
+ if err := am.Unlock(a, auth); err == nil {
+ match = &a
+ break
+ }
+ }
+ if match == nil {
+ Fatalf("None of the listed files could be unlocked.")
+ }
+ fmt.Printf("Your passphrase unlocked %s\n", match.File)
+ fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
+ for _, a := range err.Matches {
+ if a != *match {
+ fmt.Println(" ", a.File)
+ }
+ }
+ return *match
+}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index da1ba35262..280e7241ca 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -49,12 +49,6 @@ import (
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/release"
"github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/rpc/api"
- "github.com/ethereum/go-ethereum/rpc/codec"
- "github.com/ethereum/go-ethereum/rpc/comms"
- "github.com/ethereum/go-ethereum/rpc/shared"
- "github.com/ethereum/go-ethereum/rpc/useragent"
- rpc "github.com/ethereum/go-ethereum/rpc/v2"
"github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/whisper"
@@ -357,22 +351,6 @@ var (
Name: "shh",
Usage: "Enable Whisper",
}
- ChequebookAddrFlag = cli.StringFlag{
- Name: "chequebook",
- Usage: "chequebook contract address",
- }
- SwarmAccountAddrFlag = cli.StringFlag{
- Name: "bzzaccount",
- Usage: "Swarm account address (swarm disabled if empty)",
- }
- SwarmConfigPathFlag = cli.StringFlag{
- Name: "bzzconfig",
- Usage: "Swarm config file path (datadir/bzz)",
- }
- SwarmSwapDisabled = cli.BoolFlag{
- Name: "bzznoswap",
- Usage: "Swarm SWAP disabled (false)",
- }
// ATM the url is left to the user and deployment to
JSpathFlag = cli.StringFlag{
Name: "jspath",
@@ -416,6 +394,32 @@ var (
Usage: "Suggested gas price base correction factor (%)",
Value: 110,
}
+
+ // on top of develop
+ ChequebookAddrFlag = cli.StringFlag{
+ Name: "chequebook",
+ Usage: "chequebook contract address",
+ }
+ SwarmAccountAddrFlag = cli.StringFlag{
+ Name: "bzzaccount",
+ Usage: "Swarm account address (swarm disabled if empty)",
+ }
+ SwarmPortFlag = cli.StringFlag{
+ Name: "bzzport",
+ Usage: "Swarm local http api port",
+ }
+ SwarmConfigPathFlag = cli.StringFlag{
+ Name: "bzzconfig",
+ Usage: "Swarm config file path (datadir/bzz)",
+ }
+ SwarmSwapDisabled = cli.BoolFlag{
+ Name: "bzznoswap",
+ Usage: "Swarm SWAP disabled (false)",
+ }
+ SwarmSyncDisabled = cli.BoolFlag{
+ Name: "bzznosync",
+ Usage: "Swarm Syncing disabled (false)",
+ }
)
// MustMakeDataDir retrieves the currently requested data directory, terminating
@@ -664,53 +668,6 @@ func MakePasswordList(ctx *cli.Context) []string {
return lines
}
-func UnlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (common.Address, string) {
- // Try to unlock the specified account a few times
- account, err := MakeAddress(accman, address)
- if err != nil {
- Fatalf("unable to unlock account %v: %v", address, err)
- }
-
- for trials := 0; trials < 3; trials++ {
- prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
- password := GetPassPhrase(prompt, false, i, passwords)
- if err := accman.Unlock(account, password); err == nil {
- return account, password
- }
- }
- // All trials expended to unlock account, bail out
- Fatalf("Failed to unlock account: %s", address)
- return common.Address{}, ""
-}
-
-// getPassPhrase retrieves the passwor associated with an account, either fetched
-// from a list of preloaded passphrases, or requested interactively from the user.
-func GetPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
- // If a list of passwords was supplied, retrieve from them
- if len(passwords) > 0 {
- if i < len(passwords) {
- return passwords[i]
- }
- return passwords[len(passwords)-1]
- }
- // Otherwise prompt the user for the password
- fmt.Println(prompt)
- password, err := PromptPassword("Passphrase: ", true)
- if err != nil {
- Fatalf("Failed to read passphrase: %v", err)
- }
- if confirmation {
- confirm, err := PromptPassword("Repeat passphrase: ", false)
- if err != nil {
- Fatalf("Failed to read passphrase confirmation: %v", err)
- }
- if password != confirm {
- Fatalf("Passphrases do not match")
- }
- }
- return password
-}
-
// MakeSystemNode sets up a local node, configures the services to launch and
// assembles the P2P protocol stack.
func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node {
@@ -724,6 +681,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
if networks > 1 {
Fatalf("The %v flags are mutually exclusive", netFlags)
}
+ // Configure the node's service container
datadir := MustMakeDataDir(ctx)
netprv := MakeNodeKey(ctx)
// Configure the node's service container
@@ -754,7 +712,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
accounts := strings.Split(ctx.GlobalString(UnlockedAccountFlag.Name), ",")
for i, account := range accounts {
if trimmed := strings.TrimSpace(account); trimmed != "" {
- UnlockAccount(ctx, accman, trimmed, i, passwords)
+ UnlockAccount(accman, trimmed, i, passwords)
}
}
@@ -850,13 +808,12 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
}); err != nil {
Fatalf("Failed to register the account manager service: %v", err)
}
+
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return eth.New(ctx, ethConf)
}); err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
-
- // Whisper
if shhEnable {
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
Fatalf("Failed to register the Whisper service: %v", err)
@@ -866,13 +823,14 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
return release.NewReleaseService(ctx, relconf)
}); err != nil {
Fatalf("Failed to register the Geth release oracle service: %v", err)
+ }
// bzz. Swarm
var bzzconfig *bzzapi.Config
hexaddr := ctx.GlobalString(SwarmAccountAddrFlag.Name)
if hexaddr != "" {
swarmaccount := common.HexToAddress(hexaddr)
- if !accman.HasAccount(swarmaccount) {
+ if !accman.HasAddress(swarmaccount) {
Fatalf("swarm account '%v' does not exist: %v", hexaddr, err)
}
prvkey, err := accman.GetUnlocked(swarmaccount)
@@ -888,13 +846,19 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
if err != nil {
Fatalf("unable to configure swarm: %v", err)
}
- swap := ctx.GlobalBool(SwarmSwapDisabled.Name)
+ bzzport := ctx.GlobalString(SwarmPortFlag.Name)
+ if len(bzzport) > 0 {
+ bzzconfig.Port = bzzport
+ }
+ swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
+ syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- return swarm.NewSwarm(ctx, bzzconfig, swap)
+ return swarm.NewSwarm(ctx, bzzconfig, swapEnabled, syncEnabled)
}); err != nil {
Fatalf("Failed to register the Swarm service: %v", err)
}
}
+
return stack
}
diff --git a/cmd/utils/input.go b/cmd/utils/input.go
new file mode 100644
index 0000000000..21ed8b68de
--- /dev/null
+++ b/cmd/utils/input.go
@@ -0,0 +1,128 @@
+// 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 .
+
+package utils
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/peterh/liner"
+)
+
+// Holds the stdin line reader.
+// Only this reader may be used for input because it keeps
+// an internal buffer.
+var Stdin = newUserInputReader()
+
+type userInputReader struct {
+ *liner.State
+ warned bool
+ supported bool
+ normalMode liner.ModeApplier
+ rawMode liner.ModeApplier
+}
+
+func newUserInputReader() *userInputReader {
+ r := new(userInputReader)
+ // Get the original mode before calling NewLiner.
+ // This is usually regular "cooked" mode where characters echo.
+ normalMode, _ := liner.TerminalMode()
+ // Turn on liner. It switches to raw mode.
+ r.State = liner.NewLiner()
+ rawMode, err := liner.TerminalMode()
+ if err != nil || !liner.TerminalSupported() {
+ r.supported = false
+ } else {
+ r.supported = true
+ r.normalMode = normalMode
+ r.rawMode = rawMode
+ // Switch back to normal mode while we're not prompting.
+ normalMode.ApplyMode()
+ }
+ return r
+}
+
+func (r *userInputReader) Prompt(prompt string) (string, error) {
+ if r.supported {
+ r.rawMode.ApplyMode()
+ defer r.normalMode.ApplyMode()
+ } else {
+ // liner tries to be smart about printing the prompt
+ // and doesn't print anything if input is redirected.
+ // Un-smart it by printing the prompt always.
+ fmt.Print(prompt)
+ prompt = ""
+ defer fmt.Println()
+ }
+ return r.State.Prompt(prompt)
+}
+
+func (r *userInputReader) PasswordPrompt(prompt string) (passwd string, err error) {
+ if r.supported {
+ r.rawMode.ApplyMode()
+ defer r.normalMode.ApplyMode()
+ return r.State.PasswordPrompt(prompt)
+ }
+ if !r.warned {
+ fmt.Println("!! Unsupported terminal, password will be echoed.")
+ r.warned = true
+ }
+ // Just as in Prompt, handle printing the prompt here instead of relying on liner.
+ fmt.Print(prompt)
+ passwd, err = r.State.Prompt("")
+ fmt.Println()
+ return passwd, err
+}
+
+func (r *userInputReader) ConfirmPrompt(prompt string) (bool, error) {
+ prompt = prompt + " [y/N] "
+ input, err := r.Prompt(prompt)
+ if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
+ return true, nil
+ }
+ return false, err
+}
+
+// getPassPhrase retrieves the password associated with an account, either fetched
+// from a list of preloaded passphrases, or requested interactively from the user.
+func GetPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
+ // If a list of passwords was supplied, retrieve from them
+ if len(passwords) > 0 {
+ if i < len(passwords) {
+ return passwords[i]
+ }
+ return passwords[len(passwords)-1]
+ }
+ // Otherwise prompt the user for the password
+ if prompt != "" {
+ fmt.Println(prompt)
+ }
+ password, err := Stdin.PasswordPrompt("Passphrase: ")
+ if err != nil {
+ Fatalf("Failed to read passphrase: %v", err)
+ }
+ if confirmation {
+ confirm, err := Stdin.PasswordPrompt("Repeat passphrase: ")
+ if err != nil {
+ Fatalf("Failed to read passphrase confirmation: %v", err)
+ }
+ if password != confirm {
+ Fatalf("Passphrases do not match")
+ }
+ }
+ return password
+}
diff --git a/common/chequebook/contract.go b/common/chequebook/contract.go
deleted file mode 100644
index 866d7c3358..0000000000
--- a/common/chequebook/contract.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package chequebook
-
-import ()
-
-const (
- ContractCode = `606060405260008054600160a060020a03191633179055610201806100246000396000f3606060405260e060020a600035046341c0e1b58114610031578063d75d691d14610059578063fbf788d61461007e575b005b61002f60005433600160a060020a03908116911614156101ff57600054600160a060020a0316ff5b600160a060020a03600435166000908152600160205260409020546060908152602090f35b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b8575b505050505050565b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610139576100b0565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b357846001600050600088600160a060020a03168152602001908152602001600020600050819055506100b0565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
-
- ContractDeployedCode = `0x606060405260e060020a600035046341c0e1b58114610031578063d75d691d14610059578063fbf788d61461007e575b005b61002f60005433600160a060020a03908116911614156101ff57600054600160a060020a0316ff5b600160a060020a03600435166000908152600160205260409020546060908152602090f35b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b8575b505050505050565b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610139576100b0565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b357846001600050600088600160a060020a03168152602001908152602001600020600050819055506100b0565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
-
- ContractAbi = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"beneficiary","type":"address"}],"name":"getSent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]`
-
- ContractSource = `
-import "mortal";
-
-/// @title Chequebook for Ethereum micropayments
-/// @author Daniel A. Nagy
-contract chequebook is mortal {
- // Cumulative paid amount in wei to each beneficiary
- mapping (address => uint256) sent;
-
- /// @notice Overdraft event
- event Overdraft(address deadbeat);
-
- /// @notice Accessor for sent map
- ///
- /// @param beneficiary beneficiary address
- /// @return cumulative amount in wei sent to beneficiary
- function getSent(address beneficiary) returns (uint256) {
- return sent[beneficiary];
- }
-
- /// @notice Cash cheque
- ///
- /// @param beneficiary beneficiary address
- /// @param amount cumulative amount in wei
- /// @param sig_v signature parameter v
- /// @param sig_r signature parameter r
- /// @param sig_s signature parameter s
- /// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount
- function cash(address beneficiary, uint256 amount,
- uint8 sig_v, bytes32 sig_r, bytes32 sig_s) {
- // Check if the cheque is old.
- // Only cheques that are more recent than the last cashed one are considered.
- if(amount <= sent[beneficiary]) return;
- // Check the digital signature of the cheque.
- bytes32 hash = sha3(address(this), beneficiary, amount);
- if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return;
- // Attempt sending the difference between the cumulative amount on the cheque
- // and the cumulative amount on the last cashed cheque to beneficiary.
- if (beneficiary.send(amount - sent[beneficiary])) {
- // Upon success, update the cumulative amount.
- sent[beneficiary] = amount;
- } else {
- // Upon failure, punish owner for writing a bounced cheque.
- // owner.sendToDebtorsPrison();
- Overdraft(owner);
- // Compensate beneficiary.
- suicide(beneficiary);
- }
- }
-}
-`
-)
diff --git a/common/registrar/contracts.go b/common/registrar/contracts.go
deleted file mode 100644
index cd80dfcaba..0000000000
--- a/common/registrar/contracts.go
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2015 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 .
-
-package registrar
-
-const ( // built-in contracts address source code and evm code
- UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056"
-
- UrlHintSrc = `
-contract URLhint {
- function register(uint256 _hash, uint8 idx, uint256 _url) {
- if (owner[_hash] == 0 || owner[_hash] == msg.sender) {
- owner[_hash] = msg.sender;
- url[_hash][idx] = _url;
- }
- }
- mapping (uint256 => address) owner;
- (uint256 => uint256[256]) url;
-}
- `
-
- HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056"
-
- HashRegSrc = `
-contract HashReg {
- function setowner() {
- if (owner == 0) {
- owner = msg.sender;
- }
- }
- function register(uint256 _key, uint256 _content) {
- if (msg.sender == owner) {
- content[_key] = _content;
- }
- }
- address owner;
- mapping (uint256 => uint256) content;
-}
-`
-
- GlobalRegistrarSrc = `
-//sol
-
-import "owned";
-
-contract NameRegister {
- function addr(bytes32 _name) constant returns (address o_owner) {}
- function name(address _owner) constant returns (bytes32 o_name) {}
-}
-
-contract Registrar is NameRegister {
- event Changed(bytes32 indexed name);
- event PrimaryChanged(bytes32 indexed name, address indexed addr);
-
- function owner(bytes32 _name) constant returns (address o_owner) {}
- function addr(bytes32 _name) constant returns (address o_address) {}
- function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {}
- function content(bytes32 _name) constant returns (bytes32 o_content) {}
-
- function name(address _owner) constant returns (bytes32 o_name) {}
-}
-
-contract GlobalRegistrar is Registrar {
- struct Record {
- address owner;
- address primary;
- address subRegistrar;
- bytes32 content;
- uint value;
- uint renewalDate;
- }
-
- function Registrar() {
- // TODO: Populate with hall-of-fame.
- }
-
- function reserve(bytes32 _name) {
- // Don't allow the same name to be overwritten.
- // TODO: bidding mechanism
- if (m_toRecord[_name].owner == 0) {
- m_toRecord[_name].owner = msg.sender;
- Changed(_name);
- }
- }
-
- /*
- TODO
- > 12 chars: free
- <= 12 chars: auction:
- 1. new names are auctioned
- - 7 day period to collect all bid bytes32es + deposits
- - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid)
- - all valid bids are burnt except highest - difference between that and second highest is returned to winner
- 2. remember when last auctioned/renewed
- 3. anyone can force renewal process:
- - 7 day period to collect all bid bytes32es + deposits
- - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high.
- - 1% of winner burnt; original owner paid rest.
- */
-
- modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ }
-
- function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) {
- m_toRecord[_name].owner = _newOwner;
- Changed(_name);
- }
-
- function disown(bytes32 _name) onlyrecordowner(_name) {
- if (m_toName[m_toRecord[_name].primary] == _name)
- {
- PrimaryChanged(_name, m_toRecord[_name].primary);
- m_toName[m_toRecord[_name].primary] = "";
- }
- delete m_toRecord[_name];
- Changed(_name);
- }
-
- function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) {
- m_toRecord[_name].primary = _a;
- if (_primary)
- {
- PrimaryChanged(_name, _a);
- m_toName[_a] = _name;
- }
- Changed(_name);
- }
- function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) {
- m_toRecord[_name].subRegistrar = _registrar;
- Changed(_name);
- }
- function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) {
- m_toRecord[_name].content = _content;
- Changed(_name);
- }
-
- function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; }
- function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; }
-// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration.
- function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now
- function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; }
- function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; }
-
- mapping (address => bytes32) m_toName;
- mapping (bytes32 => Record) m_toRecord;
-}
-`
- GlobalRegistrarAbi = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]`
-
- GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056`
-)
diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go
deleted file mode 100644
index 6d77a9385d..0000000000
--- a/common/registrar/ethreg/api.go
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2015 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 .
-
-package ethreg
-
-import (
- "errors"
- "math/big"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/compiler"
- "github.com/ethereum/go-ethereum/common/registrar"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
-)
-
-// registryAPIBackend is a backend for an Ethereum Registry.
-type registryAPIBackend struct {
- config *core.ChainConfig
- bc *core.BlockChain
- chainDb ethdb.Database
- txPool *core.TxPool
- am *accounts.Manager
-}
-
-// PrivateRegistarAPI offers various functions to access the Ethereum registry.
-type PrivateRegistarAPI struct {
- config *core.ChainConfig
- be *registryAPIBackend
-}
-
-// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
-func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
- return &PrivateRegistarAPI{
- config: config,
- be: ®istryAPIBackend{
- config: config,
- bc: bc,
- chainDb: chainDb,
- txPool: txPool,
- am: am,
- },
- }
-}
-
-// SetGlobalRegistrar allows clients to set the global registry for the node.
-// This method can be used to deploy a new registry. First zero out the current
-// address by calling the method with namereg = '0x0' and then call this method
-// again with '' as namereg. This will submit a transaction to the network which
-// will deploy a new registry on execution. The TX hash is returned. When called
-// with namereg '' and the current address is not zero the current global is
-// address is returned..
-func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) {
- return registrar.New(api.be).SetGlobalRegistrar(namereg, from)
-}
-
-// SetHashReg queries the registry for a hash.
-func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) {
- return registrar.New(api.be).SetHashReg(hashreg, from)
-}
-
-// SetUrlHint queries the registry for an url.
-func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) {
- return registrar.New(api.be).SetUrlHint(hashreg, from)
-}
-
-// SaveInfo stores contract information on the local file system.
-func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) {
- return compiler.SaveInfo(info, filename)
-}
-
-// Register registers a new content hash in the registry.
-func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) {
- block := api.be.bc.CurrentBlock()
- state, err := state.New(block.Root(), api.be.chainDb)
- if err != nil {
- return false, err
- }
-
- codeb := state.GetCode(addr)
- codeHash := common.BytesToHash(crypto.Keccak256(codeb))
- contentHash := common.HexToHash(contentHashHex)
-
- _, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash)
- return err == nil, err
-}
-
-// RegisterUrl registers a new url in the registry.
-func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) {
- _, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url)
- return err == nil, err
-}
-
-// callmsg is the message type used for call transations.
-type callmsg struct {
- from *state.StateObject
- to *common.Address
- gas, gasPrice *big.Int
- value *big.Int
- data []byte
-}
-
-// accessor boilerplate to implement core.Message
-func (m callmsg) From() (common.Address, error) {
- return m.from.Address(), nil
-}
-func (m callmsg) FromFrontier() (common.Address, error) {
- return m.from.Address(), nil
-}
-func (m callmsg) Nonce() uint64 {
- return m.from.Nonce()
-}
-func (m callmsg) To() *common.Address {
- return m.to
-}
-func (m callmsg) GasPrice() *big.Int {
- return m.gasPrice
-}
-func (m callmsg) Gas() *big.Int {
- return m.gas
-}
-func (m callmsg) Value() *big.Int {
- return m.value
-}
-func (m callmsg) Data() []byte {
- return m.data
-}
-
-// Call forms a transaction from the given arguments and tries to execute it on
-// a private VM with a copy of the state. Any changes are therefore only temporary
-// and not part of the actual state. This allows for local execution/queries.
-func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
- block := be.bc.CurrentBlock()
- statedb, err := state.New(block.Root(), be.chainDb)
- if err != nil {
- return "", "", err
- }
-
- var from *state.StateObject
- if len(fromStr) == 0 {
- accounts := be.am.Accounts()
- if len(accounts) == 0 {
- from = statedb.GetOrNewStateObject(common.Address{})
- } else {
- from = statedb.GetOrNewStateObject(accounts[0].Address)
- }
- } else {
- from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
- }
-
- from.SetBalance(common.MaxBig)
-
- msg := callmsg{
- from: from,
- gas: common.Big(gasStr),
- gasPrice: common.Big(gasPriceStr),
- value: common.Big(valueStr),
- data: common.FromHex(dataStr),
- }
- if len(toStr) > 0 {
- addr := common.HexToAddress(toStr)
- msg.to = &addr
- }
-
- if msg.gas.Cmp(big.NewInt(0)) == 0 {
- msg.gas = big.NewInt(50000000)
- }
-
- if msg.gasPrice.Cmp(big.NewInt(0)) == 0 {
- msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
- }
-
- header := be.bc.CurrentBlock().Header()
- vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
- gp := new(core.GasPool).AddGas(common.MaxBig)
- res, gas, err := core.ApplyMessage(vmenv, msg, gp)
-
- return common.ToHex(res), gas.String(), err
-}
-
-// StorageAt returns the data stores in the state for the given address and location.
-func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string {
- block := be.bc.CurrentBlock()
- state, err := state.New(block.Root(), be.chainDb)
- if err != nil {
- return ""
- }
- return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
-}
-
-// Transact forms a transaction from the given arguments and submits it to the
-// transactio pool for execution.
-func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
- if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) {
- return "", errors.New("invalid address")
- }
-
- var (
- from = common.HexToAddress(fromStr)
- to = common.HexToAddress(toStr)
- value = common.Big(valueStr)
- gas *big.Int
- price *big.Int
- data []byte
- contractCreation bool
- )
-
- if len(gasStr) == 0 {
- gas = big.NewInt(90000)
- } else {
- gas = common.Big(gasStr)
- }
-
- if len(gasPriceStr) == 0 {
- price = big.NewInt(10000000000000)
- } else {
- price = common.Big(gasPriceStr)
- }
-
- data = common.FromHex(codeStr)
- if len(toStr) == 0 {
- contractCreation = true
- }
-
- nonce := be.txPool.State().GetNonce(from)
- if len(nonceStr) != 0 {
- nonce = common.Big(nonceStr).Uint64()
- }
-
- var tx *types.Transaction
- if contractCreation {
- tx = types.NewContractCreation(nonce, value, gas, price, data)
- } else {
- tx = types.NewTransaction(nonce, to, value, gas, price, data)
- }
-
- signature, err := be.am.Sign(from, tx.SigHash().Bytes())
- if err != nil {
- return "", err
- }
- signedTx, err := tx.WithSignature(signature)
- if err != nil {
- return "", err
- }
-
- be.txPool.SetLocal(signedTx)
- if err := be.txPool.Add(signedTx); err != nil {
- return "", nil
- }
-
- if contractCreation {
- addr := crypto.CreateAddress(from, nonce)
- glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
- } else {
- glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
- }
-
- return signedTx.Hash().Hex(), nil
-}
diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go
deleted file mode 100644
index 0606f6985f..0000000000
--- a/common/registrar/registrar.go
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright 2015 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 .
-
-package registrar
-
-import (
- "encoding/binary"
- "fmt"
- "math/big"
- "regexp"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
-)
-
-/*
-Registrar implements the Ethereum name registrar services mapping
-- arbitrary strings to ethereum addresses
-- hashes to hashes
-- hashes to arbitrary strings
-(likely will provide lookup service for all three)
-
-The Registrar is used by
-* the roundtripper transport implementation of
-url schemes to resolve domain names and services that register these names
-* contract info retrieval (NatSpec).
-
-The Registrar uses 3 contracts on the blockchain:
-* GlobalRegistrar: Name (string) -> Address (Owner)
-* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
-* UrlHint : Content Hash -> Url Hint
-
-These contracts are (currently) not included in the genesis block.
-Each Set needs to be called once on each blockchain/network once.
-
-Contract addresses need to be set the first time any Registrar method is called
-in a client session.
-This is done for frontier by default, otherwise the caller needs to make sure
-the relevant environment initialised the desired contracts
-*/
-var (
- // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
- GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
- HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
- UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
-
- zero = regexp.MustCompile("^(0x)?0*$")
-)
-
-const (
- trueHex = "0000000000000000000000000000000000000000000000000000000000000001"
- falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
-)
-
-func abiSignature(s string) string {
- return common.ToHex(crypto.Keccak256([]byte(s))[:4])
-}
-
-var (
- HashRegName = "HashReg"
- UrlHintName = "UrlHint"
-
- registerContentHashAbi = abiSignature("register(uint256,uint256)")
- registerUrlAbi = abiSignature("register(uint256,uint8,uint256)")
- setOwnerAbi = abiSignature("setowner()")
- reserveAbi = abiSignature("reserve(bytes32)")
- resolveAbi = abiSignature("addr(bytes32)")
- registerAbi = abiSignature("setAddress(bytes32,address,bool)")
- addressAbiPrefix = falseHex[:24]
-)
-
-// Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
-type Backend interface {
- StorageAt(string, string) string
- Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
- Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
-}
-
-// TODO Registrar should also just implement The Resolver and Registry interfaces
-// Simplify for now.
-type VersionedRegistrar interface {
- Resolver(*big.Int) *Registrar
- Registry() *Registrar
-}
-
-type Registrar struct {
- backend Backend
-}
-
-func New(b Backend) (res *Registrar) {
- res = &Registrar{b}
- return
-}
-
-func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
- if namereg != "" {
- GlobalRegistrarAddr = namereg
- return
- }
- if zero.MatchString(GlobalRegistrarAddr) {
- if (addr == common.Address{}) {
- err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
- return
- } else {
- txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
- if err != nil {
- err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
- return
- }
- }
- }
- return
-}
-
-func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
- if hashreg != "" {
- HashRegAddr = hashreg
- } else {
- if !zero.MatchString(HashRegAddr) {
- return
- }
- nameHex, extra := encodeName(HashRegName, 2)
- hashRegAbi := resolveAbi + nameHex + extra
- glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
- var res string
- res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
- if len(res) >= 40 {
- HashRegAddr = "0x" + res[len(res)-40:len(res)]
- }
- if err != nil || zero.MatchString(HashRegAddr) {
- if (addr == common.Address{}) {
- err = fmt.Errorf("HashReg address not found and sender for creation not given")
- return
- }
-
- txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
- if err != nil {
- err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
- }
- glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
- } else {
- glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
- return
- }
- }
-
- return
-}
-
-func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
- if urlhint != "" {
- UrlHintAddr = urlhint
- } else {
- if !zero.MatchString(UrlHintAddr) {
- return
- }
- nameHex, extra := encodeName(UrlHintName, 2)
- urlHintAbi := resolveAbi + nameHex + extra
- glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
- var res string
- res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
- if len(res) >= 40 {
- UrlHintAddr = "0x" + res[len(res)-40:len(res)]
- }
- if err != nil || zero.MatchString(UrlHintAddr) {
- if (addr == common.Address{}) {
- err = fmt.Errorf("UrlHint address not found and sender for creation not given")
- return
- }
- txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
- if err != nil {
- err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
- }
- glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
- } else {
- glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
- return
- }
- }
-
- return
-}
-
-// ReserveName(from, name) reserves name for the sender address in the globalRegistrar
-// the tx needs to be mined to take effect
-func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
- if zero.MatchString(GlobalRegistrarAddr) {
- return "", fmt.Errorf("GlobalRegistrar address is not set")
- }
- nameHex, extra := encodeName(name, 2)
- abi := reserveAbi + nameHex + extra
- glog.V(logger.Detail).Infof("Reserve data: %s", abi)
- return self.backend.Transact(
- address.Hex(),
- GlobalRegistrarAddr,
- "", "", "", "",
- abi,
- )
-}
-
-// SetAddressToName(from, name, addr) will set the Address to address for name
-// in the globalRegistrar using from as the sender of the transaction
-// the tx needs to be mined to take effect
-func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
- if zero.MatchString(GlobalRegistrarAddr) {
- return "", fmt.Errorf("GlobalRegistrar address is not set")
- }
-
- nameHex, extra := encodeName(name, 6)
- addrHex := encodeAddress(address)
-
- abi := registerAbi + nameHex + addrHex + trueHex + extra
- glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
-
- return self.backend.Transact(
- from.Hex(),
- GlobalRegistrarAddr,
- "", "", "", "",
- abi,
- )
-}
-
-// NameToAddr(from, name) queries the registrar for the address on name
-func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
- if zero.MatchString(GlobalRegistrarAddr) {
- return address, fmt.Errorf("GlobalRegistrar address is not set")
- }
-
- nameHex, extra := encodeName(name, 2)
- abi := resolveAbi + nameHex + extra
- glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
- res, _, err := self.backend.Call(
- from.Hex(),
- GlobalRegistrarAddr,
- "", "", "",
- abi,
- )
- if err != nil {
- return
- }
- address = common.HexToAddress(res)
- return
-}
-
-// called as first step in the registration process on HashReg
-func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
- if zero.MatchString(HashRegAddr) {
- return "", fmt.Errorf("HashReg address is not set")
- }
- return self.backend.Transact(
- address.Hex(),
- HashRegAddr,
- "", "", "", "",
- setOwnerAbi,
- )
-}
-
-// registers some content hash to a key/code hash
-// e.g., the contract Info combined Json Doc's ContentHash
-// to CodeHash of a contract or hash of a domain
-func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
- if zero.MatchString(HashRegAddr) {
- return "", fmt.Errorf("HashReg address is not set")
- }
-
- _, err = self.SetOwner(address)
- if err != nil {
- return
- }
- codehex := common.Bytes2Hex(codehash[:])
- dochex := common.Bytes2Hex(dochash[:])
-
- data := registerContentHashAbi + codehex + dochex
- glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr)
- return self.backend.Transact(
- address.Hex(),
- HashRegAddr,
- "", "", "", "",
- data,
- )
-}
-
-// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
-// address is used as sender for the transaction and will be the owner of a new
-// registry entry on first time use
-// FIXME: silently doing nothing if sender is not the owner
-// note that with content addressed storage, this step is no longer necessary
-func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
- if zero.MatchString(UrlHintAddr) {
- return "", fmt.Errorf("UrlHint address is not set")
- }
-
- hashHex := common.Bytes2Hex(hash[:])
- var urlHex string
- urlb := []byte(url)
- var cnt byte
- n := len(urlb)
-
- for n > 0 {
- if n > 32 {
- n = 32
- }
- urlHex = common.Bytes2Hex(urlb[:n])
- urlb = urlb[n:]
- n = len(urlb)
- bcnt := make([]byte, 32)
- bcnt[31] = cnt
- data := registerUrlAbi +
- hashHex +
- common.Bytes2Hex(bcnt) +
- common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
- txh, err = self.backend.Transact(
- address.Hex(),
- UrlHintAddr,
- "", "", "", "",
- data,
- )
- if err != nil {
- return
- }
- cnt++
- }
- return
-}
-
-// HashToHash(key) resolves contenthash for key (a hash) using HashReg
-// resolution is costless non-transactional
-// implemented as direct retrieval from db
-func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
- if zero.MatchString(HashRegAddr) {
- return common.Hash{}, fmt.Errorf("HashReg address is not set")
- }
-
- // look up in hashReg
- at := HashRegAddr[2:]
- key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
- hash := self.backend.StorageAt(at, key)
-
- if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
- err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex())
- return
- }
- copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
- return
-}
-
-// HashToUrl(contenthash) resolves the url for contenthash using UrlHint
-// resolution is costless non-transactional
-// implemented as direct retrieval from db
-// if we use content addressed storage, this step is no longer necessary
-func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
- if zero.MatchString(UrlHintAddr) {
- return "", fmt.Errorf("UrlHint address is not set")
- }
- // look up in URL reg
- var str string = " "
- var idx uint32
- for len(str) > 0 {
- mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
- key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
- hex := self.backend.StorageAt(UrlHintAddr[2:], key)
- str = string(common.Hex2Bytes(hex[2:]))
- l := 0
- for (l < len(str)) && (str[l] == 0) {
- l++
- }
-
- str = str[l:]
- uri = uri + str
- idx++
- }
-
- if len(uri) == 0 {
- err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex())
- }
- return
-}
-
-func storageIdx2Addr(varidx uint32) []byte {
- data := make([]byte, 32)
- binary.BigEndian.PutUint32(data[28:32], varidx)
- return data
-}
-
-func storageMapping(addr, key []byte) []byte {
- data := make([]byte, 64)
- copy(data[0:32], key[0:32])
- copy(data[32:64], addr[0:32])
- sha := crypto.Keccak256(data)
- return sha
-}
-
-func storageFixedArray(addr, idx []byte) []byte {
- var carry byte
- for i := 31; i >= 0; i-- {
- var b byte = addr[i] + idx[i] + carry
- if b < addr[i] {
- carry = 1
- } else {
- carry = 0
- }
- addr[i] = b
- }
- return addr
-}
-
-func storageAddress(addr []byte) string {
- return common.ToHex(addr)
-}
-
-func encodeAddress(address common.Address) string {
- return addressAbiPrefix + address.Hex()[2:]
-}
-
-func encodeName(name string, index uint8) (string, string) {
- extra := common.Bytes2Hex([]byte(name))
- if len(name) > 32 {
- return fmt.Sprintf("%064x", index), extra
- }
- return extra + falseHex[len(extra):], ""
-}
diff --git a/common/registrar/registrar_test.go b/common/registrar/registrar_test.go
deleted file mode 100644
index b2287803c2..0000000000
--- a/common/registrar/registrar_test.go
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2015 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 .
-
-package registrar
-
-import (
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
-)
-
-type testBackend struct {
- // contracts mock
- contracts map[string](map[string]string)
-}
-
-var (
- text = "test"
- codehash = common.StringToHash("1234")
- hash = common.BytesToHash(crypto.Keccak256([]byte(text)))
- url = "bzz://bzzhash/my/path/contr.act"
-)
-
-func NewTestBackend() *testBackend {
- self := &testBackend{}
- self.contracts = make(map[string](map[string]string))
- return self
-}
-
-func (self *testBackend) initHashReg() {
- self.contracts[HashRegAddr[2:]] = make(map[string]string)
- key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
- self.contracts[HashRegAddr[2:]][key] = hash.Hex()
-}
-
-func (self *testBackend) initUrlHint() {
- self.contracts[UrlHintAddr[2:]] = make(map[string]string)
- mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
-
- key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
- self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
- key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
- self.contracts[UrlHintAddr[2:]][key] = "0x0"
-}
-
-func (self *testBackend) StorageAt(ca, sa string) (res string) {
- c := self.contracts[ca]
- if c == nil {
- return "0x0"
- }
- res = c[sa]
- return
-}
-
-func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
- return "", nil
-}
-
-func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
- return "", "", nil
-}
-
-func TestSetGlobalRegistrar(t *testing.T) {
- b := NewTestBackend()
- res := New(b)
- _, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
- if err != nil {
- t.Errorf("unexpected error: %v'", err)
- }
-}
-
-func TestHashToHash(t *testing.T) {
- b := NewTestBackend()
- res := New(b)
-
- HashRegAddr = "0x0"
- got, err := res.HashToHash(codehash)
- if err == nil {
- t.Errorf("expected error")
- } else {
- exp := "HashReg address is not set"
- if err.Error() != exp {
- t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
- }
- }
-
- HashRegAddr = common.BigToAddress(common.Big1).Hex() //[2:]
- got, err = res.HashToHash(codehash)
- if err == nil {
- t.Errorf("expected error")
- } else {
- exp := "HashToHash: content hash not found for '" + codehash.Hex() + "'"
- if err.Error() != exp {
- t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
- }
- }
-
- b.initHashReg()
- got, err = res.HashToHash(codehash)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- } else {
- if got != hash {
- t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
- }
- }
-}
-
-func TestHashToUrl(t *testing.T) {
- b := NewTestBackend()
- res := New(b)
-
- UrlHintAddr = "0x0"
- got, err := res.HashToUrl(hash)
- if err == nil {
- t.Errorf("expected error")
- } else {
- exp := "UrlHint address is not set"
- if err.Error() != exp {
- t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
- }
- }
-
- UrlHintAddr = common.BigToAddress(common.Big2).Hex() //[2:]
- got, err = res.HashToUrl(hash)
- if err == nil {
- t.Errorf("expected error")
- } else {
- exp := "HashToUrl: URL hint not found for '" + hash.Hex() + "'"
- if err.Error() != exp {
- t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
- }
- }
-
- b.initUrlHint()
- got, err = res.HashToUrl(hash)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- } else {
- if got != url {
- t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
- }
- }
-}
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 85f0970956..a17990da66 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -196,9 +196,15 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
if len(hash) != 32 {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
}
+ if prv.D == nil {
+ panic("prv.D is nil")
+ }
+ if prv.Params() == nil {
+ panic("prv.Params() is nil")
+ }
seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
- defer zeroBytes(seckey)
+ // defer zeroBytes(seckey)
sig, err = secp256k1.Sign(hash, seckey)
return
}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 58b29da490..0468e283d7 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -89,6 +89,7 @@ func TestSign(t *testing.T) {
if err != nil {
t.Errorf("Sign error: %s", err)
}
+
recoveredPub, err := Ecrecover(msg, sig)
if err != nil {
t.Errorf("ECRecover error: %s", err)
diff --git a/crypto/secp256k1/curve_test.go b/crypto/secp256k1/curve_test.go
index 850cc748b8..d915ee8525 100644
--- a/crypto/secp256k1/curve_test.go
+++ b/crypto/secp256k1/curve_test.go
@@ -21,8 +21,6 @@ import (
"encoding/hex"
"math/big"
"testing"
-
- "github.com/ethereum/go-ethereum/common/registrar"
)
func TestReadBits(t *testing.T) {
@@ -39,34 +37,3 @@ func TestReadBits(t *testing.T) {
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0")
check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0")
}
-
-type Backend interface {
- registrar.Backend
- AtStateNum(int64) registrar.Backend
-}
-
-// implements a versioned Registrar on an archiving full node
-type EthReg struct {
- backend Backend
- registry *registrar.Registrar
-}
-
-func New(backend Backend) (self *EthReg) {
- self = &EthReg{backend: backend}
- self.registry = registrar.New(backend)
- return
-}
-
-func (self *EthReg) Registry() *registrar.Registrar {
- return self.registry
-}
-
-func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar {
- var s registrar.Backend
- if n != nil {
- s = self.backend.AtStateNum(n.Int64())
- } else {
- s = registrar.Backend(self.backend)
- }
- return registrar.New(s)
-}
diff --git a/eth/backend.go b/eth/backend.go
index 351cc27445..8b02d129b6 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -33,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/httpclient"
- "github.com/ethereum/go-ethereum/common/registrar/ethreg"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@@ -342,16 +341,12 @@ func (s *Ethereum) APIs() []rpc.API {
}, {
Namespace: "debug",
Version: "1.0",
- Service: NewPrivateDebugAPI(s.chainConfig, s),
+ Service: NewPrivateDebugAPI(s.ChainConfig(), s),
}, {
Namespace: "net",
Version: "1.0",
Service: s.netRPCService,
Public: true,
- }, {
- Namespace: "admin",
- Version: "1.0",
- Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
},
}...)
}
@@ -384,6 +379,8 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
+func (s *Ethereum) GPO() *GasPriceOracle { return s.gpo }
+func (s *Ethereum) ChainConfig() *core.ChainConfig { return s.chainConfig }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
diff --git a/eth/bind.go b/eth/bind.go
index c1366464f9..95868f45af 100644
--- a/eth/bind.go
+++ b/eth/bind.go
@@ -20,6 +20,8 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rlp"
@@ -68,7 +70,7 @@ func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address,
// call with the specified data as the input. The pending flag requests execution
// against the pending block, not the stable head of the chain.
func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
- if ctx == nil {
+ if ctx ==-- nil {
ctx = context.Background()
}
// Convert the input args to the API spec
@@ -80,7 +82,7 @@ func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Addr
if pending {
block = rpc.PendingBlockNumber
}
- // Execute the call and convert the output back to Go types
+ // Execute the call and convert the output ba--ck to Go types
out, err := b.bcapi.Call(ctx, args, block)
return common.FromHex(out), err
}
@@ -132,3 +134,20 @@ func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transac
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
return err
}
+
+func (b *ContractBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
+ return core.GetReceipt(b.eth.ChainDb(), txhash)
+}
+
+func (b *ContractBackend) BalanceAt(address common.Address) *big.Int {
+ currentState, err := state.New(b.eth.BlockChain().CurrentBlock().Root(), b.eth.ChainDb())
+ if err != nil {
+ return new(big.Int)
+ }
+ return currentState.GetBalance(address)
+}
+
+func (b *ContractBackend) CodeAt(address common.Address) string {
+ currentState, _ := state.New(b.eth.BlockChain().CurrentBlock().Root(), b.eth.ChainDb())
+ return common.ToHex(currentState.GetCode(address))
+}
diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go
index 743d827c76..ec339c0740 100644
--- a/eth/downloader/modes.go
+++ b/eth/downloader/modes.go
@@ -23,26 +23,4 @@ const (
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
FastSync // Quickly download the headers, full sync only at the chain head
LightSync // Download only the headers and terminate afterwards
- AdminApiName = "admin"
- BzzApiName = "bzz"
- EthApiName = "eth"
- DbApiName = "db"
- DebugApiName = "debug"
- MergedApiName = "merged"
- MinerApiName = "miner"
- NetApiName = "net"
- ShhApiName = "shh"
- TxPoolApiName = "txpool"
- PersonalApiName = "personal"
- Web3ApiName = "web3"
-
- JsonRpcVersion = "2.0"
-)
-
-var (
- // All API's
- AllApis = strings.Join([]string{
- AdminApiName, BzzApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
- ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
- }, ",")
)
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 162cf70965..5d6dcc39d7 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -18,17 +18,209 @@
package web3ext
var Modules = map[string]string{
- "admin": Admin_JS,
- "debug": Debug_JS,
- "eth": Eth_JS,
- "miner": Miner_JS,
- "net": Net_JS,
- "personal": Personal_JS,
- "rpc": RPC_JS,
- "shh": Shh_JS,
- "txpool": TxPool_JS,
+ "admin": Admin_JS,
+ "debug": Debug_JS,
+ "eth": Eth_JS,
+ "miner": Miner_JS,
+ "net": Net_JS,
+ "personal": Personal_JS,
+ "rpc": RPC_JS,
+ "shh": Shh_JS,
+ "txpool": TxPool_JS,
+ "net": Net_JS,
+ "bzz": Bzz_JS,
+ "ens": ENS_JS,
+ "chequebook": Chequebook_JS,
}
+const Bzz_JS = `
+web3._extend({
+ property: 'bzz',
+ methods:
+ [
+ new web3._extend.Method({
+ name: 'blockNetworkRead',
+ call: 'bzz_blockNetworkRead',
+ params: 1,
+ inputFormatter: [null]
+ }),
+ 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: 'retrieve',
+ call: 'bzz_retrieve',
+ params: 1,
+ inputFormatter: [null]
+ }),
+ new web3._extend.Method({
+ name: 'store',
+ call: 'bzz_store',
+ params: 2,
+ 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: 2,
+ inputFormatter: [null, null]
+ }),
+ new web3._extend.Method(
+{
+ name: 'resolve',
+ call: 'ens_resolve',
+ params: 1,
+ inputFormatter: [null]
+ }),
+ ]
+})
+`
+
+const Chequebook_JS = `
+web3._extend({
+ property: 'chequebook',
+ methods:
+ [
+ new web3._extend.Method(
+{
+ name: 'deposit',
+ call: 'chequebook_deposit',
+ }
+ out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
+ return out.Uint64(), err
+}
+
+// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
+// suggested gas price to allow a timely execution of a transaction.
+func (b *ContractBackend
+ params: 1,
+ inputFormatter: [null]
+ }),
+ new web3._extend.Propert
+y({
+ name: 'balance',
+ getter: 'chequebook_balance',
+ outputFormatter: web3._extend.utils.toDecimal
+ }),
+ new web3._extend.Method({
+ name: 'cash',
+ call: 'chequebook_cash',
+ params: 1,
+ inputFormatter: [null]
+ }),
+ new web3._extend.Method({
+ name: 'issue',
+
+ call: 'chequebook_issue',
+ params: 2,
+ inputFormatter: [null, null]
+ }),
+ ]
+});
+`
+
+const Personal_JS = `
+
+web3._extend({
+ property: 'personal',
+ methods:
+ [
+ new web3._extend.Method({
+ name: 'importRawKey',
+ call: 'personal_importRawKey',
+ params: 2
+ })
+
+ ]
+});
+`
+
+const TxPool_JS = `web3._extend({
+ property: 'txpool',
+ methods:
+ [
+ ],
+ properties:
+ [
+ new web3._extend.Property({
+ name: 'content',
+ getter: 'txpool_content'
+ }),
+ new web3._extend.Property({
+ name: 'inspect',
+ getter: 'txpool_inspect'
+ }),
+ new web3._extend.Property({
+ name: 'status',
+ getter: 'txpool_status',
+ outputFormatter: function(status) {
+ status.pending = web3._extend.utils.toDecimal(status.pending);
+ status.queued = web3._extend.utils.toDecimal(status.queued);
+ return status;
+ }
+ })
+ ]
+});
+`s
+
const Admin_JS = `
web3._extend({
property: 'admin',
@@ -134,6 +326,19 @@ web3._extend({
});
`
+const Net_JS = `
+web3._extend({
+ property: 'net',
+ methods: [],
+ properties:
+ [
+ new web3._extend.Property({
+ name: 'version',
+ getter: 'net_version'
+ })
+ ]
+});
+`
const Debug_JS = `
web3._extend({
property: 'debug',
diff --git a/rpc/api/bzz.go b/rpc/api/bzz.go
deleted file mode 100644
index c258879857..0000000000
--- a/rpc/api/bzz.go
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright 2015 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 Lesser 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 Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with go-ethereum. If not, see .
-
-package api
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/rpc/codec"
- "github.com/ethereum/go-ethereum/rpc/shared"
- "github.com/ethereum/go-ethereum/swarm"
-)
-
-const (
- BzzApiVersion = "1.0"
-)
-
-// eth api provider
-// See https://github.com/ethereum/wiki/wiki/JSON-RPC
-type bzzApi struct {
- swarm *swarm.Swarm
- methods map[string]bzzhandler
- codec codec.ApiCoder
-}
-
-// eth callback handler
-type bzzhandler func(*bzzApi, *shared.Request) (interface{}, error)
-
-var (
- bzzMapping = map[string]bzzhandler{
- "bzz_info": (*bzzApi).Info,
- "bzz_issue": (*bzzApi).Issue,
- "bzz_cash": (*bzzApi).Cash,
- "bzz_deposit": (*bzzApi).Deposit,
- "bzz_register": (*bzzApi).Register,
- "bzz_resolve": (*bzzApi).Resolve,
- "bzz_download": (*bzzApi).Download,
- "bzz_upload": (*bzzApi).Upload,
- "bzz_get": (*bzzApi).Get,
- "bzz_put": (*bzzApi).Put,
- "bzz_modify": (*bzzApi).Modify,
- }
-)
-
-func newSwarmOfflineError(method string) error {
- return shared.NewNotAvailableError(method, "swarm offline")
-}
-
-// create new bzzApi instance
-func NewBzzApi(stack *node.Node, codec codec.Codec) *bzzApi {
- var swarm *swarm.Swarm
- stack.Service(&swarm)
- return &bzzApi{swarm, bzzMapping, codec.New(nil)}
-}
-
-// collection with supported methods
-func (self *bzzApi) Methods() []string {
- methods := make([]string, len(self.methods))
- i := 0
- for k := range self.methods {
- methods[i] = k
- i++
- }
- return methods
-}
-
-// Execute given request
-func (self *bzzApi) Execute(req *shared.Request) (interface{}, error) {
- if callback, ok := self.methods[req.Method]; ok {
- return callback(self, req)
- }
-
- return nil, shared.NewNotImplementedError(req.Method)
-}
-
-func (self *bzzApi) Name() string {
- return shared.BzzApiName
-}
-
-func (self *bzzApi) ApiVersion() string {
- return BzzApiVersion
-}
-
-func (self *bzzApi) Info(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
- return s.Api().Info(), nil
-}
-
-func (self *bzzApi) Issue(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzIssueArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- cheque, err := s.Api().Issue(common.HexToAddress(args.Beneficiary), args.Amount)
- if err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- out, err := json.MarshalIndent(cheque, " ", "")
- if err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return string(out), nil
-}
-
-func (self *bzzApi) Cash(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzCashArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return s.Api().Cash(args.Cheque)
-
-}
-
-func (self *bzzApi) Deposit(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzDepositArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return s.Api().Deposit(args.Amount)
-}
-
-func (self *bzzApi) Register(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzRegisterArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- err := s.Api().Register(common.HexToAddress(args.Address), args.Domain, common.HexToHash(args.ContentHash))
- return err == nil, err
-}
-
-func (self *bzzApi) Resolve(req *shared.Request) (interface{}, error) {
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzResolveArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- key, err := s.Api().Resolve(args.Domain)
- return key.Hex(), err
-}
-
-func (self *bzzApi) Download(req *shared.Request) (interface{}, error) {
-
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzDownloadArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- err := s.Api().Download(args.BzzPath, args.LocalPath)
- return err == nil, err
-}
-
-func (self *bzzApi) Upload(req *shared.Request) (interface{}, error) {
-
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzUploadArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return s.Api().Upload(args.LocalPath, args.Index)
-}
-
-func (self *bzzApi) Get(req *shared.Request) (interface{}, error) {
-
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzGetArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- var content []byte
- var mimeType string
- var status, size int
- var err error
- content, mimeType, status, size, err = s.Api().Get(args.Path)
-
- obj := map[string]string{
- "content": string(content),
- "contentType": mimeType,
- "status": fmt.Sprintf("%v", status),
- "size": fmt.Sprintf("%v", size),
- }
-
- return obj, err
-}
-
-func (self *bzzApi) Put(req *shared.Request) (interface{}, error) {
-
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzPutArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return s.Api().Put(args.Content, args.ContenType)
-}
-
-func (self *bzzApi) Modify(req *shared.Request) (interface{}, error) {
-
- s := self.swarm
- if s == nil {
- return nil, newSwarmOfflineError(req.Method)
- }
-
- args := new(BzzModifyArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
- return nil, shared.NewDecodeParamError(err.Error())
- }
-
- return s.Api().Modify(args.RootHash, args.Path, args.ContentHash, args.ContentType)
-}
diff --git a/rpc/api/bzz_args.go b/rpc/api/bzz_args.go
deleted file mode 100644
index 2aee255e15..0000000000
--- a/rpc/api/bzz_args.go
+++ /dev/null
@@ -1,322 +0,0 @@
-// Copyright 2015 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 Lesser 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 Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with go-ethereum. If not, see .
-
-package api
-
-import (
- "encoding/json"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common/chequebook"
- "github.com/ethereum/go-ethereum/rpc/shared"
-)
-
-type BzzDepositArgs struct {
- Amount *big.Int
-}
-
-func (args *BzzDepositArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- amount, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Amount", "not a string")
- }
- args.Amount, ok = new(big.Int).SetString(amount, 10)
- if !ok {
- return shared.NewInvalidTypeError("Amount", "not a number")
- }
-
- return nil
-}
-
-type BzzCashArgs struct {
- Cheque *chequebook.Cheque
-}
-
-func (args *BzzCashArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- chequestr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Cheque", "not a string")
- }
- var cheque chequebook.Cheque
- err = json.Unmarshal([]byte(chequestr), &cheque)
- if err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
- args.Cheque = &cheque
-
- return nil
-}
-
-type BzzIssueArgs struct {
- Beneficiary string
- Amount *big.Int
-}
-
-func (args *BzzIssueArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return shared.NewInsufficientParamsError(len(obj), 2)
- }
-
- beneficiary, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Amount", "not a string")
- }
- args.Beneficiary = beneficiary
-
- amount, ok := obj[1].(string)
- if !ok {
- return shared.NewInvalidTypeError("Amount", "not a string")
- }
- args.Amount, ok = new(big.Int).SetString(amount, 10)
- if !ok {
- return shared.NewInvalidTypeError("Amount", "not a number")
- }
-
- return nil
-}
-
-type BzzRegisterArgs struct {
- Address, ContentHash, Domain string
-}
-
-func (args *BzzRegisterArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 3 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Address", "not a string")
- }
- args.Address = addstr
-
- addstr, ok = obj[1].(string)
- if !ok {
- return shared.NewInvalidTypeError("Domain", "not a string")
- }
- args.Domain = addstr
-
- addstr, ok = obj[2].(string)
- if !ok {
- return shared.NewInvalidTypeError("ContentHash", "not a string")
- }
- args.ContentHash = addstr
-
- return nil
-}
-
-type BzzResolveArgs struct {
- Domain string
-}
-
-func (args *BzzResolveArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Domain", "not a string")
- }
- args.Domain = addstr
-
- return nil
-}
-
-type BzzDownloadArgs struct {
- BzzPath, LocalPath string
-}
-
-func (args *BzzDownloadArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("BzzPath", "not a string")
- }
- args.BzzPath = addstr
-
- addstr, ok = obj[1].(string)
- if !ok {
- return shared.NewInvalidTypeError("LocalPath", "not a string")
- }
- args.LocalPath = addstr
-
- return nil
-}
-
-type BzzUploadArgs struct {
- LocalPath, Index string
-}
-
-func (args *BzzUploadArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("LocalPath", "not a string")
- }
- args.LocalPath = addstr
-
- if len(obj) > 1 {
- addstr, ok := obj[1].(string)
- if ok {
- args.Index = addstr
- }
- }
-
- return nil
-}
-
-type BzzGetArgs struct {
- Path string
-}
-
-func (args *BzzGetArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Path", "not a string")
- }
- args.Path = addstr
-
- return nil
-}
-
-type BzzPutArgs struct {
- Content, ContenType string
-}
-
-func (args *BzzPutArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("Content", "not a string")
- }
- args.Content = addstr
-
- addstr, ok = obj[1].(string)
- if !ok {
- return shared.NewInvalidTypeError("ContenType", "not a string")
- }
- args.ContenType = addstr
-
- return nil
-}
-
-type BzzModifyArgs struct {
- RootHash, Path, ContentHash, ContentType string
-}
-
-func (args *BzzModifyArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("RootHash", "not a string")
- }
- args.RootHash = addstr
-
- addstr, ok = obj[1].(string)
- if !ok {
- return shared.NewInvalidTypeError("Path", "not a string")
- }
- args.Path = addstr
-
- if len(obj) >= 4 {
- addstr, ok = obj[2].(string)
- if ok {
- args.ContentHash = addstr
- }
-
- addstr, ok = obj[3].(string)
- if ok {
- args.ContentType = addstr
- }
- }
-
- return nil
-}
diff --git a/rpc/api/bzz_js.go b/rpc/api/bzz_js.go
deleted file mode 100644
index 50bbd43d07..0000000000
--- a/rpc/api/bzz_js.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2015 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 Lesser 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 Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with go-ethereum. If not, see .
-
-package api
-
-const Bzz_JS = `
-web3._extend({
- property: 'bzz',
- methods:
- [
- new web3._extend.Method({
- name: 'deposit',
- call: 'bzz_deposit',
- params: 1,
- inputFormatter: [null]
- }),
- new web3._extend.Method({
- name: 'info',
- call: 'bzz_info',
- params: 1,
- inputFormatter: [null]
- }),
- new web3._extend.Method({
- name: 'cash',
- call: 'bzz_cash',
- params: 1,
- inputFormatter: [null]
- }),
- new web3._extend.Method({
- name: 'issue',
- call: 'bzz_issue',
- params: 2,
- inputFormatter: [null, null]
- }),
- new web3._extend.Method({
- name: 'register',
- call: 'bzz_register',
- params: 3,
- inputFormatter: [null, null, null]
- }),
- new web3._extend.Method({
- name: 'resolve',
- call: 'bzz_resolve',
- 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: '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:
- [
- ]
-});
-`
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
deleted file mode 100644
index d52f6b3dc3..0000000000
--- a/rpc/api/utils.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2015 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 .
-
-package api
-
-import (
- "strings"
-
- "fmt"
-
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/rpc/codec"
- "github.com/ethereum/go-ethereum/rpc/shared"
- "github.com/ethereum/go-ethereum/xeth"
-)
-
-var (
- // Mapping between the different methods each api supports
- AutoCompletion = map[string][]string{
- "admin": []string{
- "addPeer",
- "datadir",
- "enableUserAgent",
- "exportChain",
- "getContractInfo",
- "httpGet",
- "importChain",
- "nodeInfo",
- "peers",
- "register",
- "registerUrl",
- "saveInfo",
- "setGlobalRegistrar",
- "setHashReg",
- "setUrlHint",
- "setSolc",
- "sleep",
- "sleepBlocks",
- "startNatSpec",
- "startRPC",
- "stopNatSpec",
- "stopRPC",
- "setGlobalRegistrar",
- "setHashReg",
- "setUrlHint",
- "saveInfo",
- "getContractInfo",
- "sleep",
- "httpGet",
- "verbosity",
- },
- "bzz": []string{
- "info",
- "issue",
- "cash",
- "deposit",
- "register",
- "resolve",
- "download",
- "upload",
- "get",
- "put",
- "modify",
- },
- "db": []string{
- "getString",
- "putString",
- "getHex",
- "putHex",
- },
- "debug": []string{
- "dumpBlock",
- "getBlockRlp",
- "metrics",
- "printBlock",
- "processBlock",
- "seedHash",
- "setHead",
- },
- "eth": []string{
- "accounts",
- "blockNumber",
- "call",
- "contract",
- "coinbase",
- "compile.lll",
- "compile.serpent",
- "compile.solidity",
- "contract",
- "defaultAccount",
- "defaultBlock",
- "estimateGas",
- "filter",
- "getBalance",
- "getBlock",
- "getBlockTransactionCount",
- "getBlockUncleCount",
- "getCode",
- "getNatSpec",
- "getCompilers",
- "gasPrice",
- "getStorageAt",
- "getTransaction",
- "getTransactionCount",
- "getTransactionFromBlock",
- "getTransactionReceipt",
- "getUncle",
- "hashrate",
- "mining",
- "namereg",
- "getPendingTransactions",
- "resend",
- "sendRawTransaction",
- "sendTransaction",
- "sign",
- "syncing",
- },
- "miner": []string{
- "hashrate",
- "makeDAG",
- "setEtherbase",
- "setExtra",
- "setGasPrice",
- "startAutoDAG",
- "setEtherbase",
- "start",
- "stopAutoDAG",
- "stop",
- },
- "net": []string{
- "peerCount",
- "listening",
- },
- "personal": []string{
- "listAccounts",
- "newAccount",
- "unlockAccount",
- },
- "shh": []string{
- "post",
- "newIdentity",
- "hasIdentity",
- "newGroup",
- "addToGroup",
- "filter",
- },
- "txpool": []string{
- "status",
- },
- "web3": []string{
- "sha3",
- "version",
- "fromWei",
- "toWei",
- "toHex",
- "toAscii",
- "fromAscii",
- "toBigNumber",
- "isAddress",
- },
- }
-)
-
-// Parse a comma separated API string to individual api's
-func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, stack *node.Node) ([]shared.EthereumApi, error) {
- if len(strings.TrimSpace(apistr)) == 0 {
- return nil, fmt.Errorf("Empty apistr provided")
- }
-
- names := strings.Split(apistr, ",")
- apis := make([]shared.EthereumApi, len(names))
-
- var eth *eth.Ethereum
- if stack != nil {
- if err := stack.Service(ð); err != nil {
- return nil, err
- }
- }
- for i, name := range names {
- switch strings.ToLower(strings.TrimSpace(name)) {
- case shared.AdminApiName:
- apis[i] = NewAdminApi(xeth, stack, codec)
- case shared.BzzApiName:
- apis[i] = NewBzzApi(stack, codec)
- case shared.DebugApiName:
- apis[i] = NewDebugApi(xeth, eth, codec)
- case shared.DbApiName:
- apis[i] = NewDbApi(xeth, eth, codec)
- case shared.EthApiName:
- apis[i] = NewEthApi(xeth, eth, codec)
- case shared.MinerApiName:
- apis[i] = NewMinerApi(eth, codec)
- case shared.NetApiName:
- apis[i] = NewNetApi(xeth, eth, codec)
- case shared.ShhApiName:
- apis[i] = NewShhApi(xeth, eth, codec)
- case shared.TxPoolApiName:
- apis[i] = NewTxPoolApi(xeth, eth, codec)
- case shared.PersonalApiName:
- apis[i] = NewPersonalApi(xeth, eth, codec)
- case shared.Web3ApiName:
- apis[i] = NewWeb3Api(xeth, codec)
- case "rpc": // gives information about the RPC interface
- continue
- default:
- return nil, fmt.Errorf("Unknown API '%s'", name)
- }
- }
- return apis, nil
-}
-
-func Javascript(name string) string {
- switch strings.ToLower(strings.TrimSpace(name)) {
- case shared.AdminApiName:
- return Admin_JS
- case shared.BzzApiName:
- return Bzz_JS
- case shared.DebugApiName:
- return Debug_JS
- case shared.DbApiName:
- return Db_JS
- case shared.EthApiName:
- return Eth_JS
- case shared.MinerApiName:
- return Miner_JS
- case shared.NetApiName:
- return Net_JS
- case shared.ShhApiName:
- return Shh_JS
- case shared.TxPoolApiName:
- return TxPool_JS
- case shared.PersonalApiName:
- return Personal_JS
- }
-
- return ""
-}
diff --git a/swarm/api/api.go b/swarm/api/api.go
index 11bf4d81ec..883f226cf2 100644
--- a/swarm/api/api.go
+++ b/swarm/api/api.go
@@ -1,24 +1,16 @@
package api
import (
- "bufio"
"fmt"
"io"
- "math/big"
- "net/http"
- "os"
- "path/filepath"
"regexp"
"strings"
"sync"
- "github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/chequebook"
- "github.com/ethereum/go-ethereum/common/registrar"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/swarm/storage"
)
var (
@@ -27,68 +19,91 @@ var (
domainAndVersion = regexp.MustCompile("[@:;,]+")
)
+type Resolver interface {
+ Resolve(string) (storage.Key, error)
+}
+
/*
Api implements webserver/file system related content storage and retrieval
on top of the dpa
it is the public interface of the dpa which is included in the ethereum stack
*/
type Api struct {
- dpa *storage.DPA
- registrar registrar.VersionedRegistrar
- conf *Config
+ dpa *storage.DPA
+ dns Resolver
}
//the api constructor initialises
-func NewApi(dpa *storage.DPA, registrar registrar.VersionedRegistrar, conf *Config) (self *Api) {
- return &Api{dpa, registrar, conf}
-}
-
-// this should move over to chequebook ipc api
-func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *chequebook.Cheque, err error) {
- return self.conf.Swap.Chequebook().Issue(beneficiary, amount)
-}
-
-func (self *Api) Cash(cheque *chequebook.Cheque) (txhash string, err error) {
- return self.conf.Swap.Chequebook().Cash(cheque)
-}
-
-func (self *Api) Deposit(amount *big.Int) (txhash string, err error) {
- return self.conf.Swap.Chequebook().Deposit(amount)
-}
-
-// serialisable info about swarm
-type Info struct {
- *Config
- *chequebook.Params
-}
-
-func (self *Api) Info() *Info {
-
- return &Info{
- Config: self.conf,
- Params: chequebook.ContractParams,
- }
-
-}
-
-// Get uses iterative manifest retrieval and prefix matching
-// to resolve path to content using dpa retrieve
-func (self *Api) Get(bzzpath string) (content []byte, mimeType string, status int, size int, err error) {
- var reader storage.SectionReader
- reader, mimeType, status, err = self.getPath("/" + bzzpath)
- if err != nil {
- return
- }
- content = make([]byte, reader.Size())
- size, err = reader.Read(content)
- if err == io.EOF {
- err = nil
+func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
+ self = &Api{
+ dpa: dpa,
+ dns: dns,
}
return
}
-// Put provides singleton manifest creation and optional name registration
-// on top of dpa store
+// DPA reader API
+func (self *Api) Retrieve(key storage.Key) storage.SectionReader {
+ return self.dpa.Retrieve(key)
+}
+
+func (self *Api) Store(data storage.SectionReader, wg *sync.WaitGroup) (key storage.Key, err error) {
+ return self.dpa.Store(data, wg)
+}
+
+type ErrResolve error
+
+// DNS Resolver
+func (self *Api) Resolve(hostPort string, nameresolver bool) (contentHash storage.Key, err error) {
+ if hashMatcher.MatchString(hostPort) || self.dns == nil {
+ glog.V(logger.Detail).Infof("[BZZ] host is a contentHash: '%v'", hostPort)
+ return storage.Key(common.Hex2Bytes(hostPort)), nil
+ }
+ if !nameresolver {
+ err = fmt.Errorf("'%s' is not a content hash value.", hostPort)
+ return
+ }
+ contentHash, err = self.dns.Resolve(hostPort)
+ if err != nil {
+ err = ErrResolve(err)
+ glog.V(logger.Warn).Infof("[BZZ] DNS error : %v", err)
+ }
+ glog.V(logger.Detail).Infof("[BZZ] host lookup: %v -> %v", err)
+ return
+}
+
+func parse(uri string) (hostPort, path string) {
+ parts := slashes.Split(uri, 3)
+ var i int
+ if len(parts) == 0 {
+ return
+ }
+ // beginning with slash is now optional
+ for len(parts[i]) == 0 {
+ i++
+ }
+ hostPort = parts[i]
+ for i < len(parts)-1 {
+ i++
+ if len(path) > 0 {
+ path = path + "/" + parts[i]
+ } else {
+ path = parts[i]
+ }
+ }
+ glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
+ return
+}
+
+func (self *Api) parseAndResolve(uri string, nameresolver bool) (contentHash storage.Key, hostPort, path string, err error) {
+ hostPort, path = parse(uri)
+ //resolving host and port
+ contentHash, err = self.Resolve(hostPort, nameresolver)
+ glog.V(logger.Debug).Infof("[BZZ] Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path)
+ return
+}
+
+// Put provides singleton manifest creation on top of dpa store
func (self *Api) Put(content, contentType string) (string, error) {
sr := io.NewSectionReader(strings.NewReader(content), 0, int64(len(content)))
wg := &sync.WaitGroup{}
@@ -106,8 +121,36 @@ func (self *Api) Put(content, contentType string) (string, error) {
return key.String(), nil
}
-func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
- root := common.Hex2Bytes(rootHash)
+// Get uses iterative manifest retrieval and prefix matching
+// to resolve path to content using dpa retrieve
+// it returns a section reader, mimeType, status and an error
+func (self *Api) Get(uri string, nameresolver bool) (reader storage.SectionReader, mimeType string, status int, err error) {
+
+ key, _, path, err := self.parseAndResolve(uri, nameresolver)
+
+ trie, err := loadManifest(self.dpa, key)
+ if err != nil {
+ glog.V(logger.Warn).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
+ return
+ }
+
+ glog.V(logger.Detail).Infof("[BZZ] Swarm: getEntry(%s)", path)
+ entry, _ := trie.getEntry(path)
+ if entry != nil {
+ key = common.Hex2Bytes(entry.Hash)
+ status = entry.Status
+ mimeType = entry.ContentType
+ glog.V(logger.Detail).Infof("[BZZ] Swarm: content lookup key: '%v' (%v)", key, mimeType)
+ reader = self.dpa.Retrieve(key)
+ } else {
+ err = fmt.Errorf("manifest entry for '%s' not found", path)
+ glog.V(logger.Warn).Infof("[BZZ] Swarm: %v", err)
+ }
+ return
+}
+
+func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) {
+ root, _, path, err := self.parseAndResolve(uri, nameresolver)
trie, err := loadManifest(self.dpa, root)
if err != nil {
return
@@ -130,327 +173,3 @@ func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRoo
}
return trie.hash.String(), nil
}
-
-const maxParallelFiles = 5
-
-// Download replicates the manifest path structure on the local filesystem
-// under localpath
-func (self *Api) Download(bzzpath, localpath string) (err error) {
- lpath, err := filepath.Abs(filepath.Clean(localpath))
- if err != nil {
- return
- }
- err = os.MkdirAll(lpath, os.ModePerm)
- if err != nil {
- return
- }
-
- parts := slashes.Split(bzzpath, 3)
- if len(parts) < 2 {
- return fmt.Errorf("Invalid bzz path")
- }
- hostPort := parts[1]
- var path string
- if len(parts) > 2 {
- path = regularSlashes(parts[2]) + "/"
- }
- glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
-
- //resolving host and port
- var key storage.Key
- key, err = self.Resolve(hostPort)
- if err != nil {
- err = errResolve(err)
- glog.V(logger.Debug).Infof("[BZZ] Swarm: error : %v", err)
- return
- }
-
- trie, err := loadManifest(self.dpa, key)
- if err != nil {
- glog.V(logger.Debug).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
- return
- }
-
- type downloadListEntry struct {
- key storage.Key
- path string
- }
-
- var list []*downloadListEntry
- var mde, mderr error
-
- prevPath := lpath
- err = trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize
- key := common.Hex2Bytes(entry.Hash)
- path := lpath + "/" + suffix
- dir := filepath.Dir(path)
- if dir != prevPath {
- mde = os.MkdirAll(dir, os.ModePerm)
- if mde != nil {
- mderr = mde
- }
- prevPath = dir
- }
- if (mde == nil) && (path != dir+"/") {
- list = append(list, &downloadListEntry{key: key, path: path})
- }
- })
- if err == nil {
- err = mderr
- }
-
- cnt := len(list)
- errors := make([]error, cnt)
- done := make(chan bool, maxParallelFiles)
- dcnt := 0
-
- for i, entry := range list {
- if i >= dcnt+maxParallelFiles {
- <-done
- dcnt++
- }
- go func(i int, entry *downloadListEntry, done chan bool) {
- f, err := os.Create(entry.path) // TODO: path separators
- if err == nil {
- reader := self.dpa.Retrieve(entry.key)
- writer := bufio.NewWriter(f)
- _, err = io.CopyN(writer, reader, reader.Size()) // TODO: handle errors
- err2 := writer.Flush()
- if err == nil {
- err = err2
- }
- err2 = f.Close()
- if err == nil {
- err = err2
- }
- }
-
- errors[i] = err
- done <- true
- }(i, entry, done)
- }
- for dcnt < cnt {
- <-done
- dcnt++
- }
-
- if err != nil {
- return
- }
- for i, _ := range list {
- if errors[i] != nil {
- return errors[i]
- }
- }
- return
-}
-
-// Upload replicates a local directory as a manifest file and uploads it
-// using dpa store
-// TODO: localpath should point to a manifest
-func (self *Api) Upload(lpath, index string) (string, error) {
- var list []*manifestTrieEntry
- localpath, err := filepath.Abs(filepath.Clean(lpath))
- if err != nil {
- return "", err
- }
-
- f, err := os.Open(localpath)
- if err != nil {
- return "", err
- }
- stat, err := f.Stat()
- if err != nil {
- return "", err
- }
-
- var start int
- if stat.IsDir() {
- start = len(localpath)
- glog.V(logger.Debug).Infof("[BZZ] uploading '%s'", localpath)
- err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
- if (err == nil) && !info.IsDir() {
- //fmt.Printf("lp %s path %s\n", localpath, path)
- if len(path) <= start {
- return fmt.Errorf("Path is too short")
- }
- if path[:start] != localpath {
- return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath)
- }
- entry := &manifestTrieEntry{
- Path: path,
- }
- list = append(list, entry)
- }
- return err
- })
- if err != nil {
- return "", err
- }
- } else {
- dir := filepath.Dir(localpath)
- start = len(dir)
- if len(localpath) <= start {
- return "", fmt.Errorf("Path is too short")
- }
- if localpath[:start] != dir {
- return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir)
- }
- entry := &manifestTrieEntry{
- Path: localpath,
- }
- list = append(list, entry)
- }
-
- cnt := len(list)
- errors := make([]error, cnt)
- done := make(chan bool, maxParallelFiles)
- dcnt := 0
-
- for i, entry := range list {
- if i >= dcnt+maxParallelFiles {
- <-done
- dcnt++
- }
- go func(i int, entry *manifestTrieEntry, done chan bool) {
- f, err := os.Open(entry.Path)
- if err == nil {
- stat, _ := f.Stat()
- sr := io.NewSectionReader(f, 0, stat.Size())
- wg := &sync.WaitGroup{}
- var hash storage.Key
- hash, err = self.dpa.Store(sr, wg)
- if hash != nil {
- list[i].Hash = hash.String()
- }
- wg.Wait()
- if err == nil {
- first512 := make([]byte, 512)
- fread, _ := sr.ReadAt(first512, 0)
- if fread > 0 {
- mimeType := http.DetectContentType(first512[:fread])
- if filepath.Ext(entry.Path) == ".css" {
- mimeType = "text/css"
- }
- list[i].ContentType = mimeType
- //fmt.Printf("%v %v %v\n", entry.Path, mimeType, filepath.Ext(entry.Path))
- }
- }
- f.Close()
- }
- errors[i] = err
- done <- true
- }(i, entry, done)
- }
- for dcnt < cnt {
- <-done
- dcnt++
- }
-
- trie := &manifestTrie{
- dpa: self.dpa,
- }
- for i, entry := range list {
- if errors[i] != nil {
- return "", errors[i]
- }
- entry.Path = regularSlashes(entry.Path[start:])
- if entry.Path == index {
- ientry := &manifestTrieEntry{
- Path: "",
- Hash: entry.Hash,
- ContentType: entry.ContentType,
- }
- trie.addEntry(ientry)
- }
- trie.addEntry(entry)
- }
-
- err2 := trie.recalcAndStore()
- var hs string
- if err2 == nil {
- hs = trie.hash.String()
- }
- return hs, err2
-}
-
-func (self *Api) Register(sender common.Address, domain string, hash common.Hash) (err error) {
- domainhash := common.BytesToHash(crypto.Sha3([]byte(domain)))
-
- if self.registrar != nil {
- glog.V(logger.Debug).Infof("[BZZ] Swarm: host '%s' (hash: '%v') to be registered as '%v'", domain, domainhash.Hex(), hash.Hex())
- _, err = self.registrar.Registry().SetHashToHash(sender, domainhash, hash)
- } else {
- err = fmt.Errorf("no registry: %v", err)
- }
- return
-}
-
-type errResolve error
-
-func (self *Api) Resolve(hostPort string) (contentHash storage.Key, err error) {
- host := hostPort
- if hashMatcher.MatchString(host) {
- contentHash = storage.Key(common.Hex2Bytes(host))
- glog.V(logger.Debug).Infof("[BZZ] Swarm: host is a contentHash: '%v'", contentHash)
- } else {
- if self.registrar != nil {
- var hash common.Hash
- var version *big.Int
- parts := domainAndVersion.Split(host, 3)
- if len(parts) > 1 && parts[1] != "" {
- host = parts[0]
- version = common.Big(parts[1])
- }
- hostHash := crypto.Sha3Hash([]byte(host))
- hash, err = self.registrar.Resolver(version).HashToHash(hostHash)
- if err != nil {
- err = fmt.Errorf("unable to resolve '%s': %v", hostPort, err)
- }
- contentHash = storage.Key(hash.Bytes())
- glog.V(logger.Debug).Infof("[BZZ] Swarm: resolve host '%s' to contentHash: '%v'", hostPort, contentHash)
- } else {
- err = fmt.Errorf("no resolver '%s': %v", hostPort, err)
- }
- }
- return
-}
-
-func (self *Api) getPath(uri string) (reader storage.SectionReader, mimeType string, status int, err error) {
- parts := slashes.Split(uri, 3)
- hostPort := parts[1]
- var path string
- if len(parts) > 2 {
- path = parts[2]
- }
- glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
-
- //resolving host and port
- var key storage.Key
- key, err = self.Resolve(hostPort)
- if err != nil {
- err = errResolve(err)
- glog.V(logger.Debug).Infof("[BZZ] Swarm: error : %v", err)
- return
- }
-
- trie, err := loadManifest(self.dpa, key)
- if err != nil {
- glog.V(logger.Debug).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
- return
- }
-
- glog.V(logger.Debug).Infof("[BZZ] Swarm: getEntry(%s)", path)
- entry, _ := trie.getEntry(path)
- if entry != nil {
- key = common.Hex2Bytes(entry.Hash)
- status = entry.Status
- mimeType = entry.ContentType
- glog.V(logger.Debug).Infof("[BZZ] Swarm: content lookup key: '%v' (%v)", key, mimeType)
- reader = self.dpa.Retrieve(key)
- } else {
- err = fmt.Errorf("manifest entry for '%s' not found", path)
- glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
- }
- return
-}
diff --git a/swarm/api/api_test.go b/swarm/api/api_test.go
index 81156afcd8..0ee3ca8df7 100644
--- a/swarm/api/api_test.go
+++ b/swarm/api/api_test.go
@@ -1,205 +1,86 @@
package api
import (
- "bytes"
+ // "bytes"
"io/ioutil"
"os"
- "path"
- "runtime"
"testing"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/swarm/storage"
)
-//TODO: add tests for resolver/registrar
-// will most likely be its own package under service?
-
-var (
- testDir string
-)
-
-func init() {
- _, filename, _, _ := runtime.Caller(1)
- testDir = path.Join(path.Dir(filename), "../test")
-}
-
-func testApi() (api *Api, err error) {
+func testApi(t *testing.T, f func(*Api)) {
datadir, err := ioutil.TempDir("", "bzz-test")
if err != nil {
- return nil, err
+ t.Fatalf("unable to create temp dir: %v", err)
}
os.RemoveAll(datadir)
+ defer os.RemoveAll(datadir)
dpa, err := storage.NewLocalDPA(datadir)
if err != nil {
return
}
- prvkey, _ := crypto.GenerateKey()
+ api := NewApi(dpa, nil)
+ dpa.Start()
+ f(api)
+ dpa.Stop()
+}
- config, err := NewConfig(datadir, common.Address{}, prvkey)
- if err != nil {
- return
+type testResponse struct {
+ reader storage.SectionReader
+ *Response
+}
+
+func checkResponse(t *testing.T, resp *testResponse, exp *Response) {
+
+ if resp.MimeType != exp.MimeType {
+ t.Errorf("incorrect mimeType. expected '%s', got '%s'", exp.MimeType, resp.MimeType)
}
- api = NewApi(dpa, nil, config)
- api.dpa.Start()
+ if resp.Status != exp.Status {
+ t.Errorf("incorrect status. expected '%d', got '%d'", exp.Status, resp.Status)
+ }
+ if resp.Size != exp.Size {
+ t.Errorf("incorrect size. expected '%d', got '%d'", exp.Size, resp.Size)
+ }
+ if resp.reader != nil {
+ content := make([]byte, resp.Size)
+ read, _ := resp.reader.Read(content)
+ if int64(read) != exp.Size {
+ t.Errorf("incorrect content length. expected '%s...', got '%s...'", read, exp.Size)
+ }
+ resp.Content = string(content)
+ }
+ if resp.Content != exp.Content {
+ // if !bytes.Equal(resp.Content, exp.Content) {
+ t.Errorf("incorrect content. expected '%s...', got '%s...'", string(exp.Content), string(resp.Content))
+ }
+}
- return
+// func expResponse(content []byte, mimeType string, status int) *Response {
+func expResponse(content string, mimeType string, status int) *Response {
+ return &Response{mimeType, status, int64(len(content)), content}
+}
+
+// func testGet(t *testing.T, api *Api, bzzhash string) *testResponse {
+func testGet(t *testing.T, api *Api, bzzhash string) *testResponse {
+ reader, mimeType, status, err := api.Get(bzzhash, true)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ return &testResponse{reader, &Response{mimeType, status, reader.Size(), ""}}
+ // return &testResponse{reader, &Response{mimeType, status, reader.Size(), nil}}
}
func TestApiPut(t *testing.T) {
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- defer api.dpa.Stop()
- expContent := "hello"
- expMimeType := "text/plain"
- expStatus := 0
- expSize := len(expContent)
- bzzhash, err := api.Put(expContent, expMimeType)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- testGet(t, api, bzzhash, []byte(expContent), expMimeType, expStatus, expSize)
-}
-
-func testGet(t *testing.T, api *Api, bzzhash string, expContent []byte, expMimeType string, expStatus int, expSize int) {
- content, mimeType, status, size, err := api.Get(bzzhash)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- if !bytes.Equal(content, expContent) {
- t.Errorf("incorrect content. expected '%s...', got '%s...'", string(expContent), string(content))
- }
- if mimeType != expMimeType {
- t.Errorf("incorrect mimeType. expected '%s', got '%s'", expMimeType, mimeType)
- }
- if status != expStatus {
- t.Errorf("incorrect status. expected '%d', got '%d'", expStatus, status)
- }
- if size != expSize {
- t.Errorf("incorrect size. expected '%d', got '%d'", expSize, size)
- }
-}
-
-func TestApiDirUpload(t *testing.T) {
- t.Skip("FIXME")
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err := api.Upload(path.Join(testDir, "test0"), "")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
- testGet(t, api, path.Join(bzzhash, "index.html"), content, "text/html; charset=utf-8", 0, 202)
-
- content, err = ioutil.ReadFile(path.Join(testDir, "test0", "index.css"))
- testGet(t, api, path.Join(bzzhash, "index.css"), content, "text/css", 0, 132)
-
- content, err = ioutil.ReadFile(path.Join(testDir, "test0", "img", "logo.png"))
- testGet(t, api, path.Join(bzzhash, "img", "logo.png"), content, "image/png", 0, 18136)
-
- _, _, _, _, err = api.Get(bzzhash)
- if err == nil {
- t.Errorf("expected error: %v", err)
- }
-}
-
-func TestApiDirUploadModify(t *testing.T) {
- t.Skip("FIXME")
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err := api.Upload(path.Join(testDir, "test0"), "")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- bzzhash, err = api.Modify(bzzhash, "index.html", "", "")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err = api.Modify(bzzhash, "index2.html", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err = api.Modify(bzzhash, "img/logo.png", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
- testGet(t, api, path.Join(bzzhash, "index2.html"), content, "text/html; charset=utf-8", 0, 202)
- testGet(t, api, path.Join(bzzhash, "img", "logo.png"), content, "text/html; charset=utf-8", 0, 202)
-
- content, err = ioutil.ReadFile(path.Join(testDir, "test0", "index.css"))
- testGet(t, api, path.Join(bzzhash, "index.css"), content, "text/css", 0, 132)
-
- _, _, _, _, err = api.Get(bzzhash)
- if err == nil {
- t.Errorf("expected error: %v", err)
- }
-}
-
-func TestApiDirUploadWithRootFile(t *testing.T) {
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err := api.Upload(path.Join(testDir, "test0"), "index.html")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
- testGet(t, api, bzzhash, content, "text/html; charset=utf-8", 0, 202)
-}
-
-func TestApiFileUpload(t *testing.T) {
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err := api.Upload(path.Join(testDir, "test0", "index.html"), "")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
- testGet(t, api, path.Join(bzzhash, "index.html"), content, "text/html; charset=utf-8", 0, 202)
-}
-
-func TestApiFileUploadWithRootFile(t *testing.T) {
- api, err := testApi()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
- bzzhash, err := api.Upload(path.Join(testDir, "test0", "index.html"), "index.html")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- return
- }
-
- content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
- testGet(t, api, bzzhash, content, "text/html; charset=utf-8", 0, 202)
+ testApi(t, func(api *Api) {
+ content := "hello"
+ exp := expResponse(content, "text/plain", 0)
+ // exp := expResponse([]byte(content), "text/plain", 0)
+ bzzhash, err := api.Put(content, exp.MimeType)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ resp := testGet(t, api, bzzhash)
+ checkResponse(t, resp, exp)
+ })
}
diff --git a/swarm/api/config_test.go b/swarm/api/config_test.go
index 44bb484667..9bed72d23d 100644
--- a/swarm/api/config_test.go
+++ b/swarm/api/config_test.go
@@ -91,6 +91,7 @@ func TestConfigWriteRead(t *testing.T) {
t.Fatalf("default config file cannot be read: %v", err)
}
exp := strings.Replace(defaultConfig, "TMPDIR", tmp, -1)
+ exp = strings.Replace(exp, "\\", "\\\\", -1)
if string(data) != exp {
t.Fatalf("default config mismatch:\nexpected:\n'%v'\ngot:\n'%v'", exp, string(data))
diff --git a/swarm/api/ethereum.go b/swarm/api/ethereum.go
deleted file mode 100644
index 1027d1e768..0000000000
--- a/swarm/api/ethereum.go
+++ /dev/null
@@ -1,320 +0,0 @@
-package api
-
-import (
- "errors"
- "fmt"
- "math/big"
- "regexp"
- "sync"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/registrar"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
-)
-
-/*
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! THIS IS CURRENTLY A PLACEHOLEDER (HACK) UNTIL PRC v2
-!! https://github.com/ethereum/go-ethereum/pull/1912 is merged
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-*/
-
-var (
- defaultGasPrice = big.NewInt(10000000000000) //150000000000
- defaultGas = big.NewInt(90000) //500000
- addrReg = regexp.MustCompile(`^(0x)?[a-fA-F0-9]{40}$`)
-)
-
-type ethApi struct {
- eth *eth.Ethereum
- gpo *eth.GasPriceOracle
- transactionMu sync.RWMutex
- transactMu sync.RWMutex
- state *state.StateDB
-}
-
-func NewEthApi(ethereum *eth.Ethereum) *ethApi {
- return ðApi{
- eth: ethereum,
- gpo: eth.NewGasPriceOracle(ethereum),
- }
-}
-
-// subscribes to new head block events and
-// waits until blockchain height is greater n at any time
-// given the current head, waits for the next chain event
-// sets the state to the current head
-// loop is async and quit by closing the channel
-// used in tests and JS console debug module to control advancing private chain manually
-// Note: this is not threadsafe, only called in JS single process and tests
-func (self *ethApi) UpdateState() (wait chan *big.Int) {
- wait = make(chan *big.Int)
- self.state, _ = state.New(self.eth.BlockChain().GetBlockByNumber(0).Root(), self.eth.ChainDb())
-
- go func() {
- eventSub := self.eth.EventMux().Subscribe(core.ChainHeadEvent{})
- defer eventSub.Unsubscribe()
-
- var m, n *big.Int
- var ok bool
-
- eventCh := eventSub.Chan()
- for {
- select {
- case event, ok := <-eventCh:
- if !ok {
- // Event subscription closed, set the channel to nil to stop spinning
- eventCh = nil
- continue
- }
- // A real event arrived, process if new head block assignment
- if event, ok := event.Data.(core.ChainHeadEvent); ok {
- m = event.Block.Number()
- if n != nil && n.Cmp(m) < 0 {
- wait <- n
- n = nil
- }
- statedb, err := state.New(event.Block.Root(), self.eth.ChainDb())
- if err != nil {
- glog.V(logger.Error).Infoln("Could not create new state: %v", err)
- return
- }
- self.state = statedb
- }
- case n, ok = <-wait:
- if !ok {
- return
- }
- }
- }
- }()
- return
-}
-
-func (self *ethApi) AtStateNum(num int64) registrar.Backend {
- var st *state.StateDB
- var err error
- switch num {
- case -2:
- st = self.eth.Miner().PendingState().Copy()
- default:
- if block := self.getBlockByHeight(num); block != nil {
- st, err = state.New(block.Root(), self.eth.ChainDb())
- if err != nil {
- return nil
- }
- } else {
- st, err = state.New(self.eth.BlockChain().GetBlockByNumber(0).Root(), self.eth.ChainDb())
- if err != nil {
- return nil
- }
- }
- }
- return registrar.Backend(ðApi{
- eth: self.eth,
- state: st,
- })
-}
-func (self *ethApi) GetTxReceipt(txhash common.Hash) *types.Receipt {
- return core.GetReceipt(self.eth.ChainDb(), txhash)
-}
-
-func (self *ethApi) StorageAt(addr, storageAddr string) string {
- return self.state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
-}
-
-func (self *ethApi) CodeAt(address string) string {
- return common.ToHex(self.state.GetCode(common.HexToAddress(address)))
-}
-
-func (self *ethApi) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
- statedb := self.state.Copy()
- var from *state.StateObject
- if len(fromStr) == 0 {
- accounts, err := self.eth.AccountManager().Accounts()
- if err != nil || len(accounts) == 0 {
- from = statedb.GetOrNewStateObject(common.Address{})
- } else {
- from = statedb.GetOrNewStateObject(accounts[0].Address)
- }
- } else {
- from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
- }
-
- from.SetBalance(common.MaxBig)
-
- msg := callmsg{
- from: from,
- gas: common.Big(gasStr),
- gasPrice: common.Big(gasPriceStr),
- value: common.Big(valueStr),
- data: common.FromHex(dataStr),
- }
- if len(toStr) > 0 {
- addr := common.HexToAddress(toStr)
- msg.to = &addr
- }
-
- if msg.gas.Cmp(big.NewInt(0)) == 0 {
- msg.gas = big.NewInt(50000000)
- }
-
- if msg.gasPrice.Cmp(big.NewInt(0)) == 0 {
- msg.gasPrice = self.DefaultGasPrice()
- }
-
- header := self.CurrentBlock().Header()
- vmenv := core.NewEnv(statedb, self.eth.BlockChain(), msg, header)
- gp := new(core.GasPool).AddGas(common.MaxBig)
- res, gas, err := core.ApplyMessage(vmenv, msg, gp)
- return common.ToHex(res), gas.String(), err
-}
-
-func (self *ethApi) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
-
- if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) {
- return "", errors.New("Invalid address")
- }
-
- var (
- from = common.HexToAddress(fromStr)
- to = common.HexToAddress(toStr)
- value = common.Big(valueStr)
- gas *big.Int
- price *big.Int
- data []byte
- contractCreation bool
- )
-
- if len(gasStr) == 0 {
- gas = DefaultGas()
- } else {
- gas = common.Big(gasStr)
- }
-
- if len(gasPriceStr) == 0 {
- price = self.DefaultGasPrice()
- } else {
- price = common.Big(gasPriceStr)
- }
-
- data = common.FromHex(codeStr)
- if len(toStr) == 0 {
- contractCreation = true
- }
-
- self.transactMu.Lock()
- defer self.transactMu.Unlock()
-
- var nonce uint64
- if len(nonceStr) != 0 {
- nonce = common.Big(nonceStr).Uint64()
- } else {
- state := self.eth.TxPool().State()
- nonce = state.GetNonce(from)
- }
- var tx *types.Transaction
- if contractCreation {
- tx = types.NewContractCreation(nonce, value, gas, price, data)
- } else {
- tx = types.NewTransaction(nonce, to, value, gas, price, data)
- }
-
- signed, err := self.sign(tx, from, false)
- if err != nil {
- return "", err
- }
- if err = self.eth.TxPool().Add(signed); err != nil {
- return "", err
- }
-
- if contractCreation {
- addr := crypto.CreateAddress(from, nonce)
- glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signed.Hash().Hex(), addr.Hex())
- } else {
- glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signed.Hash().Hex(), tx.To().Hex())
- }
-
- return signed.Hash().Hex(), nil
-}
-
-func (self *ethApi) sign(tx *types.Transaction, from common.Address, didUnlock bool) (*types.Transaction, error) {
- hash := tx.SigHash()
- sig, err := self.doSign(from, hash, didUnlock)
- if err != nil {
- return tx, err
- }
- return tx.WithSignature(sig)
-}
-
-func (self *ethApi) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) {
- sig, err := self.eth.AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
- if err == accounts.ErrLocked {
- if didUnlock {
- return nil, fmt.Errorf("signer account still locked after successful unlock")
- }
- // retry signing, the account should now be unlocked.
- return self.doSign(from, hash, true)
- } else if err != nil {
- return nil, err
- }
- return sig, nil
-}
-
-func DefaultGas() *big.Int { return new(big.Int).Set(defaultGas) }
-
-func (self *ethApi) DefaultGasPrice() *big.Int {
- return self.gpo.SuggestPrice()
-}
-
-func (self *ethApi) CurrentBlock() *types.Block {
- return self.eth.BlockChain().CurrentBlock()
-}
-
-func (self *ethApi) getBlockByHeight(height int64) *types.Block {
- var num uint64
-
- switch height {
- case -2:
- return self.eth.Miner().PendingBlock()
- case -1:
- return self.CurrentBlock()
- default:
- if height < 0 {
- return nil
- }
-
- num = uint64(height)
- }
-
- return self.eth.BlockChain().GetBlockByNumber(num)
-}
-
-// callmsg is the message type used for call transations.
-type callmsg struct {
- from *state.StateObject
- to *common.Address
- gas, gasPrice *big.Int
- value *big.Int
- data []byte
-}
-
-func isAddress(addr string) bool {
- return addrReg.MatchString(addr)
-}
-
-// accessor boilerplate to implement core.Message
-func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
-func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
-func (m callmsg) To() *common.Address { return m.to }
-func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
-func (m callmsg) Gas() *big.Int { return m.gas }
-func (m callmsg) Value() *big.Int { return m.value }
-func (m callmsg) Data() []byte { return m.data }
diff --git a/swarm/api/filesystem.go b/swarm/api/filesystem.go
new file mode 100644
index 0000000000..002bb95273
--- /dev/null
+++ b/swarm/api/filesystem.go
@@ -0,0 +1,257 @@
+package api
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "path/filepath"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/swarm/storage"
+)
+
+const maxParallelFiles = 5
+
+type FileSystem struct {
+ api *Api
+}
+
+func NewFileSystem(api *Api) *FileSystem {
+ return &FileSystem{api}
+}
+
+// Upload replicates a local directory as a manifest file and uploads it
+// using dpa store
+// TODO: localpath should point to a manifest
+func (self *FileSystem) Upload(lpath, index string) (string, error) {
+ var list []*manifestTrieEntry
+ localpath, err := filepath.Abs(filepath.Clean(lpath))
+ if err != nil {
+ return "", err
+ }
+
+ f, err := os.Open(localpath)
+ if err != nil {
+ return "", err
+ }
+ stat, err := f.Stat()
+ if err != nil {
+ return "", err
+ }
+
+ var start int
+ if stat.IsDir() {
+ start = len(localpath)
+ glog.V(logger.Debug).Infof("[BZZ] uploading '%s'", localpath)
+ err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
+ if (err == nil) && !info.IsDir() {
+ //fmt.Printf("lp %s path %s\n", localpath, path)
+ if len(path) <= start {
+ return fmt.Errorf("Path is too short")
+ }
+ if path[:start] != localpath {
+ return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath)
+ }
+ entry := &manifestTrieEntry{
+ Path: path,
+ }
+ list = append(list, entry)
+ }
+ return err
+ })
+ if err != nil {
+ return "", err
+ }
+ } else {
+ dir := filepath.Dir(localpath)
+ start = len(dir)
+ if len(localpath) <= start {
+ return "", fmt.Errorf("Path is too short")
+ }
+ if localpath[:start] != dir {
+ return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir)
+ }
+ entry := &manifestTrieEntry{
+ Path: localpath,
+ }
+ list = append(list, entry)
+ }
+
+ cnt := len(list)
+ errors := make([]error, cnt)
+ done := make(chan bool, maxParallelFiles)
+ dcnt := 0
+
+ for i, entry := range list {
+ if i >= dcnt+maxParallelFiles {
+ <-done
+ dcnt++
+ }
+ go func(i int, entry *manifestTrieEntry, done chan bool) {
+ f, err := os.Open(entry.Path)
+ if err == nil {
+ stat, _ := f.Stat()
+ sr := io.NewSectionReader(f, 0, stat.Size())
+ wg := &sync.WaitGroup{}
+ var hash storage.Key
+ hash, err = self.api.dpa.Store(sr, wg)
+ if hash != nil {
+ list[i].Hash = hash.String()
+ }
+ wg.Wait()
+ if err == nil {
+ first512 := make([]byte, 512)
+ fread, _ := sr.ReadAt(first512, 0)
+ if fread > 0 {
+ mimeType := http.DetectContentType(first512[:fread])
+ if filepath.Ext(entry.Path) == ".css" {
+ mimeType = "text/css"
+ }
+ list[i].ContentType = mimeType
+ }
+ }
+ f.Close()
+ }
+ errors[i] = err
+ done <- true
+ }(i, entry, done)
+ }
+ for dcnt < cnt {
+ <-done
+ dcnt++
+ }
+
+ trie := &manifestTrie{
+ dpa: self.api.dpa,
+ }
+ for i, entry := range list {
+ if errors[i] != nil {
+ return "", errors[i]
+ }
+ entry.Path = RegularSlashes(entry.Path[start:])
+ if entry.Path == index {
+ ientry := &manifestTrieEntry{
+ Path: "",
+ Hash: entry.Hash,
+ ContentType: entry.ContentType,
+ }
+ trie.addEntry(ientry)
+ }
+ trie.addEntry(entry)
+ }
+
+ err2 := trie.recalcAndStore()
+ var hs string
+ if err2 == nil {
+ hs = trie.hash.String()
+ }
+ return hs, err2
+}
+
+// Download replicates the manifest path structure on the local filesystem
+// under localpath
+func (self *FileSystem) Download(bzzpath, localpath string) error {
+ lpath, err := filepath.Abs(filepath.Clean(localpath))
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(lpath, os.ModePerm)
+ if err != nil {
+ return err
+ }
+
+ //resolving host and port
+ key, _, path, err := self.api.parseAndResolve(bzzpath, true)
+ if err != nil {
+ return err
+ }
+ // if len(path) > 0 {
+ // path += "/"
+ // }
+
+ trie, err := loadManifest(self.api.dpa, key)
+ if err != nil {
+ glog.V(logger.Warn).Infof("[BZZ] fs.Download: loadManifestTrie error: %v", err)
+ return err
+ }
+
+ type downloadListEntry struct {
+ key storage.Key
+ path string
+ }
+
+ var list []*downloadListEntry
+ var mde, mderr error
+
+ prevPath := lpath
+ err = trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize
+ glog.V(logger.Detail).Infof("[BZZ] fs.Download: %#v", entry)
+
+ key := common.Hex2Bytes(entry.Hash)
+ path := lpath + "/" + suffix
+ dir := filepath.Dir(path)
+ if dir != prevPath {
+ mde = os.MkdirAll(dir, os.ModePerm)
+ if mde != nil {
+ mderr = mde
+ }
+ prevPath = dir
+ }
+ if (mde == nil) && (path != dir+"/") {
+ list = append(list, &downloadListEntry{key: key, path: path})
+ }
+ })
+ if err == nil {
+ err = mderr
+ }
+
+ cnt := len(list)
+ errors := make([]error, cnt)
+ done := make(chan bool, maxParallelFiles)
+ dcnt := 0
+
+ for i, entry := range list {
+ if i >= dcnt+maxParallelFiles {
+ <-done
+ dcnt++
+ }
+ go func(i int, entry *downloadListEntry, done chan bool) {
+ f, err := os.Create(entry.path) // TODO: path separators
+ if err == nil {
+ reader := self.api.dpa.Retrieve(entry.key)
+ writer := bufio.NewWriter(f)
+ _, err = io.CopyN(writer, reader, reader.Size()) // TODO: handle errors
+ err2 := writer.Flush()
+ if err == nil {
+ err = err2
+ }
+ err2 = f.Close()
+ if err == nil {
+ err = err2
+ }
+ }
+
+ errors[i] = err
+ done <- true
+ }(i, entry, done)
+ }
+ for dcnt < cnt {
+ <-done
+ dcnt++
+ }
+
+ if err != nil {
+ return err
+ }
+ for i, _ := range list {
+ if errors[i] != nil {
+ return errors[i]
+ }
+ }
+ return err
+}
diff --git a/swarm/api/filesystem_test.go b/swarm/api/filesystem_test.go
new file mode 100644
index 0000000000..e0bb8915a9
--- /dev/null
+++ b/swarm/api/filesystem_test.go
@@ -0,0 +1,176 @@
+package api
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "runtime"
+ "testing"
+)
+
+var (
+ testDir string
+ testDownloadDir string
+)
+
+func init() {
+ _, filename, _, _ := runtime.Caller(1)
+ testDir = path.Join(path.Dir(filename), "../test")
+ testDownloadDir, _ = ioutil.TempDir(os.TempDir(), "bzz-test")
+}
+
+func testFileSystem(t *testing.T, f func(*FileSystem)) {
+ testApi(t, func(api *Api) {
+ f(NewFileSystem(api))
+ })
+}
+
+func readPath(t *testing.T, parts ...string) string {
+ // func readPath(t *testing.T, parts ...string) []byte {
+ file := path.Join(parts...)
+ content, err := ioutil.ReadFile(file)
+ if err != nil {
+ t.Fatalf("unexpected error reading '%v': %v", file, err)
+ }
+ return string(content)
+}
+
+func TestApiDirUpload0(t *testing.T) {
+ // t.Skip("FIXME")
+ testFileSystem(t, func(fs *FileSystem) {
+ api := fs.api
+ bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ content := readPath(t, testDir, "test0", "index.html")
+ resp := testGet(t, api, bzzhash+"/index.html")
+ exp := expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+
+ content = readPath(t, testDir, "test0", "index.css")
+ resp = testGet(t, api, bzzhash+"/index.css")
+ exp = expResponse(content, "text/css", 0)
+ checkResponse(t, resp, exp)
+
+ content = readPath(t, testDir, "test0", "img", "logo.png")
+ resp = testGet(t, api, bzzhash+"/img/logo.png")
+ exp = expResponse(content, "image/png", 0)
+
+ _, _, _, err = api.Get(bzzhash, true)
+ if err == nil {
+ t.Fatalf("expected error: %v", err)
+ }
+
+ downloadDir := path.Join(testDownloadDir, "test0")
+ os.RemoveAll(downloadDir)
+ defer os.RemoveAll(downloadDir)
+ err = fs.Download(bzzhash, downloadDir)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ newbzzhash, err := fs.Upload(downloadDir, "")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if bzzhash != newbzzhash {
+ t.Fatalf("download %v reuploaded has incorrect hash, expected %v, got %v", downloadDir, bzzhash, newbzzhash)
+ }
+
+ })
+}
+
+func TestApiDirUploadModify(t *testing.T) {
+ // t.Skip("FIXME")
+ testFileSystem(t, func(fs *FileSystem) {
+ api := fs.api
+ bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+
+ bzzhash, err = api.Modify(bzzhash+"/index.html", "", "", true)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+ bzzhash, err = api.Modify(bzzhash+"/index2.html", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8", true)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+ bzzhash, err = api.Modify(bzzhash+"/img/logo.png", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8", true)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+
+ content := readPath(t, testDir, "test0", "index.html")
+ resp := testGet(t, api, bzzhash+"/index2.html")
+ exp := expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+
+ resp = testGet(t, api, bzzhash+"/img/logo.png")
+ exp = expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+
+ content = readPath(t, testDir, "test0", "index.css")
+ resp = testGet(t, api, bzzhash+"/index.css")
+ exp = expResponse(content, "text/css", 0)
+
+ _, _, _, err = api.Get(bzzhash, true)
+ if err == nil {
+ t.Errorf("expected error: %v", err)
+ }
+ })
+}
+
+func TestApiDirUploadWithRootFile(t *testing.T) {
+ testFileSystem(t, func(fs *FileSystem) {
+ api := fs.api
+ bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "index.html")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+
+ content := readPath(t, testDir, "test0", "index.html")
+ resp := testGet(t, api, bzzhash)
+ exp := expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+ })
+}
+
+func TestApiFileUpload(t *testing.T) {
+ testFileSystem(t, func(fs *FileSystem) {
+ api := fs.api
+ bzzhash, err := fs.Upload(path.Join(testDir, "test0", "index.html"), "")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+
+ content := readPath(t, testDir, "test0", "index.html")
+ resp := testGet(t, api, bzzhash+"/index.html")
+ exp := expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+ })
+}
+
+func TestApiFileUploadWithRootFile(t *testing.T) {
+ testFileSystem(t, func(fs *FileSystem) {
+ api := fs.api
+ bzzhash, err := fs.Upload(path.Join(testDir, "test0", "index.html"), "index.html")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ return
+ }
+
+ content := readPath(t, testDir, "test0", "index.html")
+ resp := testGet(t, api, bzzhash)
+ exp := expResponse(content, "text/html; charset=utf-8", 0)
+ checkResponse(t, resp, exp)
+ })
+}
diff --git a/swarm/api/http/roundtripper.go b/swarm/api/http/roundtripper.go
new file mode 100644
index 0000000000..69c501e5be
--- /dev/null
+++ b/swarm/api/http/roundtripper.go
@@ -0,0 +1,53 @@
+package http
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+)
+
+/*
+http roundtripper to register for bzz url scheme
+see https://github.com/ethereum/go-ethereum/issues/2040
+Usage:
+
+import (
+ "github.com/ethereum/go-ethereum/common/httpclient"
+ "github.com/ethereum/go-ethereum/swarm/api/http"
+)
+client := httpclient.New()
+// for (private) swarm proxy running locally
+client.RegisterScheme("bzz", &http.RoundTripper{Port: port})
+client.RegisterScheme("bzzi", &http.RoundTripper{Port: port})
+client.RegisterScheme("bzzr", &http.RoundTripper{Port: port})
+
+The port you give the Roundtripper is the port the swarm proxy is listening on.
+If Host is left empty, localhost is assumed.
+
+Using a public gateway, the above few lines gives you the leanest
+bzz-scheme aware read-only http client. You really only ever need this
+if you need go-native swarm access to bzz addresses, e.g.,
+github.com/ethereum/go-ethereum/common/natspec
+
+*/
+
+type RoundTripper struct {
+ Host string
+ Port string
+}
+
+func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+ host := self.Host
+ if len(host) == 0 {
+ host = "localhost"
+ }
+ url := fmt.Sprintf("http://%s:%s/%s:/%s/%s", host, self.Port, req.Proto, req.URL.Host, req.URL.Path)
+ glog.V(logger.Info).Infof("[BZZ] roundtripper: proxying request '%s' to '%s'", req.RequestURI, url)
+ reqProxy, err := http.NewRequest(req.Method, url, req.Body)
+ if err != nil {
+ return nil, err
+ }
+ return http.DefaultClient.Do(reqProxy)
+}
diff --git a/swarm/api/roundtripper_test.go b/swarm/api/http/roundtripper_test.go
similarity index 86%
rename from swarm/api/roundtripper_test.go
rename to swarm/api/http/roundtripper_test.go
index e0b15658dc..1bed107887 100644
--- a/swarm/api/roundtripper_test.go
+++ b/swarm/api/http/roundtripper_test.go
@@ -1,4 +1,4 @@
-package api
+package http
import (
"io/ioutil"
@@ -22,7 +22,7 @@ func TestRoundTripper(t *testing.T) {
})
go http.ListenAndServe(":8600", serveMux)
- rt := &RoundTripper{"8600"}
+ rt := &RoundTripper{Port: "8600"}
client := httpclient.New("/")
client.RegisterProtocol("bzz", rt)
@@ -43,8 +43,8 @@ func TestRoundTripper(t *testing.T) {
t.Errorf("expected no error, got %v", err)
return
}
- if string(content) != "/test.com/path" {
- t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/test.com/path", string(content))
+ if string(content) != "/HTTP/1.1:/test.com/path" {
+ t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/HTTP/1.1:/test.com/path", string(content))
}
}
diff --git a/swarm/api/http.go b/swarm/api/http/server.go
similarity index 80%
rename from swarm/api/http.go
rename to swarm/api/http/server.go
index b1cd039cce..6ac2b113e6 100644
--- a/swarm/api/http.go
+++ b/swarm/api/http/server.go
@@ -1,7 +1,7 @@
/*
A simple http server interface to Swarm
*/
-package api
+package http
import (
"bytes"
@@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/swarm/api"
)
const (
@@ -21,8 +22,8 @@ const (
)
var (
- bzzPrefix = regexp.MustCompile("^/+bzz:/+")
- rawUrl = regexp.MustCompile("^/+raw/*")
+ // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw)
+ bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+")
trailingSlashes = regexp.MustCompile("/+$")
// forever = func() time.Time { return time.Unix(0, 0) }
forever = time.Now
@@ -41,7 +42,7 @@ type sequentialReader struct {
// https://github.com/atom/electron/blob/master/docs/api/protocol.md
// starts up http server
-func StartHttpServer(api *Api, port string) {
+func StartHttpServer(api *api.Api, port string) {
serveMux := http.NewServeMux()
serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
handler(w, r, api)
@@ -50,7 +51,7 @@ func StartHttpServer(api *Api, port string) {
glog.V(logger.Info).Infof("[BZZ] Swarm HTTP proxy started on localhost:%s", port)
}
-func handler(w http.ResponseWriter, r *http.Request, api *Api) {
+func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
requestURL := r.URL
// This is wrong
// if requestURL.Host == "" {
@@ -61,24 +62,41 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
// return
// }
// }
- glog.V(logger.Debug).Infof("[BZZ] Swarm: HTTP request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
+ glog.V(logger.Debug).Infof("[BZZ] Swarm: HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
uri := requestURL.Path
- var raw bool
+ var raw, nameresolver bool
+ var proto string
// HTTP-based URL protocol handler
- uri = bzzPrefix.ReplaceAllString(uri, "")
glog.V(logger.Debug).Infof("[BZZ] Swarm: BZZ request URI: '%s'", uri)
- path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
- raw = true
+ path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string {
+ proto = p
return ""
})
- glog.V(logger.Debug).Infof("[BZZ] Swarm: %s request '%s' received.", r.Method, uri)
+ // protocol identification (ugly)
+ if proto == "" {
+ if glog.V(logger.Error) {
+ glog.Errorf(
+ "[BZZ] Swarm: Protocol error in request `%s`.",
+ uri,
+ )
+ http.Error(w, "BZZ protocol error", http.StatusBadRequest)
+ return
+ }
+ }
+ raw = proto[1:5] == "bzzr"
+ nameresolver = proto[1:5] != "bzzi"
+
+ glog.V(logger.Debug).Infof(
+ "[BZZ] Swarm: %s request over protocol %s '%s' received.",
+ r.Method, proto, path,
+ )
switch {
case r.Method == "POST" || r.Method == "PUT":
- key, err := api.dpa.Store(io.NewSectionReader(&sequentialReader{
+ key, err := a.Store(io.NewSectionReader(&sequentialReader{
reader: r.Body,
ahead: make(map[int64]chan bool),
}, 0, r.ContentLength), nil)
@@ -102,11 +120,11 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest)
return
} else {
- path = regularSlashes(path)
+ path = api.RegularSlashes(path)
mime := r.Header.Get("Content-Type")
// TODO proper root hash separation
glog.V(logger.Debug).Infof("[BZZ] Modify '%s' to store %v as '%s'.", path, key.Log(), mime)
- newKey, err := api.Modify(path[:64], path[65:], common.Bytes2Hex(key), mime)
+ newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver)
if err == nil {
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
w.Header().Set("Content-Type", "text/plain")
@@ -122,9 +140,9 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest)
return
} else {
- path = regularSlashes(path)
+ path = api.RegularSlashes(path)
glog.V(logger.Debug).Infof("[BZZ] Delete '%s'.", path)
- newKey, err := api.Modify(path[:64], path[65:], "", "")
+ newKey, err := a.Modify(path, "", "", nameresolver)
if err == nil {
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
w.Header().Set("Content-Type", "text/plain")
@@ -138,7 +156,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
path = trailingSlashes.ReplaceAllString(path, "")
if raw {
// resolving host
- key, err := api.Resolve(path)
+ key, err := a.Resolve(path, nameresolver)
if err != nil {
glog.V(logger.Error).Infof("[BZZ] Swarm: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
@@ -146,7 +164,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
}
// retrieving content
- reader := api.dpa.Retrieve(key)
+ reader := a.Retrieve(key)
glog.V(logger.Debug).Infof("[BZZ] Swarm: Reading %d bytes.", reader.Size())
// setting mime type
@@ -165,10 +183,9 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
glog.V(logger.Debug).Infof("[BZZ] Swarm: Structured GET request '%s' received.", uri)
- // call to api.getPath on uri
- reader, mimeType, status, err := api.getPath(path)
+ reader, mimeType, status, err := a.Get(path, nameresolver)
if err != nil {
- if _, ok := err.(errResolve); ok {
+ if _, ok := err.(api.ErrResolve); ok {
glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
status = http.StatusBadRequest
} else {
diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go
index c1781ab7d3..0337382ff0 100644
--- a/swarm/api/manifest.go
+++ b/swarm/api/manifest.go
@@ -145,7 +145,10 @@ func (self *manifestTrie) deleteEntry(path string) {
b := byte(path[0])
entry := self.entries[b]
- if (entry != nil) && (entry.Path == path) {
+ if entry == nil {
+ return
+ }
+ if entry.Path == path {
self.entries[b] = nil
return
}
@@ -290,7 +293,7 @@ func (self *manifestTrie) findPrefixOf(path string) (entry *manifestTrieEntry, p
// file system manifest always contains regularized paths
// no leading or trailing slashes, only single slashes inside
-func regularSlashes(path string) (res string) {
+func RegularSlashes(path string) (res string) {
for i := 0; i < len(path); i++ {
if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) {
res = res + path[i:i+1]
@@ -303,7 +306,7 @@ func regularSlashes(path string) (res string) {
}
func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) {
- path := regularSlashes(spath)
+ path := RegularSlashes(spath)
var pos int
entry, pos = self.findPrefixOf(path)
return entry, path[:pos]
diff --git a/swarm/api/roundtripper.go b/swarm/api/roundtripper.go
deleted file mode 100644
index 3dad7d04ef..0000000000
--- a/swarm/api/roundtripper.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package api
-
-import (
- "fmt"
- "net/http"
-
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
-
- // "github.com/ethereum/go-ethereum/common/httpclient"
- // "github.com/ethereum/go-ethereum/jsre"
-)
-
-type RoundTripper struct {
- Port string
-}
-
-func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
- url := fmt.Sprintf("http://localhost:%s/%s/%s", self.Port, req.URL.Host, req.URL.Path)
- glog.V(logger.Info).Infof("[BZZ] roundtripper: proxying request '%s' to '%s'", req.RequestURI, url)
- return http.Get(url)
-}
diff --git a/swarm/api/storage.go b/swarm/api/storage.go
new file mode 100644
index 0000000000..97b5590fdf
--- /dev/null
+++ b/swarm/api/storage.go
@@ -0,0 +1,48 @@
+package api
+
+type Response struct {
+ MimeType string
+ Status int
+ Size int64
+ // Content []byte
+ Content string
+}
+
+// implements a service
+type Storage struct {
+ api *Api
+}
+
+func NewStorage(api *Api) *Storage {
+ return &Storage{api}
+}
+
+// Put uploads the content to the swarm with a simple manifest speficying
+// its content type
+func (self *Storage) Put(content, contentType string) (string, error) {
+ return self.api.Put(content, contentType)
+}
+
+// Get retrieves the content from bzzpath and reads the response in full
+// It returns the Response object, which serialises containing the
+// response body as the value of the Content field
+// NOTE: if error is non-nil, sResponse may still have partial content
+// the actual size of which is given in len(resp.Content), while the expected
+// size is resp.Size
+func (self *Storage) Get(bzzpath string) (*Response, error) {
+ reader, mimeType, status, err := self.api.Get(bzzpath, true)
+ if err != nil {
+ return nil, err
+ }
+ expsize := reader.Size()
+ body := make([]byte, expsize)
+ size, err := reader.Read(body)
+ if int64(size) == expsize {
+ err = nil
+ }
+ return &Response{mimeType, status, expsize, string(body[:size])}, err
+}
+
+func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
+ return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true)
+}
diff --git a/swarm/api/storage_test.go b/swarm/api/storage_test.go
new file mode 100644
index 0000000000..44fa7636dc
--- /dev/null
+++ b/swarm/api/storage_test.go
@@ -0,0 +1,33 @@
+package api
+
+import (
+ "testing"
+)
+
+func testStorage(t *testing.T, f func(*Storage)) {
+ testApi(t, func(api *Api) {
+ f(NewStorage(api))
+ })
+}
+
+func TestStoragePutGet(t *testing.T) {
+ testStorage(t, func(api *Storage) {
+ content := "hello"
+ exp := expResponse(content, "text/plain", 0)
+ // exp := expResponse([]byte(content), "text/plain", 0)
+ bzzhash, err := api.Put(content, exp.MimeType)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ // to check put against the Api#Get
+ resp0 := testGet(t, api.api, bzzhash)
+ checkResponse(t, resp0, exp)
+
+ // check storage#Get
+ resp, err := api.Get(bzzhash)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ checkResponse(t, &testResponse{nil, resp}, exp)
+ })
+}
diff --git a/swarm/api/testapi.go b/swarm/api/testapi.go
new file mode 100644
index 0000000000..7e7229bbc8
--- /dev/null
+++ b/swarm/api/testapi.go
@@ -0,0 +1,30 @@
+package api
+
+import (
+ "github.com/ethereum/go-ethereum/swarm/network"
+)
+
+type Control struct {
+ api *Api
+ hive *network.Hive
+}
+
+func NewControl(api *Api, hive *network.Hive) *Control {
+ return &Control{api, hive}
+}
+
+func (self *Control) BlockNetworkRead(on bool) {
+ self.hive.BlockNetworkRead(on)
+}
+
+func (self *Control) SyncEnabled(on bool) {
+ self.hive.SyncEnabled(on)
+}
+
+func (self *Control) SwapEnabled(on bool) {
+ self.hive.SwapEnabled(on)
+}
+
+func (self *Control) Hive() string {
+ return self.hive.String()
+}
diff --git a/swarm/cmd/bzzup.sh b/swarm/cmd/bzzup.sh
index dc6490ad70..099a5057af 100755
--- a/swarm/cmd/bzzup.sh
+++ b/swarm/cmd/bzzup.sh
@@ -1,17 +1,19 @@
#! /bin/bash
INDEX='index.html'
-port="8500"
+proxy="http://localhost:8500"
delimiter='{"entries":[{'
+
if [[ ! -z "$2" ]]; then
- port="$2"
+ proxy="$2"
fi
if [ -f "$1" ]; then
-hash=`wget -q -O- --post-file="$1" http://localhost:$port/raw`
+hash=`wget -q -O- --post-file="$1" $proxy/bzzr:/`
mime=`mimetype -b "$1"`
-wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" http://localhost:8500/raw
+# echo wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" $proxy/bzzr:/
+wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" $proxy/bzzr:/
echo
else
@@ -28,8 +30,11 @@ do
name=`echo "$path" | cut -c3-`
[ _`basename "$name"` = "_$INDEX" ] && name=`dirname "$name"`
echo -n "$delimiter"
-hash=`wget -q -O- --post-file="$path" http://localhost:$port/raw`
+hash=`wget -q -O- --post-file="$path" $proxy/bzzr:/`
mime=`mimetype -b "$path"`
+if [ "$mime" = "text/plain" ]; then
+ echo -n $path|grep -q '.css' && mime="text/css"
+fi
if [ "_$name" = '_.' ]; then
echo -n "\"hash\":\"$hash\",\"contentType\":\"$mime\""
else
@@ -38,7 +43,7 @@ fi
delimiter='},{'
done
-echo -n '}]}') | wget -q -O- --post-data=`cat` http://localhost:$port/raw
+echo -n '}]}') | wget -q -O- --post-data=`cat` $proxy/bzzr:/
echo
popd > /dev/null
diff --git a/swarm/cmd/swarm/gethup.sh b/swarm/cmd/swarm/gethup.sh
new file mode 100644
index 0000000000..e9b93f69bf
--- /dev/null
+++ b/swarm/cmd/swarm/gethup.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+# Usage:
+# bash /path/to/eth-utils/gethup.sh
+
+root=$1 # base directory to use for datadir and logs
+shift
+id=$1 # double digit instance id like 00 01 02
+shift
+ip_addr=$1 # ip address to substitute
+shift
+
+# logs are output to a date-tagged file for each run , while a link is
+# created to the latest, so that monitoring be easier with the same filename
+# TODO: use this if GETH not set
+# GETH=geth
+# echo "ls -l $GETH"
+# ls -l $GETH
+
+# geth CLI params e.g., (dd=04, run=09)
+datetag=`date "+%c%y%m%d-%H%M%S"|cut -d ' ' -f 5`
+datadir=$root/data/$id # /tmp/eth/04
+log=$root/log/$id.$datetag.log # /tmp/eth/04.09.log
+linklog=$root/log/$id.current.log # /tmp/eth/04.09.log
+stablelog=$root/log/$id.log # /tmp/eth/04.09.log
+password=$id # 04
+port=303$id # 34504
+bzzport=322$id # 32204
+rpcport=302$id # 3204
+
+mkdir -p $root/data
+mkdir -p $root/enodes
+mkdir -p $root/pids
+mkdir -p $root/log
+ln -sf "$log" "$linklog"
+# if we do not have an account, create one
+# will not prompt for password, we use the double digit instance id as passwd
+# NEVER EVER USE THESE ACCOUNTS FOR INTERACTING WITH A LIVE CHAIN
+keystoredir="$datadir/keystore/"
+# echo "KeyStore dir: $keystoredir"
+if [ ! -d "$keystoredir" ]; then
+ # echo "create an account with password $id [DO NOT EVER USE THIS ON LIVE]"
+ # mkdir -p $datadir/keystore
+ $GETH --datadir $datadir --password <(echo -n $id) account new >/dev/null 2>&1
+ # create account with password 00, 01, ...
+ # note that the account key will be stored also separately outside
+ # datadir
+ # this way you can safely clear the data directory and still keep your key
+ # under `/keystore/dd
+ # LS=`ls $datadir/keystore`
+ # echo $LS
+ while [ ! -d "$keystoredir" ]; do
+ echo "."
+ ((i++))
+ if ((i>10)); then break; fi
+ sleep 1
+ done
+ # echo "copying keys $datadir/keystore $root/keystore/$id"
+ mkdir -p $root/keystore/$id
+ cp -R "$datadir/keystore/" $root/keystore/$id
+fi
+
+# # mkdir -p $datadir/keystore
+# if [ ! -d "$datadir/keystore" ]; then
+# echo "copying keys $root/keystore/$id $datadir/keystore"
+# cp -R $root/keystore/$id/keystore/ $datadir/keystore/
+# fi
+
+
+# query node's enode url
+if [ $ip_addr="" ]; then
+ pattern='\d+\.\d+\.\d+\.\d+'
+ ip_addr="[::]"
+else
+ pattern='\[\:\:\]'
+fi
+
+geth="$GETH --datadir $datadir --port $port"
+
+# echo -n "enode for instance $id... "
+if [ ! "$GETH" = "" ] && [ ! -f $root/enodes/$id.enode ]; then
+ cmd="$geth js <(echo 'console.log(admin.nodeInfo.enode); exit();') "
+ # echo $cmd '2>/dev/null |grep enode | perl -pe "s/'$pattern'/'$ip_addr'/g" | perl -pe "s/^/\"/; s/\s*$/\"/;" > '$root/enodes/$id.enode
+ eval $cmd 2>/dev/null |grep enode | perl -pe "s/$pattern/$ip_addr/g" | perl -pe "s/^/\"/; s/\s*\$/\"/;" > $root/enodes/$id.enode
+fi
+# cat $root/enodes/$id.enode
+echo
+
+# copy cluster enodes list to node's static node list
+# echo "copy cluster enodes list to node's static node list"
+if [ -f $root/enodes.all ]; then
+ cp $root/enodes.all $datadir/static-nodes.json
+fi
+
+if [ ! -f $root/pids/$id.pid ]; then
+ # bring up node `dd` (double digit)
+ # - using /
+ # - listening on port 303dd, (like 30300, 30301, ...)
+ # - with the account unlocked
+ # - launching json-rpc server on port 81dd (like 8100, 8101, 8102, ...)
+ # echo "BZZKEY=$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'"
+ BZZKEY=`$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'`
+ echo -n "starting instance $id ($BZZKEY @ $datadir )..."
+ # echo "$geth \
+ # --identity=$id \
+ # --bzzaccount=$BZZKEY --bzzport=$bzzport \
+ # --unlock=$BZZKEY \
+ # --password=<(echo -n $id) \
+ # --rpc --rpcport=$rpcport --rpccorsdomain='*' $* \
+ # 2>&1 | tee "$stablelog" > "$log" & # comment out if you pipe it to a tty etc.
+ # " >&2
+
+ if [ -f $log ] && [ -f $root/stablelog ]; then
+ cp $stablelog `cat $root/prevlog`
+ fi
+ echo $log > $root/prevlog
+
+ $GETH --datadir=$datadir \
+ --identity=$id \
+ --bzzaccount=$BZZKEY --bzzport=$bzzport \
+ --port=$port \
+ --unlock=$BZZKEY \
+ --password=<(echo -n $id) \
+ --rpc --rpcport=$rpcport --rpccorsdomain='*' $* \
+ > "$stablelog" 2>&1 & # comment out if you pipe it to a tty etc.
+
+ # wait until ready
+ # pid=`ps auxwww|grep geth|grep "ty=$id"|grep -v grep|awk '{print $2}'`
+ # echo "pid: $pid"
+ # ps auxwww|grep geth|grep "ty=$id"|grep -v grep
+ # echo $pid > $root/pids/$id.pid
+ #echo $! > $root/pids/$id.pid
+ while true; do
+ $GETH --exec="net" attach ipc:$datadir/geth.ipc > /dev/null 2>&1 && break
+ sleep 1
+ echo -n "."
+ if ((i++>10)); then
+ echo "instance $id failed to start"
+ exit 1
+ fi
+ done
+ echo -n "started - "
+ pid=`ps auxwww|grep geth|grep "ty=$id"|grep -v grep|awk '{print $2}'`
+ echo "pid: $pid"
+ # ps auxwww|grep geth|grep "ty=$id"|grep -v grep
+ echo $pid > $root/pids/$id.pid
+fi
+
+# to bring up logs, uncomment
+# tail -f $log
diff --git a/swarm/cmd/swarm/swarm.sh b/swarm/cmd/swarm/swarm.sh
new file mode 100644
index 0000000000..d150e25597
--- /dev/null
+++ b/swarm/cmd/swarm/swarm.sh
@@ -0,0 +1,316 @@
+# !/bin/bash
+# bash cluster [[params]...]
+# https://github.com/ethereum/go-ethereum/wiki/Setting-up-monitoring-on-local-cluster
+
+# sets up a local ethereum network cluster of nodes
+# - is the number of nodes in cluster
+# - is the root directory for the cluster, the nodes are set up
+# with datadir `//00`, `/ /01`, ...
+# - new accounts are created for each node
+# - they launch on port 30300, 30301, ...
+# - they star rpc on port 8100, 8101, ...
+# - by collecting the nodes nodeUrl, they get connected to each other
+# - if enode has no IP, `` is substituted
+# - if `` is not 0, they will not connect to a default client,
+# resulting in a private isolated network
+# - the nodes log into `//00..log`, `//01..log`, ...
+# - The nodes launch in mining mode
+# - the cluster can be killed with `killall geth` (FIXME: should record PIDs)
+# and restarted from the same state
+# - if you want to interact with the nodes, use rpc
+# - you can supply additional params on the command line which will be passed
+# to each node, for instance `-mine`
+
+if [ "$GETH" = "" ]; then
+ echo "env var GETH not set "
+ exit 1
+fi
+
+srcdir=`dirname $0`
+
+root=$1
+shift
+network_id=$1
+shift
+cmd=$1
+shift
+# ip_addr=`curl ipecho.net/plain 2>/dev/null;echo `
+
+# echo "external IP: $ip_addr"
+swarmoptions='--dev --maxpeers=20 --shh=false --nodiscover'
+tmpdir=/tmp
+
+function attach {
+ id=$1
+ shift
+ echo "attaching console to instance $id"
+ cmd="$GETH $* attach ipc:$root/$network_id/data/$id/geth.ipc"
+ # echo $cmd
+ eval $cmd
+}
+
+function log {
+ id=$1
+ shift
+ echo "streaming logs for instance $id"
+ cmd="tail -f $root/$network_id/log/$id.log"
+ echo $cmd
+ eval $cmd
+}
+
+function less {
+ id=$1
+ shift
+ echo "viewing logs for instance $id"
+ cmd="/usr/bin/less $root/$network_id/log/$id.log"
+ echo $cmd
+ eval $cmd
+}
+
+function start {
+ id=$1
+ shift
+ # echo -n "starting instance $id - "
+ cmd="bash $srcdir/gethup.sh $root/$network_id/ $id '$ip_addr' --networkid=$network_id $swarmoptions $*"
+ # echo "pid="`cat $root/$network_id/pids/$id.pid`
+ # echo $cmd
+ eval $cmd
+}
+
+function stop {
+ id=$1
+ shift
+ if [ $id = "all" ]; then
+ procs=`cat $root/$network_id/pids/*.pid 2>/dev/null |perl -pe 's/^\s+//;s/\s+\\$//;s/\s+/\n/g'`
+ # echo "stopping processes $procs"
+ for p in $procs; do
+ shutdown $p
+ done
+ rm -rf $root/$network_id/pids/*
+ else
+ pid=$root/$network_id/pids/$id.pid
+ if [ -f $pid ]; then
+ echo "stopping instance $id, pid="`cat $pid`
+ shutdown `cat $pid`
+ rm $pid
+ fi
+ fi
+ # ps auxwww|grep geth|grep bzz|grep -v grep
+}
+
+function shutdown {
+ echo -n "stopping $1..."
+ kill -2 $1
+ while true ;do
+ ps auxwww|grep geth|grep -v grep|awk '{print $2}'|grep -ql $1 || break
+ sleep 1
+ # ps auxwww|grep geth|grep -v grep |grep $1 #|awk '{print $2}'
+ done
+ echo "stopped"
+}
+
+function restart {
+ id=$1
+ shift
+ stop $id
+ start $id $*
+}
+
+function init {
+ stop all
+ killall geth
+ reset all
+ cluster $*
+ stop all
+ cluster $*
+}
+
+function reset {
+ id=$1
+ shift
+ if [ $id = "all" ]; then
+ rm -rf $root/$network_id
+ else
+ rm -rf$root/$network_id/*/$id*
+ fi
+
+}
+
+function cluster {
+ N=$1
+ shift
+ echo "launching cluster of $N instances"
+ # cmd="bash $srcdir/gethcluster.sh $root $network_id $N '' $swarmoptions $*"
+ # echo $cmd
+ # eval $cmd
+ dir=$root/$network_id
+ mkdir -p $dir/data
+ mkdir -p $dir/enodes
+ mkdir -p $dir/pids
+ mkdir -p $dir/log
+
+ enodes=$dir/enodes.all
+ rm -f $enodes
+ # build a static nodes(-like) list of all enodes of the local cluster
+ echo "[" >> $enodes
+ for ((i=0;i> $enodes
+ echo "," >> $enodes
+ fi
+ done
+ echo "\"\"]" >> $enodes
+
+ for ((i=0;i tail -f $dir/log/$id.log"
+ start $id $vmodule $*
+ done
+}
+
+
+function needs {
+ id=$1
+ keyfile=$2
+ target=$3
+ dir=`dirname $3`
+ dest=$tmpdir/down
+ mkdir -p $dest
+ file=$dest/`basename $target`
+ rm -f $file
+ echo -n "waiting for root hash in '$keyfile'..."
+ while true; do
+ if [ -f $keyfile ] && [ ! -z $keyfile ]; then
+ break
+ fi
+ sleep 1
+ echo -n "."
+ done
+ key=`cat $keyfile|tr -d \"`
+ echo " => $key"
+ download $id $key $dest && cmp --silent $file $target && echo "PASS" || echo "FAIL"
+ # && ls -l $keyfile $file $target
+}
+
+
+function up { #port, file
+ echo "Upload file '$2' to node $1... " 1>&2
+ file=`basename $2`
+ attach $1 "--exec 'bzz.upload(\"$2\", \"$file\")'"|tail -n1> /tmp/key
+ # key=`bash swarm/cmd/bzzup.sh $2 86$1`
+ cat /tmp/key
+}
+
+function download {
+ echo "download '$2' from node $1 to '$3'"
+ # echo attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'"
+ attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'" > /dev/null
+}
+
+
+function down {
+ echo -n "Download hash '$2' from node $1... "
+ # echo "wget -O- http://localhost:86$1/$2 > /dev/null 2>&1 && echo 'got it' || echo 'not found'"
+ # wget -O- http://localhost:86$1/$2 > /dev/null 2>&1 && echo "got it" || echo "not found"
+ while true; do
+ attach $1 "--exec 'bzz.get(\"$2\")'" 2> /dev/null |grep -qil "status" && break
+ sleep 1
+ echo -n "."
+ if ((i++>10)); then
+ echo "not found"
+ return
+ fi
+ done
+ echo "found OK"
+}
+
+function clean { #index
+ echo "Clean up for $1"
+ rm -rf $root/$network_id/data/$1/{bzz/*/chunks,bzz/*/requests/,bzz/*/bzz-peers.json,chaindata,nodes}
+}
+
+function info {
+ echo "swarm node information"
+ echo "ROOTDIR: $root"
+ echo "DATADIR: $root/$network_id/data/$1"
+ echo "LOGFILE: $root/$network_id/log/$1.log"
+ echo "HTTPAPI: http://localhost:322$1"
+ echo "ETHPORT: 303$1"
+ echo "RPCPORT: 302$1"
+ echo "ACCOUNT:" 0x`ls -1 $root/$network_id/data/$1/bzz`
+ echo "CHEQUEB:" `cat $root/$network_id/data/$1/bzz/*/config.json|grep Contract|awk -F\" '{print $4}'`
+ echo "ROOTDIR: $root"
+ echo "DATADIR: $root/$network_id/data/$1"
+ echo "LOGFILE: $root/$network_id/log/$1.log"
+}
+
+
+function status {
+ attach 00 -exec "'console.log(eth.getBalance(eth.accounts[0])); console.log(eth.getBalance(bzz.info().Swap.Contract)); console.log(chequebook.balance)'"
+}
+
+function netstatconf {
+ begin=$1
+ N=$2
+ name_prefix=$3
+ ws_server=$4
+ ws_secret=$5
+ conf="$root/$network_id/$name_prefix.netstat.json"
+
+ echo "writing netstat conf for cluster $name_prefix to $conf"
+
+ echo -e "[" > $conf
+
+ for ((i=$begin;i<$start+$N;++i)); do
+ id=`printf "%02d" $i`
+ single_template=" {\n \"name\" : \"$name_prefix-$i\",\n \"cwd\" : \".\",\n \"script\" : \"app.js\",\n \"log_date_format\" : \"YYYY-MM-DD HH:mm Z\",\n \"merge_logs\" : false,\n \"watch\" : false,\n \"exec_interpreter\" : \"node\",\n \"exec_mode\" : \"fork_mode\",\n \"env\":\n {\n \"NODE_ENV\" : \"production\",\n \"RPC_HOST\" : \"localhost\",\n \"RPC_PORT\" : \"302$id\",\n \"INSTANCE_NAME\" : \"$name_prefix-$i\",\n \"WS_SERVER\" : \"$ws_server\",\n \"WS_SECRET\" : \"$ws_secret\",\n }\n }"
+
+ endline=""
+ if (($i<$N-1)); then
+ # if [ "$i" -ne "$N" ]; then
+ endline=","
+ fi
+ echo -e "$single_template$endline" >> $conf
+ done
+
+ echo "]" >> $conf
+}
+
+case $cmd in
+ "info" )
+ info $*;;
+ "status" )
+ status $*;;
+ "clean" )
+ clean $*;;
+ "needs" )
+ needs $*;;
+ "up" )
+ up $*;;
+ "down" )
+ down $*;;
+ "init" )
+ init $*;;
+ "start" )
+ start $*;;
+ "stop" )
+ stop $* ;;
+ "restart" )
+ restart $*;;
+ "reset" )
+ reset $*;;
+ "cluster" )
+ cluster $*;;
+ "attach" )
+ attach $*;;
+ "log" )
+ log $*;;
+ "less" )
+ less $*;;
+ "netstatconf" )
+ netstatconf $*;;
+
+esac
diff --git a/swarm/cmd/swarm/test.sh b/swarm/cmd/swarm/test.sh
new file mode 100644
index 0000000000..5ded17493a
--- /dev/null
+++ b/swarm/cmd/swarm/test.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+TEST_DIR=`dirname $0`
+TEST_NAME=`basename $0 .sh`
+TEST_TYPE=`basename $TEST_DIR`
+
+
+export SWARM_BIN=$TEST_DIR/../../cmd/swarm
+export GETH=$SWARM_BIN/../../../geth
+export NETWORKID=322$TEST_NAME
+export TMPDIR=~/BZZ/test/$TEST_TYPE
+export DATA_ROOT=$TMPDIR/$NETWORKID
+# alias swarm='bash $SWARM_BIN/swarm.sh $DATA_ROOT $NETWORKID'
+EXTRA_ARGS=$*
+
+rm -rf $DATA_ROOT
+
+wait=1
+
+function swarm {
+ # echo bash $SWARM_BIN/swarm.sh $TMPDIR $NETWORKID $* $EXTRA_ARGS
+ bash $SWARM_BIN/swarm.sh $TMPDIR $NETWORKID $* $EXTRA_ARGS
+}
+
+
+function randomfile {
+ dd if=/dev/urandom of=/dev/stdout bs=1024 count=$1 2>/dev/null
+}
\ No newline at end of file
diff --git a/swarm/examples/album/file-manager.js b/swarm/examples/album/file-manager.js
new file mode 100644
index 0000000000..3e3f60deb6
--- /dev/null
+++ b/swarm/examples/album/file-manager.js
@@ -0,0 +1,135 @@
+function uploadFile(files, nr, uri) {
+ // when uploading complete - redirect to new address
+ if (files.length <= nr) {
+ if (uri != "") {
+ onUploadingComplete(uri);
+ }
+
+ return;
+ }
+
+ var currentFile = files[nr];
+ if (isNotImage(currentFile.type)) {
+ uploadFile(files, nr + 1, uri);
+ return;
+ }
+
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4) {
+ var newHash = xhr.responseText;
+ console.log("New hash - " + newHash);
+ if (newHash.length != 64) {
+ // something wrong
+ console.log("Something wrong on uploading");
+ alert('Oh, error on PUT file to BZZ. See log for more information.');
+
+ return;
+ }
+
+ insertImage(files, nr, newHash, currentFile.name, function () {
+ uploadFile(files, nr + 1, "/bzz:/" + newHash + "/");
+ });
+ }
+ };
+ xhr.open("PUT", uri + "imgs/" + currentFile.name, true);
+ xhr.setRequestHeader('Content-Type', currentFile.type);
+
+ readFile(currentFile, function (result) {
+ xhr.send(result);
+ });
+}
+
+function readFile(file, onComplete) {
+ var reader = new FileReader();
+ reader.onload = function (evt) {
+ if (onComplete) {
+ onComplete(evt.target.result)
+ }
+ };
+ reader.readAsArrayBuffer(file);
+}
+
+function insertImage(files, nr, newHash, fileName, onComplete) {
+ // insert image into index
+ var img = new Image();
+ img.onload = function () {
+ var blur = imageToUrl(img, 5, 5);
+ var thumbData = [];
+ var thumbSize = 200;
+ if (img.naturalWidth > img.naturalHeight) {
+ // landscape thumbnail
+ var h = img.naturalHeight * thumbSize / img.naturalWidth;
+ thumbData[0] = imageToUrl(img, thumbSize, h);
+ thumbData[1] = [thumbSize, h];
+ } else if (img.naturalWidth < img.naturalHeight) {
+ // portrait thumbnail
+ var w = img.naturalWidth * thumbSize / img.naturalHeight;
+ thumbData[0] = imageToUrl(img, w, thumbSize);
+ thumbData[1] = [w, thumbSize];
+ } else {
+ // square
+ thumbData[0] = imageToUrl(img, thumbSize, thumbSize);
+ thumbData[1] = [thumbSize, thumbSize];
+ }
+
+ jQuery('#currentPreview').attr('src', thumbData[0]);
+ // update index
+ var imgData = [];
+ imgData[0] = "imgs/" + fileName;
+ imgData[1] = [img.naturalWidth, img.naturalHeight];
+ imgs.data.splice(eidx, 0, {img: imgData, thumb: thumbData, blur: blur});
+ if (onComplete) {
+ onComplete();
+ }
+ };
+
+ img.onerror = function () {
+ if (onComplete) {
+ onComplete();
+ }
+ };
+
+ img.src = "/bzz:/" + newHash + "/imgs/" + fileName;
+}
+
+function isNotImage(type) {
+ var imageType = /^image\//;
+ return !imageType.test(type);
+}
+
+function onUploadingComplete(uri) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4) {
+ var i = xhr.responseText;
+ window.location.replace("/bzz:/" + i + "/");
+ }
+ };
+ sendImages(xhr, uri);
+}
+
+function handleFiles(files) {
+ uploadFile(files, 0, "");
+}
+
+function sendImages(xhr, uri) {
+ // set up request
+ xhr.open("PUT", uri + "data.json", true);
+ xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
+ // send the collected data as JSON
+ xhr.send(JSON.stringify(imgs));
+}
+
+// do it because I love jQuery
+function jqueryInit() {
+ // setup upload file selector
+ var fileElem = jQuery("#fileElem");
+ jQuery("#fileSelect").on("click", function (e) {
+ if (fileElem) {
+ fileElem.click();
+ }
+
+ e.preventDefault();
+ });
+}
\ No newline at end of file
diff --git a/swarm/examples/album/images/favicon.ico b/swarm/examples/album/images/favicon.ico
new file mode 100644
index 0000000000..95740c89b0
Binary files /dev/null and b/swarm/examples/album/images/favicon.ico differ
diff --git a/swarm/examples/album/index.css b/swarm/examples/album/index.css
index b77c3198ce..a550228d50 100644
--- a/swarm/examples/album/index.css
+++ b/swarm/examples/album/index.css
@@ -289,3 +289,7 @@ img
{
background: url(cut-mov.png) top left repeat-y, url(cut-mov.png) top right repeat-y;
}
+
+#currentPreview {
+ max-height: 120px;
+}
\ No newline at end of file
diff --git a/swarm/examples/album/index.html b/swarm/examples/album/index.html
index e42413c030..0f73c72798 100644
--- a/swarm/examples/album/index.html
+++ b/swarm/examples/album/index.html
@@ -1,20 +1,26 @@
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+