Compare commits

...

108 Commits

Author SHA1 Message Date
fd512fa12c Merge pull request #1711 from Gustav-Simonsson/timestamp_big_int
Add tests for uncle timestamps and refactor timestamp type
(cherry picked from commit abce09954b)
2015-08-25 15:54:57 +02:00
dc3fb69dce Merge pull request #1710 from bas-vk/useragent
user agent messages were dumped in some cases
(cherry picked from commit a219159e7e)
2015-08-25 15:54:26 +02:00
d51d0022ce cmd/geth: bumped version 1.1.0 2015-08-20 21:43:36 +02:00
3793991c0e remove 0x 2015-08-20 18:50:47 +02:00
b884d6ebaa canary update 2015-08-20 18:38:21 +02:00
36f7fe61c3 core, tests: Double SUICIDE fix 2015-08-20 18:22:50 +02:00
54088b0b8b cmd/geth: bumped version 1.0.3 2015-08-20 13:08:08 +02:00
9fb7bc7443 geth: bumped version 1.0.2 2015-08-19 23:05:39 +02:00
e2d44814a5 Merge pull request #1694 from obscuren/hide-fdtrack
fdtrack: hide message
2015-08-19 13:50:54 -07:00
bd3a44cac9 Merge pull request #1652 from bas-vk/autoreconnect
rpc/comms: reconnect ipc client after write error
2015-08-19 13:29:51 -07:00
61a6911eeb Merge pull request #1689 from fjl/discover-ignore-temp-errors
p2p, p2p/discover: small fixes
2015-08-19 12:55:40 -07:00
9bf17eb05a rpc/comms reconnect ipc client after write error 2015-08-19 21:48:56 +02:00
269c5c7107 Revert "fdtrack: temporary hack for tracking file descriptor usage"
This reverts commit 5c949d3b3b.
2015-08-19 21:46:01 +02:00
382d35bf40 Merge pull request #1688 from karalabe/fix-double-imports
eth: fix an issue with pulling and inserting blocks twice
2015-08-19 08:19:37 -07:00
dd54fef898 p2p/discover: don't attempt to replace nodes that are being replaced
PR #1621 changed Table locking so the mutex is not held while a
contested node is being pinged. If multiple nodes ping the local node
during this time window, multiple ping packets will be sent to the
contested node. The changes in this commit prevent multiple packets by
tracking whether the node is being replaced.
2015-08-19 14:57:16 +02:00
edccc7ae34 p2p: continue listening after temporary errors 2015-08-19 14:39:04 +02:00
7d5ff770e2 p2p/discover: continue reading after temporary errors
Might solve #1579
2015-08-19 14:38:55 +02:00
c6a11fe372 Merge pull request #1680 from maran/fix_removedb
cmd/geth: Fix chain purging from cmd line
2015-08-19 05:17:22 -07:00
941920f2aa eth: fix an issue with pulling and inserting blocks twice 2015-08-19 15:14:26 +03:00
3b997c3e16 Merge pull request #1454 from ethersphere/frozen-cryptoclean
crypto: remove obsolete code
2015-08-19 02:39:02 -07:00
0737cbc5c1 Merge pull request #1683 from ethereum/travis
Switch from Coveralls to Codecov code coverage service
2015-08-18 14:26:14 -07:00
227ff4d2d6 Merge pull request #1682 from obscuren/readme-improvements
Updated README, Added CONTRIBUTING
2015-08-18 14:16:42 -07:00
18d450b2d0 Updated README, Added CONTRIBUTING 2015-08-18 23:00:15 +02:00
cc87551edc Codecov integration 2015-08-18 22:46:48 +02:00
d0dc1b4a60 Merge pull request #1681 from obscuren/miner-receipt-fix
core, miner: write miner receipts
2015-08-18 12:53:26 -07:00
b4369e1015 core, miner: write miner receipts 2015-08-18 21:46:26 +02:00
4d5501c46a cmd/geth: Fix chain purging from cmd line 2015-08-18 15:56:37 +02:00
d4da2f630e crypto: remove obsolete key files 2015-08-18 01:25:04 +02:00
e1da124415 Merge pull request #1675 from obscuren/submithashrate-change
rpc/api: return boolean value for eth_submitHashrate
2015-08-17 11:49:15 -07:00
36081505c4 Merge pull request #1673 from karalabe/fix-api-xeth-responses
rpc: update the xeth over RPC API to use the success/failure messages
2015-08-17 11:47:55 -07:00
2497f28aa9 Merge pull request #1627 from zsfelfoldi/gpo
GPO update
2015-08-17 06:37:58 -07:00
49ece3155c GPO update 2015-08-17 15:20:33 +02:00
8839fee415 rpc/api: return boolean value for eth_submitHashrate 2015-08-17 15:09:30 +02:00
ff1f6fa09d Merge pull request #1649 from maran/pending_tx_response
rpc/api: format pendingTx response. Fixes #1648
2015-08-17 06:02:08 -07:00
7ea30f18f9 Merge pull request #1674 from tgerring/bootnodes
Added SG bootnode
2015-08-17 05:56:38 -07:00
12805c738c Merge pull request #1667 from fjl/pretty-printer-improvements
jsre: pretty printer improvements
2015-08-17 05:55:09 -07:00
80b294c3c7 Update CPP pubkey 2015-08-17 14:51:27 +02:00
8884f856ef Added SG bootnode 2015-08-17 14:36:57 +02:00
309788de37 rpc: update the xeth over RPC API to use the success/failure messages 2015-08-17 14:04:20 +03:00
f6367548e4 Merge pull request #1654 from obscuren/call-gas
xeth: call fix when doing 'create'-like calls
2015-08-16 16:33:29 -07:00
1c3ca3ce6a xeth: max gas limit 2015-08-16 15:27:30 +02:00
8603ec7055 rpc/api: format pendingTx response. Fixes #1648 2015-08-16 11:12:22 +02:00
1086e2f298 jsre: fix annoying indentation when printing arrays of objects
The pretty printer, dumb as it is, printed arrays of objects as

  [{
    ...
    }]

