aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml23
-rw-r--r--accounts/abi/bind/bind.go175
-rw-r--r--accounts/abi/bind/bind_test.go2
-rw-r--r--accounts/abi/bind/template.go115
-rw-r--r--accounts/key_store_passphrase.go12
-rw-r--r--build/ci.go224
-rw-r--r--build/mvn.pom57
-rw-r--r--build/mvn.settings24
-rw-r--r--build/pod.podspec22
-rw-r--r--cmd/abigen/main.go23
-rw-r--r--cmd/utils/bootnodes.go41
-rw-r--r--cmd/utils/flags.go33
-rw-r--r--common/registrar/ethreg/api.go2
-rw-r--r--common/types.go5
-rw-r--r--core/types/block.go4
-rw-r--r--core/types/transaction.go33
-rw-r--r--internal/build/util.go2
-rw-r--r--internal/ethapi/api.go2
-rw-r--r--les/flowcontrol/control.go11
-rw-r--r--les/handler.go5
-rw-r--r--les/odr_test.go4
-rw-r--r--light/lightchain.go8
-rw-r--r--light/odr_test.go4
-rw-r--r--mobile/accounts.go181
-rw-r--r--mobile/android_test.go203
-rw-r--r--mobile/big.go95
-rw-r--r--mobile/big_go1.7.go26
-rw-r--r--mobile/bind.go202
-rw-r--r--mobile/common.go187
-rw-r--r--mobile/context.go81
-rw-r--r--mobile/discover.go104
-rw-r--r--mobile/doc.go57
-rw-r--r--mobile/ethclient.go305
-rw-r--r--mobile/ethereum.go125
-rw-r--r--mobile/geth.go200
-rw-r--r--mobile/geth_android.go22
-rw-r--r--mobile/geth_ios.go22
-rw-r--r--mobile/geth_other.go22
-rw-r--r--mobile/init.go35
-rw-r--r--mobile/interface.go148
-rw-r--r--mobile/p2p.go74
-rw-r--r--mobile/params.go89
-rw-r--r--mobile/primitives.go54
-rw-r--r--mobile/types.go189
-rw-r--r--mobile/vm.go56
-rw-r--r--node/config.go14
-rw-r--r--node/node.go31
-rw-r--r--p2p/discover/node.go4
-rw-r--r--p2p/discv5/net.go97
-rw-r--r--p2p/discv5/table.go76
-rw-r--r--p2p/discv5/ticket.go4
-rw-r--r--p2p/discv5/udp.go4
-rw-r--r--p2p/server.go20
-rw-r--r--params/bootnodes.go52
-rw-r--r--params/config.go22
-rw-r--r--tests/state_test_util.go2
56 files changed, 3424 insertions, 210 deletions
diff --git a/.travis.yml b/.travis.yml
index 51af0570b..c93b57907 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -53,15 +53,36 @@ matrix:
- CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- # This builder does the OSX Azure uploads
+ # This builder does the OSX Azure, Android Maven and Azure and iOS CocoaPods and Azure uploads
- os: osx
go: 1.7
env:
- azure-osx
+ - mobile
+ cache:
+ directories:
+ - $HOME/.android.platforms
+ - $HOME/.cocoapods
script:
- go run build/ci.go install
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
+ # Build the iOS framework and upload it to CocoaPods and Azure
+ - gem install cocoapods --pre
+ - go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
+
+ # Build the Android archive and upload it to Maven Central and Azure
+ - brew update
+ - brew install android-sdk maven
+ - export ANDROID_HOME=/usr/local/opt/android-sdk
+
+ - mkdir -p $ANDROID_HOME/platforms
+ - mv -f $HOME/.android.platforms $ANDROID_HOME/platforms
+ - echo "y" | android update sdk --no-ui --filter platform
+
+ - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
+ - mv -f $ANDROID_HOME/platforms $HOME/.android.platforms
+
install:
- go get golang.org/x/tools/cmd/cover
script:
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index 24fe9f770..84cf22e3c 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -32,11 +32,20 @@ import (
"golang.org/x/tools/imports"
)
+// Lang is a target programming language selector to generate bindings for.
+type Lang int
+
+const (
+ LangGo Lang = iota
+ LangJava
+ LangObjC
+)
+
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
// to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention opposed to having to
// manually maintain hard coded strings that break on runtime.
-func Bind(types []string, abis []string, bytecodes []string, pkg string) (string, error) {
+func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
// Process each individual contract requested binding
contracts := make(map[string]*tmplContract)
@@ -62,7 +71,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs
normalized := original
- normalized.Name = capitalise(original.Name)
+ normalized.Name = methodNormalizer[lang](original.Name)
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
@@ -78,7 +87,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
normalized.Outputs[j].Name = capitalise(output.Name)
}
}
- // Append the methos to the call or transact lists
+ // Append the methods to the call or transact lists
if original.Const {
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
} else {
@@ -87,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
}
contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
- InputABI: strippedABI,
+ InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
InputBin: strings.TrimSpace(bytecodes[i]),
Constructor: evmABI.Constructor,
Calls: calls,
@@ -102,24 +111,38 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
buffer := new(bytes.Buffer)
funcs := map[string]interface{}{
- "bindtype": bindType,
+ "bindtype": bindType[lang],
+ "namedtype": namedType[lang],
+ "capitalise": capitalise,
+ "decapitalise": decapitalise,
}
- tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource))
+ tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
if err := tmpl.Execute(buffer, data); err != nil {
return "", err
}
- // Pass the code through goimports to clean it up and double check
- code, err := imports.Process("", buffer.Bytes(), nil)
- if err != nil {
- return "", fmt.Errorf("%v\n%s", err, buffer)
+ // For Go bindings pass the code through goimports to clean it up and double check
+ if lang == LangGo {
+ code, err := imports.Process("", buffer.Bytes(), nil)
+ if err != nil {
+ return "", fmt.Errorf("%v\n%s", err, buffer)
+ }
+ return string(code), nil
}
- return string(code), nil
+ // For all others just return as is for now
+ return string(buffer.Bytes()), nil
}
-// bindType converts a Solidity type to a Go one. Since there is no clear mapping
+// bindType is a set of type binders that convert Solidity types to some supported
+// programming language.
+var bindType = map[Lang]func(kind abi.Type) string{
+ LangGo: bindTypeGo,
+ LangJava: bindTypeJava,
+}
+
+// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. *big.Int).
-func bindType(kind abi.Type) string {
+func bindTypeGo(kind abi.Type) string {
stringKind := kind.String()
switch {
@@ -160,11 +183,137 @@ func bindType(kind abi.Type) string {
}
}
+// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
+// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeJava(kind abi.Type) string {
+ stringKind := kind.String()
+
+ switch {
+ case strings.HasPrefix(stringKind, "address"):
+ parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ if len(parts) != 2 {
+ return stringKind
+ }
+ if parts[1] == "" {
+ return fmt.Sprintf("Address")
+ }
+ return fmt.Sprintf("Addresses")
+
+ case strings.HasPrefix(stringKind, "bytes"):
+ parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ if len(parts) != 3 {
+ return stringKind
+ }
+ if parts[2] != "" {
+ return "byte[][]"
+ }
+ return "byte[]"
+
+ case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
+ parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ if len(parts) != 4 {
+ return stringKind
+ }
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ if parts[1] == "" {
+ if parts[3] == "" {
+ return fmt.Sprintf("int%s", parts[2])
+ }
+ return fmt.Sprintf("int%s[]", parts[2])
+ }
+ }
+ if parts[3] == "" {
+ return fmt.Sprintf("BigInt")
+ }
+ return fmt.Sprintf("BigInts")
+
+ case strings.HasPrefix(stringKind, "bool"):
+ parts := regexp.MustCompile("bool(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ if len(parts) != 2 {
+ return stringKind
+ }
+ if parts[1] == "" {
+ return fmt.Sprintf("bool")
+ }
+ return fmt.Sprintf("bool[]")
+
+ case strings.HasPrefix(stringKind, "string"):
+ parts := regexp.MustCompile("string(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ if len(parts) != 2 {
+ return stringKind
+ }
+ if parts[1] == "" {
+ return fmt.Sprintf("String")
+ }
+ return fmt.Sprintf("String[]")
+
+ default:
+ return stringKind
+ }
+}
+
+// namedType is a set of functions that transform language specific types to
+// named versions that my be used inside method names.
+var namedType = map[Lang]func(string, abi.Type) string{
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
+ LangJava: namedTypeJava,
+}
+
+// namedTypeJava converts some primitive data types to named variants that can
+// be used as parts of method names.
+func namedTypeJava(javaKind string, solKind abi.Type) string {
+ switch javaKind {
+ case "byte[]":
+ return "Binary"
+ case "byte[][]":
+ return "Binaries"
+ case "string":
+ return "String"
+ case "string[]":
+ return "Strings"
+ case "bool":
+ return "Bool"
+ case "bool[]":
+ return "Bools"
+ case "BigInt":
+ parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(solKind.String())
+ if len(parts) != 4 {
+ return javaKind
+ }
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ if parts[3] == "" {
+ return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
+ }
+ return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
+
+ default:
+ return javaKind
+ }
+ default:
+ return javaKind
+ }
+}
+
+// methodNormalizer is a name transformer that modifies Solidity method names to
+// conform to target language naming concentions.
+var methodNormalizer = map[Lang]func(string) string{
+ LangGo: capitalise,
+ LangJava: decapitalise,
+}
+
// capitalise makes the first character of a string upper case.
func capitalise(input string) string {
return strings.ToUpper(input[:1]) + input[1:]
}
+// decapitalise makes the first character of a string lower case.
+func decapitalise(input string) string {
+ return strings.ToLower(input[:1]) + input[1:]
+}
+
// structured checks whether a method has enough information to return a proper
// Go struct ot if flat returns are needed.
func structured(method abi.Method) bool {
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 67c09c3ad..6ebc8ea0a 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -398,7 +398,7 @@ func TestBindings(t *testing.T) {
// Generate the test suite for all the contracts
for i, tt := range bindTests {
// Generate the binding and create a Go source file in the workspace
- bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest")
+ bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest", LangGo)
if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err)
}
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
index 523122213..64dd598c0 100644
--- a/accounts/abi/bind/template.go
+++ b/accounts/abi/bind/template.go
@@ -42,9 +42,16 @@ type tmplMethod struct {
Structured bool // Whether the returns should be accumulated into a contract
}
-// tmplSource is the Go source template use to generate the contract binding
+// tmplSource is language to template mapping containing all the supported
+// programming languages the package can generate to.
+var tmplSource = map[Lang]string{
+ LangGo: tmplSourceGo,
+ LangJava: tmplSourceJava,
+}
+
+// tmplSourceGo is the Go source template use to generate the contract binding
// based on.
-const tmplSource = `
+const tmplSourceGo = `
// This file is an automatically generated Go binding. Do not modify as any
// change will likely be lost upon the next re-generation!
@@ -52,7 +59,7 @@ package {{.Package}}
{{range $contract := .Contracts}}
// {{.Type}}ABI is the input ABI used to generate the binding from.
- const {{.Type}}ABI = ` + "`" + `{{.InputABI}}` + "`" + `
+ const {{.Type}}ABI = "{{.InputABI}}"
{{if .InputBin}}
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
@@ -258,3 +265,105 @@ package {{.Package}}
{{end}}
{{end}}
`
+
+// tmplSourceJava is the Java source template use to generate the contract binding
+// based on.
+const tmplSourceJava = `
+// This file is an automatically generated Java binding. Do not modify as any
+// change will likely be lost upon the next re-generation!
+
+package {{.Package}};
+
+import org.ethereum.geth.*;
+import org.ethereum.geth.internal.*;
+
+{{range $contract := .Contracts}}
+ public class {{.Type}} {
+ // ABI is the input ABI used to generate the binding from.
+ public final static String ABI = "{{.InputABI}}";
+
+ {{if .InputBin}}
+ // BYTECODE is the compiled bytecode used for deploying new contracts.
+ public final static byte[] BYTECODE = "{{.InputBin}}".getBytes();
+
+ // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
+ {{range $index, $element := .Constructor.Inputs}}
+ args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
+ {{end}}
+ return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args));
+ }
+
+ // Internal constructor used by contract deployment.
+ private {{.Type}}(BoundContract deployment) {
+ this.Address = deployment.getAddress();
+ this.Deployer = deployment.getDeployer();
+ this.Contract = deployment;
+ }
+ {{end}}
+
+ // Ethereum address where this contract is located at.
+ public final Address Address;
+
+ // Ethereum transaction in which this contract was deployed (if known!).
+ public final Transaction Deployer;
+
+ // Contract instance bound to a blockchain address.
+ private final BoundContract Contract;
+
+ // Creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ public {{.Type}}(Address address, EthereumClient client) throws Exception {
+ this(Geth.bindContract(address, ABI, client));
+ }
+
+ {{range .Calls}}
+ {{if gt (len .Normalized.Outputs) 1}}
+ // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
+ public class {{capitalise .Normalized.Name}}Results {
+ {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
+ {{end}}
+ }
+ {{end}}
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
+ //
+ // Solidity: {{.Original.String}}
+ public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
+ {{end}}
+
+ Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
+ {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}});
+ {{end}}
+
+ if (opts == null) {
+ opts = Geth.newCallOpts();
+ }
+ this.Contract.call(opts, results, "{{.Original.Name}}", args);
+ {{if gt (len .Normalized.Outputs) 1}}
+ {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
+ {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}();
+ {{end}}
+ return result;
+ {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}}
+ {{end}}
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
+ //
+ // Solidity: {{.Original.String}}
+ public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
+ {{end}}
+
+ return this.Contract.transact(opts, "{{.Original.Name}}" , args);
+ }
+ {{end}}
+ }
+{{end}}
+`
diff --git a/accounts/key_store_passphrase.go b/accounts/key_store_passphrase.go
index 3a5155e13..4a777956d 100644
--- a/accounts/key_store_passphrase.go
+++ b/accounts/key_store_passphrase.go
@@ -46,12 +46,20 @@ import (
const (
keyHeaderKDF = "scrypt"
- // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
+ // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
StandardScryptN = 1 << 18
+
+ // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
StandardScryptP = 1
- // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU.
+ // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
LightScryptN = 1 << 12
+
+ // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
LightScryptP = 6
scryptR = 8
diff --git a/build/ci.go b/build/ci.go
index 691f5233e..e8e08268b 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -29,6 +29,8 @@ Available commands are:
importkeys -- imports signing keys from env
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
nsis -- creates a Windows NSIS installer
+ aar [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
+ xcode [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
xgo [ options ] -- cross builds according to options
For all commands, -n prevents execution of external programs (dry run mode).
@@ -37,6 +39,7 @@ For all commands, -n prevents execution of external programs (dry run mode).
package main
import (
+ "bufio"
"bytes"
"encoding/base64"
"flag"
@@ -48,6 +51,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "regexp"
"runtime"
"strings"
"time"
@@ -125,6 +129,10 @@ func main() {
doDebianSource(os.Args[2:])
case "nsis":
doWindowsInstaller(os.Args[2:])
+ case "aar":
+ doAndroidArchive(os.Args[2:])
+ case "xcode":
+ doXCodeFramework(os.Args[2:])
case "xgo":
doXgo(os.Args[2:])
default:
@@ -331,14 +339,24 @@ func archiveBasename(arch string, env build.Environment) string {
if arch == "arm" {
platform += os.Getenv("GOARM")
}
- archive := platform + "-" + build.VERSION()
+ if arch == "android" {
+ platform = "android-all"
+ }
+ if arch == "ios" {
+ platform = "ios-all"
+ }
+ return platform + "-" + archiveVersion(env)
+}
+
+func archiveVersion(env build.Environment) string {
+ version := build.VERSION()
if isUnstableBuild(env) {
- archive += "-unstable"
+ version += "-unstable"
}
if env.Commit != "" {
- archive += "-" + env.Commit[:8]
+ version += "-" + env.Commit[:8]
}
- return archive
+ return version
}
func archiveUpload(archive string, blobstore string, signer string) error {
@@ -632,6 +650,204 @@ func doWindowsInstaller(cmdline []string) {
}
}
+// Android archives
+
+func doAndroidArchive(cmdline []string) {
+ var (
+ signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
+ deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
+ upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
+ )
+ flag.CommandLine.Parse(cmdline)
+ env := build.Env()
+
+ // Build the Android archive and Maven resources
+ build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
+ build.MustRun(gomobileTool("init"))
+ build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
+
+ meta := newMavenMetadata(env)
+ build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
+
+ // Skip Maven deploy and Azure upload for PR builds
+ maybeSkipArchive(env)
+
+ // Sign and upload all the artifacts to Maven Central
+ os.Rename("geth.aar", meta.Package+".aar")
+ if *signer != "" && *deploy != "" {
+ // Import the signing key into the local GPG instance
+ if b64key := os.Getenv(*signer); b64key != "" {
+ key, err := base64.StdEncoding.DecodeString(b64key)
+ if err != nil {
+ log.Fatalf("invalid base64 %s", *signer)
+ }
+ gpg := exec.Command("gpg", "--import")
+ gpg.Stdin = bytes.NewReader(key)
+ build.MustRun(gpg)
+ }
+ // Upload the artifacts to Sonatype and/or Maven Central
+ repo := *deploy + "/service/local/staging/deploy/maven2"
+ if meta.Develop {
+ repo = *deploy + "/content/repositories/snapshots"
+ }
+ build.MustRunCommand("mvn", "gpg:sign-and-deploy-file",
+ "-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
+ "-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
+ }
+ // Sign and upload the archive to Azure
+ archive := "geth-" + archiveBasename("android", env) + ".aar"
+ os.Rename(meta.Package+".aar", archive)
+
+ if err := archiveUpload(archive, *upload, *signer); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func gomobileTool(subcmd string, args ...string) *exec.Cmd {
+ cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd)
+ cmd.Args = append(cmd.Args, args...)
+ cmd.Env = []string{
+ "GOPATH=" + build.GOPATH(),
+ }
+ for _, e := range os.Environ() {
+ if strings.HasPrefix(e, "GOPATH=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, e)
+ }
+ return cmd
+}
+
+type mavenMetadata struct {
+ Version string
+ Package string
+ Develop bool
+ Contributors []mavenContributor
+}
+
+type mavenContributor struct {
+ Name string
+ Email string
+}
+
+func newMavenMetadata(env build.Environment) mavenMetadata {
+ // Collect the list of authors from the repo root
+ contribs := []mavenContributor{}
+ if authors, err := os.Open("AUTHORS"); err == nil {
+ defer authors.Close()
+
+ scanner := bufio.NewScanner(authors)
+ for scanner.Scan() {
+ // Skip any whitespace from the authors list
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ // Split the author and insert as a contributor
+ re := regexp.MustCompile("([^<]+) <(.+>)")
+ parts := re.FindStringSubmatch(line)
+ if len(parts) == 3 {
+ contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
+ }
+ }
+ }
+ // Render the version and package strings
+ version := build.VERSION()
+ if isUnstableBuild(env) {
+ version += "-SNAPSHOT"
+ }
+ return mavenMetadata{
+ Version: version,
+ Package: "geth-" + version,
+ Develop: isUnstableBuild(env),
+ Contributors: contribs,
+ }
+}
+
+// XCode frameworks
+
+func doXCodeFramework(cmdline []string) {
+ var (
+ signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
+ deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
+ upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
+ )
+ flag.CommandLine.Parse(cmdline)
+ env := build.Env()
+
+ // Build the iOS XCode framework
+ build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
+ build.MustRun(gomobileTool("init"))
+
+ archive := "geth-" + archiveBasename("ios", env)
+ if err := os.Mkdir(archive, os.ModePerm); err != nil {
+ log.Fatal(err)
+ }
+ bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ethereum/go-ethereum/mobile")
+ bind.Dir, _ = filepath.Abs(archive)
+ build.MustRun(bind)
+ build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
+
+ // Skip CocoaPods deploy and Azure upload for PR builds
+ maybeSkipArchive(env)
+
+ // Sign and upload the framework to Azure
+ if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil {
+ log.Fatal(err)
+ }
+ // Prepare and upload a PodSpec to CocoaPods
+ if *deploy != "" {
+ meta := newPodMetadata(env)
+ build.Render("build/pod.podspec", meta.Name+".podspec", 0755, meta)
+ build.MustRunCommand("pod", *deploy, "push", meta.Name+".podspec", "--allow-warnings")
+ }
+}
+
+type podMetadata struct {
+ Name string
+ Version string
+ Commit string
+ Contributors []podContributor
+}
+
+type podContributor struct {
+ Name string
+ Email string
+}
+
+func newPodMetadata(env build.Environment) podMetadata {
+ // Collect the list of authors from the repo root
+ contribs := []podContributor{}
+ if authors, err := os.Open("AUTHORS"); err == nil {
+ defer authors.Close()
+
+ scanner := bufio.NewScanner(authors)
+ for scanner.Scan() {
+ // Skip any whitespace from the authors list
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ // Split the author and insert as a contributor
+ re := regexp.MustCompile("([^<]+) <(.+>)")
+ parts := re.FindStringSubmatch(line)
+ if len(parts) == 3 {
+ contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
+ }
+ }
+ }
+ name := "Geth"
+ if isUnstableBuild(env) {
+ name += "Develop"
+ }
+ return podMetadata{
+ Name: name,
+ Version: archiveVersion(env),
+ Commit: env.Commit,
+ Contributors: contribs,
+ }
+}
+
// Cross compilation
func doXgo(cmdline []string) {
diff --git a/build/mvn.pom b/build/mvn.pom
new file mode 100644
index 000000000..7670246ba
--- /dev/null
+++ b/build/mvn.pom
@@ -0,0 +1,57 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.ethereum</groupId>
+ <artifactId>geth</artifactId>
+ <version>{{.Version}}</version>
+ <packaging>aar</packaging>
+
+ <name>Android Ethereum Client</name>
+ <description>Android port of the go-ethereum libraries and node</description>
+ <url>https://github.com/ethereum/go-ethereum</url>
+ <inceptionYear>2015</inceptionYear>
+
+ <licenses>
+ <license>
+ <name>GNU Lesser General Public License, Version 3.0</name>
+ <url>https://www.gnu.org/licenses/lgpl-3.0.en.html</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <organization>
+ <name>Ethereum</name>
+ <url>https://ethereum.org</url>
+ </organization>
+
+ <developers>
+ <developer>
+ <id>karalabe</id>
+ <name>Péter Szilágyi</name>
+ <email>peterke@gmail.com</email>
+ <url>https://github.com/karalabe</url>
+ <properties>
+ <picUrl>https://www.gravatar.com/avatar/2ecbf0f5b4b79eebf8c193e5d324357f?s=256</picUrl>
+ </properties>
+ </developer>
+ </developers>
+
+ <contributors>{{range .Contributors}}
+ <contributor>
+ <name>{{.Name}}</name>
+ <email>{{.Email}}</email>
+ </contributor>{{end}}
+ </contributors>
+
+ <issueManagement>
+ <system>GitHub Issues</system>
+ <url>https://github.com/ethereum/go-ethereum/issues/</url>
+ </issueManagement>
+
+ <scm>
+ <url>https://github.com/ethereum/go-ethereum</url>
+ </scm>
+</project>
diff --git a/build/mvn.settings b/build/mvn.settings
new file mode 100644
index 000000000..406b409b9
--- /dev/null
+++ b/build/mvn.settings
@@ -0,0 +1,24 @@
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+ http://maven.apache.org/xsd/settings-1.0.0.xsd">
+ <servers>
+ <server>
+ <id>ossrh</id>
+ <username>${env.ANDROID_SONATYPE_USERNAME}</username>
+ <password>${env.ANDROID_SONATYPE_PASSWORD}</password>
+ </server>
+ </servers>
+ <profiles>
+ <profile>
+ <id>ossrh</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <gpg.executable>gpg</gpg.executable>
+ <gpg.passphrase></gpg.passphrase>
+ </properties>
+ </profile>
+ </profiles>
+</settings>
diff --git a/build/pod.podspec b/build/pod.podspec
new file mode 100644
index 000000000..c43af3e82
--- /dev/null
+++ b/build/pod.podspec
@@ -0,0 +1,22 @@
+Pod::Spec.new do |spec|
+ spec.name = '{{.Name}}'
+ spec.version = '{{.Version}}'
+ spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
+ spec.homepage = 'https://github.com/ethereum/go-ethereum'
+ spec.authors = { {{range .Contributors}}
+ '{{.Name}}' => '{{.Email}}',{{end}}
+ }
+ spec.summary = 'iOS Ethereum Client'
+ spec.source = { :git => 'https://github.com/ethereum/go-ethereum.git', :commit => '{{.Commit}}' }
+
+ spec.platform = :ios
+ spec.ios.deployment_target = '9.0'
+ spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
+
+ spec.prepare_command = <<-CMD
+ curl https://gethstore.blob.core.windows.net/builds/geth-ios-all-{{.Version}}.tar.gz | tar -xvz
+ mkdir Frameworks
+ mv geth-ios-all-{{.Version}}/Geth.framework Frameworks
+ rm -rf geth-ios-all-{{.Version}}
+ CMD
+end
diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go
index 9c9ab6d31..dfbd025da 100644
--- a/cmd/abigen/main.go
+++ b/cmd/abigen/main.go
@@ -31,14 +31,15 @@ import (
var (
abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind")
binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)")
- typFlag = flag.String("type", "", "Go struct name for the binding (default = package name)")
+ typFlag = flag.String("type", "", "Struct name for the binding (default = package name)")
solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind")
solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
excFlag = flag.String("exc", "", "Comma separated types to exclude from binding")
- pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into")
- outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
+ pkgFlag = flag.String("pkg", "", "Package name to generate the binding into")
+ outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
+ langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)")
)
func main() {
@@ -53,7 +54,19 @@ func main() {
os.Exit(-1)
}
if *pkgFlag == "" {
- fmt.Printf("No destination Go package specified (--pkg)\n")
+ fmt.Printf("No destination package specified (--pkg)\n")
+ os.Exit(-1)
+ }
+ var lang bind.Lang
+ switch *langFlag {
+ case "go":
+ lang = bind.LangGo
+ case "java":
+ lang = bind.LangJava
+ case "objc":
+ lang = bind.LangObjC
+ default:
+ fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag)
os.Exit(-1)
}
// If the entire solidity code was specified, build and bind based on that
@@ -108,7 +121,7 @@ func main() {
types = append(types, kind)
}
// Generate the contract binding
- code, err := bind.Bind(types, abis, bins, *pkgFlag)
+ code, err := bind.Bind(types, abis, bins, *pkgFlag, lang)
if err != nil {
fmt.Printf("Failed to generate ABI binding: %v\n", err)
os.Exit(-1)
diff --git a/cmd/utils/bootnodes.go b/cmd/utils/bootnodes.go
deleted file mode 100644
index fbbaa1f22..000000000
--- a/cmd/utils/bootnodes.go
+++ /dev/null
@@ -1,41 +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 General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
-
-package utils
-
-import "github.com/ethereum/go-ethereum/p2p/discover"
-
-// FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on
-// the Frontier network.
-var FrontierBootNodes = []*discover.Node{
- // ETH/DEV Go Bootnodes
- discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
- discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR
- discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG
-
- // ETH/DEV Cpp Bootnodes
- discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
-}
-
-// TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the
-// Morden test network.
-var TestNetBootNodes = []*discover.Node{
- // ETH/DEV Go Bootnodes
- discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"),
- discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"),
-
- // ETH/DEV Cpp Bootnodes
-}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index f86fc37f5..6d9482e69 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -46,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
@@ -487,9 +488,9 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
// Return pre-configured nodes if none were manually requested
if !ctx.GlobalIsSet(BootnodesFlag.Name) {
if ctx.GlobalBool(TestNetFlag.Name) {
- return TestNetBootNodes
+ return params.TestnetBootnodes
}
- return FrontierBootNodes
+ return params.MainnetBootnodes
}
// Otherwise parse and use the CLI bootstrap nodes
bootnodes := []*discover.Node{}
@@ -505,13 +506,36 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
return bootnodes
}
+// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line
+// flags, reverting to pre-configured ones if none have been specified.
+func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node {
+ // Return pre-configured nodes if none were manually requested
+ if !ctx.GlobalIsSet(BootnodesFlag.Name) {
+ return params.DiscoveryV5Bootnodes
+ }
+ // Otherwise parse and use the CLI bootstrap nodes
+ bootnodes := []*discv5.Node{}
+
+ for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") {
+ node, err := discv5.ParseNode(url)
+ if err != nil {
+ glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
+ continue
+ }
+ bootnodes = append(bootnodes, node)
+ }
+ return bootnodes
+}
+
// MakeListenAddress creates a TCP listening address string from set command
// line flags.
func MakeListenAddress(ctx *cli.Context) string {
return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
}
-func MakeListenAddressV5(ctx *cli.Context) string {
+// MakeDiscoveryV5Address creates a UDP listening address string from set command
+// line flags for the V5 discovery protocol.
+func MakeDiscoveryV5Address(ctx *cli.Context) string {
return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
}
@@ -647,9 +671,10 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
UserIdent: makeNodeUserIdent(ctx),
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name),
DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0,
+ DiscoveryV5Addr: MakeDiscoveryV5Address(ctx),
BootstrapNodes: MakeBootstrapNodes(ctx),
+ BootstrapNodesV5: MakeBootstrapNodesV5(ctx),
ListenAddr: MakeListenAddress(ctx),
- ListenAddrV5: MakeListenAddressV5(ctx),
NAT: MakeNAT(ctx),
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go
index a3c48345e..a32653554 100644
--- a/common/registrar/ethreg/api.go
+++ b/common/registrar/ethreg/api.go
@@ -187,7 +187,7 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr
if gasPrice.BitLen() == 0 {
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
}
- msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr))
+ msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr), false)
header := be.bc.CurrentBlock().Header()
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
diff --git a/common/types.go b/common/types.go
index d00884484..70b7e7aae 100644
--- a/common/types.go
+++ b/common/types.go
@@ -35,7 +35,10 @@ const (
var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
type (
- Hash [HashLength]byte
+ // Hash represents the 32 byte Keccak256 hash of arbitrary data.
+ Hash [HashLength]byte
+
+ // Address represents the 20 byte address of an Ethereum account.
Address [AddressLength]byte
)
diff --git a/core/types/block.go b/core/types/block.go
index fedcfdbbe..68504ffcc 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -79,7 +79,7 @@ func (n *BlockNonce) UnmarshalJSON(input []byte) error {
return nil
}
-// Header represents Ethereum block headers.
+// Header represents a block header in the Ethereum blockchain.
type Header struct {
ParentHash common.Hash // Hash to the previous block
UncleHash common.Hash // Uncles of this block
@@ -214,7 +214,7 @@ type Body struct {
Uncles []*Header
}
-// Block represents a block in the Ethereum blockchain.
+// Block represents an entire block in the Ethereum blockchain.
type Block struct {
header *Header
uncles []*Header
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 972a36706..323bfaee6 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -321,12 +321,13 @@ func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err er
// XXX Rename message to something less arbitrary?
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
msg := Message{
- nonce: tx.data.AccountNonce,
- price: new(big.Int).Set(tx.data.Price),
- gasLimit: new(big.Int).Set(tx.data.GasLimit),
- to: tx.data.Recipient,
- amount: tx.data.Amount,
- data: tx.data.Payload,
+ nonce: tx.data.AccountNonce,
+ price: new(big.Int).Set(tx.data.Price),
+ gasLimit: new(big.Int).Set(tx.data.GasLimit),
+ to: tx.data.Recipient,
+ amount: tx.data.Amount,
+ data: tx.data.Payload,
+ checkNonce: true,
}
var err error
@@ -535,17 +536,19 @@ type Message struct {
nonce uint64
amount, price, gasLimit *big.Int
data []byte
+ checkNonce bool
}
-func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte) Message {
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte, checkNonce bool) Message {
return Message{
- from: from,
- to: to,
- nonce: nonce,
- amount: amount,
- price: price,
- gasLimit: gasLimit,
- data: data,
+ from: from,
+ to: to,
+ nonce: nonce,
+ amount: amount,
+ price: price,
+ gasLimit: gasLimit,
+ data: data,
+ checkNonce: checkNonce,
}
}
@@ -556,4 +559,4 @@ func (m Message) Value() *big.Int { return m.amount }
func (m Message) Gas() *big.Int { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
-func (m Message) CheckNonce() bool { return true }
+func (m Message) CheckNonce() bool { return m.checkNonce }
diff --git a/internal/build/util.go b/internal/build/util.go
index ce17ce220..c7e0614f2 100644
--- a/internal/build/util.go
+++ b/internal/build/util.go
@@ -56,7 +56,7 @@ func GOPATH() string {
if len(path) == 0 {
log.Fatal("GOPATH is not set")
}
- // Ensure that our internal vendor folder in on GOPATH
+ // Ensure that our internal vendor folder is on GOPATH
vendor, _ := filepath.Abs(filepath.Join("build", "_vendor"))
for _, dir := range path {
if dir == vendor {
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 0e4dd4524..a25eff5ed 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -545,7 +545,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
if gasPrice.Cmp(common.Big0) == 0 {
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
}
- msg := types.NewMessage(addr, args.To, 0, args.Value.BigInt(), gas, gasPrice, common.FromHex(args.Data))
+ msg := types.NewMessage(addr, args.To, 0, args.Value.BigInt(), gas, gasPrice, common.FromHex(args.Data), false)
// Execute the call and return
vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header)
diff --git a/les/flowcontrol/control.go b/les/flowcontrol/control.go
index 0b8d7f58f..acb131ea4 100644
--- a/les/flowcontrol/control.go
+++ b/les/flowcontrol/control.go
@@ -99,7 +99,7 @@ type ServerNode struct {
params *ServerParams
sumCost uint64 // sum of req costs sent to this server
pending map[uint64]uint64 // value = sumCost after sending the given req
- lock sync.Mutex
+ lock sync.RWMutex
}
func NewServerNode(params *ServerParams) *ServerNode {
@@ -135,8 +135,8 @@ func (peer *ServerNode) canSend(maxCost uint64) uint64 {
}
func (peer *ServerNode) CanSend(maxCost uint64) uint64 {
- peer.lock.Lock()
- defer peer.lock.Unlock()
+ peer.lock.RLock()
+ defer peer.lock.RUnlock()
return peer.canSend(maxCost)
}
@@ -148,7 +148,10 @@ func (peer *ServerNode) SendRequest(reqID, maxCost uint64) {
peer.recalcBLE(getTime())
for peer.bufEstimate < maxCost {
- time.Sleep(time.Duration(peer.canSend(maxCost)))
+ wait := time.Duration(peer.canSend(maxCost))
+ peer.lock.Unlock()
+ time.Sleep(wait)
+ peer.lock.Lock()
peer.recalcBLE(getTime())
}
peer.bufEstimate -= maxCost
diff --git a/les/handler.go b/les/handler.go
index a51358676..83d73666f 100644
--- a/les/handler.go
+++ b/les/handler.go
@@ -240,6 +240,7 @@ func (pm *ProtocolManager) findServers() {
if pm.p2pServer == nil || pm.topicDisc == nil {
return
}
+ glog.V(logger.Debug).Infoln("Looking for topic", string(pm.lesTopic))
enodes := make(chan string, 100)
stop := make(chan struct{})
go pm.topicDisc.SearchTopic(pm.lesTopic, stop, enodes)
@@ -280,9 +281,9 @@ func (pm *ProtocolManager) Start(srvr *p2p.Server) {
} else {
if pm.topicDisc != nil {
go func() {
- glog.V(logger.Debug).Infoln("Starting topic register")
+ glog.V(logger.Debug).Infoln("Starting registering topic", string(pm.lesTopic))
pm.topicDisc.RegisterTopic(pm.lesTopic, pm.quitSync)
- glog.V(logger.Debug).Infoln("Stopped topic register")
+ glog.V(logger.Debug).Infoln("Stopped registering topic", string(pm.lesTopic))
}()
}
go func() {
diff --git a/les/odr_test.go b/les/odr_test.go
index f1fa59ad8..80f7b8208 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -119,7 +119,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
from := statedb.GetOrNewStateObject(testBankAddress)
from.SetBalance(common.MaxBig)
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
@@ -132,7 +132,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if err == nil {
from.SetBalance(common.MaxBig)
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
diff --git a/light/lightchain.go b/light/lightchain.go
index 4e895db61..1cea7a892 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -108,8 +108,8 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux
// add trusted CHT
if config.DAOForkSupport {
WriteTrustedCht(bc.chainDb, TrustedCht{
- Number: 612,
- Root: common.HexToHash("8c87a93e0ee531e2aca1b4460e4c201a60c19ffec4f5979262bf14ceeeff8471"),
+ Number: 637,
+ Root: common.HexToHash("01e408d9b1942f05dba1a879f3eaafe34d219edaeb8223fecf1244cc023d3e23"),
})
} else {
WriteTrustedCht(bc.chainDb, TrustedCht{
@@ -122,8 +122,8 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux
if bc.genesisBlock.Hash() == (common.Hash{12, 215, 134, 162, 66, 93, 22, 241, 82, 198, 88, 49, 108, 66, 62, 108, 225, 24, 30, 21, 195, 41, 88, 38, 215, 201, 144, 76, 186, 156, 227, 3}) {
// add trusted CHT for testnet
WriteTrustedCht(bc.chainDb, TrustedCht{
- Number: 436,
- Root: common.HexToHash("97a12df5d04d72bde4b4b840e1018e4f08aee34b7d0bf2c5dbfc052b86fe7439"),
+ Number: 452,
+ Root: common.HexToHash("511da2c88e32b14cf4a4e62f7fcbb297139faebc260a4ab5eb43cce6edcba324"),
})
glog.V(logger.Info).Infoln("Added trusted CHT for testnet")
} else {
diff --git a/light/odr_test.go b/light/odr_test.go
index 1f6bcaeb1..50255a7f3 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -167,7 +167,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
from := statedb.GetOrNewStateObject(testBankAddress)
from.SetBalance(common.MaxBig)
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
vmenv := core.NewEnv(statedb, testChainConfig(), bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
@@ -180,7 +180,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
if err == nil {
from.SetBalance(common.MaxBig)
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
vmenv := NewEnv(ctx, state, testChainConfig(), lc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
diff --git a/mobile/accounts.go b/mobile/accounts.go
new file mode 100644
index 000000000..41498b6f0
--- /dev/null
+++ b/mobile/accounts.go
@@ -0,0 +1,181 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the accounts package to support client side key
+// management on mobile platforms.
+
+package geth
+
+import (
+ "errors"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+)
+
+const (
+ // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptN = int(accounts.StandardScryptN)
+
+ // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptP = int(accounts.StandardScryptP)
+
+ // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptN = int(accounts.LightScryptN)
+
+ // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptP = int(accounts.LightScryptP)
+)
+
+// Account represents a stored key.
+type Account struct{ account accounts.Account }
+
+// Accounts represents a slice of accounts.
+type Accounts struct{ accounts []accounts.Account }
+
+// Size returns the number of accounts in the slice.
+func (a *Accounts) Size() int {
+ return len(a.accounts)
+}
+
+// Get returns the account at the given index from the slice.
+func (a *Accounts) Get(index int) (*Account, error) {
+ if index < 0 || index >= len(a.accounts) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Account{a.accounts[index]}, nil
+}
+
+// Set sets the account at the given index in the slice.
+func (a *Accounts) Set(index int, account *Account) error {
+ if index < 0 || index >= len(a.accounts) {
+ return errors.New("index out of bounds")
+ }
+ a.accounts[index] = account.account
+ return nil
+}
+
+// GetAddress retrieves the address associated with the account.
+func (a *Account) GetAddress() *Address {
+ return &Address{a.account.Address}
+}
+
+// GetFile retrieves the path of the file containing the account key.
+func (a *Account) GetFile() string {
+ return a.account.File
+}
+
+// AccountManager manages a key storage directory on disk.
+type AccountManager struct{ manager *accounts.Manager }
+
+// NewAccountManager creates a manager for the given directory.
+func NewAccountManager(keydir string, scryptN, scryptP int) *AccountManager {
+ return &AccountManager{manager: accounts.NewManager(keydir, scryptN, scryptP)}
+}
+
+// HasAddress reports whether a key with the given address is present.
+func (am *AccountManager) HasAddress(addr *Address) bool {
+ return am.manager.HasAddress(addr.address)
+}
+
+// GetAccounts returns all key files present in the directory.
+func (am *AccountManager) GetAccounts() *Accounts {
+ return &Accounts{am.manager.Accounts()}
+}
+
+// DeleteAccount deletes the key matched by account if the passphrase is correct.
+// If a contains no filename, the address must match a unique key.
+func (am *AccountManager) DeleteAccount(a *Account, passphrase string) error {
+ return am.manager.DeleteAccount(accounts.Account{
+ Address: a.account.Address,
+ File: a.account.File,
+ }, passphrase)
+}
+
+// Sign signs hash with an unlocked private key matching the given address.
+func (am *AccountManager) Sign(addr *Address, hash []byte) ([]byte, error) {
+ return am.manager.Sign(addr.address, hash)
+}
+
+// SignWithPassphrase signs hash if the private key matching the given address can be
+// decrypted with the given passphrase.
+func (am *AccountManager) SignWithPassphrase(addr *Address, passphrase string, hash []byte) ([]byte, error) {
+ return am.manager.SignWithPassphrase(addr.address, passphrase, hash)
+}
+
+// Unlock unlocks the given account indefinitely.
+func (am *AccountManager) Unlock(a *Account, passphrase string) error {
+ return am.manager.TimedUnlock(a.account, passphrase, 0)
+}
+
+// Lock removes the private key with the given address from memory.
+func (am *AccountManager) Lock(addr *Address) error {
+ return am.manager.Lock(addr.address)
+}
+
+// TimedUnlock unlocks the given account with the passphrase. The account
+// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
+// until the program exits. The account must match a unique key file.
+//
+// If the account address is already unlocked for a duration, TimedUnlock extends or
+// shortens the active unlock timeout. If the address was previously unlocked
+// indefinitely the timeout is not altered.
+func (am *AccountManager) TimedUnlock(a *Account, passphrase string, timeout int64) error {
+ return am.manager.TimedUnlock(a.account, passphrase, time.Duration(timeout))
+}
+
+// NewAccount generates a new key and stores it into the key directory,
+// encrypting it with the passphrase.
+func (am *AccountManager) NewAccount(passphrase string) (*Account, error) {
+ account, err := am.manager.NewAccount(passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return &Account{account}, nil
+}
+
+// ExportKey exports as a JSON key, encrypted with newPassphrase.
+func (am *AccountManager) ExportKey(a *Account, passphrase, newPassphrase string) ([]byte, error) {
+ return am.manager.Export(a.account, passphrase, newPassphrase)
+}
+
+// ImportKey stores the given encrypted JSON key into the key directory.
+func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (*Account, error) {
+ account, err := am.manager.Import(keyJSON, passphrase, newPassphrase)
+ if err != nil {
+ return nil, err
+ }
+ return &Account{account}, nil
+}
+
+// Update changes the passphrase of an existing account.
+func (am *AccountManager) Update(a *Account, passphrase, newPassphrase string) error {
+ return am.manager.Update(a.account, passphrase, newPassphrase)
+}
+
+// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
+// a key file in the key directory. The key file is encrypted with the same passphrase.
+func (am *AccountManager) ImportPreSaleKey(keyJSON []byte, passphrase string) (*Account, error) {
+ account, err := am.manager.ImportPreSaleKey(keyJSON, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return &Account{account}, nil
+}
diff --git a/mobile/android_test.go b/mobile/android_test.go
new file mode 100644
index 000000000..0a3fa93ae
--- /dev/null
+++ b/mobile/android_test.go
@@ -0,0 +1,203 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the accounts package to support client side key
+// management on mobile platforms.
+
+package geth
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/internal/build"
+)
+
+// androidTestClass is a Java class to do some lightweight tests against the Android
+// bindings. The goal is not to test each individual functionality, rather just to
+// catch breaking API and/or implementation changes.
+const androidTestClass = `
+package go;
+
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+
+import org.ethereum.geth.*;
+
+public class AndroidTest extends InstrumentationTestCase {
+ public AndroidTest() {}
+
+ public void testAccountManagement() {
+ try {
+ AccountManager am = new AccountManager(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
+
+ Account newAcc = am.newAccount("Creation password");
+ byte[] jsonAcc = am.exportKey(newAcc, "Creation password", "Export password");
+
+ am.deleteAccount(newAcc, "Creation password");
+ Account impAcc = am.importKey(jsonAcc, "Export password", "Import password");
+ } catch (Exception e) {
+ fail(e.toString());
+ }
+ }
+
+ public void testInprocNode() {
+ Context ctx = new Context();
+
+ try {
+ // Start up a new inprocess node
+ Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig());
+ node.start();
+
+ // Retrieve some data via function calls (we don't really care about the results)
+ NodeInfo info = node.getNodeInfo();
+ info.getName();
+ info.getListenerAddress();
+ info.getProtocols();
+
+ // Retrieve some data via the APIs (we don't really care about the results)
+ EthereumClient ec = node.getEthereumClient();
+ ec.getBlockByNumber(ctx, -1).getNumber();
+
+ NewHeadHandler handler = new NewHeadHandler() {
+ @Override public void onError(String error) {}
+ @Override public void onNewHead(final Header header) {}
+ };
+ ec.subscribeNewHead(ctx, handler, 16);
+ } catch (Exception e) {
+ fail(e.toString());
+ }
+ }
+}
+`
+
+// TestAndroid runs the Android java test class specified above.
+//
+// This requires the gradle command in PATH and the Android SDK whose path is available
+// through ANDROID_HOME environment variable. To successfully run the tests, an Android
+// device must also be available with debugging enabled.
+//
+// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest
+func TestAndroid(t *testing.T) {
+ // Skip tests on Windows altogether
+ if runtime.GOOS == "windows" {
+ t.Skip("cannot test Android bindings on Windows, skipping")
+ }
+ // Make sure all the Android tools are installed
+ if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil {
+ t.Skip("command gradle not found, skipping")
+ }
+ if sdk := os.Getenv("ANDROID_HOME"); sdk == "" {
+ t.Skip("ANDROID_HOME environment var not set, skipping")
+ }
+ if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil {
+ t.Log("gomobile missing, installing it...")
+ if _, err := exec.Command("go", "install", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
+ t.Fatalf("install failed: %v", err)
+ }
+ t.Log("initializing gomobile...")
+ start := time.Now()
+ if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil {
+ t.Fatalf("initialization failed: %v", err)
+ }
+ t.Logf("initialization took %v", time.Since(start))
+ }
+ // Create and switch to a temporary workspace
+ workspace, err := ioutil.TempDir("", "geth-android-")
+ if err != nil {
+ t.Fatalf("failed to create temporary workspace: %v", err)
+ }
+ defer os.RemoveAll(workspace)
+
+ pwd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("failed to get current working directory: %v", err)
+ }
+ if err := os.Chdir(workspace); err != nil {
+ t.Fatalf("failed to switch to temporary workspace: %v", err)
+ }
+ defer os.Chdir(pwd)
+
+ // Create the skeleton of the Android project
+ for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} {
+ err = os.MkdirAll(dir, os.ModePerm)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ // Generate the mobile bindings for Geth and add the tester class
+ gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/ethereum/go-ethereum/mobile")
+ if output, err := gobind.CombinedOutput(); err != nil {
+ t.Logf("%s", output)
+ t.Fatalf("failed to run gomobile bind: %v", err)
+ }
+ build.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar", os.ModePerm)
+
+ if err = ioutil.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil {
+ t.Fatalf("failed to write Android test class: %v", err)
+ }
+ // Finish creating the project and run the tests via gradle
+ if err = ioutil.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil {
+ t.Fatalf("failed to write Android manifest: %v", err)
+ }
+ if err = ioutil.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil {
+ t.Fatalf("failed to write gradle build file: %v", err)
+ }
+ if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil {
+ t.Logf("%s", output)
+ t.Errorf("failed to run gradle test: %v", err)
+ }
+}
+
+const androidManifest = `<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.ethereum.gethtest"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+</manifest>`
+
+const gradleConfig = `buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.5.0'
+ }
+}
+allprojects {
+ repositories { jcenter() }
+}
+apply plugin: 'com.android.library'
+android {
+ compileSdkVersion 'android-19'
+ buildToolsVersion '21.1.2'
+ defaultConfig { minSdkVersion 15 }
+}
+repositories {
+ flatDir { dirs 'libs' }
+}
+dependencies {
+ compile 'com.android.support:appcompat-v7:19.0.0'
+ compile(name: "geth", ext: "aar")
+}
+`
diff --git a/mobile/big.go b/mobile/big.go
new file mode 100644
index 000000000..6a358ba28
--- /dev/null
+++ b/mobile/big.go
@@ -0,0 +1,95 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the math/big package.
+
+package geth
+
+import (
+ "errors"
+ "math/big"
+)
+
+// A BigInt represents a signed multi-precision integer.
+type BigInt struct {
+ bigint *big.Int
+}
+
+// NewBigInt allocates and returns a new BigInt set to x.
+func NewBigInt(x int64) *BigInt {
+ return &BigInt{big.NewInt(x)}
+}
+
+// GetBytes returns the absolute value of x as a big-endian byte slice.
+func (bi *BigInt) GetBytes() []byte {
+ return bi.bigint.Bytes()
+}
+
+// String returns the value of x as a formatted decimal string.
+func (bi *BigInt) String() string {
+ return bi.bigint.String()
+}
+
+// GetInt64 returns the int64 representation of x. If x cannot be represented in
+// an int64, the result is undefined.
+func (bi *BigInt) GetInt64() int64 {
+ return bi.bigint.Int64()
+}
+
+// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets
+// the big int to that value.
+func (bi *BigInt) SetBytes(buf []byte) {
+ bi.bigint.SetBytes(buf)
+}
+
+// SetInt64 sets the big int to x.
+func (bi *BigInt) SetInt64(x int64) {
+ bi.bigint.SetInt64(x)
+}
+
+// SetString sets the big int to x.
+//
+// The string prefix determines the actual conversion base. A prefix of "0x" or
+// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix
+// selects base 2. Otherwise the selected base is 10.
+func (bi *BigInt) SetString(x string, base int) {
+ bi.bigint.SetString(x, base)
+}
+
+// BigInts represents a slice of big ints.
+type BigInts struct{ bigints []*big.Int }
+
+// Size returns the number of big ints in the slice.
+func (bi *BigInts) Size() int {
+ return len(bi.bigints)
+}
+
+// Get returns the bigint at the given index from the slice.
+func (bi *BigInts) Get(index int) (*BigInt, error) {
+ if index < 0 || index >= len(bi.bigints) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &BigInt{bi.bigints[index]}, nil
+}
+
+// Set sets the big int at the given index in the slice.
+func (bi *BigInts) Set(index int, bigint *BigInt) error {
+ if index < 0 || index >= len(bi.bigints) {
+ return errors.New("index out of bounds")
+ }
+ bi.bigints[index] = bigint.bigint
+ return nil
+}
diff --git a/mobile/big_go1.7.go b/mobile/big_go1.7.go
new file mode 100644
index 000000000..0447e1f66
--- /dev/null
+++ b/mobile/big_go1.7.go
@@ -0,0 +1,26 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains the wrappers from the math/big package that require Go 1.7 and above.
+
+// +build go1.7
+
+package geth
+
+// GetString returns the value of x as a formatted string in some number base.
+func (bi *BigInt) GetString(base int) string {
+ return bi.bigint.Text(base)
+}
diff --git a/mobile/bind.go b/mobile/bind.go
new file mode 100644
index 000000000..50adc6b0f
--- /dev/null
+++ b/mobile/bind.go
@@ -0,0 +1,202 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the bind package.
+
+package geth
+
+import (
+ "math/big"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// Signer is an interaface defining the callback when a contract requires a
+// method to sign the transaction before submission.
+type Signer interface {
+ Sign(*Address, *Transaction) (*Transaction, error)
+}
+
+type signer struct {
+ sign bind.SignerFn
+}
+
+func (s *signer) Sign(addr *Address, tx *Transaction) (*Transaction, error) {
+ sig, err := s.sign(types.HomesteadSigner{}, addr.address, tx.tx)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{sig}, nil
+}
+
+// CallOpts is the collection of options to fine tune a contract call request.
+type CallOpts struct {
+ opts bind.CallOpts
+}
+
+// NewCallOpts creates a new option set for contract calls.
+func NewCallOpts() *CallOpts {
+ return new(CallOpts)
+}
+
+func (opts *CallOpts) IsPending() bool { return opts.opts.Pending }
+func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ }
+
+// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
+// Even then it's awkward to unpack the subtleties of a Go context out to Java.
+// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} }
+
+func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending }
+func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ }
+func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context }
+
+// TransactOpts is the collection of authorization data required to create a
+// valid Ethereum transaction.
+type TransactOpts struct {
+ opts bind.TransactOpts
+}
+
+func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} }
+func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() }
+func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} }
+func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} }
+func (opts *TransactOpts) GetGasLimit() int64 { return opts.opts.GasLimit.Int64() }
+
+// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
+// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} }
+
+// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
+// Even then it's awkward to unpack the subtleties of a Go context out to Java.
+//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} }
+
+func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address }
+func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) }
+func (opts *TransactOpts) SetSigner(s Signer) {
+ opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ sig, err := s.Sign(&Address{addr}, &Transaction{tx})
+ if err != nil {
+ return nil, err
+ }
+ return sig.tx, nil
+ }
+}
+func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint }
+func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint }
+func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = big.NewInt(limit) }
+func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context }
+
+// BoundContract is the base wrapper object that reflects a contract on the
+// Ethereum network. It contains a collection of methods that are used by the
+// higher level contract bindings to operate.
+type BoundContract struct {
+ contract *bind.BoundContract
+ address common.Address
+ deployer *types.Transaction
+}
+
+// DeployContract deploys a contract onto the Ethereum blockchain and binds the
+// deployment address with a wrapper.
+func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (*BoundContract, error) {
+ // Convert all the deployment parameters to Go types
+ params := make([]interface{}, len(args.objects))
+ for i, obj := range args.objects {
+ params[i] = obj
+ }
+ // Deploy the contract to the network
+ parsed, err := abi.JSON(strings.NewReader(abiJSON))
+ if err != nil {
+ return nil, err
+ }
+ addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, bytecode, client.client, params...)
+ if err != nil {
+ return nil, err
+ }
+ return &BoundContract{
+ contract: bound,
+ address: addr,
+ deployer: tx,
+ }, nil
+}
+
+// BindContract creates a low level contract interface through which calls and
+// transactions may be made through.
+func BindContract(address *Address, abiJSON string, client *EthereumClient) (*BoundContract, error) {
+ parsed, err := abi.JSON(strings.NewReader(abiJSON))
+ if err != nil {
+ return nil, err
+ }
+ return &BoundContract{
+ contract: bind.NewBoundContract(address.address, parsed, client.client, client.client),
+ address: address.address,
+ }, nil
+}
+
+func (c *BoundContract) GetAddress() *Address { return &Address{c.address} }
+func (c *BoundContract) GetDeployer() *Transaction {
+ if c.deployer == nil {
+ return nil
+ }
+ return &Transaction{c.deployer}
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result.
+func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error {
+ // Convert all the input and output parameters to Go types
+ params := make([]interface{}, len(args.objects))
+ for i, obj := range args.objects {
+ params[i] = obj
+ }
+ results := make([]interface{}, len(out.objects))
+ for i, obj := range out.objects {
+ results[i] = obj
+ }
+ // Execute the call to the contract and wrap any results
+ if err := c.contract.Call(&opts.opts, &results, method, params...); err != nil {
+ return err
+ }
+ for i, res := range results {
+ out.objects[i] = res
+ }
+ return nil
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (*Transaction, error) {
+ params := make([]interface{}, len(args.objects))
+ for i, obj := range args.objects {
+ params[i] = obj
+ }
+ tx, err := c.contract.Transact(&opts.opts, method, params)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{tx}, nil
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (c *BoundContract) Transfer(opts *TransactOpts) (*Transaction, error) {
+ tx, err := c.contract.Transfer(&opts.opts)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{tx}, nil
+}
diff --git a/mobile/common.go b/mobile/common.go
new file mode 100644
index 000000000..ab1810bf1
--- /dev/null
+++ b/mobile/common.go
@@ -0,0 +1,187 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the common package.
+
+package geth
+
+import (
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Hash represents the 32 byte Keccak256 hash of arbitrary data.
+type Hash struct {
+ hash common.Hash
+}
+
+// NewHashFromBytes converts a slice of bytes to a hash value.
+func NewHashFromBytes(hash []byte) (*Hash, error) {
+ h := new(Hash)
+ if err := h.SetBytes(hash); err != nil {
+ return nil, err
+ }
+ return h, nil
+}
+
+// NewHashFromHex converts a hex string to a hash value.
+func NewHashFromHex(hash string) (*Hash, error) {
+ h := new(Hash)
+ if err := h.SetHex(hash); err != nil {
+ return nil, err
+ }
+ return h, nil
+}
+
+// SetBytes sets the specified slice of bytes as the hash value.
+func (h *Hash) SetBytes(hash []byte) error {
+ if length := len(hash); length != common.HashLength {
+ return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength)
+ }
+ copy(h.hash[:], hash)
+ return nil
+}
+
+// GetBytes retrieves the byte representation of the hash.
+func (h *Hash) GetBytes() []byte {
+ return h.hash[:]
+}
+
+// SetHex sets the specified hex string as the hash value.
+func (h *Hash) SetHex(hash string) error {
+ hash = strings.ToLower(hash)
+ if len(hash) >= 2 && hash[:2] == "0x" {
+ hash = hash[2:]
+ }
+ if length := len(hash); length != 2*common.HashLength {
+ return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength)
+ }
+ bin, err := hex.DecodeString(hash)
+ if err != nil {
+ return err
+ }
+ copy(h.hash[:], bin)
+ return nil
+}
+
+// GetHex retrieves the hex string representation of the hash.
+func (h *Hash) GetHex() string {
+ return h.hash.Hex()
+}
+
+// Hashes represents a slice of hashes.
+type Hashes struct{ hashes []common.Hash }
+
+// Size returns the number of hashes in the slice.
+func (h *Hashes) Size() int {
+ return len(h.hashes)
+}
+
+// Get returns the hash at the given index from the slice.
+func (h *Hashes) Get(index int) (*Hash, error) {
+ if index < 0 || index >= len(h.hashes) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Hash{h.hashes[index]}, nil
+}
+
+// Address represents the 20 byte address of an Ethereum account.
+type Address struct {
+ address common.Address
+}
+
+// NewAddressFromBytes converts a slice of bytes to a hash value.
+func NewAddressFromBytes(address []byte) (*Address, error) {
+ a := new(Address)
+ if err := a.SetBytes(address); err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// NewAddressFromHex converts a hex string to a address value.
+func NewAddressFromHex(address string) (*Address, error) {
+ a := new(Address)
+ if err := a.SetHex(address); err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// SetBytes sets the specified slice of bytes as the address value.
+func (a *Address) SetBytes(address []byte) error {
+ if length := len(address); length != common.AddressLength {
+ return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength)
+ }
+ copy(a.address[:], address)
+ return nil
+}
+
+// GetBytes retrieves the byte representation of the address.
+func (a *Address) GetBytes() []byte {
+ return a.address[:]
+}
+
+// SetHex sets the specified hex string as the address value.
+func (a *Address) SetHex(address string) error {
+ address = strings.ToLower(address)
+ if len(address) >= 2 && address[:2] == "0x" {
+ address = address[2:]
+ }
+ if length := len(address); length != 2*common.AddressLength {
+ return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength)
+ }
+ bin, err := hex.DecodeString(address)
+ if err != nil {
+ return err
+ }
+ copy(a.address[:], bin)
+ return nil
+}
+
+// GetHex retrieves the hex string representation of the address.
+func (a *Address) GetHex() string {
+ return a.address.Hex()
+}
+
+// Addresses represents a slice of addresses.
+type Addresses struct{ addresses []common.Address }
+
+// Size returns the number of addresses in the slice.
+func (a *Addresses) Size() int {
+ return len(a.addresses)
+}
+
+// Get returns the address at the given index from the slice.
+func (a *Addresses) Get(index int) (*Address, error) {
+ if index < 0 || index >= len(a.addresses) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Address{a.addresses[index]}, nil
+}
+
+// Set sets the address at the given index in the slice.
+func (a *Addresses) Set(index int, address *Address) error {
+ if index < 0 || index >= len(a.addresses) {
+ return errors.New("index out of bounds")
+ }
+ a.addresses[index] = address.address
+ return nil
+}
diff --git a/mobile/context.go b/mobile/context.go
new file mode 100644
index 000000000..9df94b689
--- /dev/null
+++ b/mobile/context.go
@@ -0,0 +1,81 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the golang.org/x/net/context package to support
+// client side context management on mobile platforms.
+
+package geth
+
+import (
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+// Context carries a deadline, a cancelation signal, and other values across API
+// boundaries.
+type Context struct {
+ context context.Context
+ cancel context.CancelFunc
+}
+
+// NewContext returns a non-nil, empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming requests.
+func NewContext() *Context {
+ return &Context{
+ context: context.Background(),
+ }
+}
+
+// WithCancel returns a copy of the original context with cancellation mechanism
+// included.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func (c *Context) WithCancel() *Context {
+ child, cancel := context.WithCancel(c.context)
+ return &Context{
+ context: child,
+ cancel: cancel,
+ }
+}
+
+// WithDeadline returns a copy of the original context with the deadline adjusted
+// to be no later than the specified time.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func (c *Context) WithDeadline(sec int64, nsec int64) *Context {
+ child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec))
+ return &Context{
+ context: child,
+ cancel: cancel,
+ }
+}
+
+// WithTimeout returns a copy of the original context with the deadline adjusted
+// to be no later than now + the duration specified.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func (c *Context) WithTimeout(nsec int64) *Context {
+ child, cancel := context.WithTimeout(c.context, time.Duration(nsec))
+ return &Context{
+ context: child,
+ cancel: cancel,
+ }
+}
diff --git a/mobile/discover.go b/mobile/discover.go
new file mode 100644
index 000000000..9df2d04c3
--- /dev/null
+++ b/mobile/discover.go
@@ -0,0 +1,104 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the accounts package to support client side enode
+// management on mobile platforms.
+
+package geth
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/p2p/discv5"
+)
+
+// Enode represents a host on the network.
+type Enode struct {
+ node *discv5.Node
+}
+
+// NewEnode parses a node designator.
+//
+// There are two basic forms of node designators
+// - incomplete nodes, which only have the public key (node ID)
+// - complete nodes, which contain the public key and IP/Port information
+//
+// For incomplete nodes, the designator must look like one of these
+//
+// enode://<hex node id>
+// <hex node id>
+//
+// For complete nodes, the node ID is encoded in the username portion
+// of the URL, separated from the host by an @ sign. The hostname can
+// only be given as an IP address, DNS domain names are not allowed.
+// The port in the host name section is the TCP listening port. If the
+// TCP and UDP (discovery) ports differ, the UDP port is specified as
+// query parameter "discport".
+//
+// In the following example, the node URL describes
+// a node with IP address 10.3.58.6, TCP listening port 30303
+// and UDP discovery port 30301.
+//
+// enode://<hex node id>@10.3.58.6:30303?discport=30301
+func NewEnode(rawurl string) (*Enode, error) {
+ node, err := discv5.ParseNode(rawurl)
+ if err != nil {
+ return nil, err
+ }
+ return &Enode{node}, nil
+}
+
+// Enodes represents a slice of accounts.
+type Enodes struct{ nodes []*discv5.Node }
+
+// NewEnodes creates a slice of uninitialized enodes.
+func NewEnodes(size int) *Enodes {
+ return &Enodes{
+ nodes: make([]*discv5.Node, size),
+ }
+}
+
+// NewEnodesEmpty creates an empty slice of Enode values.
+func NewEnodesEmpty() *Enodes {
+ return NewEnodes(0)
+}
+
+// Size returns the number of enodes in the slice.
+func (e *Enodes) Size() int {
+ return len(e.nodes)
+}
+
+// Get returns the enode at the given index from the slice.
+func (e *Enodes) Get(index int) (*Enode, error) {
+ if index < 0 || index >= len(e.nodes) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Enode{e.nodes[index]}, nil
+}
+
+// Set sets the enode at the given index in the slice.
+func (e *Enodes) Set(index int, enode *Enode) error {
+ if index < 0 || index >= len(e.nodes) {
+ return errors.New("index out of bounds")
+ }
+ e.nodes[index] = enode.node
+ return nil
+}
+
+// Append adds a new enode element to the end of the slice.
+func (e *Enodes) Append(enode *Enode) {
+ e.nodes = append(e.nodes, enode.node)
+}
diff --git a/mobile/doc.go b/mobile/doc.go
new file mode 100644
index 000000000..50cc7f4f8
--- /dev/null
+++ b/mobile/doc.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Package geth contains the simplified mobile APIs to go-ethereum.
+//
+// The scope of this package is *not* to allow writing a custom Ethereun client
+// with pieces plucked from go-ethereum, rather to allow writing native dapps on
+// mobile platforms. Keep this in mind when using or extending this package!
+//
+// API limitations
+//
+// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the
+// exposed APIs need to be manually wrapped into simplified types, with custom
+// constructors and getters/setters to ensure that they can be meaninfully used
+// from Java/ObjC too.
+//
+// With this in mind, please try to limit the scope of this package and only add
+// essentials without which mobile support cannot work, especially since manually
+// syncing the code will be unwieldy otherwise. In the long term we might consider
+// writing custom library generators, but those are out of scope now.
+//
+// Content wise each file in this package corresponds to an entire Go package
+// from the go-ethereum repository. Please adhere to this scoping to prevent this
+// package getting unmaintainable.
+//
+// Wrapping guidelines:
+//
+// Every type that is to be exposed should be wrapped into its own plain struct,
+// which internally contains a single field: the original go-ethereum version.
+// This is needed because gomobile cannot expose named types for now.
+//
+// Whenever a method argument or a return type is a custom struct, the pointer
+// variant should always be used as value types crossing over between language
+// boundaries might have strange behaviors.
+//
+// Slices of types should be converted into a single multiplicative type wrapping
+// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations
+// should not be provided to limit the remote code complexity. Arrays should be
+// avoided as much as possible since they complicate bounds checking.
+//
+// Note, a panic *cannot* cross over language boundaries, instead will result in
+// an undebuggable SEGFAULT in the process. For error handling only ever use error
+// returns, which may be the only or the second return.
+package geth
diff --git a/mobile/ethclient.go b/mobile/ethclient.go
new file mode 100644
index 000000000..668d65e32
--- /dev/null
+++ b/mobile/ethclient.go
@@ -0,0 +1,305 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains a wrapper for the Ethereum client.
+
+package geth
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/ethclient"
+)
+
+// EthereumClient provides access to the Ethereum APIs.
+type EthereumClient struct {
+ client *ethclient.Client
+}
+
+// NewEthereumClient connects a client to the given URL.
+func NewEthereumClient(rawurl string) (*EthereumClient, error) {
+ client, err := ethclient.Dial(rawurl)
+ return &EthereumClient{client}, err
+}
+
+// GetBlockByHash returns the given full block.
+func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (*Block, error) {
+ block, err := ec.client.BlockByHash(ctx.context, hash.hash)
+ return &Block{block}, err
+}
+
+// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the
+// latest known block is returned.
+func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (*Block, error) {
+ if number < 0 {
+ block, err := ec.client.BlockByNumber(ctx.context, nil)
+ return &Block{block}, err
+ }
+ block, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number))
+ return &Block{block}, err
+}
+
+// GetHeaderByHash returns the block header with the given hash.
+func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (*Header, error) {
+ header, err := ec.client.HeaderByHash(ctx.context, hash.hash)
+ return &Header{header}, err
+}
+
+// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0,
+// the latest known header is returned.
+func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (*Header, error) {
+ if number < 0 {
+ header, err := ec.client.HeaderByNumber(ctx.context, nil)
+ return &Header{header}, err
+ }
+ header, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number))
+ return &Header{header}, err
+}
+
+// GetTransactionByHash returns the transaction with the given hash.
+func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (*Transaction, error) {
+ tx, err := ec.client.TransactionByHash(ctx.context, hash.hash)
+ return &Transaction{tx}, err
+}
+
+// GetTransactionCount returns the total number of transactions in the given block.
+func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (int, error) {
+ count, err := ec.client.TransactionCount(ctx.context, hash.hash)
+ return int(count), err
+}
+
+// GetTransactionInBlock returns a single transaction at index in the given block.
+func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (*Transaction, error) {
+ tx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index))
+ return &Transaction{tx}, err
+
+}
+
+// GetTransactionReceipt returns the receipt of a transaction by transaction hash.
+// Note that the receipt is not available for pending transactions.
+func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (*Receipt, error) {
+ receipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash)
+ return &Receipt{receipt}, err
+}
+
+// SyncProgress retrieves the current progress of the sync algorithm. If there's
+// no sync currently running, it returns nil.
+func (ec *EthereumClient) SyncProgress(ctx *Context) (*SyncProgress, error) {
+ progress, err := ec.client.SyncProgress(ctx.context)
+ if progress == nil {
+ return nil, err
+ }
+ return &SyncProgress{*progress}, err
+}
+
+// NewHeadHandler is a client-side subscription callback to invoke on events and
+// subscription failure.
+type NewHeadHandler interface {
+ OnNewHead(header *Header)
+ OnError(failure string)
+}
+
+// SubscribeNewHead subscribes to notifications about the current blockchain head
+// on the given channel.
+func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (*Subscription, error) {
+ // Subscribe to the event internally
+ ch := make(chan *types.Header, buffer)
+ sub, err := ec.client.SubscribeNewHead(ctx.context, ch)
+ if err != nil {
+ return nil, err
+ }
+ // Start up a dispatcher to feed into the callback
+ go func() {
+ for {
+ select {
+ case header := <-ch:
+ handler.OnNewHead(&Header{header})
+
+ case err := <-sub.Err():
+ handler.OnError(err.Error())
+ return
+ }
+ }
+ }()
+ return &Subscription{sub}, nil
+}
+
+// State Access
+
+// GetBalanceAt returns the wei balance of the given account.
+// The block number can be <0, in which case the balance is taken from the latest known block.
+func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (*BigInt, error) {
+ if number < 0 {
+ balance, err := ec.client.BalanceAt(ctx.context, account.address, nil)
+ return &BigInt{balance}, err
+ }
+ balance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number))
+ return &BigInt{balance}, err
+}
+
+// GetStorageAt returns the value of key in the contract storage of the given account.
+// The block number can be <0, in which case the value is taken from the latest known block.
+func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) ([]byte, error) {
+ if number < 0 {
+ return ec.client.StorageAt(ctx.context, account.address, key.hash, nil)
+ }
+ return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number))
+}
+
+// GetCodeAt returns the contract code of the given account.
+// The block number can be <0, in which case the code is taken from the latest known block.
+func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) ([]byte, error) {
+ if number < 0 {
+ return ec.client.CodeAt(ctx.context, account.address, nil)
+ }
+ return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number))
+}
+
+// GetNonceAt returns the account nonce of the given account.
+// The block number can be <0, in which case the nonce is taken from the latest known block.
+func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (int64, error) {
+ if number < 0 {
+ nonce, err := ec.client.NonceAt(ctx.context, account.address, nil)
+ return int64(nonce), err
+ }
+ nonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number))
+ return int64(nonce), err
+}
+
+// Filters
+
+// FilterLogs executes a filter query.
+func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (*Logs, error) {
+ logs, err := ec.client.FilterLogs(ctx.context, query.query)
+ if err != nil {
+ return nil, err
+ }
+ // Temp hack due to vm.Logs being []*vm.Log
+ res := make(vm.Logs, len(logs))
+ for i, log := range logs {
+ res[i] = &log
+ }
+ return &Logs{res}, nil
+}
+
+// FilterLogsHandler is a client-side subscription callback to invoke on events and
+// subscription failure.
+type FilterLogsHandler interface {
+ OnFilterLogs(log *Log)
+ OnError(failure string)
+}
+
+// SubscribeFilterLogs subscribes to the results of a streaming filter query.
+func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (*Subscription, error) {
+ // Subscribe to the event internally
+ ch := make(chan vm.Log, buffer)
+ sub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch)
+ if err != nil {
+ return nil, err
+ }
+ // Start up a dispatcher to feed into the callback
+ go func() {
+ for {
+ select {
+ case log := <-ch:
+ handler.OnFilterLogs(&Log{&log})
+
+ case err := <-sub.Err():
+ handler.OnError(err.Error())
+ return
+ }
+ }
+ }()
+ return &Subscription{sub}, nil
+}
+
+// Pending State
+
+// GetPendingBalanceAt returns the wei balance of the given account in the pending state.
+func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (*BigInt, error) {
+ balance, err := ec.client.PendingBalanceAt(ctx.context, account.address)
+ return &BigInt{balance}, err
+}
+
+// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
+func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) ([]byte, error) {
+ return ec.client.PendingStorageAt(ctx.context, account.address, key.hash)
+}
+
+// GetPendingCodeAt returns the contract code of the given account in the pending state.
+func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) ([]byte, error) {
+ return ec.client.PendingCodeAt(ctx.context, account.address)
+}
+
+// GetPendingNonceAt returns the account nonce of the given account in the pending state.
+// This is the nonce that should be used for the next transaction.
+func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (int64, error) {
+ nonce, err := ec.client.PendingNonceAt(ctx.context, account.address)
+ return int64(nonce), err
+}
+
+// GetPendingTransactionCount returns the total number of transactions in the pending state.
+func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (int, error) {
+ count, err := ec.client.PendingTransactionCount(ctx.context)
+ return int(count), err
+}
+
+// Contract Calling
+
+// CallContract executes a message call transaction, which is directly executed in the VM
+// of the node, but never mined into the blockchain.
+//
+// blockNumber selects the block height at which the call runs. It can be <0, in which
+// case the code is taken from the latest known block. Note that state from very old
+// blocks might not be available.
+func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) ([]byte, error) {
+ if number < 0 {
+ return ec.client.CallContract(ctx.context, msg.msg, nil)
+ }
+ return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number))
+}
+
+// PendingCallContract executes a message call transaction using the EVM.
+// The state seen by the contract call is the pending state.
+func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) ([]byte, error) {
+ return ec.client.PendingCallContract(ctx.context, msg.msg)
+}
+
+// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+// execution of a transaction.
+func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (*BigInt, error) {
+ price, err := ec.client.SuggestGasPrice(ctx.context)
+ return &BigInt{price}, err
+}
+
+// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
+// the current pending state of the backend blockchain. There is no guarantee that this is
+// the true gas limit requirement as other transactions may be added or removed by miners,
+// but it should provide a basis for setting a reasonable default.
+func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (*BigInt, error) {
+ price, err := ec.client.EstimateGas(ctx.context, msg.msg)
+ return &BigInt{price}, err
+}
+
+// SendTransaction injects a signed transaction into the pending pool for execution.
+//
+// If the transaction was a contract creation use the TransactionReceipt method to get the
+// contract address after the transaction has been mined.
+func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error {
+ return ec.client.SendTransaction(ctx.context, tx.tx)
+}
diff --git a/mobile/ethereum.go b/mobile/ethereum.go
new file mode 100644
index 000000000..6e8046ac9
--- /dev/null
+++ b/mobile/ethereum.go
@@ -0,0 +1,125 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the go-ethereum root package.
+
+package geth
+
+import (
+ "errors"
+ "math/big"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Subscription represents an event subscription where events are
+// delivered on a data channel.
+type Subscription struct {
+ sub ethereum.Subscription
+}
+
+// Unsubscribe cancels the sending of events to the data channel
+// and closes the error channel.
+func (s *Subscription) Unsubscribe() {
+ s.sub.Unsubscribe()
+}
+
+// CallMsg contains parameters for contract calls.
+type CallMsg struct {
+ msg ethereum.CallMsg
+}
+
+// NewCallMsg creates an empty contract call parameter list.
+func NewCallMsg() *CallMsg {
+ return new(CallMsg)
+}
+
+func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} }
+func (msg *CallMsg) GetGas() int64 { return msg.msg.Gas.Int64() }
+func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} }
+func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} }
+func (msg *CallMsg) GetData() []byte { return msg.msg.Data }
+func (msg *CallMsg) GetTo() *Address {
+ if to := msg.msg.To; to != nil {
+ return &Address{*msg.msg.To}
+ }
+ return nil
+}
+
+func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address }
+func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = big.NewInt(gas) }
+func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint }
+func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint }
+func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = data }
+func (msg *CallMsg) SetTo(address *Address) {
+ if address == nil {
+ msg.msg.To = nil
+ }
+ msg.msg.To = &address.address
+}
+
+// SyncProgress gives progress indications when the node is synchronising with
+// the Ethereum network.
+type SyncProgress struct {
+ progress ethereum.SyncProgress
+}
+
+func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) }
+func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) }
+func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) }
+func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) }
+func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) }
+
+// Topics is a set of topic lists to filter events with.
+type Topics struct{ topics [][]common.Hash }
+
+// Size returns the number of topic lists inside the set
+func (t *Topics) Size() int {
+ return len(t.topics)
+}
+
+// Get returns the topic list at the given index from the slice.
+func (t *Topics) Get(index int) (*Hashes, error) {
+ if index < 0 || index >= len(t.topics) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Hashes{t.topics[index]}, nil
+}
+
+// Set sets the topic list at the given index in the slice.
+func (t *Topics) Set(index int, topics *Hashes) error {
+ if index < 0 || index >= len(t.topics) {
+ return errors.New("index out of bounds")
+ }
+ t.topics[index] = topics.hashes
+ return nil
+}
+
+// FilterQuery contains options for contact log filtering.
+type FilterQuery struct {
+ query ethereum.FilterQuery
+}
+
+// NewFilterQuery creates an empty filter query for contact log filtering.
+func NewFilterQuery() *FilterQuery {
+ return new(FilterQuery)
+}
+
+func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} }
+func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} }
+func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} }
+func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} }
diff --git a/mobile/geth.go b/mobile/geth.go
new file mode 100644
index 000000000..d7f0800e0
--- /dev/null
+++ b/mobile/geth.go
@@ -0,0 +1,200 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the node package to support client side node
+// management on mobile platforms.
+
+package geth
+
+import (
+ "fmt"
+ "math/big"
+ "path/filepath"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/ethereum/go-ethereum/les"
+ "github.com/ethereum/go-ethereum/light"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p/nat"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/whisper/whisperv2"
+)
+
+// NodeConfig represents the collection of configuration values to fine tune the Geth
+// node embedded into a mobile process. The available values are a subset of the
+// entire API provided by go-ethereum to reduce the maintenance surface and dev
+// complexity.
+type NodeConfig struct {
+ // Bootstrap nodes used to establish connectivity with the rest of the network.
+ BootstrapNodes *Enodes
+
+ // MaxPeers is the maximum number of peers that can be connected. If this is
+ // set to zero, then only the configured static and trusted peers can connect.
+ MaxPeers int
+
+ // EthereumEnabled specifies whether the node should run the Ethereum protocol.
+ EthereumEnabled bool
+
+ // EthereumNetworkID is the network identifier used by the Ethereum protocol to
+ // decide if remote peers should be accepted or not.
+ EthereumNetworkID int
+
+ // EthereumChainConfig is the default parameters of the blockchain to use. If no
+ // configuration is specified, it defaults to the main network.
+ EthereumChainConfig *ChainConfig
+
+ // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An
+ // empty genesis state is equivalent to using the mainnet's state.
+ EthereumGenesis string
+
+ // EthereumTestnetNonces specifies whether to use account nonces from the testnet
+ // range (2^20) or from the mainnet one (0).
+ EthereumTestnetNonces bool
+
+ // EthereumDatabaseCache is the system memory in MB to allocate for database caching.
+ // A minimum of 16MB is always reserved.
+ EthereumDatabaseCache int
+
+ // WhisperEnabled specifies whether the node should run the Whisper protocol.
+ WhisperEnabled bool
+}
+
+// defaultNodeConfig contains the default node configuration values to use if all
+// or some fields are missing from the user's specified list.
+var defaultNodeConfig = &NodeConfig{
+ BootstrapNodes: FoundationBootnodes(),
+ MaxPeers: 25,
+ EthereumEnabled: true,
+ EthereumNetworkID: 1,
+ EthereumChainConfig: MainnetChainConfig(),
+ EthereumDatabaseCache: 16,
+}
+
+// NewNodeConfig creates a new node option set, initialized to the default values.
+func NewNodeConfig() *NodeConfig {
+ config := *defaultNodeConfig
+ return &config
+}
+
+// Node represents a Geth Ethereum node instance.
+type Node struct {
+ node *node.Node
+}
+
+// NewNode creates and configures a new Geth node.
+func NewNode(datadir string, config *NodeConfig) (*Node, error) {
+ // If no or partial configurations were specified, use defaults
+ if config == nil {
+ config = NewNodeConfig()
+ }
+ if config.MaxPeers == 0 {
+ config.MaxPeers = defaultNodeConfig.MaxPeers
+ }
+ if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 {
+ config.BootstrapNodes = defaultNodeConfig.BootstrapNodes
+ }
+ // Create the empty networking stack
+ nodeConf := &node.Config{
+ Name: clientIdentifier,
+ DataDir: datadir,
+ KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
+ NoDiscovery: true,
+ DiscoveryV5: true,
+ DiscoveryV5Addr: ":0",
+ BootstrapNodesV5: config.BootstrapNodes.nodes,
+ ListenAddr: ":0",
+ NAT: nat.Any(),
+ MaxPeers: config.MaxPeers,
+ }
+ stack, err := node.New(nodeConf)
+ if err != nil {
+ return nil, err
+ }
+ // Register the Ethereum protocol if requested
+ if config.EthereumEnabled {
+ ethConf := &eth.Config{
+ ChainConfig: &params.ChainConfig{
+ HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock),
+ DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock),
+ DAOForkSupport: config.EthereumChainConfig.DAOForkSupport,
+ EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block),
+ EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash,
+ EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block),
+ EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block),
+ },
+ Genesis: config.EthereumGenesis,
+ LightMode: true,
+ DatabaseCache: config.EthereumDatabaseCache,
+ NetworkId: config.EthereumNetworkID,
+ GasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon),
+ GpoMinGasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon),
+ GpoMaxGasPrice: new(big.Int).Mul(big.NewInt(500), common.Shannon),
+ GpoFullBlockRatio: 80,
+ GpobaseStepDown: 10,
+ GpobaseStepUp: 100,
+ GpobaseCorrectionFactor: 110,
+ }
+ if config.EthereumTestnetNonces {
+ state.StartingNonce = 1048576 // (2**20)
+ light.StartingNonce = 1048576 // (2**20)
+ }
+ if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ return les.New(ctx, ethConf)
+ }); err != nil {
+ return nil, fmt.Errorf("ethereum init: %v", err)
+ }
+ }
+ // Register the Whisper protocol if requested
+ if config.WhisperEnabled {
+ if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil {
+ return nil, fmt.Errorf("whisper init: %v", err)
+ }
+ }
+ return &Node{stack}, nil
+}
+
+// Start creates a live P2P node and starts running it.
+func (n *Node) Start() error {
+ return n.node.Start()
+}
+
+// Stop terminates a running node along with all it's services. In the node was
+// not started, an error is returned.
+func (n *Node) Stop() error {
+ return n.node.Stop()
+}
+
+// GetEthereumClient retrieves a client to access the Ethereum subsystem.
+func (n *Node) GetEthereumClient() (*EthereumClient, error) {
+ rpc, err := n.node.Attach()
+ if err != nil {
+ return nil, err
+ }
+ return &EthereumClient{ethclient.NewClient(rpc)}, nil
+}
+
+// GetNodeInfo gathers and returns a collection of metadata known about the host.
+func (n *Node) GetNodeInfo() *NodeInfo {
+ return &NodeInfo{n.node.Server().NodeInfo()}
+}
+
+// GetPeersInfo returns an array of metadata objects describing connected peers.
+func (n *Node) GetPeersInfo() *PeerInfos {
+ return &PeerInfos{n.node.Server().PeersInfo()}
+}
diff --git a/mobile/geth_android.go b/mobile/geth_android.go
new file mode 100644
index 000000000..8e4ebe638
--- /dev/null
+++ b/mobile/geth_android.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build android
+
+package geth
+
+// clientIdentifier is a hard coded identifier to report into the network.
+var clientIdentifier = "GethDroid"
diff --git a/mobile/geth_ios.go b/mobile/geth_ios.go
new file mode 100644
index 000000000..307cd0858
--- /dev/null
+++ b/mobile/geth_ios.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build ios
+
+package geth
+
+// clientIdentifier is a hard coded identifier to report into the network.
+var clientIdentifier = "iGeth"
diff --git a/mobile/geth_other.go b/mobile/geth_other.go
new file mode 100644
index 000000000..6f0c5dda6
--- /dev/null
+++ b/mobile/geth_other.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build !android,!ios
+
+package geth
+
+// clientIdentifier is a hard coded identifier to report into the network.
+var clientIdentifier = "GethMobile"
diff --git a/mobile/init.go b/mobile/init.go
new file mode 100644
index 000000000..0fbc6bd3e
--- /dev/null
+++ b/mobile/init.go
@@ -0,0 +1,35 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains initialization code for the mbile library.
+
+package geth
+
+import (
+ "runtime"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+)
+
+func init() {
+ // Initialize the logger
+ glog.SetV(logger.Info)
+ glog.SetToStderr(true)
+
+ // Initialize the goroutine count
+ runtime.GOMAXPROCS(runtime.NumCPU())
+}
diff --git a/mobile/interface.go b/mobile/interface.go
new file mode 100644
index 000000000..b585b8642
--- /dev/null
+++ b/mobile/interface.go
@@ -0,0 +1,148 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains perverted wrappers to allow crossing over empty interfaces.
+
+package geth
+
+import (
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Interface represents a wrapped version of Go's interface{}, with the capacity
+// to store arbitrary data types.
+//
+// Since it's impossible to get the arbitrary-ness converted between Go and mobile
+// platforms, we're using explicit getters and setters for the conversions. There
+// is of course no point in enumerating everything, just enough to support the
+// contract bindins requiring client side generated code.
+type Interface struct {
+ object interface{}
+}
+
+// NewInterface creates a new empty interface that can be used to pass around
+// generic types.
+func NewInterface() *Interface {
+ return new(Interface)
+}
+
+func (i *Interface) SetBool(b bool) { i.object = &b }
+func (i *Interface) SetBools(bs []bool) { i.object = &bs }
+func (i *Interface) SetString(str string) { i.object = &str }
+func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs }
+func (i *Interface) SetBinary(binary []byte) { i.object = &binary }
+func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries }
+func (i *Interface) SetAddress(address *Address) { i.object = &address.address }
+func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses }
+func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash }
+func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes }
+func (i *Interface) SetInt8(n int8) { i.object = &n }
+func (i *Interface) SetInt16(n int16) { i.object = &n }
+func (i *Interface) SetInt32(n int32) { i.object = &n }
+func (i *Interface) SetInt64(n int64) { i.object = &n }
+func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n }
+func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n }
+func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n }
+func (i *Interface) SetUint64(bigint *BigInt) { n := uint64(bigint.bigint.Uint64()); i.object = &n }
+func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint }
+func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints }
+
+func (i *Interface) SetDefaultBool() { i.object = new(bool) }
+func (i *Interface) SetDefaultBools() { i.object = new([]bool) }
+func (i *Interface) SetDefaultString() { i.object = new(string) }
+func (i *Interface) SetDefaultStrings() { i.object = new([]string) }
+func (i *Interface) SetDefaultBinary() { i.object = new([]byte) }
+func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) }
+func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) }
+func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) }
+func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) }
+func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) }
+func (i *Interface) SetDefaultInt8() { i.object = new(int8) }
+func (i *Interface) SetDefaultInt16() { i.object = new(int16) }
+func (i *Interface) SetDefaultInt32() { i.object = new(int32) }
+func (i *Interface) SetDefaultInt64() { i.object = new(int64) }
+func (i *Interface) SetDefaultUint8() { i.object = new(uint8) }
+func (i *Interface) SetDefaultUint16() { i.object = new(uint16) }
+func (i *Interface) SetDefaultUint32() { i.object = new(uint32) }
+func (i *Interface) SetDefaultUint64() { i.object = new(uint64) }
+func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) }
+func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) }
+
+func (i *Interface) GetBool() bool { return *i.object.(*bool) }
+func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) }
+func (i *Interface) GetString() string { return *i.object.(*string) }
+func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} }
+func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) }
+func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) }
+func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} }
+func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} }
+func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} }
+func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} }
+func (i *Interface) GetInt8() int8 { return *i.object.(*int8) }
+func (i *Interface) GetInt16() int16 { return *i.object.(*int16) }
+func (i *Interface) GetInt32() int32 { return *i.object.(*int32) }
+func (i *Interface) GetInt64() int64 { return *i.object.(*int64) }
+func (i *Interface) GetUint8() *BigInt {
+ return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))}
+}
+func (i *Interface) GetUint16() *BigInt {
+ return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))}
+}
+func (i *Interface) GetUint32() *BigInt {
+ return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))}
+}
+func (i *Interface) GetUint64() *BigInt {
+ return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))}
+}
+func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} }
+func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} }
+
+// Interfaces is a slices of wrapped generic objects.
+type Interfaces struct {
+ objects []interface{}
+}
+
+// NewInterfaces creates a slice of uninitialized interfaces.
+func NewInterfaces(size int) *Interfaces {
+ return &Interfaces{
+ objects: make([]interface{}, size),
+ }
+}
+
+// Size returns the number of interfaces in the slice.
+func (i *Interfaces) Size() int {
+ return len(i.objects)
+}
+
+// Get returns the bigint at the given index from the slice.
+func (i *Interfaces) Get(index int) (*Interface, error) {
+ if index < 0 || index >= len(i.objects) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Interface{i.objects[index]}, nil
+}
+
+// Set sets the big int at the given index in the slice.
+func (i *Interfaces) Set(index int, object *Interface) error {
+ if index < 0 || index >= len(i.objects) {
+ return errors.New("index out of bounds")
+ }
+ i.objects[index] = object.object
+ return nil
+}
diff --git a/mobile/p2p.go b/mobile/p2p.go
new file mode 100644
index 000000000..97ae626dc
--- /dev/null
+++ b/mobile/p2p.go
@@ -0,0 +1,74 @@
+// Copyright 2016 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 pi copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains wrappers for the p2p package.
+
+package geth
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/p2p"
+)
+
+// NodeInfo represents pi short summary of the information known about the host.
+type NodeInfo struct {
+ info *p2p.NodeInfo
+}
+
+func (ni *NodeInfo) GetID() string { return ni.info.ID }
+func (ni *NodeInfo) GetName() string { return ni.info.Name }
+func (ni *NodeInfo) GetEnode() string { return ni.info.Enode }
+func (ni *NodeInfo) GetIP() string { return ni.info.IP }
+func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery }
+func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener }
+func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr }
+func (ni *NodeInfo) GetProtocols() *Strings {
+ protos := []string{}
+ for proto, _ := range ni.info.Protocols {
+ protos = append(protos, proto)
+ }
+ return &Strings{protos}
+}
+
+// PeerInfo represents pi short summary of the information known about pi connected peer.
+type PeerInfo struct {
+ info *p2p.PeerInfo
+}
+
+func (pi *PeerInfo) GetID() string { return pi.info.ID }
+func (pi *PeerInfo) GetName() string { return pi.info.Name }
+func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} }
+func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress }
+func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress }
+
+// PeerInfos represents a slice of infos about remote peers.
+type PeerInfos struct {
+ infos []*p2p.PeerInfo
+}
+
+// Size returns the number of peer info entries in the slice.
+func (pi *PeerInfos) Size() int {
+ return len(pi.infos)
+}
+
+// Get returns the peer info at the given index from the slice.
+func (pi *PeerInfos) Get(index int) (*PeerInfo, error) {
+ if index < 0 || index >= len(pi.infos) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &PeerInfo{pi.infos[index]}, nil
+}
diff --git a/mobile/params.go b/mobile/params.go
new file mode 100644
index 000000000..48344a538
--- /dev/null
+++ b/mobile/params.go
@@ -0,0 +1,89 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the params package.
+
+package geth
+
+import (
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/p2p/discv5"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// MainnetChainConfig returns the chain configurations for the main Ethereum network.
+func MainnetChainConfig() *ChainConfig {
+ return &ChainConfig{
+ HomesteadBlock: params.MainNetHomesteadBlock.Int64(),
+ DAOForkBlock: params.MainNetDAOForkBlock.Int64(),
+ DAOForkSupport: true,
+ EIP150Block: params.MainNetHomesteadGasRepriceBlock.Int64(),
+ EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash},
+ EIP155Block: params.MainNetSpuriousDragon.Int64(),
+ EIP158Block: params.MainNetSpuriousDragon.Int64(),
+ }
+}
+
+// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It
+// is actually empty since that defaults to the hard coded binary genesis block.
+func MainnetGenesis() string {
+ return ""
+}
+
+// TestnetChainConfig returns the chain configurations for the Ethereum test network.
+func TestnetChainConfig() *ChainConfig {
+ return &ChainConfig{
+ HomesteadBlock: params.TestNetHomesteadBlock.Int64(),
+ DAOForkBlock: 0,
+ DAOForkSupport: false,
+ EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(),
+ EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash},
+ EIP155Block: params.TestNetSpuriousDragon.Int64(),
+ EIP158Block: params.TestNetSpuriousDragon.Int64(),
+ }
+}
+
+// TestnetGenesis returns the JSON spec to use for the Ethereum test network.
+func TestnetGenesis() string {
+ return core.TestNetGenesisBlock()
+}
+
+// ChainConfig is the core config which determines the blockchain settings.
+type ChainConfig struct {
+ HomesteadBlock int64 // Homestead switch block
+ DAOForkBlock int64 // TheDAO hard-fork switch block
+ DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork
+ EIP150Block int64 // Homestead gas reprice switch block
+ EIP150Hash Hash // Homestead gas reprice switch block hash
+ EIP155Block int64 // Replay protection switch block
+ EIP158Block int64 // Empty account pruning switch block
+}
+
+// NewChainConfig creates a new chain configuration that transitions immediately
+// to homestead and has no notion of the DAO fork (ideal for a private network).
+func NewChainConfig() *ChainConfig {
+ return new(ChainConfig)
+}
+
+// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated
+// by the foundation running the V5 discovery protocol.
+func FoundationBootnodes() *Enodes {
+ nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))}
+ for i, node := range params.DiscoveryV5Bootnodes {
+ nodes.nodes[i] = node
+ }
+ return nodes
+}
diff --git a/mobile/primitives.go b/mobile/primitives.go
new file mode 100644
index 000000000..28f402d4f
--- /dev/null
+++ b/mobile/primitives.go
@@ -0,0 +1,54 @@
+// Copyright 2016 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 s copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains various wrappers for primitive types.
+
+package geth
+
+import (
+ "errors"
+ "fmt"
+)
+
+// Strings represents s slice of strs.
+type Strings struct{ strs []string }
+
+// Size returns the number of strs in the slice.
+func (s *Strings) Size() int {
+ return len(s.strs)
+}
+
+// Get returns the string at the given index from the slice.
+func (s *Strings) Get(index int) (string, error) {
+ if index < 0 || index >= len(s.strs) {
+ return "", errors.New("index out of bounds")
+ }
+ return s.strs[index], nil
+}
+
+// Set sets the string at the given index in the slice.
+func (s *Strings) Set(index int, str string) error {
+ if index < 0 || index >= len(s.strs) {
+ return errors.New("index out of bounds")
+ }
+ s.strs[index] = str
+ return nil
+}
+
+// String implements the Stringer interface.
+func (s *Strings) String() string {
+ return fmt.Sprintf("%v", s.strs)
+}
diff --git a/mobile/types.go b/mobile/types.go
new file mode 100644
index 000000000..bb5ccc625
--- /dev/null
+++ b/mobile/types.go
@@ -0,0 +1,189 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the core/types package.
+
+package geth
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that
+// a sufficient amount of computation has been carried out on a block.
+type Nonce struct {
+ nonce types.BlockNonce
+}
+
+// GetBytes retrieves the byte representation of the block nonce.
+func (n *Nonce) GetBytes() []byte {
+ return n.nonce[:]
+}
+
+// GetHex retrieves the hex string representation of the block nonce.
+func (n *Nonce) GetHex() string {
+ return fmt.Sprintf("0x%x", n.nonce[:])
+}
+
+// Bloom represents a 256 bit bloom filter.
+type Bloom struct {
+ bloom types.Bloom
+}
+
+// GetBytes retrieves the byte representation of the bloom filter.
+func (b *Bloom) GetBytes() []byte {
+ return b.bloom[:]
+}
+
+// GetHex retrieves the hex string representation of the bloom filter.
+func (b *Bloom) GetHex() string {
+ return fmt.Sprintf("0x%x", b.bloom[:])
+}
+
+// Header represents a block header in the Ethereum blockchain.
+type Header struct {
+ header *types.Header
+}
+
+func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} }
+func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} }
+func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} }
+func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} }
+func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} }
+func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} }
+func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} }
+func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} }
+func (h *Header) GetNumber() int64 { return h.header.Number.Int64() }
+func (h *Header) GetGasLimit() int64 { return h.header.GasLimit.Int64() }
+func (h *Header) GetGasUsed() int64 { return h.header.GasUsed.Int64() }
+func (h *Header) GetTime() int64 { return h.header.Time.Int64() }
+func (h *Header) GetExtra() []byte { return h.header.Extra }
+func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} }
+func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} }
+
+func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} }
+func (h *Header) GetHashNoNonce() *Hash { return &Hash{h.header.HashNoNonce()} }
+
+// Headers represents a slice of headers.
+type Headers struct{ headers []*types.Header }
+
+// Size returns the number of headers in the slice.
+func (h *Headers) Size() int {
+ return len(h.headers)
+}
+
+// Get returns the header at the given index from the slice.
+func (h *Headers) Get(index int) (*Header, error) {
+ if index < 0 || index >= len(h.headers) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Header{h.headers[index]}, nil
+}
+
+// Block represents an entire block in the Ethereum blockchain.
+type Block struct {
+ block *types.Block
+}
+
+func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} }
+func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} }
+func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} }
+func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} }
+func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} }
+func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} }
+func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} }
+func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} }
+func (b *Block) GetNumber() int64 { return b.block.Number().Int64() }
+func (b *Block) GetGasLimit() int64 { return b.block.GasLimit().Int64() }
+func (b *Block) GetGasUsed() int64 { return b.block.GasUsed().Int64() }
+func (b *Block) GetTime() int64 { return b.block.Time().Int64() }
+func (b *Block) GetExtra() []byte { return b.block.Extra() }
+func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} }
+func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) }
+
+func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} }
+func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} }
+
+func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} }
+func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} }
+func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} }
+func (b *Block) GetTransaction(hash *Hash) *Transaction {
+ return &Transaction{b.block.Transaction(hash.hash)}
+}
+
+// Transaction represents a single Ethereum transaction.
+type Transaction struct {
+ tx *types.Transaction
+}
+
+func (tx *Transaction) GetData() []byte { return tx.tx.Data() }
+func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() }
+func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} }
+func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} }
+func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) }
+
+func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} }
+func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.HomesteadSigner{})} }
+func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
+
+func (tx *Transaction) GetFrom() (*Address, error) {
+ from, err := types.Sender(types.HomesteadSigner{}, tx.tx)
+ return &Address{from}, err
+}
+
+func (tx *Transaction) GetTo() *Address {
+ if to := tx.tx.To(); to != nil {
+ return &Address{*to}
+ }
+ return nil
+}
+
+func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
+ t, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig)
+ return &Transaction{t}, err
+}
+
+// Transactions represents a slice of transactions.
+type Transactions struct{ txs types.Transactions }
+
+// Size returns the number of transactions in the slice.
+func (t *Transactions) Size() int {
+ return len(t.txs)
+}
+
+// Get returns the transaction at the given index from the slice.
+func (t *Transactions) Get(index int) (*Transaction, error) {
+ if index < 0 || index >= len(t.txs) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Transaction{t.txs[index]}, nil
+}
+
+// Receipt represents the results of a transaction.
+type Receipt struct {
+ receipt *types.Receipt
+}
+
+func (r *Receipt) GetPostState() []byte { return r.receipt.PostState }
+func (r *Receipt) GetCumulativeGasUsed() *BigInt { return &BigInt{r.receipt.CumulativeGasUsed} }
+func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} }
+func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} }
+func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} }
+func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} }
+func (r *Receipt) GetGasUsed() *BigInt { return &BigInt{r.receipt.GasUsed} }
diff --git a/mobile/vm.go b/mobile/vm.go
new file mode 100644
index 000000000..a68917ca6
--- /dev/null
+++ b/mobile/vm.go
@@ -0,0 +1,56 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Contains all the wrappers from the core/types package.
+
+package geth
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// Log represents a contract log event. These events are generated by the LOG
+// opcode and stored/indexed by the node.
+type Log struct {
+ log *vm.Log
+}
+
+func (l *Log) GetAddress() *Address { return &Address{l.log.Address} }
+func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} }
+func (l *Log) GetData() []byte { return l.log.Data }
+func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) }
+func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} }
+func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) }
+func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} }
+func (l *Log) GetIndex() int { return int(l.log.Index) }
+
+// Logs represents a slice of VM logs.
+type Logs struct{ logs vm.Logs }
+
+// Size returns the number of logs in the slice.
+func (l *Logs) Size() int {
+ return len(l.logs)
+}
+
+// Get returns the log at the given index from the slice.
+func (l *Logs) Get(index int) (*Log, error) {
+ if index < 0 || index >= len(l.logs) {
+ return nil, errors.New("index out of bounds")
+ }
+ return &Log{l.logs[index]}, nil
+}
diff --git a/node/config.go b/node/config.go
index dbefcb8a5..62655f237 100644
--- a/node/config.go
+++ b/node/config.go
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/nat"
)
@@ -95,16 +96,23 @@ type Config struct {
// or not. Disabling is usually useful for protocol debugging (manual topology).
NoDiscovery bool
+ // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
+ // protocol should be started or not.
DiscoveryV5 bool
- // Bootstrap nodes used to establish connectivity with the rest of the network.
+ // Listener address for the V5 discovery protocol UDP traffic.
+ DiscoveryV5Addr string
+
+ // BootstrapNodes used to establish connectivity with the rest of the network.
BootstrapNodes []*discover.Node
+ // BootstrapNodesV5 used to establish connectivity with the rest of the network
+ // using the V5 discovery protocol.
+ BootstrapNodesV5 []*discv5.Node
+
// Network interface address on which the node should listen for inbound peers.
ListenAddr string
- ListenAddrV5 string
-
// If set to a non-nil value, the given NAT port mapper is used to make the
// listening port available to the Internet.
NAT nat.Interface
diff --git a/node/node.go b/node/node.go
index fb11696fa..d49ae3a45 100644
--- a/node/node.go
+++ b/node/node.go
@@ -154,21 +154,22 @@ func (n *Node) Start() error {
// Initialize the p2p server. This creates the node key and
// discovery databases.
n.serverConfig = p2p.Config{
- PrivateKey: n.config.NodeKey(),
- Name: n.config.NodeName(),
- Discovery: !n.config.NoDiscovery,
- DiscoveryV5: n.config.DiscoveryV5,
- BootstrapNodes: n.config.BootstrapNodes,
- StaticNodes: n.config.StaticNodes(),
- TrustedNodes: n.config.TrusterNodes(),
- NodeDatabase: n.config.NodeDB(),
- ListenAddr: n.config.ListenAddr,
- ListenAddrV5: n.config.ListenAddrV5,
- NAT: n.config.NAT,
- Dialer: n.config.Dialer,
- NoDial: n.config.NoDial,
- MaxPeers: n.config.MaxPeers,
- MaxPendingPeers: n.config.MaxPendingPeers,
+ PrivateKey: n.config.NodeKey(),
+ Name: n.config.NodeName(),
+ Discovery: !n.config.NoDiscovery,
+ DiscoveryV5: n.config.DiscoveryV5,
+ DiscoveryV5Addr: n.config.DiscoveryV5Addr,
+ BootstrapNodes: n.config.BootstrapNodes,
+ BootstrapNodesV5: n.config.BootstrapNodesV5,
+ StaticNodes: n.config.StaticNodes(),
+ TrustedNodes: n.config.TrusterNodes(),
+ NodeDatabase: n.config.NodeDB(),
+ ListenAddr: n.config.ListenAddr,
+ NAT: n.config.NAT,
+ Dialer: n.config.Dialer,
+ NoDial: n.config.NoDial,
+ MaxPeers: n.config.MaxPeers,
+ MaxPendingPeers: n.config.MaxPendingPeers,
}
running := &p2p.Server{Config: n.serverConfig}
glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name)
diff --git a/p2p/discover/node.go b/p2p/discover/node.go
index 139a95d80..eec0bae0c 100644
--- a/p2p/discover/node.go
+++ b/p2p/discover/node.go
@@ -35,7 +35,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)
-const nodeIDBits = 512
+const NodeIDBits = 512
// Node represents a host on the network.
// The fields of Node may not be modified.
@@ -209,7 +209,7 @@ func MustParseNode(rawurl string) *Node {
// NodeID is a unique identifier for each node.
// The node identifier is a marshaled elliptic curve public key.
-type NodeID [nodeIDBits / 8]byte
+type NodeID [NodeIDBits / 8]byte
// NodeID prints as a long hexadecimal number.
func (n NodeID) String() string {
diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go
index b08cd2bc7..7ad6f1e5b 100644
--- a/p2p/discv5/net.go
+++ b/p2p/discv5/net.go
@@ -41,9 +41,10 @@ var (
)
const (
- autoRefreshInterval = 1 * time.Hour
- seedCount = 30
- seedMaxAge = 5 * 24 * time.Hour
+ autoRefreshInterval = 1 * time.Hour
+ bucketRefreshInterval = 1 * time.Minute
+ seedCount = 30
+ seedMaxAge = 5 * 24 * time.Hour
)
const testTopic = "foo"
@@ -59,13 +60,6 @@ func debugLog(s string) {
}
}
-// BootNodes are the enode URLs of the P2P bootstrap nodes for the experimental RLPx v5 "Topic Discovery" network
-// warning: local bootnodes for testing!!!
-var BootNodes = []*Node{
- //MustParseNode("enode://6f974ede10d07334e7e651c1501cb540d087dd3a6dea81432620895c913f281790b49459d72cb8011bfbbfbd24fad956356189c31b7181a96cd44ccfb68bfc71@127.0.0.1:30301"),
- MustParseNode("enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305"),
-}
-
// Network manages the table and all protocol interaction.
type Network struct {
db *nodeDB // database of known nodes
@@ -82,7 +76,6 @@ type Network struct {
tableOpResp chan struct{}
topicRegisterReq chan topicRegisterReq
topicSearchReq chan topicSearchReq
- bucketFillChn chan chan struct{}
// State of the main loop.
tab *Table
@@ -169,7 +162,6 @@ func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, d
queryReq: make(chan *findnodeQuery),
topicRegisterReq: make(chan topicRegisterReq),
topicSearchReq: make(chan topicSearchReq),
- bucketFillChn: make(chan chan struct{}, 1),
nodes: make(map[NodeID]*Node),
}
go net.loop()
@@ -353,8 +345,9 @@ func (net *Network) reqTableOp(f func()) (called bool) {
func (net *Network) loop() {
var (
- refreshTimer = time.NewTicker(autoRefreshInterval)
- refreshDone chan struct{} // closed when the 'refresh' lookup has ended
+ refreshTimer = time.NewTicker(autoRefreshInterval)
+ bucketRefreshTimer = time.NewTimer(bucketRefreshInterval)
+ refreshDone chan struct{} // closed when the 'refresh' lookup has ended
)
// Tracking the next ticket to register.
@@ -389,6 +382,7 @@ func (net *Network) loop() {
topicRegisterLookupDone chan []*Node
topicRegisterLookupTick = time.NewTimer(0)
topicSearchLookupTarget lookupInfo
+ searchReqWhenRefreshDone []topicSearchReq
)
topicSearchLookupDone := make(chan []*Node, 1)
<-topicRegisterLookupTick.C
@@ -406,6 +400,7 @@ loop:
// Ingress packet handling.
case pkt := <-net.read:
+ //fmt.Println("read", pkt.ev)
debugLog("<-net.read")
n := net.internNode(&pkt)
prestate := n.state
@@ -503,14 +498,18 @@ loop:
net.conn.sendTopicRegister(nextTicket.t.node, nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong)
case req := <-net.topicSearchReq:
- debugLog("<-net.topicSearchReq")
- if req.found == nil {
- net.ticketStore.removeSearchTopic(req.topic)
- continue
- }
- net.ticketStore.addSearchTopic(req.topic, req.found)
- if (topicSearchLookupTarget.target == common.Hash{}) {
- topicSearchLookupDone <- nil
+ if refreshDone == nil {
+ debugLog("<-net.topicSearchReq")
+ if req.found == nil {
+ net.ticketStore.removeSearchTopic(req.topic)
+ continue
+ }
+ net.ticketStore.addSearchTopic(req.topic, req.found)
+ if (topicSearchLookupTarget.target == common.Hash{}) {
+ topicSearchLookupDone <- nil
+ }
+ } else {
+ searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req)
}
case nodes := <-topicSearchLookupDone:
@@ -519,7 +518,14 @@ loop:
net.ping(n, n.addr())
return n.pingEcho
}, func(n *Node, topic Topic) []byte {
- return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration
+ if n.state == known {
+ return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration
+ } else {
+ if n.state == unknown {
+ net.ping(n, n.addr())
+ }
+ return nil
+ }
})
topicSearchLookupTarget = net.ticketStore.nextSearchLookup()
target := topicSearchLookupTarget.target
@@ -564,9 +570,12 @@ loop:
refreshDone = make(chan struct{})
net.refresh(refreshDone)
}
- case doneChn := <-net.bucketFillChn:
- debugLog("bucketFill")
- net.bucketFill(doneChn)
+ case <-bucketRefreshTimer.C:
+ target := net.tab.chooseBucketRefreshTarget()
+ go func() {
+ net.lookup(target, false)
+ bucketRefreshTimer.Reset(bucketRefreshInterval)
+ }()
case newNursery := <-net.refreshReq:
debugLog("<-net.refreshReq")
if newNursery != nil {
@@ -580,6 +589,13 @@ loop:
case <-refreshDone:
debugLog("<-net.refreshDone")
refreshDone = nil
+ list := searchReqWhenRefreshDone
+ searchReqWhenRefreshDone = nil
+ go func() {
+ for _, req := range list {
+ net.topicSearchReq <- req
+ }
+ }()
}
}
debugLog("loop stopped")
@@ -643,28 +659,13 @@ func (net *Network) refresh(done chan<- struct{}) {
}()
}
-func (net *Network) bucketFill(done chan<- struct{}) {
- target := net.tab.chooseBucketFillTarget()
- go func() {
- net.lookup(target, false)
- close(done)
- }()
-}
-
-func (net *Network) BucketFill() {
- done := make(chan struct{})
- select {
- case net.bucketFillChn <- done:
- <-done
- case <-net.closed:
- close(done)
- }
-}
-
// Node Interning.
func (net *Network) internNode(pkt *ingressPacket) *Node {
if n := net.nodes[pkt.remoteID]; n != nil {
+ n.IP = pkt.remoteAddr.IP
+ n.UDP = uint16(pkt.remoteAddr.Port)
+ n.TCP = uint16(pkt.remoteAddr.Port)
return n
}
n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port))
@@ -967,8 +968,10 @@ func init() {
// handle processes packets sent by n and events related to n.
func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error {
+ //fmt.Println("handle", n.addr().String(), n.state, ev)
if pkt != nil {
if err := net.checkPacket(n, ev, pkt); err != nil {
+ //fmt.Println("check err:", err)
return err
}
// Start the background expiration goroutine after the first
@@ -985,6 +988,7 @@ func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error {
}
next, err := n.state.handle(net, n, ev, pkt)
net.transition(n, next)
+ //fmt.Println("new state:", n.state)
return err
}
@@ -1040,6 +1044,11 @@ func (net *Network) abortTimedEvent(n *Node, ev nodeEvent) {
}
func (net *Network) ping(n *Node, addr *net.UDPAddr) {
+ //fmt.Println("ping", n.addr().String(), n.ID.String(), n.sha.Hex())
+ if n.pingEcho != nil || n.ID == net.tab.self.ID {
+ //fmt.Println(" not sent")
+ return
+ }
debugLog(fmt.Sprintf("ping(node = %x)", n.ID[:8]))
n.pingTopics = net.ticketStore.regTopicSet()
n.pingEcho = net.conn.sendPing(n, addr, n.pingTopics)
diff --git a/p2p/discv5/table.go b/p2p/discv5/table.go
index 5c8c50706..2cf05009c 100644
--- a/p2p/discv5/table.go
+++ b/p2p/discv5/table.go
@@ -25,6 +25,7 @@ package discv5
import (
"crypto/rand"
"encoding/binary"
+ "fmt"
"net"
"sort"
@@ -64,42 +65,54 @@ func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table {
return tab
}
-func (tab *Table) chooseBucketFillTarget() common.Hash {
- bucketCount := nBuckets
- for bucketCount > 0 && len(tab.buckets[nBuckets-bucketCount].entries) == 0 {
- bucketCount--
+const printTable = false
+
+// chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia
+// buckets filled with live connections and keep the network topology healthy.
+// This requires selecting addresses closer to our own with a higher probability
+// in order to refresh closer buckets too.
+//
+// This algorithm approximates the distance distribution of existing nodes in the
+// table by selecting a random node from the table and selecting a target address
+// with a distance less than twice of that of the selected node.
+// This algorithm will be improved later to specifically target the least recently
+// used buckets.
+func (tab *Table) chooseBucketRefreshTarget() common.Hash {
+ entries := 0
+ if printTable {
+ fmt.Println()
}
- var bucket int
- for {
- // select a target hash that could go into a certain randomly selected bucket
- // buckets are chosen with an even chance out of the existing ones that contain
- // less that bucketSize entries, plus a potential new one beyond these
- bucket = nBuckets - 1 - int(randUint(uint32(bucketCount+1)))
- if bucket == bucketCount || len(tab.buckets[bucket].entries) < bucketSize {
- break
+ for i, b := range tab.buckets {
+ entries += len(b.entries)
+ if printTable {
+ for _, e := range b.entries {
+ fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex())
+ }
}
}
- // calculate target that has the desired log distance from our own address hash
- target := tab.self.sha.Bytes()
- prefix := binary.BigEndian.Uint64(target[0:8])
- shift := uint(nBuckets - 1 - bucket)
- if bucket != bucketCount {
- shift++
+ prefix := binary.BigEndian.Uint64(tab.self.sha[0:8])
+ dist := ^uint64(0)
+ entry := int(randUint(uint32(entries + 1)))
+ for _, b := range tab.buckets {
+ if entry < len(b.entries) {
+ n := b.entries[entry]
+ dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix
+ break
+ }
+ entry -= len(b.entries)
}
- var b [8]byte
- rand.Read(b[:])
- rnd := binary.BigEndian.Uint64(b[:])
- rndMask := (^uint64(0)) >> shift
- addrMask := ^rndMask
- xorMask := uint64(0)
- if bucket != bucketCount {
- xorMask = rndMask + 1
+
+ ddist := ^uint64(0)
+ if dist+dist > dist {
+ ddist = dist
}
- prefix = (prefix&addrMask ^ xorMask) | (rnd & rndMask)
- binary.BigEndian.PutUint64(target[0:8], prefix)
+ targetPrefix := prefix ^ randUint64n(ddist)
+
+ var target common.Hash
+ binary.BigEndian.PutUint64(target[0:8], targetPrefix)
rand.Read(target[8:])
- return common.BytesToHash(target)
+ return target
}
// readRandomNodes fills the given slice with random nodes from the
@@ -175,6 +188,10 @@ func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
// bucket has space available, adding the node succeeds immediately.
// Otherwise, the node is added to the replacement cache for the bucket.
func (tab *Table) add(n *Node) (contested *Node) {
+ //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex())
+ if n.ID == tab.self.ID {
+ return
+ }
b := tab.buckets[logdist(tab.self.sha, n.sha)]
switch {
case b.bump(n):
@@ -228,6 +245,7 @@ outer:
// delete removes an entry from the node table (used to evacuate
// failed/non-bonded discovery peers).
func (tab *Table) delete(node *Node) {
+ //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex())
bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
for i := range bucket.entries {
if bucket.entries[i].ID == node.ID {
diff --git a/p2p/discv5/ticket.go b/p2p/discv5/ticket.go
index 3ee2f7fc4..202504314 100644
--- a/p2p/discv5/ticket.go
+++ b/p2p/discv5/ticket.go
@@ -525,7 +525,9 @@ func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, ping fu
} // else {
if s.canQueryTopic(n, lookup.topic) {
hash := query(n, lookup.topic)
- s.addTopicQuery(common.BytesToHash(hash), n, lookup)
+ if hash != nil {
+ s.addTopicQuery(common.BytesToHash(hash), n, lookup)
+ }
}
//}
}
diff --git a/p2p/discv5/udp.go b/p2p/discv5/udp.go
index af961984c..46d3200bf 100644
--- a/p2p/discv5/udp.go
+++ b/p2p/discv5/udp.go
@@ -336,14 +336,17 @@ func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node)
}
func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req interface{}) (hash []byte, err error) {
+ //fmt.Println("sendPacket", nodeEvent(ptype), toaddr.String(), toid.String())
packet, hash, err := encodePacket(t.priv, ptype, req)
if err != nil {
+ //fmt.Println(err)
return hash, err
}
glog.V(logger.Detail).Infof(">>> %v to %x@%v\n", nodeEvent(ptype), toid[:8], toaddr)
if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil {
glog.V(logger.Detail).Infoln("UDP send failed:", err)
}
+ //fmt.Println(err)
return hash, err
}
@@ -406,6 +409,7 @@ func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
pkt := ingressPacket{remoteAddr: from}
if err := decodePacket(buf, &pkt); err != nil {
glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err)
+ //fmt.Println("bad packet", err)
return err
}
t.net.reqReadPacket(pkt)
diff --git a/p2p/server.go b/p2p/server.go
index 649fbfb82..7381127dc 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -73,16 +73,26 @@ type Config struct {
// or not. Disabling is usually useful for protocol debugging (manual topology).
Discovery bool
+ // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
+ // protocol should be started or not.
DiscoveryV5 bool
+ // Listener address for the V5 discovery protocol UDP traffic.
+ DiscoveryV5Addr string
+
// Name sets the node name of this server.
// Use common.MakeName to create a name that follows existing conventions.
Name string
- // Bootstrap nodes are used to establish connectivity
+ // BootstrapNodes are used to establish connectivity
// with the rest of the network.
BootstrapNodes []*discover.Node
+ // BootstrapNodesV5 are used to establish connectivity
+ // with the rest of the network using the V5 discovery
+ // protocol.
+ BootstrapNodesV5 []*discv5.Node
+
// Static nodes are used as pre-configured connections which are always
// maintained and re-connected on disconnects.
StaticNodes []*discover.Node
@@ -108,8 +118,6 @@ type Config struct {
// the server is started.
ListenAddr string
- ListenAddrV5 string
-
// If set to a non-nil value, the given NAT port mapper
// is used to make the listening port available to the
// Internet.
@@ -359,11 +367,11 @@ func (srv *Server) Start() (err error) {
}
if srv.DiscoveryV5 {
- ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.ListenAddrV5, srv.NAT, "") //srv.NodeDatabase)
+ ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "") //srv.NodeDatabase)
if err != nil {
return err
}
- if err := ntab.SetFallbackNodes(discv5.BootNodes); err != nil {
+ if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil {
return err
}
srv.DiscV5 = ntab
@@ -748,7 +756,7 @@ type NodeInfo struct {
Protocols map[string]interface{} `json:"protocols"`
}
-// Info gathers and returns a collection of metadata known about the host.
+// NodeInfo gathers and returns a collection of metadata known about the host.
func (srv *Server) NodeInfo() *NodeInfo {
node := srv.Self()
diff --git a/params/bootnodes.go b/params/bootnodes.go
new file mode 100644
index 000000000..830b309d6
--- /dev/null
+++ b/params/bootnodes.go
@@ -0,0 +1,52 @@
+// 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 <http://www.gnu.org/licenses/>.
+
+package params
+
+import (
+ "github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/p2p/discv5"
+)
+
+// MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on
+// the main Ethereum network.
+var MainnetBootnodes = []*discover.Node{
+ // ETH/DEV Go Bootnodes
+ discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
+ discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR
+ discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG
+
+ // ETH/DEV Cpp Bootnodes
+ discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
+}
+
+// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the
+// Morden test network.
+var TestnetBootnodes = []*discover.Node{
+ // ETH/DEV Go Bootnodes
+ discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"),
+ discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"),
+
+ // ETH/DEV Cpp Bootnodes
+}
+
+// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
+// experimental RLPx v5 topic-discovery network.
+var DiscoveryV5Bootnodes = []*discv5.Node{
+ discv5.MustParseNode("enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305"),
+ discv5.MustParseNode("enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308"),
+ discv5.MustParseNode("enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309"),
+}
diff --git a/params/config.go b/params/config.go
index d63236ef8..d27973a32 100644
--- a/params/config.go
+++ b/params/config.go
@@ -22,6 +22,28 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// MainnetChainConfig is the chain parameters to run a node on the main network.
+var MainnetChainConfig = &ChainConfig{
+ HomesteadBlock: MainNetHomesteadBlock,
+ DAOForkBlock: MainNetDAOForkBlock,
+ DAOForkSupport: true,
+ EIP150Block: MainNetHomesteadGasRepriceBlock,
+ EIP150Hash: MainNetHomesteadGasRepriceHash,
+ EIP155Block: MainNetSpuriousDragon,
+ EIP158Block: MainNetSpuriousDragon,
+}
+
+// TestnetChainConfig is the chain parameters to run a node on the test network.
+var TestnetChainConfig = &ChainConfig{
+ HomesteadBlock: TestNetHomesteadBlock,
+ DAOForkBlock: TestNetDAOForkBlock,
+ DAOForkSupport: false,
+ EIP150Block: TestNetHomesteadGasRepriceBlock,
+ EIP150Hash: TestNetHomesteadGasRepriceHash,
+ EIP155Block: TestNetSpuriousDragon,
+ EIP158Block: TestNetSpuriousDragon,
+}
+
// ChainConfig is the core config which determines the blockchain settings.
//
// ChainConfig is stored in the database on a per block basis. This means
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index c08398321..01998c2a4 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -226,7 +226,7 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m
key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
- message := types.NewMessage(addr, to, nonce, value, gas, price, data)
+ message := types.NewMessage(addr, to, nonce, value, gas, price, data, true)
vmenv := NewEnvFromMap(chainConfig, statedb, env, tx)
vmenv.origin = addr