Compare commits
114 Commits
Author | SHA1 | Date | |
---|---|---|---|
59d25c0c2c | |||
cde06b4009 | |||
587669215b | |||
fc3941d1fd | |||
8f09242d7f | |||
042dcf1b28 | |||
fd512fa12c | |||
dc3fb69dce | |||
d51d0022ce | |||
3793991c0e | |||
b884d6ebaa | |||
36f7fe61c3 | |||
54088b0b8b | |||
9fb7bc7443 | |||
e2d44814a5 | |||
bd3a44cac9 | |||
61a6911eeb | |||
9bf17eb05a | |||
269c5c7107 | |||
382d35bf40 | |||
dd54fef898 | |||
edccc7ae34 | |||
7d5ff770e2 | |||
c6a11fe372 | |||
941920f2aa | |||
3b997c3e16 | |||
0737cbc5c1 | |||
227ff4d2d6 | |||
18d450b2d0 | |||
cc87551edc | |||
d0dc1b4a60 | |||
b4369e1015 | |||
4d5501c46a | |||
d4da2f630e | |||
e1da124415 | |||
36081505c4 | |||
2497f28aa9 | |||
49ece3155c | |||
8839fee415 | |||
ff1f6fa09d | |||
7ea30f18f9 | |||
12805c738c | |||
80b294c3c7 | |||
8884f856ef | |||
309788de37 | |||
f6367548e4 | |||
1c3ca3ce6a | |||
8603ec7055 | |||
1086e2f298 | |||
49703bea0a | |||
59b28cfa31 | |||
5c5c3930b7 | |||
7bb5ac045e | |||
cd81356ace | |||
c472b8f725 | |||
3a66c4ed47 | |||
29181003d4 | |||
87d1cde7e4 | |||
28b14d3e6d | |||
73c4e6005c | |||
b8ca0a830e | |||
a89cfe92cc | |||
0b0b31c7d2 | |||
1d2420323c | |||
0dd6911c62 | |||
28feafe7af | |||
0d10d5a0a5 | |||
31a2793662 | |||
f9cbd16f27 | |||
0ef80bb3d0 | |||
05c66529b2 | |||
9cacec70f9 | |||
94b6f38869 | |||
bf6ea2919d | |||
c32d6fdf74 | |||
67c8ccc309 | |||
590c99a98f | |||
01ed3fa1a9 | |||
32395ddb89 | |||
2fcf7f1241 | |||
07cb8092e7 | |||
1cbd53add8 | |||
eec38c5853 | |||
c93f0b9f4b | |||
a23478c0be | |||
312128384b | |||
3ccab5a1e8 | |||
dcb23bc3ab | |||
b6c5b3b4a7 | |||
d7580f21f6 | |||
b1fac4270d | |||
ac697326a6 | |||
184e9ae9a8 | |||
846f34f78b | |||
a33726b7db | |||
132df860d9 | |||
785b3e7a57 | |||
e89536ca0b | |||
ac10c9352e | |||
cf7cef4293 | |||
698e98d981 | |||
a3b8169938 | |||
46c9594081 | |||
7baa5977c8 | |||
803096ca0f | |||
6ee908848c | |||
3832019964 | |||
eae1191904 | |||
78b101e15d | |||
74f6d90153 | |||
c32073b11f | |||
b23b4dbd79 | |||
c1d516546d | |||
37efd08b42 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
@ -5,7 +5,7 @@ install:
|
||||
# - go get code.google.com/p/go.tools/cmd/goimports
|
||||
# - go get github.com/golang/lint/golint
|
||||
# - go get golang.org/x/tools/cmd/vet
|
||||
- go get golang.org/x/tools/cmd/cover github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
before_script:
|
||||
# - gofmt -l -w .
|
||||
# - goimports -l -w .
|
||||
@ -15,7 +15,7 @@ before_script:
|
||||
script:
|
||||
- make travis-test-with-coverage
|
||||
after_success:
|
||||
- if [ "$COVERALLS_TOKEN" ]; then goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
env:
|
||||
global:
|
||||
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
|
||||
|
9
CONTRIBUTING.md
Normal file
9
CONTRIBUTING.md
Normal file
@ -0,0 +1,9 @@
|
||||
If you'd like to contribute to go-ethereum please fork, fix, commit and
|
||||
send a pull request. Commits who do not comply with the coding standards
|
||||
are ignored (use gofmt!). If you send pull requests make absolute sure that you
|
||||
commit on the `develop` branch and that you do not merge to master.
|
||||
Commits that are directly based on master are simply ignored.
|
||||
|
||||
See [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
|
||||
for more details on configuring your environment, testing, and
|
||||
dependency management.
|
18
Godeps/Godeps.json
generated
18
Godeps/Godeps.json
generated
@ -24,14 +24,18 @@
|
||||
"Comment": "v23.1-227-g8f6ccaa",
|
||||
"Rev": "8f6ccaaef9b418553807a73a95cb5f49cd3ea39f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fatih/color",
|
||||
"Comment": "v0.1-5-gf773d4c",
|
||||
"Rev": "f773d4c806cc8e4a5749d6a35e2a4bbcd71443d6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gizak/termui",
|
||||
"Rev": "bab8dce01c193d82bc04888a0a9a7814d505f532"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/howeyc/fsnotify",
|
||||
"Comment": "v0.9.0-11-g6b1ef89",
|
||||
"Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
|
||||
"ImportPath": "github.com/hashicorp/golang-lru",
|
||||
"Rev": "7f9ef20a0256f494e24126014135cf893ab71e9e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp",
|
||||
@ -45,10 +49,6 @@
|
||||
"ImportPath": "github.com/kardianos/osext",
|
||||
"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-colorable",
|
||||
"Rev": "043ae16291351db8465272edf465c9f388161627"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-isatty",
|
||||
"Rev": "fdbe02a1b44e75977b2690062b83cf507d70c013"
|
||||
@ -78,6 +78,10 @@
|
||||
"ImportPath": "github.com/rs/cors",
|
||||
"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/shiena/ansicolor",
|
||||
"Rev": "a5e2b567a4dd6cc74545b8a4f27c9d63b9e7735b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||
"Rev": "4875955338b0a434238a31165cb87255ab6e9e4a"
|
||||
|
3
Godeps/_workspace/src/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
language: go
|
||||
go: 1.3
|
||||
|
20
Godeps/_workspace/src/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
151
Godeps/_workspace/src/github.com/fatih/color/README.md
generated
vendored
Normal file
151
Godeps/_workspace/src/github.com/fatih/color/README.md
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
# Color [](http://godoc.org/github.com/fatih/color) [](https://travis-ci.org/fatih/color)
|
||||
|
||||
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It has support for Windows too! The API can be used in several ways, pick one that suits you.
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/color
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard colors
|
||||
|
||||
```go
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
```
|
||||
|
||||
### Mix and reuse colors
|
||||
|
||||
```go
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
```
|
||||
|
||||
### Custom print functions (PrintFunc)
|
||||
|
||||
```go
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
```
|
||||
|
||||
### Insert into noncolor strings (SprintFunc)
|
||||
|
||||
```go
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Printf("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf(color.GreenString("Info:"), "an important message." )
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
```
|
||||
|
||||
### Plug into existing code
|
||||
|
||||
```go
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
```
|
||||
|
||||
### Disable color
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
```go
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
```
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
```go
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
```
|
||||
|
||||
## Todo
|
||||
|
||||
* Save/Return previous values
|
||||
* Evaluate fmt.Formatter interface
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* Windows support via @shiena: [ansicolor](https://github.com/shiena/ansicolor)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||
|
353
Godeps/_workspace/src/github.com/fatih/color/color.go
generated
vendored
Normal file
353
Godeps/_workspace/src/github.com/fatih/color/color.go
generated
vendored
Normal file
@ -0,0 +1,353 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. This is a global option and affects all colors. For more control
|
||||
// over each color block use the methods DisableColor() individually.
|
||||
var NoColor = !isatty.IsTerminal(os.Stdout.Fd())
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{params: make([]Attribute, 0)}
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) prepend(value Attribute) {
|
||||
c.params = append(c.params, 0)
|
||||
copy(c.params[1:], c.params[0:])
|
||||
c.params[0] = value
|
||||
}
|
||||
|
||||
// Output defines the standard output of the print functions. By default
|
||||
// os.Stdout is used.
|
||||
var Output = ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintln(Output, a...)
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) { c.Print(a...) }
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) { c.Printf(format, a...) }
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) { c.Println(a...) }
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjuction with
|
||||
// DisableColor(). Otherwise this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user setted action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Black is an convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { printColor(format, FgBlack, a...) }
|
||||
|
||||
// Red is an convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { printColor(format, FgRed, a...) }
|
||||
|
||||
// Green is an convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { printColor(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is an convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { printColor(format, FgYellow, a...) }
|
||||
|
||||
// Blue is an convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { printColor(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is an convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { printColor(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is an convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { printColor(format, FgCyan, a...) }
|
||||
|
||||
// White is an convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { printColor(format, FgWhite, a...) }
|
||||
|
||||
func printColor(format string, p Attribute, a ...interface{}) {
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
c := &Color{params: []Attribute{p}}
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
|
||||
// BlackString is an convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string {
|
||||
return New(FgBlack).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// RedString is an convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string {
|
||||
return New(FgRed).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// GreenString is an convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string {
|
||||
return New(FgGreen).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// YellowString is an convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string {
|
||||
return New(FgYellow).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// BlueString is an convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string {
|
||||
return New(FgBlue).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// MagentaString is an convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return New(FgMagenta).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// CyanString is an convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string {
|
||||
return New(FgCyan).SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// WhiteString is an convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string {
|
||||
return New(FgWhite).SprintfFunc()(format, a...)
|
||||
}
|
176
Godeps/_workspace/src/github.com/fatih/color/color_test.go
generated
vendored
Normal file
176
Godeps/_workspace/src/github.com/fatih/color/color_test.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
// Testing colors is kinda different. First we test for given colors and their
|
||||
// escaped formatted results. Next we create some visual tests to be tested.
|
||||
// Each visual test includes the color name to be compared.
|
||||
func TestColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
New(c.code).Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
scannedLine := fmt.Sprintf("%q", line)
|
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||
escapedForm := fmt.Sprintf("%q", colored)
|
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||
|
||||
if scannedLine != escapedForm {
|
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.DisableColor()
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
|
||||
// global check
|
||||
NoColor = true
|
||||
defer func() {
|
||||
NoColor = false
|
||||
}()
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestColorVisual(t *testing.T) {
|
||||
// First Visual Test
|
||||
fmt.Println("")
|
||||
Output = ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||
|
||||
New(FgRed).Printf("red\t")
|
||||
New(BgRed).Print(" ")
|
||||
New(FgRed, Bold).Println(" red")
|
||||
|
||||
New(FgGreen).Printf("green\t")
|
||||
New(BgGreen).Print(" ")
|
||||
New(FgGreen, Bold).Println(" green")
|
||||
|
||||
New(FgYellow).Printf("yellow\t")
|
||||
New(BgYellow).Print(" ")
|
||||
New(FgYellow, Bold).Println(" yellow")
|
||||
|
||||
New(FgBlue).Printf("blue\t")
|
||||
New(BgBlue).Print(" ")
|
||||
New(FgBlue, Bold).Println(" blue")
|
||||
|
||||
New(FgMagenta).Printf("magenta\t")
|
||||
New(BgMagenta).Print(" ")
|
||||
New(FgMagenta, Bold).Println(" magenta")
|
||||
|
||||
New(FgCyan).Printf("cyan\t")
|
||||
New(BgCyan).Print(" ")
|
||||
New(FgCyan, Bold).Println(" cyan")
|
||||
|
||||
New(FgWhite).Printf("white\t")
|
||||
New(BgWhite).Print(" ")
|
||||
New(FgWhite, Bold).Println(" white")
|
||||
fmt.Println("")
|
||||
|
||||
// Second Visual test
|
||||
Black("black")
|
||||
Red("red")
|
||||
Green("green")
|
||||
Yellow("yellow")
|
||||
Blue("blue")
|
||||
Magenta("magenta")
|
||||
Cyan("cyan")
|
||||
White("white")
|
||||
|
||||
// Third visual test
|
||||
fmt.Println()
|
||||
Set(FgBlue)
|
||||
fmt.Println("is this blue?")
|
||||
Unset()
|
||||
|
||||
Set(FgMagenta)
|
||||
fmt.Println("and this magenta?")
|
||||
Unset()
|
||||
|
||||
// Fourth Visual test
|
||||
fmt.Println()
|
||||
blue := New(FgBlue).PrintlnFunc()
|
||||
blue("blue text with custom print func")
|
||||
|
||||
red := New(FgRed).PrintfFunc()
|
||||
red("red text with a printf func: %d\n", 123)
|
||||
|
||||
put := New(FgYellow).SprintFunc()
|
||||
warn := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
// Fifth Visual Test
|
||||
fmt.Println()
|
||||
|
||||
fmt.Fprintln(Output, BlackString("black"))
|
||||
fmt.Fprintln(Output, RedString("red"))
|
||||
fmt.Fprintln(Output, GreenString("green"))
|
||||
fmt.Fprintln(Output, YellowString("yellow"))
|
||||
fmt.Fprintln(Output, BlueString("blue"))
|
||||
fmt.Fprintln(Output, MagentaString("magenta"))
|
||||
fmt.Fprintln(Output, CyanString("cyan"))
|
||||
fmt.Fprintln(Output, WhiteString("white"))
|
||||
}
|
114
Godeps/_workspace/src/github.com/fatih/color/doc.go
generated
vendored
Normal file
114
Godeps/_workspace/src/github.com/fatih/color/doc.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
Package color is an ANSI color package to output colorized or SGR defined
|
||||
output to the standard output. The API can be used in several way, pick one
|
||||
that suits you.
|
||||
|
||||
Use simple and default helper functions with predefined foreground colors:
|
||||
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red")
|
||||
color.Yellow("Yellow color too!")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
However there are times where custom color mixes are required. Below are some
|
||||
examples to create custom color objects and use the print functions of each
|
||||
separate color object.
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with White background.")
|
||||
|
||||
|
||||
You can create PrintXxx functions to simplify even more:
|
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("warning")
|
||||
red("error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("don't forget this...")
|
||||
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
|
||||
yellow := New(FgYellow).SprintFunc()
|
||||
red := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Printf("this %s rocks!\n", info("package"))
|
||||
|
||||
Windows support is enabled by default. All Print functions works as intended.
|
||||
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||
set the output to color.Output:
|
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the
|
||||
standard output to the given parameters. That way a rewrite of an existing
|
||||
code is not required.
|
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will be now in Yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.")
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
*/
|
||||
package color
|
4
Godeps/_workspace/src/github.com/huin/goupnp/httpu/httpu.go
generated
vendored
4
Godeps/_workspace/src/github.com/huin/goupnp/httpu/httpu.go
generated
vendored
@ -9,8 +9,6 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
)
|
||||
|
||||
// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical
|
||||
@ -27,7 +25,6 @@ func NewHTTPUClient() (*HTTPUClient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fdtrack.Open("upnp")
|
||||
return &HTTPUClient{conn: conn}, nil
|
||||
}
|
||||
|
||||
@ -36,7 +33,6 @@ func NewHTTPUClient() (*HTTPUClient, error) {
|
||||
func (httpu *HTTPUClient) Close() error {
|
||||
httpu.connLock.Lock()
|
||||
defer httpu.connLock.Unlock()
|
||||
fdtrack.Close("upnp")
|
||||
return httpu.conn.Close()
|
||||
}
|
||||
|
||||
|
14
Godeps/_workspace/src/github.com/huin/goupnp/soap/soap.go
generated
vendored
14
Godeps/_workspace/src/github.com/huin/goupnp/soap/soap.go
generated
vendored
@ -7,12 +7,9 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -29,17 +26,6 @@ type SOAPClient struct {
|
||||
func NewSOAPClient(endpointURL url.URL) *SOAPClient {
|
||||
return &SOAPClient{
|
||||
EndpointURL: endpointURL,
|
||||
HTTPClient: http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
c, err := net.Dial(network, addr)
|
||||
if c != nil {
|
||||
c = fdtrack.WrapConn("upnp", c)
|
||||
}
|
||||
return c, err
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
4
Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go
generated
vendored
4
Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go
generated
vendored
@ -5,8 +5,6 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
)
|
||||
|
||||
// Implement the NAT-PMP protocol, typically supported by Apple routers and open source
|
||||
@ -104,8 +102,6 @@ func (n *Client) rpc(msg []byte, resultSize int) (result []byte, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fdtrack.Open("natpmp")
|
||||
defer fdtrack.Close("natpmp")
|
||||
defer conn.Close()
|
||||
|
||||
result = make([]byte, resultSize)
|
||||
|
42
Godeps/_workspace/src/github.com/mattn/go-colorable/README.md
generated
vendored
42
Godeps/_workspace/src/github.com/mattn/go-colorable/README.md
generated
vendored
@ -1,42 +0,0 @@
|
||||
# go-colorable
|
||||
|
||||
Colorable writer for windows.
|
||||
|
||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||
This package is possible to handle escape sequence for ansi color on windows.
|
||||
|
||||
## Too Bad!
|
||||
|
||||

|
||||
|
||||
|
||||
## So Good!
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
logrus.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
logrus.Info("succeeded")
|
||||
logrus.Warn("not correct")
|
||||
logrus.Error("something error")
|
||||
logrus.Fatal("panic")
|
||||
```
|
||||
|
||||
You can compile above code on non-windows OSs.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-colorable
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
16
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
16
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
594
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
594
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
@ -1,594 +0,0 @@
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = 0x1
|
||||
foregroundGreen = 0x2
|
||||
foregroundRed = 0x4
|
||||
foregroundIntensity = 0x8
|
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||
backgroundBlue = 0x10
|
||||
backgroundGreen = 0x20
|
||||
backgroundRed = 0x40
|
||||
backgroundIntensity = 0x80
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
lastbuf bytes.Buffer
|
||||
oldattr word
|
||||
}
|
||||
|
||||
func NewColorableStdout() io.Writer {
|
||||
var csbi consoleScreenBufferInfo
|
||||
out := os.Stdout
|
||||
if !isatty.IsTerminal(out.Fd()) {
|
||||
return out
|
||||
}
|
||||
handle := syscall.Handle(out.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: out, handle: handle, oldattr: csbi.attributes}
|
||||
}
|
||||
|
||||
func NewColorableStderr() io.Writer {
|
||||
var csbi consoleScreenBufferInfo
|
||||
out := os.Stderr
|
||||
if !isatty.IsTerminal(out.Fd()) {
|
||||
return out
|
||||
}
|
||||
handle := syscall.Handle(out.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: out, handle: handle, oldattr: csbi.attributes}
|
||||
}
|
||||
|
||||
var color256 = map[int]int{
|
||||
0: 0x000000,
|
||||
1: 0x800000,
|
||||
2: 0x008000,
|
||||
3: 0x808000,
|
||||
4: 0x000080,
|
||||
5: 0x800080,
|
||||
6: 0x008080,
|
||||
7: 0xc0c0c0,
|
||||
8: 0x808080,
|
||||
9: 0xff0000,
|
||||
10: 0x00ff00,
|
||||
11: 0xffff00,
|
||||
12: 0x0000ff,
|
||||
13: 0xff00ff,
|
||||
14: 0x00ffff,
|
||||
15: 0xffffff,
|
||||
16: 0x000000,
|
||||
17: 0x00005f,
|
||||
18: 0x000087,
|
||||
19: 0x0000af,
|
||||
20: 0x0000d7,
|
||||
21: 0x0000ff,
|
||||
22: 0x005f00,
|
||||
23: 0x005f5f,
|
||||
24: 0x005f87,
|
||||
25: 0x005faf,
|
||||
26: 0x005fd7,
|
||||
27: 0x005fff,
|
||||
28: 0x008700,
|
||||
29: 0x00875f,
|
||||
30: 0x008787,
|
||||
31: 0x0087af,
|
||||
32: 0x0087d7,
|
||||
33: 0x0087ff,
|
||||
34: 0x00af00,
|
||||
35: 0x00af5f,
|
||||
36: 0x00af87,
|
||||
37: 0x00afaf,
|
||||
38: 0x00afd7,
|
||||
39: 0x00afff,
|
||||
40: 0x00d700,
|
||||
41: 0x00d75f,
|
||||
42: 0x00d787,
|
||||
43: 0x00d7af,
|
||||
44: 0x00d7d7,
|
||||
45: 0x00d7ff,
|
||||
46: 0x00ff00,
|
||||
47: 0x00ff5f,
|
||||
48: 0x00ff87,
|
||||
49: 0x00ffaf,
|
||||
50: 0x00ffd7,
|
||||
51: 0x00ffff,
|
||||
52: 0x5f0000,
|
||||
53: 0x5f005f,
|
||||
54: 0x5f0087,
|
||||
55: 0x5f00af,
|
||||
56: 0x5f00d7,
|
||||
57: 0x5f00ff,
|
||||
58: 0x5f5f00,
|
||||
59: 0x5f5f5f,
|
||||
60: 0x5f5f87,
|
||||
61: 0x5f5faf,
|
||||
62: 0x5f5fd7,
|
||||
63: 0x5f5fff,
|
||||
64: 0x5f8700,
|
||||
65: 0x5f875f,
|
||||
66: 0x5f8787,
|
||||
67: 0x5f87af,
|
||||
68: 0x5f87d7,
|
||||
69: 0x5f87ff,
|
||||
70: 0x5faf00,
|
||||
71: 0x5faf5f,
|
||||
72: 0x5faf87,
|
||||
73: 0x5fafaf,
|
||||
74: 0x5fafd7,
|
||||
75: 0x5fafff,
|
||||
76: 0x5fd700,
|
||||
77: 0x5fd75f,
|
||||
78: 0x5fd787,
|
||||
79: 0x5fd7af,
|
||||
80: 0x5fd7d7,
|
||||
81: 0x5fd7ff,
|
||||
82: 0x5fff00,
|
||||
83: 0x5fff5f,
|
||||
84: 0x5fff87,
|
||||
85: 0x5fffaf,
|
||||
86: 0x5fffd7,
|
||||
87: 0x5fffff,
|
||||
88: 0x870000,
|
||||
89: 0x87005f,
|
||||
90: 0x870087,
|
||||
91: 0x8700af,
|
||||
92: 0x8700d7,
|
||||
93: 0x8700ff,
|
||||
94: 0x875f00,
|
||||
95: 0x875f5f,
|
||||
96: 0x875f87,
|
||||
97: 0x875faf,
|
||||
98: 0x875fd7,
|
||||
99: 0x875fff,
|
||||
100: 0x878700,
|
||||
101: 0x87875f,
|
||||
102: 0x878787,
|
||||
103: 0x8787af,
|
||||
104: 0x8787d7,
|
||||
105: 0x8787ff,
|
||||
106: 0x87af00,
|
||||
107: 0x87af5f,
|
||||
108: 0x87af87,
|
||||
109: 0x87afaf,
|
||||
110: 0x87afd7,
|
||||
111: 0x87afff,
|
||||
112: 0x87d700,
|
||||
113: 0x87d75f,
|
||||
114: 0x87d787,
|
||||
115: 0x87d7af,
|
||||
116: 0x87d7d7,
|
||||
117: 0x87d7ff,
|
||||
118: 0x87ff00,
|
||||
119: 0x87ff5f,
|
||||
120: 0x87ff87,
|
||||
121: 0x87ffaf,
|
||||
122: 0x87ffd7,
|
||||
123: 0x87ffff,
|
||||
124: 0xaf0000,
|
||||
125: 0xaf005f,
|
||||
126: 0xaf0087,
|
||||
127: 0xaf00af,
|
||||
128: 0xaf00d7,
|
||||
129: 0xaf00ff,
|
||||
130: 0xaf5f00,
|
||||
131: 0xaf5f5f,
|
||||
132: 0xaf5f87,
|
||||
133: 0xaf5faf,
|
||||
134: 0xaf5fd7,
|
||||
135: 0xaf5fff,
|
||||
136: 0xaf8700,
|
||||
137: 0xaf875f,
|
||||
138: 0xaf8787,
|
||||
139: 0xaf87af,
|
||||
140: 0xaf87d7,
|
||||
141: 0xaf87ff,
|
||||
142: 0xafaf00,
|
||||
143: 0xafaf5f,
|
||||
144: 0xafaf87,
|
||||
145: 0xafafaf,
|
||||
146: 0xafafd7,
|
||||
147: 0xafafff,
|
||||
148: 0xafd700,
|
||||
149: 0xafd75f,
|
||||
150: 0xafd787,
|
||||
151: 0xafd7af,
|
||||
152: 0xafd7d7,
|
||||
153: 0xafd7ff,
|
||||
154: 0xafff00,
|
||||
155: 0xafff5f,
|
||||
156: 0xafff87,
|
||||
157: 0xafffaf,
|
||||
158: 0xafffd7,
|
||||
159: 0xafffff,
|
||||
160: 0xd70000,
|
||||
161: 0xd7005f,
|
||||
162: 0xd70087,
|
||||
163: 0xd700af,
|
||||
164: 0xd700d7,
|
||||
165: 0xd700ff,
|
||||
166: 0xd75f00,
|
||||
167: 0xd75f5f,
|
||||
168: 0xd75f87,
|
||||
169: 0xd75faf,
|
||||
170: 0xd75fd7,
|
||||
171: 0xd75fff,
|
||||
172: 0xd78700,
|
||||
173: 0xd7875f,
|
||||
174: 0xd78787,
|
||||
175: 0xd787af,
|
||||
176: 0xd787d7,
|
||||
177: 0xd787ff,
|
||||
178: 0xd7af00,
|
||||
179: 0xd7af5f,
|
||||
180: 0xd7af87,
|
||||
181: 0xd7afaf,
|
||||
182: 0xd7afd7,
|
||||
183: 0xd7afff,
|
||||
184: 0xd7d700,
|
||||
185: 0xd7d75f,
|
||||
186: 0xd7d787,
|
||||
187: 0xd7d7af,
|
||||
188: 0xd7d7d7,
|
||||
189: 0xd7d7ff,
|
||||
190: 0xd7ff00,
|
||||
191: 0xd7ff5f,
|
||||
192: 0xd7ff87,
|
||||
193: 0xd7ffaf,
|
||||
194: 0xd7ffd7,
|
||||
195: 0xd7ffff,
|
||||
196: 0xff0000,
|
||||
197: 0xff005f,
|
||||
198: 0xff0087,
|
||||
199: 0xff00af,
|
||||
200: 0xff00d7,
|
||||
201: 0xff00ff,
|
||||
202: 0xff5f00,
|
||||
203: 0xff5f5f,
|
||||
204: 0xff5f87,
|
||||
205: 0xff5faf,
|
||||
206: 0xff5fd7,
|
||||
207: 0xff5fff,
|
||||
208: 0xff8700,
|
||||
209: 0xff875f,
|
||||
210: 0xff8787,
|
||||
211: 0xff87af,
|
||||
212: 0xff87d7,
|
||||
213: 0xff87ff,
|
||||
214: 0xffaf00,
|
||||
215: 0xffaf5f,
|
||||
216: 0xffaf87,
|
||||
217: 0xffafaf,
|
||||
218: 0xffafd7,
|
||||
219: 0xffafff,
|
||||
220: 0xffd700,
|
||||
221: 0xffd75f,
|
||||
222: 0xffd787,
|
||||
223: 0xffd7af,
|
||||
224: 0xffd7d7,
|
||||
225: 0xffd7ff,
|
||||
226: 0xffff00,
|
||||
227: 0xffff5f,
|
||||
228: 0xffff87,
|
||||
229: 0xffffaf,
|
||||
230: 0xffffd7,
|
||||
231: 0xffffff,
|
||||
232: 0x080808,
|
||||
233: 0x121212,
|
||||
234: 0x1c1c1c,
|
||||
235: 0x262626,
|
||||
236: 0x303030,
|
||||
237: 0x3a3a3a,
|
||||
238: 0x444444,
|
||||
239: 0x4e4e4e,
|
||||
240: 0x585858,
|
||||
241: 0x626262,
|
||||
242: 0x6c6c6c,
|
||||
243: 0x767676,
|
||||
244: 0x808080,
|
||||
245: 0x8a8a8a,
|
||||
246: 0x949494,
|
||||
247: 0x9e9e9e,
|
||||
248: 0xa8a8a8,
|
||||
249: 0xb2b2b2,
|
||||
250: 0xbcbcbc,
|
||||
251: 0xc6c6c6,
|
||||
252: 0xd0d0d0,
|
||||
253: 0xdadada,
|
||||
254: 0xe4e4e4,
|
||||
255: 0xeeeeee,
|
||||
}
|
||||
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewBuffer(data)
|
||||
loop:
|
||||
for {
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
c1, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
fmt.Fprint(w.out, string(c1))
|
||||
continue
|
||||
}
|
||||
c2, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m rune
|
||||
for {
|
||||
c, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
m = c
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
|
||||
switch m {
|
||||
case 'm':
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
for i, ns := range token {
|
||||
if n, err = strconv.Atoi(ns); err == nil {
|
||||
switch {
|
||||
case n == 0 || n == 100:
|
||||
attr = w.oldattr
|
||||
case 1 <= n && n <= 5:
|
||||
attr |= foregroundIntensity
|
||||
case n == 7:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 22 == n || n == 25 || n == 25:
|
||||
attr |= foregroundIntensity
|
||||
case n == 27:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 30 <= n && n <= 37:
|
||||
attr = (attr & backgroundMask)
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-30)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-30)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case n == 38: // set foreground color.
|
||||
if i < len(token)-2 && token[i+1] == "5" {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256foreAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= backgroundMask
|
||||
attr |= n256foreAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & backgroundMask)
|
||||
}
|
||||
case n == 39: // reset foreground color.
|
||||
attr &= backgroundMask
|
||||
attr |= w.oldattr & foregroundMask
|
||||
case 40 <= n && n <= 47:
|
||||
attr = (attr & foregroundMask)
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-40)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-40)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
case n == 48: // set background color.
|
||||
if i < len(token)-2 && token[i+1] == "5" {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256backAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= foregroundMask
|
||||
attr |= n256backAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & foregroundMask)
|
||||
}
|
||||
case n == 49: // reset foreground color.
|
||||
attr &= foregroundMask
|
||||
attr |= w.oldattr & backgroundMask
|
||||
}
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(data) - w.lastbuf.Len(), nil
|
||||
}
|
||||
|
||||
type consoleColor struct {
|
||||
red bool
|
||||
green bool
|
||||
blue bool
|
||||
intensity bool
|
||||
}
|
||||
|
||||
func minmax3(a, b, c int) (min, max int) {
|
||||
if a < b {
|
||||
if b < c {
|
||||
return a, c
|
||||
} else if a < c {
|
||||
return a, b
|
||||
} else {
|
||||
return c, b
|
||||
}
|
||||
} else {
|
||||
if a < c {
|
||||
return b, c
|
||||
} else if b < c {
|
||||
return b, a
|
||||
} else {
|
||||
return c, a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toConsoleColor(rgb int) (c consoleColor) {
|
||||
r, g, b := (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, rgb&0x0000FF
|
||||
min, max := minmax3(r, g, b)
|
||||
a := (min + max) / 2
|
||||
if r < 128 && g < 128 && b < 128 {
|
||||
if r >= a {
|
||||
c.red = true
|
||||
}
|
||||
if g >= a {
|
||||
c.green = true
|
||||
}
|
||||
if b >= a {
|
||||
c.blue = true
|
||||
}
|
||||
// non-intensed white is lighter than intensed black, so swap those.
|
||||
if c.red && c.green && c.blue {
|
||||
c.red, c.green, c.blue = false, false, false
|
||||
c.intensity = true
|
||||
}
|
||||
} else {
|
||||
if min < 128 {
|
||||
min = 128
|
||||
a = (min + max) / 2
|
||||
}
|
||||
if r >= a {
|
||||
c.red = true
|
||||
}
|
||||
if g >= a {
|
||||
c.green = true
|
||||
}
|
||||
if b >= a {
|
||||
c.blue = true
|
||||
}
|
||||
c.intensity = true
|
||||
// intensed black is darker than non-intensed white, so swap those.
|
||||
if !c.red && !c.green && !c.blue {
|
||||
c.red, c.green, c.blue = true, true, true
|
||||
c.intensity = false
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c consoleColor) foregroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= foregroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c consoleColor) backgroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= backgroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var n256foreAttr []word
|
||||
var n256backAttr []word
|
||||
|
||||
func n256setup() {
|
||||
n256foreAttr = make([]word, 256)
|
||||
n256backAttr = make([]word, 256)
|
||||
for i, rgb := range color256 {
|
||||
c := toConsoleColor(rgb)
|
||||
n256foreAttr[i] = c.foregroundAttr()
|
||||
n256backAttr[i] = c.backgroundAttr()
|
||||
}
|
||||
}
|
27
Godeps/_workspace/src/github.com/shiena/ansicolor/.gitignore
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/shiena/ansicolor/.gitignore
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# Created by http://www.gitignore.io
|
||||
|
||||
### Go ###
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
21
Godeps/_workspace/src/github.com/shiena/ansicolor/LICENSE
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/shiena/ansicolor/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) [2014] [shiena]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
100
Godeps/_workspace/src/github.com/shiena/ansicolor/README.md
generated
vendored
Normal file
100
Godeps/_workspace/src/github.com/shiena/ansicolor/README.md
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
[](https://godoc.org/github.com/shiena/ansicolor)
|
||||
|
||||
# ansicolor
|
||||
|
||||
Ansicolor library provides color console in Windows as ANSICON for Golang.
|
||||
|
||||
## Features
|
||||
|
||||
|Escape sequence|Text attributes|
|
||||
|---------------|----|
|
||||
|\x1b[0m|All attributes off(color at startup)|
|
||||
|\x1b[1m|Bold on(enable foreground intensity)|
|
||||
|\x1b[4m|Underline on|
|
||||
|\x1b[5m|Blink on(enable background intensity)|
|
||||
|\x1b[21m|Bold off(disable foreground intensity)|
|
||||
|\x1b[24m|Underline off|
|
||||
|\x1b[25m|Blink off(disable background intensity)|
|
||||
|
||||
|Escape sequence|Foreground colors|
|
||||
|---------------|----|
|
||||
|\x1b[30m|Black|
|
||||
|\x1b[31m|Red|
|
||||
|\x1b[32m|Green|
|
||||
|\x1b[33m|Yellow|
|
||||
|\x1b[34m|Blue|
|
||||
|\x1b[35m|Magenta|
|
||||
|\x1b[36m|Cyan|
|
||||
|\x1b[37m|White|
|
||||
|\x1b[39m|Default(foreground color at startup)|
|
||||
|\x1b[90m|Light Gray|
|
||||
|\x1b[91m|Light Red|
|
||||
|\x1b[92m|Light Green|
|
||||
|\x1b[93m|Light Yellow|
|
||||
|\x1b[94m|Light Blue|
|
||||
|\x1b[95m|Light Magenta|
|
||||
|\x1b[96m|Light Cyan|
|
||||
|\x1b[97m|Light White|
|
||||
|
||||
|Escape sequence|Background colors|
|
||||
|---------------|----|
|
||||
|\x1b[40m|Black|
|
||||
|\x1b[41m|Red|
|
||||
|\x1b[42m|Green|
|
||||
|\x1b[43m|Yellow|
|
||||
|\x1b[44m|Blue|
|
||||
|\x1b[45m|Magenta|
|
||||
|\x1b[46m|Cyan|
|
||||
|\x1b[47m|White|
|
||||
|\x1b[49m|Default(background color at startup)|
|
||||
|\x1b[100m|Light Gray|
|
||||
|\x1b[101m|Light Red|
|
||||
|\x1b[102m|Light Green|
|
||||
|\x1b[103m|Light Yellow|
|
||||
|\x1b[104m|Light Blue|
|
||||
|\x1b[105m|Light Magenta|
|
||||
|\x1b[106m|Light Cyan|
|
||||
|\x1b[107m|Light White|
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||
text := "%sforeground %sbold%s %sbackground%s\n"
|
||||
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m")
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## See also:
|
||||
|
||||
- https://github.com/daviddengcn/go-colortext
|
||||
- https://github.com/adoxa/ansicon
|
||||
- https://github.com/aslakhellesoy/wac
|
||||
- https://github.com/wsxiaoys/terminal
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
20
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ansicolor provides color console in Windows as ANSICON.
|
||||
package ansicolor
|
||||
|
||||
import "io"
|
||||
|
||||
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
|
||||
// using io.Writer w as its initial contents.
|
||||
// In the console of Windows, which change the foreground and background
|
||||
// colors of the text by the escape sequence.
|
||||
// In the console of other systems, which writes to w all text.
|
||||
func NewAnsiColorWriter(w io.Writer) io.Writer {
|
||||
if _, ok := w.(*ansiColorWriter); !ok {
|
||||
return &ansiColorWriter{w: w}
|
||||
}
|
||||
return w
|
||||
}
|
27
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor/main.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor/main.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
The ansicolor command colors a console text by ANSI escape sequence like wac.
|
||||
|
||||
$ go get github.com/shiena/ansicolor/ansicolor
|
||||
|
||||
See also:
|
||||
https://github.com/aslakhellesoy/wac
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||
io.Copy(w, os.Stdin)
|
||||
}
|
17
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_ansi.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_ansi.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package ansicolor
|
||||
|
||||
import "io"
|
||||
|
||||
type ansiColorWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||
return cw.w.Write(p)
|
||||
}
|
25
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_test.go
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_test.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package ansicolor_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
func TestNewAnsiColor1(t *testing.T) {
|
||||
inner := bytes.NewBufferString("")
|
||||
w := ansicolor.NewAnsiColorWriter(inner)
|
||||
if w == inner {
|
||||
t.Errorf("Get %#v, want %#v", w, inner)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAnsiColor2(t *testing.T) {
|
||||
inner := bytes.NewBufferString("")
|
||||
w1 := ansicolor.NewAnsiColorWriter(inner)
|
||||
w2 := ansicolor.NewAnsiColorWriter(w1)
|
||||
if w1 != w2 {
|
||||
t.Errorf("Get %#v, want %#v", w1, w2)
|
||||
}
|
||||
}
|
351
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_windows.go
generated
vendored
Normal file
351
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_windows.go
generated
vendored
Normal file
@ -0,0 +1,351 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type csiState int
|
||||
|
||||
const (
|
||||
outsideCsiCode csiState = iota
|
||||
firstCsiCode
|
||||
secondCsiCode
|
||||
)
|
||||
|
||||
type ansiColorWriter struct {
|
||||
w io.Writer
|
||||
state csiState
|
||||
paramBuf bytes.Buffer
|
||||
}
|
||||
|
||||
const (
|
||||
firstCsiChar byte = '\x1b'
|
||||
secondeCsiChar byte = '['
|
||||
separatorChar byte = ';'
|
||||
sgrCode byte = 'm'
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = uint16(0x0001)
|
||||
foregroundGreen = uint16(0x0002)
|
||||
foregroundRed = uint16(0x0004)
|
||||
foregroundIntensity = uint16(0x0008)
|
||||
backgroundBlue = uint16(0x0010)
|
||||
backgroundGreen = uint16(0x0020)
|
||||
backgroundRed = uint16(0x0040)
|
||||
backgroundIntensity = uint16(0x0080)
|
||||
underscore = uint16(0x8000)
|
||||
|
||||
foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
|
||||
backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
|
||||
)
|
||||
|
||||
const (
|
||||
ansiReset = "0"
|
||||
ansiIntensityOn = "1"
|
||||
ansiIntensityOff = "21"
|
||||
ansiUnderlineOn = "4"
|
||||
ansiUnderlineOff = "24"
|
||||
ansiBlinkOn = "5"
|
||||
ansiBlinkOff = "25"
|
||||
|
||||
ansiForegroundBlack = "30"
|
||||
ansiForegroundRed = "31"
|
||||
ansiForegroundGreen = "32"
|
||||
ansiForegroundYellow = "33"
|
||||
ansiForegroundBlue = "34"
|
||||
ansiForegroundMagenta = "35"
|
||||
ansiForegroundCyan = "36"
|
||||
ansiForegroundWhite = "37"
|
||||
ansiForegroundDefault = "39"
|
||||
|
||||
ansiBackgroundBlack = "40"
|
||||
ansiBackgroundRed = "41"
|
||||
ansiBackgroundGreen = "42"
|
||||
ansiBackgroundYellow = "43"
|
||||
ansiBackgroundBlue = "44"
|
||||
ansiBackgroundMagenta = "45"
|
||||
ansiBackgroundCyan = "46"
|
||||
ansiBackgroundWhite = "47"
|
||||
ansiBackgroundDefault = "49"
|
||||
|
||||
ansiLightForegroundGray = "90"
|
||||
ansiLightForegroundRed = "91"
|
||||
ansiLightForegroundGreen = "92"
|
||||
ansiLightForegroundYellow = "93"
|
||||
ansiLightForegroundBlue = "94"
|
||||
ansiLightForegroundMagenta = "95"
|
||||
ansiLightForegroundCyan = "96"
|
||||
ansiLightForegroundWhite = "97"
|
||||
|
||||
ansiLightBackgroundGray = "100"
|
||||
ansiLightBackgroundRed = "101"
|
||||
ansiLightBackgroundGreen = "102"
|
||||
ansiLightBackgroundYellow = "103"
|
||||
ansiLightBackgroundBlue = "104"
|
||||
ansiLightBackgroundMagenta = "105"
|
||||
ansiLightBackgroundCyan = "106"
|
||||
ansiLightBackgroundWhite = "107"
|
||||
)
|
||||
|
||||
type drawType int
|
||||
|
||||
const (
|
||||
foreground drawType = iota
|
||||
background
|
||||
)
|
||||
|
||||
type winColor struct {
|
||||
code uint16
|
||||
drawType drawType
|
||||
}
|
||||
|
||||
var colorMap = map[string]winColor{
|
||||
ansiForegroundBlack: {0, foreground},
|
||||
ansiForegroundRed: {foregroundRed, foreground},
|
||||
ansiForegroundGreen: {foregroundGreen, foreground},
|
||||
ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground},
|
||||
ansiForegroundBlue: {foregroundBlue, foreground},
|
||||
ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
|
||||
ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground},
|
||||
ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||
ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||
|
||||
ansiBackgroundBlack: {0, background},
|
||||
ansiBackgroundRed: {backgroundRed, background},
|
||||
ansiBackgroundGreen: {backgroundGreen, background},
|
||||
ansiBackgroundYellow: {backgroundRed | backgroundGreen, background},
|
||||
ansiBackgroundBlue: {backgroundBlue, background},
|
||||
ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
|
||||
ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background},
|
||||
ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||
ansiBackgroundDefault: {0, background},
|
||||
|
||||
ansiLightForegroundGray: {foregroundIntensity, foreground},
|
||||
ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground},
|
||||
ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground},
|
||||
ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
|
||||
ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground},
|
||||
ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
|
||||
ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
|
||||
ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||
|
||||
ansiLightBackgroundGray: {backgroundIntensity, background},
|
||||
ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background},
|
||||
ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background},
|
||||
ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background},
|
||||
ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background},
|
||||
ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
|
||||
ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background},
|
||||
ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
defaultAttr *textAttributes
|
||||
)
|
||||
|
||||
func init() {
|
||||
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||
if screenInfo != nil {
|
||||
colorMap[ansiForegroundDefault] = winColor{
|
||||
screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
|
||||
foreground,
|
||||
}
|
||||
colorMap[ansiBackgroundDefault] = winColor{
|
||||
screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
|
||||
background,
|
||||
}
|
||||
defaultAttr = convertTextAttr(screenInfo.WAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
type coord struct {
|
||||
X, Y int16
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
Left, Top, Right, Bottom int16
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
DwSize coord
|
||||
DwCursorPosition coord
|
||||
WAttributes uint16
|
||||
SrWindow smallRect
|
||||
DwMaximumWindowSize coord
|
||||
}
|
||||
|
||||
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
|
||||
var csbi consoleScreenBufferInfo
|
||||
ret, _, _ := procGetConsoleScreenBufferInfo.Call(
|
||||
hConsoleOutput,
|
||||
uintptr(unsafe.Pointer(&csbi)))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return &csbi
|
||||
}
|
||||
|
||||
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
|
||||
ret, _, _ := procSetConsoleTextAttribute.Call(
|
||||
hConsoleOutput,
|
||||
uintptr(wAttributes))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
type textAttributes struct {
|
||||
foregroundColor uint16
|
||||
backgroundColor uint16
|
||||
foregroundIntensity uint16
|
||||
backgroundIntensity uint16
|
||||
underscore uint16
|
||||
otherAttributes uint16
|
||||
}
|
||||
|
||||
func convertTextAttr(winAttr uint16) *textAttributes {
|
||||
fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
|
||||
bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
|
||||
fgIntensity := winAttr & foregroundIntensity
|
||||
bgIntensity := winAttr & backgroundIntensity
|
||||
underline := winAttr & underscore
|
||||
otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
|
||||
return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
|
||||
}
|
||||
|
||||
func convertWinAttr(textAttr *textAttributes) uint16 {
|
||||
var winAttr uint16 = 0
|
||||
winAttr |= textAttr.foregroundColor
|
||||
winAttr |= textAttr.backgroundColor
|
||||
winAttr |= textAttr.foregroundIntensity
|
||||
winAttr |= textAttr.backgroundIntensity
|
||||
winAttr |= textAttr.underscore
|
||||
winAttr |= textAttr.otherAttributes
|
||||
return winAttr
|
||||
}
|
||||
|
||||
func changeColor(param []byte) {
|
||||
if defaultAttr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||
if screenInfo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
winAttr := convertTextAttr(screenInfo.WAttributes)
|
||||
strParam := string(param)
|
||||
if len(strParam) <= 0 {
|
||||
strParam = "0"
|
||||
}
|
||||
csiParam := strings.Split(strParam, string(separatorChar))
|
||||
for _, p := range csiParam {
|
||||
c, ok := colorMap[p]
|
||||
switch {
|
||||
case !ok:
|
||||
switch p {
|
||||
case ansiReset:
|
||||
winAttr.foregroundColor = defaultAttr.foregroundColor
|
||||
winAttr.backgroundColor = defaultAttr.backgroundColor
|
||||
winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
|
||||
winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
|
||||
winAttr.underscore = 0
|
||||
winAttr.otherAttributes = 0
|
||||
case ansiIntensityOn:
|
||||
winAttr.foregroundIntensity = foregroundIntensity
|
||||
case ansiIntensityOff:
|
||||
winAttr.foregroundIntensity = 0
|
||||
case ansiUnderlineOn:
|
||||
winAttr.underscore = underscore
|
||||
case ansiUnderlineOff:
|
||||
winAttr.underscore = 0
|
||||
case ansiBlinkOn:
|
||||
winAttr.backgroundIntensity = backgroundIntensity
|
||||
case ansiBlinkOff:
|
||||
winAttr.backgroundIntensity = 0
|
||||
default:
|
||||
// unknown code
|
||||
}
|
||||
case c.drawType == foreground:
|
||||
winAttr.foregroundColor = c.code
|
||||
case c.drawType == background:
|
||||
winAttr.backgroundColor = c.code
|
||||
}
|
||||
}
|
||||
winTextAttribute := convertWinAttr(winAttr)
|
||||
setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
|
||||
}
|
||||
|
||||
func parseEscapeSequence(command byte, param []byte) {
|
||||
switch command {
|
||||
case sgrCode:
|
||||
changeColor(param)
|
||||
}
|
||||
}
|
||||
|
||||
func isParameterChar(b byte) bool {
|
||||
return ('0' <= b && b <= '9') || b == separatorChar
|
||||
}
|
||||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||
r, nw, nc, first, last := 0, 0, 0, 0, 0
|
||||
var err error
|
||||
for i, ch := range p {
|
||||
switch cw.state {
|
||||
case outsideCsiCode:
|
||||
if ch == firstCsiChar {
|
||||
nc++
|
||||
cw.state = firstCsiCode
|
||||
}
|
||||
case firstCsiCode:
|
||||
switch ch {
|
||||
case firstCsiChar:
|
||||
nc++
|
||||
break
|
||||
case secondeCsiChar:
|
||||
nc++
|
||||
cw.state = secondCsiCode
|
||||
last = i - 1
|
||||
default:
|
||||
cw.state = outsideCsiCode
|
||||
}
|
||||
case secondCsiCode:
|
||||
nc++
|
||||
if isParameterChar(ch) {
|
||||
cw.paramBuf.WriteByte(ch)
|
||||
} else {
|
||||
nw, err = cw.w.Write(p[first:last])
|
||||
r += nw
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
first = i + 1
|
||||
param := cw.paramBuf.Bytes()
|
||||
cw.paramBuf.Reset()
|
||||
parseEscapeSequence(ch, param)
|
||||
cw.state = outsideCsiCode
|
||||
}
|
||||
default:
|
||||
cw.state = outsideCsiCode
|
||||
}
|
||||
}
|
||||
|
||||
if cw.state == outsideCsiCode {
|
||||
nw, err = cw.w.Write(p[first:len(p)])
|
||||
}
|
||||
|
||||
return r + nw + nc, err
|
||||
}
|
236
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_windows_test.go
generated
vendored
Normal file
236
Godeps/_workspace/src/github.com/shiena/ansicolor/ansicolor_windows_test.go
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
. "github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
func TestWritePlanText(t *testing.T) {
|
||||
inner := bytes.NewBufferString("")
|
||||
w := ansicolor.NewAnsiColorWriter(inner)
|
||||
expected := "plain text"
|
||||
fmt.Fprintf(w, expected)
|
||||
actual := inner.String()
|
||||
if actual != expected {
|
||||
t.Errorf("Get %s, want %s", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteParseText(t *testing.T) {
|
||||
inner := bytes.NewBufferString("")
|
||||
w := ansicolor.NewAnsiColorWriter(inner)
|
||||
|
||||
inputTail := "\x1b[0mtail text"
|
||||
expectedTail := "tail text"
|
||||
fmt.Fprintf(w, inputTail)
|
||||
actualTail := inner.String()
|
||||
inner.Reset()
|
||||
if actualTail != expectedTail {
|
||||
t.Errorf("Get %s, want %s", actualTail, expectedTail)
|
||||
}
|
||||
|
||||
inputHead := "head text\x1b[0m"
|
||||
expectedHead := "head text"
|
||||
fmt.Fprintf(w, inputHead)
|
||||
actualHead := inner.String()
|
||||
inner.Reset()
|
||||
if actualHead != expectedHead {
|
||||
t.Errorf("Get %s, want %s", actualHead, expectedHead)
|
||||
}
|
||||
|
||||
inputBothEnds := "both ends \x1b[0m text"
|
||||
expectedBothEnds := "both ends text"
|
||||
fmt.Fprintf(w, inputBothEnds)
|
||||
actualBothEnds := inner.String()
|
||||
inner.Reset()
|
||||
if actualBothEnds != expectedBothEnds {
|
||||
t.Errorf("Get %s, want %s", actualBothEnds, expectedBothEnds)
|
||||
}
|
||||
|
||||
inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc"
|
||||
expectedManyEsc := "\x1b\x1b\x1b many esc"
|
||||
fmt.Fprintf(w, inputManyEsc)
|
||||
actualManyEsc := inner.String()
|
||||
inner.Reset()
|
||||
if actualManyEsc != expectedManyEsc {
|
||||
t.Errorf("Get %s, want %s", actualManyEsc, expectedManyEsc)
|
||||
}
|
||||
|
||||
expectedSplit := "split text"
|
||||
for _, ch := range "split \x1b[0m text" {
|
||||
fmt.Fprintf(w, string(ch))
|
||||
}
|
||||
actualSplit := inner.String()
|
||||
inner.Reset()
|
||||
if actualSplit != expectedSplit {
|
||||
t.Errorf("Get %s, want %s", actualSplit, expectedSplit)
|
||||
}
|
||||
}
|
||||
|
||||
type screenNotFoundError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) {
|
||||
inner := bytes.NewBufferString("")
|
||||
w := ansicolor.NewAnsiColorWriter(inner)
|
||||
fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText)
|
||||
|
||||
actualText = inner.String()
|
||||
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||
if screenInfo != nil {
|
||||
actualAttributes = screenInfo.WAttributes
|
||||
} else {
|
||||
err = &screenNotFoundError{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type testParam struct {
|
||||
text string
|
||||
attributes uint16
|
||||
ansiColor string
|
||||
}
|
||||
|
||||
func TestWriteAnsiColorText(t *testing.T) {
|
||||
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||
if screenInfo == nil {
|
||||
t.Fatal("Could not get ConsoleScreenBufferInfo")
|
||||
}
|
||||
defer ChangeColor(screenInfo.WAttributes)
|
||||
defaultFgColor := screenInfo.WAttributes & uint16(0x0007)
|
||||
defaultBgColor := screenInfo.WAttributes & uint16(0x0070)
|
||||
defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008)
|
||||
defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080)
|
||||
|
||||
fgParam := []testParam{
|
||||
{"foreground black ", uint16(0x0000 | 0x0000), "30"},
|
||||
{"foreground red ", uint16(0x0004 | 0x0000), "31"},
|
||||
{"foreground green ", uint16(0x0002 | 0x0000), "32"},
|
||||
{"foreground yellow ", uint16(0x0006 | 0x0000), "33"},
|
||||
{"foreground blue ", uint16(0x0001 | 0x0000), "34"},
|
||||
{"foreground magenta", uint16(0x0005 | 0x0000), "35"},
|
||||
{"foreground cyan ", uint16(0x0003 | 0x0000), "36"},
|
||||
{"foreground white ", uint16(0x0007 | 0x0000), "37"},
|
||||
{"foreground default", defaultFgColor | 0x0000, "39"},
|
||||
{"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"},
|
||||
{"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"},
|
||||
{"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"},
|
||||
{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"},
|
||||
{"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"},
|
||||
{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"},
|
||||
{"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"},
|
||||
{"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"},
|
||||
}
|
||||
|
||||
bgParam := []testParam{
|
||||
{"background black ", uint16(0x0007 | 0x0000), "40"},
|
||||
{"background red ", uint16(0x0007 | 0x0040), "41"},
|
||||
{"background green ", uint16(0x0007 | 0x0020), "42"},
|
||||
{"background yellow ", uint16(0x0007 | 0x0060), "43"},
|
||||
{"background blue ", uint16(0x0007 | 0x0010), "44"},
|
||||
{"background magenta", uint16(0x0007 | 0x0050), "45"},
|
||||
{"background cyan ", uint16(0x0007 | 0x0030), "46"},
|
||||
{"background white ", uint16(0x0007 | 0x0070), "47"},
|
||||
{"background default", uint16(0x0007) | defaultBgColor, "49"},
|
||||
{"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"},
|
||||
{"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"},
|
||||
{"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"},
|
||||
{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"},
|
||||
{"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"},
|
||||
{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"},
|
||||
{"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"},
|
||||
{"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"},
|
||||
}
|
||||
|
||||
resetParam := []testParam{
|
||||
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"},
|
||||
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""},
|
||||
}
|
||||
|
||||
boldParam := []testParam{
|
||||
{"bold on", uint16(0x0007 | 0x0008), "1"},
|
||||
{"bold off", uint16(0x0007), "21"},
|
||||
}
|
||||
|
||||
underscoreParam := []testParam{
|
||||
{"underscore on", uint16(0x0007 | 0x8000), "4"},
|
||||
{"underscore off", uint16(0x0007), "24"},
|
||||
}
|
||||
|
||||
blinkParam := []testParam{
|
||||
{"blink on", uint16(0x0007 | 0x0080), "5"},
|
||||
{"blink off", uint16(0x0007), "25"},
|
||||
}
|
||||
|
||||
mixedParam := []testParam{
|
||||
{"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"},
|
||||
{"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"},
|
||||
{"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"},
|
||||
{"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"},
|
||||
{"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"},
|
||||
{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"},
|
||||
{"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"},
|
||||
{"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"},
|
||||
{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"},
|
||||
}
|
||||
|
||||
assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) {
|
||||
actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor)
|
||||
if actualText != expectedText {
|
||||
t.Errorf("Get %s, want %s", actualText, expectedText)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal("Could not get ConsoleScreenBufferInfo")
|
||||
}
|
||||
if actualAttributes != expectedAttributes {
|
||||
t.Errorf("Text: %s, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range fgParam {
|
||||
ResetColor()
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
for _, v := range bgParam {
|
||||
ChangeColor(uint16(0x0070 | 0x0007))
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
for _, v := range resetParam {
|
||||
ChangeColor(uint16(0x0000 | 0x0070 | 0x0008))
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
ResetColor()
|
||||
for _, v := range boldParam {
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
ResetColor()
|
||||
for _, v := range underscoreParam {
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
ResetColor()
|
||||
for _, v := range blinkParam {
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
|
||||
for _, v := range mixedParam {
|
||||
ResetColor()
|
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||
}
|
||||
}
|
24
Godeps/_workspace/src/github.com/shiena/ansicolor/example_test.go
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/shiena/ansicolor/example_test.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ansicolor_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/shiena/ansicolor"
|
||||
)
|
||||
|
||||
func ExampleNewAnsiColorWriter() {
|
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||
text := "%sforeground %sbold%s %sbackground%s\n"
|
||||
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m")
|
||||
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m")
|
||||
}
|
19
Godeps/_workspace/src/github.com/shiena/ansicolor/export_test.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/shiena/ansicolor/export_test.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor
|
||||
|
||||
import "syscall"
|
||||
|
||||
var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo
|
||||
|
||||
func ChangeColor(color uint16) {
|
||||
setConsoleTextAttribute(uintptr(syscall.Stdout), color)
|
||||
}
|
||||
|
||||
func ResetColor() {
|
||||
ChangeColor(uint16(0x0007))
|
||||
}
|
5
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go
generated
vendored
5
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go
generated
vendored
@ -18,7 +18,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
@ -370,8 +369,6 @@ func (fw fileWrap) Close() error {
|
||||
err := fw.File.Close()
|
||||
if err != nil {
|
||||
f.fs.log(fmt.Sprintf("close %s.%d: %v", f.Type(), f.Num(), err))
|
||||
} else {
|
||||
fdtrack.Close("leveldb")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -403,7 +400,6 @@ func (f *file) Open() (Reader, error) {
|
||||
return nil, err
|
||||
}
|
||||
ok:
|
||||
fdtrack.Open("leveldb")
|
||||
f.open = true
|
||||
f.fs.open++
|
||||
return fileWrap{of, f}, nil
|
||||
@ -422,7 +418,6 @@ func (f *file) Create() (Writer, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fdtrack.Open("leveldb")
|
||||
f.open = true
|
||||
f.fs.open++
|
||||
return fileWrap{of, f}, nil
|
||||
|
6
Makefile
6
Makefile
@ -2,7 +2,7 @@
|
||||
# with Go source code. If you know what GOPATH is then you probably
|
||||
# don't need to bother with make.
|
||||
|
||||
.PHONY: geth mist all test travis-test-with-coverage clean
|
||||
.PHONY: geth evm mist all test travis-test-with-coverage clean
|
||||
GOBIN = build/bin
|
||||
|
||||
geth:
|
||||
@ -10,6 +10,10 @@ geth:
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||
|
||||
evm:
|
||||
build/env.sh $(GOROOT)/bin/go install -v $(shell build/ldflags.sh) ./cmd/evm
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/evm to start the evm."
|
||||
mist:
|
||||
build/env.sh go install -v $(shell build/ldflags.sh) ./cmd/mist
|
||||
@echo "Done building."
|
||||
|
45
README.md
45
README.md
@ -1,19 +1,18 @@
|
||||
## Ethereum Go
|
||||
|
||||
Ethereum Go Client, by Jeffrey Wilcke (and some other people).
|
||||
Official golang implementation of the Ethereum protocol
|
||||
|
||||
| Linux | OSX | ARM | Windows | Tests
|
||||
----------|---------|-----|-----|---------|------
|
||||
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20develop%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](https://coveralls.io/r/ethereum/go-ethereum?branch=develop)
|
||||
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20master%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](https://coveralls.io/r/ethereum/go-ethereum?branch=master)
|
||||
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20develop%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](http://codecov.io/github/ethereum/go-ethereum?branch=develop)
|
||||
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20master%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](http://codecov.io/github/ethereum/go-ethereum?branch=master)
|
||||
|
||||
[](https://waffle.io/ethereum/go-ethereum)
|
||||
[](https://waffle.io/ethereum/go-ethereum)
|
||||
[](http://waffle.io/ethereum/go-ethereum)
|
||||
[](https://godoc.org/github.com/ethereum/go-ethereum)
|
||||
[](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Automated development builds
|
||||
======================
|
||||
## Automated development builds
|
||||
|
||||
The following builds are build automatically by our build servers after each push to the [develop](https://github.com/ethereum/go-ethereum/tree/develop) branch.
|
||||
|
||||
@ -25,8 +24,7 @@ The following builds are build automatically by our build servers after each pus
|
||||
* [Windows 64-bit](https://build.ethdev.com/builds/Windows%20Go%20develop%20branch/Geth-Win64-latest.zip)
|
||||
* [ARM](https://build.ethdev.com/builds/ARM%20Go%20develop%20branch/geth-ARM-latest.tar.bz2)
|
||||
|
||||
Building the source
|
||||
===================
|
||||
## Building the source
|
||||
|
||||
For prerequisites and detailed build instructions please read the
|
||||
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
||||
@ -38,34 +36,31 @@ Once the dependencies are installed, run
|
||||
|
||||
make geth
|
||||
|
||||
Executables
|
||||
===========
|
||||
## Executables
|
||||
|
||||
Go Ethereum comes with several wrappers/executables found in
|
||||
[the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd):
|
||||
|
||||
* `geth` Ethereum CLI (ethereum command line interface client)
|
||||
* `bootnode` runs a bootstrap node for the Discovery Protocol
|
||||
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/tests) suite:
|
||||
`/path/to/test.json > ethtest --test BlockTests --stdin`.
|
||||
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
|
||||
10000 -price 0 -dump`. See `-h` for a detailed description.
|
||||
* `disasm` disassembles EVM code: `echo "6001" | disasm`
|
||||
* `rlpdump` prints RLP structures
|
||||
Command | |
|
||||
----------|---------|
|
||||
`geth` | Ethereum CLI (ethereum command line interface client) |
|
||||
`bootnode` | runs a bootstrap node for the Discovery Protocol |
|
||||
`ethtest` | test tool which runs with the [tests](https://github.com/ethereum/tests) suite: `/path/to/test.json > ethtest --test BlockTests --stdin`.
|
||||
`evm` | is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas 10000 -price 0 -dump`. See `-h` for a detailed description. |
|
||||
`disasm` | disassembles EVM code: `echo "6001" | disasm` |
|
||||
`rlpdump` | prints RLP structures |
|
||||
|
||||
Command line options
|
||||
====================
|
||||
## Command line options
|
||||
|
||||
`geth` can be configured via command line options, environment variables and config files.
|
||||
|
||||
To get the options available:
|
||||
|
||||
geth --help
|
||||
geth help
|
||||
|
||||
For further details on options, see the [wiki](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)
|
||||
|
||||
Contribution
|
||||
============
|
||||
## Contribution
|
||||
|
||||
If you'd like to contribute to go-ethereum please fork, fix, commit and
|
||||
send a pull request. Commits who do not comply with the coding standards
|
||||
|
@ -1,26 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script runs all package tests and merges the resulting coverage
|
||||
# profiles. Coverage is accounted per package under test.
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
if [ ! -f "build/env.sh" ]; then
|
||||
echo "$0 must be run from the root of the repository."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "mode: count" > profile.cov
|
||||
|
||||
for pkg in $(go list ./...); do
|
||||
# drop the namespace prefix.
|
||||
dir=${pkg##github.com/ethereum/go-ethereum/}
|
||||
|
||||
if [[ $dir != "tests" ]]; then
|
||||
go test -covermode=count -coverprofile=$dir/profile.tmp $pkg
|
||||
fi
|
||||
if [[ -f $dir/profile.tmp ]]; then
|
||||
tail -n +2 $dir/profile.tmp >> profile.cov
|
||||
rm $dir/profile.tmp
|
||||
for d in $(find ./* -maxdepth 10 -type d -not -path "./build" -not -path "./Godeps/*" ); do
|
||||
if ls $d/*.go &> /dev/null; then
|
||||
go test -coverprofile=profile.out -covermode=atomic $d
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
echo '<<<<<< EOF' >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
)
|
||||
@ -62,6 +63,10 @@ var (
|
||||
Name: "skip",
|
||||
Usage: "Tests names to skip",
|
||||
}
|
||||
TraceFlag = cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Usage: "Enable VM tracing",
|
||||
}
|
||||
)
|
||||
|
||||
func runTestWithReader(test string, r io.Reader) error {
|
||||
@ -173,7 +178,6 @@ func runSuite(test, file string) {
|
||||
glog.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,6 +188,7 @@ func setupApp(c *cli.Context) {
|
||||
continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name)
|
||||
useStdIn := c.GlobalBool(ReadStdInFlag.Name)
|
||||
skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ")
|
||||
vm.Debug = c.GlobalBool(TraceFlag.Name)
|
||||
|
||||
if !useStdIn {
|
||||
runSuite(flagTest, flagFile)
|
||||
@ -211,6 +216,7 @@ func main() {
|
||||
ContinueOnErrorFlag,
|
||||
ReadStdInFlag,
|
||||
SkipTestsFlag,
|
||||
TraceFlag,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -40,6 +41,14 @@ var (
|
||||
Name: "debug",
|
||||
Usage: "output full trace logs",
|
||||
}
|
||||
ForceJitFlag = cli.BoolFlag{
|
||||
Name: "forcejit",
|
||||
Usage: "forces jit compilation",
|
||||
}
|
||||
DisableJitFlag = cli.BoolFlag{
|
||||
Name: "nojit",
|
||||
Usage: "disabled jit compilation",
|
||||
}
|
||||
CodeFlag = cli.StringFlag{
|
||||
Name: "code",
|
||||
Usage: "EVM code",
|
||||
@ -77,6 +86,8 @@ func init() {
|
||||
app = utils.NewApp("0.2", "the evm command line interface")
|
||||
app.Flags = []cli.Flag{
|
||||
DebugFlag,
|
||||
ForceJitFlag,
|
||||
DisableJitFlag,
|
||||
SysStatFlag,
|
||||
CodeFlag,
|
||||
GasFlag,
|
||||
@ -90,6 +101,10 @@ func init() {
|
||||
|
||||
func run(ctx *cli.Context) {
|
||||
vm.Debug = ctx.GlobalBool(DebugFlag.Name)
|
||||
vm.ForceJit = ctx.GlobalBool(ForceJitFlag.Name)
|
||||
vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name)
|
||||
|
||||
glog.SetToStderr(true)
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := state.New(common.Hash{}, db)
|
||||
@ -110,11 +125,6 @@ func run(ctx *cli.Context) {
|
||||
)
|
||||
vmdone := time.Since(tstart)
|
||||
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if ctx.GlobalBool(DumpFlag.Name) {
|
||||
fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
@ -133,7 +143,11 @@ num gc: %d
|
||||
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
|
||||
}
|
||||
|
||||
fmt.Printf("OUT: 0x%x\n", ret)
|
||||
fmt.Printf("OUT: 0x%x", ret)
|
||||
if e != nil {
|
||||
fmt.Printf(" error: %v", e)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func main() {
|
||||
@ -152,7 +166,7 @@ type VMEnv struct {
|
||||
|
||||
depth int
|
||||
Gas *big.Int
|
||||
time uint64
|
||||
time *big.Int
|
||||
logs []vm.StructLog
|
||||
}
|
||||
|
||||
@ -161,7 +175,7 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VM
|
||||
state: state,
|
||||
transactor: &transactor,
|
||||
value: value,
|
||||
time: uint64(time.Now().Unix()),
|
||||
time: big.NewInt(time.Now().Unix()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +183,7 @@ func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) Time() uint64 { return self.time }
|
||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
@ -192,6 +206,9 @@ func (self *VMEnv) StructLogs() []vm.StructLog {
|
||||
func (self *VMEnv) AddLog(log *state.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *VMEnv) CanTransfer(from vm.Account, balance *big.Int) bool {
|
||||
return from.Balance().Cmp(balance) >= 0
|
||||
}
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
return vm.Transfer(from, to, amount)
|
||||
}
|
||||
|
@ -74,10 +74,10 @@ func importChain(ctx *cli.Context) {
|
||||
if len(ctx.Args()) != 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
chain, blockDB, stateDB, extraDB := utils.MakeChain(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
start := time.Now()
|
||||
err := utils.ImportChain(chain, ctx.Args().First())
|
||||
closeAll(blockDB, stateDB, extraDB)
|
||||
chainDb.Close()
|
||||
if err != nil {
|
||||
utils.Fatalf("Import error: %v", err)
|
||||
}
|
||||
@ -88,7 +88,7 @@ func exportChain(ctx *cli.Context) {
|
||||
if len(ctx.Args()) < 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
chain, _, _, _ := utils.MakeChain(ctx)
|
||||
chain, _ := utils.MakeChain(ctx)
|
||||
start := time.Now()
|
||||
|
||||
var err error
|
||||
@ -115,17 +115,16 @@ func exportChain(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
func removeDB(ctx *cli.Context) {
|
||||
confirm, err := utils.PromptConfirm("Remove local databases?")
|
||||
confirm, err := utils.PromptConfirm("Remove local database?")
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
if confirm {
|
||||
fmt.Println("Removing chain and state databases...")
|
||||
fmt.Println("Removing chaindata...")
|
||||
start := time.Now()
|
||||
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "state"))
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
|
||||
|
||||
fmt.Printf("Removed in %v\n", time.Since(start))
|
||||
} else {
|
||||
@ -136,8 +135,8 @@ func removeDB(ctx *cli.Context) {
|
||||
func upgradeDB(ctx *cli.Context) {
|
||||
glog.Infoln("Upgrading blockchain database")
|
||||
|
||||
chain, blockDB, stateDB, extraDB := utils.MakeChain(ctx)
|
||||
v, _ := blockDB.Get([]byte("BlockchainVersion"))
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
v, _ := chainDb.Get([]byte("BlockchainVersion"))
|
||||
bcVersion := int(common.NewValue(v).Uint())
|
||||
if bcVersion == 0 {
|
||||
bcVersion = core.BlockChainVersion
|
||||
@ -149,15 +148,14 @@ func upgradeDB(ctx *cli.Context) {
|
||||
if err := utils.ExportChain(chain, exportFile); err != nil {
|
||||
utils.Fatalf("Unable to export chain for reimport %s", err)
|
||||
}
|
||||
closeAll(blockDB, stateDB, extraDB)
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "state"))
|
||||
chainDb.Close()
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
|
||||
|
||||
// Import the chain file.
|
||||
chain, blockDB, stateDB, extraDB = utils.MakeChain(ctx)
|
||||
blockDB.Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
|
||||
chain, chainDb = utils.MakeChain(ctx)
|
||||
chainDb.Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
|
||||
err := utils.ImportChain(chain, exportFile)
|
||||
closeAll(blockDB, stateDB, extraDB)
|
||||
chainDb.Close()
|
||||
if err != nil {
|
||||
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile)
|
||||
} else {
|
||||
@ -167,7 +165,7 @@ func upgradeDB(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
func dump(ctx *cli.Context) {
|
||||
chain, _, stateDB, _ := utils.MakeChain(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
for _, arg := range ctx.Args() {
|
||||
var block *types.Block
|
||||
if hashish(arg) {
|
||||
@ -180,10 +178,11 @@ func dump(ctx *cli.Context) {
|
||||
fmt.Println("{}")
|
||||
utils.Fatalf("block not found")
|
||||
} else {
|
||||
state := state.New(block.Root(), stateDB)
|
||||
state := state.New(block.Root(), chainDb)
|
||||
fmt.Printf("%s\n", state.Dump())
|
||||
}
|
||||
}
|
||||
chainDb.Close()
|
||||
}
|
||||
|
||||
// hashish returns true for strings that look like hashes.
|
||||
|
@ -145,19 +145,15 @@ func apiWordCompleter(line string, pos int) (head string, completions []string,
|
||||
return begin, completionWords, end
|
||||
}
|
||||
|
||||
func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
|
||||
func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool) *jsre {
|
||||
js := &jsre{ps1: "> "}
|
||||
js.wait = make(chan *big.Int)
|
||||
js.client = client
|
||||
js.ds = docserver.New("/")
|
||||
|
||||
if f == nil {
|
||||
f = js
|
||||
}
|
||||
|
||||
// update state in separare forever blocks
|
||||
js.re = re.New(libPath)
|
||||
if err := js.apiBindings(f); err != nil {
|
||||
if err := js.apiBindings(js); err != nil {
|
||||
utils.Fatalf("Unable to initialize console - %v", err)
|
||||
}
|
||||
|
||||
@ -232,15 +228,10 @@ func (self *jsre) loadAutoCompletion() {
|
||||
}
|
||||
|
||||
func (self *jsre) batch(statement string) {
|
||||
val, err := self.re.Run(statement)
|
||||
err := self.re.EvalAndPrettyPrint(statement)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v", err)
|
||||
} else if val.IsDefined() && val.IsObject() {
|
||||
obj, _ := self.re.Get("ret_result")
|
||||
fmt.Printf("%v", obj)
|
||||
} else if val.IsDefined() {
|
||||
fmt.Printf("%v", val)
|
||||
}
|
||||
|
||||
if self.atexit != nil {
|
||||
@ -252,22 +243,22 @@ func (self *jsre) batch(statement string) {
|
||||
|
||||
// show summary of current geth instance
|
||||
func (self *jsre) welcome() {
|
||||
self.re.Eval(`console.log('instance: ' + web3.version.client);`)
|
||||
self.re.Eval(`console.log(' datadir: ' + admin.datadir);`)
|
||||
self.re.Eval(`console.log("coinbase: " + eth.coinbase);`)
|
||||
self.re.Eval(`var lastBlockTimestamp = 1000 * eth.getBlock(eth.blockNumber).timestamp`)
|
||||
self.re.Eval(`console.log("at block: " + eth.blockNumber + " (" + new Date(lastBlockTimestamp).toLocaleDateString()
|
||||
+ " " + new Date(lastBlockTimestamp).toLocaleTimeString() + ")");`)
|
||||
|
||||
self.re.Run(`
|
||||
(function () {
|
||||
console.log('instance: ' + web3.version.client);
|
||||
console.log(' datadir: ' + admin.datadir);
|
||||
console.log("coinbase: " + eth.coinbase);
|
||||
var ts = 1000 * eth.getBlock(eth.blockNumber).timestamp;
|
||||
console.log("at block: " + eth.blockNumber + " (" + new Date(ts) + ")");
|
||||
})();
|
||||
`)
|
||||
if modules, err := self.supportedApis(); err == nil {
|
||||
loadedModules := make([]string, 0)
|
||||
for api, version := range modules {
|
||||
loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
|
||||
}
|
||||
sort.Strings(loadedModules)
|
||||
|
||||
self.re.Eval(fmt.Sprintf("var modules = '%s';", strings.Join(loadedModules, " ")))
|
||||
self.re.Eval(`console.log(" modules: " + modules);`)
|
||||
fmt.Println("modules:", strings.Join(loadedModules, " "))
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,7 +282,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||
utils.Fatalf("Unable to determine supported api's: %v", err)
|
||||
}
|
||||
|
||||
jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client)
|
||||
jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f)
|
||||
js.re.Set("jeth", struct{}{})
|
||||
t, _ := js.re.Get("jeth")
|
||||
jethObj := t.Object()
|
||||
@ -309,12 +300,12 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||
utils.Fatalf("Error loading web3.js: %v", err)
|
||||
}
|
||||
|
||||
_, err = js.re.Eval("var web3 = require('web3');")
|
||||
_, err = js.re.Run("var web3 = require('web3');")
|
||||
if err != nil {
|
||||
utils.Fatalf("Error requiring web3: %v", err)
|
||||
}
|
||||
|
||||
_, err = js.re.Eval("web3.setProvider(jeth)")
|
||||
_, err = js.re.Run("web3.setProvider(jeth)")
|
||||
if err != nil {
|
||||
utils.Fatalf("Error setting web3 provider: %v", err)
|
||||
}
|
||||
@ -333,13 +324,13 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||
}
|
||||
}
|
||||
|
||||
_, err = js.re.Eval(shortcuts)
|
||||
_, err = js.re.Run(shortcuts)
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Error setting namespaces: %v", err)
|
||||
}
|
||||
|
||||
js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
|
||||
js.re.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -387,6 +378,11 @@ func (self *jsre) interactive() {
|
||||
for {
|
||||
line, err := self.Prompt(<-prompt)
|
||||
if err != nil {
|
||||
if err == liner.ErrPromptAborted { // ctrl-C
|
||||
self.resetPrompt()
|
||||
inputln <- ""
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
inputln <- line
|
||||
@ -458,8 +454,7 @@ func (self *jsre) parseInput(code string) {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
value, err := self.re.Run(code)
|
||||
if err != nil {
|
||||
if err := self.re.EvalAndPrettyPrint(code); err != nil {
|
||||
if ottoErr, ok := err.(*otto.Error); ok {
|
||||
fmt.Println(ottoErr.String())
|
||||
} else {
|
||||
@ -467,12 +462,17 @@ func (self *jsre) parseInput(code string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
self.printValue(value)
|
||||
}
|
||||
|
||||
var indentCount = 0
|
||||
var str = ""
|
||||
|
||||
func (self *jsre) resetPrompt() {
|
||||
indentCount = 0
|
||||
str = ""
|
||||
self.ps1 = "> "
|
||||
}
|
||||
|
||||
func (self *jsre) setIndent() {
|
||||
open := strings.Count(str, "{")
|
||||
open += strings.Count(str, "(")
|
||||
@ -486,10 +486,3 @@ func (self *jsre) setIndent() {
|
||||
self.ps1 += " "
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) printValue(v interface{}) {
|
||||
val, err := self.re.PrettyPrint(v)
|
||||
if err == nil {
|
||||
fmt.Printf("%v", val)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
@ -38,19 +37,21 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Geth"
|
||||
Version = "1.0.1"
|
||||
Version = "1.1.3"
|
||||
VersionMajor = 1
|
||||
VersionMinor = 1
|
||||
VersionPatch = 3
|
||||
)
|
||||
|
||||
var (
|
||||
@ -307,6 +308,9 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
|
||||
utils.ExecFlag,
|
||||
utils.WhisperEnabledFlag,
|
||||
utils.VMDebugFlag,
|
||||
utils.VMForceJitFlag,
|
||||
utils.VMJitCacheFlag,
|
||||
utils.VMEnableJitFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.VerbosityFlag,
|
||||
@ -328,6 +332,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
|
||||
}
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
utils.SetupLogger(ctx)
|
||||
utils.SetupVM(ctx)
|
||||
if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
|
||||
utils.StartPProf(ctx)
|
||||
}
|
||||
@ -346,6 +351,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func makeDefaultExtra() []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), ClientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
return extra
|
||||
}
|
||||
|
||||
func run(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
if ctx.GlobalBool(utils.OlympicFlag.Name) {
|
||||
@ -353,6 +379,8 @@ func run(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
cfg.ExtraData = makeDefaultExtra()
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
@ -366,14 +394,6 @@ func run(ctx *cli.Context) {
|
||||
func attach(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
// Wrap the standard output with a colorified stream (windows)
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
if pr, pw, err := os.Pipe(); err == nil {
|
||||
go io.Copy(colorable.NewColorableStdout(), pr)
|
||||
os.Stdout = pw
|
||||
}
|
||||
}
|
||||
|
||||
var client comms.EthereumClient
|
||||
var err error
|
||||
if ctx.Args().Present() {
|
||||
@ -393,7 +413,7 @@ func attach(ctx *cli.Context) {
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
client,
|
||||
true,
|
||||
nil)
|
||||
)
|
||||
|
||||
if ctx.GlobalString(utils.ExecFlag.Name) != "" {
|
||||
repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
|
||||
@ -406,14 +426,6 @@ func attach(ctx *cli.Context) {
|
||||
func console(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
// Wrap the standard output with a colorified stream (windows)
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
if pr, pw, err := os.Pipe(); err == nil {
|
||||
go io.Copy(colorable.NewColorableStdout(), pr)
|
||||
os.Stdout = pw
|
||||
}
|
||||
}
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
@ -533,9 +545,6 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
|
||||
// Start Ethereum itself
|
||||
utils.StartEthereum(eth)
|
||||
|
||||
// Start logging file descriptor stats.
|
||||
fdtrack.Start()
|
||||
|
||||
am := eth.AccountManager()
|
||||
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
|
||||
accounts := strings.Split(account, " ")
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -152,6 +153,7 @@ func InitOlympic() {
|
||||
params.MaximumExtraDataSize = big.NewInt(1024)
|
||||
NetworkIdFlag.Value = 0
|
||||
core.BlockReward = big.NewInt(1.5e+18)
|
||||
core.ExpDiffPeriod = big.NewInt(math.MaxInt64)
|
||||
}
|
||||
|
||||
func FormatTransactionData(data string) []byte {
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
@ -138,11 +138,8 @@ func (self *DirectoryFlag) Set(value string) {
|
||||
func expandPath(p string) string {
|
||||
if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||
if user, err := user.Current(); err == nil {
|
||||
if err == nil {
|
||||
p = strings.Replace(p, "~", user.HomeDir, 1)
|
||||
}
|
||||
p = user.HomeDir + p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Clean(os.ExpandEnv(p))
|
||||
return path.Clean(os.ExpandEnv(p))
|
||||
}
|
||||
|
@ -23,18 +23,15 @@ import (
|
||||
)
|
||||
|
||||
func TestPathExpansion(t *testing.T) {
|
||||
|
||||
user, _ := user.Current()
|
||||
|
||||
tests := map[string]string{
|
||||
"/home/someuser/tmp": "/home/someuser/tmp",
|
||||
"~/tmp": user.HomeDir + "/tmp",
|
||||
"~thisOtherUser/b/": "~thisOtherUser/b",
|
||||
"$DDDXXX/a/b": "/tmp/a/b",
|
||||
"/a/b/": "/a/b",
|
||||
}
|
||||
|
||||
os.Setenv("DDDXXX", "/tmp")
|
||||
|
||||
for test, expected := range tests {
|
||||
got := expandPath(test)
|
||||
if got != expected {
|
||||
|
@ -21,29 +21,32 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/rpc/api"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
@ -172,6 +175,25 @@ var (
|
||||
Value: "",
|
||||
}
|
||||
|
||||
// vm flags
|
||||
VMDebugFlag = cli.BoolFlag{
|
||||
Name: "vmdebug",
|
||||
Usage: "Virtual Machine debug output",
|
||||
}
|
||||
VMForceJitFlag = cli.BoolFlag{
|
||||
Name: "forcejit",
|
||||
Usage: "Force the JIT VM to take precedence",
|
||||
}
|
||||
VMJitCacheFlag = cli.IntFlag{
|
||||
Name: "jitcache",
|
||||
Usage: "Amount of cached JIT VM programs",
|
||||
Value: 64,
|
||||
}
|
||||
VMEnableJitFlag = cli.BoolFlag{
|
||||
Name: "jitvm",
|
||||
Usage: "Enable the JIT VM",
|
||||
}
|
||||
|
||||
// logging and debug settings
|
||||
LogFileFlag = cli.StringFlag{
|
||||
Name: "logfile",
|
||||
@ -196,10 +218,6 @@ var (
|
||||
Usage: "The syntax of the argument is a comma-separated list of pattern=N, where pattern is a literal file name (minus the \".go\" suffix) or \"glob\" pattern and N is a log verbosity level.",
|
||||
Value: glog.GetVModule(),
|
||||
}
|
||||
VMDebugFlag = cli.BoolFlag{
|
||||
Name: "vmdebug",
|
||||
Usage: "Virtual Machine debug output",
|
||||
}
|
||||
BacktraceAtFlag = cli.GenericFlag{
|
||||
Name: "backtrace_at",
|
||||
Usage: "If set to a file and line number (e.g., \"block.go:271\") holding a logging statement, a stack trace will be logged",
|
||||
@ -434,24 +452,25 @@ func SetupLogger(ctx *cli.Context) {
|
||||
glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))
|
||||
}
|
||||
|
||||
// SetupVM configured the VM package's global settings
|
||||
func SetupVM(ctx *cli.Context) {
|
||||
vm.EnableJit = ctx.GlobalBool(VMEnableJitFlag.Name)
|
||||
vm.ForceJit = ctx.GlobalBool(VMForceJitFlag.Name)
|
||||
vm.SetJITCacheSize(ctx.GlobalInt(VMJitCacheFlag.Name))
|
||||
}
|
||||
|
||||
// MakeChain creates a chain manager from set command line flags.
|
||||
func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, extraDB common.Database) {
|
||||
func MakeChain(ctx *cli.Context) (chain *core.ChainManager, chainDb common.Database) {
|
||||
datadir := ctx.GlobalString(DataDirFlag.Name)
|
||||
cache := ctx.GlobalInt(CacheFlag.Name)
|
||||
|
||||
var err error
|
||||
if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "blockchain"), cache); err != nil {
|
||||
Fatalf("Could not open database: %v", err)
|
||||
}
|
||||
if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "state"), cache); err != nil {
|
||||
Fatalf("Could not open database: %v", err)
|
||||
}
|
||||
if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "extra"), cache); err != nil {
|
||||
if chainDb, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache); err != nil {
|
||||
Fatalf("Could not open database: %v", err)
|
||||
}
|
||||
if ctx.GlobalBool(OlympicFlag.Name) {
|
||||
InitOlympic()
|
||||
_, err := core.WriteTestNetGenesisBlock(stateDB, blockDB, 42)
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb, 42)
|
||||
if err != nil {
|
||||
glog.Fatalln(err)
|
||||
}
|
||||
@ -460,14 +479,14 @@ func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, ex
|
||||
eventMux := new(event.TypeMux)
|
||||
pow := ethash.New()
|
||||
//genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB)
|
||||
chain, err = core.NewChainManager(blockDB, stateDB, extraDB, pow, eventMux)
|
||||
chain, err = core.NewChainManager(chainDb, pow, eventMux)
|
||||
if err != nil {
|
||||
Fatalf("Could not start chainmanager: %v", err)
|
||||
}
|
||||
|
||||
proc := core.NewBlockProcessor(stateDB, extraDB, pow, chain, eventMux)
|
||||
proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux)
|
||||
chain.SetProcessor(proc)
|
||||
return chain, blockDB, stateDB, extraDB
|
||||
return chain, chainDb
|
||||
}
|
||||
|
||||
// MakeChain creates an account manager from set command line flags.
|
||||
@ -478,7 +497,7 @@ func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
|
||||
}
|
||||
|
||||
func IpcSocketPath(ctx *cli.Context) (ipcpath string) {
|
||||
if common.IsWindows() {
|
||||
if runtime.GOOS == "windows" {
|
||||
ipcpath = common.DefaultIpcPath()
|
||||
if ctx.GlobalIsSet(IPCPathFlag.Name) {
|
||||
ipcpath = ctx.GlobalString(IPCPathFlag.Name)
|
||||
@ -501,15 +520,20 @@ func StartIPC(eth *eth.Ethereum, ctx *cli.Context) error {
|
||||
Endpoint: IpcSocketPath(ctx),
|
||||
}
|
||||
|
||||
xeth := xeth.New(eth, nil)
|
||||
codec := codec.JSON
|
||||
initializer := func(conn net.Conn) (shared.EthereumApi, error) {
|
||||
fe := useragent.NewRemoteFrontend(conn, eth.AccountManager())
|
||||
xeth := xeth.New(eth, fe)
|
||||
codec := codec.JSON
|
||||
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec, xeth, eth)
|
||||
if err != nil {
|
||||
return err
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec, xeth, eth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return api.Merge(apis...), nil
|
||||
}
|
||||
|
||||
return comms.StartIpc(config, codec, api.Merge(apis...))
|
||||
return comms.StartIpc(config, codec.JSON, initializer)
|
||||
}
|
||||
|
||||
func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -94,7 +95,7 @@ func TestSaveInfo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
filename := "/tmp/solctest.info.json"
|
||||
filename := path.Join(os.TempDir(), "solctest.info.json")
|
||||
os.Remove(filename)
|
||||
cinfohash, err := SaveInfo(&cinfo, filename)
|
||||
if err != nil {
|
||||
@ -110,4 +111,4 @@ func TestSaveInfo(t *testing.T) {
|
||||
if cinfohash != infohash {
|
||||
t.Errorf("content hash for info is incorrect. expected %v, got %v", infohash.Hex(), cinfohash.Hex())
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,6 @@ func New(docRoot string) (self *DocServer) {
|
||||
DocRoot: docRoot,
|
||||
schemes: []string{"file"},
|
||||
}
|
||||
self.DocRoot = "/tmp/"
|
||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
||||
return
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -27,12 +28,18 @@ import (
|
||||
)
|
||||
|
||||
func TestGetAuthContent(t *testing.T) {
|
||||
text := "test"
|
||||
hash := common.Hash{}
|
||||
copy(hash[:], crypto.Sha3([]byte(text)))
|
||||
ioutil.WriteFile("/tmp/test.content", []byte(text), os.ModePerm)
|
||||
dir, err := ioutil.TempDir("", "docserver-test")
|
||||
if err != nil {
|
||||
t.Fatal("cannot create temporary directory:", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
ds := New(dir)
|
||||
|
||||
ds := New("/tmp/")
|
||||
text := "test"
|
||||
hash := crypto.Sha3Hash([]byte(text))
|
||||
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
||||
t.Fatal("could not write test file", err)
|
||||
}
|
||||
content, err := ds.GetAuthContent("file:///test.content", hash)
|
||||
if err != nil {
|
||||
t.Errorf("no error expected, got %v", err)
|
||||
@ -67,4 +74,4 @@ func TestRegisterScheme(t *testing.T) {
|
||||
if !ds.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme to be registered")
|
||||
}
|
||||
}
|
||||
}
|
@ -116,14 +116,3 @@ func DefaultIpcPath() string {
|
||||
}
|
||||
return filepath.Join(DefaultDataDir(), "geth.ipc")
|
||||
}
|
||||
|
||||
func IsWindows() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
func WindonizePath(path string) string {
|
||||
if string(path[0]) == "/" && IsWindows() {
|
||||
path = path[1:]
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
// Copyright 2014 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 common
|
||||
|
||||
import (
|
||||
"os"
|
||||
// "testing"
|
||||
|
||||
checker "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type CommonSuite struct{}
|
||||
|
||||
var _ = checker.Suite(&CommonSuite{})
|
||||
|
||||
func (s *CommonSuite) TestOS(c *checker.C) {
|
||||
expwin := (os.PathSeparator == '\\' && os.PathListSeparator == ';')
|
||||
res := IsWindows()
|
||||
|
||||
if !expwin {
|
||||
c.Assert(res, checker.Equals, expwin, checker.Commentf("IsWindows is", res, "but path is", os.PathSeparator))
|
||||
} else {
|
||||
c.Assert(res, checker.Not(checker.Equals), expwin, checker.Commentf("IsWindows is", res, "but path is", os.PathSeparator))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommonSuite) TestWindonziePath(c *checker.C) {
|
||||
iswindowspath := os.PathSeparator == '\\'
|
||||
path := "/opt/eth/test/file.ext"
|
||||
res := WindonizePath(path)
|
||||
ressep := string(res[0])
|
||||
|
||||
if !iswindowspath {
|
||||
c.Assert(ressep, checker.Equals, "/")
|
||||
} else {
|
||||
c.Assert(ressep, checker.Not(checker.Equals), "/")
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ func (s *SizeSuite) TestStorageSizeString(c *checker.C) {
|
||||
c.Assert(StorageSize(data3).String(), checker.Equals, exp3)
|
||||
}
|
||||
|
||||
func (s *CommonSuite) TestCommon(c *checker.C) {
|
||||
func (s *SizeSuite) TestCommon(c *checker.C) {
|
||||
ether := CurrencyToString(BigPow(10, 19))
|
||||
finney := CurrencyToString(BigPow(10, 16))
|
||||
szabo := CurrencyToString(BigPow(10, 13))
|
||||
|
@ -168,8 +168,8 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
evmux := new(event.TypeMux)
|
||||
chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux)
|
||||
chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
|
||||
chainman, _ := NewChainManager(db, FakePow{}, evmux)
|
||||
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
@ -41,8 +41,7 @@ const (
|
||||
)
|
||||
|
||||
type BlockProcessor struct {
|
||||
db common.Database
|
||||
extraDb common.Database
|
||||
chainDb common.Database
|
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex
|
||||
// Canonical block chain
|
||||
@ -57,25 +56,35 @@ type BlockProcessor struct {
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
|
||||
// TODO: type GasPool big.Int
|
||||
//
|
||||
// GasPool is implemented by state.StateObject. This is a historical
|
||||
// coincidence. Gas tracking should move out of StateObject.
|
||||
|
||||
// GasPool tracks the amount of gas available during
|
||||
// execution of the transactions in a block.
|
||||
type GasPool interface {
|
||||
AddGas(gas, price *big.Int)
|
||||
SubGas(gas, price *big.Int) error
|
||||
}
|
||||
|
||||
func NewBlockProcessor(db common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
|
||||
sm := &BlockProcessor{
|
||||
db: db,
|
||||
extraDb: extra,
|
||||
chainDb: db,
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: pow,
|
||||
bc: chainManager,
|
||||
eventMux: eventMux,
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
|
||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase())
|
||||
coinbase.SetGasLimit(block.GasLimit())
|
||||
gp := statedb.GetOrNewStateObject(block.Coinbase())
|
||||
gp.SetGasLimit(block.GasLimit())
|
||||
|
||||
// Process the transactions on to parent state
|
||||
receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
|
||||
receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -83,11 +92,8 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
|
||||
return receipts, nil
|
||||
}
|
||||
|
||||
func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
|
||||
// If we are mining this block and validating we want to set the logs back to 0
|
||||
|
||||
cb := statedb.GetStateObject(coinbase.Address())
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, cb)
|
||||
func (self *BlockProcessor) ApplyTransaction(gp GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -122,7 +128,7 @@ func (self *BlockProcessor) ChainManager() *ChainManager {
|
||||
return self.bc
|
||||
}
|
||||
|
||||
func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
|
||||
func (self *BlockProcessor) ApplyTransactions(gp GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
@ -134,7 +140,7 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
|
||||
for i, tx := range txs {
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
|
||||
receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, header, tx, totalUsedGas, transientProcess)
|
||||
receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -201,13 +207,13 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, receipts
|
||||
|
||||
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, receipts types.Receipts, err error) {
|
||||
// Create a new state based on the parent's root (e.g., create copy)
|
||||
state := state.New(parent.Root(), sm.db)
|
||||
state := state.New(parent.Root(), sm.chainDb)
|
||||
header := block.Header()
|
||||
uncles := block.Uncles()
|
||||
txs := block.Transactions()
|
||||
|
||||
// Block validation
|
||||
if err = ValidateHeader(sm.Pow, header, parent, false); err != nil {
|
||||
if err = ValidateHeader(sm.Pow, header, parent, false, false); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -331,7 +337,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
|
||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
||||
}
|
||||
|
||||
if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true); err != nil {
|
||||
if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true, true); err != nil {
|
||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
||||
}
|
||||
}
|
||||
@ -342,7 +348,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
|
||||
// GetBlockReceipts returns the receipts beloniging to the block hash
|
||||
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
||||
if block := sm.ChainManager().GetBlock(bhash); block != nil {
|
||||
return GetBlockReceipts(sm.extraDb, block.Hash())
|
||||
return GetBlockReceipts(sm.chainDb, block.Hash())
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -352,41 +358,35 @@ func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
||||
// where it tries to get it from the (updated) method which gets them from the receipts or
|
||||
// the depricated way by re-processing the block.
|
||||
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
|
||||
receipts := GetBlockReceipts(sm.extraDb, block.Hash())
|
||||
if len(receipts) > 0 {
|
||||
// coalesce logs
|
||||
for _, receipt := range receipts {
|
||||
logs = append(logs, receipt.Logs()...)
|
||||
}
|
||||
return
|
||||
receipts := GetBlockReceipts(sm.chainDb, block.Hash())
|
||||
// coalesce logs
|
||||
for _, receipt := range receipts {
|
||||
logs = append(logs, receipt.Logs()...)
|
||||
}
|
||||
|
||||
// TODO: remove backward compatibility
|
||||
var (
|
||||
parent = sm.bc.GetBlock(block.ParentHash())
|
||||
state = state.New(parent.Root(), sm.db)
|
||||
)
|
||||
|
||||
sm.TransitionState(state, parent, block, true)
|
||||
|
||||
return state.Logs(), nil
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
// Validates a block. Returns an error if the block is invalid.
|
||||
func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow bool) error {
|
||||
func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow, uncle bool) error {
|
||||
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
|
||||
}
|
||||
|
||||
if block.Time > uint64(time.Now().Unix()) {
|
||||
return BlockFutureErr
|
||||
if uncle {
|
||||
if block.Time.Cmp(common.MaxBig) == 1 {
|
||||
return BlockTSTooBigErr
|
||||
}
|
||||
} else {
|
||||
if block.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
|
||||
return BlockFutureErr
|
||||
}
|
||||
}
|
||||
if block.Time <= parent.Time() {
|
||||
if block.Time.Cmp(parent.Time()) != 1 {
|
||||
return BlockEqualTSErr
|
||||
}
|
||||
|
||||
expd := CalcDifficulty(block.Time, parent.Time(), parent.Number(), parent.Difficulty())
|
||||
expd := CalcDifficulty(block.Time.Uint64(), parent.Time().Uint64(), parent.Number(), parent.Difficulty())
|
||||
if expd.Cmp(block.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||
}
|
||||
|
@ -33,28 +33,28 @@ func proc() (*BlockProcessor, *ChainManager) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
|
||||
WriteTestNetGenesisBlock(db, db, 0)
|
||||
chainMan, err := NewChainManager(db, db, db, thePow(), &mux)
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
chainMan, err := NewChainManager(db, thePow(), &mux)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return NewBlockProcessor(db, db, ezp.New(), chainMan, &mux), chainMan
|
||||
return NewBlockProcessor(db, ezp.New(), chainMan, &mux), chainMan
|
||||
}
|
||||
|
||||
func TestNumber(t *testing.T) {
|
||||
pow := ezp.New()
|
||||
_, chain := proc()
|
||||
|
||||
statedb := state.New(chain.Genesis().Root(), chain.stateDb)
|
||||
statedb := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||
header := makeHeader(chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
err := ValidateHeader(pow, header, chain.Genesis(), false)
|
||||
err := ValidateHeader(pow, header, chain.Genesis(), false, false)
|
||||
if err != BlockNumberErr {
|
||||
t.Errorf("expected block number error, got %q", err)
|
||||
}
|
||||
|
||||
header = makeHeader(chain.Genesis(), statedb)
|
||||
err = ValidateHeader(pow, header, chain.Genesis(), false)
|
||||
err = ValidateHeader(pow, header, chain.Genesis(), false, false)
|
||||
if err == BlockNumberErr {
|
||||
t.Errorf("didn't expect block number error")
|
||||
}
|
||||
|
@ -20,8 +20,5 @@ import "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
// Set of manually tracked bad hashes (usually hard forks)
|
||||
var BadHashes = map[common.Hash]bool{
|
||||
common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"): true,
|
||||
common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"): true,
|
||||
common.HexToHash("7064455b364775a16afbdecd75370e912c6e2879f202eda85b9beae547fff3ac"): true,
|
||||
common.HexToHash("5b7c80070a6eff35f3eb3181edb023465c776d40af2885571e1bc4689f3a44d8"): true,
|
||||
common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true,
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
jeff = common.HexToAddress("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c")
|
||||
vitalik = common.HexToAddress("1baf27b88c48dd02b744999cf3522766929d2b2a")
|
||||
christoph = common.HexToAddress("60d11b58744784dc97f878f7e3749c0f1381a004")
|
||||
gav = common.HexToAddress("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a")
|
||||
jeff = common.HexToAddress("959c33de5961820567930eccce51ea715c496f85")
|
||||
vitalik = common.HexToAddress("c8158da0b567a8cc898991c2c2a073af67dc03a9")
|
||||
christoph = common.HexToAddress("7a19a893f91d5b6e2cdf941b6acbba2cbcf431ee")
|
||||
gav = common.HexToAddress("539dd9aaf45c3feb03f9c004f4098bd3268fef6b")
|
||||
)
|
||||
|
||||
// Canary will check the 0'd address of the 4 contracts above.
|
||||
|
@ -166,16 +166,21 @@ func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int,
|
||||
}
|
||||
|
||||
func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
||||
time := parent.Time() + 10 // block time is fixed at 10 seconds
|
||||
var time *big.Int
|
||||
if parent.Time() == nil {
|
||||
time = big.NewInt(10)
|
||||
} else {
|
||||
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
|
||||
}
|
||||
return &types.Header{
|
||||
Root: state.Root(),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: CalcDifficulty(time, parent.Time(), parent.Number(), parent.Difficulty()),
|
||||
Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
GasLimit: CalcGasLimit(parent),
|
||||
GasUsed: new(big.Int),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
Time: uint64(time),
|
||||
Time: time,
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,9 +189,9 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
||||
func newCanonical(n int, db common.Database) (*BlockProcessor, error) {
|
||||
evmux := &event.TypeMux{}
|
||||
|
||||
WriteTestNetGenesisBlock(db, db, 0)
|
||||
chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux)
|
||||
bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux)
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
chainman, _ := NewChainManager(db, FakePow{}, evmux)
|
||||
bman := NewBlockProcessor(db, FakePow{}, chainman, evmux)
|
||||
bman.bc.SetProcessor(bman)
|
||||
parent := bman.bc.CurrentBlock()
|
||||
if n == 0 {
|
||||
|
@ -77,8 +77,8 @@ func ExampleGenerateChain() {
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux)
|
||||
chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
|
||||
chainman, _ := NewChainManager(db, FakePow{}, evmux)
|
||||
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
|
||||
if i, err := chainman.InsertChain(chain); err != nil {
|
||||
fmt.Printf("insert error (block %d): %v\n", i, err)
|
||||
return
|
||||
|
@ -56,9 +56,7 @@ const (
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
blockDb common.Database
|
||||
stateDb common.Database
|
||||
extraDb common.Database
|
||||
chainDb common.Database
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
@ -85,12 +83,10 @@ type ChainManager struct {
|
||||
pow pow.PoW
|
||||
}
|
||||
|
||||
func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
|
||||
func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
|
||||
cache, _ := lru.New(blockCacheLimit)
|
||||
bc := &ChainManager{
|
||||
blockDb: blockDb,
|
||||
stateDb: stateDb,
|
||||
extraDb: extraDb,
|
||||
chainDb: chainDb,
|
||||
eventMux: mux,
|
||||
quit: make(chan struct{}),
|
||||
cache: cache,
|
||||
@ -103,7 +99,7 @@ func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bc.genesisBlock, err = WriteGenesisBlock(stateDb, blockDb, reader)
|
||||
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -195,15 +191,15 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
}
|
||||
|
||||
func (self *ChainManager) State() *state.StateDB {
|
||||
return state.New(self.CurrentBlock().Root(), self.stateDb)
|
||||
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
||||
}
|
||||
|
||||
func (bc *ChainManager) recover() bool {
|
||||
data, _ := bc.blockDb.Get([]byte("checkpoint"))
|
||||
data, _ := bc.chainDb.Get([]byte("checkpoint"))
|
||||
if len(data) != 0 {
|
||||
block := bc.GetBlock(common.BytesToHash(data))
|
||||
if block != nil {
|
||||
err := bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes())
|
||||
err := bc.chainDb.Put([]byte("LastBlock"), block.Hash().Bytes())
|
||||
if err != nil {
|
||||
glog.Fatalln("db write err:", err)
|
||||
}
|
||||
@ -217,7 +213,7 @@ func (bc *ChainManager) recover() bool {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setLastState() error {
|
||||
data, _ := bc.blockDb.Get([]byte("LastBlock"))
|
||||
data, _ := bc.chainDb.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
block := bc.GetBlock(common.BytesToHash(data))
|
||||
if block != nil {
|
||||
@ -264,7 +260,7 @@ func (bc *ChainManager) Reset() {
|
||||
bc.cache, _ = lru.New(blockCacheLimit)
|
||||
|
||||
// Prepare the genesis block
|
||||
err := WriteBlock(bc.blockDb, bc.genesisBlock)
|
||||
err := WriteBlock(bc.chainDb, bc.genesisBlock)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
@ -277,7 +273,7 @@ func (bc *ChainManager) Reset() {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) removeBlock(block *types.Block) {
|
||||
bc.blockDb.Delete(append(blockHashPre, block.Hash().Bytes()...))
|
||||
bc.chainDb.Delete(append(blockHashPre, block.Hash().Bytes()...))
|
||||
}
|
||||
|
||||
func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
|
||||
@ -292,7 +288,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
|
||||
gb.Td = gb.Difficulty()
|
||||
bc.genesisBlock = gb
|
||||
|
||||
err := WriteBlock(bc.blockDb, bc.genesisBlock)
|
||||
err := WriteBlock(bc.chainDb, bc.genesisBlock)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
@ -339,14 +335,14 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error
|
||||
// insert injects a block into the current chain block chain. Note, this function
|
||||
// assumes that the `mu` mutex is held!
|
||||
func (bc *ChainManager) insert(block *types.Block) {
|
||||
err := WriteHead(bc.blockDb, block)
|
||||
err := WriteHead(bc.chainDb, block)
|
||||
if err != nil {
|
||||
glog.Fatal("db write fail:", err)
|
||||
}
|
||||
|
||||
bc.checkpoint++
|
||||
if bc.checkpoint > checkpointLimit {
|
||||
err = bc.blockDb.Put([]byte("checkpoint"), block.Hash().Bytes())
|
||||
err = bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes())
|
||||
if err != nil {
|
||||
glog.Fatal("db write fail:", err)
|
||||
}
|
||||
@ -369,7 +365,7 @@ func (bc *ChainManager) HasBlock(hash common.Hash) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
data, _ := bc.blockDb.Get(append(blockHashPre, hash[:]...))
|
||||
data, _ := bc.chainDb.Get(append(blockHashPre, hash[:]...))
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
@ -399,7 +395,7 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
|
||||
return block.(*types.Block)
|
||||
}
|
||||
|
||||
block := GetBlockByHash(self.blockDb, hash)
|
||||
block := GetBlockByHash(self.chainDb, hash)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
@ -433,7 +429,7 @@ func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*
|
||||
|
||||
// non blocking version
|
||||
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
|
||||
return GetBlockByNumber(self.blockDb, num)
|
||||
return GetBlockByNumber(self.chainDb, num)
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
|
||||
@ -521,7 +517,7 @@ func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status wr
|
||||
status = SideStatTy
|
||||
}
|
||||
|
||||
err = WriteBlock(self.blockDb, block)
|
||||
err = WriteBlock(self.chainDb, block)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
@ -600,7 +596,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
|
||||
// Allow up to MaxFuture second in the future blocks. If this limit
|
||||
// is exceeded the chain is discarded and processed at a later time
|
||||
// if given.
|
||||
if max := uint64(time.Now().Unix()) + maxTimeFutureBlocks; block.Time() > max {
|
||||
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
|
||||
if block.Time().Cmp(max) == 1 {
|
||||
return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
|
||||
}
|
||||
|
||||
@ -632,15 +629,15 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
queue[i] = ChainEvent{block, block.Hash(), logs}
|
||||
queueEvent.canonicalCount++
|
||||
|
||||
// This puts transactions in a extra db for rpc
|
||||
PutTransactions(self.extraDb, block, block.Transactions())
|
||||
PutTransactions(self.chainDb, block, block.Transactions())
|
||||
// store the receipts
|
||||
PutReceipts(self.extraDb, receipts)
|
||||
PutReceipts(self.chainDb, receipts)
|
||||
case SideStatTy:
|
||||
if glog.V(logger.Detail) {
|
||||
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
@ -651,7 +648,9 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
|
||||
queue[i] = ChainSplitEvent{block, logs}
|
||||
queueEvent.splitCount++
|
||||
}
|
||||
PutBlockReceipts(self.extraDb, block, receipts)
|
||||
if err := PutBlockReceipts(self.chainDb, block, receipts); err != nil {
|
||||
glog.V(logger.Warn).Infoln("error writing block receipts:", err)
|
||||
}
|
||||
|
||||
stats.processed++
|
||||
}
|
||||
@ -733,8 +732,8 @@ func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
self.insert(block)
|
||||
// write canonical receipts and transactions
|
||||
PutTransactions(self.extraDb, block, block.Transactions())
|
||||
PutReceipts(self.extraDb, GetBlockReceipts(self.extraDb, block.Hash()))
|
||||
PutTransactions(self.chainDb, block, block.Transactions())
|
||||
PutReceipts(self.chainDb, GetBlockReceipts(self.chainDb, block.Hash()))
|
||||
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
@ -48,14 +48,14 @@ func thePow() pow.PoW {
|
||||
|
||||
func theChainManager(db common.Database, t *testing.T) *ChainManager {
|
||||
var eventMux event.TypeMux
|
||||
WriteTestNetGenesisBlock(db, db, 0)
|
||||
chainMan, err := NewChainManager(db, db, db, thePow(), &eventMux)
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
chainMan, err := NewChainManager(db, thePow(), &eventMux)
|
||||
if err != nil {
|
||||
t.Error("failed creating chainmanager:", err)
|
||||
t.FailNow()
|
||||
return nil
|
||||
}
|
||||
blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux)
|
||||
blockMan := NewBlockProcessor(db, nil, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
|
||||
return chainMan
|
||||
@ -125,7 +125,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
|
||||
|
||||
bman.bc.mu.Lock()
|
||||
{
|
||||
WriteBlock(bman.bc.blockDb, block)
|
||||
WriteBlock(bman.bc.chainDb, block)
|
||||
}
|
||||
bman.bc.mu.Unlock()
|
||||
}
|
||||
@ -387,7 +387,7 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block
|
||||
|
||||
func chm(genesis *types.Block, db common.Database) *ChainManager {
|
||||
var eventMux event.TypeMux
|
||||
bc := &ChainManager{extraDb: db, blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
|
||||
bc := &ChainManager{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
|
||||
bc.cache, _ = lru.New(100)
|
||||
bc.futureBlocks, _ = lru.New(100)
|
||||
bc.processor = bproc{}
|
||||
@ -399,7 +399,7 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
|
||||
func TestReorgLongest(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
genesis, err := WriteTestNetGenesisBlock(db, db, 0)
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
@ -422,7 +422,7 @@ func TestReorgLongest(t *testing.T) {
|
||||
|
||||
func TestReorgShortest(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, err := WriteTestNetGenesisBlock(db, db, 0)
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
@ -446,13 +446,13 @@ func TestReorgShortest(t *testing.T) {
|
||||
func TestInsertNonceError(t *testing.T) {
|
||||
for i := 1; i < 25 && !t.Failed(); i++ {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, err := WriteTestNetGenesisBlock(db, db, 0)
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
bc := chm(genesis, db)
|
||||
bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux)
|
||||
bc.processor = NewBlockProcessor(db, bc.pow, bc, bc.eventMux)
|
||||
blocks := makeChain(bc.currentBlock, i, db, 0)
|
||||
|
||||
fail := rand.Int() % len(blocks)
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
var (
|
||||
blockHashPre = []byte("block-hash-")
|
||||
blockNumPre = []byte("block-num-")
|
||||
expDiffPeriod = big.NewInt(100000)
|
||||
ExpDiffPeriod = big.NewInt(100000)
|
||||
)
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||
@ -57,7 +57,7 @@ func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int)
|
||||
}
|
||||
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, expDiffPeriod)
|
||||
periodCount.Div(periodCount, ExpDiffPeriod)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
expDiff := periodCount.Sub(periodCount, common.Big2)
|
||||
|
@ -25,9 +25,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
BlockNumberErr = errors.New("block number invalid")
|
||||
BlockFutureErr = errors.New("block time is in the future")
|
||||
BlockEqualTSErr = errors.New("block time stamp equal to previous")
|
||||
BlockNumberErr = errors.New("block number invalid")
|
||||
BlockFutureErr = errors.New("block time is in the future")
|
||||
BlockTSTooBigErr = errors.New("block time too big")
|
||||
BlockEqualTSErr = errors.New("block time stamp equal to previous")
|
||||
)
|
||||
|
||||
// Parent error. In case a parent is unknown this error will be thrown
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Execution is the execution environment for the given call or create action.
|
||||
type Execution struct {
|
||||
env vm.Environment
|
||||
address *common.Address
|
||||
@ -35,12 +36,15 @@ type Execution struct {
|
||||
Gas, price, value *big.Int
|
||||
}
|
||||
|
||||
// NewExecution returns a new execution environment that handles all calling
|
||||
// and creation logic defined by the YP.
|
||||
func NewExecution(env vm.Environment, address *common.Address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
exe := &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
exe.evm = vm.NewVm(env)
|
||||
return exe
|
||||
}
|
||||
|
||||
// Call executes within the given context
|
||||
func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]byte, error) {
|
||||
// Retrieve the executing code
|
||||
code := self.env.State().GetCode(codeAddr)
|
||||
@ -48,6 +52,9 @@ func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]by
|
||||
return self.exec(&codeAddr, code, caller)
|
||||
}
|
||||
|
||||
// Create creates a new contract and runs the initialisation procedure of the
|
||||
// contract. This returns the returned code for the contract and is stored
|
||||
// elsewhere.
|
||||
func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) {
|
||||
// Input must be nil for create
|
||||
code := self.input
|
||||
@ -63,16 +70,24 @@ func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, acco
|
||||
return
|
||||
}
|
||||
|
||||
// exec executes the given code and executes within the contextAddr context.
|
||||
func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) {
|
||||
env := self.env
|
||||
evm := self.evm
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
|
||||
return nil, vm.DepthError
|
||||
}
|
||||
|
||||
vsnapshot := env.State().Copy()
|
||||
if !env.CanTransfer(env.State().GetStateObject(caller.Address()), self.value) {
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
|
||||
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, env.State().GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
var createAccount bool
|
||||
if self.address == nil {
|
||||
// Generate a new address
|
||||
@ -95,15 +110,7 @@ func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.
|
||||
} else {
|
||||
to = env.State().GetOrNewStateObject(*self.address)
|
||||
}
|
||||
|
||||
err = env.Transfer(from, to, self.value)
|
||||
if err != nil {
|
||||
env.State().Set(vsnapshot)
|
||||
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
|
||||
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance())
|
||||
}
|
||||
vm.Transfer(from, to, self.value)
|
||||
|
||||
context := vm.NewContext(caller, to, self.value, self.Gas, self.price)
|
||||
context.SetCallCode(contextAddr, code)
|
||||
|
@ -22,6 +22,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
type AccountChange struct {
|
||||
@ -111,7 +113,7 @@ done:
|
||||
// Get the logs of the block
|
||||
unfiltered, err := self.eth.BlockProcessor().GetLogs(block)
|
||||
if err != nil {
|
||||
chainlogger.Warnln("err: filter get logs ", err)
|
||||
glog.V(logger.Warn).Infoln("err: filter get logs ", err)
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import (
|
||||
)
|
||||
|
||||
// WriteGenesisBlock writes the genesis block to the database as block number 0
|
||||
func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*types.Block, error) {
|
||||
func WriteGenesisBlock(chainDb common.Database, reader io.Reader) (*types.Block, error) {
|
||||
contents, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -59,7 +59,7 @@ func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*typ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statedb := state.New(common.Hash{}, stateDb)
|
||||
statedb := state.New(common.Hash{}, chainDb)
|
||||
for addr, account := range genesis.Alloc {
|
||||
address := common.HexToAddress(addr)
|
||||
statedb.AddBalance(address, common.String2Big(account.Balance))
|
||||
@ -73,7 +73,7 @@ func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*typ
|
||||
difficulty := common.String2Big(genesis.Difficulty)
|
||||
block := types.NewBlock(&types.Header{
|
||||
Nonce: types.EncodeNonce(common.String2Big(genesis.Nonce).Uint64()),
|
||||
Time: common.String2Big(genesis.Timestamp).Uint64(),
|
||||
Time: common.String2Big(genesis.Timestamp),
|
||||
ParentHash: common.HexToHash(genesis.ParentHash),
|
||||
Extra: common.FromHex(genesis.ExtraData),
|
||||
GasLimit: common.String2Big(genesis.GasLimit),
|
||||
@ -84,9 +84,9 @@ func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*typ
|
||||
}, nil, nil, nil)
|
||||
block.Td = difficulty
|
||||
|
||||
if block := GetBlockByHash(blockDb, block.Hash()); block != nil {
|
||||
if block := GetBlockByHash(chainDb, block.Hash()); block != nil {
|
||||
glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number")
|
||||
err := WriteCanonNumber(blockDb, block)
|
||||
err := WriteCanonNumber(chainDb, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -95,11 +95,11 @@ func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*typ
|
||||
|
||||
statedb.Sync()
|
||||
|
||||
err = WriteBlock(blockDb, block)
|
||||
err = WriteBlock(chainDb, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = WriteHead(blockDb, block)
|
||||
err = WriteHead(chainDb, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,11 +133,11 @@ func WriteGenesisBlockForTesting(db common.Database, addr common.Address, balanc
|
||||
"0x%x":{"balance":"0x%x"}
|
||||
}
|
||||
}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), addr, balance.Bytes())
|
||||
block, _ := WriteGenesisBlock(db, db, strings.NewReader(testGenesis))
|
||||
block, _ := WriteGenesisBlock(db, strings.NewReader(testGenesis))
|
||||
return block
|
||||
}
|
||||
|
||||
func WriteTestNetGenesisBlock(stateDb, blockDb common.Database, nonce uint64) (*types.Block, error) {
|
||||
func WriteTestNetGenesisBlock(chainDb common.Database, nonce uint64) (*types.Block, error) {
|
||||
testGenesis := fmt.Sprintf(`{
|
||||
"nonce":"0x%x",
|
||||
"gasLimit":"0x%x",
|
||||
@ -157,5 +157,5 @@ func WriteTestNetGenesisBlock(stateDb, blockDb common.Database, nonce uint64) (*
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
|
||||
}
|
||||
}`, types.EncodeNonce(nonce), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
|
||||
return WriteGenesisBlock(stateDb, blockDb, strings.NewReader(testGenesis))
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
|
||||
}
|
||||
|
@ -28,8 +28,7 @@ type Backend interface {
|
||||
BlockProcessor() *BlockProcessor
|
||||
ChainManager() *ChainManager
|
||||
TxPool() *TxPool
|
||||
BlockDb() common.Database
|
||||
StateDb() common.Database
|
||||
ExtraDb() common.Database
|
||||
ChainDb() common.Database
|
||||
DappDb() common.Database
|
||||
EventMux() *event.TypeMux
|
||||
}
|
||||
|
@ -82,8 +82,9 @@ type StateObject struct {
|
||||
// Mark for deletion
|
||||
// When an object is marked for deletion it will be delete from the trie
|
||||
// during the "update" phase of the state transition
|
||||
remove bool
|
||||
dirty bool
|
||||
remove bool
|
||||
deleted bool
|
||||
dirty bool
|
||||
}
|
||||
|
||||
func (self *StateObject) Reset() {
|
||||
@ -262,6 +263,7 @@ func (self *StateObject) Copy() *StateObject {
|
||||
stateObject.gasPool.Set(self.gasPool)
|
||||
stateObject.remove = self.remove
|
||||
stateObject.dirty = self.dirty
|
||||
stateObject.deleted = self.deleted
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
@ -117,3 +118,106 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||
|
||||
c.Assert(data1, checker.DeepEquals, res)
|
||||
}
|
||||
|
||||
// use testing instead of checker because checker does not support
|
||||
// printing/logging in tests (-check.vv does not work)
|
||||
func TestSnapshot2(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := New(common.Hash{}, db)
|
||||
|
||||
stateobjaddr0 := toAddr([]byte("so0"))
|
||||
stateobjaddr1 := toAddr([]byte("so1"))
|
||||
var storageaddr common.Hash
|
||||
|
||||
data0 := common.BytesToHash([]byte{17})
|
||||
data1 := common.BytesToHash([]byte{18})
|
||||
|
||||
state.SetState(stateobjaddr0, storageaddr, data0)
|
||||
state.SetState(stateobjaddr1, storageaddr, data1)
|
||||
|
||||
// db, trie are already non-empty values
|
||||
so0 := state.GetStateObject(stateobjaddr0)
|
||||
so0.balance = big.NewInt(42)
|
||||
so0.nonce = 43
|
||||
so0.gasPool = big.NewInt(44)
|
||||
so0.code = []byte{'c', 'a', 'f', 'e'}
|
||||
so0.codeHash = so0.CodeHash()
|
||||
so0.remove = true
|
||||
so0.deleted = false
|
||||
so0.dirty = false
|
||||
state.SetStateObject(so0)
|
||||
|
||||
// and one with deleted == true
|
||||
so1 := state.GetStateObject(stateobjaddr1)
|
||||
so1.balance = big.NewInt(52)
|
||||
so1.nonce = 53
|
||||
so1.gasPool = big.NewInt(54)
|
||||
so1.code = []byte{'c', 'a', 'f', 'e', '2'}
|
||||
so1.codeHash = so1.CodeHash()
|
||||
so1.remove = true
|
||||
so1.deleted = true
|
||||
so1.dirty = true
|
||||
state.SetStateObject(so1)
|
||||
|
||||
so1 = state.GetStateObject(stateobjaddr1)
|
||||
if so1 != nil {
|
||||
t.Fatalf("deleted object not nil when getting")
|
||||
}
|
||||
|
||||
snapshot := state.Copy()
|
||||
state.Set(snapshot)
|
||||
|
||||
so0Restored := state.GetStateObject(stateobjaddr0)
|
||||
so1Restored := state.GetStateObject(stateobjaddr1)
|
||||
// non-deleted is equal (restored)
|
||||
compareStateObjects(so0Restored, so0, t)
|
||||
// deleted should be nil, both before and after restore of state copy
|
||||
if so1Restored != nil {
|
||||
t.Fatalf("deleted object not nil after restoring snapshot")
|
||||
}
|
||||
}
|
||||
|
||||
func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
|
||||
if so0.address != so1.address {
|
||||
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
|
||||
}
|
||||
if so0.balance.Cmp(so1.balance) != 0 {
|
||||
t.Fatalf("Balance mismatch: have %v, want %v", so0.balance, so1.balance)
|
||||
}
|
||||
if so0.nonce != so1.nonce {
|
||||
t.Fatalf("Nonce mismatch: have %v, want %v", so0.nonce, so1.nonce)
|
||||
}
|
||||
if !bytes.Equal(so0.codeHash, so1.codeHash) {
|
||||
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.codeHash, so1.codeHash)
|
||||
}
|
||||
if !bytes.Equal(so0.code, so1.code) {
|
||||
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
|
||||
}
|
||||
if !bytes.Equal(so0.initCode, so1.initCode) {
|
||||
t.Fatalf("InitCode mismatch: have %v, want %v", so0.initCode, so1.initCode)
|
||||
}
|
||||
|
||||
for k, v := range so1.storage {
|
||||
if so0.storage[k] != v {
|
||||
t.Fatalf("Storage key %s mismatch: have %v, want %v", k, so0.storage[k], v)
|
||||
}
|
||||
}
|
||||
for k, v := range so0.storage {
|
||||
if so1.storage[k] != v {
|
||||
t.Fatalf("Storage key %s mismatch: have %v, want none.", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if so0.gasPool.Cmp(so1.gasPool) != 0 {
|
||||
t.Fatalf("GasPool mismatch: have %v, want %v", so0.gasPool, so1.gasPool)
|
||||
}
|
||||
if so0.remove != so1.remove {
|
||||
t.Fatalf("Remove mismatch: have %v, want %v", so0.remove, so1.remove)
|
||||
}
|
||||
if so0.deleted != so1.deleted {
|
||||
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
|
||||
}
|
||||
if so0.dirty != so1.dirty {
|
||||
t.Fatalf("Dirty mismatch: have %v, want %v", so0.dirty, so1.dirty)
|
||||
}
|
||||
}
|
||||
|
@ -203,18 +203,20 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
||||
|
||||
// Delete the given state object and delete it from the state trie
|
||||
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
||||
stateObject.deleted = true
|
||||
|
||||
addr := stateObject.Address()
|
||||
self.trie.Delete(addr[:])
|
||||
|
||||
//delete(self.stateObjects, addr.Str())
|
||||
}
|
||||
|
||||
// Retrieve a state object given my the address. Nil if not found
|
||||
func (self *StateDB) GetStateObject(addr common.Address) *StateObject {
|
||||
//addr = common.Address(addr)
|
||||
|
||||
stateObject := self.stateObjects[addr.Str()]
|
||||
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
||||
stateObject = self.stateObjects[addr.Str()]
|
||||
if stateObject != nil {
|
||||
if stateObject.deleted {
|
||||
stateObject = nil
|
||||
}
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
@ -236,7 +238,7 @@ func (self *StateDB) SetStateObject(object *StateObject) {
|
||||
// Retrieve a state object or create a new state object if nil
|
||||
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject == nil {
|
||||
if stateObject == nil || stateObject.deleted {
|
||||
stateObject = self.CreateAccount(addr)
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ import (
|
||||
* 6) Derive new state root
|
||||
*/
|
||||
type StateTransition struct {
|
||||
coinbase common.Address
|
||||
gp GasPool
|
||||
msg Message
|
||||
gas, gasPrice *big.Int
|
||||
initialGas *big.Int
|
||||
@ -53,8 +53,6 @@ type StateTransition struct {
|
||||
data []byte
|
||||
state *state.StateDB
|
||||
|
||||
cb, rec, sen *state.StateObject
|
||||
|
||||
env vm.Environment
|
||||
}
|
||||
|
||||
@ -96,13 +94,13 @@ func IntrinsicGas(data []byte) *big.Int {
|
||||
return igas
|
||||
}
|
||||
|
||||
func ApplyMessage(env vm.Environment, msg Message, coinbase *state.StateObject) ([]byte, *big.Int, error) {
|
||||
return NewStateTransition(env, msg, coinbase).transitionState()
|
||||
func ApplyMessage(env vm.Environment, msg Message, gp GasPool) ([]byte, *big.Int, error) {
|
||||
return NewStateTransition(env, msg, gp).transitionState()
|
||||
}
|
||||
|
||||
func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateObject) *StateTransition {
|
||||
func NewStateTransition(env vm.Environment, msg Message, gp GasPool) *StateTransition {
|
||||
return &StateTransition{
|
||||
coinbase: coinbase.Address(),
|
||||
gp: gp,
|
||||
env: env,
|
||||
msg: msg,
|
||||
gas: new(big.Int),
|
||||
@ -111,13 +109,9 @@ func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateOb
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: env.State(),
|
||||
cb: coinbase,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateTransition) Coinbase() *state.StateObject {
|
||||
return self.state.GetOrNewStateObject(self.coinbase)
|
||||
}
|
||||
func (self *StateTransition) From() (*state.StateObject, error) {
|
||||
f, err := self.msg.From()
|
||||
if err != nil {
|
||||
@ -160,7 +154,7 @@ func (self *StateTransition) BuyGas() error {
|
||||
if sender.Balance().Cmp(mgval) < 0 {
|
||||
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
|
||||
}
|
||||
if err = self.Coinbase().SubGas(mgas, self.gasPrice); err != nil {
|
||||
if err = self.gp.SubGas(mgas, self.gasPrice); err != nil {
|
||||
return err
|
||||
}
|
||||
self.AddGas(mgas)
|
||||
@ -241,13 +235,12 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
|
||||
}
|
||||
|
||||
self.refundGas()
|
||||
self.state.AddBalance(self.coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
|
||||
return ret, self.gasUsed(), err
|
||||
}
|
||||
|
||||
func (self *StateTransition) refundGas() {
|
||||
coinbase := self.Coinbase()
|
||||
sender, _ := self.From() // err already checked
|
||||
// Return remaining gas
|
||||
remaining := new(big.Int).Mul(self.gas, self.gasPrice)
|
||||
@ -258,7 +251,7 @@ func (self *StateTransition) refundGas() {
|
||||
self.gas.Add(self.gas, refund)
|
||||
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
|
||||
|
||||
coinbase.AddGas(self.gas, self.gasPrice)
|
||||
self.gp.AddGas(self.gas, self.gasPrice)
|
||||
}
|
||||
|
||||
func (self *StateTransition) gasUsed() *big.Int {
|
||||
|
@ -60,7 +60,7 @@ type Header struct {
|
||||
Number *big.Int // The block number
|
||||
GasLimit *big.Int // Gas limit
|
||||
GasUsed *big.Int // Gas used
|
||||
Time uint64 // Creation time
|
||||
Time *big.Int // Creation time
|
||||
Extra []byte // Extra data
|
||||
MixDigest common.Hash // for quick difficulty verification
|
||||
Nonce BlockNonce
|
||||
@ -94,7 +94,7 @@ func (h *Header) UnmarshalJSON(data []byte) error {
|
||||
Coinbase string
|
||||
Difficulty string
|
||||
GasLimit string
|
||||
Time uint64
|
||||
Time *big.Int
|
||||
Extra string
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
@ -210,6 +210,9 @@ func NewBlockWithHeader(header *Header) *Block {
|
||||
|
||||
func copyHeader(h *Header) *Header {
|
||||
cpy := *h
|
||||
if cpy.Time = new(big.Int); h.Time != nil {
|
||||
cpy.Time.Set(h.Time)
|
||||
}
|
||||
if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
|
||||
cpy.Difficulty.Set(h.Difficulty)
|
||||
}
|
||||
@ -301,13 +304,13 @@ func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number)
|
||||
func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) }
|
||||
func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) }
|
||||
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
|
||||
func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) }
|
||||
|
||||
func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
|
||||
func (b *Block) MixDigest() common.Hash { return b.header.MixDigest }
|
||||
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
|
||||
func (b *Block) Bloom() Bloom { return b.header.Bloom }
|
||||
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
|
||||
func (b *Block) Time() uint64 { return b.header.Time }
|
||||
func (b *Block) Root() common.Hash { return b.header.Root }
|
||||
func (b *Block) ParentHash() common.Hash { return b.header.ParentHash }
|
||||
func (b *Block) TxHash() common.Hash { return b.header.TxHash }
|
||||
|
@ -47,7 +47,7 @@ func TestBlockEncoding(t *testing.T) {
|
||||
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
|
||||
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
|
||||
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
|
||||
check("Time", block.Time(), uint64(1426516743))
|
||||
check("Time", block.Time(), big.NewInt(1426516743))
|
||||
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||
|
||||
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
|
||||
|
@ -35,6 +35,7 @@ type Context struct {
|
||||
jumpdests destinations // result of JUMPDEST analysis.
|
||||
|
||||
Code []byte
|
||||
Input []byte
|
||||
CodeAddr *common.Address
|
||||
|
||||
value, Gas, UsedGas, Price *big.Int
|
||||
|
@ -94,7 +94,7 @@ func ecrecoverFunc(in []byte) []byte {
|
||||
v := byte(vbig.Uint64())
|
||||
|
||||
if !crypto.ValidateSignatureValues(v, r, s) {
|
||||
glog.V(logger.Error).Infof("EC RECOVER FAIL: v, r or s value invalid")
|
||||
glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,10 @@ type Environment interface {
|
||||
BlockNumber() *big.Int
|
||||
GetHash(n uint64) common.Hash
|
||||
Coinbase() common.Address
|
||||
Time() uint64
|
||||
Time() *big.Int
|
||||
Difficulty() *big.Int
|
||||
GasLimit() *big.Int
|
||||
CanTransfer(from Account, balance *big.Int) bool
|
||||
Transfer(from, to Account, amount *big.Int) error
|
||||
AddLog(*state.Log)
|
||||
AddStructLog(StructLog)
|
||||
|
@ -54,8 +54,8 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.stackPush > 0 && len(stack.data)-r.stackPop+r.stackPush > int(params.StackLimit.Int64())+1 {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", len(stack.data), params.StackLimit.Int64())
|
||||
if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
}
|
||||
|
||||
gas.Add(gas, r.gas)
|
||||
|
534
core/vm/instructions.go
Normal file
534
core/vm/instructions.go
Normal file
@ -0,0 +1,534 @@
|
||||
// Copyright 2014 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 vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type instrFn func(instr instruction, env Environment, context *Context, memory *Memory, stack *stack)
|
||||
type instrExFn func(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack)
|
||||
|
||||
type instruction struct {
|
||||
op OpCode
|
||||
pc uint64
|
||||
fn instrFn
|
||||
specFn instrExFn
|
||||
data *big.Int
|
||||
|
||||
gas *big.Int
|
||||
spop int
|
||||
spush int
|
||||
}
|
||||
|
||||
func opStaticJump(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
ret.Set(instr.data)
|
||||
}
|
||||
|
||||
func opAdd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Add(x, y)))
|
||||
}
|
||||
|
||||
func opSub(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Sub(x, y)))
|
||||
}
|
||||
|
||||
func opMul(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Mul(x, y)))
|
||||
}
|
||||
|
||||
func opDiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) != 0 {
|
||||
stack.push(U256(x.Div(x, y)))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opSdiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
return
|
||||
} else {
|
||||
n := new(big.Int)
|
||||
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
|
||||
n.SetInt64(-1)
|
||||
} else {
|
||||
n.SetInt64(1)
|
||||
}
|
||||
|
||||
res := x.Div(x.Abs(x), y.Abs(y))
|
||||
res.Mul(res, n)
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
}
|
||||
|
||||
func opMod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(U256(x.Mod(x, y)))
|
||||
}
|
||||
}
|
||||
|
||||
func opSmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
n := new(big.Int)
|
||||
if x.Cmp(common.Big0) < 0 {
|
||||
n.SetInt64(-1)
|
||||
} else {
|
||||
n.SetInt64(1)
|
||||
}
|
||||
|
||||
res := x.Mod(x.Abs(x), y.Abs(y))
|
||||
res.Mul(res, n)
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
}
|
||||
|
||||
func opExp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Exp(x, y, Pow256)))
|
||||
}
|
||||
|
||||
func opSignExtend(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
back := stack.pop()
|
||||
if back.Cmp(big.NewInt(31)) < 0 {
|
||||
bit := uint(back.Uint64()*8 + 7)
|
||||
num := stack.pop()
|
||||
mask := back.Lsh(common.Big1, bit)
|
||||
mask.Sub(mask, common.Big1)
|
||||
if common.BitTest(num, int(bit)) {
|
||||
num.Or(num, mask.Not(mask))
|
||||
} else {
|
||||
num.And(num, mask)
|
||||
}
|
||||
|
||||
stack.push(U256(num))
|
||||
}
|
||||
}
|
||||
|
||||
func opNot(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x := stack.pop()
|
||||
stack.push(U256(x.Not(x)))
|
||||
}
|
||||
|
||||
func opLt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opGt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opSlt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(S256(y)) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opSgt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opEq(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opIszero(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x := stack.pop()
|
||||
if x.Cmp(common.Big0) > 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
}
|
||||
}
|
||||
|
||||
func opAnd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.And(x, y))
|
||||
}
|
||||
func opOr(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Or(x, y))
|
||||
}
|
||||
func opXor(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Xor(x, y))
|
||||
}
|
||||
func opByte(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
th, val := stack.pop(), stack.pop()
|
||||
if th.Cmp(big.NewInt(32)) < 0 {
|
||||
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||
stack.push(byte)
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
func opAddmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
add := x.Add(x, y)
|
||||
add.Mod(add, z)
|
||||
stack.push(U256(add))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
func opMulmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
mul := x.Mul(x, y)
|
||||
mul.Mod(mul, z)
|
||||
stack.push(U256(mul))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opSha3(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64()))
|
||||
|
||||
stack.push(common.BytesToBig(hash))
|
||||
}
|
||||
|
||||
func opAddress(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(common.Bytes2Big(context.Address().Bytes()))
|
||||
}
|
||||
|
||||
func opBalance(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
balance := env.State().GetBalance(addr)
|
||||
|
||||
stack.push(new(big.Int).Set(balance))
|
||||
}
|
||||
|
||||
func opOrigin(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(env.Origin().Big())
|
||||
}
|
||||
|
||||
func opCaller(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(common.Bytes2Big(context.caller.Address().Bytes()))
|
||||
}
|
||||
|
||||
func opCallValue(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(new(big.Int).Set(context.value))
|
||||
}
|
||||
|
||||
func opCalldataLoad(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(common.Bytes2Big(getData(context.Input, stack.pop(), common.Big32)))
|
||||
}
|
||||
|
||||
func opCalldataSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(big.NewInt(int64(len(context.Input))))
|
||||
}
|
||||
|
||||
func opCalldataCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), getData(context.Input, cOff, l))
|
||||
}
|
||||
|
||||
func opExtCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
l := big.NewInt(int64(len(env.State().GetCode(addr))))
|
||||
stack.push(l)
|
||||
}
|
||||
|
||||
func opCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
l := big.NewInt(int64(len(context.Code)))
|
||||
stack.push(l)
|
||||
}
|
||||
|
||||
func opCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
codeCopy := getData(context.Code, cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
}
|
||||
|
||||
func opExtCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
var (
|
||||
addr = common.BigToAddress(stack.pop())
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
codeCopy := getData(env.State().GetCode(addr), cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
}
|
||||
|
||||
func opGasprice(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(new(big.Int).Set(context.Price))
|
||||
}
|
||||
|
||||
func opBlockhash(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
num := stack.pop()
|
||||
|
||||
n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
|
||||
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
|
||||
stack.push(env.GetHash(num.Uint64()).Big())
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opCoinbase(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(env.Coinbase().Big())
|
||||
}
|
||||
|
||||
func opTimestamp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Time())))
|
||||
}
|
||||
|
||||
func opNumber(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.BlockNumber())))
|
||||
}
|
||||
|
||||
func opDifficulty(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Difficulty())))
|
||||
}
|
||||
|
||||
func opGasLimit(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.GasLimit())))
|
||||
}
|
||||
|
||||
func opPop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.pop()
|
||||
}
|
||||
|
||||
func opPush(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
}
|
||||
|
||||
func opDup(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.dup(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opSwap(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.swap(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opLog(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
n := int(instr.data.Int64())
|
||||
topics := make([]common.Hash, n)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < n; i++ {
|
||||
topics[i] = common.BigToHash(stack.pop())
|
||||
}
|
||||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
log := state.NewLog(context.Address(), topics, d, env.BlockNumber().Uint64())
|
||||
env.AddLog(log)
|
||||
}
|
||||
|
||||
func opMload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
offset := stack.pop()
|
||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
||||
stack.push(val)
|
||||
}
|
||||
|
||||
func opMstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
// pop value of the stack
|
||||
mStart, val := stack.pop(), stack.pop()
|
||||
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
|
||||
}
|
||||
|
||||
func opMstore8(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
off, val := stack.pop().Int64(), stack.pop().Int64()
|
||||
memory.store[off] = byte(val & 0xff)
|
||||
}
|
||||
|
||||
func opSload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := env.State().GetState(context.Address(), loc).Big()
|
||||
stack.push(val)
|
||||
}
|
||||
|
||||
func opSstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := stack.pop()
|
||||
|
||||
env.State().SetState(context.Address(), loc, common.BigToHash(val))
|
||||
}
|
||||
|
||||
func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
|
||||
func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
|
||||
func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
|
||||
|
||||
func opPc(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
}
|
||||
|
||||
func opMsize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(big.NewInt(int64(memory.Len())))
|
||||
}
|
||||
|
||||
func opGas(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
stack.push(new(big.Int).Set(context.Gas))
|
||||
}
|
||||
|
||||
func opCreate(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
var (
|
||||
value = stack.pop()
|
||||
offset, size = stack.pop(), stack.pop()
|
||||
input = memory.Get(offset.Int64(), size.Int64())
|
||||
gas = new(big.Int).Set(context.Gas)
|
||||
addr common.Address
|
||||
)
|
||||
|
||||
context.UseGas(context.Gas)
|
||||
ret, suberr, ref := env.Create(context, input, gas, context.Price, value)
|
||||
if suberr != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
||||
} else {
|
||||
// gas < len(ret) * Createinstr.dataGas == NO_CODE
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if context.UseGas(dataGas) {
|
||||
ref.SetCode(ret)
|
||||
}
|
||||
addr = ref.Address()
|
||||
|
||||
stack.push(addr.Big())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func opCall(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
value = U256(value)
|
||||
// pop input size and offset
|
||||
inOffset, inSize := stack.pop(), stack.pop()
|
||||
// pop return size and offset
|
||||
retOffset, retSize := stack.pop(), stack.pop()
|
||||
|
||||
address := common.BigToAddress(addr)
|
||||
|
||||
// Get the arguments from the memory
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if len(value.Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.Call(context, address, args, gas, context.Price, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
}
|
||||
|
||||
func opCallCode(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
value = U256(value)
|
||||
// pop input size and offset
|
||||
inOffset, inSize := stack.pop(), stack.pop()
|
||||
// pop return size and offset
|
||||
retOffset, retSize := stack.pop(), stack.pop()
|
||||
|
||||
address := common.BigToAddress(addr)
|
||||
|
||||
// Get the arguments from the memory
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if len(value.Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.CallCode(context, address, args, gas, context.Price, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
}
|
||||
|
||||
func opReturn(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
|
||||
func opStop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
|
||||
|
||||
func opSuicide(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
|
||||
receiver := env.State().GetOrNewStateObject(common.BigToAddress(stack.pop()))
|
||||
balance := env.State().GetBalance(context.Address())
|
||||
|
||||
receiver.AddBalance(balance)
|
||||
|
||||
env.State().Delete(context.Address())
|
||||
}
|
547
core/vm/jit.go
Normal file
547
core/vm/jit.go
Normal file
@ -0,0 +1,547 @@
|
||||
// Copyright 2014 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 vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
type progStatus int32
|
||||
|
||||
const (
|
||||
progUnknown progStatus = iota
|
||||
progCompile
|
||||
progReady
|
||||
progError
|
||||
)
|
||||
|
||||
var programs *lru.Cache
|
||||
|
||||
func init() {
|
||||
programs, _ = lru.New(defaultJitMaxCache)
|
||||
}
|
||||
|
||||
// SetJITCacheSize recreates the program cache with the max given size. Setting
|
||||
// a new cache is **not** thread safe. Use with caution.
|
||||
func SetJITCacheSize(size int) {
|
||||
programs, _ = lru.New(size)
|
||||
}
|
||||
|
||||
// GetProgram returns the program by id or nil when non-existent
|
||||
func GetProgram(id common.Hash) *Program {
|
||||
if p, ok := programs.Get(id); ok {
|
||||
return p.(*Program)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenProgramStatus returns the status of the given program id
|
||||
func GetProgramStatus(id common.Hash) progStatus {
|
||||
program := GetProgram(id)
|
||||
if program != nil {
|
||||
return progStatus(atomic.LoadInt32(&program.status))
|
||||
}
|
||||
|
||||
return progUnknown
|
||||
}
|
||||
|
||||
// Program is a compiled program for the JIT VM and holds all required for
|
||||
// running a compiled JIT program.
|
||||
type Program struct {
|
||||
Id common.Hash // Id of the program
|
||||
status int32 // status should be accessed atomically
|
||||
|
||||
context *Context
|
||||
|
||||
instructions []instruction // instruction set
|
||||
mapping map[uint64]int // real PC mapping to array indices
|
||||
destinations map[uint64]struct{} // cached jump destinations
|
||||
|
||||
code []byte
|
||||
}
|
||||
|
||||
// NewProgram returns a new JIT program
|
||||
func NewProgram(code []byte) *Program {
|
||||
program := &Program{
|
||||
Id: crypto.Sha3Hash(code),
|
||||
mapping: make(map[uint64]int),
|
||||
destinations: make(map[uint64]struct{}),
|
||||
code: code,
|
||||
}
|
||||
|
||||
programs.Add(program.Id, program)
|
||||
return program
|
||||
}
|
||||
|
||||
func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
|
||||
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
|
||||
// PUSH is also allowed to calculate the same price for all PUSHes
|
||||
// DUP requirements are handled elsewhere (except for the stack limit check)
|
||||
baseOp := op
|
||||
if op >= PUSH1 && op <= PUSH32 {
|
||||
baseOp = PUSH1
|
||||
}
|
||||
if op >= DUP1 && op <= DUP16 {
|
||||
baseOp = DUP1
|
||||
}
|
||||
base := _baseCheck[baseOp]
|
||||
instr := instruction{op, pc, fn, nil, data, base.gas, base.stackPop, base.stackPush}
|
||||
|
||||
p.instructions = append(p.instructions, instr)
|
||||
p.mapping[pc] = len(p.instructions) - 1
|
||||
}
|
||||
|
||||
// CompileProgram compiles the given program and return an error when it fails
|
||||
func CompileProgram(program *Program) (err error) {
|
||||
if progStatus(atomic.LoadInt32(&program.status)) == progCompile {
|
||||
return nil
|
||||
}
|
||||
atomic.StoreInt32(&program.status, int32(progCompile))
|
||||
defer func() {
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&program.status, int32(progError))
|
||||
} else {
|
||||
atomic.StoreInt32(&program.status, int32(progReady))
|
||||
}
|
||||
}()
|
||||
|
||||
// loop thru the opcodes and "compile" in to instructions
|
||||
for pc := uint64(0); pc < uint64(len(program.code)); pc++ {
|
||||
switch op := OpCode(program.code[pc]); op {
|
||||
case ADD:
|
||||
program.addInstr(op, pc, opAdd, nil)
|
||||
case SUB:
|
||||
program.addInstr(op, pc, opSub, nil)
|
||||
case MUL:
|
||||
program.addInstr(op, pc, opMul, nil)
|
||||
case DIV:
|
||||
program.addInstr(op, pc, opDiv, nil)
|
||||
case SDIV:
|
||||
program.addInstr(op, pc, opSdiv, nil)
|
||||
case MOD:
|
||||
program.addInstr(op, pc, opMod, nil)
|
||||
case SMOD:
|
||||
program.addInstr(op, pc, opSmod, nil)
|
||||
case EXP:
|
||||
program.addInstr(op, pc, opExp, nil)
|
||||
case SIGNEXTEND:
|
||||
program.addInstr(op, pc, opSignExtend, nil)
|
||||
case NOT:
|
||||
program.addInstr(op, pc, opNot, nil)
|
||||
case LT:
|
||||
program.addInstr(op, pc, opLt, nil)
|
||||
case GT:
|
||||
program.addInstr(op, pc, opGt, nil)
|
||||
case SLT:
|
||||
program.addInstr(op, pc, opSlt, nil)
|
||||
case SGT:
|
||||
program.addInstr(op, pc, opSgt, nil)
|
||||
case EQ:
|
||||
program.addInstr(op, pc, opEq, nil)
|
||||
case ISZERO:
|
||||
program.addInstr(op, pc, opIszero, nil)
|
||||
case AND:
|
||||
program.addInstr(op, pc, opAnd, nil)
|
||||
case OR:
|
||||
program.addInstr(op, pc, opOr, nil)
|
||||
case XOR:
|
||||
program.addInstr(op, pc, opXor, nil)
|
||||
case BYTE:
|
||||
program.addInstr(op, pc, opByte, nil)
|
||||
case ADDMOD:
|
||||
program.addInstr(op, pc, opAddmod, nil)
|
||||
case MULMOD:
|
||||
program.addInstr(op, pc, opMulmod, nil)
|
||||
case SHA3:
|
||||
program.addInstr(op, pc, opSha3, nil)
|
||||
case ADDRESS:
|
||||
program.addInstr(op, pc, opAddress, nil)
|
||||
case BALANCE:
|
||||
program.addInstr(op, pc, opBalance, nil)
|
||||
case ORIGIN:
|
||||
program.addInstr(op, pc, opOrigin, nil)
|
||||
case CALLER:
|
||||
program.addInstr(op, pc, opCaller, nil)
|
||||
case CALLVALUE:
|
||||
program.addInstr(op, pc, opCallValue, nil)
|
||||
case CALLDATALOAD:
|
||||
program.addInstr(op, pc, opCalldataLoad, nil)
|
||||
case CALLDATASIZE:
|
||||
program.addInstr(op, pc, opCalldataSize, nil)
|
||||
case CALLDATACOPY:
|
||||
program.addInstr(op, pc, opCalldataCopy, nil)
|
||||
case CODESIZE:
|
||||
program.addInstr(op, pc, opCodeSize, nil)
|
||||
case EXTCODESIZE:
|
||||
program.addInstr(op, pc, opExtCodeSize, nil)
|
||||
case CODECOPY:
|
||||
program.addInstr(op, pc, opCodeCopy, nil)
|
||||
case EXTCODECOPY:
|
||||
program.addInstr(op, pc, opExtCodeCopy, nil)
|
||||
case GASPRICE:
|
||||
program.addInstr(op, pc, opGasprice, nil)
|
||||
case BLOCKHASH:
|
||||
program.addInstr(op, pc, opBlockhash, nil)
|
||||
case COINBASE:
|
||||
program.addInstr(op, pc, opCoinbase, nil)
|
||||
case TIMESTAMP:
|
||||
program.addInstr(op, pc, opTimestamp, nil)
|
||||
case NUMBER:
|
||||
program.addInstr(op, pc, opNumber, nil)
|
||||
case DIFFICULTY:
|
||||
program.addInstr(op, pc, opDifficulty, nil)
|
||||
case GASLIMIT:
|
||||
program.addInstr(op, pc, opGasLimit, nil)
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
size := uint64(op - PUSH1 + 1)
|
||||
bytes := getData([]byte(program.code), new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
|
||||
|
||||
program.addInstr(op, pc, opPush, common.Bytes2Big(bytes))
|
||||
|
||||
pc += size
|
||||
|
||||
case POP:
|
||||
program.addInstr(op, pc, opPop, nil)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
program.addInstr(op, pc, opDup, big.NewInt(int64(op-DUP1+1)))
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
program.addInstr(op, pc, opSwap, big.NewInt(int64(op-SWAP1+2)))
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
program.addInstr(op, pc, opLog, big.NewInt(int64(op-LOG0)))
|
||||
case MLOAD:
|
||||
program.addInstr(op, pc, opMload, nil)
|
||||
case MSTORE:
|
||||
program.addInstr(op, pc, opMstore, nil)
|
||||
case MSTORE8:
|
||||
program.addInstr(op, pc, opMstore8, nil)
|
||||
case SLOAD:
|
||||
program.addInstr(op, pc, opSload, nil)
|
||||
case SSTORE:
|
||||
program.addInstr(op, pc, opSstore, nil)
|
||||
case JUMP:
|
||||
program.addInstr(op, pc, opJump, nil)
|
||||
case JUMPI:
|
||||
program.addInstr(op, pc, opJumpi, nil)
|
||||
case JUMPDEST:
|
||||
program.addInstr(op, pc, opJumpdest, nil)
|
||||
program.destinations[pc] = struct{}{}
|
||||
case PC:
|
||||
program.addInstr(op, pc, opPc, big.NewInt(int64(pc)))
|
||||
case MSIZE:
|
||||
program.addInstr(op, pc, opMsize, nil)
|
||||
case GAS:
|
||||
program.addInstr(op, pc, opGas, nil)
|
||||
case CREATE:
|
||||
program.addInstr(op, pc, opCreate, nil)
|
||||
case CALL:
|
||||
program.addInstr(op, pc, opCall, nil)
|
||||
case CALLCODE:
|
||||
program.addInstr(op, pc, opCallCode, nil)
|
||||
case RETURN:
|
||||
program.addInstr(op, pc, opReturn, nil)
|
||||
case SUICIDE:
|
||||
program.addInstr(op, pc, opSuicide, nil)
|
||||
case STOP: // Stop the context
|
||||
program.addInstr(op, pc, opStop, nil)
|
||||
default:
|
||||
program.addInstr(op, pc, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunProgram runs the program given the enviroment and context and returns an
|
||||
// error if the execution failed (non-consensus)
|
||||
func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) {
|
||||
return runProgram(program, 0, NewMemory(), newstack(), env, context, input)
|
||||
}
|
||||
|
||||
func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, context *Context, input []byte) ([]byte, error) {
|
||||
context.Input = input
|
||||
|
||||
var (
|
||||
caller = context.caller
|
||||
statedb = env.State()
|
||||
pc int = program.mapping[pcstart]
|
||||
|
||||
jump = func(to *big.Int) error {
|
||||
if !validDest(program.destinations, to) {
|
||||
nop := context.GetOp(to.Uint64())
|
||||
return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
|
||||
}
|
||||
|
||||
pc = program.mapping[to.Uint64()]
|
||||
|
||||
return nil
|
||||
}
|
||||
)
|
||||
|
||||
for pc < len(program.instructions) {
|
||||
instr := program.instructions[pc]
|
||||
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err := jitCalculateGasAndSize(env, context, caller, instr, statedb, mem, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !context.UseGas(cost) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
// Resize the memory calculated previously
|
||||
mem.Resize(newMemSize.Uint64())
|
||||
|
||||
// These opcodes return an argument and are thefor handled
|
||||
// differently from the rest of the opcodes
|
||||
switch instr.op {
|
||||
case JUMP:
|
||||
if err := jump(stack.pop()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
case JUMPI:
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
|
||||
if cond.Cmp(common.BigTrue) >= 0 {
|
||||
if err := jump(pos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
case RETURN:
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
ret := mem.GetPtr(offset.Int64(), size.Int64())
|
||||
|
||||
return context.Return(ret), nil
|
||||
case SUICIDE:
|
||||
instr.fn(instr, env, context, mem, stack)
|
||||
|
||||
return context.Return(nil), nil
|
||||
case STOP:
|
||||
return context.Return(nil), nil
|
||||
default:
|
||||
if instr.fn == nil {
|
||||
return nil, fmt.Errorf("Invalid opcode %x", instr.op)
|
||||
}
|
||||
|
||||
instr.fn(instr, env, context, mem, stack)
|
||||
}
|
||||
|
||||
pc++
|
||||
}
|
||||
|
||||
context.Input = nil
|
||||
|
||||
return context.Return(nil), nil
|
||||
}
|
||||
|
||||
// validDest checks if the given distination is a valid one given the
|
||||
// destination table of the program
|
||||
func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
|
||||
// PC cannot go beyond len(code) and certainly can't be bigger than 64bits.
|
||||
// Don't bother checking for JUMPDEST in that case.
|
||||
if dest.Cmp(bigMaxUint64) > 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := dests[dest.Uint64()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
||||
// the operation. This does not reduce gas or resizes the memory.
|
||||
func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef, instr instruction, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
)
|
||||
err := jitBaseCheck(instr, stack, gas)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// stack Check, memory resize & gas phase
|
||||
switch op := instr.op; op {
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
n := int(op - SWAP1 + 2)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
n := int(op - DUP1 + 1)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
n := int(op - LOG0)
|
||||
err := stack.require(n + 2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
|
||||
add := new(big.Int)
|
||||
gas.Add(gas, params.LogGas)
|
||||
gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||
gas.Add(gas, add.Mul(mSize, params.LogDataGas))
|
||||
|
||||
newMemSize = calcMemSize(mStart, mSize)
|
||||
case EXP:
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
||||
case SSTORE:
|
||||
err := stack.require(2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := statedb.GetState(context.Address(), common.BigToHash(x))
|
||||
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||
// 3. From a nen-zero to a non-zero (CHANGE)
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
// 0 => non 0
|
||||
g = params.SstoreSetGas
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
statedb.Refund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
} else {
|
||||
// non 0 => non 0 (or 0 => 0)
|
||||
g = params.SstoreClearGas
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !statedb.IsDeleted(context.Address()) {
|
||||
statedb.Refund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
case MSTORE8:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
||||
case MSTORE:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
case RETURN:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
case SHA3:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-2])
|
||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
case CALLDATACOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
case CODECOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
case EXTCODECOPY:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-4])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
case CREATE:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
case CALL, CALLCODE:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
if op == CALL {
|
||||
if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
}
|
||||
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSize.Mul(newMemSizeWords, u256(32))
|
||||
|
||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||
// be careful reusing variables here when changing.
|
||||
// The order has been optimised to reduce allocation
|
||||
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
||||
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
||||
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
|
||||
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||
|
||||
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
||||
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
|
||||
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
|
||||
newTotalFee := linCoef.Add(linCoef, quadCoef)
|
||||
|
||||
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
|
||||
gas.Add(gas, fee)
|
||||
}
|
||||
}
|
||||
|
||||
return newMemSize, gas, nil
|
||||
}
|
||||
|
||||
// jitBaseCheck is the same as baseCheck except it doesn't do the look up in the
|
||||
// gas table. This is done during compilation instead.
|
||||
func jitBaseCheck(instr instruction, stack *stack, gas *big.Int) error {
|
||||
err := stack.require(instr.spop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(params.StackLimit.Int64()) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
}
|
||||
|
||||
// nil on gas means no base calculation
|
||||
if instr.gas == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gas.Add(gas, instr.gas)
|
||||
|
||||
return nil
|
||||
}
|
122
core/vm/jit_test.go
Normal file
122
core/vm/jit_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2014 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 vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
const maxRun = 1000
|
||||
|
||||
type vmBench struct {
|
||||
precompile bool // compile prior to executing
|
||||
nojit bool // ignore jit (sets DisbaleJit = true
|
||||
forcejit bool // forces the jit, precompile is ignored
|
||||
|
||||
code []byte
|
||||
input []byte
|
||||
}
|
||||
|
||||
func runVmBench(test vmBench, b *testing.B) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
sender := state.NewStateObject(common.Address{}, db)
|
||||
|
||||
if test.precompile && !test.forcejit {
|
||||
NewProgram(test.code)
|
||||
}
|
||||
env := NewEnv()
|
||||
|
||||
EnableJit = !test.nojit
|
||||
ForceJit = test.forcejit
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
context := NewContext(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
context.Code = test.code
|
||||
context.CodeAddr = &common.Address{}
|
||||
_, err := New(env).Run(context, test.input)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var benchmarks = map[string]vmBench{
|
||||
"pushes": vmBench{
|
||||
false, false, false,
|
||||
common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkPushes(b *testing.B) {
|
||||
runVmBench(benchmarks["pushes"], b)
|
||||
}
|
||||
|
||||
type Env struct {
|
||||
gasLimit *big.Int
|
||||
depth int
|
||||
}
|
||||
|
||||
func NewEnv() *Env {
|
||||
return &Env{big.NewInt(10000), 0}
|
||||
}
|
||||
|
||||
func (self *Env) Origin() common.Address { return common.Address{} }
|
||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
||||
func (self *Env) AddStructLog(log StructLog) {
|
||||
}
|
||||
func (self *Env) StructLogs() []StructLog {
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
|
||||
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
|
||||
func (self *Env) State() *state.StateDB { return nil }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) VmType() Type { return StdVmTy }
|
||||
func (self *Env) GetHash(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String())))
|
||||
}
|
||||
func (self *Env) AddLog(log *state.Log) {
|
||||
}
|
||||
func (self *Env) Depth() int { return self.depth }
|
||||
func (self *Env) SetDepth(i int) { self.depth = i }
|
||||
func (self *Env) CanTransfer(from Account, balance *big.Int) bool {
|
||||
return from.Balance().Cmp(balance) >= 0
|
||||
}
|
||||
func (self *Env) Transfer(from, to Account, amount *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
func (self *Env) Call(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) CallCode(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) Create(caller ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) {
|
||||
return nil, nil, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// Copyright 2014 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
|
||||
@ -14,16 +14,12 @@
|
||||
// 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 !linux,!darwin
|
||||
package vm
|
||||
|
||||
package fdtrack
|
||||
var (
|
||||
EnableJit bool // Enables the JIT VM
|
||||
ForceJit bool // Force the JIT, skip byte VM
|
||||
MaxProgSize int // Max cache size for JIT Programs
|
||||
)
|
||||
|
||||
import "errors"
|
||||
|
||||
func fdlimit() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func fdusage() (int, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
const defaultJitMaxCache int = 64
|
@ -21,38 +21,36 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// stack is an object for basic stack operations. Items popped to the stack are
|
||||
// expected to be changed and modified. stack does not take care of adding newly
|
||||
// initialised objects.
|
||||
type stack struct {
|
||||
data []*big.Int
|
||||
}
|
||||
|
||||
func newstack() *stack {
|
||||
return &stack{}
|
||||
}
|
||||
|
||||
type stack struct {
|
||||
data []*big.Int
|
||||
ptr int
|
||||
}
|
||||
|
||||
func (st *stack) Data() []*big.Int {
|
||||
return st.data[:st.ptr]
|
||||
return st.data
|
||||
}
|
||||
|
||||
func (st *stack) push(d *big.Int) {
|
||||
// NOTE push limit (1024) is checked in baseCheck
|
||||
stackItem := new(big.Int).Set(d)
|
||||
if len(st.data) > st.ptr {
|
||||
st.data[st.ptr] = stackItem
|
||||
} else {
|
||||
st.data = append(st.data, stackItem)
|
||||
}
|
||||
st.ptr++
|
||||
//stackItem := new(big.Int).Set(d)
|
||||
//st.data = append(st.data, stackItem)
|
||||
st.data = append(st.data, d)
|
||||
}
|
||||
|
||||
func (st *stack) pop() (ret *big.Int) {
|
||||
st.ptr--
|
||||
ret = st.data[st.ptr]
|
||||
ret = st.data[len(st.data)-1]
|
||||
st.data = st.data[:len(st.data)-1]
|
||||
return
|
||||
}
|
||||
|
||||
func (st *stack) len() int {
|
||||
return st.ptr
|
||||
return len(st.data)
|
||||
}
|
||||
|
||||
func (st *stack) swap(n int) {
|
||||
@ -60,7 +58,7 @@ func (st *stack) swap(n int) {
|
||||
}
|
||||
|
||||
func (st *stack) dup(n int) {
|
||||
st.push(st.data[st.len()-n])
|
||||
st.push(new(big.Int).Set(st.data[st.len()-n]))
|
||||
}
|
||||
|
||||
func (st *stack) peek() *big.Int {
|
||||
|
127
core/vm/vm.go
127
core/vm/vm.go
@ -24,30 +24,19 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Vm implements VirtualMachine
|
||||
type Vm struct {
|
||||
env Environment
|
||||
|
||||
err error
|
||||
// For logging
|
||||
debug bool
|
||||
|
||||
BreakPoints []int64
|
||||
Stepping bool
|
||||
Fn string
|
||||
|
||||
Recoverable bool
|
||||
|
||||
// Will be called before the vm returns
|
||||
After func(*Context, error)
|
||||
}
|
||||
|
||||
// New returns a new Virtual Machine
|
||||
func New(env Environment) *Vm {
|
||||
return &Vm{env: env, debug: Debug, Recoverable: true}
|
||||
return &Vm{env: env}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data
|
||||
@ -55,17 +44,67 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
self.env.SetDepth(self.env.Depth() + 1)
|
||||
defer self.env.SetDepth(self.env.Depth() - 1)
|
||||
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
|
||||
context.UseGas(context.Gas)
|
||||
|
||||
ret = context.Return(nil)
|
||||
}
|
||||
}()
|
||||
|
||||
if context.CodeAddr != nil {
|
||||
if p := Precompiled[context.CodeAddr.Str()]; p != nil {
|
||||
return self.RunPrecompiled(p, input, context)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching
|
||||
program *Program
|
||||
)
|
||||
if EnableJit {
|
||||
// Fetch program status.
|
||||
// * If ready run using JIT
|
||||
// * If unknown, compile in a seperate goroutine
|
||||
// * If forced wait for compilation and run once done
|
||||
if status := GetProgramStatus(codehash); status == progReady {
|
||||
return RunProgram(GetProgram(codehash), self.env, context, input)
|
||||
} else if status == progUnknown {
|
||||
if ForceJit {
|
||||
// Create and compile program
|
||||
program = NewProgram(context.Code)
|
||||
perr := CompileProgram(program)
|
||||
if perr == nil {
|
||||
return RunProgram(program, self.env, context, input)
|
||||
}
|
||||
glog.V(logger.Info).Infoln("error compiling program", err)
|
||||
} else {
|
||||
// create and compile the program. Compilation
|
||||
// is done in a seperate goroutine
|
||||
program = NewProgram(context.Code)
|
||||
go func() {
|
||||
err := CompileProgram(program)
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infoln("error compiling program", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
caller = context.caller
|
||||
code = context.Code
|
||||
value = context.value
|
||||
price = context.Price
|
||||
|
||||
op OpCode // current opcode
|
||||
codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
statedb = self.env.State() // current state
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
statedb = self.env.State() // current state
|
||||
// For optimisation reason we're using uint64 as the program counter.
|
||||
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
|
||||
pc = uint64(0) // program counter
|
||||
@ -89,32 +128,25 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if self.After != nil {
|
||||
self.After(context, err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
self.log(pc, op, context.Gas, cost, mem, stack, context, err)
|
||||
|
||||
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
|
||||
context.UseGas(context.Gas)
|
||||
|
||||
ret = context.Return(nil)
|
||||
}
|
||||
}()
|
||||
|
||||
if context.CodeAddr != nil {
|
||||
if p := Precompiled[context.CodeAddr.Str()]; p != nil {
|
||||
return self.RunPrecompiled(p, input, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't bother with the execution if there's no code.
|
||||
if len(code) == 0 {
|
||||
return context.Return(nil), nil
|
||||
}
|
||||
|
||||
for {
|
||||
// Overhead of the atomic read might not be worth it
|
||||
/* TODO this still causes a few issues in the tests
|
||||
if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
|
||||
// move execution
|
||||
glog.V(logger.Info).Infoln("Moved execution to JIT")
|
||||
return runProgram(program, pc, mem, stack, self.env, context, input)
|
||||
}
|
||||
*/
|
||||
// The base for all big integer arithmetic
|
||||
base := new(big.Int)
|
||||
|
||||
@ -122,7 +154,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
op = context.GetOp(pc)
|
||||
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
||||
newMemSize, cost, err = calculateGasAndSize(self.env, context, caller, op, statedb, mem, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -130,11 +162,9 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !context.UseGas(cost) {
|
||||
|
||||
context.UseGas(context.Gas)
|
||||
|
||||
return context.Return(nil), OutOfGasError
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
|
||||
// Resize the memory calculated previously
|
||||
mem.Resize(newMemSize.Uint64())
|
||||
// Add a log message
|
||||
@ -376,7 +406,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
balance := statedb.GetBalance(addr)
|
||||
|
||||
stack.push(balance)
|
||||
stack.push(new(big.Int).Set(balance))
|
||||
|
||||
case ORIGIN:
|
||||
origin := self.env.Origin()
|
||||
@ -388,7 +418,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
stack.push(common.Bytes2Big(caller.Bytes()))
|
||||
|
||||
case CALLVALUE:
|
||||
stack.push(value)
|
||||
stack.push(new(big.Int).Set(value))
|
||||
|
||||
case CALLDATALOAD:
|
||||
data := getData(input, stack.pop(), common.Big32)
|
||||
@ -441,7 +471,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
mem.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
|
||||
case GASPRICE:
|
||||
stack.push(context.Price)
|
||||
stack.push(new(big.Int).Set(context.Price))
|
||||
|
||||
case BLOCKHASH:
|
||||
num := stack.pop()
|
||||
@ -461,7 +491,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
case TIMESTAMP:
|
||||
time := self.env.Time()
|
||||
|
||||
stack.push(new(big.Int).SetUint64(time))
|
||||
stack.push(new(big.Int).Set(time))
|
||||
|
||||
case NUMBER:
|
||||
number := self.env.BlockNumber()
|
||||
@ -471,11 +501,11 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
case DIFFICULTY:
|
||||
difficulty := self.env.Difficulty()
|
||||
|
||||
stack.push(difficulty)
|
||||
stack.push(new(big.Int).Set(difficulty))
|
||||
|
||||
case GASLIMIT:
|
||||
|
||||
stack.push(self.env.GasLimit())
|
||||
stack.push(new(big.Int).Set(self.env.GasLimit()))
|
||||
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
size := uint64(op - PUSH1 + 1)
|
||||
@ -555,8 +585,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
case MSIZE:
|
||||
stack.push(big.NewInt(int64(mem.Len())))
|
||||
case GAS:
|
||||
stack.push(context.Gas)
|
||||
|
||||
stack.push(new(big.Int).Set(context.Gas))
|
||||
case CREATE:
|
||||
|
||||
var (
|
||||
@ -652,7 +681,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||
|
||||
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
||||
// the operation. This does not reduce gas or resizes the memory.
|
||||
func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
||||
func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
@ -759,7 +788,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
if op == CALL {
|
||||
if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
||||
if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *type
|
||||
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
|
||||
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
|
||||
func (self *VMEnv) Time() uint64 { return self.header.Time }
|
||||
func (self *VMEnv) Time() *big.Int { return self.header.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
@ -69,6 +69,10 @@ func (self *VMEnv) GetHash(n uint64) common.Hash {
|
||||
func (self *VMEnv) AddLog(log *state.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *VMEnv) CanTransfer(from vm.Account, balance *big.Int) bool {
|
||||
return from.Balance().Cmp(balance) >= 0
|
||||
}
|
||||
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
return vm.Transfer(from, to, amount)
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
// Copyright 2014 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 crypto
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
)
|
||||
|
||||
type KeyPair struct {
|
||||
PrivateKey []byte
|
||||
PublicKey []byte
|
||||
address []byte
|
||||
mnemonic string
|
||||
// The associated account
|
||||
// account *StateObject
|
||||
}
|
||||
|
||||
func GenerateNewKeyPair() *KeyPair {
|
||||
_, prv := secp256k1.GenerateKeyPair()
|
||||
keyPair, _ := NewKeyPairFromSec(prv) // swallow error, this one cannot err
|
||||
return keyPair
|
||||
}
|
||||
|
||||
func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) {
|
||||
pubkey, err := secp256k1.GeneratePubKey(seckey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &KeyPair{PrivateKey: seckey, PublicKey: pubkey}, nil
|
||||
}
|
||||
|
||||
func (k *KeyPair) Address() []byte {
|
||||
if k.address == nil {
|
||||
k.address = Sha3(k.PublicKey[1:])[12:]
|
||||
}
|
||||
return k.address
|
||||
}
|
||||
|
||||
func (k *KeyPair) Mnemonic() string {
|
||||
if k.mnemonic == "" {
|
||||
k.mnemonic = strings.Join(MnemonicEncode(common.Bytes2Hex(k.PrivateKey)), " ")
|
||||
}
|
||||
return k.mnemonic
|
||||
}
|
||||
|
||||
func (k *KeyPair) AsStrings() (string, string, string, string) {
|
||||
return k.Mnemonic(), common.Bytes2Hex(k.Address()), common.Bytes2Hex(k.PrivateKey), common.Bytes2Hex(k.PublicKey)
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// Copyright 2014 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 crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TODO: See if we can refactor this into a shared util lib if we need it multiple times
|
||||
func IndexOf(slice []string, value string) int64 {
|
||||
for p, v := range slice {
|
||||
if v == value {
|
||||
return int64(p)
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func MnemonicEncode(message string) []string {
|
||||
var out []string
|
||||
n := int64(len(MnemonicWords))
|
||||
|
||||
for i := 0; i < len(message); i += (len(message) / 8) {
|
||||
x := message[i : i+8]
|
||||
bit, _ := strconv.ParseInt(x, 16, 64)
|
||||
w1 := (bit % n)
|
||||
w2 := ((bit / n) + w1) % n
|
||||
w3 := ((bit / n / n) + w2) % n
|
||||
out = append(out, MnemonicWords[w1], MnemonicWords[w2], MnemonicWords[w3])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func MnemonicDecode(wordsar []string) string {
|
||||
var out string
|
||||
n := int64(len(MnemonicWords))
|
||||
|
||||
for i := 0; i < len(wordsar); i += 3 {
|
||||
word1 := wordsar[i]
|
||||
word2 := wordsar[i+1]
|
||||
word3 := wordsar[i+2]
|
||||
w1 := IndexOf(MnemonicWords, word1)
|
||||
w2 := IndexOf(MnemonicWords, word2)
|
||||
w3 := IndexOf(MnemonicWords, word3)
|
||||
|
||||
y := (w2 - w1) % n
|
||||
z := (w3 - w2) % n
|
||||
|
||||
// Golang handles modulo with negative numbers different then most languages
|
||||
// The modulo can be negative, we don't want that.
|
||||
if z < 0 {
|
||||
z += n
|
||||
}
|
||||
if y < 0 {
|
||||
y += n
|
||||
}
|
||||
x := w1 + n*(y) + n*n*(z)
|
||||
out += fmt.Sprintf("%08x", x)
|
||||
}
|
||||
return out
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
// Copyright 2014 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 crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMnDecode(t *testing.T) {
|
||||
words := []string{
|
||||
"ink",
|
||||
"balance",
|
||||
"gain",
|
||||
"fear",
|
||||
"happen",
|
||||
"melt",
|
||||
"mom",
|
||||
"surface",
|
||||
"stir",
|
||||
"bottle",
|
||||
"unseen",
|
||||
"expression",
|
||||
"important",
|
||||
"curl",
|
||||
"grant",
|
||||
"fairy",
|
||||
"across",
|
||||
"back",
|
||||
"figure",
|
||||
"breast",
|
||||
"nobody",
|
||||
"scratch",
|
||||
"worry",
|
||||
"yesterday",
|
||||
}
|
||||
encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
|
||||
result := MnemonicDecode(words)
|
||||
if encode != result {
|
||||
t.Error("We expected", encode, "got", result, "instead")
|
||||
}
|
||||
}
|
||||
func TestMnEncode(t *testing.T) {
|
||||
encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
|
||||
result := []string{
|
||||
"ink",
|
||||
"balance",
|
||||
"gain",
|
||||
"fear",
|
||||
"happen",
|
||||
"melt",
|
||||
"mom",
|
||||
"surface",
|
||||
"stir",
|
||||
"bottle",
|
||||
"unseen",
|
||||
"expression",
|
||||
"important",
|
||||
"curl",
|
||||
"grant",
|
||||
"fairy",
|
||||
"across",
|
||||
"back",
|
||||
"figure",
|
||||
"breast",
|
||||
"nobody",
|
||||
"scratch",
|
||||
"worry",
|
||||
"yesterday",
|
||||
}
|
||||
words := MnemonicEncode(encode)
|
||||
for i, word := range words {
|
||||
if word != result[i] {
|
||||
t.Error("Mnenonic does not match:", words, result)
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
212
eth/backend.go
212
eth/backend.go
@ -45,7 +45,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
@ -62,10 +61,11 @@ var (
|
||||
|
||||
defaultBootNodes = []*discover.Node{
|
||||
// ETH/DEV Go Bootnodes
|
||||
discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"),
|
||||
discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"),
|
||||
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-ethereum (poc-9.ethdev.com)
|
||||
discover.MustParseNode("enode://487611428e6c99a11a9795a6abe7b529e81315ca6aad66e2a2fc76e3adf263faba0d35466c2f8f68d561dbefa8878d4df5f1f2ddb1fbeab7f42ffb8cd328bd4a@5.1.83.226:30303"),
|
||||
discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
|
||||
}
|
||||
|
||||
staticNodes = "static-nodes.json" // Path within <datadir> to search for the static node list
|
||||
@ -92,6 +92,7 @@ type Config struct {
|
||||
NatSpec bool
|
||||
AutoDAG bool
|
||||
PowTest bool
|
||||
ExtraData []byte
|
||||
|
||||
MaxPeers int
|
||||
MaxPendingPeers int
|
||||
@ -206,9 +207,8 @@ type Ethereum struct {
|
||||
shutdownChan chan bool
|
||||
|
||||
// DB interfaces
|
||||
blockDb common.Database // Block chain database
|
||||
stateDb common.Database // State changes database
|
||||
extraDb common.Database // Extra database (txs, etc)
|
||||
chainDb common.Database // Block chain databe
|
||||
dappDb common.Database // Dapp database
|
||||
|
||||
// Closed when databases are flushed and closed
|
||||
databasesClosed chan bool
|
||||
@ -266,27 +266,27 @@ func New(config *Config) (*Ethereum, error) {
|
||||
if newdb == nil {
|
||||
newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
|
||||
}
|
||||
blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain"))
|
||||
|
||||
// attempt to merge database together, upgrading from an old version
|
||||
if err := mergeDatabases(config.DataDir, newdb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("blockchain db err: %v", err)
|
||||
}
|
||||
if db, ok := blockDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/block/")
|
||||
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/chaindata/")
|
||||
}
|
||||
stateDb, err := newdb(filepath.Join(config.DataDir, "state"))
|
||||
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("state db err: %v", err)
|
||||
return nil, fmt.Errorf("dapp db err: %v", err)
|
||||
}
|
||||
if db, ok := stateDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/state/")
|
||||
}
|
||||
extraDb, err := newdb(filepath.Join(config.DataDir, "extra"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extra db err: %v", err)
|
||||
}
|
||||
if db, ok := extraDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/extra/")
|
||||
if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/dapp/")
|
||||
}
|
||||
|
||||
nodeDb := filepath.Join(config.DataDir, "nodes")
|
||||
glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
|
||||
|
||||
@ -296,7 +296,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := core.WriteGenesisBlock(stateDb, blockDb, fr)
|
||||
block, err := core.WriteGenesisBlock(chainDb, fr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -304,7 +304,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||
}
|
||||
|
||||
if config.Olympic {
|
||||
_, err := core.WriteTestNetGenesisBlock(stateDb, blockDb, 42)
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb, 42)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -313,26 +313,25 @@ func New(config *Config) (*Ethereum, error) {
|
||||
|
||||
// This is for testing only.
|
||||
if config.GenesisBlock != nil {
|
||||
core.WriteBlock(blockDb, config.GenesisBlock)
|
||||
core.WriteHead(blockDb, config.GenesisBlock)
|
||||
core.WriteBlock(chainDb, config.GenesisBlock)
|
||||
core.WriteHead(chainDb, config.GenesisBlock)
|
||||
}
|
||||
|
||||
if !config.SkipBcVersionCheck {
|
||||
b, _ := blockDb.Get([]byte("BlockchainVersion"))
|
||||
b, _ := chainDb.Get([]byte("BlockchainVersion"))
|
||||
bcVersion := int(common.NewValue(b).Uint())
|
||||
if bcVersion != config.BlockChainVersion && bcVersion != 0 {
|
||||
return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, config.BlockChainVersion)
|
||||
}
|
||||
saveBlockchainVersion(blockDb, config.BlockChainVersion)
|
||||
saveBlockchainVersion(chainDb, config.BlockChainVersion)
|
||||
}
|
||||
glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion)
|
||||
|
||||
eth := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
databasesClosed: make(chan bool),
|
||||
blockDb: blockDb,
|
||||
stateDb: stateDb,
|
||||
extraDb: extraDb,
|
||||
chainDb: chainDb,
|
||||
dappDb: dappDb,
|
||||
eventMux: &event.TypeMux{},
|
||||
accountManager: config.AccountManager,
|
||||
DataDir: config.DataDir,
|
||||
@ -362,7 +361,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||
eth.pow = ethash.New()
|
||||
}
|
||||
//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
|
||||
eth.chainManager, err = core.NewChainManager(blockDb, stateDb, extraDb, eth.pow, eth.EventMux())
|
||||
eth.chainManager, err = core.NewChainManager(chainDb, eth.pow, eth.EventMux())
|
||||
if err != nil {
|
||||
if err == core.ErrNoGenesis {
|
||||
return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)
|
||||
@ -372,18 +371,13 @@ func New(config *Config) (*Ethereum, error) {
|
||||
}
|
||||
eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit)
|
||||
|
||||
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.chainManager, eth.EventMux())
|
||||
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.chainManager, eth.EventMux())
|
||||
eth.chainManager.SetProcessor(eth.blockProcessor)
|
||||
eth.protocolManager = NewProtocolManager(config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.chainManager)
|
||||
|
||||
eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
|
||||
eth.miner.SetGasPrice(config.GasPrice)
|
||||
|
||||
extra := config.Name
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
extra = extra[:params.MaximumExtraDataSize.Uint64()]
|
||||
}
|
||||
eth.miner.SetExtra([]byte(extra))
|
||||
eth.miner.SetExtra(config.ExtraData)
|
||||
|
||||
if config.Shh {
|
||||
eth.whisper = whisper.New()
|
||||
@ -525,9 +519,8 @@ func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcess
|
||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) BlockDb() common.Database { return s.blockDb }
|
||||
func (s *Ethereum) StateDb() common.Database { return s.stateDb }
|
||||
func (s *Ethereum) ExtraDb() common.Database { return s.extraDb }
|
||||
func (s *Ethereum) ChainDb() common.Database { return s.chainDb }
|
||||
func (s *Ethereum) DappDb() common.Database { return s.dappDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
||||
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
||||
@ -574,23 +567,19 @@ done:
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// don't change the order of database flushes
|
||||
if err := s.extraDb.Flush(); err != nil {
|
||||
glog.Fatalf("fatal error: flush extraDb: %v (Restart your node. We are aware of this issue)\n", err)
|
||||
if err := s.dappDb.Flush(); err != nil {
|
||||
glog.Fatalf("fatal error: flush dappDb: %v (Restart your node. We are aware of this issue)\n", err)
|
||||
}
|
||||
if err := s.stateDb.Flush(); err != nil {
|
||||
glog.Fatalf("fatal error: flush stateDb: %v (Restart your node. We are aware of this issue)\n", err)
|
||||
}
|
||||
if err := s.blockDb.Flush(); err != nil {
|
||||
glog.Fatalf("fatal error: flush blockDb: %v (Restart your node. We are aware of this issue)\n", err)
|
||||
if err := s.chainDb.Flush(); err != nil {
|
||||
glog.Fatalf("fatal error: flush chainDb: %v (Restart your node. We are aware of this issue)\n", err)
|
||||
}
|
||||
case <-s.shutdownChan:
|
||||
break done
|
||||
}
|
||||
}
|
||||
|
||||
s.blockDb.Close()
|
||||
s.stateDb.Close()
|
||||
s.extraDb.Close()
|
||||
s.chainDb.Close()
|
||||
s.dappDb.Close()
|
||||
|
||||
close(s.databasesClosed)
|
||||
}
|
||||
@ -688,14 +677,6 @@ func (self *Ethereum) StartAutoDAG() {
|
||||
}()
|
||||
}
|
||||
|
||||
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
|
||||
dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
|
||||
return dag, "full-R" + dag
|
||||
}
|
||||
|
||||
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
|
||||
func (self *Ethereum) StopAutoDAG() {
|
||||
if self.autodagquit != nil {
|
||||
@ -705,30 +686,6 @@ func (self *Ethereum) StopAutoDAG() {
|
||||
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
||||
}
|
||||
|
||||
/*
|
||||
// The databases were previously tied to protocol versions. Currently we
|
||||
// are moving away from this decision as approaching Frontier. The below
|
||||
// code was left in for now but should eventually be just dropped.
|
||||
|
||||
func saveProtocolVersion(db common.Database, protov int) {
|
||||
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||
protocolVersion := common.NewValue(d).Uint()
|
||||
|
||||
if protocolVersion == 0 {
|
||||
db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func saveBlockchainVersion(db common.Database, bcVersion int) {
|
||||
d, _ := db.Get([]byte("BlockchainVersion"))
|
||||
blockchainVersion := common.NewValue(d).Uint()
|
||||
|
||||
if blockchainVersion == 0 {
|
||||
db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Ethereum) Solc() (*compiler.Solidity, error) {
|
||||
var err error
|
||||
if self.solc == nil {
|
||||
@ -743,3 +700,92 @@ func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) {
|
||||
self.solc = nil
|
||||
return self.Solc()
|
||||
}
|
||||
|
||||
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
|
||||
dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
|
||||
return dag, "full-R" + dag
|
||||
}
|
||||
|
||||
func saveBlockchainVersion(db common.Database, bcVersion int) {
|
||||
d, _ := db.Get([]byte("BlockchainVersion"))
|
||||
blockchainVersion := common.NewValue(d).Uint()
|
||||
|
||||
if blockchainVersion == 0 {
|
||||
db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// mergeDatabases when required merge old database layout to one single database
|
||||
func mergeDatabases(datadir string, newdb func(path string) (common.Database, error)) error {
|
||||
// Check if already upgraded
|
||||
data := filepath.Join(datadir, "chaindata")
|
||||
if _, err := os.Stat(data); !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
// make sure it's not just a clean path
|
||||
chainPath := filepath.Join(datadir, "blockchain")
|
||||
if _, err := os.Stat(chainPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
glog.Infoln("Database upgrade required. Upgrading...")
|
||||
|
||||
database, err := newdb(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating data db err: %v", err)
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
// Migrate blocks
|
||||
chainDb, err := newdb(chainPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("state db err: %v", err)
|
||||
}
|
||||
defer chainDb.Close()
|
||||
|
||||
if chain, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
||||
glog.Infoln("Merging blockchain database...")
|
||||
it := chain.NewIterator()
|
||||
for it.Next() {
|
||||
database.Put(it.Key(), it.Value())
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
// Migrate state
|
||||
stateDb, err := newdb(filepath.Join(datadir, "state"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("state db err: %v", err)
|
||||
}
|
||||
defer stateDb.Close()
|
||||
|
||||
if state, ok := stateDb.(*ethdb.LDBDatabase); ok {
|
||||
glog.Infoln("Merging state database...")
|
||||
it := state.NewIterator()
|
||||
for it.Next() {
|
||||
database.Put(it.Key(), it.Value())
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
// Migrate transaction / receipts
|
||||
extraDb, err := newdb(filepath.Join(datadir, "extra"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("state db err: %v", err)
|
||||
}
|
||||
defer extraDb.Close()
|
||||
|
||||
if extra, ok := extraDb.(*ethdb.LDBDatabase); ok {
|
||||
glog.Infoln("Merging transaction database...")
|
||||
|
||||
it := extra.NewIterator()
|
||||
for it.Next() {
|
||||
database.Put(it.Key(), it.Value())
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -37,12 +37,11 @@ type blockPriceInfo struct {
|
||||
type GasPriceOracle struct {
|
||||
eth *Ethereum
|
||||
chain *core.ChainManager
|
||||
pool *core.TxPool
|
||||
events event.Subscription
|
||||
blocks map[uint64]*blockPriceInfo
|
||||
firstProcessed, lastProcessed uint64
|
||||
lastBaseMutex sync.Mutex
|
||||
lastBase *big.Int
|
||||
lastBase, minBase *big.Int
|
||||
}
|
||||
|
||||
func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) {
|
||||
@ -50,13 +49,15 @@ func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) {
|
||||
self.blocks = make(map[uint64]*blockPriceInfo)
|
||||
self.eth = eth
|
||||
self.chain = eth.chainManager
|
||||
self.pool = eth.txPool
|
||||
self.events = eth.EventMux().Subscribe(
|
||||
core.ChainEvent{},
|
||||
core.ChainSplitEvent{},
|
||||
core.TxPreEvent{},
|
||||
core.TxPostEvent{},
|
||||
)
|
||||
|
||||
minbase := new(big.Int).Mul(self.eth.GpoMinGasPrice, big.NewInt(100))
|
||||
minbase = minbase.Div(minbase, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
|
||||
self.minBase = minbase
|
||||
|
||||
self.processPastBlocks()
|
||||
go self.listenLoop()
|
||||
return
|
||||
@ -93,8 +94,6 @@ func (self *GasPriceOracle) listenLoop() {
|
||||
self.processBlock(ev.Block)
|
||||
case core.ChainSplitEvent:
|
||||
self.processBlock(ev.Block)
|
||||
case core.TxPreEvent:
|
||||
case core.TxPostEvent:
|
||||
}
|
||||
}
|
||||
self.events.Unsubscribe()
|
||||
@ -131,6 +130,10 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
|
||||
newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand))
|
||||
newBase.Div(newBase, big.NewInt(1000000))
|
||||
|
||||
if newBase.Cmp(self.minBase) < 0 {
|
||||
newBase = self.minBase
|
||||
}
|
||||
|
||||
bpi := self.blocks[i]
|
||||
if bpi == nil {
|
||||
bpi = &blockPriceInfo{}
|
||||
@ -146,7 +149,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
|
||||
|
||||
// returns the lowers possible price with which a tx was or could have been included
|
||||
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
|
||||
gasUsed := new(big.Int)
|
||||
gasUsed := big.NewInt(0)
|
||||
|
||||
receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash())
|
||||
if len(receipts) > 0 {
|
||||
@ -158,12 +161,12 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
|
||||
if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
|
||||
big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 {
|
||||
// block is not full, could have posted a tx with MinGasPrice
|
||||
return self.eth.GpoMinGasPrice
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
txs := block.Transactions()
|
||||
if len(txs) == 0 {
|
||||
return self.eth.GpoMinGasPrice
|
||||
return big.NewInt(0)
|
||||
}
|
||||
// block is full, find smallest gasPrice
|
||||
minPrice := txs[0].GasPrice()
|
||||
|
@ -117,7 +117,7 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
|
||||
manager.downloader = downloader.New(manager.eventMux, manager.chainman.HasBlock, manager.chainman.GetBlock, manager.chainman.CurrentBlock, manager.chainman.InsertChain, manager.removePeer)
|
||||
|
||||
validator := func(block *types.Block, parent *types.Block) error {
|
||||
return core.ValidateHeader(pow, block.Header(), parent, true)
|
||||
return core.ValidateHeader(pow, block.Header(), parent, true, false)
|
||||
}
|
||||
heighter := func() uint64 {
|
||||
return manager.chainman.CurrentBlock().NumberU64()
|
||||
@ -413,10 +413,12 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
|
||||
pm.fetcher.Enqueue(p.id, request.Block)
|
||||
|
||||
// TODO: Schedule a sync to cover potential gaps (this needs proto update)
|
||||
// Update the peers total difficulty if needed, schedule a download if gapped
|
||||
if request.TD.Cmp(p.Td()) > 0 {
|
||||
p.SetTd(request.TD)
|
||||
go pm.synchronise(p)
|
||||
if request.TD.Cmp(new(big.Int).Add(pm.chainman.Td(), request.Block.Difficulty())) > 0 {
|
||||
go pm.synchronise(p)
|
||||
}
|
||||
}
|
||||
|
||||
case TxMsg:
|
||||
|
@ -179,10 +179,10 @@ type testPeer struct {
|
||||
|
||||
func newProtocolManagerForTesting(txAdded chan<- []*types.Transaction) *ProtocolManager {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
core.WriteTestNetGenesisBlock(db, db, 0)
|
||||
core.WriteTestNetGenesisBlock(db, 0)
|
||||
var (
|
||||
em = new(event.TypeMux)
|
||||
chain, _ = core.NewChainManager(db, db, db, core.FakePow{}, em)
|
||||
chain, _ = core.NewChainManager(db, core.FakePow{}, em)
|
||||
txpool = &fakeTxPool{added: txAdded}
|
||||
pm = NewProtocolManager(NetworkId, em, txpool, core.FakePow{}, chain)
|
||||
)
|
||||
|
@ -39,9 +39,8 @@ var OpenFileLimit = 64
|
||||
// cacheRatio specifies how the total alloted cache is distributed between the
|
||||
// various system databases.
|
||||
var cacheRatio = map[string]float64{
|
||||
"blockchain": 1.0 / 13.0,
|
||||
"extra": 2.0 / 13.0,
|
||||
"state": 10.0 / 13.0,
|
||||
"dapp": 2.0 / 13.0,
|
||||
"chaindata": 11.0 / 13.0,
|
||||
}
|
||||
|
||||
type LDBDatabase struct {
|
||||
|
@ -1,112 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package fdtrack logs statistics about open file descriptors.
|
||||
package fdtrack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
mutex sync.Mutex
|
||||
all = make(map[string]int)
|
||||
)
|
||||
|
||||
func Open(desc string) {
|
||||
mutex.Lock()
|
||||
all[desc] += 1
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func Close(desc string) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if c, ok := all[desc]; ok {
|
||||
if c == 1 {
|
||||
delete(all, desc)
|
||||
} else {
|
||||
all[desc]--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WrapListener(desc string, l net.Listener) net.Listener {
|
||||
Open(desc)
|
||||
return &wrappedListener{l, desc}
|
||||
}
|
||||
|
||||
type wrappedListener struct {
|
||||
net.Listener
|
||||
desc string
|
||||
}
|
||||
|
||||
func (w *wrappedListener) Accept() (net.Conn, error) {
|
||||
c, err := w.Listener.Accept()
|
||||
if err == nil {
|
||||
c = WrapConn(w.desc, c)
|
||||
}
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (w *wrappedListener) Close() error {
|
||||
err := w.Listener.Close()
|
||||
if err == nil {
|
||||
Close(w.desc)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func WrapConn(desc string, conn net.Conn) net.Conn {
|
||||
Open(desc)
|
||||
return &wrappedConn{conn, desc}
|
||||
}
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
desc string
|
||||
}
|
||||
|
||||
func (w *wrappedConn) Close() error {
|
||||
err := w.Conn.Close()
|
||||
if err == nil {
|
||||
Close(w.desc)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Start() {
|
||||
go func() {
|
||||
for range time.Tick(15 * time.Second) {
|
||||
mutex.Lock()
|
||||
var sum, tracked = 0, []string{}
|
||||
for what, n := range all {
|
||||
sum += n
|
||||
tracked = append(tracked, fmt.Sprintf("%s:%d", what, n))
|
||||
}
|
||||
mutex.Unlock()
|
||||
used, _ := fdusage()
|
||||
sort.Strings(tracked)
|
||||
glog.Infof("fd usage %d/%d, tracked %d %v", used, fdlimit(), sum, tracked)
|
||||
}
|
||||
}()
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build darwin
|
||||
|
||||
package fdtrack
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// #cgo CFLAGS: -lproc
|
||||
// #include <libproc.h>
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
|
||||
func fdlimit() int {
|
||||
var nofile syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
|
||||
return 0
|
||||
}
|
||||
return int(nofile.Cur)
|
||||
}
|
||||
|
||||
func fdusage() (int, error) {
|
||||
pid := C.int(os.Getpid())
|
||||
// Query for a rough estimate on the amout of data that
|
||||
// proc_pidinfo will return.
|
||||
rlen, err := C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, nil, 0)
|
||||
if rlen <= 0 {
|
||||
return 0, err
|
||||
}
|
||||
// Load the list of file descriptors. We don't actually care about
|
||||
// the content, only about the size. Since the number of fds can
|
||||
// change while we're reading them, the loop enlarges the buffer
|
||||
// until proc_pidinfo says the result fitted.
|
||||
var buf unsafe.Pointer
|
||||
defer func() {
|
||||
if buf != nil {
|
||||
C.free(buf)
|
||||
}
|
||||
}()
|
||||
for buflen := rlen; ; buflen *= 2 {
|
||||
buf, err = C.reallocf(buf, C.size_t(buflen))
|
||||
if buf == nil {
|
||||
return 0, err
|
||||
}
|
||||
rlen, err = C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, buf, buflen)
|
||||
if rlen <= 0 {
|
||||
return 0, err
|
||||
} else if rlen == buflen {
|
||||
continue
|
||||
}
|
||||
return int(rlen / C.PROC_PIDLISTFD_SIZE), nil
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
3079
jsre/ethereum_js.go
3079
jsre/ethereum_js.go
File diff suppressed because it is too large
Load Diff
34
jsre/jsre.go
34
jsre/jsre.go
@ -65,8 +65,8 @@ func New(assetPath string) *JSRE {
|
||||
}
|
||||
re.loopWg.Add(1)
|
||||
go re.runEventLoop()
|
||||
re.Compile("pp.js", pp_js) // load prettyprint func definition
|
||||
re.Set("loadScript", re.loadScript)
|
||||
re.Set("inspect", prettyPrintJS)
|
||||
return re
|
||||
}
|
||||
|
||||
@ -255,35 +255,19 @@ func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
|
||||
return otto.TrueValue()
|
||||
}
|
||||
|
||||
// PrettyPrint writes v to standard output.
|
||||
func (self *JSRE) PrettyPrint(v interface{}) (val otto.Value, err error) {
|
||||
var method otto.Value
|
||||
// EvalAndPrettyPrint evaluates code and pretty prints the result to
|
||||
// standard output.
|
||||
func (self *JSRE) EvalAndPrettyPrint(code string) (err error) {
|
||||
self.do(func(vm *otto.Otto) {
|
||||
val, err = vm.ToValue(v)
|
||||
var val otto.Value
|
||||
val, err = vm.Run(code)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
method, err = vm.Get("prettyPrint")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
val, err = method.Call(method, val)
|
||||
prettyPrint(vm, val)
|
||||
fmt.Println()
|
||||
})
|
||||
return val, err
|
||||
}
|
||||
|
||||
// Eval evaluates JS function and returns result in a pretty printed string format.
|
||||
func (self *JSRE) Eval(code string) (s string, err error) {
|
||||
var val otto.Value
|
||||
val, err = self.Run(code)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
val, err = self.PrettyPrint(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Compile compiles and then runs a piece of JS code.
|
||||
|
@ -19,6 +19,7 @@ package jsre
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -40,10 +41,23 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value
|
||||
return v
|
||||
}
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
jsre := New("/tmp")
|
||||
func newWithTestJS(t *testing.T, testjs string) (*JSRE, string) {
|
||||
dir, err := ioutil.TempDir("", "jsre-test")
|
||||
if err != nil {
|
||||
t.Fatal("cannot create temporary directory:", err)
|
||||
}
|
||||
if testjs != "" {
|
||||
if err := ioutil.WriteFile(path.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil {
|
||||
t.Fatal("cannot create test.js:", err)
|
||||
}
|
||||
}
|
||||
return New(dir), dir
|
||||
}
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
jsre, dir := newWithTestJS(t, `msg = "testMsg"`)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
|
||||
err := jsre.Exec("test.js")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
@ -64,9 +78,9 @@ func TestExec(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNatto(t *testing.T) {
|
||||
jsre := New("/tmp")
|
||||
jsre, dir := newWithTestJS(t, `setTimeout(function(){msg = "testMsg"}, 1);`)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
ioutil.WriteFile("/tmp/test.js", []byte(`setTimeout(function(){msg = "testMsg"}, 1);`), os.ModePerm)
|
||||
err := jsre.Exec("test.js")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
@ -88,26 +102,21 @@ func TestNatto(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
jsre := New("/tmp")
|
||||
jsre := New("")
|
||||
defer jsre.Stop(false)
|
||||
|
||||
jsre.Bind("no", &testNativeObjectBinding{})
|
||||
|
||||
val, err := jsre.Run(`no.TestMethod("testMsg")`)
|
||||
_, err := jsre.Run(`no.TestMethod("testMsg")`)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
pp, err := jsre.PrettyPrint(val)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
t.Logf("no: %v", pp)
|
||||
jsre.Stop(false)
|
||||
}
|
||||
|
||||
func TestLoadScript(t *testing.T) {
|
||||
jsre := New("/tmp")
|
||||
jsre, dir := newWithTestJS(t, `msg = "testMsg"`)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
|
||||
_, err := jsre.Run(`loadScript("test.js")`)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
|
137
jsre/pp_js.go
137
jsre/pp_js.go
@ -1,137 +0,0 @@
|
||||
// Copyright 2014 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 jsre
|
||||
|
||||
const pp_js = `
|
||||
function pp(object, indent) {
|
||||
try {
|
||||
JSON.stringify(object)
|
||||
} catch(e) {
|
||||
return pp(e, indent);
|
||||
}
|
||||
|
||||
var str = "";
|
||||
if(object instanceof Array) {
|
||||
str += "[";
|
||||
for(var i = 0, l = object.length; i < l; i++) {
|
||||
str += pp(object[i], indent);
|
||||
|
||||
if(i < l-1) {
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
str += " ]";
|
||||
} else if (object instanceof Error) {
|
||||
str += "\033[31m" + "Error:\033[0m " + object.message;
|
||||
} else if (isBigNumber(object)) {
|
||||
str += "\033[32m'" + object.toString(10) + "'";
|
||||
} else if(typeof(object) === "object") {
|
||||
str += "{\n";
|
||||
indent += " ";
|
||||
|
||||
var fields = getFields(object);
|
||||
var last = fields[fields.length - 1];
|
||||
fields.forEach(function (key) {
|
||||
str += indent + key + ": ";
|
||||
try {
|
||||
str += pp(object[key], indent);
|
||||
} catch (e) {
|
||||
str += pp(e, indent);
|
||||
}
|
||||
if(key !== last) {
|
||||
str += ",";
|
||||
}
|
||||
str += "\n";
|
||||
});
|
||||
str += indent.substr(2, indent.length) + "}";
|
||||
} else if(typeof(object) === "string") {
|
||||
str += "\033[32m'" + object + "'";
|
||||
} else if(typeof(object) === "undefined") {
|
||||
str += "\033[1m\033[30m" + object;
|
||||
} else if(typeof(object) === "number") {
|
||||
str += "\033[31m" + object;
|
||||
} else if(typeof(object) === "function") {
|
||||
str += "\033[35m" + object.toString().split(" {")[0];
|
||||
} else {
|
||||
str += object;
|
||||
}
|
||||
|
||||
str += "\033[0m";
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
var redundantFields = [
|
||||
'valueOf',
|
||||
'toString',
|
||||
'toLocaleString',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'constructor'
|
||||
];
|
||||
|
||||
var getFields = function (object) {
|
||||
var members = Object.getOwnPropertyNames(object);
|
||||
if (object.constructor && object.constructor.prototype) {
|
||||
members = members.concat(Object.getOwnPropertyNames(object.constructor.prototype));
|
||||
}
|
||||
|
||||
var fields = members.filter(function (member) {
|
||||
return !isMemberFunction(object, member)
|
||||
}).sort()
|
||||
var funcs = members.filter(function (member) {
|
||||
return isMemberFunction(object, member)
|
||||
}).sort()
|
||||
|
||||
var results = fields.concat(funcs);
|
||||
return results.filter(function (field) {
|
||||
return redundantFields.indexOf(field) === -1;
|
||||
});
|
||||
};
|
||||
|
||||
var isMemberFunction = function(object, member) {
|
||||
try {
|
||||
return typeof(object[member]) === "function";
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var isBigNumber = function (object) {
|
||||
var result = typeof BigNumber !== 'undefined' && object instanceof BigNumber;
|
||||
|
||||
if (!result) {
|
||||
if (typeof(object) === "object" && object.constructor != null) {
|
||||
result = object.constructor.toString().indexOf("function BigNumber(") == 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
};
|
||||
|
||||
function prettyPrint(/* */) {
|
||||
var args = arguments;
|
||||
var ret = "";
|
||||
for(var i = 0, l = args.length; i < l; i++) {
|
||||
ret += pp(args[i], "") + "\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var print = prettyPrint;
|
||||
`
|
231
jsre/pretty.go
Normal file
231
jsre/pretty.go
Normal file
@ -0,0 +1,231 @@
|
||||
// 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 jsre
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPrettyPrintLevel = 3
|
||||
indentString = " "
|
||||
)
|
||||
|
||||
var (
|
||||
functionColor = color.New(color.FgMagenta)
|
||||
specialColor = color.New(color.Bold)
|
||||
numberColor = color.New(color.FgRed)
|
||||
stringColor = color.New(color.FgGreen)
|
||||
)
|
||||
|
||||
// these fields are hidden when printing objects.
|
||||
var boringKeys = map[string]bool{
|
||||
"valueOf": true,
|
||||
"toString": true,
|
||||
"toLocaleString": true,
|
||||
"hasOwnProperty": true,
|
||||
"isPrototypeOf": true,
|
||||
"propertyIsEnumerable": true,
|
||||
"constructor": true,
|
||||
}
|
||||
|
||||
// prettyPrint writes value to standard output.
|
||||
func prettyPrint(vm *otto.Otto, value otto.Value) {
|
||||
ppctx{vm}.printValue(value, 0, false)
|
||||
}
|
||||
|
||||
func prettyPrintJS(call otto.FunctionCall) otto.Value {
|
||||
for _, v := range call.ArgumentList {
|
||||
prettyPrint(call.Otto, v)
|
||||
fmt.Println()
|
||||
}
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
type ppctx struct{ vm *otto.Otto }
|
||||
|
||||
func (ctx ppctx) indent(level int) string {
|
||||
return strings.Repeat(indentString, level)
|
||||
}
|
||||
|
||||
func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) {
|
||||
switch {
|
||||
case v.IsObject():
|
||||
ctx.printObject(v.Object(), level, inArray)
|
||||
case v.IsNull():
|
||||
specialColor.Print("null")
|
||||
case v.IsUndefined():
|
||||
specialColor.Print("undefined")
|
||||
case v.IsString():
|
||||
s, _ := v.ToString()
|
||||
stringColor.Printf("%q", s)
|
||||
case v.IsBoolean():
|
||||
b, _ := v.ToBoolean()
|
||||
specialColor.Printf("%t", b)
|
||||
case v.IsNaN():
|
||||
numberColor.Printf("NaN")
|
||||
case v.IsNumber():
|
||||
s, _ := v.ToString()
|
||||
numberColor.Printf("%s", s)
|
||||
default:
|
||||
fmt.Printf("<unprintable>")
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
|
||||
switch obj.Class() {
|
||||
case "Array":
|
||||
lv, _ := obj.Get("length")
|
||||
len, _ := lv.ToInteger()
|
||||
if len == 0 {
|
||||
fmt.Printf("[]")
|
||||
return
|
||||
}
|
||||
if level > maxPrettyPrintLevel {
|
||||
fmt.Print("[...]")
|
||||
return
|
||||
}
|
||||
fmt.Print("[")
|
||||
for i := int64(0); i < len; i++ {
|
||||
el, err := obj.Get(strconv.FormatInt(i, 10))
|
||||
if err == nil {
|
||||
ctx.printValue(el, level+1, true)
|
||||
}
|
||||
if i < len-1 {
|
||||
fmt.Printf(", ")
|
||||
}
|
||||
}
|
||||
fmt.Print("]")
|
||||
|
||||
case "Object":
|
||||
// Print values from bignumber.js as regular numbers.
|
||||
if ctx.isBigNumber(obj) {
|
||||
numberColor.Print(toString(obj))
|
||||
return
|
||||
}
|
||||
// Otherwise, print all fields indented, but stop if we're too deep.
|
||||
keys := ctx.fields(obj)
|
||||
if len(keys) == 0 {
|
||||
fmt.Print("{}")
|
||||
return
|
||||
}
|
||||
if level > maxPrettyPrintLevel {
|
||||
fmt.Print("{...}")
|
||||
return
|
||||
}
|
||||
fmt.Println("{")
|
||||
for i, k := range keys {
|
||||
v, _ := obj.Get(k)
|
||||
fmt.Printf("%s%s: ", ctx.indent(level+1), k)
|
||||
ctx.printValue(v, level+1, false)
|
||||
if i < len(keys)-1 {
|
||||
fmt.Printf(",")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
if inArray {
|
||||
level--
|
||||
}
|
||||
fmt.Printf("%s}", ctx.indent(level))
|
||||
|
||||
case "Function":
|
||||
// Use toString() to display the argument list if possible.
|
||||
if robj, err := obj.Call("toString"); err != nil {
|
||||
functionColor.Print("function()")
|
||||
} else {
|
||||
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
|
||||
desc = strings.Replace(desc, " (", "(", 1)
|
||||
functionColor.Print(desc)
|
||||
}
|
||||
|
||||
case "RegExp":
|
||||
stringColor.Print(toString(obj))
|
||||
|
||||
default:
|
||||
if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel {
|
||||
s, _ := obj.Call("toString")
|
||||
fmt.Printf("<%s %s>", obj.Class(), s.String())
|
||||
} else {
|
||||
fmt.Printf("<%s>", obj.Class())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx ppctx) fields(obj *otto.Object) []string {
|
||||
var (
|
||||
vals, methods []string
|
||||
seen = make(map[string]bool)
|
||||
)
|
||||
add := func(k string) {
|
||||
if seen[k] || boringKeys[k] {
|
||||
return
|
||||
}
|
||||
seen[k] = true
|
||||
if v, _ := obj.Get(k); v.IsFunction() {
|
||||
methods = append(methods, k)
|
||||
} else {
|
||||
vals = append(vals, k)
|
||||
}
|
||||
}
|
||||
// add own properties
|
||||
ctx.doOwnProperties(obj.Value(), add)
|
||||
// add properties of the constructor
|
||||
if cp := constructorPrototype(obj); cp != nil {
|
||||
ctx.doOwnProperties(cp.Value(), add)
|
||||
}
|
||||
sort.Strings(vals)
|
||||
sort.Strings(methods)
|
||||
return append(vals, methods...)
|
||||
}
|
||||
|
||||
func (ctx ppctx) doOwnProperties(v otto.Value, f func(string)) {
|
||||
Object, _ := ctx.vm.Object("Object")
|
||||
rv, _ := Object.Call("getOwnPropertyNames", v)
|
||||
gv, _ := rv.Export()
|
||||
for _, v := range gv.([]interface{}) {
|
||||
f(v.(string))
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx ppctx) isBigNumber(v *otto.Object) bool {
|
||||
BigNumber, err := ctx.vm.Run("BigNumber.prototype")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cp := constructorPrototype(v)
|
||||
return cp != nil && cp.Value() == BigNumber
|
||||
}
|
||||
|
||||
func toString(obj *otto.Object) string {
|
||||
s, _ := obj.Call("toString")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func constructorPrototype(obj *otto.Object) *otto.Object {
|
||||
if v, _ := obj.Get("constructor"); v.Object() != nil {
|
||||
if v, _ = v.Object().Get("prototype"); v.Object() != nil {
|
||||
return v.Object()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
package miner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
@ -29,6 +30,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
@ -139,12 +141,24 @@ func (self *Miner) Mining() bool {
|
||||
return atomic.LoadInt32(&self.mining) > 0
|
||||
}
|
||||
|
||||
func (self *Miner) HashRate() int64 {
|
||||
return self.pow.GetHashrate()
|
||||
func (self *Miner) HashRate() (tot int64) {
|
||||
tot += self.pow.GetHashrate()
|
||||
// do we care this might race? is it worth we're rewriting some
|
||||
// aspects of the worker/locking up agents so we can get an accurate
|
||||
// hashrate?
|
||||
for _, agent := range self.worker.agents {
|
||||
tot += agent.GetHashRate()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Miner) SetExtra(extra []byte) {
|
||||
func (self *Miner) SetExtra(extra []byte) error {
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
|
||||
}
|
||||
|
||||
self.worker.extra = extra
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Miner) PendingState() *state.StateDB {
|
||||
|
@ -27,6 +27,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
type hashrate struct {
|
||||
ping time.Time
|
||||
rate uint64
|
||||
}
|
||||
|
||||
type RemoteAgent struct {
|
||||
mu sync.Mutex
|
||||
|
||||
@ -36,14 +41,24 @@ type RemoteAgent struct {
|
||||
|
||||
currentWork *Work
|
||||
work map[common.Hash]*Work
|
||||
|
||||
hashrateMu sync.RWMutex
|
||||
hashrate map[common.Hash]hashrate
|
||||
}
|
||||
|
||||
func NewRemoteAgent() *RemoteAgent {
|
||||
agent := &RemoteAgent{work: make(map[common.Hash]*Work)}
|
||||
agent := &RemoteAgent{work: make(map[common.Hash]*Work), hashrate: make(map[common.Hash]hashrate)}
|
||||
|
||||
return agent
|
||||
}
|
||||
|
||||
func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) {
|
||||
a.hashrateMu.Lock()
|
||||
defer a.hashrateMu.Unlock()
|
||||
|
||||
a.hashrate[id] = hashrate{time.Now(), rate}
|
||||
}
|
||||
|
||||
func (a *RemoteAgent) Work() chan<- *Work {
|
||||
return a.workCh
|
||||
}
|
||||
@ -63,7 +78,17 @@ func (a *RemoteAgent) Stop() {
|
||||
close(a.workCh)
|
||||
}
|
||||
|
||||
func (a *RemoteAgent) GetHashRate() int64 { return 0 }
|
||||
// GetHashRate returns the accumulated hashrate of all identifier combined
|
||||
func (a *RemoteAgent) GetHashRate() (tot int64) {
|
||||
a.hashrateMu.RLock()
|
||||
defer a.hashrateMu.RUnlock()
|
||||
|
||||
// this could overflow
|
||||
for _, hashrate := range a.hashrate {
|
||||
tot += int64(hashrate.rate)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (a *RemoteAgent) GetWork() [3]string {
|
||||
a.mu.Lock()
|
||||
@ -131,6 +156,14 @@ out:
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
|
||||
a.hashrateMu.Lock()
|
||||
for id, hashrate := range a.hashrate {
|
||||
if time.Since(hashrate.ping) > 10*time.Second {
|
||||
delete(a.hashrate, id)
|
||||
}
|
||||
}
|
||||
a.hashrateMu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ type worker struct {
|
||||
eth core.Backend
|
||||
chain *core.ChainManager
|
||||
proc *core.BlockProcessor
|
||||
extraDb common.Database
|
||||
chainDb common.Database
|
||||
|
||||
coinbase common.Address
|
||||
gasPrice *big.Int
|
||||
@ -126,7 +126,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
|
||||
worker := &worker{
|
||||
eth: eth,
|
||||
mux: eth.EventMux(),
|
||||
extraDb: eth.ExtraDb(),
|
||||
chainDb: eth.ChainDb(),
|
||||
recv: make(chan *Result, resultQueueSize),
|
||||
gasPrice: new(big.Int),
|
||||
chain: eth.ChainManager(),
|
||||
@ -278,7 +278,7 @@ func (self *worker) wait() {
|
||||
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
||||
continue
|
||||
}
|
||||
if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent, true); err != nil && err != core.BlockFutureErr {
|
||||
if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent, true, false); err != nil && err != core.BlockFutureErr {
|
||||
glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
|
||||
continue
|
||||
}
|
||||
@ -291,20 +291,23 @@ func (self *worker) wait() {
|
||||
// check if canon block and write transactions
|
||||
if stat == core.CanonStatTy {
|
||||
// This puts transactions in a extra db for rpc
|
||||
core.PutTransactions(self.extraDb, block, block.Transactions())
|
||||
core.PutTransactions(self.chainDb, block, block.Transactions())
|
||||
// store the receipts
|
||||
core.PutReceipts(self.extraDb, work.receipts)
|
||||
core.PutReceipts(self.chainDb, work.receipts)
|
||||
}
|
||||
|
||||
// broadcast before waiting for validation
|
||||
go func(block *types.Block, logs state.Logs) {
|
||||
go func(block *types.Block, logs state.Logs, receipts []*types.Receipt) {
|
||||
self.mux.Post(core.NewMinedBlockEvent{block})
|
||||
self.mux.Post(core.ChainEvent{block, block.Hash(), logs})
|
||||
if stat == core.CanonStatTy {
|
||||
self.mux.Post(core.ChainHeadEvent{block})
|
||||
self.mux.Post(logs)
|
||||
}
|
||||
}(block, work.state.Logs())
|
||||
if err := core.PutBlockReceipts(self.chainDb, block, receipts); err != nil {
|
||||
glog.V(logger.Warn).Infoln("error writing block receipts:", err)
|
||||
}
|
||||
}(block, work.state.Logs(), work.receipts)
|
||||
}
|
||||
|
||||
// check staleness and display confirmation
|
||||
@ -344,7 +347,7 @@ func (self *worker) push(work *Work) {
|
||||
|
||||
// makeCurrent creates a new environment for the current cycle.
|
||||
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) {
|
||||
state := state.New(parent.Root(), self.eth.StateDb())
|
||||
state := state.New(parent.Root(), self.eth.ChainDb())
|
||||
work := &Work{
|
||||
state: state,
|
||||
ancestors: set.New(),
|
||||
@ -431,8 +434,8 @@ func (self *worker) commitNewWork() {
|
||||
tstart := time.Now()
|
||||
parent := self.chain.CurrentBlock()
|
||||
tstamp := tstart.Unix()
|
||||
if tstamp <= int64(parent.Time()) {
|
||||
tstamp = int64(parent.Time()) + 1
|
||||
if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
|
||||
tstamp = parent.Time().Int64() + 1
|
||||
}
|
||||
// this will ensure we're not going off too far in the future
|
||||
if now := time.Now().Unix(); tstamp > now+4 {
|
||||
@ -445,12 +448,12 @@ func (self *worker) commitNewWork() {
|
||||
header := &types.Header{
|
||||
ParentHash: parent.Hash(),
|
||||
Number: num.Add(num, common.Big1),
|
||||
Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time(), parent.Number(), parent.Difficulty()),
|
||||
Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()),
|
||||
GasLimit: core.CalcGasLimit(parent),
|
||||
GasUsed: new(big.Int),
|
||||
Coinbase: self.coinbase,
|
||||
Extra: self.extra,
|
||||
Time: uint64(tstamp),
|
||||
Time: big.NewInt(tstamp),
|
||||
}
|
||||
|
||||
previous := self.current
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/fdtrack"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
@ -213,7 +212,6 @@ func (t *dialTask) Do(srv *Server) {
|
||||
glog.V(logger.Detail).Infof("dial error: %v", err)
|
||||
return
|
||||
}
|
||||
fd = fdtrack.WrapConn("p2p", fd)
|
||||
mfd := newMeteredConn(fd, false)
|
||||
|
||||
srv.setupConn(mfd, t.flags, t.dest)
|
||||
|
@ -48,6 +48,10 @@ type Node struct {
|
||||
// In those tests, the content of sha will not actually correspond
|
||||
// with ID.
|
||||
sha common.Hash
|
||||
|
||||
// whether this node is currently being pinged in order to replace
|
||||
// it in a bucket
|
||||
contested bool
|
||||
}
|
||||
|
||||
func newNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {
|
||||
|
@ -78,9 +78,8 @@ type transport interface {
|
||||
close()
|
||||
}
|
||||
|
||||
// bucket contains nodes, ordered by their last activity.
|
||||
// the entry that was most recently active is the last element
|
||||
// in entries.
|
||||
// bucket contains nodes, ordered by their last activity. the entry
|
||||
// that was most recently active is the first element in entries.
|
||||
type bucket struct {
|
||||
lastLookup time.Time
|
||||
entries []*Node
|
||||
@ -164,7 +163,9 @@ func randUint(max uint32) uint32 {
|
||||
|
||||
// Close terminates the network listener and flushes the node database.
|
||||
func (tab *Table) Close() {
|
||||
tab.net.close()
|
||||
if tab.net != nil {
|
||||
tab.net.close()
|
||||
}
|
||||
tab.db.close()
|
||||
}
|
||||
|
||||
@ -233,7 +234,7 @@ func (tab *Table) Lookup(targetID NodeID) []*Node {
|
||||
|
||||
if fails >= maxFindnodeFailures {
|
||||
glog.V(logger.Detail).Infof("Evacuating node %x: %d findnode failures", n.ID[:8], fails)
|
||||
tab.del(n)
|
||||
tab.delete(n)
|
||||
}
|
||||
}
|
||||
reply <- tab.bondall(r)
|
||||
@ -399,15 +400,11 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16
|
||||
node = w.n
|
||||
}
|
||||
}
|
||||
// Even if bonding temporarily failed, give the node a chance
|
||||
if node != nil {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
b := tab.buckets[logdist(tab.self.sha, node.sha)]
|
||||
if !b.bump(node) {
|
||||
tab.pingreplace(node, b)
|
||||
}
|
||||
// Add the node to the table even if the bonding ping/pong
|
||||
// fails. It will be relaced quickly if it continues to be
|
||||
// unresponsive.
|
||||
tab.add(node)
|
||||
tab.db.updateFindFails(id, 0)
|
||||
}
|
||||
return node, result
|
||||
@ -418,7 +415,7 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd
|
||||
<-tab.bondslots
|
||||
defer func() { tab.bondslots <- struct{}{} }()
|
||||
|
||||
// Ping the remote side and wait for a pong
|
||||
// Ping the remote side and wait for a pong.
|
||||
if w.err = tab.ping(id, addr); w.err != nil {
|
||||
close(w.done)
|
||||
return
|
||||
@ -429,33 +426,14 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd
|
||||
// waitping will simply time out.
|
||||
tab.net.waitping(id)
|
||||
}
|
||||
// Bonding succeeded, update the node database
|
||||
// Bonding succeeded, update the node database.
|
||||
w.n = newNode(id, addr.IP, uint16(addr.Port), tcpPort)
|
||||
tab.db.updateNode(w.n)
|
||||
close(w.done)
|
||||
}
|
||||
|
||||
func (tab *Table) pingreplace(new *Node, b *bucket) {
|
||||
if len(b.entries) == bucketSize {
|
||||
oldest := b.entries[bucketSize-1]
|
||||
if err := tab.ping(oldest.ID, oldest.addr()); err == nil {
|
||||
// The node responded, we don't need to replace it.
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Add a slot at the end so the last entry doesn't
|
||||
// fall off when adding the new node.
|
||||
b.entries = append(b.entries, nil)
|
||||
}
|
||||
copy(b.entries[1:], b.entries)
|
||||
b.entries[0] = new
|
||||
if tab.nodeAddedHook != nil {
|
||||
tab.nodeAddedHook(new)
|
||||
}
|
||||
}
|
||||
|
||||
// ping a remote endpoint and wait for a reply, also updating the node database
|
||||
// accordingly.
|
||||
// ping a remote endpoint and wait for a reply, also updating the node
|
||||
// database accordingly.
|
||||
func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
|
||||
// Update the last ping and send the message
|
||||
tab.db.updateLastPing(id, time.Now())
|
||||
@ -465,24 +443,60 @@ func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
|
||||
// Pong received, update the database and return
|
||||
tab.db.updateLastPong(id, time.Now())
|
||||
tab.db.ensureExpirer()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// add puts the entries into the table if their corresponding
|
||||
// bucket is not full. The caller must hold tab.mutex.
|
||||
func (tab *Table) add(entries []*Node) {
|
||||
// add attempts to add the given node its corresponding bucket. If the
|
||||
// bucket has space available, adding the node succeeds immediately.
|
||||
// Otherwise, the node is added if the least recently active node in
|
||||
// the bucket does not respond to a ping packet.
|
||||
//
|
||||
// The caller must not hold tab.mutex.
|
||||
func (tab *Table) add(new *Node) {
|
||||
b := tab.buckets[logdist(tab.self.sha, new.sha)]
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
if b.bump(new) {
|
||||
return
|
||||
}
|
||||
var oldest *Node
|
||||
if len(b.entries) == bucketSize {
|
||||
oldest = b.entries[bucketSize-1]
|
||||
if oldest.contested {
|
||||
// The node is already being replaced, don't attempt
|
||||
// to replace it.
|
||||
return
|
||||
}
|
||||
oldest.contested = true
|
||||
// Let go of the mutex so other goroutines can access
|
||||
// the table while we ping the least recently active node.
|
||||
tab.mutex.Unlock()
|
||||
err := tab.ping(oldest.ID, oldest.addr())
|
||||
tab.mutex.Lock()
|
||||
oldest.contested = false
|
||||
if err == nil {
|
||||
// The node responded, don't replace it.
|
||||
return
|
||||
}
|
||||
}
|
||||
added := b.replace(new, oldest)
|
||||
if added && tab.nodeAddedHook != nil {
|
||||
tab.nodeAddedHook(new)
|
||||
}
|
||||
}
|
||||
|
||||
// stuff adds nodes the table to the end of their corresponding bucket
|
||||
// if the bucket is not full. The caller must hold tab.mutex.
|
||||
func (tab *Table) stuff(nodes []*Node) {
|
||||
outer:
|
||||
for _, n := range entries {
|
||||
for _, n := range nodes {
|
||||
if n.ID == tab.self.ID {
|
||||
// don't add self.
|
||||
continue
|
||||
continue // don't add self
|
||||
}
|
||||
bucket := tab.buckets[logdist(tab.self.sha, n.sha)]
|
||||
for i := range bucket.entries {
|
||||
if bucket.entries[i].ID == n.ID {
|
||||
// already in bucket
|
||||
continue outer
|
||||
continue outer // already in bucket
|
||||
}
|
||||
}
|
||||
if len(bucket.entries) < bucketSize {
|
||||
@ -494,12 +508,11 @@ outer:
|
||||
}
|
||||
}
|
||||
|
||||
// del removes an entry from the node table (used to evacuate failed/non-bonded
|
||||
// discovery peers).
|
||||
func (tab *Table) del(node *Node) {
|
||||
// delete removes an entry from the node table (used to evacuate
|
||||
// failed/non-bonded discovery peers).
|
||||
func (tab *Table) delete(node *Node) {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
|
||||
for i := range bucket.entries {
|
||||
if bucket.entries[i].ID == node.ID {
|
||||
@ -509,6 +522,27 @@ func (tab *Table) del(node *Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bucket) replace(n *Node, last *Node) bool {
|
||||
// Don't add if b already contains n.
|
||||
for i := range b.entries {
|
||||
if b.entries[i].ID == n.ID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Replace last if it is still the last entry or just add n if b
|
||||
// isn't full. If is no longer the last entry, it has either been
|
||||
// replaced with someone else or became active.
|
||||
if len(b.entries) == bucketSize && (last == nil || b.entries[bucketSize-1].ID != last.ID) {
|
||||
return false
|
||||
}
|
||||
if len(b.entries) < bucketSize {
|
||||
b.entries = append(b.entries, nil)
|
||||
}
|
||||
copy(b.entries[1:], b.entries)
|
||||
b.entries[0] = n
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *bucket) bump(n *Node) bool {
|
||||
for i := range b.entries {
|
||||
if b.entries[i].ID == n.ID {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user