With this change, they now print as:

  [{
    ...
  }]
2015-08-16 00:35:00 +01:00
49703bea0a jsre: bind the pretty printer to "inspect" in JS 2015-08-15 23:55:42 +01:00
59b28cfa31 Merge pull request #1663 from obscuren/issue-1662
xeth: added a transact mu
2015-08-15 14:55:04 -07:00
5c5c3930b7 Merge pull request #1659 from bas-vk/exec-output
Javascript --exec output
2015-08-15 06:23:29 -07:00
7bb5ac045e xeth: added a transact mu
Added a transact mutex. The transact mutex will fix an issue where
transactions were created with the same nonce resulting in some
transactions being dropped. This happened when two concurrent calls
would call the `Transact` method (which is OK) which would both call
`GetNonce`. While the managed is thread safe it does not help us in this
case.
2015-08-15 00:33:52 +02:00
cd81356ace Merge pull request #1658 from bas-vk/liner-ctrl-c
Clear current line on ctrl-C
2015-08-14 04:36:15 -07:00
c472b8f725 main clear current line on ctrl-C 2015-08-14 13:23:41 +02:00
3a66c4ed47 Merge pull request #1642 from ethereum/fix-js-console-windows
cmd/geth, jsre: restore command line editing on windows
2015-08-14 04:14:11 -07:00
29181003d4 Merge pull request #1655 from obscuren/db-merge-fix
eth, trie: removed key prefixing from state entries & merge db fix
2015-08-14 04:12:33 -07:00
87d1cde7e4 main print console output for js statement given by the exec argument 2015-08-14 13:06:34 +02:00
28b14d3e6d Merge pull request #1635 from bas-vk/useragent
support for user agents
2015-08-13 16:25:33 -07:00
73c4e6005c Merge pull request #1638 from obscuren/jit-fixes
core/vm: fixed jit error & added inline docs
2015-08-13 11:49:01 -07:00
b8ca0a830e eth, trie: removed key prefixing from state entries & merge db fix
Fixed database merge strategy to use the correct database. Due to a copy
paste fail when doing type evaluation the same database was being
iterated (chain), all others were ignored.

