diff options
Diffstat (limited to 'internal')
-rw-r--r-- | internal/build/env.go | 116 | ||||
-rw-r--r-- | internal/build/util.go | 23 | ||||
-rw-r--r-- | internal/debug/flags.go | 9 | ||||
-rw-r--r-- | internal/ethapi/api.go | 148 | ||||
-rw-r--r-- | internal/ethapi/tracer_test.go | 22 | ||||
-rw-r--r-- | internal/web3ext/web3ext.go | 30 |
6 files changed, 310 insertions, 38 deletions
diff --git a/internal/build/env.go b/internal/build/env.go new file mode 100644 index 000000000..cd3355092 --- /dev/null +++ b/internal/build/env.go @@ -0,0 +1,116 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package build + +import ( + "flag" + "fmt" + "os" +) + +var ( + // These flags override values in build env. + GitCommitFlag = flag.String("git-commit", "", `Overrides git commit hash embedded into executables`) + GitBranchFlag = flag.String("git-branch", "", `Overrides git branch being built`) + GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`) + BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`) + PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`) +) + +// Environment contains metadata provided by the build environment. +type Environment struct { + Name string // name of the environment + Repo string // name of GitHub repo + Commit, Branch, Tag string // Git info + Buildnum string + IsPullRequest bool +} + +func (env Environment) String() string { + return fmt.Sprintf("%s env (commit:%s branch:%s tag:%s buildnum:%s pr:%t)", + env.Name, env.Commit, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest) +} + +// Env returns metadata about the current CI environment, falling back to LocalEnv +// if not running on CI. +func Env() Environment { + switch { + case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true": + return Environment{ + Name: "travis", + Repo: os.Getenv("TRAVIS_REPO_SLUG"), + Commit: os.Getenv("TRAVIS_COMMIT"), + Branch: os.Getenv("TRAVIS_BRANCH"), + Tag: os.Getenv("TRAVIS_TAG"), + Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"), + IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false", + } + case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True": + return Environment{ + Name: "appveyor", + Repo: os.Getenv("APPVEYOR_REPO_NAME"), + Commit: os.Getenv("APPVEYOR_REPO_COMMIT"), + Branch: os.Getenv("APPVEYOR_REPO_BRANCH"), + Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"), + Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"), + IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", + } + default: + return LocalEnv() + } +} + +// LocalEnv returns build environment metadata gathered from git. +func LocalEnv() Environment { + env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"}) + if _, err := os.Stat(".git"); err != nil { + return env + } + if env.Commit == "" { + env.Commit = RunGit("rev-parse", "HEAD") + } + if env.Branch == "" { + if b := RunGit("rev-parse", "--abbrev-ref", "HEAD"); b != "HEAD" { + env.Branch = b + } + } + // Note that we don't get the current git tag. It would slow down + // builds and isn't used by anything. + return env +} + +func applyEnvFlags(env Environment) Environment { + if !flag.Parsed() { + panic("you need to call flag.Parse before Env or LocalEnv") + } + if *GitCommitFlag != "" { + env.Commit = *GitCommitFlag + } + if *GitBranchFlag != "" { + env.Branch = *GitBranchFlag + } + if *GitTagFlag != "" { + env.Tag = *GitTagFlag + } + if *BuildnumFlag != "" { + env.Buildnum = *BuildnumFlag + } + if *PullRequestFlag { + env.IsPullRequest = true + } + return env +} diff --git a/internal/build/util.go b/internal/build/util.go index eead824b2..a821cd7f2 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -29,9 +29,7 @@ import ( "text/template" ) -var ( - DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") -) +var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") // MustRun executes the given command and exits the host process for // any error. @@ -57,18 +55,18 @@ func GOPATH() string { if len(path) == 0 { log.Fatal("GOPATH is not set") } - // Ensure Godeps workspace is present in the path. - godeps, _ := filepath.Abs(filepath.Join("Godeps", "_workspace")) + // Ensure that our internal vendor folder in on GOPATH + vendor, _ := filepath.Abs(filepath.Join("build", "_vendor")) for _, dir := range path { - if dir == godeps { + if dir == vendor { return strings.Join(path, string(filepath.ListSeparator)) } } - newpath := append(path[:1], godeps) - newpath = append(newpath, path[1:]...) + newpath := append(path[:1], append([]string{vendor}, path[1:]...)...) return strings.Join(newpath, string(filepath.ListSeparator)) } +// VERSION returns the content of the VERSION file. func VERSION() string { version, err := ioutil.ReadFile("VERSION") if err != nil { @@ -77,10 +75,8 @@ func VERSION() string { return string(bytes.TrimSpace(version)) } -func GitCommit() string { - return RunGit("rev-parse", "HEAD") -} - +// RunGit runs a git subcommand and returns its output. +// The command must complete successfully. func RunGit(args ...string) string { cmd := exec.Command("git", args...) var stdout, stderr bytes.Buffer @@ -94,12 +90,13 @@ func RunGit(args ...string) string { return strings.TrimSpace(stdout.String()) } -// Render renders the given template file. +// Render renders the given template file into outputFile. func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) { tpl := template.Must(template.ParseFiles(templateFile)) render(tpl, outputFile, outputPerm, x) } +// RenderString renders the given template string into outputFile. func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) { tpl := template.Must(template.New("").Parse(templateContent)) render(tpl, outputFile, outputPerm, x) diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 9fc5fc4fe..ed17f87c4 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -52,6 +52,11 @@ var ( Usage: "pprof HTTP server listening port", Value: 6060, } + pprofAddrFlag = cli.StringFlag{ + Name: "pprofaddr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + } memprofilerateFlag = cli.IntFlag{ Name: "memprofilerate", Usage: "Turn on memory profiling with the given rate", @@ -74,7 +79,7 @@ var ( // Flags holds all command-line flags required for debugging. var Flags = []cli.Flag{ verbosityFlag, vmoduleFlag, backtraceAtFlag, - pprofFlag, pprofPortFlag, + pprofFlag, pprofAddrFlag, pprofPortFlag, memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, } @@ -101,7 +106,7 @@ func Setup(ctx *cli.Context) error { // pprof server if ctx.GlobalBool(pprofFlag.Name) { - address := fmt.Sprintf("127.0.0.1:%d", ctx.GlobalInt(pprofPortFlag.Name)) + address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) go func() { glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address) glog.Errorln(http.ListenAndServe(address, nil)) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9a97be25f..0e8e905aa 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" "golang.org/x/net/context" ) @@ -286,6 +287,66 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs return submitTransaction(ctx, s.b, tx, signature) } +// signHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. The hash is calulcated with: +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +func signHash(message string) []byte { + data := common.FromHex(message) + // Give context to the signed message. This prevents an adversery to sign a tx. + // It has no cryptographic purpose. + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + // Always hash, this prevents choosen plaintext attacks that can extract key information + return crypto.Keccak256([]byte(msg)) +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (s *PrivateAccountAPI) Sign(ctx context.Context, message string, addr common.Address, passwd string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignWithPassphrase(addr, passwd, hash) + if err != nil { + return "0x", err + } + return common.ToHex(signature), nil +} + +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (s *PrivateAccountAPI) EcRecover(ctx context.Context, message string, signature string) (common.Address, error) { + var ( + hash = signHash(message) + sig = common.FromHex(signature) + ) + + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + + // see crypto.Ecrecover description + if sig[64] == 27 || sig[64] == 28 { + sig[64] -= 27 + } + + rpk, err := crypto.Ecrecover(hash, sig) + if err != nil { + return common.Address{}, err + } + + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + + return recoveredAddr, nil +} + // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // and will be removed in the future. It primary goal is to give clients time to update. func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { @@ -597,7 +658,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "gasUsed": rpc.NewHexNumber(head.GasUsed), "timestamp": rpc.NewHexNumber(head.Time), "transactionsRoot": head.TxHash, - "receiptRoot": head.ReceiptHash, + "receiptsRoot": head.ReceiptHash, } if inclTx { @@ -699,6 +760,16 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti return nil, nil } +// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. +func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexBytes, error) { + if txIndex >= 0 && txIndex < len(b.Transactions()) { + tx := b.Transactions()[txIndex] + return rlp.EncodeToBytes(tx) + } + + return nil, nil +} + // newRPCTransaction returns a transaction that will serialize to the RPC representation. func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { for idx, tx := range b.Transactions() { @@ -770,6 +841,22 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context return nil, nil } +// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + +// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { state, _, err := s.b.StateAndHeaderByNumber(blockNr) @@ -835,6 +922,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txH return nil, nil } +// GetRawTransactionByHash returns the bytes of the transaction for the given hash. +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { + var tx *types.Transaction + var err error + + if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } else if tx == nil { + return nil, nil + } + + return rlp.EncodeToBytes(tx) +} + // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { receipt := core.GetReceipt(s.b.ChainDb(), txHash) @@ -887,7 +989,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma // sign is a helper function that signs a transaction with the private key of the given address. func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - signature, err := s.b.AccountManager().Sign(addr, tx.SigHash().Bytes()) + signature, err := s.b.AccountManager().SignEthereum(addr, tx.SigHash().Bytes()) if err != nil { return nil, err } @@ -969,7 +1071,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signature, err := s.b.AccountManager().Sign(args.From, tx.SigHash().Bytes()) + signature, err := s.b.AccountManager().SignEthereum(args.From, tx.SigHash().Bytes()) if err != nil { return common.Hash{}, err } @@ -1003,11 +1105,16 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return tx.Hash().Hex(), nil } -// Sign signs the given hash using the key that matches the address. The key must be -// unlocked in order to sign the hash. -func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (string, error) { - signature, error := s.b.AccountManager().Sign(addr, hash[:]) - return common.ToHex(signature), error +// Sign calculates an ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). +// +// The account associated with addr must be unlocked. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign +func (s *PublicTransactionPoolAPI) Sign(addr common.Address, message string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignEthereum(addr, hash) + return common.ToHex(signature), err } // SignTransactionArgs represents the arguments to sign a transaction. @@ -1170,8 +1277,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. -func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - +func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { pending := s.b.GetPoolTransactions() for _, p := range pending { if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { @@ -1184,9 +1290,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, var newTx *types.Transaction if tx.tx.To() == nil { - newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } else { - newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } signedTx, err := s.sign(tx.From, newTx) @@ -1281,6 +1387,24 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { return ldb.LDB().GetProperty(property) } +func (api *PrivateDebugAPI) ChaindbCompact() error { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return fmt.Errorf("chaindbCompact does not work for memory databases") + } + for b := byte(0); b < 255; b++ { + glog.V(logger.Info).Infof("compacting chain DB range 0x%0.2X-0x%0.2X", b, b+1) + err := ldb.LDB().CompactRange(util.Range{Start: []byte{b}, Limit: []byte{b + 1}}) + if err != nil { + glog.Errorf("compaction error: %v", err) + return err + } + } + return nil +} + // SetHead rewinds the head of the blockchain to a previous block. func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { api.b.SetHead(uint64(number.Int64())) diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index 301ff4840..577b426f1 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -26,11 +26,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" ) type ruleSet struct{} -func (self *ruleSet) IsHomestead(*big.Int) bool { return true } +func (self *ruleSet) IsHomestead(*big.Int) bool { return true } +func (*ruleSet) GasTable(*big.Int) params.GasTable { return params.GasTableHomesteadGasRepriceFork } type Env struct { gasLimit *big.Int @@ -50,14 +52,14 @@ func (self *Env) Origin() common.Address { return common.Address{} } func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) } //func (self *Env) PrevHash() []byte { return self.parent } -func (self *Env) Coinbase() common.Address { return common.Address{} } -func (self *Env) MakeSnapshot() vm.Database { return nil } -func (self *Env) SetSnapshot(vm.Database) {} -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) Db() vm.Database { return nil } -func (self *Env) GasLimit() *big.Int { return self.gasLimit } -func (self *Env) VmType() vm.Type { return vm.StdVmTy } +func (self *Env) Coinbase() common.Address { return common.Address{} } +func (self *Env) SnapshotDatabase() int { return 0 } +func (self *Env) RevertToSnapshot(int) {} +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) Db() vm.Database { return nil } +func (self *Env) GasLimit() *big.Int { return self.gasLimit } +func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) } @@ -93,7 +95,7 @@ func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } func (account) ReturnGas(*big.Int, *big.Int) {} -func (account) SetCode([]byte) {} +func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func runTrace(tracer *JavascriptTracer) (interface{}, error) { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 04b13e483..c8a0cac8c 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -338,6 +338,10 @@ web3._extend({ outputFormatter: console.log }), new web3._extend.Method({ + name: 'chaindbCompact', + call: 'debug_chaindbCompact', + }), + new web3._extend.Method({ name: 'metrics', call: 'debug_metrics', params: 1 @@ -468,6 +472,19 @@ web3._extend({ call: 'eth_submitTransaction', params: 1, inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'getRawTransaction', + call: 'eth_getRawTransactionByHash', + params: 1 + }), + new web3._extend.Method({ + name: 'getRawTransactionFromBlock', + call: function(args) { + return (web3._extend.utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getRawTransactionByBlockHashAndIndex' : 'eth_getRawTransactionByBlockNumberAndIndex'; + }, + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex] }) ], properties: @@ -570,9 +587,20 @@ web3._extend({ call: 'personal_sendTransaction', params: 2, inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] + }), + new web3._extend.Method({ + name: 'sign', + call: 'personal_sign', + params: 3, + inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null] + }), + new web3._extend.Method({ + name: 'ecRecover', + call: 'personal_ecRecover', + params: 2 }) ] -}); +}) ` const RPC_JS = ` |