Removed state prefixing because {H(code): code} is stored in the same
database as the rest of the state.
2015-08-13 20:44:03 +02:00
a89cfe92cc Merge pull request #1470 from ebuchman/encHandshake
p2p: validate recovered ephemeral pubkey
2015-08-13 11:59:27 +02:00
0b0b31c7d2 Merge pull request #1651 from karalabe/rlp-boolean-support
rlp: boolean support
2015-08-13 11:10:26 +02:00
1d2420323c rlp: add support for boolean encoding/decoding 2015-08-13 12:05:39 +03:00
0dd6911c62 Merge pull request #1647 from fjl/fix-disc-reason
p2p: fix value of DiscSubprotocolError
2015-08-12 21:20:21 +02:00
28feafe7af Merge pull request #1646 from fjl/fix-client-identifier
cmd/geth: remove spaces in client identifier
2015-08-12 14:38:48 +02:00
0d10d5a0a5 p2p: fix value of DiscSubprotocolError
We had the wrong value (12) since forever.
2015-08-12 14:15:54 +02:00
31a2793662 cmd/geth: remove spaces in client identifier 2015-08-12 13:32:52 +02:00
f9cbd16f27 support for user agents 2015-08-12 12:22:16 +02:00
0ef80bb3d0 cmd/geth, jsre: restore command line editing on windows
PR #856 broke command line editing by wrapping stdout with a filter that
interprets ANSI escape sequences to fix colored printing on windows.
Implement the printer in Go instead so it can do its own
platform-dependent coloring.

As a nice side effect, the JS console is now noticeably more responsive
when printing results.

Fixes #1608
Fixes #1612
2015-08-12 12:04:00 +02:00
05c66529b2 Merge pull request #1621 from ethereum/fix-discover-hangs
p2p/discover: fix two major bugs in reply packet handling
2015-08-11 12:17:13 -07:00
9cacec70f9 cmd/evm, core/vm, tests: changed DisableVm to EnableVm 2015-08-11 18:43:22 +02:00
94b6f38869 Merge pull request #1641 from obscuren/web3-update
web3: updated
2015-08-11 08:55:55 -07:00
bf6ea2919d web3: updated 2015-08-11 17:17:20 +02:00
c32d6fdf74 Merge pull request #1640 from obscuren/trace-flag-ethtest
cmd/ethtest: added trace flag for debugging
2015-08-11 02:53:02 -07:00
67c8ccc309 cmd/ethtest: added trace flag for debugging 2015-08-11 11:46:52 +02:00
590c99a98f p2p/discover: fix UDP reply packet timeout handling
If the timeout fired (even just nanoseconds) before the deadline of the
next pending reply, the timer was not rescheduled. The timer would've
been rescheduled anyway once the next packet was sent, but there were
cases where no next packet could ever be sent due to the locking issue
fixed in the previous commit.

As timing-related bugs go, this issue had been present for a long time
and I could never reproduce it. The test added in this commit did
reproduce the issue on about one out of 15 runs.
2015-08-11 11:42:17 +02:00
01ed3fa1a9 p2p/discover: unlock the table during ping replacement
Table.mutex was being held while waiting for a reply packet, which
effectively made many parts of the whole stack block on that packet,
including the net_peerCount RPC call.
2015-08-11 11:42:17 +02:00
32395ddb89 core/vm: fixed jit error & added inline docs
opNumber did not create a new big int which could lead to the block's
number being modified.
2015-08-11 00:16:38 +02:00
2fcf7f1241 Merge pull request #1604 from obscuren/db-merge
core, eth, trie, xeth: merged state, chain, extra databases in one
2015-08-09 05:16:37 -07:00
07cb8092e7 Merge pull request #1611 from obscuren/expdiff-olympic-fix
cmd/utils, core: disable exp diff for olympic net
2015-08-09 05:15:13 -07:00
1cbd53add8 Merge pull request #1626 from obscuren/defaults-fix
cmd/geth, core/vm: setup vm settings and defaulted JIT disabled
2015-08-08 17:11:28 -07:00
eec38c5853 cmd/geth, core/vm: setup vm settings and defaulted JIT disabled 2015-08-09 02:06:16 +02:00
c93f0b9f4b Merge pull request #1490 from obscuren/jit-vm
core/vm: jit vm
2015-08-08 06:36:26 -07:00
a23478c0be core, eth, trie, xeth: merged state, chain, extra databases in one 2015-08-07 22:29:02 +02:00
312128384b Merge pull request #1620 from caktux/develop
string version for build server
2015-08-07 11:41:41 -07:00
3ccab5a1e8 string version for build server 2015-08-07 14:13:33 -04:00
dcb23bc3ab Merge pull request #1615 from obscuren/contract-addr-fix
xeth: fixed contract addr check
2015-08-07 05:38:02 -07:00
b6c5b3b4a7 xeth: fixed contract addr check 2015-08-07 14:32:06 +02:00
d7580f21f6 Merge pull request #1595 from obscuren/extra-data
cmd/geth, eth: added canonical extra data
2015-08-07 05:00:36 -07:00
b1fac4270d Merge pull request #1614 from obscuren/web3-finite-fix
web3: regression. Fixes #1613
2015-08-07 04:59:15 -07:00
ac697326a6 core/vm: reduced big int allocations
Reduced big int allocation by making stack items modifiable. Instead of
adding items such as `common.Big0` to the stack, `new(big.Int)` is
added instead. One must expect that any item that is added to the stack
might change.
2015-08-07 12:52:23 +02:00
184e9ae9a8 core, tests: reduced state copy by N calls
Reduced the amount of state copied that are required by N calls by doing
a balance check prior to any state modifications.
2015-08-07 12:52:23 +02:00
846f34f78b core/vm, tests: implemented semi-jit vm
* changed stack and removed stack ptr. Let go decide on slice reuse.
2015-08-07 12:52:17 +02:00
a33726b7db web3: regression. Fixes #1613 2015-08-07 12:33:12 +02:00
132df860d9 miner, rpc: added length check for extra data 2015-08-07 12:24:44 +02:00
785b3e7a57 cmd/geth, eth: added canonical extra data
Implemented canonical extra data according to
https://github.com/ethereum/wiki/wiki/Extra-Data
2015-08-07 12:24:32 +02:00
e89536ca0b Merge pull request #1596 from obscuren/submit-hashrate
miner, rpc: added submit hashrate for remote agents
2015-08-07 03:08:48 -07:00
ac10c9352e Merge pull request #1610 from obscuren/address-check
xeth: added address hex check and length check
2015-08-07 02:05:54 -07:00
cf7cef4293 xeth: added address hex check and length check 2015-08-07 09:52:12 +02:00
698e98d981 Merge pull request #1600 from ethereum/fix-tests-windows
Fix tests on windows
2015-08-06 12:39:29 -07:00
a3b8169938 Merge pull request #1603 from ebuchman/trie_hex_fix
trie: hex fix
2015-08-06 12:02:55 -07:00
46c9594081 trie: run codec tests, add benchmarks, faster 2015-08-06 14:04:16 -04:00
7baa5977c8 Merge pull request #1594 from ebuchman/trie_hex_fix
faster hex-prefix codec and string -> []byte
2015-08-06 08:44:19 -07:00
803096ca0f .gitattributes: add 2015-08-06 17:18:59 +02:00
6ee908848c p2p/nat: disable UPnP test on windows 2015-08-06 17:18:59 +02:00
3832019964 common/compiler, common/docserver, jsre: fix tests on windows 2015-08-06 17:18:59 +02:00
eae1191904 cmd/utils: fix path expansion on windows 2015-08-06 17:18:59 +02:00
78b101e15d common: remove windows path functions
They were unused and their tests failed on Windows.
2015-08-06 16:43:43 +02:00
74f6d90153 cmd/utils, core: disable exp diff for olympic net 2015-08-06 13:29:06 +02:00
c32073b11f miner, rpc: added submit hashrate for remote agents 2015-08-06 12:58:54 +02:00
b23b4dbd79 p2p/discover: close Table during testing
Not closing the table used to be fine, but now the table has a database.
2015-08-06 12:27:59 +02:00
c1d516546d faster hex-prefix codec and string -> []byte 2015-08-06 03:17:59 -04:00
37efd08b42 p2p: validate recovered ephemeral pubkey against checksum in decodeAuthMsg 2015-07-14 03:06:44 +00:00
150 changed files with 8163 additions and 4953 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -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
View 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
View File

@ -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"

View File

@ -0,0 +1,3 @@
language: go
go: 1.3

View 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
View File

@ -0,0 +1,151 @@
# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](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.
![Color](http://i.imgur.com/c1JI0lA.png)
## 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
View 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...)
}

View 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
View 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

View File

@ -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()
}

View File

@ -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
},
},
},
}
}

View File

@ -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)

View File

@ -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!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
## So Good!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
## 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)

View File

@ -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
}

View File

@ -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()
}
}

View 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

View 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.

View File

@ -0,0 +1,100 @@
[![GoDoc](https://godoc.org/github.com/shiena/ansicolor?status.svg)](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")
}
```
![screenshot](https://gist.githubusercontent.com/shiena/a1bada24b525314a7d5e/raw/c763aa7cda6e4fefaccf831e2617adc40b6151c7/main.png)
## 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

View 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
}

View 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)
}

View 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)
}

View 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)
}
}

View 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
}

View 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)
}
}

View 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")
}

View 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))
}

View File

@ -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

View File

@ -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."

View File

@ -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 | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=ARM%20Go%20develop%20branch)](https://build.ethdev.com/builders/ARM%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20Go%20develop%20branch)](https://build.ethdev.com/builders/Windows%20Go%20develop%20branch/builds/-1) | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=develop)](https://travis-ci.org/ethereum/go-ethereum) [![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.svg?branch=develop)](https://coveralls.io/r/ethereum/go-ethereum?branch=develop)
master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=ARM%20Go%20master%20branch)](https://build.ethdev.com/builders/ARM%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20Go%20master%20branch)](https://build.ethdev.com/builders/Windows%20Go%20master%20branch/builds/-1) | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum) [![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.svg?branch=master)](https://coveralls.io/r/ethereum/go-ethereum?branch=master)
develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=ARM%20Go%20develop%20branch)](https://build.ethdev.com/builders/ARM%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20Go%20develop%20branch)](https://build.ethdev.com/builders/Windows%20Go%20develop%20branch/builds/-1) | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=develop)](https://travis-ci.org/ethereum/go-ethereum) [![codecov.io](http://codecov.io/github/ethereum/go-ethereum/coverage.svg?branch=develop)](http://codecov.io/github/ethereum/go-ethereum?branch=develop)
master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=ARM%20Go%20master%20branch)](https://build.ethdev.com/builders/ARM%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20Go%20master%20branch)](https://build.ethdev.com/builders/Windows%20Go%20master%20branch/builds/-1) | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum) [![codecov.io](http://codecov.io/github/ethereum/go-ethereum/coverage.svg?branch=master)](http://codecov.io/github/ethereum/go-ethereum?branch=master)
[![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum)
[![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum)
[![Stories in Progress](https://badge.waffle.io/ethereum/go-ethereum.svg?label=in%20progress&title=In Progress)](http://waffle.io/ethereum/go-ethereum)
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/ethereum/go-ethereum)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](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

View File

@ -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

View File

@ -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 {

View File

@ -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)
}

View File

@ -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.

View File

@ -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)
}
}

View File

@ -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.0"
VersionMajor = 1
VersionMinor = 1
VersionPatch = 0
)
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, " ")

View File

@ -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 {

View File

@ -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))
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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())
}
}
}

View File

@ -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
}

View File

@ -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")
}
}
}

View File

@ -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
}

View File

@ -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), "/")
}
}

View File

@ -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))

View File

@ -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()

View File

@ -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,10 +56,9 @@ type BlockProcessor struct {
eventMux *event.TypeMux
}
func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
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,
@ -84,8 +82,6 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
}
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)
if err != nil {
@ -201,13 +197,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 +327,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 +338,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 +348,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)
}

View File

@ -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")
}

View File

@ -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,
}

View File

@ -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.

View File

@ -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 {

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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() {

View File

@ -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)
}

View File

@ -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 }

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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
View 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
View 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
View 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
}

View File

@ -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

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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()

View File

@ -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:

View File

@ -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)
)

View File

@ -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 {

View File

@ -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)
}
}()
}

View File

@ -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")
}

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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)

View File

@ -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
View 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
}

View File

@ -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 {

View File

@ -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()
}
}
}

View File

@ -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)) != 1 {
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

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -35,6 +35,7 @@ func TestTable_pingReplace(t *testing.T) {
doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
transport := newPingRecorder()
tab := newTable(transport, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close()
pingSender := newNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
// fill up the sender's bucket.
@ -158,9 +159,7 @@ func newPingRecorder() *pingRecorder {
func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
panic("findnode called on pingRecorder")
}
func (t *pingRecorder) close() {
panic("close called on pingRecorder")
}
func (t *pingRecorder) close() {}
func (t *pingRecorder) waitping(from NodeID) error {
return nil // remote always pings
}
@ -179,7 +178,8 @@ func TestTable_closest(t *testing.T) {
test := func(test *closeTest) bool {
// for any node table, Target and N
tab := newTable(nil, test.Self, &net.UDPAddr{}, "")
tab.add(test.All)
defer tab.Close()
tab.stuff(test.All)
// check that doClosest(Target, N) returns nodes
result := tab.closest(test.Target, test.N).entries
@ -237,9 +237,10 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
}
test := func(buf []*Node) bool {
tab := newTable(nil, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close()
for i := 0; i < len(buf); i++ {
ld := cfg.Rand.Intn(len(tab.buckets))
tab.add([]*Node{nodeAtDistance(tab.self.sha, ld)})
tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)})
}
gotN := tab.ReadRandomNodes(buf)
if gotN != tab.len() {
@ -279,6 +280,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
func TestTable_Lookup(t *testing.T) {
self := nodeAtDistance(common.Hash{}, 0)
tab := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "")
defer tab.Close()
// lookup on empty table returns no nodes
if results := tab.Lookup(lookupTestnet.target); len(results) > 0 {
@ -286,7 +288,7 @@ func TestTable_Lookup(t *testing.T) {
}
// seed table with initial node (otherwise lookup will terminate immediately)
seed := newNode(lookupTestnet.dists[256][0], net.IP{}, 256, 0)
tab.add([]*Node{seed})
tab.stuff([]*Node{seed})
results := tab.Lookup(lookupTestnet.target)
t.Logf("results:")

View File

@ -18,6 +18,7 @@ package discover
import (
"bytes"
"container/list"
"crypto/ecdsa"
"errors"
"fmt"
@ -25,7 +26,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/crypto"
"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/nat"
@ -43,6 +43,7 @@ var (
errUnsolicitedReply = errors.New("unsolicited reply")
errUnknownNode = errors.New("unknown node")
errTimeout = errors.New("RPC timeout")
errClockWarp = errors.New("reply deadline too far in the future")
errClosed = errors.New("socket closed")
)
@ -198,7 +199,6 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
if err != nil {
return nil, err
}
fdtrack.Open("p2p")
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return nil, err
@ -236,7 +236,6 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
func (t *udp) close() {
close(t.closing)
fdtrack.Close("p2p")
t.conn.Close()
// TODO: wait for the loops to end.
}
@ -296,7 +295,7 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-
}
func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
matched := make(chan bool)
matched := make(chan bool, 1)
select {
case t.gotreply <- reply{from, ptype, req, matched}:
// loop will handle it
@ -310,68 +309,82 @@ func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
// the refresh timer and the pending reply queue.
func (t *udp) loop() {
var (
pending []*pending
nextDeadline time.Time
timeout = time.NewTimer(0)
refresh = time.NewTicker(refreshInterval)
plist = list.New()
timeout = time.NewTimer(0)
nextTimeout *pending // head of plist when timeout was last reset
refresh = time.NewTicker(refreshInterval)
)
<-timeout.C // ignore first timeout
defer refresh.Stop()
defer timeout.Stop()
rearmTimeout := func() {
now := time.Now()
if len(pending) == 0 || now.Before(nextDeadline) {
resetTimeout := func() {
if plist.Front() == nil || nextTimeout == plist.Front().Value {
return
}
nextDeadline = pending[0].deadline
timeout.Reset(nextDeadline.Sub(now))
// Start the timer so it fires when the next pending reply has expired.
now := time.Now()
for el := plist.Front(); el != nil; el = el.Next() {
nextTimeout = el.Value.(*pending)
if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout {
timeout.Reset(dist)
return
}
// Remove pending replies whose deadline is too far in the
// future. These can occur if the system clock jumped
// backwards after the deadline was assigned.
nextTimeout.errc <- errClockWarp
plist.Remove(el)
}
nextTimeout = nil
timeout.Stop()
}
for {
resetTimeout()
select {
case <-refresh.C:
go t.refresh()
case <-t.closing:
for _, p := range pending {
p.errc <- errClosed
for el := plist.Front(); el != nil; el = el.Next() {
el.Value.(*pending).errc <- errClosed
}
pending = nil
return
case p := <-t.addpending:
p.deadline = time.Now().Add(respTimeout)
pending = append(pending, p)
rearmTimeout()
plist.PushBack(p)
case r := <-t.gotreply:
var matched bool
for i := 0; i < len(pending); i++ {
if p := pending[i]; p.from == r.from && p.ptype == r.ptype {
for el := plist.Front(); el != nil; el = el.Next() {
p := el.Value.(*pending)
if p.from == r.from && p.ptype == r.ptype {
matched = true
// Remove the matcher if its callback indicates
// that all replies have been received. This is
// required for packet types that expect multiple
// reply packets.
if p.callback(r.data) {
// callback indicates the request is done, remove it.
p.errc <- nil
copy(pending[i:], pending[i+1:])
pending = pending[:len(pending)-1]
i--
plist.Remove(el)
}
}
}
r.matched <- matched
case now := <-timeout.C:
// notify and remove callbacks whose deadline is in the past.
i := 0
for ; i < len(pending) && now.After(pending[i].deadline); i++ {
pending[i].errc <- errTimeout
nextTimeout = nil
// Notify and remove callbacks whose deadline is in the past.
for el := plist.Front(); el != nil; el = el.Next() {
p := el.Value.(*pending)
if now.After(p.deadline) || now.Equal(p.deadline) {
p.errc <- errTimeout
plist.Remove(el)
}
}
if i > 0 {
copy(pending, pending[i:])
pending = pending[:len(pending)-i]
}
rearmTimeout()
}
}
}
@ -385,7 +398,7 @@ const (
var (
headSpace = make([]byte, headSize)
// Neighbors responses are sent across multiple packets to
// Neighbors replies are sent across multiple packets to
// stay below the 1280 byte limit. We compute the maximum number
// of entries by stuffing a packet until it grows too large.
maxNeighbors int
@ -442,6 +455,10 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte,
return packet, nil
}
type tempError interface {
Temporary() bool
}
// readLoop runs in its own goroutine. it handles incoming UDP packets.
func (t *udp) readLoop() {
defer t.conn.Close()
@ -451,7 +468,13 @@ func (t *udp) readLoop() {
buf := make([]byte, 1280)
for {
nbytes, from, err := t.conn.ReadFromUDP(buf)
if err != nil {
if tempErr, ok := err.(tempError); ok && tempErr.Temporary() {
// Ignore temporary read errors.
glog.V(logger.Debug).Infof("Temporary read error: %v", err)
continue
} else if err != nil {
// Shut down the loop for permament errors.
glog.V(logger.Debug).Infof("Read error: %v", err)
return
}
t.handlePacket(from, buf[:nbytes])

Some files were not shown because too many files have changed in this diff Show More