diff --git a/cmd/status/library.go b/cmd/status/library.go index 030df9836..f14c19e36 100644 --- a/cmd/status/library.go +++ b/cmd/status/library.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "github.com/status-im/status-go/geth" "github.com/status-im/status-go/jail" ) diff --git a/cmd/status/utils.go b/cmd/status/utils.go index 7921dae08..44609cc07 100644 --- a/cmd/status/utils.go +++ b/cmd/status/utils.go @@ -22,10 +22,10 @@ import ( const ( testDataDir = "../../.ethereumtest" testNodeSyncSeconds = 120 - testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4" - testAddressPassword = "asdf" + testAddress = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + testAddressPassword = "asdfasdf" newAccountPassword = "badpassword" - testAddress1 = "0xf82da7547534045b4e00442bc89e16186cf8c272" + testAddress1 = "0xadd4d1d02e71c7360c53296968e59d57fd15e2ba" testStatusJsFile = "../../jail/testdata/status.js" ) diff --git a/data/morden-test-account.pk b/data/morden-test-account.pk new file mode 100644 index 000000000..7ea0a9af4 --- /dev/null +++ b/data/morden-test-account.pk @@ -0,0 +1 @@ +{"address":"89b50b2b26947ccad43accaef76c21d175ad85f4","crypto":{"cipher":"aes-128-ctr","ciphertext":"cc8f600a59f8c5ac3d6ab849722a6602f61de0adc5c9617a1cd014d3d9638a95","cipherparams":{"iv":"836bb6b5df64c29a84f95e7628510e71"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4cc1f86eaa707ee37dc3b686712f09be49b7175a2046c63cfd1d7a3e7ebee8ab"},"mac":"164f2e53c67b5c85267b79e4fc8b2f9117a66b5e0546933403d85d52fffa1f52"},"id":"3ade037d-722b-467a-ad8e-2bcae28b9642","version":3,"whisperenabled":true} diff --git a/data/static-nodes.json b/data/static-nodes.json index 4a02af7e4..00f81dbb2 100644 --- a/data/static-nodes.json +++ b/data/static-nodes.json @@ -1,7 +1,6 @@ [ - "enode://b8d29479eb403aa190ecc45157c74535a4015e00a4162a45a7d5e5110d4bf025bff3a65267d086d937a9bf5f45b08329bac6b1029f28eef3337411f38ec9bf81@162.243.63.248:30303", - "enode://3c0ab251e18c979a3358644f49bcadf264186847aeee4fe80de60da0d196ce879c913bc1c9b5e750847c0d7e63a707a1cf5153c4f242da0d058e42fdf985ef62@198.199.105.122:30303", - "enode://fc3065bb80bfced98a01441718e2b70a0353f023b9da3d57beb8f96a827402d23702b3a461e1c1b6c7a208cb09cc0aea9b7c42bf953bb8f732529c198b158db4@95.85.40.211:30303", - "enode://5ffa3a39f95614d881e07d24e265865218c45fe73b3a5f5d05868190e385cbf60d03ac8beaa4c31b7ee84a0ec947f22c969e2dd1783041a4d7381f7774c74526@188.166.229.119:30303", - "enode://3c0ab251e18c979a3358644f49bcadf264186847aeee4fe80de60da0d196ce879c913bc1c9b5e750847c0d7e63a707a1cf5153c4f242da0d058e42fdf985ef62@139.59.212.114:30303" + "enode://e19d89e6faf2772e2f250e9625478ee7f313fcc0bb5e9310d5d407371496d9d7d73ccecd9f226cc2a8be34484525f72ba9db9d26f0222f4efc3c6d9d995ee224@198.199.105.122:30303", + "enode://5f23bf4913dd005ce945648cb12d3ef970069818d8563a3fe054e5e1dc3898b9cb83e0af1f51b2dce75eaffc76e93f996caf538e21c5b64db5fa324958d59630@95.85.40.211:30303", + "enode://b9de2532421f15ac55da9d9a7cddc0dc08b0d646d631fd7ab2a170bd2163fb86b095dd8bde66b857592812f7cd9539f2919b6c64bc1a784a1d1c6ec8137681ed@188.166.229.119:30303", + "enode://1ad53266faaa9258ae71eef4d162022ba0d39498e1a3488e6c65fd86e0fb528e2aa68ad0e199da69fd39f4a3a38e9e8e95ac53ba5cc7676dfeaacf5fd6c0ad27@139.59.212.114:30303" ] diff --git a/data/test-account.pk b/data/test-account.pk index 7ea0a9af4..20d5bf033 100644 --- a/data/test-account.pk +++ b/data/test-account.pk @@ -1 +1 @@ -{"address":"89b50b2b26947ccad43accaef76c21d175ad85f4","crypto":{"cipher":"aes-128-ctr","ciphertext":"cc8f600a59f8c5ac3d6ab849722a6602f61de0adc5c9617a1cd014d3d9638a95","cipherparams":{"iv":"836bb6b5df64c29a84f95e7628510e71"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4cc1f86eaa707ee37dc3b686712f09be49b7175a2046c63cfd1d7a3e7ebee8ab"},"mac":"164f2e53c67b5c85267b79e4fc8b2f9117a66b5e0546933403d85d52fffa1f52"},"id":"3ade037d-722b-467a-ad8e-2bcae28b9642","version":3,"whisperenabled":true} +{"address":"adaf150b905cf5e6a778e553e15a139b6618bbb7","crypto":{"cipher":"aes-128-ctr","ciphertext":"e6b4a87b62eca6b654b45ec806a19b5a8fa2ee1b39d2ca17406f11fb81428455","cipherparams":{"iv":"863515dcd60f8b9fec7d5ba59f2a895e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"83e4d852dbd1d4bb93c1989736dd4cf4443b0842978a6eec3069a13406cb3605"},"mac":"7d00f96aeb1684717248fbcbdfec2d6da69dadea60e65239d20642e39097b923"},"id":"bc042f57-ad91-4459-a056-607ce80a760d","version":3,"whisperenabled":true,"extendedkey":{"cipher":"","ciphertext":"","cipherparams":{"iv":""},"kdf":"","kdfparams":null,"mac":""},"subaccountindex":0} \ No newline at end of file diff --git a/geth/node.go b/geth/node.go index f5243b90b..1dcd090cd 100644 --- a/geth/node.go +++ b/geth/node.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) @@ -38,7 +38,8 @@ const ( versionPatch = 0 // Patch version component of the current release versionMeta = "unstable" // Version metadata to append to the version string - RPCPort = 8545 // RPC port (replaced in unit tests) + RPCPort = 8545 // RPC port (replaced in unit tests) + NetworkPort = 30303 EventNodeStarted = "node.started" ) @@ -126,6 +127,7 @@ func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node { set.String("rpcapi", "db,eth,net,web3,shh,personal,admin", "rpc api(s)") set.String("datadir", datadir, "data directory for geth") set.String("logdir", datadir, "log dir for glog") + set.Int("port", NetworkPort, "network listening port") m.ctx = cli.NewContext(nil, set, nil) utils.DebugSetup(m.ctx) diff --git a/geth/node_test.go b/geth/node_test.go index c25f71cb3..0568c39bb 100644 --- a/geth/node_test.go +++ b/geth/node_test.go @@ -10,10 +10,10 @@ import ( ) const ( - testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4" - testAddressPassword = "asdf" + testAddress = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + testAddressPassword = "asdfasdf" newAccountPassword = "badpassword" - testAddress1 = "0xf82da7547534045b4e00442bc89e16186cf8c272" + testAddress1 = "0xadd4d1d02e71c7360c53296968e59d57fd15e2ba" whisperMessage1 = "test message 1 (K1 -> K1)" whisperMessage2 = "test message 2 (K1 -> '')" diff --git a/geth/whisper.go b/geth/whisper.go index 4548108e2..19125e8da 100644 --- a/geth/whisper.go +++ b/geth/whisper.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" ) var ( diff --git a/geth/whisper_test.go b/geth/whisper_test.go index 358735867..57aff0ad8 100644 --- a/geth/whisper_test.go +++ b/geth/whisper_test.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "github.com/status-im/status-go/geth" ) diff --git a/jail/jail_test.go b/jail/jail_test.go index 9c27a4dbc..69eafc9cd 100644 --- a/jail/jail_test.go +++ b/jail/jail_test.go @@ -13,8 +13,8 @@ import ( ) const ( - TEST_ADDRESS = "0x89b50b2b26947ccad43accaef76c21d175ad85f4" - TEST_ADDRESS_PASSWORD = "asdf" + TEST_ADDRESS = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + TEST_ADDRESS_PASSWORD = "asdfasdf" CHAT_ID_INIT = "CHAT_ID_INIT_TEST" CHAT_ID_CALL = "CHAT_ID_CALL_TEST" CHAT_ID_SEND = "CHAT_ID_CALL_SEND" diff --git a/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s b/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s new file mode 100644 index 000000000..66109f4f3 --- /dev/null +++ b/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s @@ -0,0 +1,6 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// This file is intentionally empty. +// It's a workaround for https://github.com/golang/go/issues/15006 diff --git a/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go b/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go new file mode 100644 index 000000000..efc1b92a6 --- /dev/null +++ b/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go @@ -0,0 +1,24 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// Package monotime provides a fast monotonic clock source. +package monotime + +import ( + _ "unsafe" // required to use //go:linkname +) + +//go:noescape +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +// Now returns the current time in nanoseconds from a monotonic clock. +// The time returned is based on some arbitrary platform-specific point in the +// past. The time returned is guaranteed to increase monotonically at a +// constant rate, unlike time.Now() from the Go standard library, which may +// slow down, speed up, jump forward or backward, due to NTP activity or leap +// seconds. +func Now() uint64 { + return uint64(nanotime()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go index c127cd7a9..2efac1307 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go @@ -98,28 +98,46 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { case HashTy: // hash must be of slice hash refSlice = reflect.ValueOf([]common.Hash(nil)) case FixedBytesTy: - refSlice = reflect.ValueOf([]byte(nil)) + refSlice = reflect.ValueOf([][]byte(nil)) default: // no other types are supported return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) } - // get the offset which determines the start of this array ... - offset := int(common.BytesToBig(output[index : index+32]).Uint64()) - if offset+32 > len(output) { - return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + + var slice []byte + var size int + var offset int + if t.Type.IsSlice { + + // get the offset which determines the start of this array ... + offset = int(common.BytesToBig(output[index : index+32]).Uint64()) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + } + + slice = output[offset:] + // ... starting with the size of the array in elements ... + size = int(common.BytesToBig(slice[:32]).Uint64()) + slice = slice[32:] + // ... and make sure that we've at the very least the amount of bytes + // available in the buffer. + if size*32 > len(slice) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) + } + + // reslice to match the required size + slice = slice[:(size * 32)] + } else if t.Type.IsArray { + //get the number of elements in the array + size = t.Type.SliceSize + + //check to make sure array size matches up + if index+32*size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size) + } + //slice is there for a fixed amount of times + slice = output[index : index+size*32] } - slice := output[offset:] - // ... starting with the size of the array in elements ... - size := int(common.BytesToBig(slice[:32]).Uint64()) - slice = slice[32:] - // ... and make sure that we've at the very least the amount of bytes - // available in the buffer. - if size*32 > len(slice) { - return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) - } - - // reslice to match the required size - slice = slice[:(size * 32)] for i := 0; i < size; i++ { var ( inter interface{} // interface type @@ -136,6 +154,8 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { inter = common.BytesToAddress(returnOutput) case HashTy: inter = common.BytesToHash(returnOutput) + case FixedBytesTy: + inter = returnOutput } // append the item to our reflect slice refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go index 2cf22768c..a20852fca 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go @@ -48,15 +48,15 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { keyAddr := crypto.PubkeyToAddress(key.PublicKey) return &TransactOpts{ From: keyAddr, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { if address != keyAddr { return nil, errors.New("not authorized to sign this account") } - signature, err := crypto.Sign(tx.SigHash().Bytes(), key) + signature, err := crypto.SignEthereum(signer.Hash(tx).Bytes(), key) if err != nil { return nil, err } - return tx.WithSignature(signature) + return tx.WithSignature(signer, signature) }, } } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go index a4e90914f..4509e222d 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go index 74203a468..00a8cd3e9 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -31,11 +31,12 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) -var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} +var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) @@ -51,6 +52,8 @@ type SimulatedBackend struct { mu sync.Mutex pendingBlock *types.Block // Currently pending block that will be imported on request pendingState *state.StateDB // Currently pending state that will be the active on on request + + config *params.ChainConfig } // NewSimulatedBackend creates a new binding backend using a simulated blockchain @@ -85,7 +88,7 @@ func (b *SimulatedBackend) Rollback() { } func (b *SimulatedBackend) rollback() { - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } @@ -234,7 +237,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa b.mu.Lock() defer b.mu.Unlock() - sender, err := tx.From() + sender, err := types.Sender(types.HomesteadSigner{}, tx) if err != nil { panic(fmt.Errorf("invalid transaction: %v", err)) } @@ -243,7 +246,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) } - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) } @@ -259,12 +262,11 @@ type callmsg struct { ethereum.CallMsg } -func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.CallMsg.To } -func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } -func (m callmsg) Value() *big.Int { return m.CallMsg.Value } -func (m callmsg) Data() []byte { return m.CallMsg.Data } +func (m callmsg) From() common.Address { return m.CallMsg.From } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go index 965f51e85..7df02e83f 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -31,7 +31,7 @@ import ( // SignerFn is a signer function callback when a contract requires a method to // sign the transaction before submission. -type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) +type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) // CallOpts is the collection of options to fine tune a contract call request. type CallOpts struct { @@ -214,7 +214,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") } - signedTx, err := opts.Signer(opts.From, rawTx) + signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) if err != nil { return nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go index 24fe9f770..84cf22e3c 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go @@ -32,11 +32,20 @@ import ( "golang.org/x/tools/imports" ) +// Lang is a target programming language selector to generate bindings for. +type Lang int + +const ( + LangGo Lang = iota + LangJava + LangObjC +) + // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to // manually maintain hard coded strings that break on runtime. -func Bind(types []string, abis []string, bytecodes []string, pkg string) (string, error) { +func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) { // Process each individual contract requested binding contracts := make(map[string]*tmplContract) @@ -62,7 +71,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string for _, original := range evmABI.Methods { // Normalize the method for capital cases and non-anonymous inputs/outputs normalized := original - normalized.Name = capitalise(original.Name) + normalized.Name = methodNormalizer[lang](original.Name) normalized.Inputs = make([]abi.Argument, len(original.Inputs)) copy(normalized.Inputs, original.Inputs) @@ -78,7 +87,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string normalized.Outputs[j].Name = capitalise(output.Name) } } - // Append the methos to the call or transact lists + // Append the methods to the call or transact lists if original.Const { calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} } else { @@ -87,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string } contracts[types[i]] = &tmplContract{ Type: capitalise(types[i]), - InputABI: strippedABI, + InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), InputBin: strings.TrimSpace(bytecodes[i]), Constructor: evmABI.Constructor, Calls: calls, @@ -102,24 +111,38 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string buffer := new(bytes.Buffer) funcs := map[string]interface{}{ - "bindtype": bindType, + "bindtype": bindType[lang], + "namedtype": namedType[lang], + "capitalise": capitalise, + "decapitalise": decapitalise, } - tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource)) + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) if err := tmpl.Execute(buffer, data); err != nil { return "", err } - // Pass the code through goimports to clean it up and double check - code, err := imports.Process("", buffer.Bytes(), nil) - if err != nil { - return "", fmt.Errorf("%v\n%s", err, buffer) + // For Go bindings pass the code through goimports to clean it up and double check + if lang == LangGo { + code, err := imports.Process("", buffer.Bytes(), nil) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil } - return string(code), nil + // For all others just return as is for now + return string(buffer.Bytes()), nil } -// bindType converts a Solidity type to a Go one. Since there is no clear mapping +// bindType is a set of type binders that convert Solidity types to some supported +// programming language. +var bindType = map[Lang]func(kind abi.Type) string{ + LangGo: bindTypeGo, + LangJava: bindTypeJava, +} + +// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly // mapped will use an upscaled type (e.g. *big.Int). -func bindType(kind abi.Type) string { +func bindTypeGo(kind abi.Type) string { stringKind := kind.String() switch { @@ -160,11 +183,137 @@ func bindType(kind abi.Type) string { } } +// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping +// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. BigDecimal). +func bindTypeJava(kind abi.Type) string { + stringKind := kind.String() + + switch { + case strings.HasPrefix(stringKind, "address"): + parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("Address") + } + return fmt.Sprintf("Addresses") + + case strings.HasPrefix(stringKind, "bytes"): + parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + if parts[2] != "" { + return "byte[][]" + } + return "byte[]" + + case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): + parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 4 { + return stringKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[1] == "" { + if parts[3] == "" { + return fmt.Sprintf("int%s", parts[2]) + } + return fmt.Sprintf("int%s[]", parts[2]) + } + } + if parts[3] == "" { + return fmt.Sprintf("BigInt") + } + return fmt.Sprintf("BigInts") + + case strings.HasPrefix(stringKind, "bool"): + parts := regexp.MustCompile("bool(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("bool") + } + return fmt.Sprintf("bool[]") + + case strings.HasPrefix(stringKind, "string"): + parts := regexp.MustCompile("string(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("String") + } + return fmt.Sprintf("String[]") + + default: + return stringKind + } +} + +// namedType is a set of functions that transform language specific types to +// named versions that my be used inside method names. +var namedType = map[Lang]func(string, abi.Type) string{ + LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, + LangJava: namedTypeJava, +} + +// namedTypeJava converts some primitive data types to named variants that can +// be used as parts of method names. +func namedTypeJava(javaKind string, solKind abi.Type) string { + switch javaKind { + case "byte[]": + return "Binary" + case "byte[][]": + return "Binaries" + case "string": + return "String" + case "string[]": + return "Strings" + case "bool": + return "Bool" + case "bool[]": + return "Bools" + case "BigInt": + parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(solKind.String()) + if len(parts) != 4 { + return javaKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[3] == "" { + return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) + } + return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) + + default: + return javaKind + } + default: + return javaKind + } +} + +// methodNormalizer is a name transformer that modifies Solidity method names to +// conform to target language naming concentions. +var methodNormalizer = map[Lang]func(string) string{ + LangGo: capitalise, + LangJava: decapitalise, +} + // capitalise makes the first character of a string upper case. func capitalise(input string) string { return strings.ToUpper(input[:1]) + input[1:] } +// decapitalise makes the first character of a string lower case. +func decapitalise(input string) string { + return strings.ToLower(input[:1]) + input[1:] +} + // structured checks whether a method has enough information to return a proper // Go struct ot if flat returns are needed. func structured(method abi.Method) bool { diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go index 523122213..64dd598c0 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go @@ -42,9 +42,16 @@ type tmplMethod struct { Structured bool // Whether the returns should be accumulated into a contract } -// tmplSource is the Go source template use to generate the contract binding +// tmplSource is language to template mapping containing all the supported +// programming languages the package can generate to. +var tmplSource = map[Lang]string{ + LangGo: tmplSourceGo, + LangJava: tmplSourceJava, +} + +// tmplSourceGo is the Go source template use to generate the contract binding // based on. -const tmplSource = ` +const tmplSourceGo = ` // This file is an automatically generated Go binding. Do not modify as any // change will likely be lost upon the next re-generation! @@ -52,7 +59,7 @@ package {{.Package}} {{range $contract := .Contracts}} // {{.Type}}ABI is the input ABI used to generate the binding from. - const {{.Type}}ABI = ` + "`" + `{{.InputABI}}` + "`" + ` + const {{.Type}}ABI = "{{.InputABI}}" {{if .InputBin}} // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. @@ -258,3 +265,105 @@ package {{.Package}} {{end}} {{end}} ` + +// tmplSourceJava is the Java source template use to generate the contract binding +// based on. +const tmplSourceJava = ` +// This file is an automatically generated Java binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package {{.Package}}; + +import org.ethereum.geth.*; +import org.ethereum.geth.internal.*; + +{{range $contract := .Contracts}} + public class {{.Type}} { + // ABI is the input ABI used to generate the binding from. + public final static String ABI = "{{.InputABI}}"; + + {{if .InputBin}} + // BYTECODE is the compiled bytecode used for deploying new contracts. + public final static byte[] BYTECODE = "{{.InputBin}}".getBytes(); + + // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it. + public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}}); + {{range $index, $element := .Constructor.Inputs}} + args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args)); + } + + // Internal constructor used by contract deployment. + private {{.Type}}(BoundContract deployment) { + this.Address = deployment.getAddress(); + this.Deployer = deployment.getDeployer(); + this.Contract = deployment; + } + {{end}} + + // Ethereum address where this contract is located at. + public final Address Address; + + // Ethereum transaction in which this contract was deployed (if known!). + public final Transaction Deployer; + + // Contract instance bound to a blockchain address. + private final BoundContract Contract; + + // Creates a new instance of {{.Type}}, bound to a specific deployed contract. + public {{.Type}}(Address address, EthereumClient client) throws Exception { + this(Geth.bindContract(address, ABI, client)); + } + + {{range .Calls}} + {{if gt (len .Normalized.Outputs) 1}} + // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}. + public class {{capitalise .Normalized.Name}}Results { + {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}}; + {{end}} + } + {{end}} + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}}); + {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}}); + {{end}} + + if (opts == null) { + opts = Geth.newCallOpts(); + } + this.Contract.call(opts, results, "{{.Original.Name}}", args); + {{if gt (len .Normalized.Outputs) 1}} + {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results(); + {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}(); + {{end}} + return result; + {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}} + {{end}} + } + {{end}} + + {{range .Transacts}} + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + return this.Contract.transact(opts, "{{.Original.Name}}" , args); + } + {{end}} + } +{{end}} +` diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go index ab5020200..7970ba8ac 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go @@ -78,10 +78,6 @@ func set(dst, src reflect.Value, output Argument) error { case dstType.AssignableTo(src.Type()): dst.Set(src) case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: - if !dstType.Elem().AssignableTo(r_byte) { - return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem()) - } - if dst.Len() < output.Type.SliceSize { return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go index 2235bad61..39b843f81 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go @@ -170,6 +170,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy { var packed []byte + for i := 0; i < v.Len(); i++ { val, err := t.Elem.pack(v.Index(i)) if err != nil { @@ -177,7 +178,11 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { } packed = append(packed, val...) } - return packBytesSlice(packed, v.Len()), nil + if t.IsSlice { + return packBytesSlice(packed, v.Len()), nil + } else if t.IsArray { + return packed, nil + } } return packElement(t, v), nil @@ -186,5 +191,5 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { // requireLengthPrefix returns whether the type requires any sort of length // prefixing. func (t Type) requiresLengthPrefix() bool { - return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice || t.IsArray) + return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go b/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go index a295bcee5..faddc7cf0 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go @@ -141,8 +141,11 @@ func (am *Manager) DeleteAccount(a Account, passphrase string) error { return err } -// Sign signs hash with an unlocked private key matching the given address. -func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err error) { +// Sign calculates a ECDSA signature for the given hash. +// Note, Ethereum signatures have a particular format as described in the +// yellow paper. Use the SignEthereum function to calculate a signature +// in Ethereum format. +func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) { am.mu.RLock() defer am.mu.RUnlock() unlockedKey, found := am.unlocked[addr] @@ -152,8 +155,20 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err return crypto.Sign(hash, unlockedKey.PrivateKey) } -// SignWithPassphrase signs hash if the private key matching the given address can be -// decrypted with the given passphrase. +// SignEthereum calculates a ECDSA signature for the given hash. +// The signature has the format as described in the Ethereum yellow paper. +func (am *Manager) SignEthereum(addr common.Address, hash []byte) ([]byte, error) { + am.mu.RLock() + defer am.mu.RUnlock() + unlockedKey, found := am.unlocked[addr] + if !found { + return nil, ErrLocked + } + return crypto.SignEthereum(hash, unlockedKey.PrivateKey) +} + +// SignWithPassphrase signs hash if the private key matching the given +// address can be decrypted with the given passphrase. func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) { _, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase) if err != nil { @@ -161,7 +176,7 @@ func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, ha } defer zeroKey(key.PrivateKey) - return crypto.Sign(hash, key.PrivateKey) + return crypto.SignEthereum(hash, key.PrivateKey) } // Unlock unlocks the given account indefinitely. @@ -218,11 +233,17 @@ func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Durati return nil } -func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { +// Find resolves the given account into a unique entry in the keystore. +func (am *Manager) Find(a Account) (Account, error) { am.cache.maybeReload() am.cache.mu.Lock() a, err := am.cache.find(a) am.cache.mu.Unlock() + return a, err +} + +func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { + a, err := am.Find(a) if err != nil { return a, nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go b/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go index 7758e6f27..493602352 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go @@ -47,12 +47,20 @@ import ( const ( keyHeaderKDF = "scrypt" - // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU. + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. StandardScryptN = 1 << 18 + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. StandardScryptP = 1 - // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU. + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. LightScryptN = 1 << 12 + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. LightScryptP = 6 scryptR = 8 diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go index 9c9ab6d31..dfbd025da 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go @@ -31,14 +31,15 @@ import ( var ( abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") - typFlag = flag.String("type", "", "Go struct name for the binding (default = package name)") + typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") - pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into") - outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") + pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") + outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") + langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)") ) func main() { @@ -53,7 +54,19 @@ func main() { os.Exit(-1) } if *pkgFlag == "" { - fmt.Printf("No destination Go package specified (--pkg)\n") + fmt.Printf("No destination package specified (--pkg)\n") + os.Exit(-1) + } + var lang bind.Lang + switch *langFlag { + case "go": + lang = bind.LangGo + case "java": + lang = bind.LangJava + case "objc": + lang = bind.LangObjC + default: + fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) os.Exit(-1) } // If the entire solidity code was specified, build and bind based on that @@ -108,7 +121,7 @@ func main() { types = append(types, kind) } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, *pkgFlag) + code, err := bind.Bind(types, abis, bins, *pkgFlag, lang) if err != nil { fmt.Printf("Failed to generate ABI binding: %v\n", err) os.Exit(-1) diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go index a6c5dfe4e..abecac3d8 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go @@ -40,7 +40,6 @@ func main() { nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") - v5test = flag.Bool("v5test", false, "run a v5 topic discovery test node (adds default bootnodes to form a test network)") nodeKey *ecdsa.PrivateKey err error @@ -82,15 +81,9 @@ func main() { os.Exit(0) } - if *runv5 || *v5test { - if ntab, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { + if *runv5 { + if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { utils.Fatalf("%v", err) - } else { - if *v5test { - if err := ntab.SetFallbackNodes(discv5.BootNodes); err != nil { - utils.Fatalf("%v", err) - } - } } } else { if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go new file mode 100644 index 000000000..b2f14a4a9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go @@ -0,0 +1,246 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm" + bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "gopkg.in/urfave/cli.v1" +) + +const clientIdentifier = "bzzd" + +var ( + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) + app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon") +) + +var ( + ChequebookAddrFlag = cli.StringFlag{ + Name: "chequebook", + Usage: "chequebook contract address", + } + SwarmAccountFlag = cli.StringFlag{ + Name: "bzzaccount", + Usage: "Swarm account key file", + } + SwarmPortFlag = cli.StringFlag{ + Name: "bzzport", + Usage: "Swarm local http api port", + } + SwarmConfigPathFlag = cli.StringFlag{ + Name: "bzzconfig", + Usage: "Swarm config file path (datadir/bzz)", + } + SwarmSwapDisabled = cli.BoolFlag{ + Name: "bzznoswap", + Usage: "Swarm SWAP disabled (default false)", + } + SwarmSyncDisabled = cli.BoolFlag{ + Name: "bzznosync", + Usage: "Swarm Syncing disabled (default false)", + } + EthAPI = cli.StringFlag{ + Name: "ethapi", + Usage: "URL of the Ethereum API provider", + Value: node.DefaultIPCEndpoint("geth"), + } +) + +var defaultBootnodes = []string{} + +func init() { + // Override flag defaults so bzzd can run alongside geth. + utils.ListenPortFlag.Value = 30399 + utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} + + // Set up the cli app. + app.Commands = nil + app.Action = bzzd + app.Flags = []cli.Flag{ + utils.IdentityFlag, + utils.DataDirFlag, + utils.BootnodesFlag, + utils.KeyStoreDirFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + // bzzd-specific flags + EthAPI, + SwarmConfigPathFlag, + SwarmSwapDisabled, + SwarmSyncDisabled, + SwarmPortFlag, + SwarmAccountFlag, + ChequebookAddrFlag, + } + app.Flags = append(app.Flags, debug.Flags...) + app.Before = func(ctx *cli.Context) error { + runtime.GOMAXPROCS(runtime.NumCPU()) + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil + } +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func bzzd(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + registerBzzService(ctx, stack) + utils.StartNode(stack) + + // Add bootnodes as initial peers. + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name)) + } else { + injectBootnodes(stack.Server(), defaultBootnodes) + } + + stack.Wait() + return nil +} + +func registerBzzService(ctx *cli.Context, stack *node.Node) { + prvkey := getAccount(ctx, stack) + + chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) + bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name) + if bzzdir == "" { + bzzdir = stack.InstanceDir() + } + bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey) + if err != nil { + utils.Fatalf("unable to configure swarm: %v", err) + } + bzzport := ctx.GlobalString(SwarmPortFlag.Name) + if len(bzzport) > 0 { + bzzconfig.Port = bzzport + } + swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name) + syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name) + + ethapi := ctx.GlobalString(EthAPI.Name) + if ethapi == "" { + utils.Fatalf("Option %q must not be empty", EthAPI.Name) + } + + boot := func(ctx *node.ServiceContext) (node.Service, error) { + client, err := ethclient.Dial(ethapi) + if err != nil { + utils.Fatalf("Can't connect: %v", err) + } + return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) + } + if err := stack.Register(boot); err != nil { + utils.Fatalf("Failed to register the Swarm service: %v", err) + } +} + +func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { + keyid := ctx.GlobalString(SwarmAccountFlag.Name) + if keyid == "" { + utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) + } + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(keyid); err == nil { + glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey)) + return key + } + // Otherwise try getting it from the keystore. + return decryptStoreAccount(stack.AccountManager(), keyid) +} + +func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey { + var a accounts.Account + var err error + if common.IsHexAddress(account) { + a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)}) + } else if ix, ixerr := strconv.Atoi(account); ixerr == nil { + a, err = accman.AccountByIndex(ix) + } else { + utils.Fatalf("Can't find swarm account key %s", account) + } + if err != nil { + utils.Fatalf("Can't find swarm account key: %v", err) + } + keyjson, err := ioutil.ReadFile(a.File) + if err != nil { + utils.Fatalf("Can't load swarm account key: %v", err) + } + for i := 1; i <= 3; i++ { + passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) + key, err := accounts.DecryptKey(keyjson, passphrase) + if err == nil { + return key.PrivateKey + } + } + utils.Fatalf("Can't decrypt swarm account key") + return nil +} + +func promptPassphrase(prompt string) string { + if prompt != "" { + fmt.Println(prompt) + } + password, err := console.Stdin.PromptPassword("Passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + return password +} + +func injectBootnodes(srv *p2p.Server, nodes []string) { + for _, url := range nodes { + n, err := discover.ParseNode(url) + if err != nil { + glog.Errorf("invalid bootnode %q", err) + } + srv.AddPeer(n) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go new file mode 100644 index 000000000..0ae99acc0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go @@ -0,0 +1,49 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzhash computes a swarm tree hash. +package main + +import ( + "fmt" + "os" + "runtime" + + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + if len(os.Args) < 2 { + fmt.Println("Usage: bzzhash ") + os.Exit(0) + } + f, err := os.Open(os.Args[1]) + if err != nil { + fmt.Println("Error opening file " + os.Args[1]) + os.Exit(1) + } + + stat, _ := f.Stat() + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + key, err := chunker.Split(f, stat.Size(), nil, nil, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + } else { + fmt.Printf("%v\n", key) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go new file mode 100644 index 000000000..7d251aadb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go @@ -0,0 +1,161 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzup uploads files to the swarm HTTP API. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "net/http" + "os" + "path/filepath" + "strings" +) + +func main() { + var ( + bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint") + recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively") + manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload") + ) + log.SetOutput(os.Stderr) + log.SetFlags(0) + flag.Parse() + if flag.NArg() != 1 { + log.Fatal("need filename as the first and only argument") + } + + var ( + file = flag.Arg(0) + client = &client{api: *bzzapiFlag} + mroot manifest + ) + fi, err := os.Stat(file) + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + if !*recursiveFlag { + log.Fatal("argument is a directory and recursive upload is disabled") + } + mroot, err = client.uploadDirectory(file) + } else { + mroot, err = client.uploadFile(file, fi) + if *manifestFlag { + // Wrap the raw file entry in a proper manifest so both hashes get printed. + mroot = manifest{Entries: []manifest{mroot}} + } + } + if err != nil { + log.Fatalln("upload failed:", err) + } + if *manifestFlag { + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + mroot.Hash = hash + } + + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) +} + +// client wraps interaction with the swarm HTTP gateway. +type client struct { + api string +} + +// manifest is the JSON representation of a swarm manifest. +type manifest struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` + Entries []manifest `json:"entries,omitempty"` +} + +func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { + hash, err := c.uploadFileContent(file, fi) + m := manifest{ + Hash: hash, + ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), + } + return m, err +} + +func (c *client) uploadDirectory(dir string) (manifest, error) { + dirm := manifest{} + prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" + err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || fi.IsDir() { + return err + } + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("path %s outside directory %s", path, dir) + } + entry, err := c.uploadFile(path, fi) + entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) + dirm.Entries = append(dirm.Entries, entry) + return err + }) + return dirm, err +} + +func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { + fd, err := os.Open(file) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes)", file, fi.Size()) + return c.postRaw("application/octet-stream", fi.Size(), fd) +} + +func (c *client) uploadManifest(m manifest) (string, error) { + jsm, err := json.Marshal(m) + if err != nil { + panic(err) + } + log.Println("uploading manifest") + return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) +} + +func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { + req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) + if err != nil { + return "", err + } + req.Header.Set("content-type", mimetype) + req.ContentLength = size + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt b/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt deleted file mode 100644 index a964ad9d2..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt +++ /dev/null @@ -1 +0,0 @@ -60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50 diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt b/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt deleted file mode 100644 index 1b6857ab9..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000023000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003700000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000043000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000004700000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000049000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000004b000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004d000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000004f0000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000053000000000000000000000000000000000000000000000000000000000000005400000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000005700000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000059000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000006700000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006f0000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007100000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000073000000000000000000000000000000000000000000000000000000000000007400000000000000000000000000000000000000000000000000000000000000750000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000007700000000000000000000000000000000000000000000000000000000000000780000000000000000000000000000000000000000000000000000000000000079000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000007d000000000000000000000000000000000000000000000000000000000000007e000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008100000000000000000000000000000000000000000000000000000000000000820000000000000000000000000000000000000000000000000000000000000083000000000000000000000000000000000000000000000000000000000000008400000000000000000000000000000000000000000000000000000000000000850000000000000000000000000000000000000000000000000000000000000086000000000000000000000000000000000000000000000000000000000000008700000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000000000000000000000000089000000000000000000000000000000000000000000000000000000000000008a000000000000000000000000000000000000000000000000000000000000008b000000000000000000000000000000000000000000000000000000000000008c000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000008f0000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009100000000000000000000000000000000000000000000000000000000000000920000000000000000000000000000000000000000000000000000000000000093000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000950000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000009700000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000099000000000000000000000000000000000000000000000000000000000000009a000000000000000000000000000000000000000000000000000000000000009b000000000000000000000000000000000000000000000000000000000000009c000000000000000000000000000000000000000000000000000000000000009d000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000009f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000a100000000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000000000000000a300000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000a500000000000000000000000000000000000000000000000000000000000000a600000000000000000000000000000000000000000000000000000000000000a700000000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000000000000000a900000000000000000000000000000000000000000000000000000000000000aa00000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000ad00000000000000000000000000000000000000000000000000000000000000ae00000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000b100000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000b400000000000000000000000000000000000000000000000000000000000000b500000000000000000000000000000000000000000000000000000000000000b600000000000000000000000000000000000000000000000000000000000000b700000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000b900000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000000bb00000000000000000000000000000000000000000000000000000000000000bc00000000000000000000000000000000000000000000000000000000000000bd00000000000000000000000000000000000000000000000000000000000000be00000000000000000000000000000000000000000000000000000000000000bf00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c100000000000000000000000000000000000000000000000000000000000000c200000000000000000000000000000000000000000000000000000000000000c300000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c500000000000000000000000000000000000000000000000000000000000000c600000000000000000000000000000000000000000000000000000000000000c7 diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go index 9ca761b7b..2c4329fa5 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go @@ -19,6 +19,7 @@ package main import ( "fmt" + "io/ioutil" "math/big" "os" "runtime" @@ -58,6 +59,10 @@ var ( Name: "code", Usage: "EVM code", } + CodeFileFlag = cli.StringFlag{ + Name: "codefile", + Usage: "file containing EVM code", + } GasFlag = cli.StringFlag{ Name: "gas", Usage: "gas limit for the evm", @@ -104,6 +109,7 @@ func init() { DisableJitFlag, SysStatFlag, CodeFlag, + CodeFileFlag, GasFlag, PriceFlag, ValueFlag, @@ -133,12 +139,35 @@ func run(ctx *cli.Context) error { tstart := time.Now() var ( - ret []byte - err error + code []byte + ret []byte + err error ) + if ctx.GlobalString(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + } else { + var hexcode []byte + if ctx.GlobalString(CodeFileFlag.Name) != "" { + var err error + hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) + if err != nil { + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) + } + } else { + var err error + hexcode, err = ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) + } + } + code = common.Hex2Bytes(string(hexcode[:])) + } + if ctx.GlobalBool(CreateFlag.Name) { - input := append(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) + input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) ret, _, err = vmenv.Create( sender, input, @@ -149,7 +178,6 @@ func run(ctx *cli.Context) error { } else { receiver := statedb.CreateAccount(common.StringToAddress("receiver")) - code := common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) receiver.SetCode(crypto.Keccak256Hash(code), code) ret, err = vmenv.Call( sender, @@ -163,7 +191,7 @@ func run(ctx *cli.Context) error { vmdone := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit() + statedb.Commit(true) fmt.Println(string(statedb.Dump())) } vm.StdErrFormat(logger.StructLogs()) @@ -223,7 +251,7 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg return env } -// ruleSet implements vm.RuleSet and will always default to the homestead rule set. +// ruleSet implements vm.ChainConfig and will always default to the homestead rule set. type ruleSet struct{} func (ruleSet) IsHomestead(*big.Int) bool { return true } @@ -231,22 +259,22 @@ func (ruleSet) GasTable(*big.Int) params.GasTable { return params.GasTableHomesteadGasRepriceFork } -func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } -func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } -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() *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 } -func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } -func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } -func (self *VMEnv) Depth() int { return 0 } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig } +func (self *VMEnv) Vm() vm.Vm { return self.evm } +func (self *VMEnv) Db() vm.Database { return self.state } +func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } +func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } +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() *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 } +func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } +func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } +func (self *VMEnv) Depth() int { return 0 } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { return self.block.Hash() diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go index bb0167964..6d489b123 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go @@ -31,30 +31,37 @@ import ( var ( walletCommand = cli.Command{ - Name: "wallet", - Usage: "ethereum presale wallet", - Subcommands: []cli.Command{ - { - Action: importWallet, - Name: "import", - Usage: "import ethereum presale wallet", - }, - }, + Name: "wallet", + Usage: "Manage Ethereum presale wallets", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", Description: ` - - get wallet import /path/to/my/presale.wallet + geth wallet import /path/to/my/presale.wallet will prompt for your password and imports your ether presale account. It can be used non-interactively with the --password option taking a passwordfile as argument containing the wallet password in plaintext. -`} +`, + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "Import Ethereum presale wallet", + ArgsUsage: "", + Description: ` +TODO: Please write this +`, + }, + }, + } accountCommand = cli.Command{ - Action: accountList, - Name: "account", - Usage: "manage accounts", + Action: accountList, + Name: "account", + Usage: "Manage accounts", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", Description: ` - Manage accounts lets you create new accounts, list all existing accounts, import a private key into a new account. @@ -86,17 +93,21 @@ And finally. DO NOT FORGET YOUR PASSWORD. `, Subcommands: []cli.Command{ { - Action: accountList, - Name: "list", - Usage: "print account addresses", + Action: accountList, + Name: "list", + Usage: "Print account addresses", + ArgsUsage: " ", + Description: ` +TODO: Please write this +`, }, { - Action: accountCreate, - Name: "new", - Usage: "create a new account", + Action: accountCreate, + Name: "new", + Usage: "Create a new account", + ArgsUsage: " ", Description: ` - - ethereum account new + geth account new Creates a new account. Prints the address. @@ -106,19 +117,19 @@ You must remember this passphrase to unlock your account in the future. For non-interactive use the passphrase can be specified with the --password flag: - ethereum --password account new + geth --password account new Note, this is meant to be used for testing only, it is a bad idea to save your password to file or expose in any other way. - `, +`, }, { - Action: accountUpdate, - Name: "update", - Usage: "update an existing account", + Action: accountUpdate, + Name: "update", + Usage: "Update an existing account", + ArgsUsage: "
", Description: ` - - ethereum account update
+ geth account update
Update an existing account. @@ -130,19 +141,19 @@ format to the newest format or change the password for an account. For non-interactive use the passphrase can be specified with the --password flag: - ethereum --password account update
+ geth --password account update
Since only one password can be given, only format update can be performed, changing your password is only possible interactively. - `, +`, }, { - Action: accountImport, - Name: "import", - Usage: "import a private key into a new account", + Action: accountImport, + Name: "import", + Usage: "Import a private key into a new account", + ArgsUsage: "", Description: ` - - ethereum account import + geth account import Imports an unencrypted private key from and creates a new account. Prints the address. @@ -155,13 +166,13 @@ You must remember this passphrase to unlock your account in the future. For non-interactive use the passphrase can be specified with the -password flag: - ethereum --password account import + geth --password account import Note: As you can directly copy your encrypted accounts to another ethereum instance, this import mechanism is not needed when you transfer an account between nodes. - `, +`, }, }, } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go index 153e3ddbd..1a7a9eb18 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go @@ -20,7 +20,9 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strconv" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/cmd/utils" @@ -31,41 +33,61 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/trie" "github.com/syndtr/goleveldb/leveldb/util" "gopkg.in/urfave/cli.v1" ) var ( importCommand = cli.Command{ - Action: importChain, - Name: "import", - Usage: `import a blockchain file`, + Action: importChain, + Name: "import", + Usage: "Import a blockchain file", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } exportCommand = cli.Command{ - Action: exportChain, - Name: "export", - Usage: `export blockchain into file`, + Action: exportChain, + Name: "export", + Usage: "Export blockchain into file", + ArgsUsage: " [ ]", + Category: "BLOCKCHAIN COMMANDS", Description: ` Requires a first argument of the file to write to. Optional second and third arguments control the first and last block to write. In this mode, the file will be appended if already existing. - `, +`, } upgradedbCommand = cli.Command{ - Action: upgradeDB, - Name: "upgradedb", - Usage: "upgrade chainblock database", + Action: upgradeDB, + Name: "upgradedb", + Usage: "Upgrade chainblock database", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } removedbCommand = cli.Command{ - Action: removeDB, - Name: "removedb", - Usage: "Remove blockchain and state databases", + Action: removeDB, + Name: "removedb", + Usage: "Remove blockchain and state databases", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } dumpCommand = cli.Command{ - Action: dump, - Name: "dump", - Usage: `dump a specific block from storage`, + Action: dump, + Name: "dump", + Usage: "Dump a specific block from storage", + ArgsUsage: "[ | ]...", + Category: "BLOCKCHAIN COMMANDS", Description: ` The arguments are interpreted as block numbers or hashes. Use "ethereum dump 0" to dump the genesis block. @@ -77,34 +99,66 @@ func importChain(ctx *cli.Context) error { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") } - if ctx.GlobalBool(utils.TestNetFlag.Name) { - state.StartingNonce = 1048576 // (2**20) - } stack := makeFullNode(ctx) chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() + // Start periodically gathering memory profiles + var peakMemAlloc, peakMemSys uint64 + go func() { + stats := new(runtime.MemStats) + for { + runtime.ReadMemStats(stats) + if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { + atomic.StoreUint64(&peakMemAlloc, stats.Alloc) + } + if atomic.LoadUint64(&peakMemSys) < stats.Sys { + atomic.StoreUint64(&peakMemSys, stats.Sys) + } + time.Sleep(5 * time.Second) + } + }() // Import the chain start := time.Now() if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { utils.Fatalf("Import error: %v", err) } - fmt.Printf("Import done in %v, compacting...\n", time.Since(start)) + fmt.Printf("Import done in %v.\n\n", time.Since(start)) + + // Output pre-compaction stats mostly to see the import trashing + db := chainDb.(*ethdb.LDBDatabase) + + stats, err := db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) + fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) + + // Print the memory statistics used by the importing + mem := new(runtime.MemStats) + runtime.ReadMemStats(mem) + + fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) + fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) + fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) + fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) // Compact the entire database to more accurately measure disk io and print the stats - if db, ok := chainDb.(*ethdb.LDBDatabase); ok { - start = time.Now() - if err := db.LDB().CompactRange(util.Range{}); err != nil { - utils.Fatalf("Compaction failed: %v", err) - } - fmt.Printf("Compaction done in %v.\n", time.Since(start)) - - stats, err := db.LDB().GetProperty("leveldb.stats") - if err != nil { - utils.Fatalf("Failed to read database stats: %v", err) - } - fmt.Println(stats) + start = time.Now() + fmt.Println("Compacting entire database...") + if err = db.LDB().CompactRange(util.Range{}); err != nil { + utils.Fatalf("Compaction failed: %v", err) } + fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + + stats, err = db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go index 066247303..b1c435e00 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go @@ -30,9 +30,11 @@ import ( var ( consoleCommand = cli.Command{ - Action: localConsole, - Name: "console", - Usage: `Geth Console: interactive JavaScript environment`, + Action: localConsole, + Name: "console", + Usage: "Start an interactive JavaScript environment", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. @@ -40,20 +42,24 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console `, } attachCommand = cli.Command{ - Action: remoteConsole, - Name: "attach", - Usage: `Geth Console: interactive JavaScript environment (connect to node)`, + Action: remoteConsole, + Name: "attach", + Usage: "Start an interactive JavaScript environment (connect to node)", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. This command allows to open a console on a running geth node. - `, +`, } javascriptCommand = cli.Command{ - Action: ephemeralConsole, - Name: "js", - Usage: `executes the given JavaScript files in the Geth JavaScript VM`, + Action: ephemeralConsole, + Name: "js", + Usage: "Execute the specified JavaScript files", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The JavaScript VM exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json b/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json deleted file mode 100644 index 63f2163a9..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json +++ /dev/null @@ -1 +0,0 @@ -{"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go index 2ee2a1d26..b6928a1c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go @@ -34,14 +34,12 @@ import ( "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/contracts/release" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p/discover" "gopkg.in/urfave/cli.v1" ) @@ -62,6 +60,7 @@ func init() { // Initialize the CLI app and start Geth app.Action = geth app.HideVersion = true // we have a command to print the version + app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" app.Commands = []cli.Command{ importCommand, exportCommand, @@ -75,9 +74,11 @@ func init() { attachCommand, javascriptCommand, { - Action: makedag, - Name: "makedag", - Usage: "generate ethash dag (for testing)", + Action: makedag, + Name: "makedag", + Usage: "Generate ethash DAG (for testing)", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", Description: ` The makedag command generates an ethash DAG in /tmp/dag. @@ -86,43 +87,33 @@ Regular users do not need to execute it. `, }, { - Action: gpuinfo, - Name: "gpuinfo", - Usage: "gpuinfo", - Description: ` -Prints OpenCL device info for all found GPUs. -`, - }, - { - Action: gpubench, - Name: "gpubench", - Usage: "benchmark GPU", - Description: ` -Runs quick benchmark on first GPU found. -`, - }, - { - Action: version, - Name: "version", - Usage: "print ethereum version numbers", + Action: version, + Name: "version", + Usage: "Print version numbers", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", Description: ` The output of this command is supposed to be machine-readable. `, }, { - Action: initGenesis, - Name: "init", - Usage: "bootstraps and initialises a new genesis block (JSON)", + Action: initGenesis, + Name: "init", + Usage: "Bootstrap and initialize a new genesis block", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", Description: ` -The init command initialises a new genesis block and definition for the network. +The init command initializes a new genesis block and definition for the network. This is a destructive action and changes the network in which you will be participating. `, }, { - Action: license, - Name: "license", - Usage: "displays geth's license information", + Action: license, + Name: "license", + Usage: "Display license information", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", }, } @@ -136,14 +127,13 @@ participating. utils.OlympicFlag, utils.FastSyncFlag, utils.LightModeFlag, - utils.NoDefSrvFlag, utils.LightServFlag, utils.LightPeersFlag, - utils.CacheFlag, utils.LightKDFFlag, + utils.CacheFlag, + utils.TrieCacheGenFlag, utils.JSpathFlag, utils.ListenPortFlag, - utils.ListenPortV5Flag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.EtherbaseFlag, @@ -152,7 +142,6 @@ participating. utils.OpposeDAOFork, utils.MinerThreadsFlag, utils.MiningEnabledFlag, - utils.MiningGPUFlag, utils.AutoDAGFlag, utils.TargetGasLimitFlag, utils.NATFlag, @@ -248,10 +237,6 @@ func initGenesis(ctx *cli.Context) error { utils.Fatalf("must supply path to genesis JSON file") } - if ctx.GlobalBool(utils.TestNetFlag.Name) { - state.StartingNonce = 1048576 // (2**20) - } - stack := makeFullNode(ctx) chaindb := utils.MakeChainDatabase(ctx, stack) @@ -304,29 +289,6 @@ func startNode(ctx *cli.Context, stack *node.Node) { // Start up the node itself utils.StartNode(stack) - if ctx.GlobalBool(utils.LightModeFlag.Name) && !ctx.GlobalBool(utils.NoDefSrvFlag.Name) { - // add default light server; test phase only - addPeer := func(url string) { - node, err := discover.ParseNode(url) - if err == nil { - stack.Server().AddPeer(node) - } - } - - if ctx.GlobalBool(utils.TestNetFlag.Name) { - // TestNet (John Gerryts @phonikg) - addPeer("enode://6807cacb2b43b39d19162254c189b8828c008bb6539d39d832e98d3c65aeb70e10ce2698772b07e704b21ce7a6a4407ad0e15951ebb63b452f878cd366a1c3f5@50.112.52.169:30301") - } else { - if ctx.GlobalBool(utils.OpposeDAOFork.Name) { - } else { - // MainNet (Azure) - addPeer("enode://97d280903aff3db6049b5d5f8a5fb2c7ea9228b4352eeaa0ee919772b20009a22d1801ec4365f25c60d2f2dc9c35c6017a1d5a654e027f066ee765be4ecc5019@40.118.3.223:30303") - // MainNet (John Gerryts @phonikg) - addPeer("enode://08cc6631556d7ef632de642c0bcbbb0f9dc457155ecf1b5b92ba28baff076cd6cbfdd9e0524584fde021c691a508f133c3d019d5caad502b39944fc6ba5ce02f@50.112.52.169:30300") - } - } - } - // Unlock any account specifically requested accman := stack.AccountManager() passwords := utils.MakePasswordList(ctx) @@ -342,7 +304,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err := stack.Service(ðereum); err != nil { utils.Fatalf("ethereum service not running: %v", err) } - if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { + if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil { utils.Fatalf("Failed to start mining: %v", err) } } @@ -378,48 +340,22 @@ func makedag(ctx *cli.Context) error { return nil } -func gpuinfo(ctx *cli.Context) error { - eth.PrintOpenCLDevices() - return nil -} - -func gpubench(ctx *cli.Context) error { - args := ctx.Args() - wrongArgs := func() { - utils.Fatalf(`Usage: geth gpubench `) - } - switch { - case len(args) == 1: - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { - wrongArgs() - } - eth.GPUBench(n) - case len(args) == 0: - eth.GPUBench(0) - default: - wrongArgs() - } - return nil -} - -func version(c *cli.Context) error { +func version(ctx *cli.Context) error { fmt.Println(strings.Title(clientIdentifier)) fmt.Println("Version:", utils.Version) if gitCommit != "" { fmt.Println("Git Commit:", gitCommit) } fmt.Println("Protocol Versions:", eth.ProtocolVersions) - fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name)) + fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Go Version:", runtime.Version()) fmt.Println("OS:", runtime.GOOS) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) - return nil } -func license(c *cli.Context) error { +func license(_ *cli.Context) error { fmt.Println(`Geth is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -433,6 +369,5 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with geth. If not, see . `) - return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go index b74315dab..03a124494 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go @@ -49,9 +49,11 @@ var ( Usage: "Refresh interval in seconds", } monitorCommand = cli.Command{ - Action: monitor, - Name: "monitor", - Usage: `Geth Monitor: node metrics monitoring and visualization`, + Action: monitor, + Name: "monitor", + Usage: "Monitor and visualize node metrics", + ArgsUsage: " ", + Category: "MONITOR COMMANDS", Description: ` The Geth monitor is a tool to collect and visualize various internal metrics gathered by the node, supporting different chart types as well as the capacity diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go index 14ea2a2b0..5e52040d5 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go @@ -73,11 +73,16 @@ var AppHelpFlagGroups = []flagGroup{ utils.IdentityFlag, utils.FastSyncFlag, utils.LightModeFlag, - utils.NoDefSrvFlag, utils.LightServFlag, utils.LightPeersFlag, utils.LightKDFFlag, + }, + }, + { + Name: "PERFORMANCE TUNING", + Flags: []cli.Flag{ utils.CacheFlag, + utils.TrieCacheGenFlag, }, }, { @@ -113,7 +118,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.BootnodesFlag, utils.ListenPortFlag, - utils.ListenPortV5Flag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.NATFlag, @@ -128,7 +132,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.MiningEnabledFlag, utils.MinerThreadsFlag, - utils.MiningGPUFlag, utils.AutoDAGFlag, utils.EtherbaseFlag, utils.TargetGasLimitFlag, diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go index d0d6e1618..850bf8eb2 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go @@ -23,7 +23,6 @@ import ( "os" "os/signal" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" @@ -31,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" ) const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" @@ -122,7 +121,7 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) { ethConf := ð.Config{ TestGenesisState: db, TestGenesisBlock: test.Genesis, - ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, } if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { return nil, err diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go deleted file mode 100644 index fbbaa1f22..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package utils - -import "github.com/ethereum/go-ethereum/p2p/discover" - -// FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on -// the Frontier network. -var FrontierBootNodes = []*discover.Node{ - // ETH/DEV Go Bootnodes - discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE - discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR - discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG - - // ETH/DEV Cpp Bootnodes - discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"), -} - -// TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the -// Morden test network. -var TestNetBootNodes = []*discover.Node{ - // ETH/DEV Go Bootnodes - discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"), - discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"), - - // ETH/DEV Cpp Bootnodes -} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go index 870f4433a..3b4863e2b 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -22,13 +22,11 @@ import ( "io/ioutil" "math" "math/big" - "math/rand" "os" "path/filepath" "runtime" "strconv" "strings" - "time" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" @@ -41,17 +39,17 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) @@ -144,11 +142,6 @@ var ( Usage: "Document Root for HTTPClient file scheme", Value: DirectoryString{homeDir()}, } - CacheFlag = cli.IntFlag{ - Name: "cache", - Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", - Value: 128, - } FastSyncFlag = cli.BoolFlag{ Name: "fast", Usage: "Enable fast syncing through state downloads", @@ -157,14 +150,10 @@ var ( Name: "light", Usage: "Enable light client mode", } - NoDefSrvFlag = cli.BoolFlag{ - Name: "nodefsrv", - Usage: "Don't add default LES server (only for test version)", - } LightServFlag = cli.IntFlag{ Name: "lightserv", Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", - Value: 80, + Value: 0, } LightPeersFlag = cli.IntFlag{ Name: "lightpeers", @@ -175,6 +164,17 @@ var ( Name: "lightkdf", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", } + // Performance tuning settings + CacheFlag = cli.IntFlag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", + Value: 128, + } + TrieCacheGenFlag = cli.IntFlag{ + Name: "trie-cache-gens", + Usage: "Number of trie node generations to keep in memory", + Value: int(state.MaxTrieCacheGen), + } // Fork settings SupportDAOFork = cli.BoolFlag{ Name: "support-dao-fork", @@ -185,7 +185,6 @@ var ( Usage: "Updates the chain rules to oppose the DAO hard-fork", } // Miner settings - // TODO: refactor CPU vs GPU mining flags MiningEnabledFlag = cli.BoolFlag{ Name: "mine", Usage: "Enable mining", @@ -195,10 +194,6 @@ var ( Usage: "Number of CPU threads to use for mining", Value: runtime.NumCPU(), } - MiningGPUFlag = cli.StringFlag{ - Name: "minergpus", - Usage: "List of GPUs to use for mining (e.g. '0,1' will use the first two GPUs found)", - } TargetGasLimitFlag = cli.StringFlag{ Name: "targetgaslimit", Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", @@ -346,11 +341,6 @@ var ( Usage: "Network listening port", Value: 30303, } - ListenPortV5Flag = cli.IntFlag{ - Name: "v5port", - Usage: "Experimental RLPx V5 (Topic Discovery) listening port", - Value: 30304, - } BootnodesFlag = cli.StringFlag{ Name: "bootnodes", Usage: "Comma separated enode URLs for P2P discovery bootstrap", @@ -507,9 +497,9 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { // Return pre-configured nodes if none were manually requested if !ctx.GlobalIsSet(BootnodesFlag.Name) { if ctx.GlobalBool(TestNetFlag.Name) { - return TestNetBootNodes + return params.TestnetBootnodes } - return FrontierBootNodes + return params.MainnetBootnodes } // Otherwise parse and use the CLI bootstrap nodes bootnodes := []*discover.Node{} @@ -525,14 +515,37 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { return bootnodes } +// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line +// flags, reverting to pre-configured ones if none have been specified. +func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node { + // Return pre-configured nodes if none were manually requested + if !ctx.GlobalIsSet(BootnodesFlag.Name) { + return params.DiscoveryV5Bootnodes + } + // Otherwise parse and use the CLI bootstrap nodes + bootnodes := []*discv5.Node{} + + for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") { + node, err := discv5.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) + continue + } + bootnodes = append(bootnodes, node) + } + return bootnodes +} + // MakeListenAddress creates a TCP listening address string from set command // line flags. func MakeListenAddress(ctx *cli.Context) string { return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) } -func MakeListenAddressV5(ctx *cli.Context) string { - return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortV5Flag.Name)) +// MakeDiscoveryV5Address creates a UDP listening address string from set command +// line flags for the V5 discovery protocol. +func MakeDiscoveryV5Address(ctx *cli.Context) string { + return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) } // MakeNAT creates a port mapper from set command line flags. @@ -665,11 +678,12 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { Name: name, Version: vsn, UserIdent: makeNodeUserIdent(ctx), - NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name), - DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name), + NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), + DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0, + DiscoveryV5Addr: MakeDiscoveryV5Address(ctx), BootstrapNodes: MakeBootstrapNodes(ctx), + BootstrapNodesV5: MakeBootstrapNodesV5(ctx), ListenAddr: MakeListenAddress(ctx), - ListenAddrV5: MakeListenAddressV5(ctx), NAT: MakeNAT(ctx), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), @@ -718,22 +732,11 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { Fatalf("The %v flags are mutually exclusive", netFlags) } - // initialise new random number generator - rand := rand.New(rand.NewSource(time.Now().UnixNano())) - // get enabled jit flag - jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name) - // if the jit is not enabled enable it for 10 pct of the people - if !jitEnabled && rand.Float64() < 0.1 { - jitEnabled = true - glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false") - } - ethConf := ð.Config{ Etherbase: MakeEtherbase(stack.AccountManager(), ctx), ChainConfig: MakeChainConfig(ctx, stack), FastSync: ctx.GlobalBool(FastSyncFlag.Name), LightMode: ctx.GlobalBool(LightModeFlag.Name), - NoDefSrv: ctx.GlobalBool(NoDefSrvFlag.Name), LightServ: ctx.GlobalInt(LightServFlag.Name), LightPeers: ctx.GlobalInt(LightPeersFlag.Name), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), @@ -744,8 +747,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { ExtraData: MakeMinerExtra(extra, ctx), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), DocRoot: ctx.GlobalString(DocRootFlag.Name), - EnableJit: jitEnabled, - ForceJit: ctx.GlobalBool(VMForceJitFlag.Name), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)), GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)), @@ -767,11 +768,9 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { case ctx.GlobalBool(TestNetFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - ethConf.NetworkId = 2 + ethConf.NetworkId = 3 } - ethConf.Genesis = core.TestNetGenesisBlock() - state.StartingNonce = 1048576 // (2**20) - light.StartingNonce = 1048576 // (2**20) + ethConf.Genesis = core.DefaultTestnetGenesisBlock() case ctx.GlobalBool(DevModeFlag.Name): ethConf.Genesis = core.OlympicGenesisBlock() @@ -780,6 +779,10 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { } ethConf.PowTest = true } + // Override any global options pertaining to the Ethereum protocol + if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { + state.MaxTrieCacheGen = uint16(gen) + } if ethConf.LightMode { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { @@ -824,7 +827,7 @@ func SetupNetwork(ctx *cli.Context) { } // MakeChainConfig reads the chain configuration from the database in ctx.Datadir. -func MakeChainConfig(ctx *cli.Context, stack *node.Node) *core.ChainConfig { +func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig { db := MakeChainDatabase(ctx, stack) defer db.Close() @@ -832,9 +835,9 @@ func MakeChainConfig(ctx *cli.Context, stack *node.Node) *core.ChainConfig { } // MakeChainConfigFromDb reads the chain configuration from the given database. -func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig { +func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig { // If the chain is already initialized, use any existing chain configs - config := new(core.ChainConfig) + config := new(params.ChainConfig) genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0) if genesis != nil { @@ -848,27 +851,35 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfi Fatalf("Could not make chain configuration: %v", err) } } - // Set any missing fields due to them being unset or system upgrade - if config.HomesteadBlock == nil { + // set chain id in case it's zero. + if config.ChainId == nil { + config.ChainId = new(big.Int) + } + // Check whether we are allowed to set default config params or not: + // - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`) + // - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet) + defaults := genesis == nil || + (genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) || + (genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name)) + + if defaults { if ctx.GlobalBool(TestNetFlag.Name) { - config.HomesteadBlock = params.TestNetHomesteadBlock + config = params.TestnetChainConfig } else { + // Homestead fork config.HomesteadBlock = params.MainNetHomesteadBlock - } - } - if config.DAOForkBlock == nil { - if ctx.GlobalBool(TestNetFlag.Name) { - config.DAOForkBlock = params.TestNetDAOForkBlock - } else { + // DAO fork config.DAOForkBlock = params.MainNetDAOForkBlock - } - config.DAOForkSupport = true - } - if config.HomesteadGasRepriceBlock == nil { - if ctx.GlobalBool(TestNetFlag.Name) { - config.HomesteadGasRepriceBlock = params.TestNetHomesteadGasRepriceBlock - } else { - config.HomesteadGasRepriceBlock = params.MainNetHomesteadGasRepriceBlock + config.DAOForkSupport = true + + // DoS reprice fork + config.EIP150Block = params.MainNetHomesteadGasRepriceBlock + config.EIP150Hash = params.MainNetHomesteadGasRepriceHash + + // DoS state cleanup fork + config.EIP155Block = params.MainNetSpuriousDragon + config.EIP158Block = params.MainNetSpuriousDragon + config.ChainId = params.MainNetChainID } } // Force override any existing configs if explicitly requested diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go index 03633d694..3070d10b5 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go @@ -1,4 +1,4 @@ -// Copyright 2014 The go-ethereum Authors +// Copyright 2016 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 5 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 4 // Patch version component of the current release VersionMeta = "unstable" // Version metadata to append to the version string ) diff --git a/vendor/github.com/ethereum/go-ethereum/common/bytes.go b/vendor/github.com/ethereum/go-ethereum/common/bytes.go index 4fb016a97..b9fb3b2da 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/bytes.go +++ b/vendor/github.com/ethereum/go-ethereum/common/bytes.go @@ -37,7 +37,7 @@ func ToHex(b []byte) string { func FromHex(s string) []byte { if len(s) > 1 { - if s[0:2] == "0x" { + if s[0:2] == "0x" || s[0:2] == "0X" { s = s[2:] } if len(s)%2 == 1 { diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/exp.go b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go new file mode 100644 index 000000000..6f6c040e0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go @@ -0,0 +1,44 @@ +// 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 . + +package math + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// wordSize is the size number of bits in a big.Int Word. +const wordSize = 32 << (uint64(^big.Word(0)) >> 63) + +// Exp implement exponentiation by squaring algorithm. +// +// Courtesy @karalabe and @chfast +func Exp(base, exponent *big.Int) *big.Int { + result := big.NewInt(1) + + for _, word := range exponent.Bits() { + for i := 0; i < wordSize; i++ { + if word&1 == 1 { + common.U256(result.Mul(result, base)) + } + common.U256(base.Mul(base, base)) + word >>= 1 + } + } + return result +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go index 760429377..92005252e 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go +++ b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go @@ -20,11 +20,11 @@ package mclock import ( "time" - "github.com/aristanetworks/goarista/atime" + "github.com/aristanetworks/goarista/monotime" ) -type AbsTime time.Duration // absolute monotonic time +type AbsTime time.Duration // absolute monotonic time func Now() AbsTime { - return AbsTime(atime.NanoTime()) + return AbsTime(monotime.Now()) } diff --git a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go b/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go deleted file mode 100644 index 4521ff7a1..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go +++ /dev/null @@ -1,262 +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 . - -// +build ignore - -package natspec - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/registrar" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/xeth" - "github.com/robertkrimen/otto" -) - -type abi2method map[[8]byte]*method - -type NatSpec struct { - jsvm *otto.Otto - abiDocJson []byte - userDoc userDoc - tx, data string -} - -// main entry point for to get natspec notice for a transaction -// the implementation is frontend friendly in that it always gives back -// a notice that is safe to display -// :FIXME: the second return value is an error, which can be used to fine-tune bahaviour -func GetNotice(xeth *xeth.XEth, tx string, http *httpclient.HTTPClient) (notice string) { - ns, err := New(xeth, tx, http) - if err != nil { - if ns == nil { - return getFallbackNotice(fmt.Sprintf("no NatSpec info found for contract: %v", err), tx) - } else { - return getFallbackNotice(fmt.Sprintf("invalid NatSpec info: %v", err), tx) - } - } - - notice, err = ns.Notice() - if err != nil { - return getFallbackNotice(fmt.Sprintf("NatSpec notice error: %v", err), tx) - } - - return -} - -func getFallbackNotice(comment, tx string) string { - return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx) -} - -type transaction struct { - To string `json:"to"` - Data string `json:"data"` -} - -type jsonTx struct { - Params []transaction `json:"params"` -} - -type contractInfo struct { - Source string `json:"source"` - Language string `json:"language"` - Version string `json:"compilerVersion"` - AbiDefinition json.RawMessage `json:"abiDefinition"` - UserDoc userDoc `json:"userDoc"` - DeveloperDoc json.RawMessage `json:"developerDoc"` -} - -func New(xeth *xeth.XEth, jsontx string, http *httpclient.HTTPClient) (self *NatSpec, err error) { - - // extract contract address from tx - var tx jsonTx - err = json.Unmarshal([]byte(jsontx), &tx) - if err != nil { - return - } - t := tx.Params[0] - contractAddress := t.To - - content, err := FetchDocsForContract(contractAddress, xeth, http) - if err != nil { - return - } - - self, err = NewWithDocs(content, jsontx, t.Data) - return -} - -// also called by admin.contractInfo.get -func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, client *httpclient.HTTPClient) (content []byte, err error) { - // retrieve contract hash from state - codehex := xeth.CodeAt(contractAddress) - codeb := xeth.CodeAtBytes(contractAddress) - - if codehex == "0x" { - err = fmt.Errorf("contract (%v) not found", contractAddress) - return - } - codehash := common.BytesToHash(crypto.Keccak256(codeb)) - // set up nameresolver with natspecreg + urlhint contract addresses - reg := registrar.New(xeth) - - // resolve host via HashReg/UrlHint Resolver - hash, err := reg.HashToHash(codehash) - if err != nil { - return - } - if client.HasScheme("bzz") { - content, err = client.Get("bzz://"+hash.Hex()[2:], "") - if err == nil { // non-fatal - return - } - err = nil - //falling back to urlhint - } - - uri, err := reg.HashToUrl(hash) - if err != nil { - return - } - - // get content via http client and authenticate content using hash - content, err = client.GetAuthContent(uri, hash) - if err != nil { - return - } - return -} - -func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) { - - var contract contractInfo - err = json.Unmarshal(infoDoc, &contract) - if err != nil { - return - } - - self = &NatSpec{ - jsvm: otto.New(), - abiDocJson: []byte(contract.AbiDefinition), - userDoc: contract.UserDoc, - tx: tx, - data: data, - } - - // load and require natspec js (but it is meant to be protected environment) - _, err = self.jsvm.Run(natspecJS) - if err != nil { - return - } - _, err = self.jsvm.Run("var natspec = require('natspec');") - return -} - -// type abiDoc []method - -// type method struct { -// Name string `json:name` -// Inputs []input `json:inputs` -// abiKey [8]byte -// } - -// type input struct { -// Name string `json:name` -// Type string `json:type` -// } - -// json skeleton for abi doc (contract method definitions) -type method struct { - Notice string `json:notice` - name string -} - -type userDoc struct { - Methods map[string]*method `json:methods` -} - -func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) { - for signature, m := range self.userDoc.Methods { - name := strings.Split(signature, "(")[0] - hash := []byte(common.Bytes2Hex(crypto.Keccak256([]byte(signature)))) - var key [8]byte - copy(key[:], hash[:8]) - if bytes.Equal(key[:], abiKey[:]) { - meth = m - meth.name = name - return - } - } - return -} - -func (self *NatSpec) Notice() (notice string, err error) { - var abiKey [8]byte - if len(self.data) < 10 { - err = fmt.Errorf("Invalid transaction data") - return - } - copy(abiKey[:], self.data[2:10]) - meth := self.makeAbi2method(abiKey) - - if meth == nil { - err = fmt.Errorf("abi key does not match any method") - return - } - notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice) - return -} - -func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) { - - if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil { - return "", fmt.Errorf("natspec.js error setting transaction: %v", err) - } - - if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil { - return "", fmt.Errorf("natspec.js error setting abi: %v", err) - } - - if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil { - return "", fmt.Errorf("natspec.js error setting method: %v", err) - } - - if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil { - return "", fmt.Errorf("natspec.js error setting expression: %v", err) - } - - self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};") - value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);") - if err != nil { - return "", fmt.Errorf("natspec.js error evaluating expression: %v", err) - } - evalError := "Natspec evaluation failed, wrong input params" - if value.String() == evalError { - return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression) - } - if len(value.String()) == 0 { - return "", fmt.Errorf("natspec.js error evaluating expression") - } - - return value.String(), nil - -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go b/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go deleted file mode 100644 index 0375bb4d5..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go +++ /dev/null @@ -1,4060 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package natspec - -const natspecJS = //`require=function t(e,n,r){function i(f,u){if(!n[f]){if(!e[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[f]={exports:{}};e[f][0].call(a.exports,function(t){var n=e[f][1][t];return i(n?n:t)},a,a.exports,t,e,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;fv;v++)d.push(g(e.slice(0,s))),e=e.slice(s);n.push(d)}else r.prefixedType("string")(t[c].type)?(a=a.slice(s),n.push(g(e.slice(0,s))),e=e.slice(s)):(n.push(g(e.slice(0,s))),e=e.slice(s))}),n},g=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},m=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e};e.exports={inputParser:g,outputParser:m,formatInput:a,formatOutput:h}},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3}}).call(this,t("_process"))},{_process:2,"bignumber.js":8}],5:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=t("./utils"),o=t("./const"),f=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},u=function(t){var e=2*o.ETH_PADDING;return t instanceof r||"number"==typeof t?("number"==typeof t&&(t=new r(t)),r.config(o.ETH_BIGNUMBER_ROUNDING_MODE),t=t.round(),t.lessThan(0)&&(t=new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(t).plus(1)),t=t.toString(16)):t=0===t.indexOf("0x")?t.substr(2):"string"==typeof t?u(new r(t)):(+t).toString(16),f(t,e)},s=function(t){return i.fromAscii(t,o.ETH_PADDING).substr(2)},c=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},a=function(t){return u(new r(t).times(new r(2).pow(128)))},l=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},p=function(t){return t=t||"0",l(t)?new r(t,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(t,16)},h=function(t){return t=t||"0",new r(t,16)},g=function(t){return p(t).dividedBy(new r(2).pow(128))},m=function(t){return h(t).dividedBy(new r(2).pow(128))},d=function(t){return"0x"+t},v=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},w=function(t){return i.toAscii(t)},y=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:u,formatInputString:s,formatInputBool:c,formatInputReal:a,formatOutputInt:p,formatOutputUInt:h,formatOutputReal:g,formatOutputUReal:m,formatOutputHash:d,formatOutputBool:v,formatOutputString:w,formatOutputAddress:y}}).call(this,t("_process"))},{"./const":4,"./utils":7,_process:2,"bignumber.js":8}],6:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},i=function(t){return function(e){return t===e}},o=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("hash"),format:n.formatInputInt},{type:r("string"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:i("address"),format:n.formatInputInt},{type:i("bool"),format:n.formatInputBool}]},f=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("hash"),format:n.formatOutputHash},{type:r("string"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:i("address"),format:n.formatOutputAddress},{type:i("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:i,inputTypes:o,outputTypes:f}},{"./formatters":5}],7:[function(t,e){var n=t("./const"),r=function(t,e){for(var n=!1,r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&rr?"i":"").test(c))return m(a,c,u,r);u?(a.s=0>1/t?(c=c.slice(1),-1):1,$&&c.replace(/^0\.0*|\./,"").length>15&&U(L,O,t),u=!1):a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,a.s)}else{if(t instanceof e)return a.s=t.s,a.e=t.e,a.c=(t=t.c)?t.slice():t,void(L=0);if((u="number"==typeof t)&&0*t==0){if(a.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,f=t;f>=10;f/=10,o++);return a.e=o,a.c=[t],void(L=0)}c=t+""}else{if(!d.test(c=t+""))return m(a,c,u);a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(f=c.search(/e/i))>0?(0>o&&(o=f),o+=+c.slice(f+1),c=c.substring(0,f)):0>o&&(o=c.length),f=0;48===c.charCodeAt(f);f++);for(s=c.length;48===c.charCodeAt(--s););if(c=c.slice(f,s+1))if(s=c.length,u&&$&&s>15&&U(L,O,a.s*t),o=o-f-1,o>q)a.c=a.e=null;else if(k>o)a.c=[a.e=0];else{if(a.e=o,a.c=[],f=(o+1)%I,0>o&&(f+=I),s>f){for(f&&a.c.push(+c.slice(0,f)),s-=I;s>f;)a.c.push(+c.slice(f,f+=I));c=c.slice(f),f=I-c.length}else f-=s;for(;f--;c+="0");a.c.push(+c)}else a.c=[a.e=0];L=0}function n(t,n,r,i){var f,u,s,a,p,h,g,m=t.indexOf("."),d=B,v=H;for(37>r&&(t=t.toLowerCase()),m>=0&&(s=Y,Y=0,t=t.replace(".",""),g=new e(r),p=g.pow(t.length-m),Y=s,g.c=c(l(o(p.c),p.e),10,n),g.e=g.c.length),h=c(t,r,n),u=s=h.length;0==h[--s];h.pop());if(!h[0])return"0";if(0>m?--u:(p.c=h,p.e=u,p.s=i,p=G(p,g,d,v,n),h=p.c,a=p.r,u=p.e),f=u+d+1,m=h[f],s=n/2,a=a||0>f||null!=h[f+1],a=4>v?(null!=m||a)&&(0==v||v==(p.s<0?3:2)):m>s||m==s&&(4==v||a||6==v&&1&h[f-1]||v==(p.s<0?8:7)),1>f||!h[0])t=a?l("1",-d):"0";else{if(h.length=f,a)for(--n;++h[--f]>n;)h[f]=0,f||(++u,h.unshift(1));for(s=h.length;!h[--s];);for(m=0,t="";s>=m;t+=N.charAt(h[m++]));t=l(t,u)}return t}function h(t,n,r,i){var f,u,s,c,p;if(r=null!=r&&z(r,0,8,i,b)?0|r:H,!t.c)return t.toString();if(f=t.c[0],s=t.e,null==n)p=o(t.c),p=19==i||24==i&&C>=s?a(p,s):l(p,s);else if(t=F(new e(t),n,r),u=t.e,p=o(t.c),c=p.length,19==i||24==i&&(u>=n||C>=u)){for(;n>c;p+="0",c++);p=a(p,u)}else if(n-=s,p=l(p,u),u+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=u-c,n>0)for(u+1==c&&(p+=".");n--;p+="0");return t.s<0&&f?"-"+p:p}function S(t,n){var r,i,o=0;for(s(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&U(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function R(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>q?t.c=t.e=null:k>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function U(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",L=0,r}function F(t,e,n,r){var i,o,f,u,s,c,a,l=t.c,p=_;if(l){t:{for(i=1,u=l[0];u>=10;u/=10,i++);if(o=e-i,0>o)o+=I,f=e,s=l[c=0],a=s/p[i-f-1]%10|0;else if(c=v((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));s=a=0,i=1,o%=I,f=o-I+1}else{for(s=u=l[c],i=1;u>=10;u/=10,i++);o%=I,f=o-I+i,a=0>f?0:s/p[i-f-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>f?s:s%p[i-f-1]),r=4>n?(a||r)&&(0==n||n==(t.s<0?3:2)):a>5||5==a&&(4==n||r||6==n&&(o>0?f>0?s/p[i-f]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,u=1,c--):(l.length=c+1,u=p[I-o],l[c]=f>0?w(s/p[i-f]%p[f])*u:0),r)for(;;){if(0==c){for(o=1,f=l[0];f>=10;f/=10,o++);for(f=l[0]+=u,u=1;f>=10;f/=10,u++);o!=u&&(t.e++,l[0]==E&&(l[0]=1));break}if(l[c]+=u,l[c]!=E)break;l[c--]=0,u=1}for(o=l.length;0===l[--o];l.pop());}t.e>q?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return f(e="DECIMAL_PLACES")&&z(t,0,D,2,e)&&(B=0|t),r[e]=B,f(e="ROUNDING_MODE")&&z(t,0,8,2,e)&&(H=0|t),r[e]=H,f(e="EXPONENTIAL_AT")&&(s(t)?z(t[0],-D,0,2,e)&&z(t[1],0,D,2,e)&&(C=0|t[0],j=0|t[1]):z(t,-D,D,2,e)&&(C=-(j=0|(0>t?-t:t)))),r[e]=[C,j],f(e="RANGE")&&(s(t)?z(t[0],-D,-1,2,e)&&z(t[1],1,D,2,e)&&(k=0|t[0],q=0|t[1]):z(t,-D,D,2,e)&&(0|t?k=-(q=0|(0>t?-t:t)):$&&U(2,e+" cannot be zero",t))),r[e]=[k,q],f(e="ERRORS")&&(t===!!t||1===t||0===t?(L=0,z=($=!!t)?A:u):$&&U(2,e+y,t)),r[e]=$,f(e="CRYPTO")&&(t===!!t||1===t||0===t?(V=!(!t||!g||"object"!=typeof g),t&&!V&&$&&U(2,"crypto unavailable",g)):$&&U(2,e+y,t)),r[e]=V,f(e="MODULO_MODE")&&z(t,0,9,2,e)&&(W=0|t),r[e]=W,f(e="POW_PRECISION")&&z(t,0,D,2,e)&&(Y=0|t),r[e]=Y,f(e="FORMAT")&&("object"==typeof t?Z=t:$&&U(2,e+" not an object",t)),r[e]=Z,r},e.max=function(){return S(arguments,M.lt)},e.min=function(){return S(arguments,M.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return w(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,f,u,s=0,c=[],a=new e(P);if(t=null!=t&&z(t,0,D,14)?0|t:B,f=v(t/I),V)if(g&&g.getRandomValues){for(r=g.getRandomValues(new Uint32Array(f*=2));f>s;)u=131072*r[s]+(r[s+1]>>>11),u>=9e15?(i=g.getRandomValues(new Uint32Array(2)),r[s]=i[0],r[s+1]=i[1]):(c.push(u%1e14),s+=2);s=f/2}else if(g&&g.randomBytes){for(r=g.randomBytes(f*=7);f>s;)u=281474976710656*(31&r[s])+1099511627776*r[s+1]+4294967296*r[s+2]+16777216*r[s+3]+(r[s+4]<<16)+(r[s+5]<<8)+r[s+6],u>=9e15?g.randomBytes(7).copy(r,s):(c.push(u%1e14),s+=7);s=f/7}else $&&U(14,"crypto unavailable",g);if(!s)for(;f>s;)u=n(),9e15>u&&(c[s++]=u%1e14);for(f=c[--s],t%=I,f&&t&&(u=_[I-t],c[s]=w(f/u)*u);0===c[s];c.pop(),s--);if(0>s)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(s=1,u=c[0];u>=10;u/=10,s++);I>s&&(o-=I-s)}return a.e=o,a.c=c,a}}(),G=function(){function t(t,e,n){var r,i,o,f,u=0,s=t.length,c=e%T,a=e/T|0;for(t=t.slice();s--;)o=t[s]%T,f=t[s]/T|0,r=a*o+f*c,i=c*o+r%T*T+u,u=(i/n|0)+(r/T|0)+a*f,t[s]=i%n;return u&&t.unshift(u),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,f,u,s,c){var a,l,p,h,g,m,d,v,y,b,O,N,x,_,T,D,S,A=o.s==f.s?1:-1,R=o.c,U=f.c;if(!(R&&R[0]&&U&&U[0]))return new e(o.s&&f.s&&(R?!U||R[0]!=U[0]:U)?R&&0==R[0]||!U?0*A:A/0:0/0);for(v=new e(A),y=v.c=[],l=o.e-f.e,A=u+l+1,c||(c=E,l=i(o.e/I)-i(f.e/I),A=A/I|0),p=0;U[p]==(R[p]||0);p++);if(U[p]>(R[p]||0)&&l--,0>A)y.push(1),h=!0;else{for(_=R.length,D=U.length,p=0,A+=2,g=w(c/(U[0]+1)),g>1&&(U=t(U,g,c),R=t(R,g,c),D=U.length,_=R.length),x=D,b=R.slice(0,D),O=b.length;D>O;b[O++]=0);S=U.slice(),S.unshift(0),T=U[0],U[1]>=c/2&&T++;do g=0,a=n(U,b,D,O),0>a?(N=b[0],D!=O&&(N=N*c+(b[1]||0)),g=w(N/T),g>1?(g>=c&&(g=c-1),m=t(U,g,c),d=m.length,O=b.length,a=n(m,b,d,O),1==a&&(g--,r(m,d>D?S:U,d,c))):(0==g&&(a=g=1),m=U.slice()),d=m.length,O>d&&m.unshift(0),r(b,m,O,c),-1==a&&(O=b.length,a=n(U,b,D,O),1>a&&(g++,r(b,O>D?S:U,O,c))),O=b.length):0===a&&(g++,b=[0]),y[p++]=g,a&&b[0]?b[O++]=R[x]||0:(b=[R[x]],O=1);while((x++<_||null!=b[0])&&A--);h=null!=b[0],y[0]||y.shift()}if(c==E){for(p=1,A=y[0];A>=10;A/=10,p++);F(v,u+(v.e=p+l*I-1)+1,s,h)}else v.e=l,v.r=+h;return v}}(),m==function(){var t=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+([\w.])|^\s+|\s+$/g;return function(f,u,s,c){var a,l=s?u:u.replace(o,"$1");if(i.test(l))f.s=isNaN(l)?null:0>l?-1:1;else{if(!s&&(l=l.replace(t,function(t,e,n){return a="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=a?t:e}),c&&(a=c,l=l.replace(n,"$1").replace(r,"0.$1")),u!=l))return new e(l,a);$&&U(L,"not a"+(c?" base "+c:"")+" number",u),f.s=null}f.c=f.e=null,L=0}}(),M.absoluteValue=M.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},M.ceil=function(){return F(new e(this),this.e+1,2)},M.comparedTo=M.cmp=function(t,n){return L=1,f(this,new e(t,n))},M.decimalPlaces=M.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},M.dividedBy=M.div=function(t,n){return L=3,G(this,new e(t,n),B,H)},M.dividedToIntegerBy=M.divToInt=function(t,n){return L=4,G(this,new e(t,n),0,1)},M.equals=M.eq=function(t,n){return L=5,0===f(this,new e(t,n))},M.floor=function(){return F(new e(this),this.e+1,3)},M.greaterThan=M.gt=function(t,n){return L=6,f(this,new e(t,n))>0},M.greaterThanOrEqualTo=M.gte=function(t,n){return L=7,1===(n=f(this,new e(t,n)))||0===n},M.isFinite=function(){return!!this.c},M.isInteger=M.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},M.isNaN=function(){return!this.s},M.isNegative=M.isNeg=function(){return this.s<0},M.isZero=function(){return!!this.c&&0==this.c[0]},M.lessThan=M.lt=function(t,n){return L=8,f(this,new e(t,n))<0},M.lessThanOrEqualTo=M.lte=function(t,n){return L=9,-1===(n=f(this,new e(t,n)))||0===n},M.minus=M.sub=function(t,n){var r,o,f,u,s=this,c=s.s;if(L=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,s.plus(t);var a=s.e/I,l=t.e/I,p=s.c,h=t.c;if(!a||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?s:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?s:3==H?-0:0)}if(a=i(a),l=i(l),p=p.slice(),c=a-l){for((u=0>c)?(c=-c,f=p):(l=a,f=h),f.reverse(),n=c;n--;f.push(0));f.reverse()}else for(o=(u=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){u=p[n]0)for(;n--;p[r++]=0);for(n=E-1;o>c;){if(p[--o]0?(s=u,r=a):(f=-f,r=c),r.reverse();f--;r.push(0));r.reverse()}for(f=c.length,n=a.length,0>f-n&&(r=a,a=c,c=r,n=f),f=0;n;)f=(c[--n]=c[n]+a[n]+f)/E|0,c[n]%=E;return f&&(c.unshift(f),++s),R(t,c,s)},M.precision=M.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&($&&U(13,"argument"+y,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},M.round=function(t,n){var r=new e(this);return(null==t||z(t,0,D,15))&&F(r,~~t+this.e+1,null!=n&&z(n,0,8,15,b)?0|n:H),r},M.shift=function(t){var n=this;return z(t,-x,x,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-x>t||t>x)?n.s*(0>t?0:1/0):n)},M.squareRoot=M.sqrt=function(){var t,n,r,f,u,s=this,c=s.c,a=s.s,l=s.e,p=B+4,h=new e("0.5");if(1!==a||!c||!c[0])return new e(!a||0>a&&(!c||c[0])?0/0:c?s:1/0);if(a=Math.sqrt(+s),0==a||a==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),a=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),a==1/0?n="1e"+l:(n=a.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(a+""),r.c[0])for(l=r.e,a=l+p,3>a&&(a=0);;)if(u=r,r=h.times(u.plus(G(s,u,p,1))),o(u.c).slice(0,a)===(n=o(r.c)).slice(0,a)){if(r.ea&&(d=b,b=O,O=d,f=a,a=h,h=f),f=a+h,d=[];f--;d.push(0));for(v=E,w=T,f=h;--f>=0;){for(r=0,g=O[f]%w,m=O[f]/w|0,s=a,u=f+s;u>f;)l=b[--s]%w,p=b[s]/w|0,c=m*l+p*g,l=g*l+c%w*w+d[u]+r,r=(l/v|0)+(c/w|0)+m*p,d[u--]=l%v;d[u]=r}return r?++o:d.shift(),R(t,d,o)},M.toDigits=function(t,n){var r=new e(this);return t=null!=t&&z(t,1,D,18,"precision")?0|t:null,n=null!=n&&z(n,0,8,18,b)?0|n:H,t?F(r,t,n):r},M.toExponential=function(t,e){return h(this,null!=t&&z(t,0,D,19)?~~t+1:null,e,19)},M.toFixed=function(t,e){return h(this,null!=t&&z(t,0,D,20)?~~t+this.e+1:null,e,20)},M.toFormat=function(t,e){var n=h(this,null!=t&&z(t,0,D,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+Z.groupSize,f=+Z.secondaryGroupSize,u=Z.groupSeparator,s=i[0],c=i[1],a=this.s<0,l=a?s.slice(1):s,p=l.length;if(f&&(r=o,o=f,f=r,p-=r),o>0&&p>0){for(r=p%o||o,s=l.substr(0,r);p>r;r+=o)s+=u+l.substr(r,o);f>0&&(s+=u+l.slice(r)),a&&(s="-"+s)}n=c?s+Z.decimalSeparator+((f=+Z.fractionGroupSize)?c.replace(new RegExp("\\d{"+f+"}\\B","g"),"$&"+Z.fractionGroupSeparator):c):s}return n},M.toFraction=function(t){var n,r,i,f,u,s,c,a,l,p=$,h=this,g=h.c,m=new e(P),d=r=new e(P),v=c=new e(P);if(null!=t&&($=!1,s=new e(t),$=p,(!(p=s.isInt())||s.lt(P))&&($&&U(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&s.c&&F(s,s.e+1,1).gte(P)?s:null)),!g)return h.toString();for(l=o(g),f=m.e=l.length-h.e-1,m.c[0]=_[(u=f%I)<0?I+u:u],t=!t||s.cmp(m)>0?f>0?m:d:s,u=q,q=1/0,s=new e(l),c.c[0]=0;a=G(s,m,0,1),i=r.plus(a.times(v)),1!=i.cmp(t);)r=v,v=i,d=c.plus(a.times(i=d)),c=i,m=s.minus(a.times(i=m)),s=i;return i=G(t.minus(r),v,0,1),c=c.plus(i.times(d)),r=r.plus(i.times(v)),c.s=d.s=h.s,f*=2,n=G(d,v,f,H).minus(h).abs().cmp(G(c,r,f,H).minus(h).abs())<1?[d.toString(),v.toString()]:[c.toString(),r.toString()],q=u,n},M.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},M.toPower=M.pow=function(t){var n,r,i=w(0>t?-t:+t),o=this;if(!z(t,-x,x,23,"exponent")&&(!isFinite(t)||i>x&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=Y?v(Y/I+2):0,r=new e(P);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=w(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=P.div(r)),n?F(r,Y,H):r},M.toPrecision=function(t,e){return h(this,null!=t&&z(t,1,D,24,"precision")?0|t:null,e,24)},M.toString=function(t){var e,r=this,i=r.s,f=r.e;return null===f?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&z(t,2,64,25,"base")?n(l(e,f),0|t,10,i):C>=f||f>=j?a(e,f):l(e,f),0>i&&r.c[0]&&(e="-"+e)),e},M.truncated=M.trunc=function(){return F(new e(this),this.e+1,1)},M.valueOf=M.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function f(t,e){var n,r,i=t.c,o=e.c,f=t.s,u=e.s,s=t.e,c=e.e;if(!f||!u)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-u:f;if(f!=u)return f;if(n=0>f,r=s==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return s>c^n?1:-1;for(u=(s=i.length)<(c=o.length)?s:c,f=0;u>f;f++)if(i[f]!=o[f])return i[f]>o[f]^n?1:-1;return s==c?0:s>c^n?1:-1}function u(t,e,n){return(t=p(t))>=e&&n>=t}function s(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],f=0,u=t.length;u>f;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=N.indexOf(t.charAt(f++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function a(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?v(t):w(t)}var h,g,m,d=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,v=Math.ceil,w=Math.floor,y=" not a boolean or binary digit",b="rounding mode",O="number type has more than 15 significant digits",N="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",E=1e14,I=14,x=9007199254740991,_=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],T=1e7,D=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!g)try{g=t("crypto")}catch(S){}}else n.BigNumber=h}(this)},{crypto:1}],natspec:[function(t,e){var n=t("./node_modules/ethereum.js/lib/abi.js"),r=function(){var t=function(t,e){Object.keys(t).forEach(function(n){e[n]=t[n]})},e=function(t){return Object.keys(t).reduce(function(t,e){return t+"var "+e+" = context['"+e+"'];\n"},"")},r=function(t,e){return t.filter(function(t){return t.name===e})[0]},i=function(t,e){var r=n.formatOutput(t.inputs,"0x"+e.params[0].data.slice(10));return t.inputs.reduce(function(t,e,n){return t[e.name]=r[n],t},{})},o=function(t,e){var n,r="",i=/\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim,o=0;try{for(;null!==(n=i.exec(t));){var f=i.lastIndex-n[0].length,u=n[0].slice(1,n[0].length-1);r+=t.slice(o,f);var s=e(u);r+=s,o=i.lastIndex}r+=t.slice(o)}catch(c){throw new Error("Natspec evaluation failed, wrong input params")}return r},f=function(n,f){var u={};if(f)try{var s=r(f.abi,f.method),c=i(s,f.transaction);t(c,u)}catch(a){throw new Error("Natspec evaluation failed, method does not exist")}var l=e(u),p=o(n,function(t){var e=new Function("context",l+"return "+t+";");return e(u).toString()});return p},u=function(t,e){try{return f(t,e)}catch(n){return n.message}};return{evaluateExpression:f,evaluateExpressionSafe:u}}();e.exports=r},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); -` -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. -*/ -/** - * @file abi.js - * @author Marek Kotewicz - * @author Gav Wood - * @date 2014 - */ - -var utils = require('../utils/utils'); -var c = require('../utils/config'); -var types = require('./types'); -var f = require('./formatters'); -var solUtils = require('./utils'); - -/** - * throw incorrect type error - * - * @method throwTypeError - * @param {String} type - * @throws incorrect type error - */ -var throwTypeError = function (type) { - throw new Error('parser does not support type: ' + type); -}; - -/** This method should be called if we want to check if givent type is an array type - * - * @method isArrayType - * @param {String} type name - * @returns {Boolean} true if it is, otherwise false - */ -var isArrayType = function (type) { - return type.slice(-2) === '[]'; -}; - -/** - * This method should be called to return dynamic type length in hex - * - * @method dynamicTypeBytes - * @param {String} type - * @param {String|Array} dynamic type - * @return {String} length of dynamic type in hex or empty string if type is not dynamic - */ -var dynamicTypeBytes = function (type, value) { - // TODO: decide what to do with array of strings - if (isArrayType(type) || type === 'bytes') - return f.formatInputInt(value.length); - return ""; -}; - -var inputTypes = types.inputTypes(); - -/** - * Formats input params to bytes - * - * @method formatInput - * @param {Array} abi inputs of method - * @param {Array} params that will be formatted to bytes - * @returns bytes representation of input params - */ -var formatInput = function (inputs, params) { - var bytes = ""; - var toAppendConstant = ""; - var toAppendArrayContent = ""; - - /// first we iterate in search for dynamic - inputs.forEach(function (input, index) { - bytes += dynamicTypeBytes(input.type, params[index]); - }); - - inputs.forEach(function (input, i) { - /*jshint maxcomplexity:5 */ - var typeMatch = false; - for (var j = 0; j < inputTypes.length && !typeMatch; j++) { - typeMatch = inputTypes[j].type(inputs[i].type, params[i]); - } - if (!typeMatch) { - throwTypeError(inputs[i].type); - } - - var formatter = inputTypes[j - 1].format; - - if (isArrayType(inputs[i].type)) - toAppendArrayContent += params[i].reduce(function (acc, curr) { - return acc + formatter(curr); - }, ""); - else if (inputs[i].type === 'bytes') - toAppendArrayContent += formatter(params[i]); - else - toAppendConstant += formatter(params[i]); - }); - - bytes += toAppendConstant + toAppendArrayContent; - - return bytes; -}; - -/** - * This method should be called to predict the length of dynamic type - * - * @method dynamicBytesLength - * @param {String} type - * @returns {Number} length of dynamic type, 0 or multiplication of ETH_PADDING (32) - */ -var dynamicBytesLength = function (type) { - if (isArrayType(type) || type === 'bytes') - return c.ETH_PADDING * 2; - return 0; -}; - -var outputTypes = types.outputTypes(); - -/** - * Formats output bytes back to param list - * - * @method formatOutput - * @param {Array} abi outputs of method - * @param {String} bytes represention of output - * @returns {Array} output params - */ -var formatOutput = function (outs, output) { - - output = output.slice(2); - var result = []; - var padding = c.ETH_PADDING * 2; - - var dynamicPartLength = outs.reduce(function (acc, curr) { - return acc + dynamicBytesLength(curr.type); - }, 0); - - var dynamicPart = output.slice(0, dynamicPartLength); - output = output.slice(dynamicPartLength); - - outs.forEach(function (out, i) { - /*jshint maxcomplexity:6 */ - var typeMatch = false; - for (var j = 0; j < outputTypes.length && !typeMatch; j++) { - typeMatch = outputTypes[j].type(outs[i].type); - } - - if (!typeMatch) { - throwTypeError(outs[i].type); - } - - var formatter = outputTypes[j - 1].format; - if (isArrayType(outs[i].type)) { - var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); - dynamicPart = dynamicPart.slice(padding); - var array = []; - for (var k = 0; k < size; k++) { - array.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } - result.push(array); - } - else if (types.prefixedType('bytes')(outs[i].type)) { - dynamicPart = dynamicPart.slice(padding); - result.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } else { - result.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } - }); - - return result; -}; - -/** - * Should be called to create input parser for contract with given abi - * - * @method inputParser - * @param {Array} contract abi - * @returns {Object} input parser object for given json abi - * TODO: refactor creating the parser, do not double logic from contract - */ -var inputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function () { - var params = Array.prototype.slice.call(arguments); - return formatInput(method.inputs, params); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -/** - * Should be called to create output parser for contract with given abi - * - * @method outputParser - * @param {Array} contract abi - * @returns {Object} output parser for given json abi - */ -var outputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function (output) { - return formatOutput(method.outputs, output); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -var formatConstructorParams = function (abi, params) { - var constructor = solUtils.getConstructor(abi, params.length); - if (!constructor) { - if (params.length > 0) { - console.warn("didn't found matching constructor, using default one"); - } - return ''; - } - return formatInput(constructor.inputs, params); -}; - -module.exports = { - inputParser: inputParser, - outputParser: outputParser, - formatInput: formatInput, - formatOutput: formatOutput, - formatConstructorParams: formatConstructorParams -}; - -},{"../utils/config":6,"../utils/utils":7,"./formatters":3,"./types":4,"./utils":5}],3:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file formatters.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var BigNumber = require('bignumber.js'); -var utils = require('../utils/utils'); -var c = require('../utils/config'); - -/** - * Formats input value to byte representation of int - * If value is negative, return it's two's complement - * If the value is floating point, round it down - * - * @method formatInputInt - * @param {String|Number|BigNumber} value that needs to be formatted - * @returns {String} right-aligned byte representation of int - */ -var formatInputInt = function (value) { - var padding = c.ETH_PADDING * 2; - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - return utils.padLeft(utils.toTwosComplement(value).round().toString(16), padding); -}; - -/** - * Formats input value to byte representation of string - * - * @method formatInputString - * @param {String} - * @returns {String} left-algined byte representation of string - */ -var formatInputString = function (value) { - return utils.fromAscii(value, c.ETH_PADDING).substr(2); -}; - -/** - * Formats input value to byte representation of bool - * - * @method formatInputBool - * @param {Boolean} - * @returns {String} right-aligned byte representation bool - */ -var formatInputBool = function (value) { - return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); -}; - -/** - * Formats input value to byte representation of real - * Values are multiplied by 2^m and encoded as integers - * - * @method formatInputReal - * @param {String|Number|BigNumber} - * @returns {String} byte representation of real - */ -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - -/** - * Check if input value is negative - * - * @method signedIsNegative - * @param {String} value is hex format - * @returns {Boolean} true if it is negative, otherwise false - */ -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; -}; - -/** - * Formats right-aligned output bytes to int - * - * @method formatOutputInt - * @param {String} bytes - * @returns {BigNumber} right-aligned output bytes formatted to big number - */ -var formatOutputInt = function (value) { - - value = value || "0"; - - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); - } - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to uint - * - * @method formatOutputUInt - * @param {String} bytes - * @returns {BigNumeber} right-aligned output bytes formatted to uint - */ -var formatOutputUInt = function (value) { - value = value || "0"; - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to real - * - * @method formatOutputReal - * @param {String} - * @returns {BigNumber} input bytes formatted to real - */ -var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Formats right-aligned output bytes to ureal - * - * @method formatOutputUReal - * @param {String} - * @returns {BigNumber} input bytes formatted to ureal - */ -var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Should be used to format output hash - * - * @method formatOutputHash - * @param {String} - * @returns {String} right-aligned output bytes formatted to hex - */ -var formatOutputHash = function (value) { - return "0x" + value; -}; - -/** - * Should be used to format output bool - * - * @method formatOutputBool - * @param {String} - * @returns {Boolean} right-aligned input bytes formatted to bool - */ -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; -}; - -/** - * Should be used to format output string - * - * @method formatOutputString - * @param {Sttring} left-aligned hex representation of string - * @returns {String} ascii string - */ -var formatOutputString = function (value) { - return utils.toAscii(value); -}; - -/** - * Should be used to format output address - * - * @method formatOutputAddress - * @param {String} right-aligned input bytes - * @returns {String} address - */ -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); -}; - -module.exports = { - formatInputInt: formatInputInt, - formatInputString: formatInputString, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputHash: formatOutputHash, - formatOutputBool: formatOutputBool, - formatOutputString: formatOutputString, - formatOutputAddress: formatOutputAddress -}; - - -},{"../utils/config":6,"../utils/utils":7,"bignumber.js":8}],4:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file types.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var f = require('./formatters'); - -/// @param expected type prefix (string) -/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false -var prefixedType = function (prefix) { - return function (type) { - return type.indexOf(prefix) === 0; - }; -}; - -/// @param expected type name (string) -/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false -var namedType = function (name) { - return function (type) { - return name === type; - }; -}; - -/// Setups input formatters for solidity types -/// @returns an array of input formatters -var inputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatInputInt }, - { type: prefixedType('int'), format: f.formatInputInt }, - { type: prefixedType('bytes'), format: f.formatInputString }, - { type: prefixedType('real'), format: f.formatInputReal }, - { type: prefixedType('ureal'), format: f.formatInputReal }, - { type: namedType('address'), format: f.formatInputInt }, - { type: namedType('bool'), format: f.formatInputBool } - ]; -}; - -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatOutputUInt }, - { type: prefixedType('int'), format: f.formatOutputInt }, - { type: prefixedType('bytes'), format: f.formatOutputString }, - { type: prefixedType('real'), format: f.formatOutputReal }, - { type: prefixedType('ureal'), format: f.formatOutputUReal }, - { type: namedType('address'), format: f.formatOutputAddress }, - { type: namedType('bool'), format: f.formatOutputBool } - ]; -}; - -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; - - -},{"./formatters":3}],5:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** - * @file utils.js - * @author Marek Kotewicz - * @date 2015 - */ - -/** - * Returns the contstructor with matching number of arguments - * - * @method getConstructor - * @param {Array} abi - * @param {Number} numberOfArgs - * @returns {Object} constructor function abi - */ -var getConstructor = function (abi, numberOfArgs) { - return abi.filter(function (f) { - return f.type === 'constructor' && f.inputs.length === numberOfArgs; - })[0]; -}; - -/** - * Filters all functions from input abi - * - * @method filterFunctions - * @param {Array} abi - * @returns {Array} abi array with filtered objects of type 'function' - */ -var filterFunctions = function (json) { - return json.filter(function (current) { - return current.type === 'function'; - }); -}; - -/** - * Filters all events from input abi - * - * @method filterEvents - * @param {Array} abi - * @returns {Array} abi array with filtered objects of type 'event' - */ -var filterEvents = function (json) { - return json.filter(function (current) { - return current.type === 'event'; - }); -}; - -module.exports = { - getConstructor: getConstructor, - filterFunctions: filterFunctions, - filterEvents: filterEvents -}; - - -},{}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file config.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] config - * @constructor - */ - -/// required to define ETH_BIGNUMBER_ROUNDING_MODE -var BigNumber = require('bignumber.js'); - -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' -]; - -module.exports = { - ETH_PADDING: 32, - ETH_SIGNATURE_LENGTH: 4, - ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000, - ETH_DEFAULTBLOCK: 'latest' -}; - - -},{"bignumber.js":8}],7:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file utils.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] utils - * @constructor - */ - -var BigNumber = require('bignumber.js'); - -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'szabo': '1000000000000', - 'finney': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; - -/** - * Should be called to pad string to expected length - * - * @method padLeft - * @param {String} string to be padded - * @param {Number} characters that result string should have - * @param {String} sign, by default 0 - * @returns {String} right aligned string - */ -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/** Finds first index of array element matching pattern - * - * @method findIndex - * @param {Array} - * @param {Function} pattern - * @returns {Number} index of element - */ -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; -}; - -/** - * Should be called to get sting from its hex representation - * - * @method toAscii - * @param {String} string in hex - * @returns {String} ascii string representation of hex value - */ -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method fromAscii - * @param {String} string - * @returns {String} hex representation of input string - */ -var toHexNative = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return hex; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method fromAscii - * @param {String} string - * @param {Number} optional padding - * @returns {String} hex representation of input string - */ -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHexNative(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; - -/** - * Should be called to get display name of contract function - * - * @method extractDisplayName - * @param {String} name of function/event - * @returns {String} display name for function/event eg. multiply(uint256) -> multiply - */ -var extractDisplayName = function (name) { - var length = name.indexOf('('); - return length !== -1 ? name.substr(0, length) : name; -}; - -/// @returns overloaded part of function/event name -var extractTypeName = function (name) { - /// TODO: make it invulnerable - var length = name.indexOf('('); - return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; -}; - -/** - * Converts value to its decimal representation in string - * - * @method toDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var toDecimal = function (value) { - return toBigNumber(value).toNumber(); -}; - -/** - * Converts value to its hex representation - * - * @method fromDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var fromDecimal = function (value) { - var number = toBigNumber(value); - var result = number.toString(16); - - return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result; -}; - -/** - * Auto converts any given value into its hex representation. - * - * And even stringifys objects before. - * - * @method toHex - * @param {String|Number|BigNumber|Object} - * @return {String} - */ -var toHex = function (val) { - /*jshint maxcomplexity:7 */ - - if (isBoolean(val)) - return fromDecimal(+val); - - if (isBigNumber(val)) - return fromDecimal(val); - - if (isObject(val)) - return fromAscii(JSON.stringify(val)); - - // if its a negative number, pass it through fromDecimal - if (isString(val)) { - if (val.indexOf('-0x') === 0) - return fromDecimal(val); - else if (!isFinite(val)) - return fromAscii(val); - } - - return fromDecimal(val); -}; - -/** - * Returns value of unit in Wei - * - * @method getValueOfUnit - * @param {String} unit the unit to convert to, default ether - * @returns {BigNumber} value of the unit (in Wei) - * @throws error if the unit is not correct:w - */ -var getValueOfUnit = function (unit) { - unit = unit ? unit.toLowerCase() : 'ether'; - var unitValue = unitMap[unit]; - if (unitValue === undefined) { - throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); - } - return new BigNumber(unitValue, 10); -}; - -/** - * Takes a number of wei and converts it to any other ether unit. - * - * Possible units are: - * - kwei/ada - * - mwei/babbage - * - gwei/shannon - * - szabo - * - finney - * - ether - * - kether/grand/einstein - * - mether - * - gether - * - tether - * - * @method fromWei - * @param {Number|String} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert to, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var fromWei = function(number, unit) { - var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes a number of a unit and converts it to wei. - * - * Possible units are: - * - kwei/ada - * - mwei/babbage - * - gwei/shannon - * - szabo - * - finney - * - ether - * - kether/grand/einstein - * - mether - * - gether - * - tether - * - * @method toWei - * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert from, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes an input and transforms it into an bignumber - * - * @method toBigNumber - * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber - * @return {BigNumber} BigNumber -*/ -var toBigNumber = function(number) { - /*jshint maxcomplexity:5 */ - number = number || 0; - if (isBigNumber(number)) - return number; - - if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { - return new BigNumber(number.replace('0x',''), 16); - } - - return new BigNumber(number.toString(10), 10); -}; - -/** - * Takes and input transforms it into bignumber and if it is negative value, into two's complement - * - * @method toTwosComplement - * @param {Number|String|BigNumber} - * @return {BigNumber} - */ -var toTwosComplement = function (number) { - var bigNumber = toBigNumber(number); - if (bigNumber.lessThan(0)) { - return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); - } - return bigNumber; -}; - -/** - * Checks if the given string is strictly an address - * - * @method isStrictAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isStrictAddress = function (address) { - return /^0x[0-9a-f]{40}$/.test(address); -}; - -/** - * Checks if the given string is an address - * - * @method isAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isAddress = function (address) { - return /^(0x)?[0-9a-f]{40}$/.test(address); -}; - -/** - * Transforms given string to valid 20 bytes-length addres with 0x prefix - * - * @method toAddress - * @param {String} address - * @return {String} formatted address - */ -var toAddress = function (address) { - if (isStrictAddress(address)) { - return address; - } - - if (/^[0-9a-f]{40}$/.test(address)) { - return '0x' + address; - } - - return '0x' + padLeft(toHex(address).substr(2), 40); -}; - -/** - * Returns true if object is BigNumber, otherwise false - * - * @method isBigNumber - * @param {Object} - * @return {Boolean} - */ -var isBigNumber = function (object) { - return object instanceof BigNumber || - (object && object.constructor && object.constructor.name === 'BigNumber'); -}; - -/** - * Returns true if object is string, otherwise false - * - * @method isString - * @param {Object} - * @return {Boolean} - */ -var isString = function (object) { - return typeof object === 'string' || - (object && object.constructor && object.constructor.name === 'String'); -}; - -/** - * Returns true if object is function, otherwise false - * - * @method isFunction - * @param {Object} - * @return {Boolean} - */ -var isFunction = function (object) { - return typeof object === 'function'; -}; - -/** - * Returns true if object is Objet, otherwise false - * - * @method isObject - * @param {Object} - * @return {Boolean} - */ -var isObject = function (object) { - return typeof object === 'object'; -}; - -/** - * Returns true if object is boolean, otherwise false - * - * @method isBoolean - * @param {Object} - * @return {Boolean} - */ -var isBoolean = function (object) { - return typeof object === 'boolean'; -}; - -/** - * Returns true if object is array, otherwise false - * - * @method isArray - * @param {Object} - * @return {Boolean} - */ -var isArray = function (object) { - return object instanceof Array; -}; - -/** - * Returns true if given string is valid json object - * - * @method isJson - * @param {String} - * @return {Boolean} - */ -var isJson = function (str) { - try { - return !!JSON.parse(str); - } catch (e) { - return false; - } -}; - -module.exports = { - padLeft: padLeft, - findIndex: findIndex, - toHex: toHex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - toWei: toWei, - fromWei: fromWei, - toBigNumber: toBigNumber, - toTwosComplement: toTwosComplement, - toAddress: toAddress, - isBigNumber: isBigNumber, - isStrictAddress: isStrictAddress, - isAddress: isAddress, - isFunction: isFunction, - isString: isString, - isObject: isObject, - isBoolean: isBoolean, - isArray: isArray, - isJson: isJson -}; - - -},{"bignumber.js":8}],8:[function(require,module,exports){ -/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */ - -;(function (global) { - 'use strict'; - - /* - bignumber.js v2.0.7 - A JavaScript library for arbitrary-precision arithmetic. - https://github.com/MikeMcl/bignumber.js - Copyright (c) 2015 Michael Mclaughlin - MIT Expat Licence - */ - - - var BigNumber, crypto, parseNumeric, - isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, - mathceil = Math.ceil, - mathfloor = Math.floor, - notBool = ' not a boolean or binary digit', - roundingMode = 'rounding mode', - tooManyDigits = 'number type has more than 15 significant digits', - ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', - BASE = 1e14, - LOG_BASE = 14, - MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 - // MAX_INT32 = 0x7fffffff, // 2^31 - 1 - POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], - SQRT_BASE = 1e7, - - /* - * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and - * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an - * exception is thrown (if ERRORS is true). - */ - MAX = 1E9; // 0 to MAX_INT32 - - - /* - * Create and return a BigNumber constructor. - */ - function another(configObj) { - var div, - - // id tracks the caller function, so its name can be included in error messages. - id = 0, - P = BigNumber.prototype, - ONE = new BigNumber(1), - - - /********************************* EDITABLE DEFAULTS **********************************/ - - - /* - * The default values below must be integers within the inclusive ranges stated. - * The values can also be changed at run-time using BigNumber.config. - */ - - // The maximum number of decimal places for operations involving division. - DECIMAL_PLACES = 20, // 0 to MAX - - /* - * The rounding mode used when rounding to the above decimal places, and when using - * toExponential, toFixed, toFormat and toPrecision, and round (default value). - * UP 0 Away from zero. - * DOWN 1 Towards zero. - * CEIL 2 Towards +Infinity. - * FLOOR 3 Towards -Infinity. - * HALF_UP 4 Towards nearest neighbour. If equidistant, up. - * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. - * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. - * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. - * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. - */ - ROUNDING_MODE = 4, // 0 to 8 - - // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] - - // The exponent value at and beneath which toString returns exponential notation. - // Number type: -7 - TO_EXP_NEG = -7, // 0 to -MAX - - // The exponent value at and above which toString returns exponential notation. - // Number type: 21 - TO_EXP_POS = 21, // 0 to MAX - - // RANGE : [MIN_EXP, MAX_EXP] - - // The minimum exponent value, beneath which underflow to zero occurs. - // Number type: -324 (5e-324) - MIN_EXP = -1e7, // -1 to -MAX - - // The maximum exponent value, above which overflow to Infinity occurs. - // Number type: 308 (1.7976931348623157e+308) - // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. - MAX_EXP = 1e7, // 1 to MAX - - // Whether BigNumber Errors are ever thrown. - ERRORS = true, // true or false - - // Change to intValidatorNoErrors if ERRORS is false. - isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors - - // Whether to use cryptographically-secure random number generation, if available. - CRYPTO = false, // true or false - - /* - * The modulo mode used when calculating the modulus: a mod n. - * The quotient (q = a / n) is calculated according to the corresponding rounding mode. - * The remainder (r) is calculated as: r = a - n * q. - * - * UP 0 The remainder is positive if the dividend is negative, else is negative. - * DOWN 1 The remainder has the same sign as the dividend. - * This modulo mode is commonly known as 'truncated division' and is - * equivalent to (a % n) in JavaScript. - * FLOOR 3 The remainder has the same sign as the divisor (Python %). - * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. - * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). - * The remainder is always positive. - * - * The truncated division, floored division, Euclidian division and IEEE 754 remainder - * modes are commonly used for the modulus operation. - * Although the other rounding modes can also be used, they may not give useful results. - */ - MODULO_MODE = 1, // 0 to 9 - - // The maximum number of significant digits of the result of the toPower operation. - // If POW_PRECISION is 0, there will be unlimited significant digits. - POW_PRECISION = 100, // 0 to MAX - - // The format specification used by the BigNumber.prototype.toFormat method. - FORMAT = { - decimalSeparator: '.', - groupSeparator: ',', - groupSize: 3, - secondaryGroupSize: 0, - fractionGroupSeparator: '\xA0', // non-breaking space - fractionGroupSize: 0 - }; - - - /******************************************************************************************/ - - - // CONSTRUCTOR - - - /* - * The BigNumber constructor and exported function. - * Create and return a new instance of a BigNumber object. - * - * n {number|string|BigNumber} A numeric value. - * [b] {number} The base of n. Integer, 2 to 64 inclusive. - */ - function BigNumber( n, b ) { - var c, e, i, num, len, str, - x = this; - - // Enable constructor usage without new. - if ( !( x instanceof BigNumber ) ) { - - // 'BigNumber() constructor call without new: {n}' - if (ERRORS) raise( 26, 'constructor call without new', n ); - return new BigNumber( n, b ); - } - - // 'new BigNumber() base not an integer: {b}' - // 'new BigNumber() base out of range: {b}' - if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { - - // Duplicate. - if ( n instanceof BigNumber ) { - x.s = n.s; - x.e = n.e; - x.c = ( n = n.c ) ? n.slice() : n; - id = 0; - return; - } - - if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { - x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; - - // Fast path for integers. - if ( n === ~~n ) { - for ( e = 0, i = n; i >= 10; i /= 10, e++ ); - x.e = e; - x.c = [n]; - id = 0; - return; - } - - str = n + ''; - } else { - if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - } else { - b = b | 0; - str = n + ''; - - // Ensure return value is rounded to DECIMAL_PLACES as with other bases. - // Allow exponential notation to be used with base 10 argument. - if ( b == 10 ) { - x = new BigNumber( n instanceof BigNumber ? n : str ); - return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); - } - - // Avoid potential interpretation of Infinity and NaN as base 44+ values. - // Any number in exponential form will fail due to the [Ee][+-]. - if ( ( num = typeof n == 'number' ) && n * 0 != 0 || - !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + - '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { - return parseNumeric( x, str, num, b ); - } - - if (num) { - x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; - - if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { - - // 'new BigNumber() number type has more than 15 significant digits: {n}' - raise( id, tooManyDigits, n ); - } - - // Prevent later check for length on converted number. - num = false; - } else { - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - - str = convertBase( str, 10, b, x.s ); - } - - // Decimal point? - if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); - - // Exponential form? - if ( ( i = str.search( /e/i ) ) > 0 ) { - - // Determine exponent. - if ( e < 0 ) e = i; - e += +str.slice( i + 1 ); - str = str.substring( 0, i ); - } else if ( e < 0 ) { - - // Integer. - e = str.length; - } - - // Determine leading zeros. - for ( i = 0; str.charCodeAt(i) === 48; i++ ); - - // Determine trailing zeros. - for ( len = str.length; str.charCodeAt(--len) === 48; ); - str = str.slice( i, len + 1 ); - - if (str) { - len = str.length; - - // Disallow numbers with over 15 significant digits if number type. - // 'new BigNumber() number type has more than 15 significant digits: {n}' - if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); - - e = e - i - 1; - - // Overflow? - if ( e > MAX_EXP ) { - - // Infinity. - x.c = x.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - x.c = [ x.e = 0 ]; - } else { - x.e = e; - x.c = []; - - // Transform base - - // e is the base 10 exponent. - // i is where to slice str to get the first element of the coefficient array. - i = ( e + 1 ) % LOG_BASE; - if ( e < 0 ) i += LOG_BASE; - - if ( i < len ) { - if (i) x.c.push( +str.slice( 0, i ) ); - - for ( len -= LOG_BASE; i < len; ) { - x.c.push( +str.slice( i, i += LOG_BASE ) ); - } - - str = str.slice(i); - i = LOG_BASE - str.length; - } else { - i -= len; - } - - for ( ; i--; str += '0' ); - x.c.push( +str ); - } - } else { - - // Zero. - x.c = [ x.e = 0 ]; - } - - id = 0; - } - - - // CONSTRUCTOR PROPERTIES - - - BigNumber.another = another; - - BigNumber.ROUND_UP = 0; - BigNumber.ROUND_DOWN = 1; - BigNumber.ROUND_CEIL = 2; - BigNumber.ROUND_FLOOR = 3; - BigNumber.ROUND_HALF_UP = 4; - BigNumber.ROUND_HALF_DOWN = 5; - BigNumber.ROUND_HALF_EVEN = 6; - BigNumber.ROUND_HALF_CEIL = 7; - BigNumber.ROUND_HALF_FLOOR = 8; - BigNumber.EUCLID = 9; - - - /* - * Configure infrequently-changing library-wide settings. - * - * Accept an object or an argument list, with one or many of the following properties or - * parameters respectively: - * - * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive - * ROUNDING_MODE {number} Integer, 0 to 8 inclusive - * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or - * [integer -MAX to 0 incl., 0 to MAX incl.] - * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - * [integer -MAX to -1 incl., integer 1 to MAX incl.] - * ERRORS {boolean|number} true, false, 1 or 0 - * CRYPTO {boolean|number} true, false, 1 or 0 - * MODULO_MODE {number} 0 to 9 inclusive - * POW_PRECISION {number} 0 to MAX inclusive - * FORMAT {object} See BigNumber.prototype.toFormat - * decimalSeparator {string} - * groupSeparator {string} - * groupSize {number} - * secondaryGroupSize {number} - * fractionGroupSeparator {string} - * fractionGroupSize {number} - * - * (The values assigned to the above FORMAT object properties are not checked for validity.) - * - * E.g. - * BigNumber.config(20, 4) is equivalent to - * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) - * - * Ignore properties/parameters set to null or undefined. - * Return an object with the properties current values. - */ - BigNumber.config = function () { - var v, p, - i = 0, - r = {}, - a = arguments, - o = a[0], - has = o && typeof o == 'object' - ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } - : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; - - // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. - // 'config() DECIMAL_PLACES not an integer: {v}' - // 'config() DECIMAL_PLACES out of range: {v}' - if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { - DECIMAL_PLACES = v | 0; - } - r[p] = DECIMAL_PLACES; - - // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. - // 'config() ROUNDING_MODE not an integer: {v}' - // 'config() ROUNDING_MODE out of range: {v}' - if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { - ROUNDING_MODE = v | 0; - } - r[p] = ROUNDING_MODE; - - // EXPONENTIAL_AT {number|number[]} - // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. - // 'config() EXPONENTIAL_AT not an integer: {v}' - // 'config() EXPONENTIAL_AT out of range: {v}' - if ( has( p = 'EXPONENTIAL_AT' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { - TO_EXP_NEG = v[0] | 0; - TO_EXP_POS = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); - } - } - r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; - - // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. - // 'config() RANGE not an integer: {v}' - // 'config() RANGE cannot be zero: {v}' - // 'config() RANGE out of range: {v}' - if ( has( p = 'RANGE' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { - MIN_EXP = v[0] | 0; - MAX_EXP = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); - else if (ERRORS) raise( 2, p + ' cannot be zero', v ); - } - } - r[p] = [ MIN_EXP, MAX_EXP ]; - - // ERRORS {boolean|number} true, false, 1 or 0. - // 'config() ERRORS not a boolean or binary digit: {v}' - if ( has( p = 'ERRORS' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - id = 0; - isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = ERRORS; - - // CRYPTO {boolean|number} true, false, 1 or 0. - // 'config() CRYPTO not a boolean or binary digit: {v}' - // 'config() crypto unavailable: {crypto}' - if ( has( p = 'CRYPTO' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - CRYPTO = !!( v && crypto && typeof crypto == 'object' ); - if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = CRYPTO; - - // MODULO_MODE {number} Integer, 0 to 9 inclusive. - // 'config() MODULO_MODE not an integer: {v}' - // 'config() MODULO_MODE out of range: {v}' - if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { - MODULO_MODE = v | 0; - } - r[p] = MODULO_MODE; - - // POW_PRECISION {number} Integer, 0 to MAX inclusive. - // 'config() POW_PRECISION not an integer: {v}' - // 'config() POW_PRECISION out of range: {v}' - if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { - POW_PRECISION = v | 0; - } - r[p] = POW_PRECISION; - - // FORMAT {object} - // 'config() FORMAT not an object: {v}' - if ( has( p = 'FORMAT' ) ) { - - if ( typeof v == 'object' ) { - FORMAT = v; - } else if (ERRORS) { - raise( 2, p + ' not an object', v ); - } - } - r[p] = FORMAT; - - return r; - }; - - - /* - * Return a new BigNumber whose value is the maximum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; - - - /* - * Return a new BigNumber whose value is the minimum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; - - - /* - * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, - * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing - * zeros are produced). - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * - * 'random() decimal places not an integer: {dp}' - * 'random() decimal places out of range: {dp}' - * 'random() crypto unavailable: {crypto}' - */ - BigNumber.random = (function () { - var pow2_53 = 0x20000000000000; - - // Return a 53 bit integer n, where 0 <= n < 9007199254740992. - // Check if Math.random() produces more than 32 bits of randomness. - // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. - // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. - var random53bitInt = (Math.random() * pow2_53) & 0x1fffff - ? function () { return mathfloor( Math.random() * pow2_53 ); } - : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + - (Math.random() * 0x800000 | 0); }; - - return function (dp) { - var a, b, e, k, v, - i = 0, - c = [], - rand = new BigNumber(ONE); - - dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; - k = mathceil( dp / LOG_BASE ); - - if (CRYPTO) { - - // Browsers supporting crypto.getRandomValues. - if ( crypto && crypto.getRandomValues ) { - - a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); - - for ( ; i < k; ) { - - // 53 bits: - // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) - // 11111 11111111 11111111 11111111 11100000 00000000 00000000 - // ((Math.pow(2, 32) - 1) >>> 11).toString(2) - // 11111 11111111 11111111 - // 0x20000 is 2^21. - v = a[i] * 0x20000 + (a[i + 1] >>> 11); - - // Rejection sampling: - // 0 <= v < 9007199254740992 - // Probability that v >= 9e15, is - // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 - if ( v >= 9e15 ) { - b = crypto.getRandomValues( new Uint32Array(2) ); - a[i] = b[0]; - a[i + 1] = b[1]; - } else { - - // 0 <= v <= 8999999999999999 - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 2; - } - } - i = k / 2; - - // Node.js supporting crypto.randomBytes. - } else if ( crypto && crypto.randomBytes ) { - - // buffer - a = crypto.randomBytes( k *= 7 ); - - for ( ; i < k; ) { - - // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 - // 0x100000000 is 2^32, 0x1000000 is 2^24 - // 11111 11111111 11111111 11111111 11111111 11111111 11111111 - // 0 <= v < 9007199254740992 - v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + - ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + - ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; - - if ( v >= 9e15 ) { - crypto.randomBytes(7).copy( a, i ); - } else { - - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 7; - } - } - i = k / 7; - } else if (ERRORS) { - raise( 14, 'crypto unavailable', crypto ); - } - } - - // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. - if (!i) { - - for ( ; i < k; ) { - v = random53bitInt(); - if ( v < 9e15 ) c[i++] = v % 1e14; - } - } - - k = c[--i]; - dp %= LOG_BASE; - - // Convert trailing digits to zeros according to dp. - if ( k && dp ) { - v = POWS_TEN[LOG_BASE - dp]; - c[i] = mathfloor( k / v ) * v; - } - - // Remove trailing elements which are zero. - for ( ; c[i] === 0; c.pop(), i-- ); - - // Zero? - if ( i < 0 ) { - c = [ e = 0 ]; - } else { - - // Remove leading elements which are zero and adjust exponent accordingly. - for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); - - // Count the digits of the first element of c to determine leading zeros, and... - for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); - - // adjust the exponent accordingly. - if ( i < LOG_BASE ) e -= LOG_BASE - i; - } - - rand.e = e; - rand.c = c; - return rand; - }; - })(); - - - // PRIVATE FUNCTIONS - - - // Convert a numeric string of baseIn to a numeric string of baseOut. - function convertBase( str, baseOut, baseIn, sign ) { - var d, e, k, r, x, xc, y, - i = str.indexOf( '.' ), - dp = DECIMAL_PLACES, - rm = ROUNDING_MODE; - - if ( baseIn < 37 ) str = str.toLowerCase(); - - // Non-integer. - if ( i >= 0 ) { - k = POW_PRECISION; - - // Unlimited precision. - POW_PRECISION = 0; - str = str.replace( '.', '' ); - y = new BigNumber(baseIn); - x = y.pow( str.length - i ); - POW_PRECISION = k; - - // Convert str as if an integer, then restore the fraction part by dividing the - // result by its base raised to a power. - y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); - y.e = y.c.length; - } - - // Convert the number as integer. - xc = toBaseOut( str, baseIn, baseOut ); - e = k = xc.length; - - // Remove trailing zeros. - for ( ; xc[--k] == 0; xc.pop() ); - if ( !xc[0] ) return '0'; - - if ( i < 0 ) { - --e; - } else { - x.c = xc; - x.e = e; - - // sign is needed for correct rounding. - x.s = sign; - x = div( x, y, dp, rm, baseOut ); - xc = x.c; - r = x.r; - e = x.e; - } - - d = e + dp + 1; - - // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. - i = xc[d]; - k = baseOut / 2; - r = r || d < 0 || xc[d + 1] != null; - - r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( d < 1 || !xc[0] ) { - - // 1^-dp or 0. - str = r ? toFixedPoint( '1', -dp ) : '0'; - } else { - xc.length = d; - - if (r) { - - // Rounding up may mean the previous digit has to be rounded up and so on. - for ( --baseOut; ++xc[--d] > baseOut; ) { - xc[d] = 0; - - if ( !d ) { - ++e; - xc.unshift(1); - } - } - } - - // Determine trailing zeros. - for ( k = xc.length; !xc[--k]; ); - - // E.g. [4, 11, 15] becomes 4bf. - for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); - str = toFixedPoint( str, e ); - } - - // The caller will add the sign. - return str; - } - - - // Perform division in the specified base. Called by div and convertBase. - div = (function () { - - // Assume non-zero x and k. - function multiply( x, k, base ) { - var m, temp, xlo, xhi, - carry = 0, - i = x.length, - klo = k % SQRT_BASE, - khi = k / SQRT_BASE | 0; - - for ( x = x.slice(); i--; ) { - xlo = x[i] % SQRT_BASE; - xhi = x[i] / SQRT_BASE | 0; - m = khi * xlo + xhi * klo; - temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; - carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; - x[i] = temp % base; - } - - if (carry) x.unshift(carry); - - return x; - } - - function compare( a, b, aL, bL ) { - var i, cmp; - - if ( aL != bL ) { - cmp = aL > bL ? 1 : -1; - } else { - - for ( i = cmp = 0; i < aL; i++ ) { - - if ( a[i] != b[i] ) { - cmp = a[i] > b[i] ? 1 : -1; - break; - } - } - } - return cmp; - } - - function subtract( a, b, aL, base ) { - var i = 0; - - // Subtract b from a. - for ( ; aL--; ) { - a[aL] -= i; - i = a[aL] < b[aL] ? 1 : 0; - a[aL] = i * base + a[aL] - b[aL]; - } - - // Remove leading zeros. - for ( ; !a[0] && a.length > 1; a.shift() ); - } - - // x: dividend, y: divisor. - return function ( x, y, dp, rm, base ) { - var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, - yL, yz, - s = x.s == y.s ? 1 : -1, - xc = x.c, - yc = y.c; - - // Either NaN, Infinity or 0? - if ( !xc || !xc[0] || !yc || !yc[0] ) { - - return new BigNumber( - - // Return NaN if either NaN, or both Infinity or 0. - !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : - - // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. - xc && xc[0] == 0 || !yc ? s * 0 : s / 0 - ); - } - - q = new BigNumber(s); - qc = q.c = []; - e = x.e - y.e; - s = dp + e + 1; - - if ( !base ) { - base = BASE; - e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); - s = s / LOG_BASE | 0; - } - - // Result exponent may be one less then the current value of e. - // The coefficients of the BigNumbers from convertBase may have trailing zeros. - for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); - if ( yc[i] > ( xc[i] || 0 ) ) e--; - - if ( s < 0 ) { - qc.push(1); - more = true; - } else { - xL = xc.length; - yL = yc.length; - i = 0; - s += 2; - - // Normalise xc and yc so highest order digit of yc is >= base / 2. - - n = mathfloor( base / ( yc[0] + 1 ) ); - - // Not necessary, but to handle odd bases where yc[0] == ( base / 2 ) - 1. - // if ( n > 1 || n++ == 1 && yc[0] < base / 2 ) { - if ( n > 1 ) { - yc = multiply( yc, n, base ); - xc = multiply( xc, n, base ); - yL = yc.length; - xL = xc.length; - } - - xi = yL; - rem = xc.slice( 0, yL ); - remL = rem.length; - - // Add zeros to make remainder as long as divisor. - for ( ; remL < yL; rem[remL++] = 0 ); - yz = yc.slice(); - yz.unshift(0); - yc0 = yc[0]; - if ( yc[1] >= base / 2 ) yc0++; - // Not necessary, but to prevent trial digit n > base, when using base 3. - // else if ( base == 3 && yc0 == 1 ) yc0 = 1 + 1e-15; - - do { - n = 0; - - // Compare divisor and remainder. - cmp = compare( yc, rem, yL, remL ); - - // If divisor < remainder. - if ( cmp < 0 ) { - - // Calculate trial digit, n. - - rem0 = rem[0]; - if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); - - // n is how many times the divisor goes into the current remainder. - n = mathfloor( rem0 / yc0 ); - - // Algorithm: - // 1. product = divisor * trial digit (n) - // 2. if product > remainder: product -= divisor, n-- - // 3. remainder -= product - // 4. if product was < remainder at 2: - // 5. compare new remainder and divisor - // 6. If remainder > divisor: remainder -= divisor, n++ - - if ( n > 1 ) { - - // n may be > base only when base is 3. - if (n >= base) n = base - 1; - - // product = divisor * trial digit. - prod = multiply( yc, n, base ); - prodL = prod.length; - remL = rem.length; - - // Compare product and remainder. - // If product > remainder. - // Trial digit n too high. - // n is 1 too high about 5% of the time, and is not known to have - // ever been more than 1 too high. - while ( compare( prod, rem, prodL, remL ) == 1 ) { - n--; - - // Subtract divisor from product. - subtract( prod, yL < prodL ? yz : yc, prodL, base ); - prodL = prod.length; - cmp = 1; - } - } else { - - // n is 0 or 1, cmp is -1. - // If n is 0, there is no need to compare yc and rem again below, - // so change cmp to 1 to avoid it. - // If n is 1, leave cmp as -1, so yc and rem are compared again. - if ( n == 0 ) { - - // divisor < remainder, so n must be at least 1. - cmp = n = 1; - } - - // product = divisor - prod = yc.slice(); - prodL = prod.length; - } - - if ( prodL < remL ) prod.unshift(0); - - // Subtract product from remainder. - subtract( rem, prod, remL, base ); - remL = rem.length; - - // If product was < remainder. - if ( cmp == -1 ) { - - // Compare divisor and new remainder. - // If divisor < new remainder, subtract divisor from remainder. - // Trial digit n too low. - // n is 1 too low about 5% of the time, and very rarely 2 too low. - while ( compare( yc, rem, yL, remL ) < 1 ) { - n++; - - // Subtract divisor from remainder. - subtract( rem, yL < remL ? yz : yc, remL, base ); - remL = rem.length; - } - } - } else if ( cmp === 0 ) { - n++; - rem = [0]; - } // else cmp === 1 and n will be 0 - - // Add the next digit, n, to the result array. - qc[i++] = n; - - // Update the remainder. - if ( rem[0] ) { - rem[remL++] = xc[xi] || 0; - } else { - rem = [ xc[xi] ]; - remL = 1; - } - } while ( ( xi++ < xL || rem[0] != null ) && s-- ); - - more = rem[0] != null; - - // Leading zero? - if ( !qc[0] ) qc.shift(); - } - - if ( base == BASE ) { - - // To calculate q.e, first get the number of digits of qc[0]. - for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); - round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); - - // Caller is convertBase. - } else { - q.e = e; - q.r = +more; - } - - return q; - }; - })(); - - - /* - * Return a string representing the value of BigNumber n in fixed-point or exponential - * notation rounded to the specified decimal places or significant digits. - * - * n is a BigNumber. - * i is the index of the last digit required (i.e. the digit that may be rounded up). - * rm is the rounding mode. - * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. - */ - function format( n, i, rm, caller ) { - var c0, e, ne, len, str; - - rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) - ? rm | 0 : ROUNDING_MODE; - - if ( !n.c ) return n.toString(); - c0 = n.c[0]; - ne = n.e; - - if ( i == null ) { - str = coeffToString( n.c ); - str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG - ? toExponential( str, ne ) - : toFixedPoint( str, ne ); - } else { - n = round( new BigNumber(n), i, rm ); - - // n.e may have changed if the value was rounded up. - e = n.e; - - str = coeffToString( n.c ); - len = str.length; - - // toPrecision returns exponential notation if the number of significant digits - // specified is less than the number of digits necessary to represent the integer - // part of the value in fixed-point notation. - - // Exponential notation. - if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { - - // Append zeros? - for ( ; len < i; str += '0', len++ ); - str = toExponential( str, e ); - - // Fixed-point notation. - } else { - i -= ne; - str = toFixedPoint( str, e ); - - // Append zeros? - if ( e + 1 > len ) { - if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); - } else { - i += e - len; - if ( i > 0 ) { - if ( e + 1 == len ) str += '.'; - for ( ; i--; str += '0' ); - } - } - } - } - - return n.s < 0 && c0 ? '-' + str : str; - } - - - // Handle BigNumber.max and BigNumber.min. - function maxOrMin( args, method ) { - var m, n, - i = 0; - - if ( isArray( args[0] ) ) args = args[0]; - m = new BigNumber( args[0] ); - - for ( ; ++i < args.length; ) { - n = new BigNumber( args[i] ); - - // If any number is NaN, return NaN. - if ( !n.s ) { - m = n; - break; - } else if ( method.call( m, n ) ) { - m = n; - } - } - - return m; - } - - - /* - * Return true if n is an integer in range, otherwise throw. - * Use for argument validation when ERRORS is true. - */ - function intValidatorWithErrors( n, min, max, caller, name ) { - if ( n < min || n > max || n != truncate(n) ) { - raise( caller, ( name || 'decimal places' ) + - ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); - } - - return true; - } - - - /* - * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. - * Called by minus, plus and times. - */ - function normalise( n, c, e ) { - var i = 1, - j = c.length; - - // Remove trailing zeros. - for ( ; !c[--j]; c.pop() ); - - // Calculate the base 10 exponent. First get the number of digits of c[0]. - for ( j = c[0]; j >= 10; j /= 10, i++ ); - - // Overflow? - if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { - - // Infinity. - n.c = n.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - n.c = [ n.e = 0 ]; - } else { - n.e = e; - n.c = c; - } - - return n; - } - - - // Handle values that fail the validity test in BigNumber. - parseNumeric = (function () { - var basePrefix=/^(-?)0([xbo])/i, - dotAfter=/^([^.]+)\.$/, - dotBefore=/^\.([^.]+)$/, - isInfinityOrNaN=/^-?(Infinity|NaN)$/, - whitespaceOrPlus=/^\s*\+|^\s+|\s+$/g; - - return function ( x, str, num, b ) { - var base, - s = num ? str : str.replace( whitespaceOrPlus, '' ); - - // No exception on ±Infinity or NaN. - if ( isInfinityOrNaN.test(s) ) { - x.s = isNaN(s) ? null : s < 0 ? -1 : 1; - } else { - if ( !num ) { - - // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i - s = s.replace( basePrefix, function ( m, p1, p2 ) { - base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; - return !b || b == base ? p1 : m; - }); - - if (b) { - base = b; - - // E.g. '1.' to '1', '.1' to '0.1' - s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); - } - - if ( str != s ) return new BigNumber( s, base ); - } - - // 'new BigNumber() not a number: {n}' - // 'new BigNumber() not a base {b} number: {n}' - if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); - x.s = null; - } - - x.c = x.e = null; - id = 0; - } - })(); - - - // Throw a BigNumber Error. - function raise( caller, msg, val ) { - var error = new Error( [ - 'new BigNumber', // 0 - 'cmp', // 1 - 'config', // 2 - 'div', // 3 - 'divToInt', // 4 - 'eq', // 5 - 'gt', // 6 - 'gte', // 7 - 'lt', // 8 - 'lte', // 9 - 'minus', // 10 - 'mod', // 11 - 'plus', // 12 - 'precision', // 13 - 'random', // 14 - 'round', // 15 - 'shift', // 16 - 'times', // 17 - 'toDigits', // 18 - 'toExponential', // 19 - 'toFixed', // 20 - 'toFormat', // 21 - 'toFraction', // 22 - 'pow', // 23 - 'toPrecision', // 24 - 'toString', // 25 - 'BigNumber' // 26 - ][caller] + '() ' + msg + ': ' + val ); - - error.name = 'BigNumber Error'; - id = 0; - throw error; - } - - - /* - * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. - * If r is truthy, it is known that there are more digits after the rounding digit. - */ - function round( x, sd, rm, r ) { - var d, i, j, k, n, ni, rd, - xc = x.c, - pows10 = POWS_TEN; - - // if x is not Infinity or NaN... - if (xc) { - - // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. - // n is a base 1e14 number, the value of the element of array x.c containing rd. - // ni is the index of n within x.c. - // d is the number of digits of n. - // i is the index of rd within n including leading zeros. - // j is the actual index of rd within n (if < 0, rd is a leading zero). - out: { - - // Get the number of digits of the first element of xc. - for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); - i = sd - d; - - // If the rounding digit is in the first element of xc... - if ( i < 0 ) { - i += LOG_BASE; - j = sd; - n = xc[ ni = 0 ]; - - // Get the rounding digit at index j of n. - rd = n / pows10[ d - j - 1 ] % 10 | 0; - } else { - ni = mathceil( ( i + 1 ) / LOG_BASE ); - - if ( ni >= xc.length ) { - - if (r) { - - // Needed by sqrt. - for ( ; xc.length <= ni; xc.push(0) ); - n = rd = 0; - d = 1; - i %= LOG_BASE; - j = i - LOG_BASE + 1; - } else { - break out; - } - } else { - n = k = xc[ni]; - - // Get the number of digits of n. - for ( d = 1; k >= 10; k /= 10, d++ ); - - // Get the index of rd within n. - i %= LOG_BASE; - - // Get the index of rd within n, adjusted for leading zeros. - // The number of leading zeros of n is given by LOG_BASE - d. - j = i - LOG_BASE + d; - - // Get the rounding digit at index j of n. - rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; - } - } - - r = r || sd < 0 || - - // Are there any non-zero digits after the rounding digit? - // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right - // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. - xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); - - r = rm < 4 - ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && - - // Check whether the digit to the left of the rounding digit is odd. - ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( sd < 1 || !xc[0] ) { - xc.length = 0; - - if (r) { - - // Convert sd to decimal places. - sd -= x.e + 1; - - // 1, 0.1, 0.01, 0.001, 0.0001 etc. - xc[0] = pows10[ sd % LOG_BASE ]; - x.e = -sd || 0; - } else { - - // Zero. - xc[0] = x.e = 0; - } - - return x; - } - - // Remove excess digits. - if ( i == 0 ) { - xc.length = ni; - k = 1; - ni--; - } else { - xc.length = ni + 1; - k = pows10[ LOG_BASE - i ]; - - // E.g. 56700 becomes 56000 if 7 is the rounding digit. - // j > 0 means i > number of leading zeros of n. - xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; - } - - // Round up? - if (r) { - - for ( ; ; ) { - - // If the digit to be rounded up is in the first element of xc... - if ( ni == 0 ) { - - // i will be the length of xc[0] before k is added. - for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); - j = xc[0] += k; - for ( k = 1; j >= 10; j /= 10, k++ ); - - // if i != k the length has increased. - if ( i != k ) { - x.e++; - if ( xc[0] == BASE ) xc[0] = 1; - } - - break; - } else { - xc[ni] += k; - if ( xc[ni] != BASE ) break; - xc[ni--] = 0; - k = 1; - } - } - } - - // Remove trailing zeros. - for ( i = xc.length; xc[--i] === 0; xc.pop() ); - } - - // Overflow? Infinity. - if ( x.e > MAX_EXP ) { - x.c = x.e = null; - - // Underflow? Zero. - } else if ( x.e < MIN_EXP ) { - x.c = [ x.e = 0 ]; - } - } - - return x; - } - - - // PROTOTYPE/INSTANCE METHODS - - - /* - * Return a new BigNumber whose value is the absolute value of this BigNumber. - */ - P.absoluteValue = P.abs = function () { - var x = new BigNumber(this); - if ( x.s < 0 ) x.s = 1; - return x; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of Infinity. - */ - P.ceil = function () { - return round( new BigNumber(this), this.e + 1, 2 ); - }; - - - /* - * Return - * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), - * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), - * 0 if they have the same value, - * or null if the value of either is NaN. - */ - P.comparedTo = P.cmp = function ( y, b ) { - id = 1; - return compare( this, new BigNumber( y, b ) ); - }; - - - /* - * Return the number of decimal places of the value of this BigNumber, or null if the value - * of this BigNumber is ±Infinity or NaN. - */ - P.decimalPlaces = P.dp = function () { - var n, v, - c = this.c; - - if ( !c ) return null; - n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; - - // Subtract the number of trailing zeros of the last number. - if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); - if ( n < 0 ) n = 0; - - return n; - }; - - - /* - * n / 0 = I - * n / N = N - * n / I = 0 - * 0 / n = 0 - * 0 / 0 = N - * 0 / N = N - * 0 / I = 0 - * N / n = N - * N / 0 = N - * N / N = N - * N / I = N - * I / n = I - * I / 0 = I - * I / N = N - * I / I = N - * - * Return a new BigNumber whose value is the value of this BigNumber divided by the value of - * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.dividedBy = P.div = function ( y, b ) { - id = 3; - return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); - }; - - - /* - * Return a new BigNumber whose value is the integer part of dividing the value of this - * BigNumber by the value of BigNumber(y, b). - */ - P.dividedToIntegerBy = P.divToInt = function ( y, b ) { - id = 4; - return div( this, new BigNumber( y, b ), 0, 1 ); - }; - - - /* - * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), - * otherwise returns false. - */ - P.equals = P.eq = function ( y, b ) { - id = 5; - return compare( this, new BigNumber( y, b ) ) === 0; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of -Infinity. - */ - P.floor = function () { - return round( new BigNumber(this), this.e + 1, 3 ); - }; - - - /* - * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.greaterThan = P.gt = function ( y, b ) { - id = 6; - return compare( this, new BigNumber( y, b ) ) > 0; - }; - - - /* - * Return true if the value of this BigNumber is greater than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.greaterThanOrEqualTo = P.gte = function ( y, b ) { - id = 7; - return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; - - }; - - - /* - * Return true if the value of this BigNumber is a finite number, otherwise returns false. - */ - P.isFinite = function () { - return !!this.c; - }; - - - /* - * Return true if the value of this BigNumber is an integer, otherwise return false. - */ - P.isInteger = P.isInt = function () { - return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; - }; - - - /* - * Return true if the value of this BigNumber is NaN, otherwise returns false. - */ - P.isNaN = function () { - return !this.s; - }; - - - /* - * Return true if the value of this BigNumber is negative, otherwise returns false. - */ - P.isNegative = P.isNeg = function () { - return this.s < 0; - }; - - - /* - * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. - */ - P.isZero = function () { - return !!this.c && this.c[0] == 0; - }; - - - /* - * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.lessThan = P.lt = function ( y, b ) { - id = 8; - return compare( this, new BigNumber( y, b ) ) < 0; - }; - - - /* - * Return true if the value of this BigNumber is less than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.lessThanOrEqualTo = P.lte = function ( y, b ) { - id = 9; - return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; - }; - - - /* - * n - 0 = n - * n - N = N - * n - I = -I - * 0 - n = -n - * 0 - 0 = 0 - * 0 - N = N - * 0 - I = -I - * N - n = N - * N - 0 = N - * N - N = N - * N - I = N - * I - n = I - * I - 0 = I - * I - N = N - * I - I = N - * - * Return a new BigNumber whose value is the value of this BigNumber minus the value of - * BigNumber(y, b). - */ - P.minus = P.sub = function ( y, b ) { - var i, j, t, xLTy, - x = this, - a = x.s; - - id = 10; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.plus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Either Infinity? - if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); - - // Either zero? - if ( !xc[0] || !yc[0] ) { - - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : - - // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity - ROUNDING_MODE == 3 ? -0 : 0 ); - } - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Determine which is the bigger number. - if ( a = xe - ye ) { - - if ( xLTy = a < 0 ) { - a = -a; - t = xc; - } else { - ye = xe; - t = yc; - } - - t.reverse(); - - // Prepend zeros to equalise exponents. - for ( b = a; b--; t.push(0) ); - t.reverse(); - } else { - - // Exponents equal. Check digit by digit. - j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; - - for ( a = b = 0; b < j; b++ ) { - - if ( xc[b] != yc[b] ) { - xLTy = xc[b] < yc[b]; - break; - } - } - } - - // x < y? Point xc to the array of the bigger number. - if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; - - b = ( j = yc.length ) - ( i = xc.length ); - - // Append zeros to xc if shorter. - // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. - if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); - b = BASE - 1; - - // Subtract yc from xc. - for ( ; j > a; ) { - - if ( xc[--j] < yc[j] ) { - for ( i = j; i && !xc[--i]; xc[i] = b ); - --xc[i]; - xc[j] += BASE; - } - - xc[j] -= yc[j]; - } - - // Remove leading zeros and adjust exponent accordingly. - for ( ; xc[0] == 0; xc.shift(), --ye ); - - // Zero? - if ( !xc[0] ) { - - // Following IEEE 754 (2008) 6.3, - // n - n = +0 but n - n = -0 when rounding towards -Infinity. - y.s = ROUNDING_MODE == 3 ? -1 : 1; - y.c = [ y.e = 0 ]; - return y; - } - - // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity - // for finite x and y. - return normalise( y, xc, ye ); - }; - - - /* - * n % 0 = N - * n % N = N - * n % I = n - * 0 % n = 0 - * -0 % n = -0 - * 0 % 0 = N - * 0 % N = N - * 0 % I = 0 - * N % n = N - * N % 0 = N - * N % N = N - * N % I = N - * I % n = N - * I % 0 = N - * I % N = N - * I % I = N - * - * Return a new BigNumber whose value is the value of this BigNumber modulo the value of - * BigNumber(y, b). The result depends on the value of MODULO_MODE. - */ - P.modulo = P.mod = function ( y, b ) { - var q, s, - x = this; - - id = 11; - y = new BigNumber( y, b ); - - // Return NaN if x is Infinity or NaN, or y is NaN or zero. - if ( !x.c || !y.s || y.c && !y.c[0] ) { - return new BigNumber(NaN); - - // Return x if y is Infinity or x is zero. - } else if ( !y.c || x.c && !x.c[0] ) { - return new BigNumber(x); - } - - if ( MODULO_MODE == 9 ) { - - // Euclidian division: q = sign(y) * floor(x / abs(y)) - // r = x - qy where 0 <= r < abs(y) - s = y.s; - y.s = 1; - q = div( x, y, 0, 3 ); - y.s = s; - q.s *= s; - } else { - q = div( x, y, 0, MODULO_MODE ); - } - - return x.minus( q.times(y) ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber negated, - * i.e. multiplied by -1. - */ - P.negated = P.neg = function () { - var x = new BigNumber(this); - x.s = -x.s || null; - return x; - }; - - - /* - * n + 0 = n - * n + N = N - * n + I = I - * 0 + n = n - * 0 + 0 = 0 - * 0 + N = N - * 0 + I = I - * N + n = N - * N + 0 = N - * N + N = N - * N + I = N - * I + n = I - * I + 0 = I - * I + N = N - * I + I = I - * - * Return a new BigNumber whose value is the value of this BigNumber plus the value of - * BigNumber(y, b). - */ - P.plus = P.add = function ( y, b ) { - var t, - x = this, - a = x.s; - - id = 12; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.minus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Return ±Infinity if either ±Infinity. - if ( !xc || !yc ) return new BigNumber( a / 0 ); - - // Either zero? - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. - if ( a = xe - ye ) { - if ( a > 0 ) { - ye = xe; - t = yc; - } else { - a = -a; - t = xc; - } - - t.reverse(); - for ( ; a--; t.push(0) ); - t.reverse(); - } - - a = xc.length; - b = yc.length; - - // Point xc to the longer array, and b to the shorter length. - if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; - - // Only start adding at yc.length - 1 as the further digits of xc can be ignored. - for ( a = 0; b; ) { - a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; - xc[b] %= BASE; - } - - if (a) { - xc.unshift(a); - ++ye; - } - - // No need to check for zero, as +x + +y != 0 && -x + -y != 0 - // ye = MAX_EXP + 1 possible - return normalise( y, xc, ye ); - }; - - - /* - * Return the number of significant digits of the value of this BigNumber. - * - * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. - */ - P.precision = P.sd = function (z) { - var n, v, - x = this, - c = x.c; - - // 'precision() argument not a boolean or binary digit: {z}' - if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { - if (ERRORS) raise( 13, 'argument' + notBool, z ); - if ( z != !!z ) z = null; - } - - if ( !c ) return null; - v = c.length - 1; - n = v * LOG_BASE + 1; - - if ( v = c[v] ) { - - // Subtract the number of trailing zeros of the last element. - for ( ; v % 10 == 0; v /= 10, n-- ); - - // Add the number of digits of the first element. - for ( v = c[0]; v >= 10; v /= 10, n++ ); - } - - if ( z && x.e + 1 > n ) n = x.e + 1; - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if - * omitted. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'round() decimal places out of range: {dp}' - * 'round() decimal places not an integer: {dp}' - * 'round() rounding mode not an integer: {rm}' - * 'round() rounding mode out of range: {rm}' - */ - P.round = function ( dp, rm ) { - var n = new BigNumber(this); - - if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { - round( n, ~~dp + this.e + 1, rm == null || - !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); - } - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber shifted by k places - * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. - * - * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. - * - * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity - * otherwise. - * - * 'shift() argument not an integer: {k}' - * 'shift() argument out of range: {k}' - */ - P.shift = function (k) { - var n = this; - return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) - - // k < 1e+21, or truncate(k) will produce exponential notation. - ? n.times( '1e' + truncate(k) ) - : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) - ? n.s * ( k < 0 ? 0 : 1 / 0 ) - : n ); - }; - - - /* - * sqrt(-n) = N - * sqrt( N) = N - * sqrt(-I) = N - * sqrt( I) = I - * sqrt( 0) = 0 - * sqrt(-0) = -0 - * - * Return a new BigNumber whose value is the square root of the value of this BigNumber, - * rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.squareRoot = P.sqrt = function () { - var m, n, r, rep, t, - x = this, - c = x.c, - s = x.s, - e = x.e, - dp = DECIMAL_PLACES + 4, - half = new BigNumber('0.5'); - - // Negative/NaN/Infinity/zero? - if ( s !== 1 || !c || !c[0] ) { - return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); - } - - // Initial estimate. - s = Math.sqrt( +x ); - - // Math.sqrt underflow/overflow? - // Pass x to Math.sqrt as integer, then adjust the exponent of the result. - if ( s == 0 || s == 1 / 0 ) { - n = coeffToString(c); - if ( ( n.length + e ) % 2 == 0 ) n += '0'; - s = Math.sqrt(n); - e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); - - if ( s == 1 / 0 ) { - n = '1e' + e; - } else { - n = s.toExponential(); - n = n.slice( 0, n.indexOf('e') + 1 ) + e; - } - - r = new BigNumber(n); - } else { - r = new BigNumber( s + '' ); - } - - // Check for zero. - // r could be zero if MIN_EXP is changed after the this value was created. - // This would cause a division by zero (x/t) and hence Infinity below, which would cause - // coeffToString to throw. - if ( r.c[0] ) { - e = r.e; - s = e + dp; - if ( s < 3 ) s = 0; - - // Newton-Raphson iteration. - for ( ; ; ) { - t = r; - r = half.times( t.plus( div( x, t, dp, 1 ) ) ); - - if ( coeffToString( t.c ).slice( 0, s ) === ( n = - coeffToString( r.c ) ).slice( 0, s ) ) { - - // The exponent of r may here be one less than the final result exponent, - // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits - // are indexed correctly. - if ( r.e < e ) --s; - n = n.slice( s - 3, s + 1 ); - - // The 4th rounding digit may be in error by -1 so if the 4 rounding digits - // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the - // iteration. - if ( n == '9999' || !rep && n == '4999' ) { - - // On the first iteration only, check to see if rounding up gives the - // exact result as the nines may infinitely repeat. - if ( !rep ) { - round( t, t.e + DECIMAL_PLACES + 2, 0 ); - - if ( t.times(t).eq(x) ) { - r = t; - break; - } - } - - dp += 4; - s += 4; - rep = 1; - } else { - - // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact - // result. If not, then there are further digits and m will be truthy. - if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { - - // Truncate to the first rounding digit. - round( r, r.e + DECIMAL_PLACES + 2, 1 ); - m = !r.times(r).eq(x); - } - - break; - } - } - } - } - - return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); - }; - - - /* - * n * 0 = 0 - * n * N = N - * n * I = I - * 0 * n = 0 - * 0 * 0 = 0 - * 0 * N = N - * 0 * I = N - * N * n = N - * N * 0 = N - * N * N = N - * N * I = N - * I * n = I - * I * 0 = N - * I * N = N - * I * I = I - * - * Return a new BigNumber whose value is the value of this BigNumber times the value of - * BigNumber(y, b). - */ - P.times = P.mul = function ( y, b ) { - var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, - base, sqrtBase, - x = this, - xc = x.c, - yc = ( id = 17, y = new BigNumber( y, b ) ).c; - - // Either NaN, ±Infinity or ±0? - if ( !xc || !yc || !xc[0] || !yc[0] ) { - - // Return NaN if either is NaN, or one is 0 and the other is Infinity. - if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { - y.c = y.e = y.s = null; - } else { - y.s *= x.s; - - // Return ±Infinity if either is ±Infinity. - if ( !xc || !yc ) { - y.c = y.e = null; - - // Return ±0 if either is ±0. - } else { - y.c = [0]; - y.e = 0; - } - } - - return y; - } - - e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); - y.s *= x.s; - xcL = xc.length; - ycL = yc.length; - - // Ensure xc points to longer array and xcL to its length. - if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; - - // Initialise the result array with zeros. - for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); - - base = BASE; - sqrtBase = SQRT_BASE; - - for ( i = ycL; --i >= 0; ) { - c = 0; - ylo = yc[i] % sqrtBase; - yhi = yc[i] / sqrtBase | 0; - - for ( k = xcL, j = i + k; j > i; ) { - xlo = xc[--k] % sqrtBase; - xhi = xc[k] / sqrtBase | 0; - m = yhi * xlo + xhi * ylo; - xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; - c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; - zc[j--] = xlo % base; - } - - zc[j] = c; - } - - if (c) { - ++e; - } else { - zc.shift(); - } - - return normalise( y, zc, e ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toDigits() precision out of range: {sd}' - * 'toDigits() precision not an integer: {sd}' - * 'toDigits() rounding mode not an integer: {rm}' - * 'toDigits() rounding mode out of range: {rm}' - */ - P.toDigits = function ( sd, rm ) { - var n = new BigNumber(this); - sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; - rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; - return sd ? round( n, sd, rm ) : n; - }; - - - /* - * Return a string representing the value of this BigNumber in exponential notation and - * rounded using ROUNDING_MODE to dp fixed decimal places. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toExponential() decimal places not an integer: {dp}' - * 'toExponential() decimal places out of range: {dp}' - * 'toExponential() rounding mode not an integer: {rm}' - * 'toExponential() rounding mode out of range: {rm}' - */ - P.toExponential = function ( dp, rm ) { - return format( this, - dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounding - * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', - * but e.g. (-0.00001).toFixed(0) is '-0'. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFixed() decimal places not an integer: {dp}' - * 'toFixed() decimal places out of range: {dp}' - * 'toFixed() rounding mode not an integer: {rm}' - * 'toFixed() rounding mode out of range: {rm}' - */ - P.toFixed = function ( dp, rm ) { - return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) - ? ~~dp + this.e + 1 : null, rm, 20 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounded - * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties - * of the FORMAT object (see BigNumber.config). - * - * FORMAT = { - * decimalSeparator : '.', - * groupSeparator : ',', - * groupSize : 3, - * secondaryGroupSize : 0, - * fractionGroupSeparator : '\xA0', // non-breaking space - * fractionGroupSize : 0 - * }; - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFormat() decimal places not an integer: {dp}' - * 'toFormat() decimal places out of range: {dp}' - * 'toFormat() rounding mode not an integer: {rm}' - * 'toFormat() rounding mode out of range: {rm}' - */ - P.toFormat = function ( dp, rm ) { - var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) - ? ~~dp + this.e + 1 : null, rm, 21 ); - - if ( this.c ) { - var i, - arr = str.split('.'), - g1 = +FORMAT.groupSize, - g2 = +FORMAT.secondaryGroupSize, - groupSeparator = FORMAT.groupSeparator, - intPart = arr[0], - fractionPart = arr[1], - isNeg = this.s < 0, - intDigits = isNeg ? intPart.slice(1) : intPart, - len = intDigits.length; - - if (g2) i = g1, g1 = g2, g2 = i, len -= i; - - if ( g1 > 0 && len > 0 ) { - i = len % g1 || g1; - intPart = intDigits.substr( 0, i ); - - for ( ; i < len; i += g1 ) { - intPart += groupSeparator + intDigits.substr( i, g1 ); - } - - if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); - if (isNeg) intPart = '-' + intPart; - } - - str = fractionPart - ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) - ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), - '$&' + FORMAT.fractionGroupSeparator ) - : fractionPart ) - : intPart; - } - - return str; - }; - - - /* - * Return a string array representing the value of this BigNumber as a simple fraction with - * an integer numerator and an integer denominator. The denominator will be a positive - * non-zero value less than or equal to the specified maximum denominator. If a maximum - * denominator is not specified, the denominator will be the lowest value necessary to - * represent the number exactly. - * - * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. - * - * 'toFraction() max denominator not an integer: {md}' - * 'toFraction() max denominator out of range: {md}' - */ - P.toFraction = function (md) { - var arr, d0, d2, e, exp, n, n0, q, s, - k = ERRORS, - x = this, - xc = x.c, - d = new BigNumber(ONE), - n1 = d0 = new BigNumber(ONE), - d1 = n0 = new BigNumber(ONE); - - if ( md != null ) { - ERRORS = false; - n = new BigNumber(md); - ERRORS = k; - - if ( !( k = n.isInt() ) || n.lt(ONE) ) { - - if (ERRORS) { - raise( 22, - 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); - } - - // ERRORS is false: - // If md is a finite non-integer >= 1, round it to an integer and use it. - md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; - } - } - - if ( !xc ) return x.toString(); - s = coeffToString(xc); - - // Determine initial denominator. - // d is a power of 10 and the minimum max denominator that specifies the value exactly. - e = d.e = s.length - x.e - 1; - d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; - md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; - - exp = MAX_EXP; - MAX_EXP = 1 / 0; - n = new BigNumber(s); - - // n0 = d1 = 0 - n0.c[0] = 0; - - for ( ; ; ) { - q = div( n, d, 0, 1 ); - d2 = d0.plus( q.times(d1) ); - if ( d2.cmp(md) == 1 ) break; - d0 = d1; - d1 = d2; - n1 = n0.plus( q.times( d2 = n1 ) ); - n0 = d2; - d = n.minus( q.times( d2 = d ) ); - n = d2; - } - - d2 = div( md.minus(d0), d1, 0, 1 ); - n0 = n0.plus( d2.times(n1) ); - d0 = d0.plus( d2.times(d1) ); - n0.s = n1.s = x.s; - e *= 2; - - // Determine which fraction is closer to x, n0/d0 or n1/d1 - arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( - div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 - ? [ n1.toString(), d1.toString() ] - : [ n0.toString(), d0.toString() ]; - - MAX_EXP = exp; - return arr; - }; - - - /* - * Return the value of this BigNumber converted to a number primitive. - */ - P.toNumber = function () { - var x = this; - - // Ensure zero has correct sign. - return +x || ( x.s ? x.s * 0 : NaN ); - }; - - - /* - * Return a BigNumber whose value is the value of this BigNumber raised to the power n. - * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. - * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. - * - * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. - * (Performs 54 loop iterations for n of 9007199254740992.) - * - * 'pow() exponent not an integer: {n}' - * 'pow() exponent out of range: {n}' - */ - P.toPower = P.pow = function (n) { - var k, y, - i = mathfloor( n < 0 ? -n : +n ), - x = this; - - // Pass ±Infinity to Math.pow if exponent is out of range. - if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && - ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || - parseFloat(n) != n && !( n = NaN ) ) ) { - return new BigNumber( Math.pow( +x, n ) ); - } - - // Truncating each coefficient array to a length of k after each multiplication equates - // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a - // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) - k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; - y = new BigNumber(ONE); - - for ( ; ; ) { - - if ( i % 2 ) { - y = y.times(x); - if ( !y.c ) break; - if ( k && y.c.length > k ) y.c.length = k; - } - - i = mathfloor( i / 2 ); - if ( !i ) break; - - x = x.times(x); - if ( k && x.c && x.c.length > k ) x.c.length = k; - } - - if ( n < 0 ) y = ONE.div(y); - return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; - }; - - - /* - * Return a string representing the value of this BigNumber rounded to sd significant digits - * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits - * necessary to represent the integer part of the value in fixed-point notation, then use - * exponential notation. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toPrecision() precision not an integer: {sd}' - * 'toPrecision() precision out of range: {sd}' - * 'toPrecision() rounding mode not an integer: {rm}' - * 'toPrecision() rounding mode out of range: {rm}' - */ - P.toPrecision = function ( sd, rm ) { - return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) - ? sd | 0 : null, rm, 24 ); - }; - - - /* - * Return a string representing the value of this BigNumber in base b, or base 10 if b is - * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and - * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent - * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than - * TO_EXP_NEG, return exponential notation. - * - * [b] {number} Integer, 2 to 64 inclusive. - * - * 'toString() base not an integer: {b}' - * 'toString() base out of range: {b}' - */ - P.toString = function (b) { - var str, - n = this, - s = n.s, - e = n.e; - - // Infinity or NaN? - if ( e === null ) { - - if (s) { - str = 'Infinity'; - if ( s < 0 ) str = '-' + str; - } else { - str = 'NaN'; - } - } else { - str = coeffToString( n.c ); - - if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { - str = e <= TO_EXP_NEG || e >= TO_EXP_POS - ? toExponential( str, e ) - : toFixedPoint( str, e ); - } else { - str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); - } - - if ( s < 0 && n.c[0] ) str = '-' + str; - } - - return str; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole - * number. - */ - P.truncated = P.trunc = function () { - return round( new BigNumber(this), this.e + 1, 1 ); - }; - - - - /* - * Return as toString, but do not accept a base argument. - */ - P.valueOf = P.toJSON = function () { - return this.toString(); - }; - - - // Aliases for BigDecimal methods. - //P.add = P.plus; // P.add included above - //P.subtract = P.minus; // P.sub included above - //P.multiply = P.times; // P.mul included above - //P.divide = P.div; - //P.remainder = P.mod; - //P.compareTo = P.cmp; - //P.negate = P.neg; - - - if ( configObj != null ) BigNumber.config(configObj); - - return BigNumber; - } - - - // PRIVATE HELPER FUNCTIONS - - - function bitFloor(n) { - var i = n | 0; - return n > 0 || n === i ? i : i - 1; - } - - - // Return a coefficient array as a string of base 10 digits. - function coeffToString(a) { - var s, z, - i = 1, - j = a.length, - r = a[0] + ''; - - for ( ; i < j; ) { - s = a[i++] + ''; - z = LOG_BASE - s.length; - for ( ; z--; s = '0' + s ); - r += s; - } - - // Determine trailing zeros. - for ( j = r.length; r.charCodeAt(--j) === 48; ); - return r.slice( 0, j + 1 || 1 ); - } - - - // Compare the value of BigNumbers x and y. - function compare( x, y ) { - var a, b, - xc = x.c, - yc = y.c, - i = x.s, - j = y.s, - k = x.e, - l = y.e; - - // Either NaN? - if ( !i || !j ) return null; - - a = xc && !xc[0]; - b = yc && !yc[0]; - - // Either zero? - if ( a || b ) return a ? b ? 0 : -j : i; - - // Signs differ? - if ( i != j ) return i; - - a = i < 0; - b = k == l; - - // Either Infinity? - if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; - - // Compare exponents. - if ( !b ) return k > l ^ a ? 1 : -1; - - j = ( k = xc.length ) < ( l = yc.length ) ? k : l; - - // Compare digit by digit. - for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; - - // Compare lengths. - return k == l ? 0 : k > l ^ a ? 1 : -1; - } - - - /* - * Return true if n is a valid number in range, otherwise false. - * Use for argument validation when ERRORS is false. - * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. - */ - function intValidatorNoErrors( n, min, max ) { - return ( n = truncate(n) ) >= min && n <= max; - } - - - function isArray(obj) { - return Object.prototype.toString.call(obj) == '[object Array]'; - } - - - /* - * Convert string of baseIn to an array of numbers of baseOut. - * Eg. convertBase('255', 10, 16) returns [15, 15]. - * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. - */ - function toBaseOut( str, baseIn, baseOut ) { - var j, - arr = [0], - arrL, - i = 0, - len = str.length; - - for ( ; i < len; ) { - for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); - arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); - - for ( ; j < arr.length; j++ ) { - - if ( arr[j] > baseOut - 1 ) { - if ( arr[j + 1] == null ) arr[j + 1] = 0; - arr[j + 1] += arr[j] / baseOut | 0; - arr[j] %= baseOut; - } - } - } - - return arr.reverse(); - } - - - function toExponential( str, e ) { - return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + - ( e < 0 ? 'e' : 'e+' ) + e; - } - - - function toFixedPoint( str, e ) { - var len, z; - - // Negative exponent? - if ( e < 0 ) { - - // Prepend zeros. - for ( z = '0.'; ++e; z += '0' ); - str = z + str; - - // Positive exponent - } else { - len = str.length; - - // Append zeros. - if ( ++e > len ) { - for ( z = '0', e -= len; --e; z += '0' ); - str += z; - } else if ( e < len ) { - str = str.slice( 0, e ) + '.' + str.slice(e); - } - } - - return str; - } - - - function truncate(n) { - n = parseFloat(n); - return n < 0 ? mathceil(n) : mathfloor(n); - } - - - // EXPORT - - - BigNumber = another(); - - // AMD. - if ( typeof define == 'function' && define.amd ) { - define( function () { return BigNumber; } ); - - // Node and other environments that support module.exports. - } else if ( typeof module != 'undefined' && module.exports ) { - module.exports = BigNumber; - if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} - - // Browser. - } else { - global.BigNumber = BigNumber; - } -})(this); - -},{"crypto":1}],"natspec":[function(require,module,exports){ -/* - This file is part of natspec.js. - - natspec.js 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. - - natspec.js 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 natspec.js. If not, see . -*/ -/** @file natspec.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var abi = require('./node_modules/web3/lib/solidity/abi.js'); - -/** - * This object should be used to evaluate natspec expression - * It has one method evaluateExpression which shoul be used - */ -var natspec = (function () { - /** - * Helper method - * Should be called to copy values from object to global context - * - * @method copyToContext - * @param {Object} object from which we want to copy properties - * @param {Object} object to which we copy - */ - var copyToContext = function (obj, context) { - Object.keys(obj).forEach(function (key) { - context[key] = obj[key]; - }); - } - - /** - * Should be used to generate codes, which will be evaluated - * - * @method generateCode - * @param {Object} object from which code will be generated - * @return {String} javascript code which is used to initalized variables - */ - var generateCode = function (obj) { - return Object.keys(obj).reduce(function (acc, key) { - return acc + "var " + key + " = context['" + key + "'];\n"; - }, ""); - }; - - /** - * Helper method - * Should be called to get method with given name from the abi - * - * @method getMethodWithName - * @param {Array} contract's abi - * @param {String} name of the method that we are looking for - * @return {Object} abi for method with name - */ - var getMethodWithName = function(abi, name) { - return abi.filter(function (method) { - return method.name === name; - })[0]; - }; - - /** - * Should be used to get all contract method input variables - * - * @method getMethodInputParams - * @param {Object} abi for certain method - * @param {Object} transaction object - * @return {Object} object with all contract's method input variables - */ - var getMethodInputParams = function (method, transaction) { - // do it with output formatter (cause we have to decode) - var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); - - return method.inputs.reduce(function (acc, current, index) { - acc[current.name] = params[index]; - return acc; - }, {}); - }; - - /** - * Should be called when we want to evaluate natspec expression - * Replaces all natspec 'subexpressions' with evaluated value - * - * @method mapExpressionToEvaluate - * @param {String} expression to evaluate - * @param {Function} callback which is called to evaluate te expression - * @return {String} evaluated expression - */ - var mapExpressionsToEvaluate = function (expression, cb) { - var evaluatedExpression = ""; - - // match everything in quotes - var pattern = /\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim - var match; - var lastIndex = 0; - try { - while ((match = pattern.exec(expression)) !== null) { - var startIndex = pattern.lastIndex - match[0].length; - var toEval = match[0].slice(1, match[0].length - 1); - evaluatedExpression += expression.slice(lastIndex, startIndex); - var evaluatedPart = cb(toEval); - evaluatedExpression += evaluatedPart; - lastIndex = pattern.lastIndex; - } - - evaluatedExpression += expression.slice(lastIndex); - } - catch (err) { - throw new Error("Natspec evaluation failed, wrong input params"); - } - - return evaluatedExpression; - }; - - /** - * Should be called to evaluate single expression - * Is internally using javascript's 'eval' method - * - * @method evaluateExpression - * @param {String} expression which should be evaluated - * @param {Object} [call] object containing contract abi, transaction, called method - * @return {String} evaluated expression - * @throws exception if method is not found or we are trying to evaluate input params that does not exists - */ - - var utils = require('../utils/utils'); - - var evaluateExpression = function (expression, call) { - //var self = this; - var context = {}; - - if (!!call) { - try { - var method = getMethodWithName(call.abi, call.method); - var params = getMethodInputParams(method, call.transaction); - copyToContext(params, context); - } - catch (err) { - throw new Error("Natspec evaluation failed, method does not exist"); - } - } - - var code = generateCode(context); - - var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { - //var fn = new Function("context", "toHex", code + "return " + toEval + ";"); - //return fn(context, toHex).toString(); - var fn = new Function("context", "utils", code + "return " + toEval + ";"); - return fn(context, utils).toString(); - }); - - return evaluatedExpression; - }; - - /** - * Safe version of evaluateExpression - * Instead of throwing an exception it returns it as a string - * - * @method evaluateExpressionSafe - * @param {String} expression which should be evaluated - * @param {Object} [call] object containing contract abi, transaction, called method - * @return {String} evaluated expression - */ - var evaluateExpressionSafe = function (expression, call) { - try { - return evaluateExpression(expression, call); - } - catch (err) { - return err.message; - } - }; - - return { - evaluateExpression: evaluateExpression, - evaluateExpressionSafe: evaluateExpressionSafe - }; - -})(); - -module.exports = natspec; - - -},{"./node_modules/web3/lib/solidity/abi.js":2,"../utils/utils":7}]},{},[]); -` - -//# sourceMappingURL=natspec.js.map` diff --git a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go b/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go index 6dd0ef46f..a32653554 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go +++ b/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go @@ -32,11 +32,12 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) // registryAPIBackend is a backend for an Ethereum Registry. type registryAPIBackend struct { - config *core.ChainConfig + config *params.ChainConfig bc *core.BlockChain chainDb ethdb.Database txPool *core.TxPool @@ -45,12 +46,12 @@ type registryAPIBackend struct { // PrivateRegistarAPI offers various functions to access the Ethereum registry. type PrivateRegistarAPI struct { - config *core.ChainConfig + config *params.ChainConfig be *registryAPIBackend } // NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { +func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { return &PrivateRegistarAPI{ config: config, be: ®istryAPIBackend{ @@ -173,25 +174,20 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr from.SetBalance(common.MaxBig) - msg := callmsg{ - from: from, - gas: common.Big(gasStr), - gasPrice: common.Big(gasPriceStr), - value: common.Big(valueStr), - data: common.FromHex(dataStr), - } + var to *common.Address if len(toStr) > 0 { addr := common.HexToAddress(toStr) - msg.to = &addr + to = &addr } - - if msg.gas.Cmp(big.NewInt(0)) == 0 { - msg.gas = big.NewInt(50000000) + gas := common.Big(gasStr) + if gas.BitLen() == 0 { + gas = big.NewInt(50000000) } - - if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + gasPrice := common.Big(gasPriceStr) + if gasPrice.BitLen() == 0 { + gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) } + msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr), false) header := be.bc.CurrentBlock().Header() vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{}) @@ -257,11 +253,12 @@ func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasSt tx = types.NewTransaction(nonce, to, value, gas, price, data) } - signature, err := be.am.Sign(from, tx.SigHash().Bytes()) + sigHash := (types.HomesteadSigner{}).Hash(tx) + signature, err := be.am.SignEthereum(from, sigHash.Bytes()) if err != nil { return "", err } - signedTx, err := tx.WithSignature(signature) + signedTx, err := tx.WithSignature(types.HomesteadSigner{}, signature) if err != nil { return "", err } diff --git a/vendor/github.com/ethereum/go-ethereum/common/types.go b/vendor/github.com/ethereum/go-ethereum/common/types.go index d00884484..70b7e7aae 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/types.go +++ b/vendor/github.com/ethereum/go-ethereum/common/types.go @@ -35,7 +35,10 @@ const ( var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes") type ( - Hash [HashLength]byte + // Hash represents the 32 byte Keccak256 hash of arbitrary data. + Hash [HashLength]byte + + // Address represents the 20 byte address of an Ethereum account. Address [AddressLength]byte ) diff --git a/vendor/github.com/ethereum/go-ethereum/console/bridge.go b/vendor/github.com/ethereum/go-ethereum/console/bridge.go index 22ed7192b..7f7e6feb1 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/bridge.go +++ b/vendor/github.com/ethereum/go-ethereum/console/bridge.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -126,6 +126,44 @@ func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) { return val } +// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password +// prompt to aquire the passphrase and executes the original RPC method (saved in +// jeth.sign) with it to actually execute the RPC call. +func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) { + var ( + message = call.Argument(0) + account = call.Argument(1) + passwd = call.Argument(2) + ) + + if !message.IsString() { + throwJSException("first argument must be the message to sign") + } + if !account.IsString() { + throwJSException("second argument must be the account to sign with") + } + + // if the password is not given or null ask the user and ensure password is a string + if passwd.IsUndefined() || passwd.IsNull() { + fmt.Fprintf(b.printer, "Give password for account %s\n", account) + if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + } + if !passwd.IsString() { + throwJSException("third argument must be the password to unlock the account") + } + + // Send the request to the backend and return + val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd) + if err != nil { + throwJSException(err.Error()) + } + return val +} + // Sleep will block the console for the specified number of seconds. func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) { if call.Argument(0).IsNumber() { diff --git a/vendor/github.com/ethereum/go-ethereum/console/console.go b/vendor/github.com/ethereum/go-ethereum/console/console.go index f224f0c2e..6e3d7e43c 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/console.go +++ b/vendor/github.com/ethereum/go-ethereum/console/console.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -156,10 +156,9 @@ func (c *Console) init(preload []string) error { if err != nil { return err } - // Override the unlockAccount and newAccount methods since these require user interaction. - // Assign the jeth.unlockAccount and jeth.newAccount in the Console the original web3 callbacks. - // These will be called by the jeth.* methods after they got the password from the user and send - // the original web3 request to the backend. + // Override the unlockAccount, newAccount and sign methods since these require user interaction. + // Assign these method in the Console the original web3 callbacks. These will be called by the jeth.* + // methods after they got the password from the user and send the original web3 request to the backend. if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { return fmt.Errorf("personal.unlockAccount: %v", err) @@ -167,8 +166,12 @@ func (c *Console) init(preload []string) error { if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { return fmt.Errorf("personal.newAccount: %v", err) } + if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { + return fmt.Errorf("personal.sign: %v", err) + } obj.Set("unlockAccount", bridge.UnlockAccount) obj.Set("newAccount", bridge.NewAccount) + obj.Set("sign", bridge.Sign) } } // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer. diff --git a/vendor/github.com/ethereum/go-ethereum/console/prompter.go b/vendor/github.com/ethereum/go-ethereum/console/prompter.go index 0e4a8a53e..5946d9ece 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/prompter.go +++ b/vendor/github.com/ethereum/go-ethereum/console/prompter.go @@ -95,7 +95,7 @@ func newTerminalPrompter() *terminalPrompter { } p.SetCtrlCAborts(true) p.SetTabCompletionStyle(liner.TabPrints) - + p.SetMultiLineMode(true) return p } diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go index 8d1710197..cd79112cd 100644 --- a/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/core/block_validator.go b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go index e5bc6178b..65f975345 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/block_validator.go +++ b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go @@ -41,13 +41,13 @@ var ( // // BlockValidator implements Validator. type BlockValidator struct { - config *ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - Pow pow.PoW // Proof of work used for validating + config *params.ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { validator := &BlockValidator{ config: config, Pow: pow, @@ -93,14 +93,14 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { // Verify UncleHash before running other uncle validations unclesSha := types.CalcUncleHash(block.Uncles()) if unclesSha != header.UncleHash { - return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) + return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha) } // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) // can be used by light clients to make sure they've received the correct Txs txSha := types.DeriveSha(block.Transactions()) if txSha != header.TxHash { - return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) + return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha) } return nil @@ -113,23 +113,23 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { header := block.Header() if block.GasUsed().Cmp(usedGas) != 0 { - return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas)) + return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)) } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) if rbloom != header.Bloom { - return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom) + return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) if receiptSha != header.ReceiptHash { - return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) + return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } // Validate the state root against the received state root and throw // an error if they don't match. - if root := statedb.IntermediateRoot(); header.Root != root { - return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } return nil } @@ -203,7 +203,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b // Validates a header. Returns an error if the header is invalid. // // See YP section 4.3.4. "Block Header Validity" -func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { +func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) } @@ -223,7 +223,7 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) if expd.Cmp(header.Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) + return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) } a := new(big.Int).Set(parent.GasLimit) @@ -232,7 +232,7 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare b := new(big.Int).Set(parent.GasLimit) b = b.Div(b, params.GasLimitBoundDivisor) if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { - return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) + return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b) } num := new(big.Int).Set(parent.Number) @@ -248,13 +248,21 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare } } // If all checks passed, validate the extra-data field for hard forks - return ValidateDAOHeaderExtraData(config, header) + if err := ValidateDAOHeaderExtraData(config, header); err != nil { + return err + } + if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { + if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { + return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) + } + } + return nil } // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. -func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { +func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) } else { diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go index c822615b5..9ace6d79a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -38,6 +38,7 @@ import ( "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/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -78,7 +79,7 @@ const ( // included in the canonical one where as GetBlockByNumber always represents the // canonical chain. type BlockChain struct { - config *ChainConfig // chain & network configuration + config *params.ChainConfig // chain & network configuration hc *HeaderChain chainDb ethdb.Database @@ -113,7 +114,7 @@ type BlockChain struct { // NewBlockChain returns a fully initialised block chain using information // available in the database. It initialiser the default Ethereum Validator and // Processor. -func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { +func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -633,17 +634,19 @@ func (self *BlockChain) Rollback(chain []common.Hash) { } // SetReceiptsData computes all the non-consensus fields of the receipts -func SetReceiptsData(block *types.Block, receipts types.Receipts) { +func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) { + signer := types.MakeSigner(config, block.Number()) + transactions, logIndex := block.Transactions(), uint(0) for j := 0; j < len(receipts); j++ { // The transaction hash can be retrieved from the transaction itself receipts[j].TxHash = transactions[j].Hash() + tx, _ := transactions[j].AsMessage(signer) // The contract address can be derived from the transaction itself - if MessageCreatesContract(transactions[j]) { - from, _ := transactions[j].From() - receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) + if MessageCreatesContract(tx) { + receipts[j].ContractAddress = crypto.CreateAddress(tx.From(), tx.Nonce()) } // The used gas can be calculated based on previous receipts if j == 0 { @@ -665,6 +668,7 @@ func SetReceiptsData(block *types.Block, receipts types.Receipts) { // InsertReceiptChain attempts to complete an already existing header chain with // transaction and receipt data. +// XXX should this be moved to the test? func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { self.wg.Add(1) defer self.wg.Done() @@ -704,7 +708,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain continue } // Compute all the non-consensus fields of the receipts - SetReceiptsData(block, receipts) + SetReceiptsData(self.config, block, receipts) // Write all the data out into the database if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil { errs[index] = fmt.Errorf("failed to write block body: %v", err) @@ -780,7 +784,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain if stats.ignored > 0 { ignored = fmt.Sprintf(" (%d ignored)", stats.ignored) } - glog.V(logger.Info).Infof("imported %d receipts%s in %9v. #%d [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + glog.V(logger.Info).Infof("imported %d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored) return 0, nil } @@ -874,7 +878,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { if BadHashes[block.Hash()] { err := BadHashError(block.Hash()) - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Stage 1 validation of the block using the chain's validator @@ -906,7 +910,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { continue } - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } @@ -920,23 +924,23 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { err = self.stateCache.Reset(chain[i-1].Root()) } if err != nil { - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Process block using the parent state as reference point. - receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.config.VmConfig) + receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{}) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Validate the state using the default validator err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Write state changes to database - _, err = self.stateCache.Commit() + _, err = self.stateCache.Commit(self.config.IsEIP158(block.Number())) if err != nil { return i, err } @@ -987,6 +991,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { stats.processed++ if glog.V(logger.Info) { + stats.usedGas += usedGas.Uint64() stats.report(chain, i) } } @@ -999,6 +1004,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // insertStats tracks and reports on block insertion. type insertStats struct { queued, processed, ignored int + usedGas uint64 lastIndex int startTime time.Time } @@ -1010,10 +1016,15 @@ const statsReportLimit = 8 * time.Second // report prints statistics if some number of blocks have been processed // or more than a few seconds have passed since the last message. func (st *insertStats) report(chain []*types.Block, index int) { + // Fetch the timings for the batch var ( now = time.Now() elapsed = now.Sub(st.startTime) ) + if elapsed == 0 { // Yes Windows, I'm looking at you + elapsed = 1 + } + // If we're at the last block of the batch or report period reached, log if index == len(chain)-1 || elapsed >= statsReportLimit { start, end := chain[st.lastIndex], chain[index] txcount := countTransactions(chain[st.lastIndex : index+1]) @@ -1022,7 +1033,13 @@ func (st *insertStats) report(chain []*types.Block, index int) { if st.queued > 0 || st.ignored > 0 { extra = fmt.Sprintf(" (%d queued %d ignored)", st.queued, st.ignored) } - glog.Infof("imported %d blocks%s, %5d txs in %9v. #%v [%x… / %x…]\n", st.processed, extra, txcount, common.PrettyDuration(elapsed), end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) + hashes := "" + if st.processed > 1 { + hashes = fmt.Sprintf("%x… / %x…", start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) + } else { + hashes = fmt.Sprintf("%x…", end.Hash().Bytes()[:4]) + } + glog.Infof("imported %d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra) *st = insertStats{startTime: now, lastIndex: index} } @@ -1190,10 +1207,23 @@ func (self *BlockChain) update() { } // reportBlock logs a bad block error. -func reportBlock(block *types.Block, err error) { +func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { if glog.V(logger.Error) { - glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex()) - glog.Errorf(" %v", err) + var receiptString string + for _, receipt := range receipts { + receiptString += fmt.Sprintf("\t%v\n", receipt) + } + glog.Errorf(` +########## BAD BLOCK ######### +Chain config: %v + +Number: %v +Hash: 0x%x +%v + +Error: %v +############################## +`, bc.config, block.Number(), block.Hash(), receiptString, err) } } @@ -1296,4 +1326,4 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { } // Config retrieves the blockchain's chain configuration. -func (self *BlockChain) Config() *ChainConfig { return self.config } +func (self *BlockChain) Config() *params.ChainConfig { return self.config } diff --git a/vendor/github.com/ethereum/go-ethereum/core/blocks.go b/vendor/github.com/ethereum/go-ethereum/core/blocks.go index ecccc541f..cf8c86507 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blocks.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blocks.go @@ -21,4 +21,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("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true, + common.HexToHash("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f"): true, } diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go index e3ad9cda0..e1dafb32d 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go @@ -35,8 +35,8 @@ import ( */ // MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. -func MakeChainConfig() *ChainConfig { - return &ChainConfig{ +func MakeChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, @@ -73,6 +73,8 @@ type BlockGen struct { txs []*types.Transaction receipts []*types.Receipt uncles []*types.Header + + config *params.ChainConfig } // SetCoinbase sets the coinbase of the generated block. @@ -106,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -178,10 +180,10 @@ func (b *BlockGen) OffsetTime(seconds int64) { // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. -func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { +func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { - b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} + b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config} // Mutate the state and block according to any hard-fork specs if config == nil { @@ -203,7 +205,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, gen(i, b) } AccumulateRewards(statedb, h, b.uncles) - root, err := statedb.Commit() + root, err := statedb.Commit(config.IsEIP158(h.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -215,7 +217,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, if err != nil { panic(err) } - header := makeHeader(parent, statedb) + header := makeHeader(config, parent, statedb) block, receipt := genblock(i, header, statedb) blocks[i] = block receipts[i] = receipt @@ -224,7 +226,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, return blocks, receipts } -func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { +func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) @@ -232,7 +234,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } return &types.Header{ - Root: state.IntermediateRoot(), + Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), @@ -283,7 +285,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ // makeBlockChain creates a deterministic chain of blocks rooted at parent. func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block { - blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) return blocks diff --git a/vendor/github.com/ethereum/go-ethereum/core/config.go b/vendor/github.com/ethereum/go-ethereum/core/config.go deleted file mode 100644 index 3ab04e520..000000000 --- a/vendor/github.com/ethereum/go-ethereum/core/config.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 . - -package core - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" -) - -var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error - -// ChainConfig is the core config which determines the blockchain settings. -// -// ChainConfig is stored in the database on a per block basis. This means -// that any network, identified by its genesis block, can have its own -// set of configuration options. -type ChainConfig struct { - HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) - DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) - DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork - - HomesteadGasRepriceBlock *big.Int `json:"homesteadGasRepriceBlock"` // Homestead gas reprice switch block (nil = no fork) - - VmConfig vm.Config `json:"-"` -} - -// IsHomestead returns whether num is either equal to the homestead block or greater. -func (c *ChainConfig) IsHomestead(num *big.Int) bool { - if c.HomesteadBlock == nil || num == nil { - return false - } - return num.Cmp(c.HomesteadBlock) >= 0 -} - -// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). -// -// The returned GasTable's fields shouldn't, under any circumstances, be changed. -func (c *ChainConfig) GasTable(num *big.Int) params.GasTable { - if c.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(c.HomesteadGasRepriceBlock) < 0 { - return params.GasTableHomestead - } - - return params.GasTableHomesteadGasRepriceFork -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/dao.go b/vendor/github.com/ethereum/go-ethereum/core/dao.go index e315c9884..1260c310a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/dao.go +++ b/vendor/github.com/ethereum/go-ethereum/core/dao.go @@ -33,7 +33,7 @@ import ( // with the fork specific extra-data set // b) if the node is pro-fork, require blocks in the specific range to have the // unique extra-data set. -func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error { +func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { // Short circuit validation if the node doesn't care about the DAO fork if config.DAOForkBlock == nil { return nil diff --git a/vendor/github.com/ethereum/go-ethereum/core/database_util.go b/vendor/github.com/ethereum/go-ethereum/core/database_util.go index 73fac20aa..84669de35 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/database_util.go +++ b/vendor/github.com/ethereum/go-ethereum/core/database_util.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "math/big" @@ -28,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -59,6 +61,8 @@ var ( oldBlockNumPrefix = []byte("block-num-") oldBlockReceiptsPrefix = []byte("receipts-block-") oldBlockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] + + ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error ) // encodeBlockNumber encodes a block number as big endian uint64 @@ -600,7 +604,7 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) { } // WriteChainConfig writes the chain config settings to the database. -func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error { +func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error { // short circuit and ignore if nil config. GetChainConfig // will return a default. if cfg == nil { @@ -616,13 +620,13 @@ func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) err } // GetChainConfig will fetch the network settings based on the given hash. -func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { +func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) { jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...)) if len(jsonChainConfig) == 0 { return nil, ChainConfigNotFoundErr } - var config ChainConfig + var config params.ChainConfig if err := json.Unmarshal(jsonChainConfig, &config); err != nil { return nil, err } @@ -632,24 +636,24 @@ func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { // FindCommonAncestor returns the last common ancestor of two block headers func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header { - for a.GetNumberU64() > b.GetNumberU64() { - a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1) + for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) if a == nil { return nil } } - for a.GetNumberU64() < b.GetNumberU64() { - b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1) + for an := a.Number.Uint64(); an < b.Number.Uint64(); { + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) if b == nil { return nil } } for a.Hash() != b.Hash() { - a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1) + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) if a == nil { return nil } - b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1) + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) if b == nil { return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go index 6ffe04ee8..cbb5a0fc8 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go +++ b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go @@ -19,3 +19,5 @@ package core // defaultGenesisBlock is a gzip compressed dump of the official default Ethereum // genesis block. const defaultGenesisBlock = "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" + +const defaultTestnetGenesisBlock = "QlpoOTFBWSZTWezl5v8AGI59gHRQABBcBH/0FGQ/795qUAN+PcGHAoQMVIwm0zCSp+ihiAANB6gqp+mmp6eMmqo0AADRpoABgAGjTQBk0BoMgNAbSqNAyDQyaBoNAyAGBUkhNNNA0JGmgD0g0AekxJtVIgTtRupd52vqSSSVfG2ALQBhhdbCIgmAgUiIJnEQTviQF8kkN1IOKiRVVFVSVXHT1cHLHdiSQBlAAG2JtCtNgCS5IEB6dNtP782Z729Tbt3Z6+uFr7u3w79widyYSMAhtlL5XeGZWdwEBNdFm+Hh5gVRBoslgksAMkUKmYlSJRTxBsQAAKKlnd3WjPWrNd66NR7WAu6AQL40vqlny1We+p3l1QThy4gRd2lSVjEKISLDFrVJ1UtjPEPctVSZGjBTJWRzpdgRmCCprJWGe3DK9wvz4eGvdv1ZZcMvxGgdakeMAVEeFywVQWLWCrUWg60Y9ZLoAuZ0uSJJUELb8N2e7j05d3Tlz7vRwgk+LisedJALLVUgFjLGzfv5SEGPLlhhjpx2aaO8T87whcYDmgPLVDBI3RF1TLnzvymWtN732u93GiAGiBdm5s1q9Haru7+yCLJAk3wBDTtrGeMVh3iJmXHo+OBAsqoDOZwjKM3d3mZmcHdBJiSVouSKVkDg+L4vSzNRqs7u6NEaIiUKoYxERGfTWIhoiHwQFESGzta21qu1qtZ3cAXQshVAUybTLbWttWh3iHLoDJEgkACRVCzbeX774Z6558+mfDTdz058efTpDbXai+tddbQBbn/YA9fwxy8/Pq3HFW7deL8W447duuyI2RGqIAAA9SOqHeXzbRtGiXeHiYBMggAAIyQh/f5vW96Xvvu7b7777yMURRHFE6EkEnxRIiOlY2ijs7RV4gYIB0R4LmgB49xg0Oz0bVr2a95u4AZE6rTjl2w58Cntu1ZV6e3Lw3ne2RegHw7R58NdbtVlJEkuqRV2WKa/hpbPIx3bIyX43aNWmezrCBry2ePzkA8reSwB/xdyRThQkOzl5v8=" diff --git a/vendor/github.com/ethereum/go-ethereum/core/events.go b/vendor/github.com/ethereum/go-ethereum/core/events.go index bed8c7b90..322bcb769 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/events.go +++ b/vendor/github.com/ethereum/go-ethereum/core/events.go @@ -38,9 +38,6 @@ type PendingLogsEvent struct { // PendingStateEvent is posted pre mining and notifies of pending state changes. type PendingStateEvent struct{} -// NewBlockEvent is posted when a block has been imported. -type NewBlockEvent struct{ Block *types.Block } - // NewMinedBlockEvent is posted when a block has been imported. type NewMinedBlockEvent struct{ Block *types.Block } diff --git a/vendor/github.com/ethereum/go-ethereum/core/execution.go b/vendor/github.com/ethereum/go-ethereum/core/execution.go index 1cb507ee7..e3ea1006c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/execution.go +++ b/vendor/github.com/ethereum/go-ethereum/core/execution.go @@ -27,62 +27,17 @@ import ( // Call executes within the given contract func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) - return ret, err -} - -// CallCode executes the given address' code as the given contract address -func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - callerAddr := caller.Address() - ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) - return ret, err -} - -// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope -func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { - callerAddr := caller.Address() - originAddr := env.Origin() - callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue) - return ret, err -} - -// Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { - ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value) - // Here we get an error if we run into maximum stack depth, - // See: https://github.com/ethereum/yellowpaper/pull/131 - // and YP definitions for CREATE instruction - if err != nil { - return nil, address, err - } - return ret, address, err -} - -func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas, gasPrice) - return nil, common.Address{}, vm.DepthError + return nil, vm.DepthError } - if !env.CanTransfer(caller.Address(), value) { caller.ReturnGas(gas, gasPrice) - return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - var createAccount bool - if address == nil { - // Create a new account on the state - nonce := env.Db().GetNonce(caller.Address()) - env.Db().SetNonce(caller.Address(), nonce+1) - addr = crypto.CreateAddress(caller.Address(), nonce) - address = &addr - createAccount = true + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) } snapshotPreTransfer := env.SnapshotDatabase() @@ -90,14 +45,15 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A from = env.Db().GetAccount(caller.Address()) to vm.Account ) - if createAccount { - to = env.Db().CreateAccount(*address) - } else { - if !env.Db().Exist(*address) { - to = env.Db().CreateAccount(*address) - } else { - to = env.Db().GetAccount(*address) + if !env.Db().Exist(addr) { + if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 { + caller.ReturnGas(gas, gasPrice) + return nil, nil } + + to = env.Db().CreateAccount(addr) + } else { + to = env.Db().GetAccount(addr) } env.Transfer(from, to, value) @@ -105,19 +61,106 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(codeAddr, codeHash, code) + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) defer contract.Finalise() - ret, err = evm.Run(contract, input) + ret, err = env.Vm().Run(contract, input) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil { + contract.UseGas(contract.Gas) + + env.RevertToSnapshot(snapshotPreTransfer) + } + return ret, err +} + +// CallCode executes the given address' code as the given contract address +func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth() > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas, gasPrice) + + return nil, vm.DepthError + } + if !env.CanTransfer(caller.Address(), value) { + caller.ReturnGas(gas, gasPrice) + + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) + } + + var ( + snapshotPreTransfer = env.SnapshotDatabase() + to = env.Db().GetAccount(caller.Address()) + ) + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) + defer contract.Finalise() + + ret, err = env.Vm().Run(contract, input) + if err != nil { + contract.UseGas(contract.Gas) + + env.RevertToSnapshot(snapshotPreTransfer) + } + + return ret, err +} + +// Create creates a new contract with the given code +func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth() > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas, gasPrice) + + return nil, common.Address{}, vm.DepthError + } + if !env.CanTransfer(caller.Address(), value) { + caller.ReturnGas(gas, gasPrice) + + return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) + } + + // Create a new account on the state + nonce := env.Db().GetNonce(caller.Address()) + env.Db().SetNonce(caller.Address(), nonce+1) + + snapshotPreTransfer := env.SnapshotDatabase() + var ( + addr = crypto.CreateAddress(caller.Address(), nonce) + from = env.Db().GetAccount(caller.Address()) + to = env.Db().CreateAccount(addr) + ) + if env.ChainConfig().IsEIP158(env.BlockNumber()) { + env.Db().SetNonce(addr, 1) + } + env.Transfer(from, to, value) + + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) + defer contract.Finalise() + + ret, err = env.Vm().Run(contract, nil) + // check whether the max code size has been exceeded + maxCodeSizeExceeded := len(ret) > params.MaxCodeSize // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. - if err == nil && createAccount { + if err == nil && !maxCodeSizeExceeded { dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) if contract.UseGas(dataGas) { - env.Db().SetCode(*address, ret) + env.Db().SetCode(addr, ret) } else { err = vm.CodeStoreOutOfGasError } @@ -126,46 +169,45 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if maxCodeSizeExceeded || + (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) { contract.UseGas(contract.Gas) - env.RevertToSnapshot(snapshotPreTransfer) + + // Nothing should be returned when an error is thrown. + return nil, addr, err } return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() +// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope +func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas, gasPrice) - return nil, common.Address{}, vm.DepthError + return nil, vm.DepthError } - snapshot := env.SnapshotDatabase() - - var to vm.Account - if !env.Db().Exist(*toAddr) { - to = env.Db().CreateAccount(*toAddr) - } else { - to = env.Db().GetAccount(*toAddr) - } + var ( + snapshot = env.SnapshotDatabase() + to = env.Db().GetAccount(caller.Address()) + ) // Iinitialise a new contract and make initialise the delegate values - contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate() - contract.SetCallCode(codeAddr, codeHash, code) + contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate() + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) defer contract.Finalise() - ret, err = evm.Run(contract, input) + ret, err = env.Vm().Run(contract, input) if err != nil { contract.UseGas(contract.Gas) env.RevertToSnapshot(snapshot) } - return ret, addr, err + return ret, err } // generic transfer method diff --git a/vendor/github.com/ethereum/go-ethereum/core/genesis.go b/vendor/github.com/ethereum/go-ethereum/core/genesis.go index 637b63320..895b24a82 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/genesis.go +++ b/vendor/github.com/ethereum/go-ethereum/core/genesis.go @@ -17,6 +17,7 @@ package core import ( + "compress/bzip2" "compress/gzip" "encoding/base64" "encoding/json" @@ -43,7 +44,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } var genesis struct { - ChainConfig *ChainConfig `json:"config"` + ChainConfig *params.ChainConfig `json:"config"` Nonce string Timestamp string ParentHash string @@ -73,7 +74,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) } } - root, stateBatch := statedb.CommitBatch() + root, stateBatch := statedb.CommitBatch(false) difficulty := common.String2Big(genesis.Difficulty) block := types.NewBlock(&types.Header{ @@ -128,7 +129,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big statedb, _ := state.New(common.Hash{}, db) obj := statedb.GetOrNewStateObject(addr) obj.SetBalance(balance) - root, err := statedb.Commit() + root, err := statedb.Commit(false) if err != nil { panic(fmt.Sprintf("cannot write state: %v", err)) } @@ -174,7 +175,7 @@ func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { // WriteTestNetGenesisBlock assembles the Morden test network genesis block and // writes it - along with all associated state - into a chain database. func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { - return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock())) + return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock())) } // WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it @@ -197,6 +198,15 @@ func DefaultGenesisBlock() string { return string(blob) } +func DefaultTestnetGenesisBlock() string { + reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock))) + blob, err := ioutil.ReadAll(reader) + if err != nil { + panic(fmt.Sprintf("failed to load default genesis: %v", err)) + } + return string(blob) +} + // OlympicGenesisBlock assembles a JSON string representing the Olympic genesis // block. func OlympicGenesisBlock() string { @@ -220,25 +230,3 @@ func OlympicGenesisBlock() string { } }`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes()) } - -// TestNetGenesisBlock assembles a JSON string representing the Morden test net -// genenis block. -func TestNetGenesisBlock() string { - return fmt.Sprintf(`{ - "nonce": "0x%x", - "difficulty": "0x20000", - "mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578", - "coinbase": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2FEFD8", - "alloc": { - "0000000000000000000000000000000000000001": { "balance": "1" }, - "0000000000000000000000000000000000000002": { "balance": "1" }, - "0000000000000000000000000000000000000003": { "balance": "1" }, - "0000000000000000000000000000000000000004": { "balance": "1" }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } - } - }`, types.EncodeNonce(0x6d6f7264656e)) -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go index 8ca06d9b4..c53694571 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "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" "github.com/hashicorp/golang-lru" ) @@ -48,7 +49,7 @@ const ( // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { - config *ChainConfig + config *params.ChainConfig chainDb ethdb.Database genesisHeader *types.Header @@ -73,7 +74,7 @@ type getHeaderValidatorFn func() HeaderValidator // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) numberCache, _ := lru.New(numberCacheLimit) @@ -490,13 +491,13 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { // // headerValidator implements HeaderValidator. type headerValidator struct { - config *ChainConfig + config *params.ChainConfig hc *HeaderChain // Canonical header chain Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { +func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { return &headerValidator{ config: config, Pow: pow, diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/journal.go b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go index 720c821b9..d1e73e7d0 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/journal.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go @@ -67,10 +67,13 @@ type ( addLogChange struct { txhash common.Hash } + touchChange struct { + account *common.Address + prev bool + } ) func (ch createObjectChange) undo(s *StateDB) { - s.GetStateObject(*ch.account).deleted = true delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) } @@ -87,6 +90,15 @@ func (ch suicideChange) undo(s *StateDB) { } } +var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") + +func (ch touchChange) undo(s *StateDB) { + if !ch.prev && *ch.account != ripemd { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) + } +} + func (ch balanceChange) undo(s *StateDB) { s.GetStateObject(*ch.account).setBalance(ch.prev) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go index edb073173..d40b42d83 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go @@ -87,10 +87,16 @@ type StateObject struct { // during the "update" phase of the state transition. dirtyCode bool // true if the code was updated suicided bool + touched bool deleted bool onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } +// empty returns whether the account is considered empty. +func (s *StateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + // Account is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. type Account struct { @@ -134,6 +140,18 @@ func (self *StateObject) markSuicided() { } } +func (c *StateObject) touch() { + c.db.journal = append(c.db.journal, touchChange{ + account: &c.address, + prev: c.touched, + }) + if c.onDirty != nil { + c.onDirty(c.Address()) + c.onDirty = nil + } + c.touched = true +} + func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { if c.trie == nil { var err error @@ -221,8 +239,16 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e return err } +// AddBalance removes amount from c's balance. +// It is used to add funds to the destination account of a transfer. func (c *StateObject) AddBalance(amount *big.Int) { + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. if amount.Cmp(common.Big0) == 0 { + if c.empty() { + c.touch() + } + return } c.SetBalance(new(big.Int).Add(c.Balance(), amount)) @@ -232,6 +258,8 @@ func (c *StateObject) AddBalance(amount *big.Int) { } } +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. func (c *StateObject) SubBalance(amount *big.Int) { if amount.Cmp(common.Big0) == 0 { return diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go index dcb897628..3742c178b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -34,17 +34,13 @@ import ( lru "github.com/hashicorp/golang-lru" ) -// The starting nonce determines the default nonce when new accounts are being -// created. -var StartingNonce uint64 +// Trie cache generation limit after which to evic trie nodes from memory. +var MaxTrieCacheGen = uint16(120) const ( - // Number of past tries to keep. The arbitrarily chosen value here - // is max uncle depth + 1. - maxPastTries = 8 - - // Trie cache generation limit. - maxTrieCacheGen = 100 + // Number of past tries to keep. This value is chosen such that + // reasonable chain reorg depths will hit an existing trie. + maxPastTries = 12 // Number of codehash->size associations to keep. codeSizeCacheSize = 100000 @@ -89,7 +85,7 @@ type StateDB struct { // Create a new state from a given trie func New(root common.Hash, db ethdb.Database) (*StateDB, error) { - tr, err := trie.NewSecure(root, db, maxTrieCacheGen) + tr, err := trie.NewSecure(root, db, MaxTrieCacheGen) if err != nil { return nil, err } @@ -158,7 +154,7 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { return &tr, nil } } - return trie.NewSecure(root, self.db, maxTrieCacheGen) + return trie.NewSecure(root, self.db, MaxTrieCacheGen) } func (self *StateDB) pushTrie(t *trie.SecureTrie) { @@ -213,6 +209,13 @@ func (self *StateDB) Exist(addr common.Address) bool { return self.GetStateObject(addr) != nil } +// Empty returns whether the state object is either non-existant +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (self *StateDB) Empty(addr common.Address) bool { + so := self.GetStateObject(addr) + return so == nil || so.empty() +} + func (self *StateDB) GetAccount(addr common.Address) vm.Account { return self.GetStateObject(addr) } @@ -232,7 +235,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 { return stateObject.Nonce() } - return StartingNonce + return 0 } func (self *StateDB) GetCode(addr common.Address) []byte { @@ -416,7 +419,7 @@ func (self *StateDB) MarkStateObjectDirty(addr common.Address) { func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) { prev = self.GetStateObject(addr) newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) - newobj.setNonce(StartingNonce) // sets the object to dirty + newobj.setNonce(0) // sets the object to dirty if prev == nil { if glog.V(logger.Core) { glog.Infof("(+) %x\n", addr) @@ -516,10 +519,10 @@ func (self *StateDB) GetRefund() *big.Int { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot() common.Hash { +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { for addr, _ := range s.stateObjectsDirty { stateObject := s.stateObjects[addr] - if stateObject.suicided { + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) } else { stateObject.updateRoot(s.db) @@ -553,17 +556,19 @@ func (s *StateDB) DeleteSuicides() { } // Commit commits all state changes to the database. -func (s *StateDB) Commit() (root common.Hash, err error) { - root, batch := s.CommitBatch() +func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { + root, batch := s.CommitBatch(deleteEmptyObjects) return root, batch.Write() } // CommitBatch commits all state changes to a write batch but does not // execute the batch. It is used to validate state changes against // the root hash stored in a block. -func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { +func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { batch = s.db.NewBatch() - root, _ = s.commit(batch) + root, _ = s.commit(batch, deleteEmptyObjects) + + glog.V(logger.Debug).Infof("Trie cache stats: %d misses, %d unloads", trie.CacheMisses(), trie.CacheUnloads()) return root, batch } @@ -573,16 +578,18 @@ func (s *StateDB) clearJournalAndRefund() { s.refund = new(big.Int) } -func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) { +func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() // Commit objects to the trie. for addr, stateObject := range s.stateObjects { - if stateObject.suicided { + _, isDirty := s.stateObjectsDirty[addr] + switch { + case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. s.deleteStateObject(stateObject) - } else if _, ok := s.stateObjectsDirty[addr]; ok { + case isDirty: // Write any contract code associated with the state object if stateObject.code != nil && stateObject.dirtyCode { if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/sync.go b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go index ef2b4b84c..bab9c8e7e 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go @@ -59,8 +59,10 @@ func (s *StateSync) Missing(max int) []common.Hash { return (*trie.TrieSync)(s).Missing(max) } -// Process injects a batch of retrieved trie nodes data. -func (s *StateSync) Process(list []trie.SyncResult) (int, error) { +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *StateSync) Process(list []trie.SyncResult) (bool, int, error) { return (*trie.TrieSync)(s).Process(list) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go index fd8e9762e..e346917c3 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go @@ -25,6 +25,7 @@ import ( "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" ) var ( @@ -37,12 +38,12 @@ var ( // // StateProcessor implements Processor. type StateProcessor struct { - config *ChainConfig + config *params.ChainConfig bc *BlockChain } // NewStateProcessor initialises a new StateProcessor. -func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor { +func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor { return &StateProcessor{ config: config, bc: bc, @@ -71,10 +72,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { + //fmt.Println("tx:", i) statedb.StartRecord(tx.Hash(), block.Hash(), i) receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { - return nil, nil, totalUsedGas, err + return nil, nil, nil, err } receipts = append(receipts, receipt) allLogs = append(allLogs, logs...) @@ -89,20 +91,24 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // // ApplyTransactions returns the generated receipts and vm logs during the // execution of the state transition phase. -func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) +func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + if err != nil { + return nil, nil, nil, err + } + + _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp) if err != nil { return nil, nil, nil, err } // Update the state with pending changes usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) + receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) - if MessageCreatesContract(tx) { - from, _ := tx.From() - receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) + if MessageCreatesContract(msg) { + receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce()) } logs := statedb.GetLogs(tx.Hash()) diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go index 9e6b2f567..8abe17b0a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go @@ -62,8 +62,8 @@ type StateTransition struct { // Message represents a message sent to a contract. type Message interface { - From() (common.Address, error) - FromFrontier() (common.Address, error) + From() common.Address + //FromFrontier() (common.Address, error) To() *common.Address GasPrice() *big.Int @@ -134,23 +134,12 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In return ret, gasUsed, err } -func (self *StateTransition) from() (vm.Account, error) { - var ( - f common.Address - err error - ) - if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { - f, err = self.msg.From() - } else { - f, err = self.msg.FromFrontier() - } - if err != nil { - return nil, err - } +func (self *StateTransition) from() vm.Account { + f := self.msg.From() if !self.state.Exist(f) { - return self.state.CreateAccount(f), nil + return self.state.CreateAccount(f) } - return self.state.GetAccount(f), nil + return self.state.GetAccount(f) } func (self *StateTransition) to() vm.Account { @@ -185,14 +174,11 @@ func (self *StateTransition) buyGas() error { mgas := self.msg.Gas() mgval := new(big.Int).Mul(mgas, self.gasPrice) - sender, err := self.from() - if err != nil { - return err - } + sender := self.from() if sender.Balance().Cmp(mgval) < 0 { return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance()) } - if err = self.gp.SubGas(mgas); err != nil { + if err := self.gp.SubGas(mgas); err != nil { return err } self.addGas(mgas) @@ -203,10 +189,7 @@ func (self *StateTransition) buyGas() error { func (self *StateTransition) preCheck() (err error) { msg := self.msg - sender, err := self.from() - if err != nil { - return err - } + sender := self.from() // Make sure this transaction's nonce is correct if msg.CheckNonce() { @@ -232,9 +215,9 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b return } msg := self.msg - sender, _ := self.from() // err checked in preCheck + sender := self.from() // err checked in preCheck - homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber()) + homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { @@ -282,7 +265,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b func (self *StateTransition) refundGas() { // Return eth for remaining gas to the sender account, // exchanged at the original rate. - sender, _ := self.from() // err already checked + sender := self.from() // err already checked remaining := new(big.Int).Mul(self.gas, self.gasPrice) sender.AddBalance(remaining) diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go index 2c8a5c396..edcbc21eb 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go @@ -30,6 +30,8 @@ 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/metrics" + "github.com/ethereum/go-ethereum/params" "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) @@ -55,6 +57,23 @@ var ( evictionInterval = time.Minute // Time interval to check for evictable transactions ) +var ( + // Metrics for the pending pool + pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard") + pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace") + pendingRLCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting + pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds + + // Metrics for the queued pool + queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard") + queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace") + queuedRLCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting + queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds + + // General tx metrics + invalidTxCounter = metrics.NewCounter("txpool/invalid") +) + type stateFn func() (*state.StateDB, error) // TxPool contains all currently known transactions. Transactions @@ -65,7 +84,7 @@ type stateFn func() (*state.StateDB, error) // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - config *ChainConfig + config *params.ChainConfig currentState stateFn // The state function which will allow us to do some pre checks pendingState *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback @@ -73,6 +92,7 @@ type TxPool struct { eventMux *event.TypeMux events event.Subscription localTx *txSet + signer types.Signer mu sync.RWMutex pending map[common.Address]*txList // All currently processable transactions @@ -86,9 +106,10 @@ type TxPool struct { homestead bool } -func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { pool := &TxPool{ config: config, + signer: types.NewEIP155Signer(config.ChainId), pending: make(map[common.Address]*txList), queue: make(map[common.Address]*txList), all: make(map[common.Hash]*types.Transaction), @@ -120,8 +141,10 @@ func (pool *TxPool) eventLoop() { switch ev := ev.Data.(type) { case ChainHeadEvent: pool.mu.Lock() - if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) { - pool.homestead = true + if ev.Block != nil { + if pool.config.IsHomestead(ev.Block.Number()) { + pool.homestead = true + } } pool.resetState() @@ -253,7 +276,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return err } - from, err := tx.From() + from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } @@ -288,7 +311,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return ErrInsufficientFunds } - intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead) + intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) if tx.Gas().Cmp(intrGas) < 0 { return ErrIntrinsicGas } @@ -306,6 +329,7 @@ func (pool *TxPool) add(tx *types.Transaction) error { } // Otherwise ensure basic validation passes and queue it up if err := pool.validateTx(tx); err != nil { + invalidTxCounter.Inc(1) return err } pool.enqueueTx(hash, tx) @@ -316,7 +340,7 @@ func (pool *TxPool) add(tx *types.Transaction) error { if to := tx.To(); to != nil { rcpt = common.Bytes2Hex(to[:4]) } - from, _ := tx.From() // from already verified during tx validation + from, _ := types.Sender(pool.signer, tx) // from already verified during tx validation glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash) } return nil @@ -327,17 +351,19 @@ func (pool *TxPool) add(tx *types.Transaction) error { // Note, this method assumes the pool lock is held! func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) { // Try to insert the transaction into the future queue - from, _ := tx.From() // already validated + from, _ := types.Sender(pool.signer, tx) // already validated if pool.queue[from] == nil { pool.queue[from] = newTxList(false) } inserted, old := pool.queue[from].Add(tx) if !inserted { + queuedDiscardCounter.Inc(1) return // An older transaction was better, discard this } // Discard any previous transaction and mark this if old != nil { delete(pool.all, old.Hash()) + queuedReplaceCounter.Inc(1) } pool.all[hash] = tx } @@ -360,11 +386,13 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T if !inserted { // An older transaction was better, discard this delete(pool.all, hash) + pendingDiscardCounter.Inc(1) return } // Otherwise discard any previous transaction and mark this if old != nil { delete(pool.all, old.Hash()) + pendingReplaceCounter.Inc(1) } pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests) @@ -435,7 +463,7 @@ func (pool *TxPool) removeTx(hash common.Hash) { if !ok { return } - addr, _ := tx.From() // already validated during insertion + addr, _ := types.Sender(pool.signer, tx) // already validated during insertion // Remove it from the list of known transactions delete(pool.all, hash) @@ -499,6 +527,7 @@ func (pool *TxPool) promoteExecutables() { glog.Infof("Removed unpayable queued transaction: %v", tx) } delete(pool.all, tx.Hash()) + queuedNofundsCounter.Inc(1) } // Gather all executable transactions and promote them for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) { @@ -513,6 +542,7 @@ func (pool *TxPool) promoteExecutables() { glog.Infof("Removed cap-exceeding queued transaction: %v", tx) } delete(pool.all, tx.Hash()) + queuedRLCounter.Inc(1) } queued += uint64(list.Len()) @@ -527,6 +557,7 @@ func (pool *TxPool) promoteExecutables() { pending += uint64(list.Len()) } if pending > maxPendingTotal { + pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first spammers := prque.New() for addr, list := range pool.pending { @@ -573,6 +604,7 @@ func (pool *TxPool) promoteExecutables() { } } } + pendingRLCounter.Inc(int64(pendingBeforeCap - pending)) } // If we've queued more transactions than the hard limit, drop oldest ones if queued > maxQueuedInTotal { @@ -596,6 +628,7 @@ func (pool *TxPool) promoteExecutables() { pool.removeTx(tx.Hash()) } drop -= size + queuedRLCounter.Inc(int64(size)) continue } // Otherwise drop only last few transactions @@ -603,6 +636,7 @@ func (pool *TxPool) promoteExecutables() { for i := len(txs) - 1; i >= 0 && drop > 0; i-- { pool.removeTx(txs[i].Hash()) drop-- + queuedRLCounter.Inc(1) } } } @@ -636,6 +670,7 @@ func (pool *TxPool) demoteUnexecutables() { glog.Infof("Removed unpayable pending transaction: %v", tx) } delete(pool.all, tx.Hash()) + pendingNofundsCounter.Inc(1) } for _, tx := range invalids { if glog.V(logger.Core) { diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/block.go b/vendor/github.com/ethereum/go-ethereum/core/types/block.go index 4accb0ee3..68504ffcc 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/block.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block.go @@ -79,7 +79,7 @@ func (n *BlockNonce) UnmarshalJSON(input []byte) error { return nil } -// Header represents Ethereum block headers. +// Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash // Hash to the previous block UncleHash common.Hash // Uncles of this block @@ -116,15 +116,6 @@ type jsonHeader struct { Nonce *BlockNonce `json:"nonce"` } -func (h *Header) GetNumber() *big.Int { return new(big.Int).Set(h.Number) } -func (h *Header) GetGasLimit() *big.Int { return new(big.Int).Set(h.GasLimit) } -func (h *Header) GetGasUsed() *big.Int { return new(big.Int).Set(h.GasUsed) } -func (h *Header) GetDifficulty() *big.Int { return new(big.Int).Set(h.Difficulty) } -func (h *Header) GetTime() *big.Int { return new(big.Int).Set(h.Time) } -func (h *Header) GetNumberU64() uint64 { return h.Number.Uint64() } -func (h *Header) GetNonce() uint64 { return binary.BigEndian.Uint64(h.Nonce[:]) } -func (h *Header) GetExtra() []byte { return common.CopyBytes(h.Extra) } - // Hash returns the block hash of the header, which is simply the keccak256 hash of its // RLP encoding. func (h *Header) Hash() common.Hash { @@ -223,7 +214,7 @@ type Body struct { Uncles []*Header } -// Block represents a block in the Ethereum blockchain. +// Block represents an entire block in the Ethereum blockchain. type Block struct { header *Header uncles []*Header diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go index f0512ae7e..323bfaee6 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -36,8 +37,18 @@ var ErrInvalidSig = errors.New("invalid transaction v, r, s values") var ( errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields") errMissingTxFields = errors.New("missing required JSON transaction fields") + errNoSigner = errors.New("missing signing methods") ) +// deriveSigner makes a *best* guess about which signer to use. +func deriveSigner(V *big.Int) Signer { + if V.BitLen() > 0 && isProtectedV(V) { + return EIP155Signer{chainId: deriveChainId(V)} + } else { + return HomesteadSigner{} + } +} + type Transaction struct { data txdata // caches @@ -52,7 +63,7 @@ type txdata struct { Recipient *common.Address `rlp:"nil"` // nil means contract creation Amount *big.Int Payload []byte - V byte // signature + V *big.Int // signature R, S *big.Int // signature } @@ -64,40 +75,31 @@ type jsonTransaction struct { Recipient *common.Address `json:"to"` Amount *hexBig `json:"value"` Payload *hexBytes `json:"input"` - V *hexUint64 `json:"v"` + V *hexBig `json:"v"` R *hexBig `json:"r"` S *hexBig `json:"s"` } -// NewContractCreation creates a new transaction with no recipient. -func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { - if len(data) > 0 { - data = common.CopyBytes(data) - } - return &Transaction{data: txdata{ - AccountNonce: nonce, - Recipient: nil, - Amount: new(big.Int).Set(amount), - GasLimit: new(big.Int).Set(gasLimit), - Price: new(big.Int).Set(gasPrice), - Payload: data, - R: new(big.Int), - S: new(big.Int), - }} +func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) } -// NewTransaction creates a new transaction with the given fields. -func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { +func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +} + +func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { if len(data) > 0 { data = common.CopyBytes(data) } d := txdata{ AccountNonce: nonce, - Recipient: &to, + Recipient: to, Payload: data, Amount: new(big.Int), GasLimit: new(big.Int), Price: new(big.Int), + V: new(big.Int), R: new(big.Int), S: new(big.Int), } @@ -110,9 +112,42 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice if gasPrice != nil { d.Price.Set(gasPrice) } + return &Transaction{data: d} } +func pickSigner(rules params.Rules) Signer { + var signer Signer + switch { + case rules.IsEIP155: + signer = NewEIP155Signer(rules.ChainId) + case rules.IsHomestead: + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// ChainId returns which chain id this transaction was signed for (if at all) +func (tx *Transaction) ChainId() *big.Int { + return deriveChainId(tx.data.V) +} + +// Protected returns whether the transaction is pretected from replay protection +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.data.V) +} + +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 are considered unprotected + return true +} + // DecodeRLP implements rlp.Encoder func (tx *Transaction) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &tx.data) @@ -125,12 +160,13 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { if err == nil { tx.size.Store(common.StorageSize(rlp.ListSize(size))) } + return err } // MarshalJSON encodes transactions into the web3 RPC response block format. func (tx *Transaction) MarshalJSON() ([]byte, error) { - hash, v := tx.Hash(), uint64(tx.data.V) + hash := tx.Hash() return json.Marshal(&jsonTransaction{ Hash: &hash, @@ -140,7 +176,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { Recipient: tx.data.Recipient, Amount: (*hexBig)(tx.data.Amount), Payload: (*hexBytes)(&tx.data.Payload), - V: (*hexUint64)(&v), + V: (*hexBig)(tx.data.V), R: (*hexBig)(tx.data.R), S: (*hexBig)(tx.data.S), }) @@ -159,9 +195,17 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { if dec.V == nil || dec.R == nil || dec.S == nil { return errMissingTxSignatureFields } - if !crypto.ValidateSignatureValues(byte(*dec.V), (*big.Int)(dec.R), (*big.Int)(dec.S), false) { + + var V byte + if isProtectedV((*big.Int)(dec.V)) { + V = normaliseV(NewEIP155Signer(deriveChainId((*big.Int)(dec.V))), (*big.Int)(dec.V)) + } else { + V = byte(((*big.Int)(dec.V)).Uint64()) + } + if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) { return ErrInvalidSig } + if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil { return errMissingTxFields } @@ -175,7 +219,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { GasLimit: (*big.Int)(dec.GasLimit), Price: (*big.Int)(dec.Price), Payload: *dec.Payload, - V: byte(*dec.V), + V: (*big.Int)(dec.V), R: (*big.Int)(dec.R), S: (*big.Int)(dec.S), } @@ -211,15 +255,8 @@ func (tx *Transaction) Hash() common.Hash { // SigHash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. -func (tx *Transaction) SigHash() common.Hash { - return rlpHash([]interface{}{ - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Recipient, - tx.data.Amount, - tx.data.Payload, - }) +func (tx *Transaction) SigHash(signer Signer) common.Hash { + return signer.Hash(tx) } func (tx *Transaction) Size() common.StorageSize { @@ -232,6 +269,7 @@ func (tx *Transaction) Size() common.StorageSize { return common.StorageSize(c) } +/* // From returns the address derived from the signature (V, R, S) using secp256k1 // elliptic curve and an error if it failed deriving or upon an incorrect // signature. @@ -247,32 +285,15 @@ func (tx *Transaction) Size() common.StorageSize { // both txs before and after the first homestead block. Signatures // valid in homestead are a subset of valid ones in Frontier) func (tx *Transaction) From() (common.Address, error) { - return doFrom(tx, true) -} + if tx.signer == nil { + return common.Address{}, errNoSigner + } -// FromFrontier returns the address derived from the signature (V, R, S) using -// secp256k1 elliptic curve and an error if it failed deriving or upon an -// incorrect signature. -// -// FromFrantier uses the frontier consensus rules to determine whether the -// signature is valid. -// -// FromFrontier caches the address, allowing it to be used regardless of -// Frontier / Homestead. however, the first time called it runs -// signature validations, so we need two versions. This makes it -// easier to ensure backwards compatibility of things like package rpc -// where eth_getblockbynumber uses tx.From() and needs to work for -// both txs before and after the first homestead block. Signatures -// valid in homestead are a subset of valid ones in Frontier) -func (tx *Transaction) FromFrontier() (common.Address, error) { - return doFrom(tx, false) -} - -func doFrom(tx *Transaction, homestead bool) (common.Address, error) { if from := tx.from.Load(); from != nil { return from.(common.Address), nil } - pubkey, err := tx.publicKey(homestead) + + pubkey, err := tx.signer.PublicKey(tx) if err != nil { return common.Address{}, err } @@ -282,6 +303,52 @@ func doFrom(tx *Transaction, homestead bool) (common.Address, error) { return addr, nil } +// SignatureValues returns the ECDSA signature values contained in the transaction. +func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err error) { + if tx.signer == nil { + return 0, nil, nil,errNoSigner + } + + return normaliseV(tx.signer, tx.data.V), new(big.Int).Set(tx.data.R),new(big.Int).Set(tx.data.S), nil +} + +*/ + +// AsMessage returns the transaction as a core.Message. +// +// AsMessage requires a signer to derive the sender. +// +// XXX Rename message to something less arbitrary? +func (tx *Transaction) AsMessage(s Signer) (Message, error) { + msg := Message{ + nonce: tx.data.AccountNonce, + price: new(big.Int).Set(tx.data.Price), + gasLimit: new(big.Int).Set(tx.data.GasLimit), + to: tx.data.Recipient, + amount: tx.data.Amount, + data: tx.data.Payload, + checkNonce: true, + } + + var err error + msg.from, err = Sender(s, tx) + return msg, err +} + +// SignECDSA signs the transaction using the given signer and private key +// +// XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should +// we keep this? +func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { + return signer.SignECDSA(tx, prv) +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { + return signer.WithSignature(tx, sig) +} + // Cost returns amount + gasprice * gaslimit. func (tx *Transaction) Cost() *big.Int { total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit) @@ -289,59 +356,18 @@ func (tx *Transaction) Cost() *big.Int { return total } -// SignatureValues returns the ECDSA signature values contained in the transaction. -func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) { - return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) -} - -func (tx *Transaction) publicKey(homestead bool) ([]byte, error) { - if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) { - return nil, ErrInvalidSig - } - - // encode the signature in uncompressed format - r, s := tx.data.R.Bytes(), tx.data.S.Bytes() - sig := make([]byte, 65) - copy(sig[32-len(r):32], r) - copy(sig[64-len(s):64], s) - sig[64] = tx.data.V - 27 - - // recover the public key from the signature - hash := tx.SigHash() - pub, err := crypto.Ecrecover(hash[:], sig) - if err != nil { - return nil, err - } - if len(pub) == 0 || pub[0] != 4 { - return nil, errors.New("invalid public key") - } - return pub, nil -} - -func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { - if len(sig) != 65 { - panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) - } - cpy := &Transaction{data: tx.data} - cpy.data.R = new(big.Int).SetBytes(sig[:32]) - cpy.data.S = new(big.Int).SetBytes(sig[32:64]) - cpy.data.V = sig[64] + 27 - return cpy, nil -} - -func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) { - h := tx.SigHash() - sig, err := crypto.Sign(h[:], prv) - if err != nil { - return nil, err - } - return tx.WithSignature(sig) +func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { + return tx.data.V, tx.data.R, tx.data.S } func (tx *Transaction) String() string { + // make a best guess about the signer and use that to derive + // the sender. + signer := deriveSigner(tx.data.V) + var from, to string - if f, err := tx.From(); err != nil { - from = "[invalid sender]" + if f, err := Sender(signer, tx); err != nil { // derive but don't cache + from = "[invalid sender: invalid sig]" } else { from = fmt.Sprintf("%x", f[:]) } @@ -483,8 +509,9 @@ func (t *TransactionsByPriceAndNonce) Peek() *Transaction { // Shift replaces the current best head with the next one from the same account. func (t *TransactionsByPriceAndNonce) Shift() { - acc, _ := t.heads[0].From() // we only sort valid txs so this cannot fail - + signer := deriveSigner(t.heads[0].data.V) + // derive signer but don't cache. + acc, _ := Sender(signer, t.heads[0]) // we only sort valid txs so this cannot fail if txs, ok := t.txs[acc]; ok && len(txs) > 0 { t.heads[0], t.txs[acc] = txs[0], txs[1:] heap.Fix(&t.heads, 0) @@ -499,3 +526,37 @@ func (t *TransactionsByPriceAndNonce) Shift() { func (t *TransactionsByPriceAndNonce) Pop() { heap.Pop(&t.heads) } + +// Message is a fully derived transaction and implements core.Message +// +// NOTE: In a future PR this will be removed. +type Message struct { + to *common.Address + from common.Address + nonce uint64 + amount, price, gasLimit *big.Int + data []byte + checkNonce bool +} + +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte, checkNonce bool) Message { + return Message{ + from: from, + to: to, + nonce: nonce, + amount: amount, + price: price, + gasLimit: gasLimit, + data: data, + checkNonce: checkNonce, + } +} + +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.price } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() *big.Int { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) CheckNonce() bool { return m.checkNonce } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go new file mode 100644 index 000000000..7d571643f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go @@ -0,0 +1,362 @@ +// 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 . + +package types + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +var ErrInvalidChainId = errors.New("invalid chaid id for signer") + +// sigCache is used to cache the derived sender and contains +// the signer used to derive it. +type sigCache struct { + signer Signer + from common.Address +} + +// MakeSigner returns a Signer based on the given chain config and block number. +func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { + var signer Signer + switch { + case config.IsEIP155(blockNumber): + signer = NewEIP155Signer(config.ChainId) + case config.IsHomestead(blockNumber): + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// SignECDSA signs the transaction using the given signer and private key +func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// Sender derives the sender from the tx using the signer derivation +// functions. + +// Sender returns the address derived from the signature (V, R, S) using secp256k1 +// elliptic curve and an error if it failed deriving or upon an incorrect +// signature. +// +// Sender may cache the address, allowing it to be used regardless of +// signing method. The cache is invalidated if the cached signer does +// not match the signer used in the current call. +func Sender(signer Signer, tx *Transaction) (common.Address, error) { + if sc := tx.from.Load(); sc != nil { + sigCache := sc.(sigCache) + // If the signer used to derive from in a previous + // call is not the same as used current, invalidate + // the cache. + if sigCache.signer.Equal(signer) { + return sigCache.from, nil + } + } + + pubkey, err := signer.PublicKey(tx) + if err != nil { + return common.Address{}, err + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) + tx.from.Store(sigCache{signer: signer, from: addr}) + return addr, nil +} + +// SignatureValues returns the ECDSA signature values contained in the transaction. +func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) { + return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) +} + +type Signer interface { + // Hash returns the rlp encoded hash for signatures + Hash(tx *Transaction) common.Hash + // PubilcKey returns the public key derived from the signature + PublicKey(tx *Transaction) ([]byte, error) + // SignECDSA signs the transaction with the given and returns a copy of the tx + SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) + // WithSignature returns a copy of the transaction with the given signature + WithSignature(tx *Transaction, sig []byte) (*Transaction, error) + // Checks for equality on the signers + Equal(Signer) bool +} + +// EIP155Transaction implements TransactionInterface using the +// EIP155 rules +type EIP155Signer struct { + HomesteadSigner + + chainId, chainIdMul *big.Int +} + +func NewEIP155Signer(chainId *big.Int) EIP155Signer { + return EIP155Signer{ + chainId: chainId, + chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), + } +} + +func (s EIP155Signer) Equal(s2 Signer) bool { + eip155, ok := s2.(EIP155Signer) + return ok && eip155.chainId.Cmp(s.chainId) == 0 +} + +func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + return SignECDSA(s, tx, prv) +} + +func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { + // if the transaction is not protected fall back to homestead signer + if !tx.Protected() { + return (HomesteadSigner{}).PublicKey(tx) + } + + if tx.ChainId().Cmp(s.chainId) != 0 { + return nil, ErrInvalidChainId + } + + V := normaliseV(s, tx.data.V) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + + // encode the signature in uncompressed format + R, S := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(R):32], R) + copy(sig[64-len(S):64], S) + sig[64] = V - 27 + + // recover the public key from the signature + hash := s.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + if s.chainId.BitLen() > 0 { + cpy.data.V = big.NewInt(int64(sig[64] - 27 + 35)) + cpy.data.V.Add(cpy.data.V, s.chainIdMul) + } + return cpy, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s EIP155Signer) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + s.chainId, uint(0), uint(0), + }) +} + +func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// HomesteadTransaction implements TransactionInterface using the +// homestead rules. +type HomesteadSigner struct{ FrontierSigner } + +func (s HomesteadSigner) Equal(s2 Signer) bool { + _, ok := s2.(HomesteadSigner) + return ok +} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + return cpy, nil +} + +func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := hs.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return hs.WithSignature(tx, sig) +} + +func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := hs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +type FrontierSigner struct{} + +func (s FrontierSigner) Equal(s2 Signer) bool { + _, ok := s2.(FrontierSigner) + return ok +} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + return cpy, nil +} + +func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := fs.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return fs.WithSignature(tx, sig) +} + +// Hash returns the hash to be sned by the sender. +// It does not uniquely identify the transaction. +func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + }) +} + +func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := fs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// normaliseV returns the Ethereum version of the V parameter +func normaliseV(s Signer, v *big.Int) byte { + if s, ok := s.(EIP155Signer); ok { + stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28) + if s.chainId.BitLen() > 0 && !stdV { + nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27) + return nv + } + } + return byte(v.Uint64()) +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go index f8996e648..e97c1e58c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go @@ -23,20 +23,11 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// RuleSet is an interface that defines the current rule set during the -// execution of the EVM instructions (e.g. whether it's homestead) -type RuleSet interface { - IsHomestead(*big.Int) bool - // GasTable returns the gas prices for this phase, which is based on - // block number passed in. - GasTable(*big.Int) params.GasTable -} - // Environment is an EVM requirement and helper which allows access to outside // information such as states. type Environment interface { // The current ruleset - RuleSet() RuleSet + ChainConfig() *params.ChainConfig // The state database Db() Database // Creates a restorable snapshot @@ -115,6 +106,9 @@ type Database interface { // Exist reports whether the given account exists in state. // Notably this should also return true for suicided accounts. Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool } // Account represents a contract or basic ethereum account. diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go index 116fbe456..1766bf9fb 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go @@ -26,3 +26,4 @@ import ( var OutOfGasError = errors.New("Out of gas") var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas") var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth) +var TraceLimitReachedError = errors.New("The number of logs reached the specified limit") diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go index 3352877c1..4f98953b5 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -192,8 +193,8 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, } func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - x, y := stack.pop(), stack.pop() - stack.push(U256(x.Exp(x, y, Pow256))) + base, exponent := stack.pop(), stack.pop() + stack.push(math.Exp(base, exponent)) } func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { @@ -514,7 +515,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract input = memory.Get(offset.Int64(), size.Int64()) gas = new(big.Int).Set(contract.Gas) ) - if env.RuleSet().GasTable(env.BlockNumber()).CreateBySuicide != nil { + if env.ChainConfig().IsEIP150(env.BlockNumber()) { gas.Div(gas, n64) gas = gas.Sub(contract.Gas, gas) } @@ -525,7 +526,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { stack.push(new(big.Int)) } else if suberr != nil && suberr != CodeStoreOutOfGasError { stack.push(new(big.Int)) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go index 55d2e0477..b75558d39 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go @@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env }() } - homestead := env.RuleSet().IsHomestead(env.BlockNumber()) + homestead := env.ChainConfig().IsHomestead(env.BlockNumber()) for pc < uint64(len(program.instructions)) { instrCount++ diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go index 255c4f189..4e997f636 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go @@ -16,7 +16,11 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type jumpPtr struct { fn instrFn @@ -25,7 +29,7 @@ type jumpPtr struct { type vmJumpTable [256]jumpPtr -func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable { +func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable { var jumpTable vmJumpTable // when initialising a new VM execution we must first check the homestead diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go index ae62b6b57..9e13d703b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go @@ -42,6 +42,7 @@ type LogConfig struct { DisableStack bool // disable stack capture DisableStorage bool // disable storage capture FullStorage bool // show full storage (slow) + Limit int // maximum length of output, but zero means unlimited } // StructLog is emitted to the Environment each cycle and lists information about the current internal state @@ -64,7 +65,7 @@ type StructLog struct { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { - CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) + CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error } // StructLogger is an EVM state logger and implements Tracer. @@ -93,7 +94,12 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { // captureState logs a new structured log message and pushes it out to the environment // // captureState also tracks SSTORE ops to track dirty values. -func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) { +func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + // check if already accumulated the specified number of logs + if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { + return TraceLimitReachedError + } + // initialise new changed values storage container for this contract // if not present. if l.changedValues[contract.Address()] == nil { @@ -152,6 +158,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err} l.logs = append(l.logs, log) + return nil } // StructLogs returns a list of captured log entries diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go index 7d861f1de..9d2b037a5 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go @@ -248,7 +248,7 @@ var opCodeToString = map[OpCode]string{ CALLDATACOPY: "CALLDATACOPY", CODESIZE: "CODESIZE", CODECOPY: "CODECOPY", - GASPRICE: "TXGASPRICE", + GASPRICE: "GASPRICE", // 0x40 range - block operations BLOCKHASH: "BLOCKHASH", diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go index 59fbaa792..f1a2b60d3 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go @@ -23,13 +23,14 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) // Env is a basic runtime environment required for running the EVM. type Env struct { - ruleSet vm.RuleSet - depth int - state *state.StateDB + chainConfig *params.ChainConfig + depth int + state *state.StateDB origin common.Address coinbase common.Address @@ -47,14 +48,14 @@ type Env struct { // NewEnv returns a new vm.Environment func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { env := &Env{ - ruleSet: cfg.RuleSet, - state: state, - origin: cfg.Origin, - coinbase: cfg.Coinbase, - number: cfg.BlockNumber, - time: cfg.Time, - difficulty: cfg.Difficulty, - gasLimit: cfg.GasLimit, + chainConfig: cfg.ChainConfig, + state: state, + origin: cfg.Origin, + coinbase: cfg.Coinbase, + number: cfg.BlockNumber, + time: cfg.Time, + difficulty: cfg.Difficulty, + gasLimit: cfg.GasLimit, } env.evm = vm.New(env, vm.Config{ Debug: cfg.Debug, @@ -65,16 +66,16 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { return env } -func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) Vm() vm.Vm { return self.evm } -func (self *Env) Origin() common.Address { return self.origin } -func (self *Env) BlockNumber() *big.Int { return self.number } -func (self *Env) Coinbase() common.Address { return self.coinbase } -func (self *Env) Time() *big.Int { return self.time } -func (self *Env) Difficulty() *big.Int { return self.difficulty } -func (self *Env) Db() vm.Database { return self.state } -func (self *Env) GasLimit() *big.Int { return self.gasLimit } -func (self *Env) VmType() vm.Type { return vm.StdVmTy } +func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig } +func (self *Env) Vm() vm.Vm { return self.evm } +func (self *Env) Origin() common.Address { return self.origin } +func (self *Env) BlockNumber() *big.Int { return self.number } +func (self *Env) Coinbase() common.Address { return self.coinbase } +func (self *Env) Time() *big.Int { return self.time } +func (self *Env) Difficulty() *big.Int { return self.difficulty } +func (self *Env) Db() vm.Database { return self.state } +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 self.getHashFn(n) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go index 343aee514..d51b435f8 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" @@ -39,7 +38,7 @@ func (ruleSet) GasTable(*big.Int) params.GasTable { // Config is a basic type specifying certain configuration flags for running // the EVM. type Config struct { - RuleSet vm.RuleSet + ChainConfig *params.ChainConfig Difficulty *big.Int Origin common.Address Coinbase common.Address @@ -57,8 +56,16 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { - if cfg.RuleSet == nil { - cfg.RuleSet = ruleSet{} + if cfg.ChainConfig == nil { + cfg.ChainConfig = ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + } } if cfg.Difficulty == nil { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go index fcffcf317..56aca6912 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go @@ -51,9 +51,9 @@ type EVM struct { func New(env Environment, cfg Config) *EVM { return &EVM{ env: env, - jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), + jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()), cfg: cfg, - gasTable: env.RuleSet().GasTable(env.BlockNumber()), + gasTable: env.ChainConfig().GasTable(env.BlockNumber()), } } @@ -78,7 +78,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { codehash = crypto.Keccak256Hash(contract.Code) } var program *Program - if evm.cfg.EnableJit { + if false { + // JIT disabled due to JIT not being Homestead gas reprice ready. + // If the JIT is enabled check the status of the JIT program, // if it doesn't exist compile a new program in a separate // goroutine or wait for compilation to finish if the JIT is @@ -170,6 +172,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // Get the memory location of pc op = contract.GetOp(pc) + //fmt.Printf("OP %d %v\n", op, op) // calculate the new memory size and gas price for the current executing opcode newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) if err != nil { @@ -186,7 +189,10 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { mem.Resize(newMemSize.Uint64()) // Add a log message if evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) + err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) + if err != nil { + return nil, err + } } if opPtr := evm.jumpTable[op]; opPtr.valid { @@ -249,10 +255,20 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co // stack Check, memory resize & gas phase switch op { case SUICIDE: - // if suicide is not nil: homestead gas fork + // EIP150 homestead gas reprice fork: if gasTable.CreateBySuicide != nil { gas.Set(gasTable.Suicide) - if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) { + var ( + address = common.BigToAddress(stack.data[len(stack.data)-1]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + + if eip158 { + // if empty and transfers value + if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 { + gas.Add(gas, gasTable.CreateBySuicide) + } + } else if !env.Db().Exist(address) { gas.Add(gas, gasTable.CreateBySuicide) } } @@ -297,7 +313,8 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co quadMemGas(mem, newMemSize, gas) case EXP: - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) + expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8) + gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte)) case SSTORE: err := stack.require(2) if err != nil { @@ -373,12 +390,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co case CALL, CALLCODE: gas.Set(gasTable.Calls) + transfersValue := stack.data[len(stack.data)-3].BitLen() > 0 if op == CALL { - if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { + var ( + address = common.BigToAddress(stack.data[len(stack.data)-2]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + if eip158 { + if env.Db().Empty(address) && transfersValue { + gas.Add(gas, params.CallNewAccountGas) + } + } else if !env.Db().Exist(address) { gas.Add(gas, params.CallNewAccountGas) } } - if len(stack.data[stack.len()-3].Bytes()) > 0 { + if transfersValue { gas.Add(gas, params.CallValueTransferGas) } x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go index d62eebbd9..43637bd13 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) // GetHashFn returns a function for which the VM env can query block hashes through @@ -41,18 +42,18 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { } type VMEnv struct { - chainConfig *ChainConfig // Chain configuration - state *state.StateDB // State to use for executing - evm *vm.EVM // The Ethereum Virtual Machine - depth int // Current execution depth - msg Message // Message appliod + chainConfig *params.ChainConfig // Chain configuration + state *state.StateDB // State to use for executing + evm *vm.EVM // The Ethereum Virtual Machine + depth int // Current execution depth + msg Message // Message appliod header *types.Header // Header information chain *BlockChain // Blockchain handle getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes } -func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { +func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { env := &VMEnv{ chainConfig: chainConfig, chain: chain, @@ -66,18 +67,18 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m return env } -func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -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() *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() } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } +func (self *VMEnv) Vm() vm.Vm { return self.evm } +func (self *VMEnv) Origin() common.Address { return self.msg.From() } +func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } +func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } +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() } +func (self *VMEnv) Db() vm.Database { return self.state } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { return self.getHashFn(n) } diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go index 85f097095..e611bd8f4 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go @@ -78,6 +78,12 @@ func Ripemd160(data []byte) []byte { return ripemd.Sum(nil) } +// Ecrecover returns the public key for the private key that was used to +// calculate the signature. +// +// Note: secp256k1 expects the recover id to be either 0, 1. Ethereum +// signatures have a recover id with an offset of 27. Callers must take +// this into account and if "recovering" from an Ethereum signature adjust. func Ecrecover(hash, sig []byte) ([]byte, error) { return secp256k1.RecoverPubkey(hash, sig) } @@ -192,17 +198,40 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil } -func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { - if len(hash) != 32 { - return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) +// Sign calculates an ECDSA signature. +// This function is susceptible to choosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be choosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// Note: the calculated signature is not Ethereum compliant. The yellow paper +// dictates Ethereum singature to have a V value with and offset of 27 v in [27,28]. +// Use SignEthereum to get an Ethereum compliant signature. +func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + if len(data) != 32 { + return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(data)) } seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8) defer zeroBytes(seckey) - sig, err = secp256k1.Sign(hash, seckey) + sig, err = secp256k1.Sign(data, seckey) return } +// SignEthereum calculates an Ethereum ECDSA signature. +// This function is susceptible to choosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be freely choosen by an adversery. +// Common solution is to hash the message before calculating the signature. +func SignEthereum(data []byte, prv *ecdsa.PrivateKey) ([]byte, error) { + sig, err := Sign(data, prv) + if err != nil { + return nil, err + } + sig[64] += 27 // as described in the yellow paper + return sig, err +} + func Encrypt(pub *ecdsa.PublicKey, message []byte) ([]byte, error) { return ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pub), message, nil, nil) } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go index c2fdbe99c..b3185c392 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" @@ -102,7 +103,7 @@ func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty func (s *PublicMinerAPI) GetWork() (work [3]string, err error) { if !s.e.IsMining() { - if err := s.e.StartMining(0, ""); err != nil { + if err := s.e.StartMining(0); err != nil { return work, err } } @@ -141,7 +142,7 @@ func (s *PrivateMinerAPI) Start(threads *rpc.HexNumber) (bool, error) { threads = rpc.NewHexNumber(runtime.NumCPU()) } - err := s.e.StartMining(threads.Int(), "") + err := s.e.StartMining(threads.Int()) if err == nil { return true, nil } @@ -303,13 +304,13 @@ func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { // PrivateDebugAPI is the collection of Etheruem full node APIs exposed over // the private debugging endpoint. type PrivateDebugAPI struct { - config *core.ChainConfig + config *params.ChainConfig eth *Ethereum } // NewPrivateDebugAPI creates a new API definition for the full node-related // private debug methods of the Ethereum service. -func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI { +func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI { return &PrivateDebugAPI{config: config, eth: eth} } @@ -505,21 +506,15 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. if err != nil { return nil, err } + + signer := types.MakeSigner(api.config, block.Number()) // Mutate the state and trace the selected transaction for idx, tx := range block.Transactions() { // Assemble the transaction call message - from, err := tx.FromFrontier() + msg, err := tx.AsMessage(signer) if err != nil { return nil, fmt.Errorf("sender retrieval failed: %v", err) } - msg := callmsg{ - addr: from, - to: tx.To(), - gas: tx.Gas(), - gasPrice: tx.GasPrice(), - value: tx.Value(), - data: tx.Data(), - } // Mutate the state if we haven't reached the tracing transaction yet if uint64(idx) < txIndex { vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go index 639f186c1..0925132ef 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -1,18 +1,18 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. +// This file is part of the go-ethereum library. // -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by +// The 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. // -// go-ethereum is distributed in the hope that it will be useful, +// 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 General Public License for more details. +// GNU Lesser General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . package eth @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -40,6 +41,14 @@ type EthApiBackend struct { gpo *gasprice.GasPriceOracle } +func (b *EthApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *EthApiBackend) CurrentBlock() *types.Block { + return b.eth.blockchain.CurrentBlock() +} + func (b *EthApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } @@ -99,11 +108,10 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { statedb := state.(EthApiState).state - addr, _ := msg.From() - from := statedb.GetOrNewStateObject(addr) + from := statedb.GetOrNewStateObject(msg.From()) from.SetBalance(common.MaxBig) vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil + return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go index 61d64c80e..2351a62c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -47,6 +46,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -64,13 +64,12 @@ var ( ) type Config struct { - ChainConfig *core.ChainConfig // chain configuration + ChainConfig *params.ChainConfig // chain configuration NetworkId int // Network ID to use for selecting peers to connect to Genesis string // Genesis JSON to seed the chain database with FastSync bool // Enables the state download based fast synchronisation algorithm LightMode bool // Running in light client mode - NoDefSrv bool // No default LES server LightServ int // Maximum percentage of time allowed for serving LES requests LightPeers int // Maximum number of LES client peers MaxPeers int // Maximum number of global peers @@ -106,14 +105,14 @@ type Config struct { } type LesServer interface { - Start() + Start(srvr *p2p.Server) Stop() Protocols() []p2p.Protocol } // Ethereum implements the Ethereum full node service. type Ethereum struct { - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Channel for shutting down the ethereum stopDbUpgrade func() // stop chain db sequential key upgrade @@ -218,10 +217,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } + + glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig) eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { @@ -339,7 +336,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Service: filters.NewPublicFilterAPI(s.ApiBackend, false), Public: true, }, { Namespace: "admin", @@ -389,6 +386,17 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) { self.miner.SetEtherbase(etherbase) } +func (s *Ethereum) StartMining(threads int) error { + eb, err := s.Etherbase() + if err != nil { + err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) + glog.V(logger.Error).Infoln(err) + return err + } + go s.miner.Start(eb, threads) + return nil +} + func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } @@ -423,7 +431,7 @@ func (s *Ethereum) Start(srvr *p2p.Server) error { } s.protocolManager.Start() if s.lesServer != nil { - s.lesServer.Start() + s.lesServer.Start(srvr) } return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go index 3a6c3d85c..e0f05f540 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bind.go b/vendor/github.com/ethereum/go-ethereum/eth/bind.go index 0931c5f3b..747965d37 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/bind.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go index 172bb0954..5fd73a586 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go @@ -1,4 +1,4 @@ -// Copyright 2014 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go index 36c46f44d..b1f4b8169 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go @@ -64,12 +64,12 @@ var ( maxHeadersProcess = 2048 // Number of header download results to import at once into the chain maxResultsProcess = 2048 // Number of content download results to import at once into the chain - fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync - fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected - fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it - fsPivotInterval = 512 // Number of headers out of which to randomize the pivot point - fsMinFullBlocks = 1024 // Number of blocks to retrieve fully even in fast sync - fsCriticalTrials = 10 // Number of times to retry in the cricical section before bailing + fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync + fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected + fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it + fsPivotInterval = 256 // Number of headers out of which to randomize the pivot point + fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync + fsCriticalTrials = uint32(32) // Number of times to retry in the cricical section before bailing ) var ( @@ -105,7 +105,7 @@ type Downloader struct { peers *peerSet // Set of active peers from which download can proceed fsPivotLock *types.Header // Pivot header on critical section entry (cannot change between retries) - fsPivotFails int // Number of fast sync failures in the critical section + fsPivotFails uint32 // Number of subsequent fast sync failures in the critical section rttEstimate uint64 // Round trip time to target for download requests rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops) @@ -361,7 +361,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode // Set the requested sync mode, unless it's forbidden d.mode = mode - if d.mode == FastSync && d.fsPivotFails >= fsCriticalTrials { + if d.mode == FastSync && atomic.LoadUint32(&d.fsPivotFails) >= fsCriticalTrials { d.mode = FullSync } // Retrieve the origin peer and initiate the downloading process @@ -480,6 +480,11 @@ func (d *Downloader) spawnSync(origin uint64, fetchers ...func() error) error { d.queue.Close() d.cancel() wg.Wait() + + // If sync failed in the critical section, bump the fail counter + if err != nil && d.mode == FastSync && d.fsPivotLock != nil { + atomic.AddUint32(&d.fsPivotFails, 1) + } return err } @@ -926,10 +931,10 @@ func (d *Downloader) fetchNodeData() error { var ( deliver = func(packet dataPack) (int, error) { start := time.Now() - return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(err error, delivered int) { + return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(delivered int, progressed bool, err error) { // If the peer returned old-requested data, forgive if err == trie.ErrNotRequested { - glog.V(logger.Info).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) + glog.V(logger.Debug).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) return } if err != nil { @@ -948,11 +953,17 @@ func (d *Downloader) fetchNodeData() error { } d.syncStatsLock.Lock() d.syncStatsStateDone += uint64(delivered) + syncStatsStateDone := d.syncStatsStateDone // Thread safe copy for the log below d.syncStatsLock.Unlock() + // If real database progress was made, reset any fast-sync pivot failure + if progressed && atomic.LoadUint32(&d.fsPivotFails) > 1 { + glog.V(logger.Debug).Infof("fast-sync progressed, resetting fail counter from %d", atomic.LoadUint32(&d.fsPivotFails)) + atomic.StoreUint32(&d.fsPivotFails, 1) // Don't ever reset to 0, as that will unlock the pivot block + } // Log a message to the user and return if delivered > 0 { - glog.V(logger.Info).Infof("imported %d state entries in %9v: processed %d, pending at least %d", delivered, common.PrettyDuration(time.Since(start)), d.syncStatsStateDone, pending) + glog.V(logger.Info).Infof("imported %3d state entries in %9v: processed %d, pending at least %d", delivered, common.PrettyDuration(time.Since(start)), syncStatsStateDone, pending) } }) } @@ -1189,7 +1200,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { // If we're already past the pivot point, this could be an attack, thread carefully if rollback[len(rollback)-1].Number.Uint64() > pivot { // If we didn't ever fail, lock in te pivot header (must! not! change!) - if d.fsPivotFails == 0 { + if atomic.LoadUint32(&d.fsPivotFails) == 0 { for _, header := range rollback { if header.Number.Uint64() == pivot { glog.V(logger.Warn).Infof("Fast-sync critical section failure, locked pivot to header #%d [%x…]", pivot, header.Hash().Bytes()[:4]) @@ -1197,7 +1208,6 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { } } } - d.fsPivotFails++ } } }() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go index fd239f7e4..b7ad92099 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go @@ -362,20 +362,20 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { // Make sure chain order is honoured and preserved throughout hash := header.Hash() if header.Number == nil || header.Number.Uint64() != from { - glog.V(logger.Warn).Infof("Header #%v [%x] broke chain ordering, expected %d", header.Number, hash[:4], from) + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ordering, expected %d", header.Number, hash[:4], from) break } if q.headerHead != (common.Hash{}) && q.headerHead != header.ParentHash { - glog.V(logger.Warn).Infof("Header #%v [%x] broke chain ancestry", header.Number, hash[:4]) + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ancestry", header.Number, hash[:4]) break } // Make sure no duplicate requests are executed if _, ok := q.blockTaskPool[hash]; ok { - glog.V(logger.Warn).Infof("Header #%d [%x] already scheduled for block fetch", header.Number.Uint64(), hash[:4]) + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for block fetch", header.Number.Uint64(), hash[:4]) continue } if _, ok := q.receiptTaskPool[hash]; ok { - glog.V(logger.Warn).Infof("Header #%d [%x] already scheduled for receipt fetch", header.Number.Uint64(), hash[:4]) + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for receipt fetch", header.Number.Uint64(), hash[:4]) continue } // Queue the header for content retrieval @@ -388,7 +388,16 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) } if q.mode == FastSync && header.Number.Uint64() == q.fastSyncPivot { - // Pivoting point of the fast sync, retrieve the state tries + // Pivoting point of the fast sync, switch the state retrieval to this + glog.V(logger.Debug).Infof("Switching state downloads to %d [%x…]", header.Number.Uint64(), header.Hash().Bytes()[:4]) + + q.stateTaskIndex = 0 + q.stateTaskPool = make(map[common.Hash]int) + q.stateTaskQueue.Reset() + for _, req := range q.statePendPool { + req.Hashes = make(map[common.Hash]int) // Make sure executing requests fail, but don't disappear + } + q.stateSchedLock.Lock() q.stateScheduler = state.NewStateSync(header.Root, q.stateDatabase) q.stateSchedLock.Unlock() @@ -866,10 +875,10 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh accepted := len(headers) == MaxHeaderFetch if accepted { if headers[0].Number.Uint64() != request.From { - glog.V(logger.Detail).Infof("Peer %s: first header #%v [%x] broke chain ordering, expected %d", id, headers[0].Number, headers[0].Hash().Bytes()[:4], request.From) + glog.V(logger.Detail).Infof("Peer %s: first header #%v [%x…] broke chain ordering, expected %d", id, headers[0].Number, headers[0].Hash().Bytes()[:4], request.From) accepted = false } else if headers[len(headers)-1].Hash() != target { - glog.V(logger.Detail).Infof("Peer %s: last header #%v [%x] broke skeleton structure, expected %x", id, headers[len(headers)-1].Number, headers[len(headers)-1].Hash().Bytes()[:4], target[:4]) + glog.V(logger.Detail).Infof("Peer %s: last header #%v [%x…] broke skeleton structure, expected %x", id, headers[len(headers)-1].Number, headers[len(headers)-1].Hash().Bytes()[:4], target[:4]) accepted = false } } @@ -877,12 +886,12 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh for i, header := range headers[1:] { hash := header.Hash() if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { - glog.V(logger.Warn).Infof("Peer %s: header #%v [%x] broke chain ordering, expected %d", id, header.Number, hash[:4], want) + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ordering, expected %d", id, header.Number, hash[:4], want) accepted = false break } if headers[i].Hash() != header.ParentHash { - glog.V(logger.Warn).Infof("Peer %s: header #%v [%x] broke chain ancestry", id, header.Number, hash[:4]) + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ancestry", id, header.Number, hash[:4]) accepted = false break } @@ -1039,9 +1048,8 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ } // DeliverNodeData injects a node state data retrieval response into the queue. -// The method returns the number of node state entries originally requested, and -// the number of them actually accepted from the delivery. -func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) (int, error) { +// The method returns the number of node state accepted from the delivery. +func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, bool, error)) (int, error) { q.lock.Lock() defer q.lock.Unlock() @@ -1099,31 +1107,34 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i // deliverNodeData is the asynchronous node data processor that injects a batch // of sync results into the state scheduler. -func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(error, int)) { +func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(int, bool, error)) { // Wake up WaitResults after the state has been written because it // might be waiting for the pivot block state to get completed. defer q.active.Signal() // Process results one by one to permit task fetches in between + progressed := false for i, result := range results { q.stateSchedLock.Lock() if q.stateScheduler == nil { // Syncing aborted since this async delivery started, bail out q.stateSchedLock.Unlock() - callback(errNoFetchesPending, i) + callback(i, progressed, errNoFetchesPending) return } - if _, err := q.stateScheduler.Process([]trie.SyncResult{result}); err != nil { + if prog, _, err := q.stateScheduler.Process([]trie.SyncResult{result}); err != nil { // Processing a state result failed, bail out q.stateSchedLock.Unlock() - callback(err, i) + callback(i, progressed, err) return + } else if prog { + progressed = true } // Item processing succeeded, release the lock (temporarily) q.stateSchedLock.Unlock() } - callback(nil, len(results)) + callback(len(results), progressed, nil) } // Prepare configures the result cache to allow accepting and caching inbound diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go index 5da25fea1..834513262 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go @@ -63,13 +63,13 @@ type PublicFilterAPI struct { } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, useMipMap bool) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { api := &PublicFilterAPI{ backend: backend, - useMipMap: useMipMap, + useMipMap: !lightMode, mux: backend.EventMux(), chainDb: backend.ChainDb(), - events: NewEventSystem(backend.EventMux()), + events: NewEventSystem(backend.EventMux(), backend, lightMode), filters: make(map[rpc.ID]*filter), } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go index 6aa05513b..4004af300 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go @@ -219,9 +219,13 @@ Logs: } func (f *Filter) bloomFilter(bloom types.Bloom) bool { - if len(f.addresses) > 0 { + return bloomFilter(bloom, f.addresses, f.topics) +} + +func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { var included bool - for _, addr := range f.addresses { + for _, addr := range addresses { if types.BloomLookup(bloom, addr) { included = true break @@ -233,7 +237,7 @@ func (f *Filter) bloomFilter(bloom types.Bloom) bool { } } - for _, sub := range f.topics { + for _, sub := range topics { var included bool for _, topic := range sub { if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go index 04a55fd09..c2c072a9f 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) // Type determines the kind of filter and is used to put the filter in to @@ -95,6 +96,9 @@ type subscription struct { type EventSystem struct { mux *event.TypeMux sub event.Subscription + backend Backend + lightMode bool + lastHead *types.Header install chan *subscription // install filter for event notification uninstall chan *subscription // remove filter for event notification } @@ -105,9 +109,11 @@ type EventSystem struct { // // The returned manager has a loop that needs to be stopped with the Stop function // or by stopping the given mux. -func NewEventSystem(mux *event.TypeMux) *EventSystem { +func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { m := &EventSystem{ mux: mux, + backend: backend, + lightMode: lightMode, install: make(chan *subscription), uninstall: make(chan *subscription), } @@ -235,7 +241,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti type filterIndex map[Type]map[rpc.ID]*subscription // broadcast event to filters that match criteria. -func broadcast(filters filterIndex, ev *event.Event) { +func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { if ev == nil { return } @@ -279,9 +285,79 @@ func broadcast(filters filterIndex, ev *event.Event) { f.headers <- e.Block.Header() } } + if es.lightMode && len(filters[LogsSubscription]) > 0 { + es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { + for _, f := range filters[LogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + }) + } } } +func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { + oldh := es.lastHead + es.lastHead = newHeader + if oldh == nil { + return + } + newh := newHeader + // find common ancestor, create list of rolled back and new block hashes + var oldHeaders, newHeaders []*types.Header + for oldh.Hash() != newh.Hash() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { + oldHeaders = append(oldHeaders, oldh) + oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) + } + if oldh.Number.Uint64() < newh.Number.Uint64() { + newHeaders = append(newHeaders, newh) + newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) + if newh == nil { + // happens when CHT syncing, nothing to do + newh = oldh + } + } + } + // roll back old blocks + for _, h := range oldHeaders { + callBack(h, true) + } + // check new blocks (array is in reverse order) + for i := len(newHeaders) - 1; i >= 0; i-- { + callBack(newHeaders[i], false) + } +} + +// filter logs of a single header in light client mode +func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []Log { + //fmt.Println("lightFilterLogs", header.Number.Uint64(), remove) + if bloomFilter(header.Bloom, addresses, topics) { + //fmt.Println("bloom match") + // Get the logs of the block + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + receipts, err := es.backend.GetReceipts(ctx, header.Hash()) + if err != nil { + return nil + } + var unfiltered []Log + for _, receipt := range receipts { + rl := make([]Log, len(receipt.Logs)) + for i, l := range receipt.Logs { + rl[i] = Log{l, remove} + } + unfiltered = append(unfiltered, rl...) + } + logs := filterLogs(unfiltered, addresses, topics) + //fmt.Println("found", len(logs)) + return logs + } + return nil +} + // eventLoop (un)installs filters and processes mux events. func (es *EventSystem) eventLoop() { var ( @@ -294,7 +370,7 @@ func (es *EventSystem) eventLoop() { if !active { // system stopped return } - broadcast(index, ev) + es.broadcast(index, ev) case f := <-es.install: if _, found := index[f.typ]; !found { index[f.typ] = make(map[rpc.ID]*subscription) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go index ce075ff4e..8886d32d7 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -78,7 +78,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error return lastPrice, nil } - blockNum := head.GetNumberU64() + blockNum := head.Number.Uint64() chn := make(chan lpResult, LpoMaxBlocks) sent := 0 exp := 0 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go b/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go deleted file mode 100644 index 12fa74601..000000000 --- a/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go +++ /dev/null @@ -1,102 +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 . - -// +build opencl - -package eth - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "time" - - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/miner" -) - -func (s *Ethereum) StartMining(threads int, gpus string) error { - eb, err := s.Etherbase() - if err != nil { - err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) - glog.V(logger.Error).Infoln(err) - return err - } - - // GPU mining - if gpus != "" { - var ids []int - for _, s := range strings.Split(gpus, ",") { - i, err := strconv.Atoi(s) - if err != nil { - return fmt.Errorf("Invalid GPU id(s): %v", err) - } - if i < 0 { - return fmt.Errorf("Invalid GPU id: %v", i) - } - ids = append(ids, i) - } - - // TODO: re-creating miner is a bit ugly - s.miner = miner.New(s, s.chainConfig, s.EventMux(), ethash.NewCL(ids)) - go s.miner.Start(eb, len(ids)) - return nil - } - - // CPU mining - go s.miner.Start(eb, threads) - return nil -} - -func GPUBench(gpuid uint64) { - e := ethash.NewCL([]int{int(gpuid)}) - - var h common.Hash - bogoHeader := &types.Header{ - ParentHash: h, - Number: big.NewInt(int64(42)), - Difficulty: big.NewInt(int64(999999999999999)), - } - bogoBlock := types.NewBlock(bogoHeader, nil, nil, nil) - - err := ethash.InitCL(bogoBlock.NumberU64(), e) - if err != nil { - fmt.Println("OpenCL init error: ", err) - return - } - - stopChan := make(chan struct{}) - reportHashRate := func() { - for { - time.Sleep(3 * time.Second) - fmt.Printf("hashes/s : %v\n", e.GetHashrate()) - } - } - fmt.Printf("Starting benchmark (%v seconds)\n", 60) - go reportHashRate() - go e.Search(bogoBlock, stopChan, 0) - time.Sleep(60 * time.Second) - fmt.Println("OK.") -} - -func PrintOpenCLDevices() { - ethash.PrintDevices() -} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler.go b/vendor/github.com/ethereum/go-ethereum/eth/handler.go index 9d6b1ced2..8f05cc5b1 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" ) @@ -67,7 +68,7 @@ type ProtocolManager struct { txpool txPool blockchain *core.BlockChain chaindb ethdb.Database - chainconfig *core.ChainConfig + chainconfig *params.ChainConfig maxPeers int downloader *downloader.Downloader @@ -95,7 +96,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { +func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkId, diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go index aa7796f32..a095aa076 100644 --- a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go @@ -145,9 +145,9 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H // TransactionByHash returns the transaction with the given hash. func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { var tx *types.Transaction - err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash, false) + err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) if err == nil { - if _, r, _ := tx.SignatureValues(); r == nil { + if _, r, _ := tx.RawSignatureValues(); r == nil { return nil, fmt.Errorf("server returned transaction without signature") } } @@ -166,7 +166,11 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, var tx *types.Transaction err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index) if err == nil { - if _, r, _ := tx.SignatureValues(); r == nil { + var signer types.Signer = types.HomesteadSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + if _, r, _ := types.SignatureValues(signer, tx); r == nil { return nil, fmt.Errorf("server returned transaction without signature") } } diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go index ed17f87c4..d7bbfae1e 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go @@ -53,7 +53,7 @@ var ( Value: 6060, } pprofAddrFlag = cli.StringFlag{ - Name: "pprofaddr", + Name: "pprofaddr", Usage: "pprof HTTP server listening interface", Value: "127.0.0.1", } diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go index de8c7a0d9..89a17255c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -42,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/pborman/uuid" "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" "golang.org/x/net/context" ) @@ -293,7 +294,8 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + signature, err := s.am.SignWithPassphrase(args.From, passwd, signer.Hash(tx).Bytes()) if err != nil { return common.Hash{}, err } @@ -301,6 +303,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) { @@ -491,22 +553,14 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr } // Assemble the CALL invocation - msg := callmsg{ - addr: addr, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), + gas, gasPrice := args.Gas.BigInt(), args.GasPrice.BigInt() + if gas.Cmp(common.Big0) == 0 { + gas = big.NewInt(50000000) } - - if msg.gas.Cmp(common.Big0) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(common.Big0) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + if gasPrice.Cmp(common.Big0) == 0 { + gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) } + msg := types.NewMessage(addr, args.To, 0, args.Value.BigInt(), gas, gasPrice, common.FromHex(args.Data), false) // Execute the call and return vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) @@ -668,8 +722,12 @@ type RPCTransaction struct { // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { - from, _ := tx.FromFrontier() - v, r, s := tx.SignatureValues() + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + v, r, s := types.SignatureValues(signer, tx) return &RPCTransaction{ From: from, Gas: rpc.NewHexNumber(tx.Gas()), @@ -689,11 +747,12 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { if txIndex >= 0 && txIndex < len(b.Transactions()) { tx := b.Transactions()[txIndex] - from, err := tx.FromFrontier() - if err != nil { - return nil, err + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) } - v, r, s := tx.SignatureValues() + from, _ := types.Sender(signer, tx) + v, r, s := tx.RawSignatureValues() return &RPCTransaction{ BlockHash: b.Hash(), BlockNumber: rpc.NewHexNumber(b.Number()), @@ -926,11 +985,11 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma return nil, nil } - from, err := tx.FromFrontier() - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) } + from, _ := types.Sender(signer, tx) fields := map[string]interface{}{ "root": rpc.HexBytes(receipt.PostState), @@ -958,11 +1017,13 @@ 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()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + + signature, err := s.b.AccountManager().SignEthereum(addr, signer.Hash(tx).Bytes()) if err != nil { return nil, err } - return tx.WithSignature(signature) + return tx.WithSignature(signer, signature) } // SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. @@ -996,7 +1057,9 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA // submitTransaction is a helper function that submits tx to txPool and creates a log entry. func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signature []byte) (common.Hash, error) { - signedTx, err := tx.WithSignature(signature) + signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + + signedTx, err := tx.WithSignature(signer, signature) if err != nil { return common.Hash{}, err } @@ -1006,7 +1069,7 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si } if signedTx.To() == nil { - from, _ := signedTx.From() + from, _ := types.Sender(signer, signedTx) addr := crypto.CreateAddress(from, signedTx.Nonce()) glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) } else { @@ -1078,7 +1141,8 @@ func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context 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().SignWithPassphrase(args.From, passphrase, tx.SigHash().Bytes()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + signature, err := s.b.AccountManager().SignWithPassphrase(args.From, passphrase, tx.SigHash(signer).Bytes()) if err != nil { return common.Hash{}, err } @@ -1098,8 +1162,9 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return "", err } + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) if tx.To() == nil { - from, err := tx.FromFrontier() + from, err := types.Sender(signer, tx) if err != nil { return "", err } @@ -1112,11 +1177,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. @@ -1203,7 +1273,12 @@ type SignTransactionResult struct { } func newTx(t *types.Transaction) *Tx { - from, _ := t.FromFrontier() + var signer types.Signer = types.HomesteadSigner{} + if t.Protected() { + signer = types.NewEIP155Signer(t.ChainId()) + } + + from, _ := types.Sender(signer, t) return &Tx{ tx: t, To: t.To(), @@ -1269,7 +1344,11 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { pending := s.b.GetPoolTransactions() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { - from, _ := tx.FromFrontier() + var signer types.Signer = types.HomesteadSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) if s.b.AccountManager().HasAddress(from) { transactions = append(transactions, newRPCPendingTransaction(tx)) } @@ -1282,7 +1361,12 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { 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() { + var signer types.Signer = types.HomesteadSigner{} + if p.Protected() { + signer = types.NewEIP155Signer(p.ChainId()) + } + + if pFrom, err := types.Sender(signer, p); err == nil && pFrom == tx.From && signer.Hash(p) == signer.Hash(tx.tx) { if gasPrice == nil { gasPrice = rpc.NewHexNumber(tx.tx.GasPrice()) } @@ -1389,6 +1473,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/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go index ec5a84c5c..fdc4a39dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -59,6 +60,9 @@ type Backend interface { GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + + ChainConfig() *params.ChainConfig + CurrentBlock() *types.Block } type State interface { diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go index 16ec6ebf0..5f69826a3 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go @@ -167,17 +167,17 @@ func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { // JavascriptTracer provides an implementation of Tracer that evaluates a // Javascript function for each VM execution step. type JavascriptTracer struct { - vm *otto.Otto // Javascript VM instance - traceobj *otto.Object // User-supplied object to call - log map[string]interface{} // (Reusable) map for the `log` arg to `step` - logvalue otto.Value // JS view of `log` - memory *memoryWrapper // Wrapper around the VM memory - memvalue otto.Value // JS view of `memory` - stack *stackWrapper // Wrapper around the VM stack - stackvalue otto.Value // JS view of `stack` - db *dbWrapper // Wrapper around the VM environment - dbvalue otto.Value // JS view of `db` - err error // Error, if one has occurred + vm *otto.Otto // Javascript VM instance + traceobj *otto.Object // User-supplied object to call + log map[string]interface{} // (Reusable) map for the `log` arg to `step` + logvalue otto.Value // JS view of `log` + memory *memoryWrapper // Wrapper around the VM memory + memvalue otto.Value // JS view of `memory` + stack *stackWrapper // Wrapper around the VM stack + stackvalue otto.Value // JS view of `stack` + db *dbWrapper // Wrapper around the VM environment + dbvalue otto.Value // JS view of `db` + err error // Error, if one has occurred } // NewJavascriptTracer instantiates a new JavascriptTracer instance. @@ -222,17 +222,17 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) { db := &dbWrapper{} return &JavascriptTracer{ - vm: vm, - traceobj: jstracer, - log: log, - logvalue: logvalue, - memory: mem, - memvalue: mem.toValue(vm), - stack: stack, - stackvalue: stack.toValue(vm), - db: db, - dbvalue: db.toValue(vm), - err: nil, + vm: vm, + traceobj: jstracer, + log: log, + logvalue: logvalue, + memory: mem, + memvalue: mem.toValue(vm), + stack: stack, + stackvalue: stack.toValue(vm), + db: db, + dbvalue: db.toValue(vm), + err: nil, }, nil } @@ -278,7 +278,7 @@ func wrapError(context string, err error) error { } // CaptureState implements the Tracer interface to trace a single step of VM execution -func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) { +func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { jst.memory.memory = memory jst.stack.stack = stack @@ -301,6 +301,7 @@ func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.O jst.err = wrapError("step", err) } } + return nil } // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go index f32e16243..8fe00cc4c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go index cbbab0ece..c8a0cac8c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -337,6 +337,10 @@ web3._extend({ params: 1, outputFormatter: console.log }), + new web3._extend.Method({ + name: 'chaindbCompact', + call: 'debug_chaindbCompact', + }), new web3._extend.Method({ name: 'metrics', call: 'debug_metrics', @@ -583,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 = ` diff --git a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go index d50b3ea33..b77767ed7 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -1,18 +1,18 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. // -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by +// The 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. // -// go-ethereum is distributed in the hope that it will be useful, +// 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 General Public License for more details. +// GNU Lesser General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . package les @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -39,6 +40,14 @@ type LesApiBackend struct { gpo *gasprice.LightPriceOracle } +func (b *LesApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *LesApiBackend) CurrentBlock() *types.Block { + return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) +} + func (b *LesApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } @@ -81,13 +90,13 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { stateDb := state.(*light.LightState).Copy() - addr, _ := msg.From() + addr := msg.From() from, err := stateDb.GetOrNewStateObject(ctx, addr) if err != nil { return nil, nil, err } from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig) + env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}) return env, env.Error, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/les/backend.go b/vendor/github.com/ethereum/go-ethereum/les/backend.go index bd44a7509..d7b9c7e28 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/backend.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -24,11 +24,11 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" @@ -37,15 +37,18 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" ) type LightEthereum struct { odr *LesOdr relay *LesTxRelay - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Handlers @@ -106,10 +109,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return nil, errors.New("missing chain config") } eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.pow, eth.eventMux) if err != nil { if err == core.ErrNoGenesis { @@ -132,11 +131,38 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return eth, nil } +type LightDummyAPI struct{} + +// Etherbase is the address that mining rewards will be send to +func (s *LightDummyAPI) Etherbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Coinbase is the address that mining rewards will be send to (alias for Etherbase) +func (s *LightDummyAPI) Coinbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Hashrate returns the POW hashrate +func (s *LightDummyAPI) Hashrate() *rpc.HexNumber { + return rpc.NewHexNumber(0) +} + +// Mining returns an indication if this node is currently mining. +func (s *LightDummyAPI) Mining() bool { + return false +} + // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ { + Namespace: "eth", + Version: "1.0", + Service: &LightDummyAPI{}, + Public: true, + }, { Namespace: "eth", Version: "1.0", Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), @@ -144,7 +170,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, false), + Service: filters.NewPublicFilterAPI(s.ApiBackend, true), Public: true, }, { Namespace: "net", @@ -173,8 +199,9 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *LightEthereum) Start(srvr *p2p.Server) error { + glog.V(logger.Info).Infof("WARNING: light client mode is an experimental feature") s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId) - s.protocolManager.Start() + s.protocolManager.Start(srvr) return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go index bf9f010fb..ae9bf8474 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go +++ b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -18,7 +18,6 @@ package les import ( -//"fmt" "math/big" "sync" "time" @@ -29,58 +28,62 @@ import ( ) type lightFetcher struct { - pm *ProtocolManager - odr *LesOdr - chain BlockChain + pm *ProtocolManager + odr *LesOdr + chain BlockChain - headAnnouncedMu sync.Mutex - headAnnouncedBy map[common.Hash][]*peer - currentTd *big.Int - deliverChn chan fetchResponse - reqMu sync.RWMutex - requested map[uint64]fetchRequest - timeoutChn chan uint64 - notifyChn chan bool // true if initiated from outside - syncing bool - syncDone chan struct{} + headAnnouncedMu sync.Mutex + headAnnouncedBy map[common.Hash][]*peer + currentTd *big.Int + deliverChn chan fetchResponse + reqMu sync.RWMutex + requested map[uint64]fetchRequest + timeoutChn chan uint64 + notifyChn chan bool // true if initiated from outside + syncing bool + syncDone chan struct{} } type fetchRequest struct { - hash common.Hash + hash common.Hash amount uint64 - peer *peer + peer *peer } type fetchResponse struct { - reqID uint64 + reqID uint64 headers []*types.Header } func newLightFetcher(pm *ProtocolManager) *lightFetcher { f := &lightFetcher{ - pm: pm, - chain: pm.blockchain, - odr: pm.odr, + pm: pm, + chain: pm.blockchain, + odr: pm.odr, headAnnouncedBy: make(map[common.Hash][]*peer), - deliverChn: make(chan fetchResponse, 100), + deliverChn: make(chan fetchResponse, 100), requested: make(map[uint64]fetchRequest), - timeoutChn: make(chan uint64), - notifyChn: make(chan bool, 100), - syncDone: make(chan struct{}), - currentTd: big.NewInt(0), + timeoutChn: make(chan uint64), + notifyChn: make(chan bool, 100), + syncDone: make(chan struct{}), + currentTd: big.NewInt(0), } go f.syncLoop() return f } -func (f *lightFetcher) notify(p *peer, head *newBlockHashData) { +func (f *lightFetcher) notify(p *peer, head *announceData) { var headHash common.Hash if head == nil { // initial notify headHash = p.Head() - } else { + } else { + if core.GetTd(f.pm.chainDb, head.Hash, head.Number) != nil { + head.haveHeaders = head.Number + } + //fmt.Println("notify", p.id, head.Number, head.ReorgDepth, head.haveHeaders) if !p.addNotify(head) { -//fmt.Println("addNotify fail") + //fmt.Println("addNotify fail") f.pm.removePeer(p.id) } headHash = head.Hash @@ -100,27 +103,27 @@ func (f *lightFetcher) gotHeader(header *types.Header) { if peerList == nil { return } - number := header.GetNumberU64() + number := header.Number.Uint64() td := core.GetTd(f.pm.chainDb, hash, number) for _, peer := range peerList { peer.lock.Lock() ok := peer.gotHeader(hash, number, td) peer.lock.Unlock() if !ok { -//fmt.Println("gotHeader fail") + //fmt.Println("gotHeader fail") f.pm.removePeer(peer.id) } - } + } delete(f.headAnnouncedBy, hash) } -func (f *lightFetcher) nextRequest() (*peer, *newBlockHashData) { +func (f *lightFetcher) nextRequest() (*peer, *announceData) { var bestPeer *peer bestTd := f.currentTd for _, peer := range f.pm.peers.AllPeers() { peer.lock.RLock() if !peer.headInfo.requested && (peer.headInfo.Td.Cmp(bestTd) > 0 || - (bestPeer != nil && peer.headInfo.Td.Cmp(bestTd) == 0 && peer.headInfo.haveHeaders > bestPeer.headInfo.haveHeaders)) { + (bestPeer != nil && peer.headInfo.Td.Cmp(bestTd) == 0 && peer.headInfo.haveHeaders > bestPeer.headInfo.haveHeaders)) { bestPeer = peer bestTd = peer.headInfo.Td } @@ -133,6 +136,15 @@ func (f *lightFetcher) nextRequest() (*peer, *newBlockHashData) { res := bestPeer.headInfo res.requested = true bestPeer.lock.Unlock() + for _, peer := range f.pm.peers.AllPeers() { + if peer != bestPeer { + peer.lock.Lock() + if peer.headInfo.Hash == bestPeer.headInfo.Hash && peer.headInfo.haveHeaders == bestPeer.headInfo.haveHeaders { + peer.headInfo.requested = true + } + peer.lock.Unlock() + } + } return bestPeer, res } @@ -147,18 +159,18 @@ func (f *lightFetcher) requestedID(reqID uint64) bool { return ok } -func (f *lightFetcher) request(p *peer, block *newBlockHashData) { -//fmt.Println("request", block.Number, block.haveHeaders) - amount := block.Number-block.haveHeaders +func (f *lightFetcher) request(p *peer, block *announceData) { + //fmt.Println("request", p.id, block.Number, block.haveHeaders) + amount := block.Number - block.haveHeaders if amount == 0 { return } if amount > 100 { f.syncing = true go func() { -//fmt.Println("f.pm.synchronise(p)") + //fmt.Println("f.pm.synchronise(p)") f.pm.synchronise(p) -//fmt.Println("sync done") + //fmt.Println("sync done") f.syncDone <- struct{}{} }() return @@ -189,7 +201,7 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo return false } for _, header := range headers { - td := core.GetTd(f.pm.chainDb, header.Hash(), header.GetNumberU64()) + td := core.GetTd(f.pm.chainDb, header.Hash(), header.Number.Uint64()) if td == nil { return false } @@ -202,15 +214,15 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo } func (f *lightFetcher) checkSyncedHeaders() { -//fmt.Println("checkSyncedHeaders()") + //fmt.Println("checkSyncedHeaders()") for _, peer := range f.pm.peers.AllPeers() { peer.lock.Lock() h := peer.firstHeadInfo remove := false -loop: + loop: for h != nil { if td := core.GetTd(f.pm.chainDb, h.Hash, h.Number); td != nil { -//fmt.Println(" found", h.Number) + //fmt.Println(" found", h.Number) ok := peer.gotHeader(h.Hash, h.Number, td) if !ok { remove = true @@ -224,7 +236,7 @@ loop: } peer.lock.Unlock() if remove { -//fmt.Println("checkSync fail") + //fmt.Println("checkSync fail") f.pm.removePeer(peer.id) } } @@ -240,7 +252,7 @@ func (f *lightFetcher) syncLoop() { case <-f.pm.quitSync: return case ext := <-f.notifyChn: -//fmt.Println("<-f.notifyChn", f.syncing, ext, srtoNotify) + //fmt.Println("<-f.notifyChn", f.syncing, ext, srtoNotify) s := srtoNotify srtoNotify = false if !f.syncing && !(ext && s) { @@ -261,21 +273,21 @@ func (f *lightFetcher) syncLoop() { } f.reqMu.Unlock() if ok { -//fmt.Println("hard timeout") + //fmt.Println("hard timeout") f.pm.removePeer(req.peer.id) } case resp := <-f.deliverChn: -//fmt.Println("<-f.deliverChn", f.syncing) + //fmt.Println("<-f.deliverChn", f.syncing) f.reqMu.Lock() req, ok := f.requested[resp.reqID] delete(f.requested, resp.reqID) f.reqMu.Unlock() if !ok || !(f.syncing || f.processResponse(req, resp)) { -//fmt.Println("processResponse fail") + //fmt.Println("processResponse fail") f.pm.removePeer(req.peer.id) } case <-f.syncDone: -//fmt.Println("<-f.syncDone", f.syncing) + //fmt.Println("<-f.syncDone", f.syncing) f.checkSyncedHeaders() f.syncing = false } diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go index 1b569db0b..acb131ea4 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -99,7 +99,7 @@ type ServerNode struct { params *ServerParams sumCost uint64 // sum of req costs sent to this server pending map[uint64]uint64 // value = sumCost after sending the given req - lock sync.Mutex + lock sync.RWMutex } func NewServerNode(params *ServerParams) *ServerNode { @@ -135,8 +135,8 @@ func (peer *ServerNode) canSend(maxCost uint64) uint64 { } func (peer *ServerNode) CanSend(maxCost uint64) uint64 { - peer.lock.Lock() - defer peer.lock.Unlock() + peer.lock.RLock() + defer peer.lock.RUnlock() return peer.canSend(maxCost) } @@ -148,7 +148,10 @@ func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { peer.recalcBLE(getTime()) for peer.bufEstimate < maxCost { - time.Sleep(time.Duration(peer.canSend(maxCost))) + wait := time.Duration(peer.canSend(maxCost)) + peer.lock.Unlock() + time.Sleep(wait) + peer.lock.Lock() peer.recalcBLE(getTime()) } peer.bufEstimate -= maxCost diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go index c0469e7b6..786884437 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler.go b/vendor/github.com/ethereum/go-ethereum/les/handler.go index 1130e6ab4..83d73666f 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/les/handler.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -23,6 +23,7 @@ import ( "fmt" "math/big" "sync" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -36,6 +37,8 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -93,12 +96,16 @@ type ProtocolManager struct { txpool txPool txrelay *LesTxRelay networkId int - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig blockchain BlockChain chainDb ethdb.Database odr *LesOdr server *LesServer + topicDisc *discv5.Network + lesTopic discv5.Topic + p2pServer *p2p.Server + downloader *downloader.Downloader fetcher *lightFetcher peers *peerSet @@ -123,7 +130,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(chainConfig *core.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { +func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ lightSync: lightSync, @@ -229,11 +236,56 @@ func (pm *ProtocolManager) removePeer(id string) { } } -func (pm *ProtocolManager) Start() { +func (pm *ProtocolManager) findServers() { + if pm.p2pServer == nil || pm.topicDisc == nil { + return + } + glog.V(logger.Debug).Infoln("Looking for topic", string(pm.lesTopic)) + enodes := make(chan string, 100) + stop := make(chan struct{}) + go pm.topicDisc.SearchTopic(pm.lesTopic, stop, enodes) + go func() { + added := make(map[string]bool) + for { + select { + case enode := <-enodes: + if !added[enode] { + glog.V(logger.Info).Infoln("Found LES server:", enode) + added[enode] = true + if node, err := discover.ParseNode(enode); err == nil { + pm.p2pServer.AddPeer(node) + } + } + case <-stop: + return + } + } + }() + select { + case <-time.After(time.Second * 20): + case <-pm.quitSync: + } + close(stop) +} + +func (pm *ProtocolManager) Start(srvr *p2p.Server) { + pm.p2pServer = srvr + if srvr != nil { + pm.topicDisc = srvr.DiscV5 + } + pm.lesTopic = discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8])) if pm.lightSync { // start sync handler + go pm.findServers() go pm.syncer() } else { + if pm.topicDisc != nil { + go func() { + glog.V(logger.Debug).Infoln("Starting registering topic", string(pm.lesTopic)) + pm.topicDisc.RegisterTopic(pm.lesTopic, pm.quitSync) + glog.V(logger.Debug).Infoln("Stopped registering topic", string(pm.lesTopic)) + }() + } go func() { for range pm.newPeerCh { } @@ -329,9 +381,8 @@ func (pm *ProtocolManager) handle(p *peer) error { // new block announce loop for { select { - case announce := <-p.newBlockHashChn: - p.SendNewBlockHash(announce) - //fmt.Println(" BROADCAST sent") + case announce := <-p.announceChn: + p.SendAnnounce(announce) case <-stop: return } @@ -342,11 +393,9 @@ func (pm *ProtocolManager) handle(p *peer) error { for { if err := pm.handleMsg(p); err != nil { glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err) - //fmt.Println("handleMsg err:", err) return err } } - return nil } var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg} @@ -363,7 +412,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { var costs *requestCosts var reqCnt, maxReqs int - //fmt.Println("MSG", msg.Code, msg.Size) + glog.V(logger.Debug).Infoln("msg:", msg.Code, msg.Size) if rc, ok := p.fcCosts[msg.Code]; ok { // check if msg is a supported request type costs = rc if p.fcClient == nil { @@ -392,21 +441,23 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { // Handle the message depending on its contents switch msg.Code { case StatusMsg: - glog.V(logger.Debug).Infof("LES: received StatusMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== StatusMsg from peer %v", p.id) // Status messages should never arrive after the handshake return errResp(ErrExtraStatusMsg, "uncontrolled status message") // Block header query, collect the requested headers and reply - case NewBlockHashMsg: - var req newBlockHashData + case AnnounceMsg: + glog.V(logger.Debug).Infoln("<=== AnnounceMsg from peer %v:", p.id) + + var req announceData if err := msg.Decode(&req); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } - //fmt.Println("RECEIVED", req.Number, req.Hash, req.Td, req.ReorgDepth) + glog.V(logger.Detail).Infoln("AnnounceMsg:", req.Number, req.Hash, req.Td, req.ReorgDepth) pm.fetcher.notify(p, &req) case GetBlockHeadersMsg: - glog.V(logger.Debug).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetBlockHeadersMsg from peer %v", p.id) // Decode the complex header query var req struct { ReqID uint64 @@ -491,7 +542,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received BlockHeadersMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== BlockHeadersMsg from peer %v", p.id) // A batch of headers arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -511,7 +562,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetBlockBodiesMsg: - glog.V(logger.Debug).Infof("LES: received GetBlockBodiesMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetBlockBodiesMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -548,7 +599,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received BlockBodiesMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== BlockBodiesMsg from peer %v", p.id) // A batch of block bodies arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -565,7 +616,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetCodeMsg: - glog.V(logger.Debug).Infof("LES: received GetCodeMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetCodeMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -609,7 +660,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received CodeMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== CodeMsg from peer %v", p.id) // A batch of node state data arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -626,7 +677,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetReceiptsMsg: - glog.V(logger.Debug).Infof("LES: received GetReceiptsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetReceiptsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -672,7 +723,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received ReceiptsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== ReceiptsMsg from peer %v", p.id) // A batch of receipts arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -689,7 +740,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetProofsMsg: - glog.V(logger.Debug).Infof("LES: received GetProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetProofsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -739,7 +790,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received ProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== ProofsMsg from peer %v", p.id) // A batch of merkle proofs arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -756,7 +807,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetHeaderProofsMsg: - glog.V(logger.Debug).Infof("LES: received GetHeaderProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetHeaderProofsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -800,7 +851,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received HeaderProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== HeaderProofsMsg from peer %v", p.id) var resp struct { ReqID, BV uint64 Data []ChtResp @@ -833,7 +884,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) default: - glog.V(logger.Debug).Infof("LES: received unknown message with code %d from peer %v", msg.Code, p.id) + glog.V(logger.Debug).Infof("<=== unknown message with code %d from peer %v", msg.Code, p.id) return errResp(ErrInvalidMsgCode, "%v", msg.Code) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/metrics.go b/vendor/github.com/ethereum/go-ethereum/les/metrics.go index 88e6726e2..aa0796790 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/les/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr.go b/vendor/github.com/ethereum/go-ethereum/les/odr.go index 2674ba6a1..444b1da2a 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go b/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go index 0323ce27f..e9b7eec7f 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go index bf0346977..f4bd51888 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/peer.go b/vendor/github.com/ethereum/go-ethereum/les/peer.go index e5ec3457b..5d566d899 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/les/peer.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -51,11 +51,11 @@ type peer struct { id string - firstHeadInfo, headInfo *newBlockHashData + firstHeadInfo, headInfo *announceData headInfoLen int lock sync.RWMutex - newBlockHashChn chan newBlockHashData + announceChn chan announceData fcClient *flowcontrol.ClientNode // nil if the peer is server only fcServer *flowcontrol.ServerNode // nil if the peer is client only @@ -67,12 +67,12 @@ func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { id := p.ID() return &peer{ - Peer: p, - rw: rw, - version: version, - network: network, - id: fmt.Sprintf("%x", id[:8]), - newBlockHashChn: make(chan newBlockHashData, 20), + Peer: p, + rw: rw, + version: version, + network: network, + id: fmt.Sprintf("%x", id[:8]), + announceChn: make(chan announceData, 20), } } @@ -109,7 +109,7 @@ func (p *peer) headBlockInfo() blockInfo { return blockInfo{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td} } -func (p *peer) addNotify(announce *newBlockHashData) bool { +func (p *peer) addNotify(announce *announceData) bool { p.lock.Lock() defer p.lock.Unlock() @@ -121,11 +121,13 @@ func (p *peer) addNotify(announce *newBlockHashData) bool { p.firstHeadInfo = p.firstHeadInfo.next p.headInfoLen-- } - hh := p.headInfo.Number - announce.ReorgDepth - if p.headInfo.haveHeaders < hh { - hh = p.headInfo.haveHeaders + if announce.haveHeaders == 0 { + hh := p.headInfo.Number - announce.ReorgDepth + if p.headInfo.haveHeaders < hh { + hh = p.headInfo.haveHeaders + } + announce.haveHeaders = hh } - announce.haveHeaders = hh p.headInfo.next = announce p.headInfo = announce p.headInfoLen++ @@ -200,10 +202,10 @@ func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 { return cost } -// SendNewBlockHash announces the availability of a number of blocks through +// SendAnnounce announces the availability of a number of blocks through // a hash notification. -func (p *peer) SendNewBlockHash(request newBlockHashData) error { - return p2p.Send(p.rw, NewBlockHashMsg, request) +func (p *peer) SendAnnounce(request announceData) error { + return p2p.Send(p.rw, AnnounceMsg, request) } // SendBlockHeaders sends a batch of block headers to the remote peer. @@ -451,7 +453,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis p.fcCosts = MRC.decode() } - p.firstHeadInfo = &newBlockHashData{Td: rTd, Hash: rHash, Number: rNum} + p.firstHeadInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum} p.headInfo = p.firstHeadInfo p.headInfoLen = 1 return nil diff --git a/vendor/github.com/ethereum/go-ethereum/les/protocol.go b/vendor/github.com/ethereum/go-ethereum/les/protocol.go index 0ef2d8c06..46da2b8c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/les/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -47,7 +47,7 @@ const ( const ( // Protocol messages belonging to LPV1 StatusMsg = 0x00 - NewBlockHashMsg = 0x01 + AnnounceMsg = 0x01 GetBlockHeadersMsg = 0x02 BlockHeadersMsg = 0x03 GetBlockBodiesMsg = 0x04 @@ -111,30 +111,20 @@ type chainManager interface { Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) } -// statusData is the network packet for the status message. -type statusData struct { - ProtocolVersion uint32 - NetworkId uint32 - TD *big.Int - CurrentBlock common.Hash - GenesisBlock common.Hash - History uint64 - BL, MRR uint64 - MRC RequestCostList -} - -// newBlockHashData is the network packet for the block announcements. -type newBlockHashData struct{ - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced - Td *big.Int // Total difficulty of one particular block being announced +// announceData is the network packet for the block announcements. +type announceData struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced + Td *big.Int // Total difficulty of one particular block being announced ReorgDepth uint64 + Update keyValueList haveHeaders uint64 // we have the headers of the remote peer's chain up to this number - headKnown bool - requested bool - next *newBlockHashData + headKnown bool + requested bool + next *announceData } + type blockInfo struct { Hash common.Hash // Hash of one particular block being announced Number uint64 // Number of one particular block being announced diff --git a/vendor/github.com/ethereum/go-ethereum/les/server.go b/vendor/github.com/ethereum/go-ethereum/les/server.go index f751b6a91..c763e8c63 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/server.go +++ b/vendor/github.com/ethereum/go-ethereum/les/server.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -19,7 +19,6 @@ package les import ( "encoding/binary" - "fmt" "math" "sync" "time" @@ -31,6 +30,8 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -66,8 +67,9 @@ func (s *LesServer) Protocols() []p2p.Protocol { return s.protocolManager.SubProtocols } -func (s *LesServer) Start() { - s.protocolManager.Start() +func (s *LesServer) Start(srvr *p2p.Server) { + s.protocolManager.Start(srvr) + } func (s *LesServer) Stop() { @@ -278,20 +280,22 @@ func (pm *ProtocolManager) blockLoop() { if len(peers) > 0 { header := ev.Data.(core.ChainHeadEvent).Block.Header() hash := header.Hash() - number := header.GetNumberU64() + number := header.Number.Uint64() td := core.GetTd(pm.chainDb, hash, number) if td != nil && td.Cmp(lastBroadcastTd) > 0 { var reorg uint64 if lastHead != nil { - reorg = lastHead.GetNumberU64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).GetNumberU64() + reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() } lastHead = header lastBroadcastTd = td - //fmt.Println("BROADCAST", number, hash, td, reorg) - announce := newBlockHashData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} + + glog.V(logger.Debug).Infoln("===> ", number, hash, td, reorg) + + announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} for _, p := range peers { select { - case p.newBlockHashChn <- announce: + case p.announceChn <- announce: default: pm.removePeer(p.id) } @@ -390,7 +394,9 @@ func makeCht(db ethdb.Database) bool { lastChtNum = 0 } else { lastChtNum++ - fmt.Printf("CHT %d %064x\n", lastChtNum, root) + + glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root) + storeChtRoot(db, lastChtNum, root) var data [8]byte binary.BigEndian.PutUint64(data[:], lastChtNum) diff --git a/vendor/github.com/ethereum/go-ethereum/les/sync.go b/vendor/github.com/ethereum/go-ethereum/les/sync.go index f92f8ce04..72c979c61 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/les/sync.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/txrelay.go b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go index 366e69a62..036158f5d 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/txrelay.go +++ b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( @@ -106,12 +107,12 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) { } } - for peer, list := range sendTo { - cost := peer.GetRequestCost(SendTxMsg, len(list)) - go func() { - peer.fcServer.SendRequest(0, cost) - peer.SendTxs(cost, list) - }() + for p, list := range sendTo { + cost := p.GetRequestCost(SendTxMsg, len(list)) + go func(p *peer, list types.Transactions, cost uint64) { + p.fcServer.SendRequest(0, cost) + p.SendTxs(cost, list) + }(p, list, cost) } } diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go index acce41705..1cea7a892 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package light import ( @@ -28,6 +29,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" "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" @@ -70,7 +72,7 @@ type LightChain struct { // NewLightChain returns a fully initialised light chain using information // available in the database. It initialises the default Ethereum header // validator. -func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { +func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -106,8 +108,8 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e // add trusted CHT if config.DAOForkSupport { WriteTrustedCht(bc.chainDb, TrustedCht{ - Number: 601, - Root: common.HexToHash("7d417d315bb8873c03fad7447bfbb0e92a22afe2937eaf064d3d48077ec1dd87"), + Number: 637, + Root: common.HexToHash("01e408d9b1942f05dba1a879f3eaafe34d219edaeb8223fecf1244cc023d3e23"), }) } else { WriteTrustedCht(bc.chainDb, TrustedCht{ @@ -120,8 +122,8 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e if bc.genesisBlock.Hash() == (common.Hash{12, 215, 134, 162, 66, 93, 22, 241, 82, 198, 88, 49, 108, 66, 62, 108, 225, 24, 30, 21, 195, 41, 88, 38, 215, 201, 144, 76, 186, 156, 227, 3}) { // add trusted CHT for testnet WriteTrustedCht(bc.chainDb, TrustedCht{ - Number: 436, - Root: common.HexToHash("97a12df5d04d72bde4b4b840e1018e4f08aee34b7d0bf2c5dbfc052b86fe7439"), + Number: 452, + Root: common.HexToHash("511da2c88e32b14cf4a4e62f7fcbb297139faebc260a4ab5eb43cce6edcba324"), }) glog.V(logger.Info).Infoln("Added trusted CHT for testnet") } else { diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go index 458f8233b..5c72f90e9 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go +++ b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package light import ( diff --git a/vendor/github.com/ethereum/go-ethereum/light/state.go b/vendor/github.com/ethereum/go-ethereum/light/state.go index 88f60efbb..9f2376809 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state.go @@ -26,9 +26,6 @@ import ( "golang.org/x/net/context" ) -// StartingNonce determines the default nonce when new accounts are being created. -var StartingNonce uint64 - // LightState is a memory representation of a state. // This version is ODR capable, caching only the already accessed part of the // state, retrieving unknown parts on-demand from the ODR backend. Changes are @@ -238,7 +235,6 @@ func (self *LightState) newStateObject(addr common.Address) *StateObject { } stateObject := NewStateObject(addr, self.odr) - stateObject.SetNonce(StartingNonce) self.stateObjects[addr.Str()] = stateObject return stateObject diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool.go b/vendor/github.com/ethereum/go-ethereum/light/txpool.go index c49d4e74a..3eaa0cf54 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/txpool.go +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -28,6 +28,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/rlp" "golang.org/x/net/context" ) @@ -42,7 +43,8 @@ var txPermanent = uint64(500) // always receive all locally signed transactions in the same order as they are // created. type TxPool struct { - config *core.ChainConfig + config *params.ChainConfig + signer types.Signer quit chan bool eventMux *event.TypeMux events event.Subscription @@ -76,9 +78,10 @@ type TxRelayBackend interface { } // NewTxPool creates a new light transaction pool -func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { pool := &TxPool{ config: config, + signer: types.NewEIP155Signer(config.ChainId), nonce: make(map[common.Address]uint64), pending: make(map[common.Hash]*types.Transaction), mined: make(map[common.Hash][]*types.Transaction), @@ -90,7 +93,7 @@ func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightCh odr: chain.Odr(), chainDb: chain.Odr().Database(), head: chain.CurrentHeader().Hash(), - clearIdx: chain.CurrentHeader().GetNumberU64(), + clearIdx: chain.CurrentHeader().Number.Uint64(), } go pool.eventLoop() @@ -197,7 +200,7 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, idx uin if len(receipts) != len(block.Transactions()) { panic(nil) // should never happen if hashes did match } - core.SetReceiptsData(block, receipts) + core.SetReceiptsData(pool.config, block, receipts) } //fmt.Println("WriteReceipt", receipts[i].TxHash) core.WriteReceipt(pool.chainDb, receipts[i]) @@ -241,11 +244,11 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx // find common ancestor, create list of rolled back and new block hashes var oldHashes, newHashes []common.Hash for oldh.Hash() != newh.Hash() { - if oldh.GetNumberU64() >= newh.GetNumberU64() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { oldHashes = append(oldHashes, oldh.Hash()) oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) } - if oldh.GetNumberU64() < newh.GetNumberU64() { + if oldh.Number.Uint64() < newh.Number.Uint64() { newHashes = append(newHashes, newh.Hash()) newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) if newh == nil { @@ -254,8 +257,8 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx } } } - if oldh.GetNumberU64() < pool.clearIdx { - pool.clearIdx = oldh.GetNumberU64() + if oldh.Number.Uint64() < pool.clearIdx { + pool.clearIdx = oldh.Number.Uint64() } // roll back old blocks for _, hash := range oldHashes { @@ -265,14 +268,14 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx // check mined txs of new blocks (array is in reversed order) for i := len(newHashes) - 1; i >= 0; i-- { hash := newHashes[i] - if err := pool.checkMinedTxs(ctx, hash, newHeader.GetNumberU64()-uint64(i), txc); err != nil { + if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { return txc, err } pool.head = hash } // clear old mined tx entries of old blocks - if idx := newHeader.GetNumberU64(); idx > pool.clearIdx+txPermanent { + if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { idx2 := idx - txPermanent for i := pool.clearIdx; i < idx2; i++ { hash := core.GetCanonicalHash(pool.chainDb, i) @@ -308,6 +311,7 @@ func (pool *TxPool) eventLoop() { m, r := txc.getLists() pool.relay.NewHead(pool.head, m, r) pool.homestead = pool.config.IsHomestead(head.Number) + pool.signer = types.MakeSigner(pool.config, head.Number) pool.mu.Unlock() } } @@ -339,7 +343,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // Validate the transaction sender and it's sig. Throw // if the from fields is invalid. - if from, err = tx.From(); err != nil { + if from, err = types.Sender(pool.signer, tx); err != nil { return core.ErrInvalidSender } @@ -388,7 +392,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - if tx.Gas().Cmp(core.IntrinsicGas(tx.Data(), core.MessageCreatesContract(tx), pool.homestead)) < 0 { + if tx.Gas().Cmp(core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)) < 0 { return core.ErrIntrinsicGas } @@ -412,7 +416,8 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { self.pending[hash] = tx nonce := tx.Nonce() + 1 - addr, _ := tx.From() + + addr, _ := types.Sender(self.signer, tx) if nonce > self.nonce[addr] { self.nonce[addr] = nonce } @@ -420,7 +425,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. - go self.eventMux.Post(core.TxPreEvent{tx}) + go self.eventMux.Post(core.TxPreEvent{Tx: tx}) } if glog.V(logger.Debug) { @@ -432,7 +437,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { } // we can ignore the error here because From is // verified in ValidateTransaction. - f, _ := tx.From() + f, _ := types.Sender(self.signer, tx) from := common.Bytes2Hex(f[:4]) glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } @@ -517,7 +522,7 @@ func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common // Retrieve all the pending transactions and sort by account and by nonce pending := make(map[common.Address]types.Transactions) for _, tx := range self.pending { - account, _ := tx.From() + account, _ := types.Sender(self.signer, tx) pending[account] = append(pending[account], tx) } // There are no queued transactions in a light pool, just return an empty map diff --git a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go index 0f4b90908..5d330b072 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) @@ -34,7 +35,7 @@ import ( type VMEnv struct { vm.Environment ctx context.Context - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig evm *vm.EVM state *VMState header *types.Header @@ -45,7 +46,7 @@ type VMEnv struct { } // NewEnv creates a new execution environment based on an ODR capable light state -func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { +func NewEnv(ctx context.Context, state *LightState, chainConfig *params.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { env := &VMEnv{ chainConfig: chainConfig, chain: chain, @@ -58,20 +59,20 @@ func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfi return env } -func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -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() *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) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } +func (self *VMEnv) Vm() vm.Vm { return self.evm } +func (self *VMEnv) Origin() common.Address { return self.msg.From() } +func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } +func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } +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) Db() vm.Database { return self.state } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { - if header.GetNumberU64() == n { + if header.Number.Uint64() == n { return header.Hash() } } diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go index 7f647cd00..d756894f3 100644 --- a/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go @@ -48,6 +48,15 @@ func init() { exp.Exp(metrics.DefaultRegistry) } +// NewCounter create a new metrics Counter, either a real one of a NOP stub depending +// on the metrics flag. +func NewCounter(name string) metrics.Counter { + if !Enabled { + return new(metrics.NilCounter) + } + return metrics.GetOrRegisterCounter(name, metrics.DefaultRegistry) +} + // NewMeter create a new metrics Meter, either a real one of a NOP stub depending // on the metrics flag. func NewMeter(name string) metrics.Meter { diff --git a/vendor/github.com/ethereum/go-ethereum/miner/miner.go b/vendor/github.com/ethereum/go-ethereum/miner/miner.go index c16cbe6ae..c85a1cd8e 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/miner.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/miner.go @@ -50,8 +50,6 @@ type Miner struct { worker *worker - MinAcceptedGasPrice *big.Int - threads int coinbase common.Address mining int32 @@ -62,7 +60,7 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { miner := &Miner{ eth: eth, mux: mux, @@ -107,12 +105,15 @@ out: } } +func (m *Miner) GasPrice() *big.Int { + return new(big.Int).Set(m.worker.gasPrice) +} + func (m *Miner) SetGasPrice(price *big.Int) { // FIXME block tests set a nil gas price. Quick dirty fix if price == nil { return } - m.worker.setGasPrice(price) } diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go index e5348cef4..2933b6bd3 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -63,7 +63,9 @@ type uint64RingBuffer struct { // Work is the workers current environment and holds // all of the current state information type Work struct { - config *core.ChainConfig + config *params.ChainConfig + signer types.Signer + state *state.StateDB // apply state changes here ancestors *set.Set // ancestor set (used for checking uncle parent validity) family *set.Set // family set (used for checking uncle invalidity) @@ -90,7 +92,7 @@ type Result struct { // worker is the main object which takes care of applying messages to the new state type worker struct { - config *core.ChainConfig + config *params.ChainConfig mu sync.Mutex @@ -128,7 +130,7 @@ type worker struct { fullValidation bool } -func newWorker(config *core.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { +func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { worker := &worker{ config: config, eth: eth, @@ -169,7 +171,7 @@ func (self *worker) pending() (*types.Block, *state.StateDB) { self.current.txs, nil, self.current.receipts, - ), self.current.state + ), self.current.state.Copy() } return self.current.Block, self.current.state.Copy() } @@ -235,7 +237,7 @@ func (self *worker) update() { if atomic.LoadInt32(&self.mining) == 0 { self.currentMu.Lock() - acc, _ := ev.Tx.From() + acc, _ := types.Sender(self.current.signer, ev.Tx) txs := map[common.Address]types.Transactions{acc: types.Transactions{ev.Tx}} txset := types.NewTransactionsByPriceAndNonce(txs) @@ -276,7 +278,7 @@ func (self *worker) wait() { } go self.mux.Post(core.NewMinedBlockEvent{Block: block}) } else { - work.state.Commit() + work.state.Commit(self.config.IsEIP158(block.Number())) parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { glog.V(logger.Error).Infoln("Invalid block found during mining") @@ -367,6 +369,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error } work := &Work{ config: self.config, + signer: types.NewEIP155Signer(self.config.ChainId), state: state, ancestors: set.New(), family: set.New(), @@ -528,7 +531,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { // commit state root after all state transitions. core.AccumulateRewards(work.state, header, uncles) - header.Root = work.state.IntermediateRoot() + header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number)) } // create the new block whose nonce will be mined. @@ -570,7 +573,17 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB } // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. - from, _ := tx.From() + // + // We use the eip155 signer regardless of the current hf. + from, _ := types.Sender(env.signer, tx) + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !env.config.IsEIP155(env.header.Number) { + glog.V(logger.Detail).Infof("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash()) + + txs.Pop() + continue + } // Ignore any transactions (and accounts subsequently) with low gas limits if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { @@ -620,14 +633,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { snap := env.state.Snapshot() - // this is a bit of a hack to force jit for the miners - config := env.config.VmConfig - if !(config.EnableJit && config.ForceJit) { - config.EnableJit = false - } - config.ForceJit = false // disable forcing jit - - receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) if err != nil { env.state.RevertToSnapshot(snap) return err, nil diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go new file mode 100644 index 000000000..41498b6f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go @@ -0,0 +1,181 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the accounts package to support client side key +// management on mobile platforms. + +package geth + +import ( + "errors" + "time" + + "github.com/ethereum/go-ethereum/accounts" +) + +const ( + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptN = int(accounts.StandardScryptN) + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptP = int(accounts.StandardScryptP) + + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptN = int(accounts.LightScryptN) + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptP = int(accounts.LightScryptP) +) + +// Account represents a stored key. +type Account struct{ account accounts.Account } + +// Accounts represents a slice of accounts. +type Accounts struct{ accounts []accounts.Account } + +// Size returns the number of accounts in the slice. +func (a *Accounts) Size() int { + return len(a.accounts) +} + +// Get returns the account at the given index from the slice. +func (a *Accounts) Get(index int) (*Account, error) { + if index < 0 || index >= len(a.accounts) { + return nil, errors.New("index out of bounds") + } + return &Account{a.accounts[index]}, nil +} + +// Set sets the account at the given index in the slice. +func (a *Accounts) Set(index int, account *Account) error { + if index < 0 || index >= len(a.accounts) { + return errors.New("index out of bounds") + } + a.accounts[index] = account.account + return nil +} + +// GetAddress retrieves the address associated with the account. +func (a *Account) GetAddress() *Address { + return &Address{a.account.Address} +} + +// GetFile retrieves the path of the file containing the account key. +func (a *Account) GetFile() string { + return a.account.File +} + +// AccountManager manages a key storage directory on disk. +type AccountManager struct{ manager *accounts.Manager } + +// NewAccountManager creates a manager for the given directory. +func NewAccountManager(keydir string, scryptN, scryptP int) *AccountManager { + return &AccountManager{manager: accounts.NewManager(keydir, scryptN, scryptP)} +} + +// HasAddress reports whether a key with the given address is present. +func (am *AccountManager) HasAddress(addr *Address) bool { + return am.manager.HasAddress(addr.address) +} + +// GetAccounts returns all key files present in the directory. +func (am *AccountManager) GetAccounts() *Accounts { + return &Accounts{am.manager.Accounts()} +} + +// DeleteAccount deletes the key matched by account if the passphrase is correct. +// If a contains no filename, the address must match a unique key. +func (am *AccountManager) DeleteAccount(a *Account, passphrase string) error { + return am.manager.DeleteAccount(accounts.Account{ + Address: a.account.Address, + File: a.account.File, + }, passphrase) +} + +// Sign signs hash with an unlocked private key matching the given address. +func (am *AccountManager) Sign(addr *Address, hash []byte) ([]byte, error) { + return am.manager.Sign(addr.address, hash) +} + +// SignWithPassphrase signs hash if the private key matching the given address can be +// decrypted with the given passphrase. +func (am *AccountManager) SignWithPassphrase(addr *Address, passphrase string, hash []byte) ([]byte, error) { + return am.manager.SignWithPassphrase(addr.address, passphrase, hash) +} + +// Unlock unlocks the given account indefinitely. +func (am *AccountManager) Unlock(a *Account, passphrase string) error { + return am.manager.TimedUnlock(a.account, passphrase, 0) +} + +// Lock removes the private key with the given address from memory. +func (am *AccountManager) Lock(addr *Address) error { + return am.manager.Lock(addr.address) +} + +// TimedUnlock unlocks the given account with the passphrase. The account +// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account +// until the program exits. The account must match a unique key file. +// +// If the account address is already unlocked for a duration, TimedUnlock extends or +// shortens the active unlock timeout. If the address was previously unlocked +// indefinitely the timeout is not altered. +func (am *AccountManager) TimedUnlock(a *Account, passphrase string, timeout int64) error { + return am.manager.TimedUnlock(a.account, passphrase, time.Duration(timeout)) +} + +// NewAccount generates a new key and stores it into the key directory, +// encrypting it with the passphrase. +func (am *AccountManager) NewAccount(passphrase string) (*Account, error) { + account, err := am.manager.NewAccount(passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} + +// ExportKey exports as a JSON key, encrypted with newPassphrase. +func (am *AccountManager) ExportKey(a *Account, passphrase, newPassphrase string) ([]byte, error) { + return am.manager.Export(a.account, passphrase, newPassphrase) +} + +// ImportKey stores the given encrypted JSON key into the key directory. +func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (*Account, error) { + account, err := am.manager.Import(keyJSON, passphrase, newPassphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} + +// Update changes the passphrase of an existing account. +func (am *AccountManager) Update(a *Account, passphrase, newPassphrase string) error { + return am.manager.Update(a.account, passphrase, newPassphrase) +} + +// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores +// a key file in the key directory. The key file is encrypted with the same passphrase. +func (am *AccountManager) ImportPreSaleKey(keyJSON []byte, passphrase string) (*Account, error) { + account, err := am.manager.ImportPreSaleKey(keyJSON, passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big.go b/vendor/github.com/ethereum/go-ethereum/mobile/big.go new file mode 100644 index 000000000..6a358ba28 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big.go @@ -0,0 +1,95 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the math/big package. + +package geth + +import ( + "errors" + "math/big" +) + +// A BigInt represents a signed multi-precision integer. +type BigInt struct { + bigint *big.Int +} + +// NewBigInt allocates and returns a new BigInt set to x. +func NewBigInt(x int64) *BigInt { + return &BigInt{big.NewInt(x)} +} + +// GetBytes returns the absolute value of x as a big-endian byte slice. +func (bi *BigInt) GetBytes() []byte { + return bi.bigint.Bytes() +} + +// String returns the value of x as a formatted decimal string. +func (bi *BigInt) String() string { + return bi.bigint.String() +} + +// GetInt64 returns the int64 representation of x. If x cannot be represented in +// an int64, the result is undefined. +func (bi *BigInt) GetInt64() int64 { + return bi.bigint.Int64() +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets +// the big int to that value. +func (bi *BigInt) SetBytes(buf []byte) { + bi.bigint.SetBytes(buf) +} + +// SetInt64 sets the big int to x. +func (bi *BigInt) SetInt64(x int64) { + bi.bigint.SetInt64(x) +} + +// SetString sets the big int to x. +// +// The string prefix determines the actual conversion base. A prefix of "0x" or +// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix +// selects base 2. Otherwise the selected base is 10. +func (bi *BigInt) SetString(x string, base int) { + bi.bigint.SetString(x, base) +} + +// BigInts represents a slice of big ints. +type BigInts struct{ bigints []*big.Int } + +// Size returns the number of big ints in the slice. +func (bi *BigInts) Size() int { + return len(bi.bigints) +} + +// Get returns the bigint at the given index from the slice. +func (bi *BigInts) Get(index int) (*BigInt, error) { + if index < 0 || index >= len(bi.bigints) { + return nil, errors.New("index out of bounds") + } + return &BigInt{bi.bigints[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (bi *BigInts) Set(index int, bigint *BigInt) error { + if index < 0 || index >= len(bi.bigints) { + return errors.New("index out of bounds") + } + bi.bigints[index] = bigint.bigint + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go new file mode 100644 index 000000000..0447e1f66 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the wrappers from the math/big package that require Go 1.7 and above. + +// +build go1.7 + +package geth + +// GetString returns the value of x as a formatted string in some number base. +func (bi *BigInt) GetString(base int) string { + return bi.bigint.Text(base) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/bind.go b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go new file mode 100644 index 000000000..50adc6b0f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go @@ -0,0 +1,202 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the bind package. + +package geth + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Signer is an interaface defining the callback when a contract requires a +// method to sign the transaction before submission. +type Signer interface { + Sign(*Address, *Transaction) (*Transaction, error) +} + +type signer struct { + sign bind.SignerFn +} + +func (s *signer) Sign(addr *Address, tx *Transaction) (*Transaction, error) { + sig, err := s.sign(types.HomesteadSigner{}, addr.address, tx.tx) + if err != nil { + return nil, err + } + return &Transaction{sig}, nil +} + +// CallOpts is the collection of options to fine tune a contract call request. +type CallOpts struct { + opts bind.CallOpts +} + +// NewCallOpts creates a new option set for contract calls. +func NewCallOpts() *CallOpts { + return new(CallOpts) +} + +func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } +func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } +func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } +func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// TransactOpts is the collection of authorization data required to create a +// valid Ethereum transaction. +type TransactOpts struct { + opts bind.TransactOpts +} + +func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } +func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } +func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } +func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } +func (opts *TransactOpts) GetGasLimit() int64 { return opts.opts.GasLimit.Int64() } + +// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } +func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } +func (opts *TransactOpts) SetSigner(s Signer) { + opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { + sig, err := s.Sign(&Address{addr}, &Transaction{tx}) + if err != nil { + return nil, err + } + return sig.tx, nil + } +} +func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } +func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } +func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = big.NewInt(limit) } +func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// BoundContract is the base wrapper object that reflects a contract on the +// Ethereum network. It contains a collection of methods that are used by the +// higher level contract bindings to operate. +type BoundContract struct { + contract *bind.BoundContract + address common.Address + deployer *types.Transaction +} + +// DeployContract deploys a contract onto the Ethereum blockchain and binds the +// deployment address with a wrapper. +func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (*BoundContract, error) { + // Convert all the deployment parameters to Go types + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + // Deploy the contract to the network + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, bytecode, client.client, params...) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bound, + address: addr, + deployer: tx, + }, nil +} + +// BindContract creates a low level contract interface through which calls and +// transactions may be made through. +func BindContract(address *Address, abiJSON string, client *EthereumClient) (*BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bind.NewBoundContract(address.address, parsed, client.client, client.client), + address: address.address, + }, nil +} + +func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } +func (c *BoundContract) GetDeployer() *Transaction { + if c.deployer == nil { + return nil + } + return &Transaction{c.deployer} +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. +func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { + // Convert all the input and output parameters to Go types + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + results := make([]interface{}, len(out.objects)) + for i, obj := range out.objects { + results[i] = obj + } + // Execute the call to the contract and wrap any results + if err := c.contract.Call(&opts.opts, &results, method, params...); err != nil { + return err + } + for i, res := range results { + out.objects[i] = res + } + return nil +} + +// Transact invokes the (paid) contract method with params as input values. +func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (*Transaction, error) { + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + tx, err := c.contract.Transact(&opts.opts, method, params) + if err != nil { + return nil, err + } + return &Transaction{tx}, nil +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (c *BoundContract) Transfer(opts *TransactOpts) (*Transaction, error) { + tx, err := c.contract.Transfer(&opts.opts) + if err != nil { + return nil, err + } + return &Transaction{tx}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/common.go b/vendor/github.com/ethereum/go-ethereum/mobile/common.go new file mode 100644 index 000000000..ab1810bf1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/common.go @@ -0,0 +1,187 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the common package. + +package geth + +import ( + "encoding/hex" + "errors" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Hash represents the 32 byte Keccak256 hash of arbitrary data. +type Hash struct { + hash common.Hash +} + +// NewHashFromBytes converts a slice of bytes to a hash value. +func NewHashFromBytes(hash []byte) (*Hash, error) { + h := new(Hash) + if err := h.SetBytes(hash); err != nil { + return nil, err + } + return h, nil +} + +// NewHashFromHex converts a hex string to a hash value. +func NewHashFromHex(hash string) (*Hash, error) { + h := new(Hash) + if err := h.SetHex(hash); err != nil { + return nil, err + } + return h, nil +} + +// SetBytes sets the specified slice of bytes as the hash value. +func (h *Hash) SetBytes(hash []byte) error { + if length := len(hash); length != common.HashLength { + return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) + } + copy(h.hash[:], hash) + return nil +} + +// GetBytes retrieves the byte representation of the hash. +func (h *Hash) GetBytes() []byte { + return h.hash[:] +} + +// SetHex sets the specified hex string as the hash value. +func (h *Hash) SetHex(hash string) error { + hash = strings.ToLower(hash) + if len(hash) >= 2 && hash[:2] == "0x" { + hash = hash[2:] + } + if length := len(hash); length != 2*common.HashLength { + return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) + } + bin, err := hex.DecodeString(hash) + if err != nil { + return err + } + copy(h.hash[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the hash. +func (h *Hash) GetHex() string { + return h.hash.Hex() +} + +// Hashes represents a slice of hashes. +type Hashes struct{ hashes []common.Hash } + +// Size returns the number of hashes in the slice. +func (h *Hashes) Size() int { + return len(h.hashes) +} + +// Get returns the hash at the given index from the slice. +func (h *Hashes) Get(index int) (*Hash, error) { + if index < 0 || index >= len(h.hashes) { + return nil, errors.New("index out of bounds") + } + return &Hash{h.hashes[index]}, nil +} + +// Address represents the 20 byte address of an Ethereum account. +type Address struct { + address common.Address +} + +// NewAddressFromBytes converts a slice of bytes to a hash value. +func NewAddressFromBytes(address []byte) (*Address, error) { + a := new(Address) + if err := a.SetBytes(address); err != nil { + return nil, err + } + return a, nil +} + +// NewAddressFromHex converts a hex string to a address value. +func NewAddressFromHex(address string) (*Address, error) { + a := new(Address) + if err := a.SetHex(address); err != nil { + return nil, err + } + return a, nil +} + +// SetBytes sets the specified slice of bytes as the address value. +func (a *Address) SetBytes(address []byte) error { + if length := len(address); length != common.AddressLength { + return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) + } + copy(a.address[:], address) + return nil +} + +// GetBytes retrieves the byte representation of the address. +func (a *Address) GetBytes() []byte { + return a.address[:] +} + +// SetHex sets the specified hex string as the address value. +func (a *Address) SetHex(address string) error { + address = strings.ToLower(address) + if len(address) >= 2 && address[:2] == "0x" { + address = address[2:] + } + if length := len(address); length != 2*common.AddressLength { + return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) + } + bin, err := hex.DecodeString(address) + if err != nil { + return err + } + copy(a.address[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the address. +func (a *Address) GetHex() string { + return a.address.Hex() +} + +// Addresses represents a slice of addresses. +type Addresses struct{ addresses []common.Address } + +// Size returns the number of addresses in the slice. +func (a *Addresses) Size() int { + return len(a.addresses) +} + +// Get returns the address at the given index from the slice. +func (a *Addresses) Get(index int) (*Address, error) { + if index < 0 || index >= len(a.addresses) { + return nil, errors.New("index out of bounds") + } + return &Address{a.addresses[index]}, nil +} + +// Set sets the address at the given index in the slice. +func (a *Addresses) Set(index int, address *Address) error { + if index < 0 || index >= len(a.addresses) { + return errors.New("index out of bounds") + } + a.addresses[index] = address.address + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/context.go b/vendor/github.com/ethereum/go-ethereum/mobile/context.go new file mode 100644 index 000000000..9df94b689 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/context.go @@ -0,0 +1,81 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the golang.org/x/net/context package to support +// client side context management on mobile platforms. + +package geth + +import ( + "time" + + "golang.org/x/net/context" +) + +// Context carries a deadline, a cancelation signal, and other values across API +// boundaries. +type Context struct { + context context.Context + cancel context.CancelFunc +} + +// NewContext returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming requests. +func NewContext() *Context { + return &Context{ + context: context.Background(), + } +} + +// WithCancel returns a copy of the original context with cancellation mechanism +// included. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithCancel() *Context { + child, cancel := context.WithCancel(c.context) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithDeadline returns a copy of the original context with the deadline adjusted +// to be no later than the specified time. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithDeadline(sec int64, nsec int64) *Context { + child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithTimeout returns a copy of the original context with the deadline adjusted +// to be no later than now + the duration specified. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithTimeout(nsec int64) *Context { + child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/discover.go b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go new file mode 100644 index 000000000..9df2d04c3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go @@ -0,0 +1,104 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the accounts package to support client side enode +// management on mobile platforms. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p/discv5" +) + +// Enode represents a host on the network. +type Enode struct { + node *discv5.Node +} + +// NewEnode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func NewEnode(rawurl string) (*Enode, error) { + node, err := discv5.ParseNode(rawurl) + if err != nil { + return nil, err + } + return &Enode{node}, nil +} + +// Enodes represents a slice of accounts. +type Enodes struct{ nodes []*discv5.Node } + +// NewEnodes creates a slice of uninitialized enodes. +func NewEnodes(size int) *Enodes { + return &Enodes{ + nodes: make([]*discv5.Node, size), + } +} + +// NewEnodesEmpty creates an empty slice of Enode values. +func NewEnodesEmpty() *Enodes { + return NewEnodes(0) +} + +// Size returns the number of enodes in the slice. +func (e *Enodes) Size() int { + return len(e.nodes) +} + +// Get returns the enode at the given index from the slice. +func (e *Enodes) Get(index int) (*Enode, error) { + if index < 0 || index >= len(e.nodes) { + return nil, errors.New("index out of bounds") + } + return &Enode{e.nodes[index]}, nil +} + +// Set sets the enode at the given index in the slice. +func (e *Enodes) Set(index int, enode *Enode) error { + if index < 0 || index >= len(e.nodes) { + return errors.New("index out of bounds") + } + e.nodes[index] = enode.node + return nil +} + +// Append adds a new enode element to the end of the slice. +func (e *Enodes) Append(enode *Enode) { + e.nodes = append(e.nodes, enode.node) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/doc.go b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go new file mode 100644 index 000000000..50cc7f4f8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go @@ -0,0 +1,57 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package geth contains the simplified mobile APIs to go-ethereum. +// +// The scope of this package is *not* to allow writing a custom Ethereun client +// with pieces plucked from go-ethereum, rather to allow writing native dapps on +// mobile platforms. Keep this in mind when using or extending this package! +// +// API limitations +// +// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the +// exposed APIs need to be manually wrapped into simplified types, with custom +// constructors and getters/setters to ensure that they can be meaninfully used +// from Java/ObjC too. +// +// With this in mind, please try to limit the scope of this package and only add +// essentials without which mobile support cannot work, especially since manually +// syncing the code will be unwieldy otherwise. In the long term we might consider +// writing custom library generators, but those are out of scope now. +// +// Content wise each file in this package corresponds to an entire Go package +// from the go-ethereum repository. Please adhere to this scoping to prevent this +// package getting unmaintainable. +// +// Wrapping guidelines: +// +// Every type that is to be exposed should be wrapped into its own plain struct, +// which internally contains a single field: the original go-ethereum version. +// This is needed because gomobile cannot expose named types for now. +// +// Whenever a method argument or a return type is a custom struct, the pointer +// variant should always be used as value types crossing over between language +// boundaries might have strange behaviors. +// +// Slices of types should be converted into a single multiplicative type wrapping +// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations +// should not be provided to limit the remote code complexity. Arrays should be +// avoided as much as possible since they complicate bounds checking. +// +// Note, a panic *cannot* cross over language boundaries, instead will result in +// an undebuggable SEGFAULT in the process. For error handling only ever use error +// returns, which may be the only or the second return. +package geth diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go new file mode 100644 index 000000000..668d65e32 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go @@ -0,0 +1,305 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains a wrapper for the Ethereum client. + +package geth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethclient" +) + +// EthereumClient provides access to the Ethereum APIs. +type EthereumClient struct { + client *ethclient.Client +} + +// NewEthereumClient connects a client to the given URL. +func NewEthereumClient(rawurl string) (*EthereumClient, error) { + client, err := ethclient.Dial(rawurl) + return &EthereumClient{client}, err +} + +// GetBlockByHash returns the given full block. +func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (*Block, error) { + block, err := ec.client.BlockByHash(ctx.context, hash.hash) + return &Block{block}, err +} + +// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the +// latest known block is returned. +func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (*Block, error) { + if number < 0 { + block, err := ec.client.BlockByNumber(ctx.context, nil) + return &Block{block}, err + } + block, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) + return &Block{block}, err +} + +// GetHeaderByHash returns the block header with the given hash. +func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (*Header, error) { + header, err := ec.client.HeaderByHash(ctx.context, hash.hash) + return &Header{header}, err +} + +// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, +// the latest known header is returned. +func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (*Header, error) { + if number < 0 { + header, err := ec.client.HeaderByNumber(ctx.context, nil) + return &Header{header}, err + } + header, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) + return &Header{header}, err +} + +// GetTransactionByHash returns the transaction with the given hash. +func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (*Transaction, error) { + tx, err := ec.client.TransactionByHash(ctx.context, hash.hash) + return &Transaction{tx}, err +} + +// GetTransactionCount returns the total number of transactions in the given block. +func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (int, error) { + count, err := ec.client.TransactionCount(ctx.context, hash.hash) + return int(count), err +} + +// GetTransactionInBlock returns a single transaction at index in the given block. +func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (*Transaction, error) { + tx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) + return &Transaction{tx}, err + +} + +// GetTransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (*Receipt, error) { + receipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) + return &Receipt{receipt}, err +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *EthereumClient) SyncProgress(ctx *Context) (*SyncProgress, error) { + progress, err := ec.client.SyncProgress(ctx.context) + if progress == nil { + return nil, err + } + return &SyncProgress{*progress}, err +} + +// NewHeadHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type NewHeadHandler interface { + OnNewHead(header *Header) + OnError(failure string) +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (*Subscription, error) { + // Subscribe to the event internally + ch := make(chan *types.Header, buffer) + sub, err := ec.client.SubscribeNewHead(ctx.context, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case header := <-ch: + handler.OnNewHead(&Header{header}) + + case err := <-sub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{sub}, nil +} + +// State Access + +// GetBalanceAt returns the wei balance of the given account. +// The block number can be <0, in which case the balance is taken from the latest known block. +func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (*BigInt, error) { + if number < 0 { + balance, err := ec.client.BalanceAt(ctx.context, account.address, nil) + return &BigInt{balance}, err + } + balance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) + return &BigInt{balance}, err +} + +// GetStorageAt returns the value of key in the contract storage of the given account. +// The block number can be <0, in which case the value is taken from the latest known block. +func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) ([]byte, error) { + if number < 0 { + return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) + } + return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) +} + +// GetCodeAt returns the contract code of the given account. +// The block number can be <0, in which case the code is taken from the latest known block. +func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) ([]byte, error) { + if number < 0 { + return ec.client.CodeAt(ctx.context, account.address, nil) + } + return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) +} + +// GetNonceAt returns the account nonce of the given account. +// The block number can be <0, in which case the nonce is taken from the latest known block. +func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (int64, error) { + if number < 0 { + nonce, err := ec.client.NonceAt(ctx.context, account.address, nil) + return int64(nonce), err + } + nonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) + return int64(nonce), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (*Logs, error) { + logs, err := ec.client.FilterLogs(ctx.context, query.query) + if err != nil { + return nil, err + } + // Temp hack due to vm.Logs being []*vm.Log + res := make(vm.Logs, len(logs)) + for i, log := range logs { + res[i] = &log + } + return &Logs{res}, nil +} + +// FilterLogsHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type FilterLogsHandler interface { + OnFilterLogs(log *Log) + OnError(failure string) +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (*Subscription, error) { + // Subscribe to the event internally + ch := make(chan vm.Log, buffer) + sub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case log := <-ch: + handler.OnFilterLogs(&Log{&log}) + + case err := <-sub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{sub}, nil +} + +// Pending State + +// GetPendingBalanceAt returns the wei balance of the given account in the pending state. +func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (*BigInt, error) { + balance, err := ec.client.PendingBalanceAt(ctx.context, account.address) + return &BigInt{balance}, err +} + +// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. +func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) ([]byte, error) { + return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) +} + +// GetPendingCodeAt returns the contract code of the given account in the pending state. +func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) ([]byte, error) { + return ec.client.PendingCodeAt(ctx.context, account.address) +} + +// GetPendingNonceAt returns the account nonce of the given account in the pending state. +// This is the nonce that should be used for the next transaction. +func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (int64, error) { + nonce, err := ec.client.PendingNonceAt(ctx.context, account.address) + return int64(nonce), err +} + +// GetPendingTransactionCount returns the total number of transactions in the pending state. +func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (int, error) { + count, err := ec.client.PendingTransactionCount(ctx.context) + return int(count), err +} + +// Contract Calling + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be <0, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) ([]byte, error) { + if number < 0 { + return ec.client.CallContract(ctx.context, msg.msg, nil) + } + return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) +} + +// PendingCallContract executes a message call transaction using the EVM. +// The state seen by the contract call is the pending state. +func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) ([]byte, error) { + return ec.client.PendingCallContract(ctx.context, msg.msg) +} + +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (*BigInt, error) { + price, err := ec.client.SuggestGasPrice(ctx.context) + return &BigInt{price}, err +} + +// EstimateGas tries to estimate the gas needed to execute a specific transaction based on +// the current pending state of the backend blockchain. There is no guarantee that this is +// the true gas limit requirement as other transactions may be added or removed by miners, +// but it should provide a basis for setting a reasonable default. +func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (*BigInt, error) { + price, err := ec.client.EstimateGas(ctx.context, msg.msg) + return &BigInt{price}, err +} + +// SendTransaction injects a signed transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { + return ec.client.SendTransaction(ctx.context, tx.tx) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go new file mode 100644 index 000000000..6e8046ac9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go @@ -0,0 +1,125 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the go-ethereum root package. + +package geth + +import ( + "errors" + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" +) + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription struct { + sub ethereum.Subscription +} + +// Unsubscribe cancels the sending of events to the data channel +// and closes the error channel. +func (s *Subscription) Unsubscribe() { + s.sub.Unsubscribe() +} + +// CallMsg contains parameters for contract calls. +type CallMsg struct { + msg ethereum.CallMsg +} + +// NewCallMsg creates an empty contract call parameter list. +func NewCallMsg() *CallMsg { + return new(CallMsg) +} + +func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } +func (msg *CallMsg) GetGas() int64 { return msg.msg.Gas.Int64() } +func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } +func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } +func (msg *CallMsg) GetData() []byte { return msg.msg.Data } +func (msg *CallMsg) GetTo() *Address { + if to := msg.msg.To; to != nil { + return &Address{*msg.msg.To} + } + return nil +} + +func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } +func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = big.NewInt(gas) } +func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } +func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } +func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = data } +func (msg *CallMsg) SetTo(address *Address) { + if address == nil { + msg.msg.To = nil + } + msg.msg.To = &address.address +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + progress ethereum.SyncProgress +} + +func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } +func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } +func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } +func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } +func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } + +// Topics is a set of topic lists to filter events with. +type Topics struct{ topics [][]common.Hash } + +// Size returns the number of topic lists inside the set +func (t *Topics) Size() int { + return len(t.topics) +} + +// Get returns the topic list at the given index from the slice. +func (t *Topics) Get(index int) (*Hashes, error) { + if index < 0 || index >= len(t.topics) { + return nil, errors.New("index out of bounds") + } + return &Hashes{t.topics[index]}, nil +} + +// Set sets the topic list at the given index in the slice. +func (t *Topics) Set(index int, topics *Hashes) error { + if index < 0 || index >= len(t.topics) { + return errors.New("index out of bounds") + } + t.topics[index] = topics.hashes + return nil +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + query ethereum.FilterQuery +} + +// NewFilterQuery creates an empty filter query for contact log filtering. +func NewFilterQuery() *FilterQuery { + return new(FilterQuery) +} + +func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } +func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } +func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } +func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go new file mode 100644 index 000000000..738c0c548 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go @@ -0,0 +1,191 @@ +// 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 . + +// Contains all the wrappers from the node package to support client side node +// management on mobile platforms. + +package geth + +import ( + "fmt" + "math/big" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/whisper/whisperv2" +) + +// NodeConfig represents the collection of configuration values to fine tune the Geth +// node embedded into a mobile process. The available values are a subset of the +// entire API provided by go-ethereum to reduce the maintenance surface and dev +// complexity. +type NodeConfig struct { + // Bootstrap nodes used to establish connectivity with the rest of the network. + BootstrapNodes *Enodes + + // MaxPeers is the maximum number of peers that can be connected. If this is + // set to zero, then only the configured static and trusted peers can connect. + MaxPeers int + + // EthereumEnabled specifies whether the node should run the Ethereum protocol. + EthereumEnabled bool + + // EthereumNetworkID is the network identifier used by the Ethereum protocol to + // decide if remote peers should be accepted or not. + EthereumNetworkID int + + // EthereumChainConfig is the default parameters of the blockchain to use. If no + // configuration is specified, it defaults to the main network. + EthereumChainConfig *ChainConfig + + // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An + // empty genesis state is equivalent to using the mainnet's state. + EthereumGenesis string + + // EthereumDatabaseCache is the system memory in MB to allocate for database caching. + // A minimum of 16MB is always reserved. + EthereumDatabaseCache int + + // WhisperEnabled specifies whether the node should run the Whisper protocol. + WhisperEnabled bool +} + +// defaultNodeConfig contains the default node configuration values to use if all +// or some fields are missing from the user's specified list. +var defaultNodeConfig = &NodeConfig{ + BootstrapNodes: FoundationBootnodes(), + MaxPeers: 25, + EthereumEnabled: true, + EthereumNetworkID: 1, + EthereumChainConfig: MainnetChainConfig(), + EthereumDatabaseCache: 16, +} + +// NewNodeConfig creates a new node option set, initialized to the default values. +func NewNodeConfig() *NodeConfig { + config := *defaultNodeConfig + return &config +} + +// Node represents a Geth Ethereum node instance. +type Node struct { + node *node.Node +} + +// NewNode creates and configures a new Geth node. +func NewNode(datadir string, config *NodeConfig) (*Node, error) { + // If no or partial configurations were specified, use defaults + if config == nil { + config = NewNodeConfig() + } + if config.MaxPeers == 0 { + config.MaxPeers = defaultNodeConfig.MaxPeers + } + if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { + config.BootstrapNodes = defaultNodeConfig.BootstrapNodes + } + // Create the empty networking stack + nodeConf := &node.Config{ + Name: clientIdentifier, + DataDir: datadir, + KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! + NoDiscovery: true, + DiscoveryV5: true, + DiscoveryV5Addr: ":0", + BootstrapNodesV5: config.BootstrapNodes.nodes, + ListenAddr: ":0", + NAT: nat.Any(), + MaxPeers: config.MaxPeers, + } + stack, err := node.New(nodeConf) + if err != nil { + return nil, err + } + // Register the Ethereum protocol if requested + if config.EthereumEnabled { + ethConf := ð.Config{ + ChainConfig: ¶ms.ChainConfig{ + ChainId: big.NewInt(config.EthereumChainConfig.ChainID), + HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock), + DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock), + DAOForkSupport: config.EthereumChainConfig.DAOForkSupport, + EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block), + EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash, + EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block), + EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block), + }, + Genesis: config.EthereumGenesis, + LightMode: true, + DatabaseCache: config.EthereumDatabaseCache, + NetworkId: config.EthereumNetworkID, + GasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMinGasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMaxGasPrice: new(big.Int).Mul(big.NewInt(500), common.Shannon), + GpoFullBlockRatio: 80, + GpobaseStepDown: 10, + GpobaseStepUp: 100, + GpobaseCorrectionFactor: 110, + } + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, ethConf) + }); err != nil { + return nil, fmt.Errorf("ethereum init: %v", err) + } + } + // Register the Whisper protocol if requested + if config.WhisperEnabled { + if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil { + return nil, fmt.Errorf("whisper init: %v", err) + } + } + return &Node{stack}, nil +} + +// Start creates a live P2P node and starts running it. +func (n *Node) Start() error { + return n.node.Start() +} + +// Stop terminates a running node along with all it's services. In the node was +// not started, an error is returned. +func (n *Node) Stop() error { + return n.node.Stop() +} + +// GetEthereumClient retrieves a client to access the Ethereum subsystem. +func (n *Node) GetEthereumClient() (*EthereumClient, error) { + rpc, err := n.node.Attach() + if err != nil { + return nil, err + } + return &EthereumClient{ethclient.NewClient(rpc)}, nil +} + +// GetNodeInfo gathers and returns a collection of metadata known about the host. +func (n *Node) GetNodeInfo() *NodeInfo { + return &NodeInfo{n.node.Server().NodeInfo()} +} + +// GetPeersInfo returns an array of metadata objects describing connected peers. +func (n *Node) GetPeersInfo() *PeerInfos { + return &PeerInfos{n.node.Server().PeersInfo()} +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go new file mode 100644 index 000000000..8e4ebe638 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build android + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethDroid" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go new file mode 100644 index 000000000..307cd0858 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build ios + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "iGeth" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go new file mode 100644 index 000000000..6f0c5dda6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !android,!ios + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethMobile" diff --git a/vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go b/vendor/github.com/ethereum/go-ethereum/mobile/init.go similarity index 55% rename from vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go rename to vendor/github.com/ethereum/go-ethereum/mobile/init.go index 3469d394e..0fbc6bd3e 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go +++ b/vendor/github.com/ethereum/go-ethereum/mobile/init.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -14,41 +14,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build !opencl +// Contains initialization code for the mbile library. -package eth +package geth import ( - "errors" - "fmt" + "runtime" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) -const disabledInfo = "Set GO_OPENCL and re-build to enable." +func init() { + // Initialize the logger + glog.SetV(logger.Info) + glog.SetToStderr(true) -func (s *Ethereum) StartMining(threads int, gpus string) error { - eb, err := s.Etherbase() - if err != nil { - err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) - glog.V(logger.Error).Infoln(err) - return err - } - - if gpus != "" { - return errors.New("GPU mining disabled. " + disabledInfo) - } - - // CPU mining - go s.miner.Start(eb, threads) - return nil -} - -func GPUBench(gpuid uint64) { - fmt.Println("GPU mining disabled. " + disabledInfo) -} - -func PrintOpenCLDevices() { - fmt.Println("OpenCL disabled. " + disabledInfo) + // Initialize the goroutine count + runtime.GOMAXPROCS(runtime.NumCPU()) } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/interface.go b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go new file mode 100644 index 000000000..b585b8642 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go @@ -0,0 +1,148 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains perverted wrappers to allow crossing over empty interfaces. + +package geth + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Interface represents a wrapped version of Go's interface{}, with the capacity +// to store arbitrary data types. +// +// Since it's impossible to get the arbitrary-ness converted between Go and mobile +// platforms, we're using explicit getters and setters for the conversions. There +// is of course no point in enumerating everything, just enough to support the +// contract bindins requiring client side generated code. +type Interface struct { + object interface{} +} + +// NewInterface creates a new empty interface that can be used to pass around +// generic types. +func NewInterface() *Interface { + return new(Interface) +} + +func (i *Interface) SetBool(b bool) { i.object = &b } +func (i *Interface) SetBools(bs []bool) { i.object = &bs } +func (i *Interface) SetString(str string) { i.object = &str } +func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } +func (i *Interface) SetBinary(binary []byte) { i.object = &binary } +func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries } +func (i *Interface) SetAddress(address *Address) { i.object = &address.address } +func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } +func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } +func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } +func (i *Interface) SetInt8(n int8) { i.object = &n } +func (i *Interface) SetInt16(n int16) { i.object = &n } +func (i *Interface) SetInt32(n int32) { i.object = &n } +func (i *Interface) SetInt64(n int64) { i.object = &n } +func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint64(bigint *BigInt) { n := uint64(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } +func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } + +func (i *Interface) SetDefaultBool() { i.object = new(bool) } +func (i *Interface) SetDefaultBools() { i.object = new([]bool) } +func (i *Interface) SetDefaultString() { i.object = new(string) } +func (i *Interface) SetDefaultStrings() { i.object = new([]string) } +func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } +func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } +func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } +func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } +func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } +func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } +func (i *Interface) SetDefaultInt8() { i.object = new(int8) } +func (i *Interface) SetDefaultInt16() { i.object = new(int16) } +func (i *Interface) SetDefaultInt32() { i.object = new(int32) } +func (i *Interface) SetDefaultInt64() { i.object = new(int64) } +func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } +func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } +func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } +func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } +func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } +func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } + +func (i *Interface) GetBool() bool { return *i.object.(*bool) } +func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) } +func (i *Interface) GetString() string { return *i.object.(*string) } +func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } +func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } +func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) } +func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } +func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } +func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } +func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } +func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } +func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } +func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } +func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } +func (i *Interface) GetUint8() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} +} +func (i *Interface) GetUint16() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} +} +func (i *Interface) GetUint32() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} +} +func (i *Interface) GetUint64() *BigInt { + return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} +} +func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } +func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } + +// Interfaces is a slices of wrapped generic objects. +type Interfaces struct { + objects []interface{} +} + +// NewInterfaces creates a slice of uninitialized interfaces. +func NewInterfaces(size int) *Interfaces { + return &Interfaces{ + objects: make([]interface{}, size), + } +} + +// Size returns the number of interfaces in the slice. +func (i *Interfaces) Size() int { + return len(i.objects) +} + +// Get returns the bigint at the given index from the slice. +func (i *Interfaces) Get(index int) (*Interface, error) { + if index < 0 || index >= len(i.objects) { + return nil, errors.New("index out of bounds") + } + return &Interface{i.objects[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (i *Interfaces) Set(index int, object *Interface) error { + if index < 0 || index >= len(i.objects) { + return errors.New("index out of bounds") + } + i.objects[index] = object.object + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go new file mode 100644 index 000000000..97ae626dc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received pi copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains wrappers for the p2p package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p" +) + +// NodeInfo represents pi short summary of the information known about the host. +type NodeInfo struct { + info *p2p.NodeInfo +} + +func (ni *NodeInfo) GetID() string { return ni.info.ID } +func (ni *NodeInfo) GetName() string { return ni.info.Name } +func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } +func (ni *NodeInfo) GetIP() string { return ni.info.IP } +func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } +func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } +func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } +func (ni *NodeInfo) GetProtocols() *Strings { + protos := []string{} + for proto, _ := range ni.info.Protocols { + protos = append(protos, proto) + } + return &Strings{protos} +} + +// PeerInfo represents pi short summary of the information known about pi connected peer. +type PeerInfo struct { + info *p2p.PeerInfo +} + +func (pi *PeerInfo) GetID() string { return pi.info.ID } +func (pi *PeerInfo) GetName() string { return pi.info.Name } +func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } +func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } +func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } + +// PeerInfos represents a slice of infos about remote peers. +type PeerInfos struct { + infos []*p2p.PeerInfo +} + +// Size returns the number of peer info entries in the slice. +func (pi *PeerInfos) Size() int { + return len(pi.infos) +} + +// Get returns the peer info at the given index from the slice. +func (pi *PeerInfos) Get(index int) (*PeerInfo, error) { + if index < 0 || index >= len(pi.infos) { + return nil, errors.New("index out of bounds") + } + return &PeerInfo{pi.infos[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/params.go b/vendor/github.com/ethereum/go-ethereum/mobile/params.go new file mode 100644 index 000000000..507c15349 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/params.go @@ -0,0 +1,92 @@ +// 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 . + +// Contains all the wrappers from the params package. + +package geth + +import ( + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" +) + +// MainnetChainConfig returns the chain configurations for the main Ethereum network. +func MainnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.MainNetChainID.Int64(), + HomesteadBlock: params.MainNetHomesteadBlock.Int64(), + DAOForkBlock: params.MainNetDAOForkBlock.Int64(), + DAOForkSupport: true, + EIP150Block: params.MainNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash}, + EIP155Block: params.MainNetSpuriousDragon.Int64(), + EIP158Block: params.MainNetSpuriousDragon.Int64(), + } +} + +// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It +// is actually empty since that defaults to the hard coded binary genesis block. +func MainnetGenesis() string { + return "" +} + +// TestnetChainConfig returns the chain configurations for the Ethereum test network. +func TestnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.TestNetChainID.Int64(), + HomesteadBlock: params.TestNetHomesteadBlock.Int64(), + DAOForkBlock: 0, + DAOForkSupport: true, + EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash}, + EIP155Block: params.TestNetSpuriousDragon.Int64(), + EIP158Block: params.TestNetSpuriousDragon.Int64(), + } +} + +// TestnetGenesis returns the JSON spec to use for the Ethereum test network. +func TestnetGenesis() string { + return core.DefaultTestnetGenesisBlock() +} + +// ChainConfig is the core config which determines the blockchain settings. +type ChainConfig struct { + ChainID int64 // Chain ID for replay protection + HomesteadBlock int64 // Homestead switch block + DAOForkBlock int64 // TheDAO hard-fork switch block + DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork + EIP150Block int64 // Homestead gas reprice switch block + EIP150Hash Hash // Homestead gas reprice switch block hash + EIP155Block int64 // Replay protection switch block + EIP158Block int64 // Empty account pruning switch block +} + +// NewChainConfig creates a new chain configuration that transitions immediately +// to homestead and has no notion of the DAO fork (ideal for a private network). +func NewChainConfig() *ChainConfig { + return new(ChainConfig) +} + +// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated +// by the foundation running the V5 discovery protocol. +func FoundationBootnodes() *Enodes { + nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))} + for i, node := range params.DiscoveryV5Bootnodes { + nodes.nodes[i] = node + } + return nodes +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go new file mode 100644 index 000000000..28f402d4f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go @@ -0,0 +1,54 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received s copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains various wrappers for primitive types. + +package geth + +import ( + "errors" + "fmt" +) + +// Strings represents s slice of strs. +type Strings struct{ strs []string } + +// Size returns the number of strs in the slice. +func (s *Strings) Size() int { + return len(s.strs) +} + +// Get returns the string at the given index from the slice. +func (s *Strings) Get(index int) (string, error) { + if index < 0 || index >= len(s.strs) { + return "", errors.New("index out of bounds") + } + return s.strs[index], nil +} + +// Set sets the string at the given index in the slice. +func (s *Strings) Set(index int, str string) error { + if index < 0 || index >= len(s.strs) { + return errors.New("index out of bounds") + } + s.strs[index] = str + return nil +} + +// String implements the Stringer interface. +func (s *Strings) String() string { + return fmt.Sprintf("%v", s.strs) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/types.go b/vendor/github.com/ethereum/go-ethereum/mobile/types.go new file mode 100644 index 000000000..bb5ccc625 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/types.go @@ -0,0 +1,189 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core/types" +) + +// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that +// a sufficient amount of computation has been carried out on a block. +type Nonce struct { + nonce types.BlockNonce +} + +// GetBytes retrieves the byte representation of the block nonce. +func (n *Nonce) GetBytes() []byte { + return n.nonce[:] +} + +// GetHex retrieves the hex string representation of the block nonce. +func (n *Nonce) GetHex() string { + return fmt.Sprintf("0x%x", n.nonce[:]) +} + +// Bloom represents a 256 bit bloom filter. +type Bloom struct { + bloom types.Bloom +} + +// GetBytes retrieves the byte representation of the bloom filter. +func (b *Bloom) GetBytes() []byte { + return b.bloom[:] +} + +// GetHex retrieves the hex string representation of the bloom filter. +func (b *Bloom) GetHex() string { + return fmt.Sprintf("0x%x", b.bloom[:]) +} + +// Header represents a block header in the Ethereum blockchain. +type Header struct { + header *types.Header +} + +func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } +func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } +func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } +func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } +func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } +func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } +func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } +func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } +func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } +func (h *Header) GetGasLimit() int64 { return h.header.GasLimit.Int64() } +func (h *Header) GetGasUsed() int64 { return h.header.GasUsed.Int64() } +func (h *Header) GetTime() int64 { return h.header.Time.Int64() } +func (h *Header) GetExtra() []byte { return h.header.Extra } +func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } +func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } + +func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } +func (h *Header) GetHashNoNonce() *Hash { return &Hash{h.header.HashNoNonce()} } + +// Headers represents a slice of headers. +type Headers struct{ headers []*types.Header } + +// Size returns the number of headers in the slice. +func (h *Headers) Size() int { + return len(h.headers) +} + +// Get returns the header at the given index from the slice. +func (h *Headers) Get(index int) (*Header, error) { + if index < 0 || index >= len(h.headers) { + return nil, errors.New("index out of bounds") + } + return &Header{h.headers[index]}, nil +} + +// Block represents an entire block in the Ethereum blockchain. +type Block struct { + block *types.Block +} + +func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } +func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } +func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } +func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } +func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } +func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } +func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } +func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } +func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } +func (b *Block) GetGasLimit() int64 { return b.block.GasLimit().Int64() } +func (b *Block) GetGasUsed() int64 { return b.block.GasUsed().Int64() } +func (b *Block) GetTime() int64 { return b.block.Time().Int64() } +func (b *Block) GetExtra() []byte { return b.block.Extra() } +func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } +func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } + +func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } +func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} } + +func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } +func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } +func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } +func (b *Block) GetTransaction(hash *Hash) *Transaction { + return &Transaction{b.block.Transaction(hash.hash)} +} + +// Transaction represents a single Ethereum transaction. +type Transaction struct { + tx *types.Transaction +} + +func (tx *Transaction) GetData() []byte { return tx.tx.Data() } +func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() } +func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } +func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } +func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } + +func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } +func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.HomesteadSigner{})} } +func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } + +func (tx *Transaction) GetFrom() (*Address, error) { + from, err := types.Sender(types.HomesteadSigner{}, tx.tx) + return &Address{from}, err +} + +func (tx *Transaction) GetTo() *Address { + if to := tx.tx.To(); to != nil { + return &Address{*to} + } + return nil +} + +func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { + t, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig) + return &Transaction{t}, err +} + +// Transactions represents a slice of transactions. +type Transactions struct{ txs types.Transactions } + +// Size returns the number of transactions in the slice. +func (t *Transactions) Size() int { + return len(t.txs) +} + +// Get returns the transaction at the given index from the slice. +func (t *Transactions) Get(index int) (*Transaction, error) { + if index < 0 || index >= len(t.txs) { + return nil, errors.New("index out of bounds") + } + return &Transaction{t.txs[index]}, nil +} + +// Receipt represents the results of a transaction. +type Receipt struct { + receipt *types.Receipt +} + +func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } +func (r *Receipt) GetCumulativeGasUsed() *BigInt { return &BigInt{r.receipt.CumulativeGasUsed} } +func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } +func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } +func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } +func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } +func (r *Receipt) GetGasUsed() *BigInt { return &BigInt{r.receipt.GasUsed} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/vm.go b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go new file mode 100644 index 000000000..a68917ca6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go @@ -0,0 +1,56 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core/vm" +) + +// Log represents a contract log event. These events are generated by the LOG +// opcode and stored/indexed by the node. +type Log struct { + log *vm.Log +} + +func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } +func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } +func (l *Log) GetData() []byte { return l.log.Data } +func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } +func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } +func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } +func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } +func (l *Log) GetIndex() int { return int(l.log.Index) } + +// Logs represents a slice of VM logs. +type Logs struct{ logs vm.Logs } + +// Size returns the number of logs in the slice. +func (l *Logs) Size() int { + return len(l.logs) +} + +// Get returns the log at the given index from the slice. +func (l *Logs) Get(index int) (*Log, error) { + if index < 0 || index >= len(l.logs) { + return nil, errors.New("index out of bounds") + } + return &Log{l.logs[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/config.go b/vendor/github.com/ethereum/go-ethereum/node/config.go index 8af9215a0..8d85b7ff8 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/config.go +++ b/vendor/github.com/ethereum/go-ethereum/node/config.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -95,16 +96,23 @@ type Config struct { // or not. Disabling is usually useful for protocol debugging (manual topology). NoDiscovery bool + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. DiscoveryV5 bool - // Bootstrap nodes used to establish connectivity with the rest of the network. + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + + // BootstrapNodes used to establish connectivity with the rest of the network. BootstrapNodes []*discover.Node + // BootstrapNodesV5 used to establish connectivity with the rest of the network + // using the V5 discovery protocol. + BootstrapNodesV5 []*discv5.Node + // Network interface address on which the node should listen for inbound peers. ListenAddr string - ListenAddrV5 string - // If set to a non-nil value, the given NAT port mapper is used to make the // listening port available to the Internet. NAT nat.Interface @@ -268,7 +276,7 @@ func (c *Config) name() string { return c.Name } -// These resources are resolved differently for the "geth" and "geth-testnet" instances. +// These resources are resolved differently for "geth" instances. var isOldGethResource = map[string]bool{ "chaindata": true, "nodes": true, @@ -297,7 +305,14 @@ func (c *Config) resolvePath(path string) string { return oldpath } } - return filepath.Join(c.DataDir, c.name(), path) + return filepath.Join(c.instanceDir(), path) +} + +func (c *Config) instanceDir() string { + if c.DataDir == "" { + return "" + } + return filepath.Join(c.DataDir, c.name()) } // NodeKey retrieves the currently configured private key of the node, checking @@ -350,12 +365,11 @@ func (c *Config) TrusterNodes() []*discover.Node { // parsePersistentNodes parses a list of discovery node URLs loaded from a .json // file from within the data directory. -func (c *Config) parsePersistentNodes(file string) []*discover.Node { +func (c *Config) parsePersistentNodes(path string) []*discover.Node { // Short circuit if no node config is present if c.DataDir == "" { return nil } - path := filepath.Join(c.DataDir, file) if _, err := os.Stat(path); err != nil { return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/node/node.go b/vendor/github.com/ethereum/go-ethereum/node/node.go index 15f43fc6b..d49ae3a45 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/node.go +++ b/vendor/github.com/ethereum/go-ethereum/node/node.go @@ -154,21 +154,22 @@ func (n *Node) Start() error { // Initialize the p2p server. This creates the node key and // discovery databases. n.serverConfig = p2p.Config{ - PrivateKey: n.config.NodeKey(), - Name: n.config.NodeName(), - Discovery: !n.config.NoDiscovery, - DiscoveryV5: n.config.DiscoveryV5, - BootstrapNodes: n.config.BootstrapNodes, - StaticNodes: n.config.StaticNodes(), - TrustedNodes: n.config.TrusterNodes(), - NodeDatabase: n.config.NodeDB(), - ListenAddr: n.config.ListenAddr, - ListenAddrV5: n.config.ListenAddrV5, - NAT: n.config.NAT, - Dialer: n.config.Dialer, - NoDial: n.config.NoDial, - MaxPeers: n.config.MaxPeers, - MaxPendingPeers: n.config.MaxPendingPeers, + PrivateKey: n.config.NodeKey(), + Name: n.config.NodeName(), + Discovery: !n.config.NoDiscovery, + DiscoveryV5: n.config.DiscoveryV5, + DiscoveryV5Addr: n.config.DiscoveryV5Addr, + BootstrapNodes: n.config.BootstrapNodes, + BootstrapNodesV5: n.config.BootstrapNodesV5, + StaticNodes: n.config.StaticNodes(), + TrustedNodes: n.config.TrusterNodes(), + NodeDatabase: n.config.NodeDB(), + ListenAddr: n.config.ListenAddr, + NAT: n.config.NAT, + Dialer: n.config.Dialer, + NoDial: n.config.NoDial, + MaxPeers: n.config.MaxPeers, + MaxPendingPeers: n.config.MaxPendingPeers, } running := &p2p.Server{Config: n.serverConfig} glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name) @@ -601,10 +602,16 @@ func (n *Node) Service(service interface{}) error { } // DataDir retrieves the current datadir used by the protocol stack. +// Deprecated: No files should be stored in this directory, use InstanceDir instead. func (n *Node) DataDir() string { return n.config.DataDir } +// InstanceDir retrieves the instance directory used by the protocol stack. +func (n *Node) InstanceDir() string { + return n.config.instanceDir() +} + // AccountManager retrieves the account manager used by the protocol stack. func (n *Node) AccountManager() *accounts.Manager { return n.accman diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go index 139a95d80..eec0bae0c 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go @@ -35,7 +35,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/secp256k1" ) -const nodeIDBits = 512 +const NodeIDBits = 512 // Node represents a host on the network. // The fields of Node may not be modified. @@ -209,7 +209,7 @@ func MustParseNode(rawurl string) *Node { // NodeID is a unique identifier for each node. // The node identifier is a marshaled elliptic curve public key. -type NodeID [nodeIDBits / 8]byte +type NodeID [NodeIDBits / 8]byte // NodeID prints as a long hexadecimal number. func (n NodeID) String() string { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go index f68d03908..48b2a8a72 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go index f251f53d3..7ad6f1e5b 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" @@ -40,9 +41,10 @@ var ( ) const ( - autoRefreshInterval = 1 * time.Hour - seedCount = 30 - seedMaxAge = 5 * 24 * time.Hour + autoRefreshInterval = 1 * time.Hour + bucketRefreshInterval = 1 * time.Minute + seedCount = 30 + seedMaxAge = 5 * 24 * time.Hour ) const testTopic = "foo" @@ -58,16 +60,6 @@ func debugLog(s string) { } } -// BootNodes are the enode URLs of the P2P bootstrap nodes for the experimental RLPx v5 "Topic Discovery" network -// warning: local bootnodes for testing!!! -var BootNodes = []*Node{ - MustParseNode("enode://fcb7ff7a1437465711900bebc8831b6814d9f3176f3745e8549808af897ede4b51f8e32b52caf1a1c4aee90dc14f2aaeebf8b099f5d19245fd00ab160f59b9c8@127.0.0.1:30001"), - MustParseNode("enode://9ec7e836e3eff84c5f51b169d8bd26a90e9c0a1c5a5b01125722fbcc7808803451fdde5c31e0fbf32fa730e11bef3a4ec35c751bd896a3db601155c1313d48a2@127.0.0.1:30002"), - MustParseNode("enode://1949788c417a52653b438a33a2b08a70dc495b24c83d0ae533be13b1c7da0af0159bd0b50823fee4a1bc8f24f81e23435fe167df972256968d881f98f1eeb5de@127.0.0.1:30003"), - MustParseNode("enode://2a09e932145093688ce0fe885256dfac432402a5546c5b467afe988e205bbaf1a5793f4b625638ba5ba214549965aaacf4881201a82f92bfbaf37e40e7fe7132@127.0.0.1:30004"), - MustParseNode("enode://fc3d5a1696c48e02a45a78a6e94554f4292265de3c7461dd2b27ec664b09a23613c6f6be9fe7ab18f4788390abd592c272d549c583af6e92b5b8254f335d71bc@127.0.0.1:30005"), -} - // Network manages the table and all protocol interaction. type Network struct { db *nodeDB // database of known nodes @@ -83,6 +75,7 @@ type Network struct { tableOpReq chan func() tableOpResp chan struct{} topicRegisterReq chan topicRegisterReq + topicSearchReq chan topicSearchReq // State of the main loop. tab *Table @@ -111,7 +104,7 @@ type transport interface { sendTopicRegister(remote *Node, topics []Topic, topicIdx int, pong []byte) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) - send(remote *Node, ptype nodeEvent, p interface{}) + send(remote *Node, ptype nodeEvent, p interface{}) (hash []byte) localAddr() *net.UDPAddr Close() @@ -129,6 +122,11 @@ type topicRegisterReq struct { topic Topic } +type topicSearchReq struct { + topic Topic + found chan<- string +} + type timeoutEvent struct { ev nodeEvent node *Node @@ -163,6 +161,7 @@ func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, d tableOpResp: make(chan struct{}), queryReq: make(chan *findnodeQuery), topicRegisterReq: make(chan topicRegisterReq), + topicSearchReq: make(chan topicSearchReq), nodes: make(map[NodeID]*Node), } go net.loop() @@ -290,6 +289,22 @@ func (net *Network) RegisterTopic(topic Topic, stop <-chan struct{}) { } } +func (net *Network) SearchTopic(topic Topic, stop <-chan struct{}, found chan<- string) { + select { + case net.topicSearchReq <- topicSearchReq{topic, found}: + case <-net.closed: + return + } + select { + case <-net.closed: + case <-stop: + select { + case net.topicSearchReq <- topicSearchReq{topic, nil}: + case <-net.closed: + } + } +} + func (net *Network) reqRefresh(nursery []*Node) <-chan struct{} { select { case net.refreshReq <- nursery: @@ -330,8 +345,9 @@ func (net *Network) reqTableOp(f func()) (called bool) { func (net *Network) loop() { var ( - refreshTimer = time.NewTicker(autoRefreshInterval) - refreshDone chan struct{} // closed when the 'refresh' lookup has ended + refreshTimer = time.NewTicker(autoRefreshInterval) + bucketRefreshTimer = time.NewTimer(bucketRefreshInterval) + refreshDone chan struct{} // closed when the 'refresh' lookup has ended ) // Tracking the next ticket to register. @@ -360,12 +376,15 @@ func (net *Network) loop() { } } - // Tracking registration lookups. + // Tracking registration and search lookups. var ( topicRegisterLookupTarget lookupInfo topicRegisterLookupDone chan []*Node topicRegisterLookupTick = time.NewTimer(0) + topicSearchLookupTarget lookupInfo + searchReqWhenRefreshDone []topicSearchReq ) + topicSearchLookupDone := make(chan []*Node, 1) <-topicRegisterLookupTick.C statsDump := time.NewTicker(10 * time.Second) @@ -381,6 +400,7 @@ loop: // Ingress packet handling. case pkt := <-net.read: + //fmt.Println("read", pkt.ev) debugLog("<-net.read") n := net.internNode(&pkt) prestate := n.state @@ -474,8 +494,45 @@ loop: case <-nextRegisterTime: debugLog("<-nextRegisterTime") net.ticketStore.ticketRegistered(*nextTicket) + //fmt.Println("sendTopicRegister", nextTicket.t.node.addr().String(), nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) net.conn.sendTopicRegister(nextTicket.t.node, nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) + case req := <-net.topicSearchReq: + if refreshDone == nil { + debugLog("<-net.topicSearchReq") + if req.found == nil { + net.ticketStore.removeSearchTopic(req.topic) + continue + } + net.ticketStore.addSearchTopic(req.topic, req.found) + if (topicSearchLookupTarget.target == common.Hash{}) { + topicSearchLookupDone <- nil + } + } else { + searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req) + } + + case nodes := <-topicSearchLookupDone: + debugLog("<-topicSearchLookupDone") + net.ticketStore.searchLookupDone(topicSearchLookupTarget, nodes, func(n *Node) []byte { + net.ping(n, n.addr()) + return n.pingEcho + }, func(n *Node, topic Topic) []byte { + if n.state == known { + return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration + } else { + if n.state == unknown { + net.ping(n, n.addr()) + } + return nil + } + }) + topicSearchLookupTarget = net.ticketStore.nextSearchLookup() + target := topicSearchLookupTarget.target + if (target != common.Hash{}) { + go func() { topicSearchLookupDone <- net.lookup(target, false) }() + } + case <-statsDump.C: debugLog("<-statsDump.C") /*r, ok := net.ticketStore.radius[testTopic] @@ -488,14 +545,13 @@ loop: fmt.Printf("(%x) topics:%d radius:%d tickets:%d @ %v\n", net.tab.self.ID[:8], topics, rad, tickets, time.Now()) }*/ - tm := monotonicTime() + tm := mclock.Now() for topic, r := range net.ticketStore.radius { - if r.converged { + if printTestImgLogs { rad := r.radius / (maxRadius/1000000 + 1) - if printTestImgLogs { - fmt.Printf("*R %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], rad) - fmt.Printf("*MR %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], net.ticketStore.minRadius/(maxRadius/1000000+1)) - } + minrad := r.minRadius / (maxRadius/1000000 + 1) + fmt.Printf("*R %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], rad) + fmt.Printf("*MR %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], minrad) } } for topic, t := range net.topictab.topics { @@ -514,6 +570,12 @@ loop: refreshDone = make(chan struct{}) net.refresh(refreshDone) } + case <-bucketRefreshTimer.C: + target := net.tab.chooseBucketRefreshTarget() + go func() { + net.lookup(target, false) + bucketRefreshTimer.Reset(bucketRefreshInterval) + }() case newNursery := <-net.refreshReq: debugLog("<-net.refreshReq") if newNursery != nil { @@ -527,8 +589,14 @@ loop: case <-refreshDone: debugLog("<-net.refreshDone") refreshDone = nil + list := searchReqWhenRefreshDone + searchReqWhenRefreshDone = nil + go func() { + for _, req := range list { + net.topicSearchReq <- req + } + }() } - debugLog("3") } debugLog("loop stopped") @@ -595,6 +663,9 @@ func (net *Network) refresh(done chan<- struct{}) { func (net *Network) internNode(pkt *ingressPacket) *Node { if n := net.nodes[pkt.remoteID]; n != nil { + n.IP = pkt.remoteAddr.IP + n.UDP = uint16(pkt.remoteAddr.Port) + n.TCP = uint16(pkt.remoteAddr.Port) return n } n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port)) @@ -793,6 +864,9 @@ func init() { name: "verifywait", handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { switch ev { + case pingPacket: + net.handlePing(n, pkt) + return verifywait, nil case pongPacket: err := net.handleKnownPong(n, pkt) return known, err @@ -894,8 +968,10 @@ func init() { // handle processes packets sent by n and events related to n. func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error { + //fmt.Println("handle", n.addr().String(), n.state, ev) if pkt != nil { if err := net.checkPacket(n, ev, pkt); err != nil { + //fmt.Println("check err:", err) return err } // Start the background expiration goroutine after the first @@ -912,13 +988,14 @@ func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error { } next, err := n.state.handle(net, n, ev, pkt) net.transition(n, next) + //fmt.Println("new state:", n.state) return err } func (net *Network) checkPacket(n *Node, ev nodeEvent, pkt *ingressPacket) error { // Replay prevention checks. switch ev { - case pingPacket, findnodePacket, neighborsPacket: + case pingPacket, findnodeHashPacket, neighborsPacket: // TODO: check date is > last date seen // TODO: check ping version case pongPacket: @@ -967,6 +1044,11 @@ func (net *Network) abortTimedEvent(n *Node, ev nodeEvent) { } func (net *Network) ping(n *Node, addr *net.UDPAddr) { + //fmt.Println("ping", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.pingEcho != nil || n.ID == net.tab.self.ID { + //fmt.Println(" not sent") + return + } debugLog(fmt.Sprintf("ping(node = %x)", n.ID[:8])) n.pingTopics = net.ticketStore.regTopicSet() n.pingEcho = net.conn.sendPing(n, addr, n.pingTopics) @@ -974,12 +1056,13 @@ func (net *Network) ping(n *Node, addr *net.UDPAddr) { } func (net *Network) handlePing(n *Node, pkt *ingressPacket) { + debugLog(fmt.Sprintf("handlePing(node = %x)", n.ID[:8])) ping := pkt.data.(*ping) n.TCP = ping.From.TCP t := net.topictab.getTicket(n, ping.Topics) pong := &pong{ - To: makeEndpoint(n.addr(), 0), // TODO: maybe use known TCP port from DB + To: makeEndpoint(n.addr(), n.TCP), // TODO: maybe use known TCP port from DB ReplyTok: pkt.hash, Expiration: uint64(time.Now().Add(expiration).Unix()), } @@ -990,7 +1073,7 @@ func (net *Network) handlePing(n *Node, pkt *ingressPacket) { func (net *Network) handleKnownPong(n *Node, pkt *ingressPacket) error { debugLog(fmt.Sprintf("handleKnownPong(node = %x)", n.ID[:8])) net.abortTimedEvent(n, pongTimeout) - now := monotonicTime() + now := mclock.Now() ticket, err := pongToTicket(now, n.pingTopics, n, pkt) if err == nil { // fmt.Printf("(%x) ticket: %+v\n", net.tab.self.ID[:8], pkt.data) @@ -1039,10 +1122,15 @@ func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) //fmt.Println(err) return n.state, fmt.Errorf("bad waiting ticket: %v", err) } - net.topictab.useTicket(n, pong.TicketSerial, regdata.Topics, regdata.Idx, pong.Expiration, pong.WaitPeriods) + net.topictab.useTicket(n, pong.TicketSerial, regdata.Topics, int(regdata.Idx), pong.Expiration, pong.WaitPeriods) return n.state, nil case topicQueryPacket: - results := net.topictab.getEntries(pkt.data.(*topicQuery).Topic) + // TODO: handle expiration + topic := pkt.data.(*topicQuery).Topic + results := net.topictab.getEntries(topic) + if _, ok := net.ticketStore.tickets[topic]; ok { + results = append(results, net.tab.self) // we're not registering in our own table but if we're advertising, return ourselves too + } if len(results) > 10 { results = results[:10] } @@ -1051,18 +1139,13 @@ func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) net.conn.sendTopicNodes(n, hash, results) return n.state, nil case topicNodesPacket: - // if n.pendingTopicNodes != nil { - // n.pendingNeighbours.reply <- nil - // n.pendingNeighbours = nil - // } - // n.queryTimeouts++ - // if n.queryTimeouts > maxFindnodeFailures && n.state == known { - // return contested, errors.New("too many timeouts") - // } - // - // if n.pendingTopicNodes != nil { - // - // } + p := pkt.data.(*topicNodes) + if net.ticketStore.gotTopicNodes(n, p.Echo, p.Nodes) { + n.queryTimeouts++ + if n.queryTimeouts > maxFindnodeFailures && n.state == known { + return contested, errors.New("too many timeouts") + } + } return n.state, nil default: @@ -1072,7 +1155,6 @@ func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { var pongpkt ingressPacket - //fmt.Println("got", data.Topics, data.Pong) if err := decodePacket(data.Pong, &pongpkt); err != nil { return nil, err } @@ -1087,7 +1169,7 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { return nil, errors.New("topic hash mismatch") } - if data.Idx < 0 || data.Idx >= len(data.Topics) { + if data.Idx < 0 || int(data.Idx) >= len(data.Topics) { return nil, errors.New("topic index out of range") } return pongpkt.data.(*pong), nil diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go index f3f2797f2..2cf05009c 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -25,6 +25,7 @@ package discv5 import ( "crypto/rand" "encoding/binary" + "fmt" "net" "sort" @@ -64,6 +65,56 @@ func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table { return tab } +const printTable = false + +// chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia +// buckets filled with live connections and keep the network topology healthy. +// This requires selecting addresses closer to our own with a higher probability +// in order to refresh closer buckets too. +// +// This algorithm approximates the distance distribution of existing nodes in the +// table by selecting a random node from the table and selecting a target address +// with a distance less than twice of that of the selected node. +// This algorithm will be improved later to specifically target the least recently +// used buckets. +func (tab *Table) chooseBucketRefreshTarget() common.Hash { + entries := 0 + if printTable { + fmt.Println() + } + for i, b := range tab.buckets { + entries += len(b.entries) + if printTable { + for _, e := range b.entries { + fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex()) + } + } + } + + prefix := binary.BigEndian.Uint64(tab.self.sha[0:8]) + dist := ^uint64(0) + entry := int(randUint(uint32(entries + 1))) + for _, b := range tab.buckets { + if entry < len(b.entries) { + n := b.entries[entry] + dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix + break + } + entry -= len(b.entries) + } + + ddist := ^uint64(0) + if dist+dist > dist { + ddist = dist + } + targetPrefix := prefix ^ randUint64n(ddist) + + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], targetPrefix) + rand.Read(target[8:]) + return target +} + // readRandomNodes fills the given slice with random nodes from the // table. It will not write the same node more than once. The nodes in // the slice are copies and can be modified by the caller. @@ -101,7 +152,7 @@ func (tab *Table) readRandomNodes(buf []*Node) (n int) { } func randUint(max uint32) uint32 { - if max == 0 { + if max < 2 { return 0 } var b [4]byte @@ -109,6 +160,15 @@ func randUint(max uint32) uint32 { return binary.BigEndian.Uint32(b[:]) % max } +func randUint64n(max uint64) uint64 { + if max < 2 { + return 0 + } + var b [8]byte + rand.Read(b[:]) + return binary.BigEndian.Uint64(b[:]) % max +} + // closest returns the n nodes in the table that are closest to the // given id. The caller must hold tab.mutex. func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { @@ -128,6 +188,10 @@ func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { // bucket has space available, adding the node succeeds immediately. // Otherwise, the node is added to the replacement cache for the bucket. func (tab *Table) add(n *Node) (contested *Node) { + //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.ID == tab.self.ID { + return + } b := tab.buckets[logdist(tab.self.sha, n.sha)] switch { case b.bump(n): @@ -181,6 +245,7 @@ outer: // delete removes an entry from the node table (used to evacuate // failed/non-bonded discovery peers). func (tab *Table) delete(node *Node) { + //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex()) bucket := tab.buckets[logdist(tab.self.sha, node.sha)] for i := range bucket.entries { if bucket.entries[i].ID == node.ID { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/1 b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/1 deleted file mode 100644 index f2acfdc06..000000000 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/1 +++ /dev/null @@ -1,173 +0,0 @@ -package main - -import ( - "bufio" - "encoding/binary" - "fmt" - "image" - "image/png" - "os" - "sort" - "strconv" - - "github.com/ethereum/go-ethereum/crypto" -) - -var xs, ys, maxTime int - -func set(pic *image.NRGBA, x, y, c, v int) { - if v > 255 { - v = 255 - } - if x >= 0 && x < xs && y >= 0 && y < ys { - pic.Pix[y*pic.Stride+x*4+c] = uint8(v) - } -} - -func main() { - topicHash := crypto.Keccak256Hash([]byte("foo")) - fmt.Println(topicHash) - topicPrefix := binary.BigEndian.Uint64(topicHash[:8]) - var nodes uint64Slice - - inputFile := "test.out" - if len(os.Args) > 1 { - inputFile = os.Args[1] - } - - f, _ := os.Open(inputFile) - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - w := scanner.Text() - if w == "*N" { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - nodes = append(nodes, prefix^topicPrefix) - } - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(time) > maxTime { - maxTime = int(time) - } - } - } - f.Close() - sort.Sort(nodes) - nodeIdx := make(map[uint64]int) - for i, v := range nodes { - nodeIdx[v^topicPrefix] = i - } - - xs = maxTime / 10000 - ys = len(nodes) - - pic := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic, x, y, 3, 255) - } - } - - pic2 := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic2, x, y, 3, 255) - } - } - - f, _ = os.Open(inputFile) - scanner = bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - - nodeRad := make(map[uint64]int) - - for scanner.Scan() { - w := scanner.Text() - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(rad) != nodeRad[prefix] { - nodeRad[prefix] = int(rad) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 1, 255) - } - } - if w == "*MR" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 0, 255) - } - if w == "*W" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - wp, _ := strconv.ParseInt(scanner.Text(), 10, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic2, x, y, 0, int(wp/100000)) - set(pic2, x, y, 1, int(wp/10000)) - set(pic2, x, y, 2, int(wp/1000)) - } - if w == "*+" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic, x, y, 2, 255) - scanner.Scan() - } - } - f.Close() - - f, _ = os.Create("test.png") - w := bufio.NewWriter(f) - png.Encode(w, pic) - w.Flush() - f.Close() - - f, _ = os.Create("test2.png") - w = bufio.NewWriter(f) - png.Encode(w, pic2) - w.Flush() - f.Close() -} - -type uint64Slice []uint64 - -// Len is the number of elements in the collection. -func (s uint64Slice) Len() int { - return len(s) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (s uint64Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -// Swap swaps the elements with indexes i and j. -func (s uint64Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/2 b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/2 deleted file mode 100644 index 2638eff4f..000000000 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/2 +++ /dev/null @@ -1,178 +0,0 @@ -package main - -import ( - "bufio" - "encoding/binary" - "fmt" - "image" - "image/png" - "os" - "sort" - "strconv" - - "github.com/ethereum/go-ethereum/crypto" -) - -var xs, ys, maxTime int - -func set(pic *image.NRGBA, x, y, c, v int) { - if v > 255 { - v = 255 - } - if x >= 0 && x < xs && y >= 0 && y < ys { - pic.Pix[y*pic.Stride+x*4+c] = uint8(v) - } -} - -func main() { - topics := make(map[string]uint64) - var nodes uint64Slice - - inputFile := "test.out" - if len(os.Args) > 1 { - inputFile = os.Args[1] - } - - f, _ := os.Open(inputFile) - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - w := scanner.Text() - if w == "*N" { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - nodes = append(nodes, prefix^topicPrefix) - } - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(time) > maxTime { - maxTime = int(time) - } - scanner.Scan() - topic := scanner.Text() - if _, ok := topics[topic]; !ok { - fmt.Println(topic) - topicHash := crypto.Keccak256Hash([]byte(topic)) - topics[topic] := binary.BigEndian.Uint64(topicHash[:8]) - } - } - } - f.Close() - sort.Sort(nodes) - nodeIdx := make(map[uint64]int) - for i, v := range nodes { - nodeIdx[v^topicPrefix] = i - } - - xs = maxTime / 10000 - ys = len(nodes) - - pic := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic, x, y, 3, 255) - } - } - - pic2 := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic2, x, y, 3, 255) - } - } - - f, _ = os.Open(inputFile) - scanner = bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - - nodeRad := make(map[uint64]int) - - for scanner.Scan() { - w := scanner.Text() - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(rad) != nodeRad[prefix] { - nodeRad[prefix] = int(rad) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 1, 255) - } - } - if w == "*MR" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 0, 255) - } - if w == "*W" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - wp, _ := strconv.ParseInt(scanner.Text(), 10, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic2, x, y, 0, int(wp/100000)) - set(pic2, x, y, 1, int(wp/10000)) - set(pic2, x, y, 2, int(wp/1000)) - } - if w == "*+" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic, x, y, 2, 255) - scanner.Scan() - } - } - f.Close() - - f, _ = os.Create("test.png") - w := bufio.NewWriter(f) - png.Encode(w, pic) - w.Flush() - f.Close() - - f, _ = os.Create("test2.png") - w = bufio.NewWriter(f) - png.Encode(w, pic2) - w.Flush() - f.Close() -} - -type uint64Slice []uint64 - -// Len is the number of elements in the collection. -func (s uint64Slice) Len() int { - return len(s) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (s uint64Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -// Swap swaps the elements with indexes i and j. -func (s uint64Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.1 b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.1 deleted file mode 100644 index 32268aa97..000000000 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.1 +++ /dev/null @@ -1,191 +0,0 @@ -package main - -import ( - "bufio" - "encoding/binary" - "fmt" - "image" - "image/png" - "os" - "sort" - "strconv" - - "github.com/ethereum/go-ethereum/crypto" -) - -var xs, ys, maxTime int - -func set(pic *image.NRGBA, x, y, c, v int) { - if v > 255 { - v = 255 - } - if x >= 0 && x < xs && y >= 0 && y < ys { - pic.Pix[y*pic.Stride+x*4+c] = uint8(v) - } -} - -func main() { - var nodes uint64Slice - - inputFile := "test.out" - if len(os.Args) > 1 { - inputFile = os.Args[1] - } - - topic := "foo" - if len(os.Args) > 2 { - topic = os.Args[2] - } - - topicHash := crypto.Keccak256Hash([]byte(topic)) - fmt.Println(topicHash) - topicPrefix := binary.BigEndian.Uint64(topicHash[:8]) - - f, _ := os.Open(inputFile) - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - w := scanner.Text() - if w == "*N" { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - nodes = append(nodes, prefix^topicPrefix) - } - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(time) > maxTime { - maxTime = int(time) - } - } - } - f.Close() - sort.Sort(nodes) - nodeIdx := make(map[uint64]int) - for i, v := range nodes { - nodeIdx[v^topicPrefix] = i - } - - xs = maxTime / 10000 - ys = len(nodes) - - pic := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic, x, y, 3, 255) - } - } - - pic2 := image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(pic2, x, y, 3, 255) - } - } - - f, _ = os.Open(inputFile) - scanner = bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - - nodeRad := make(map[uint64]int) - - for scanner.Scan() { - w := scanner.Text() - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - if scanner.Text() == topic { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(rad) != nodeRad[prefix] { - nodeRad[prefix] = int(rad) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 1, 255) - } - } - } - if w == "*MR" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - if scanner.Text() == topic { - scanner.Scan() - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return nodes[i] > radUint - }) - set(pic, x, y, 0, 255) - } - } - if w == "*W" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - if scanner.Text() == topic { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - wp, _ := strconv.ParseInt(scanner.Text(), 10, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic2, x, y, 0, int(wp/100000)) - set(pic2, x, y, 1, int(wp/10000)) - set(pic2, x, y, 2, int(wp/1000)) - } - } - if w == "*+" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - if scanner.Text() == topic { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := nodeIdx[prefix] - set(pic, x, y, 2, 255) - scanner.Scan() - } - } - } - f.Close() - - f, _ = os.Create("test.png") - w := bufio.NewWriter(f) - png.Encode(w, pic) - w.Flush() - f.Close() - - f, _ = os.Create("test2.png") - w = bufio.NewWriter(f) - png.Encode(w, pic2) - w.Flush() - f.Close() -} - -type uint64Slice []uint64 - -// Len is the number of elements in the collection. -func (s uint64Slice) Len() int { - return len(s) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (s uint64Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -// Swap swaps the elements with indexes i and j. -func (s uint64Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.go deleted file mode 100644 index fb7bcafc7..000000000 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/testimg/testimg.go +++ /dev/null @@ -1,324 +0,0 @@ -package main - -import ( - "bufio" - "encoding/binary" - "fmt" - "image" - "image/png" - "os" - "sort" - "strconv" - - "github.com/ethereum/go-ethereum/crypto" -) - -var xs, ys, maxTime int - -func set(pic *image.NRGBA, x, y, c, v int) { - if v > 255 { - v = 255 - } - if x >= 0 && x < xs && y >= 0 && y < ys { - pic.Pix[y*pic.Stride+x*4+c] = uint8(v) - } -} - -type nodeStats []struct{ wpSum, wpCnt, wpXcnt, regCnt, regXcnt uint64 } - -type nodeInfo struct { - maxMR int - topics map[string]struct{} -} - -const ( - regStatDiv = 60 - regStatYdiv = 30 -) - -type topicInfo struct { - prefix uint64 - nodes uint64Slice - nodeStats nodeStats - nodeIdx map[uint64]int - pic, pic2 *image.NRGBA - nodeRad map[uint64]int - regStats []int -} - -func main() { - var nodes uint64Slice - topics := make(map[string]*topicInfo) - - inputFile := "test.out" - if len(os.Args) > 1 { - inputFile = os.Args[1] - } - - f, _ := os.Open(inputFile) - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - w := scanner.Text() - if w == "*N" { - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - nodes = append(nodes, prefix) - } - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(time) > maxTime { - maxTime = int(time) - } - scanner.Scan() - topic := scanner.Text() - if _, ok := topics[topic]; !ok { - fmt.Println(topic) - topicHash := crypto.Keccak256Hash([]byte(topic)) - topics[topic] = &topicInfo{prefix: binary.BigEndian.Uint64(topicHash[:8])} - } - } - } - f.Close() - - xs = maxTime / 10000 - ys = len(nodes) - nodeIdx := make(map[uint64]int) - for i, v := range nodes { - nodeIdx[v] = i - } - nodeInfo := make([]nodeInfo, len(nodes)) - - for _, t := range topics { - t.nodes = make(uint64Slice, len(nodes)) - t.nodeStats = make(nodeStats, len(nodes)) - for i, v := range nodes { - t.nodes[i] = v ^ t.prefix - } - sort.Sort(t.nodes) - t.nodeIdx = make(map[uint64]int) - for i, v := range t.nodes { - t.nodeIdx[v^t.prefix] = i - } - - t.pic = image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(t.pic, x, y, 3, 255) - } - } - - t.pic2 = image.NewNRGBA(image.Rect(0, 0, xs, ys)) - for y := 0; y < ys; y++ { - for x := 0; x < xs; x++ { - set(t.pic2, x, y, 3, 255) - } - } - t.nodeRad = make(map[uint64]int) - t.regStats = make([]int, xs/regStatDiv+1) - } - - f, _ = os.Open(inputFile) - scanner = bufio.NewScanner(f) - scanner.Split(bufio.ScanWords) - statBegin := int64(40000000) - statEnd := int64(maxTime - 10000000) - - for scanner.Scan() { - w := scanner.Text() - if w == "*R" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - t := topics[scanner.Text()] - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - if int(rad) != t.nodeRad[prefix] { - t.nodeRad[prefix] = int(rad) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return t.nodes[i] > radUint - }) - set(t.pic, x, y, 1, 255) - } - } - if w == "*MR" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - topic := scanner.Text() - t := topics[topic] - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - rad, _ := strconv.ParseInt(scanner.Text(), 10, 64) - radUint := uint64(rad) * ((^uint64(0)) / 1000000) - x := int(time * int64(xs) / int64(maxTime)) - y := sort.Search(ys, func(i int) bool { - return t.nodes[i] > radUint - }) - set(t.pic, x, y, 0, 255) - ni := nodeInfo[nodeIdx[prefix]] - if int(rad) > ni.maxMR { - ni.maxMR = int(rad) - if ni.topics == nil { - ni.topics = make(map[string]struct{}) - } - ni.topics[topic] = struct{}{} - } - nodeInfo[nodeIdx[prefix]] = ni - } - if w == "*W" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - t := topics[scanner.Text()] - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - scanner.Scan() - wp, _ := strconv.ParseInt(scanner.Text(), 10, 64) - x := int(time * int64(xs) / int64(maxTime)) - y := t.nodeIdx[prefix] - if time >= statBegin && time < statEnd { - t.nodeStats[y].wpSum += uint64(wp) - if wp >= 600000 { - t.nodeStats[y].wpXcnt++ - } - t.nodeStats[y].wpCnt++ - } - /*set(t.pic2, x, y, 0, int(wp/100000)) - set(t.pic2, x, y, 1, int(wp/10000)) - set(t.pic2, x, y, 2, int(wp/1000))*/ - if wp >= 1800000 { - set(t.pic2, x, y, 0, 255) - } - if wp >= 600000 { - set(t.pic2, x, y, 1, 255) - } - if wp >= 60000 { - set(t.pic2, x, y, 2, 255) - } - } - if w == "*+" { - scanner.Scan() - time, _ := strconv.ParseInt(scanner.Text(), 10, 64) - scanner.Scan() - t := topics[scanner.Text()] - scanner.Scan() - prefix, _ := strconv.ParseUint(scanner.Text(), 16, 64) - x := int(time * int64(xs) / int64(maxTime)) - if x < xs { - t.regStats[x/regStatDiv]++ - } - y := t.nodeIdx[prefix] - set(t.pic, x, y, 2, 255) - scanner.Scan() - prefix2, _ := strconv.ParseUint(scanner.Text(), 16, 64) - y2 := t.nodeIdx[prefix2] - if time >= statBegin && time < statEnd { - t.nodeStats[y].regCnt++ - t.nodeStats[y2].regXcnt++ - } - } - } - f.Close() - - for tt, t := range topics { - f, _ = os.Create("test_" + tt + ".png") - w := bufio.NewWriter(f) - png.Encode(w, t.pic) - w.Flush() - f.Close() - - for x := 0; x < xs; x++ { - yy := t.regStats[x/regStatDiv] / regStatYdiv - if yy > ys { - yy = ys - } - for y := 0; y < yy; y++ { - set(t.pic2, x, ys-1-y, 1, 255) - } - } - - f, _ = os.Create("test2_" + tt + ".png") - w = bufio.NewWriter(f) - png.Encode(w, t.pic2) - w.Flush() - f.Close() - - if statEnd > statBegin { - xxs := len(t.nodeStats) - yys := 1000 - yyh := yys / 2 - pic3 := image.NewNRGBA(image.Rect(0, 0, xxs, yys)) - for y := 0; y < yys; y++ { - for x := 0; x < xxs; x++ { - set(pic3, x, y, 3, 255) - } - } - for x := 0; x < xxs; x++ { - wpy := 0 - if t.nodeStats[x].wpCnt > 0 { - // wpy = int(t.nodeStats[x].wpSum / t.nodeStats[x].wpCnt / 10000) - wpy = int(uint64(yyh) * t.nodeStats[x].wpXcnt / t.nodeStats[x].wpCnt) - } - if wpy > yyh { - wpy = yyh - } - for y := 0; y < wpy; y++ { - set(pic3, x, yys-1-y, 1, 255) - } - regy := int(t.nodeStats[x].regCnt * 2400000 / uint64(statEnd-statBegin)) - if regy > yyh { - regy = yyh - } - for y := 0; y < regy; y++ { - set(pic3, x, yyh-1-y, 2, 255) - } - regy2 := int(t.nodeStats[x].regXcnt * 2400000 / uint64(statEnd-statBegin)) - if regy2 > yyh { - regy2 = yyh - } - for y := 0; y < regy2; y++ { - set(pic3, x, yyh-1-y, 0, 255) - } - } - - f, _ = os.Create("test3_" + tt + ".png") - w = bufio.NewWriter(f) - png.Encode(w, pic3) - w.Flush() - f.Close() - } - } - - for i, ni := range nodeInfo { - fmt.Printf("%d %016x maxMR = %d ", i, nodes[i], ni.maxMR) - for t, _ := range ni.topics { - fmt.Printf(" %s", t) - } - fmt.Println() - } -} - -type uint64Slice []uint64 - -// Len is the number of elements in the collection. -func (s uint64Slice) Len() int { - return len(s) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (s uint64Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -// Swap swaps the elements with indexes i and j. -func (s uint64Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go index 0807600c9..202504314 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -20,12 +20,13 @@ import ( "bytes" "encoding/binary" "fmt" + "math" "math/rand" "sort" "time" - "github.com/aristanetworks/goarista/atime" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/crypto" ) @@ -39,35 +40,33 @@ const ( maxRegisterDebt = 5 keepTicketConst = time.Minute * 10 keepTicketExp = time.Minute * 5 - maxRadius = 0xffffffffffffffff - minRadAverage = 100 - minRadStableAfter = 50 targetWaitTime = time.Minute * 10 - adjustRatio = 0.001 - adjustCooldownStart = 0.1 - adjustCooldownStep = 0.01 - radiusExtendRatio = 1.5 + topicQueryTimeout = time.Second * 5 + topicQueryResend = time.Minute + // topic radius detection + maxRadius = 0xffffffffffffffff + radiusTC = time.Minute * 20 + radiusBucketsPerBit = 8 + minSlope = 1 + minPeakSize = 40 + maxNoAdjust = 20 + lookupWidth = 8 + minRightSum = 20 + searchForceQuery = 4 ) -// absTime represents absolute monotonic time in nanoseconds. -type absTime time.Duration - -func monotonicTime() absTime { - return absTime(atime.NanoTime()) -} - // timeBucket represents absolute monotonic time in minutes. // It is used as the index into the per-topic ticket buckets. type timeBucket int type ticket struct { topics []Topic - regTime []absTime // Per-topic local absolute time when the ticket can be used. + regTime []mclock.AbsTime // Per-topic local absolute time when the ticket can be used. // The serial number that was issued by the server. serial uint32 // Used by registrar, tracks absolute time when the ticket was created. - issueTime absTime + issueTime mclock.AbsTime // Fields used only by registrants node *Node // the registrar node that signed this ticket @@ -85,11 +84,11 @@ func (ref ticketRef) topic() Topic { return ref.t.topics[ref.idx] } -func (ref ticketRef) topicRegTime() absTime { +func (ref ticketRef) topicRegTime() mclock.AbsTime { return ref.t.regTime[ref.idx] } -func pongToTicket(localTime absTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) { +func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) { wps := p.data.(*pong).WaitPeriods if len(topics) != len(wps) { return nil, fmt.Errorf("bad wait period list: got %d values, want %d", len(topics), len(wps)) @@ -102,17 +101,17 @@ func pongToTicket(localTime absTime, topics []Topic, node *Node, p *ingressPacke node: node, topics: topics, pong: p.rawData, - regTime: make([]absTime, len(wps)), + regTime: make([]mclock.AbsTime, len(wps)), } // Convert wait periods to local absolute time. for i, wp := range wps { - t.regTime[i] = localTime + absTime(time.Second*time.Duration(wp)) + t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp)) } return t, nil } func ticketToPong(t *ticket, pong *pong) { - pong.Expiration = uint64(t.issueTime / absTime(time.Second)) + pong.Expiration = uint64(t.issueTime / mclock.AbsTime(time.Second)) pong.TopicHash = rlpHash(t.topics) pong.TicketSerial = t.serial pong.WaitPeriods = make([]uint32, len(t.regTime)) @@ -136,30 +135,44 @@ type ticketStore struct { lastBucketFetched timeBucket nextTicketCached *ticketRef - nextTicketReg absTime + nextTicketReg mclock.AbsTime - minRadCnt, minRadPtr uint64 - minRadius, minRadSum uint64 - lastMinRads [minRadAverage]uint64 + searchTopicMap map[Topic]searchTopic + searchTopicList []Topic + searchTopicPtr int + nextTopicQueryCleanup mclock.AbsTime + queriesSent map[*Node]map[common.Hash]sentQuery + radiusLookupCnt int +} + +type searchTopic struct { + foundChn chan<- string + listIdx int +} + +type sentQuery struct { + sent mclock.AbsTime + lookup lookupInfo } type topicTickets struct { buckets map[timeBucket][]ticketRef - nextLookup, nextReg absTime + nextLookup, nextReg mclock.AbsTime } func newTicketStore() *ticketStore { return &ticketStore{ - radius: make(map[Topic]*topicRadius), - tickets: make(map[Topic]topicTickets), - nodes: make(map[*Node]*ticket), - nodeLastReq: make(map[*Node]reqInfo), + radius: make(map[Topic]*topicRadius), + tickets: make(map[Topic]topicTickets), + nodes: make(map[*Node]*ticket), + nodeLastReq: make(map[*Node]reqInfo), + searchTopicMap: make(map[Topic]searchTopic), + queriesSent: make(map[*Node]map[common.Hash]sentQuery), } } // addTopic starts tracking a topic. If register is true, // the local node will register the topic and tickets will be collected. -// It can be called even func (s *ticketStore) addTopic(t Topic, register bool) { debugLog(fmt.Sprintf(" addTopic(%v, %v)", t, register)) if s.radius[t] == nil { @@ -170,6 +183,27 @@ func (s *ticketStore) addTopic(t Topic, register bool) { } } +func (s *ticketStore) addSearchTopic(t Topic, foundChn chan<- string) { + s.addTopic(t, false) + if s.searchTopicMap[t].foundChn == nil { + s.searchTopicList = append(s.searchTopicList, t) + s.searchTopicMap[t] = searchTopic{foundChn: foundChn, listIdx: len(s.searchTopicList) - 1} + } +} + +func (s *ticketStore) removeSearchTopic(t Topic) { + if st := s.searchTopicMap[t]; st.foundChn != nil { + lastIdx := len(s.searchTopicList) - 1 + lastTopic := s.searchTopicList[lastIdx] + s.searchTopicList[st.listIdx] = lastTopic + sl := s.searchTopicMap[lastTopic] + sl.listIdx = st.listIdx + s.searchTopicMap[lastTopic] = sl + s.searchTopicList = s.searchTopicList[:lastIdx] + delete(s.searchTopicMap, t) + } +} + // removeRegisterTopic deletes all tickets for the given topic. func (s *ticketStore) removeRegisterTopic(topic Topic) { debugLog(fmt.Sprintf(" removeRegisterTopic(%v)", topic)) @@ -200,9 +234,9 @@ func (s *ticketStore) nextRegisterLookup() (lookup lookupInfo, delay time.Durati for topic := firstTopic; ok; { debugLog(fmt.Sprintf(" checking topic %v, len(s.tickets[topic]) = %d", topic, len(s.tickets[topic].buckets))) if s.tickets[topic].buckets != nil && s.needMoreTickets(topic) { - next := s.radius[topic].nextTarget() - debugLog(fmt.Sprintf(" %x 1s", next[:8])) - return lookupInfo{target: next, topic: topic}, 1 * time.Second + next := s.radius[topic].nextTarget(false) + debugLog(fmt.Sprintf(" %x 1s", next.target[:8])) + return next, 100 * time.Millisecond } topic, ok = s.iterRegTopics() if topic == firstTopic { @@ -213,6 +247,24 @@ func (s *ticketStore) nextRegisterLookup() (lookup lookupInfo, delay time.Durati return lookupInfo{}, 40 * time.Second } +func (s *ticketStore) nextSearchLookup() lookupInfo { + if len(s.searchTopicList) == 0 { + return lookupInfo{} + } + if s.searchTopicPtr >= len(s.searchTopicList) { + s.searchTopicPtr = 0 + } + topic := s.searchTopicList[s.searchTopicPtr] + s.searchTopicPtr++ + target := s.radius[topic].nextTarget(s.radiusLookupCnt >= searchForceQuery) + if target.radiusLookup { + s.radiusLookupCnt++ + } else { + s.radiusLookupCnt = 0 + } + return target +} + // iterRegTopics returns topics to register in arbitrary order. // The second return value is false if there are no topics. func (s *ticketStore) iterRegTopics() (Topic, bool) { @@ -234,7 +286,7 @@ func (s *ticketStore) iterRegTopics() (Topic, bool) { } func (s *ticketStore) needMoreTickets(t Topic) bool { - return s.tickets[t].nextLookup < monotonicTime() + return s.tickets[t].nextLookup < mclock.Now() } // ticketsInWindow returns the tickets of a given topic in the registration window. @@ -267,7 +319,7 @@ func (s ticketRefByWaitTime) Len() int { return len(s) } -func (r ticketRef) waitTime() absTime { +func (r ticketRef) waitTime() mclock.AbsTime { return r.t.regTime[r.idx] - r.t.issueTime } @@ -288,28 +340,28 @@ func (s *ticketStore) addTicketRef(r ticketRef) { if t.buckets == nil { return } - bucket := timeBucket(r.t.regTime[r.idx] / absTime(ticketTimeBucketLen)) + bucket := timeBucket(r.t.regTime[r.idx] / mclock.AbsTime(ticketTimeBucketLen)) t.buckets[bucket] = append(t.buckets[bucket], r) r.t.refCnt++ - min := monotonicTime() - absTime(collectFrequency)*maxCollectDebt + min := mclock.Now() - mclock.AbsTime(collectFrequency)*maxCollectDebt if t.nextLookup < min { t.nextLookup = min } - t.nextLookup += absTime(collectFrequency) + t.nextLookup += mclock.AbsTime(collectFrequency) s.tickets[topic] = t //s.removeExcessTickets(topic) } func (s *ticketStore) nextFilteredTicket() (t *ticketRef, wait time.Duration) { - now := monotonicTime() + now := mclock.Now() for { t, wait = s.nextRegisterableTicket() if t == nil { return } - regTime := now + absTime(wait) + regTime := now + mclock.AbsTime(wait) topic := t.t.topics[t.idx] if regTime >= s.tickets[topic].nextReg { return @@ -319,15 +371,15 @@ func (s *ticketStore) nextFilteredTicket() (t *ticketRef, wait time.Duration) { } func (s *ticketStore) ticketRegistered(t ticketRef) { - now := monotonicTime() + now := mclock.Now() topic := t.t.topics[t.idx] tt := s.tickets[topic] - min := now - absTime(registerFrequency)*maxRegisterDebt + min := now - mclock.AbsTime(registerFrequency)*maxRegisterDebt if min > tt.nextReg { tt.nextReg = min } - tt.nextReg += absTime(registerFrequency) + tt.nextReg += mclock.AbsTime(registerFrequency) s.tickets[topic] = tt s.removeTicketRef(t) @@ -351,7 +403,7 @@ func (s *ticketStore) nextRegisterableTicket() (t *ticketRef, wait time.Duration }() debugLog("nextRegisterableTicket()") - now := monotonicTime() + now := mclock.Now() if s.nextTicketCached != nil { return s.nextTicketCached, time.Duration(s.nextTicketCached.topicRegTime() - now) } @@ -395,7 +447,7 @@ func (s *ticketStore) removeTicketRef(ref ticketRef) { if tickets == nil { return } - bucket := timeBucket(ref.t.regTime[ref.idx] / absTime(ticketTimeBucketLen)) + bucket := timeBucket(ref.t.regTime[ref.idx] / mclock.AbsTime(ticketTimeBucketLen)) list := tickets[bucket] idx := -1 for i, bt := range list { @@ -424,13 +476,15 @@ func (s *ticketStore) removeTicketRef(ref ticketRef) { } type lookupInfo struct { - target common.Hash - topic Topic + target common.Hash + topic Topic + radiusLookup bool } type reqInfo struct { pingHash []byte - topic Topic + lookup lookupInfo + time mclock.AbsTime } // returns -1 if not found @@ -444,80 +498,84 @@ func (t *ticket) findIdx(topic Topic) int { } func (s *ticketStore) registerLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte) { - now := monotonicTime() - //fmt.Printf("registerLookupDone target = %016x\n", target[:8]) - if len(nodes) > 0 { - s.adjustMinRadius(lookup.target, nodes[0].sha) - } + now := mclock.Now() for i, n := range nodes { - if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.minRadius { - if t := s.nodes[n]; t != nil { - // adjust radius with already stored ticket - if idx := t.findIdx(lookup.topic); idx != -1 { - s.adjustWithTicket(now, t, idx, false) + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} } } else { - // request a new pong packet - s.nodeLastReq[n] = reqInfo{pingHash: ping(n), topic: lookup.topic} + if s.nodes[n] == nil { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } } } } } -func (s *ticketStore) adjustWithTicket(localTime absTime, t *ticket, idx int, onlyConverging bool) { - if onlyConverging { - for i, topic := range t.topics { - if tt, ok := s.radius[topic]; ok && !tt.converged && tt.isInRadius(t, true) { - tt.adjust(localTime, ticketRef{t, i}, s.minRadius, s.minRadCnt >= minRadStableAfter) - debugLog(fmt.Sprintf("adjust converging topic: %v, rad: %v, cd: %v, converged: %v", topic, float64(tt.radius)/maxRadius, tt.adjustCooldown, tt.converged)) +func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte, query func(n *Node, topic Topic) []byte) { + now := mclock.Now() + for i, n := range nodes { + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } // else { + if s.canQueryTopic(n, lookup.topic) { + hash := query(n, lookup.topic) + if hash != nil { + s.addTopicQuery(common.BytesToHash(hash), n, lookup) + } } - } - } else { - topic := t.topics[idx] - if tt, ok := s.radius[topic]; ok && tt.isInRadius(t, true) { - tt.adjust(localTime, ticketRef{t, idx}, s.minRadius, s.minRadCnt >= minRadStableAfter) - debugLog(fmt.Sprintf("adjust topic: %v, rad: %v, cd: %v, converged: %v", topic, float64(tt.radius)/maxRadius, tt.adjustCooldown, tt.converged)) + //} } } } -func (s *ticketStore) addTicket(localTime absTime, pingHash []byte, t *ticket) { +func (s *ticketStore) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t *ticket) { + for i, topic := range t.topics { + if tt, ok := s.radius[topic]; ok { + tt.adjustWithTicket(now, targetHash, ticketRef{t, i}) + } + } +} + +func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, t *ticket) { debugLog(fmt.Sprintf("add(node = %x sn = %v)", t.node.ID[:8], t.serial)) - if s.nodes[t.node] != nil { - return - } - lastReq, ok := s.nodeLastReq[t.node] if !(ok && bytes.Equal(pingHash, lastReq.pingHash)) { - s.adjustWithTicket(localTime, t, -1, true) return } - topic := lastReq.topic + s.adjustWithTicket(localTime, lastReq.lookup.target, t) + + if lastReq.lookup.radiusLookup || s.nodes[t.node] != nil { + return + } + + topic := lastReq.lookup.topic topicIdx := t.findIdx(topic) if topicIdx == -1 { return } - s.adjustWithTicket(localTime, t, topicIdx, false) - bucket := timeBucket(localTime / absTime(ticketTimeBucketLen)) + bucket := timeBucket(localTime / mclock.AbsTime(ticketTimeBucketLen)) if s.lastBucketFetched == 0 || bucket < s.lastBucketFetched { s.lastBucketFetched = bucket } - for topicIdx, topic := range t.topics { - if tt, ok := s.radius[topic]; ok && tt.isInRadius(t, false) { - if _, ok := s.tickets[topic]; ok && tt.converged { - wait := t.regTime[topicIdx] - localTime - rnd := rand.ExpFloat64() - if rnd > 10 { - rnd = 10 - } - if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd { - // use the ticket to register this topic - s.addTicketRef(ticketRef{t, topicIdx}) - } - } + if _, ok := s.tickets[topic]; ok { + wait := t.regTime[topicIdx] - localTime + rnd := rand.ExpFloat64() + if rnd > 10 { + rnd = 10 + } + if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd { + // use the ticket to register this topic + //fmt.Println("addTicket", t.node.ID[:8], t.node.addr().String(), t.serial, t.pong) + s.addTicketRef(ticketRef{t, topicIdx}) } } @@ -536,43 +594,137 @@ func (s *ticketStore) getNodeTicket(node *Node) *ticket { return s.nodes[node] } -func (s *ticketStore) adjustMinRadius(target, found common.Hash) { - tp := binary.BigEndian.Uint64(target[0:8]) - fp := binary.BigEndian.Uint64(found[0:8]) - dist := tp ^ fp - - var mr uint64 - if dist < maxRadius/16 { - mr = dist * 16 - } else { - mr = maxRadius +func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool { + qq := s.queriesSent[node] + if qq != nil { + now := mclock.Now() + for _, sq := range qq { + if sq.lookup.topic == topic && sq.sent > now-mclock.AbsTime(topicQueryResend) { + return false + } + } } - mr /= minRadAverage + return true +} - s.minRadSum -= s.lastMinRads[s.minRadPtr] - s.lastMinRads[s.minRadPtr] = mr - s.minRadSum += mr - s.minRadPtr++ - if s.minRadPtr == minRadAverage { - s.minRadPtr = 0 +func (s *ticketStore) addTopicQuery(hash common.Hash, node *Node, lookup lookupInfo) { + now := mclock.Now() + qq := s.queriesSent[node] + if qq == nil { + qq = make(map[common.Hash]sentQuery) + s.queriesSent[node] = qq } - s.minRadCnt++ + qq[hash] = sentQuery{sent: now, lookup: lookup} + s.cleanupTopicQueries(now) +} - if s.minRadCnt < minRadAverage { - s.minRadius = (s.minRadSum / s.minRadCnt) * minRadAverage - } else { - s.minRadius = s.minRadSum +func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) { + if s.nextTopicQueryCleanup > now { + return } - debugLog(fmt.Sprintf("adjustMinRadius() %v", float64(s.minRadius)/maxRadius)) + exp := now - mclock.AbsTime(topicQueryResend) + for n, qq := range s.queriesSent { + for h, q := range qq { + if q.sent < exp { + delete(qq, h) + } + } + if len(qq) == 0 { + delete(s.queriesSent, n) + } + } + s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout) +} + +func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) { + now := mclock.Now() + //fmt.Println("got", from.addr().String(), hash, len(nodes)) + qq := s.queriesSent[from] + if qq == nil { + return true + } + q, ok := qq[hash] + if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) { + return true + } + inside := float64(0) + if len(nodes) > 0 { + inside = 1 + } + s.radius[q.lookup.topic].adjust(now, q.lookup.target, from.sha, inside) + chn := s.searchTopicMap[q.lookup.topic].foundChn + if chn == nil { + //fmt.Println("no channel") + return false + } + for _, node := range nodes { + ip := node.IP + if ip.IsUnspecified() || ip.IsLoopback() { + ip = from.IP + } + enode := NewNode(node.ID, ip, node.UDP-1, node.TCP-1).String() // subtract one from port while discv5 is running in test mode on UDPport+1 + select { + case chn <- enode: + default: + return false + } + } + return false } type topicRadius struct { - topic Topic - topicHashPrefix uint64 - radius uint64 - adjustCooldown float64 // only for convergence detection - converged bool - intExtBalance float64 + topic Topic + topicHashPrefix uint64 + radius, minRadius uint64 + buckets []topicRadiusBucket +} + +type topicRadiusEvent int + +const ( + trOutside topicRadiusEvent = iota + trInside + trNoAdjust + trCount +) + +type topicRadiusBucket struct { + weights [trCount]float64 + lastTime mclock.AbsTime + value float64 + lookupSent map[common.Hash]mclock.AbsTime +} + +func (b *topicRadiusBucket) update(now mclock.AbsTime) { + if now == b.lastTime { + return + } + exp := math.Exp(-float64(now-b.lastTime) / float64(radiusTC)) + for i, w := range b.weights { + b.weights[i] = w * exp + } + b.lastTime = now + + for target, tm := range b.lookupSent { + if now-tm > mclock.AbsTime(pingTimeout) { + b.weights[trNoAdjust] += 1 + delete(b.lookupSent, target) + } + } +} + +func (b *topicRadiusBucket) adjust(now mclock.AbsTime, inside float64) { + b.update(now) + if inside <= 0 { + b.weights[trOutside] += 1 + } else { + if inside >= 1 { + b.weights[trInside] += 1 + } else { + b.weights[trInside] += inside + b.weights[trOutside] += 1 - inside + } + } } func newTopicRadius(t Topic) *topicRadius { @@ -583,102 +735,237 @@ func newTopicRadius(t Topic) *topicRadius { topic: t, topicHashPrefix: topicHashPrefix, radius: maxRadius, - adjustCooldown: adjustCooldownStart, - converged: false, + minRadius: maxRadius, } } -func (r *topicRadius) isInRadius(t *ticket, extRadius bool) bool { - nodePrefix := binary.BigEndian.Uint64(t.node.sha[0:8]) - dist := nodePrefix ^ r.topicHashPrefix - if extRadius { - return float64(dist) < float64(r.radius)*radiusExtendRatio +func (r *topicRadius) getBucketIdx(addrHash common.Hash) int { + prefix := binary.BigEndian.Uint64(addrHash[0:8]) + var log2 float64 + if prefix != r.topicHashPrefix { + log2 = math.Log2(float64(prefix ^ r.topicHashPrefix)) } - return dist < r.radius -} - -func randUint64n(n uint64) uint64 { // don't care about lowest bit, 63 bit randomness is more than enough - if n < 4 { + bucket := int((64 - log2) * radiusBucketsPerBit) + max := 64*radiusBucketsPerBit - 1 + if bucket > max { + return max + } + if bucket < 0 { return 0 } - return uint64(rand.Int63n(int64(n/2))) * 2 + return bucket } -func (r *topicRadius) nextTarget() common.Hash { - var rnd uint64 - if r.intExtBalance < 0 { - // select target from inner region - rnd = randUint64n(r.radius) - } else { - // select target from outer region - e := float64(r.radius) * radiusExtendRatio - extRadius := uint64(maxRadius) - if e < maxRadius { - extRadius = uint64(e) - } - rnd = r.radius + randUint64n(extRadius-r.radius) +func (r *topicRadius) targetForBucket(bucket int) common.Hash { + min := math.Pow(2, 64-float64(bucket+1)/radiusBucketsPerBit) + max := math.Pow(2, 64-float64(bucket)/radiusBucketsPerBit) + a := uint64(min) + b := randUint64n(uint64(max - min)) + xor := a + b + if xor < a { + xor = ^uint64(0) } - prefix := r.topicHashPrefix ^ rnd + prefix := r.topicHashPrefix ^ xor var target common.Hash binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) return target } -func (r *topicRadius) adjust(localTime absTime, t ticketRef, minRadius uint64, minRadStable bool) { - var balanceStep, stepSign float64 - if r.isInRadius(t.t, false) { - balanceStep = radiusExtendRatio - 1 - stepSign = 1 - } else { - balanceStep = -1 - stepSign = -1 +// package rand provides a Read function in Go 1.6 and later, but +// we can't use it yet because we still support Go 1.5. +func globalRandRead(b []byte) { + pos := 0 + val := 0 + for n := 0; n < len(b); n++ { + if pos == 0 { + val = rand.Int() + pos = 7 + } + b[n] = byte(val) + val >>= 8 + pos-- + } +} + +func (r *topicRadius) isInRadius(addrHash common.Hash) bool { + nodePrefix := binary.BigEndian.Uint64(addrHash[0:8]) + dist := nodePrefix ^ r.topicHashPrefix + return dist < r.radius +} + +func (r *topicRadius) chooseLookupBucket(a, b int) int { + if a < 0 { + a = 0 + } + if a > b { + return -1 + } + c := 0 + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + c++ + } + } + if c == 0 { + return -1 + } + rnd := randUint(uint32(c)) + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + if rnd == 0 { + return i + } + rnd-- + } + } + panic(nil) // should never happen +} + +func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool { + var max float64 + if a < 0 { + a = 0 + } + if b >= len(r.buckets) { + b = len(r.buckets) - 1 + if r.buckets[b].value > max { + max = r.buckets[b].value + } + } + if b >= a { + for i := a; i <= b; i++ { + if r.buckets[i].value > max { + max = r.buckets[i].value + } + } + } + return maxValue-max < minPeakSize +} + +func (r *topicRadius) recalcRadius() (radius uint64, radiusLookup int) { + maxBucket := 0 + maxValue := float64(0) + now := mclock.Now() + v := float64(0) + for i, _ := range r.buckets { + r.buckets[i].update(now) + v += r.buckets[i].weights[trOutside] - r.buckets[i].weights[trInside] + r.buckets[i].value = v + //fmt.Printf("%v %v | ", v, r.buckets[i].weights[trNoAdjust]) + } + //fmt.Println() + slopeCross := -1 + for i, b := range r.buckets { + v := b.value + if v < float64(i)*minSlope { + slopeCross = i + break + } + if v > maxValue { + maxValue = v + maxBucket = i + 1 + } } - if r.intExtBalance*stepSign > 3 { + minRadBucket := len(r.buckets) + sum := float64(0) + for minRadBucket > 0 && sum < minRightSum { + minRadBucket-- + b := r.buckets[minRadBucket] + sum += b.weights[trInside] + b.weights[trOutside] + } + r.minRadius = uint64(math.Pow(2, 64-float64(minRadBucket)/radiusBucketsPerBit)) + + lookupLeft := -1 + if r.needMoreLookups(0, maxBucket-lookupWidth-1, maxValue) { + lookupLeft = r.chooseLookupBucket(maxBucket-lookupWidth, maxBucket-1) + } + lookupRight := -1 + if slopeCross != maxBucket && (minRadBucket <= maxBucket || r.needMoreLookups(maxBucket+lookupWidth, len(r.buckets)-1, maxValue)) { + for len(r.buckets) <= maxBucket+lookupWidth { + r.buckets = append(r.buckets, topicRadiusBucket{lookupSent: make(map[common.Hash]mclock.AbsTime)}) + } + lookupRight = r.chooseLookupBucket(maxBucket, maxBucket+lookupWidth-1) + } + if lookupLeft == -1 { + radiusLookup = lookupRight + } else { + if lookupRight == -1 { + radiusLookup = lookupLeft + } else { + if randUint(2) == 0 { + radiusLookup = lookupLeft + } else { + radiusLookup = lookupRight + } + } + } + + //fmt.Println("mb", maxBucket, "sc", slopeCross, "mrb", minRadBucket, "ll", lookupLeft, "lr", lookupRight, "mv", maxValue) + + if radiusLookup == -1 { + // no more radius lookups needed at the moment, return a radius + rad := maxBucket + if minRadBucket < rad { + rad = minRadBucket + } + radius = ^uint64(0) + if rad > 0 { + radius = uint64(math.Pow(2, 64-float64(rad)/radiusBucketsPerBit)) + } + r.radius = radius + } + + return +} + +func (r *topicRadius) nextTarget(forceRegular bool) lookupInfo { + if !forceRegular { + _, radiusLookup := r.recalcRadius() + if radiusLookup != -1 { + target := r.targetForBucket(radiusLookup) + r.buckets[radiusLookup].lookupSent[target] = mclock.Now() + return lookupInfo{target: target, topic: r.topic, radiusLookup: true} + } + } + + radExt := r.radius / 2 + if radExt > maxRadius-r.radius { + radExt = maxRadius - r.radius + } + rnd := randUint64n(r.radius) + randUint64n(2*radExt) + if rnd > radExt { + rnd -= radExt + } else { + rnd = radExt - rnd + } + + prefix := r.topicHashPrefix ^ rnd + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) + return lookupInfo{target: target, topic: r.topic, radiusLookup: false} +} + +func (r *topicRadius) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t ticketRef) { + wait := t.t.regTime[t.idx] - t.t.issueTime + inside := float64(wait)/float64(targetWaitTime) - 0.5 + if inside > 1 { + inside = 1 + } + if inside < 0 { + inside = 0 + } + r.adjust(now, targetHash, t.t.node.sha, inside) +} + +func (r *topicRadius) adjust(now mclock.AbsTime, targetHash, addrHash common.Hash, inside float64) { + bucket := r.getBucketIdx(addrHash) + //fmt.Println("adjust", bucket, len(r.buckets), inside) + if bucket >= len(r.buckets) { return } - r.intExtBalance += balanceStep - - wait := t.t.regTime[t.idx] - t.t.issueTime // localTime - adjust := (float64(wait)/float64(targetWaitTime) - 1) * 2 - if adjust > 1 { - adjust = 1 - } - if adjust < -1 { - adjust = -1 - } - /*var adjust float64 - if wait > absTime(targetWaitTime) { - adjust = 1 - } else { - adjust = -1 - }*/ - - if r.converged { - adjust *= adjustRatio - } else { - adjust *= r.adjustCooldown - } - - /*if adjust > 0 { - adjust *= radiusExtendRatio*2 - 1 - }*/ - - radius := float64(r.radius) * (1 + adjust) - if radius > float64(maxRadius) { - r.radius = maxRadius - } else { - r.radius = uint64(radius) - if r.radius < minRadius { - r.radius = minRadius - } - } - - if !r.converged && (adjust > 0 || (r.radius == minRadius && minRadStable)) { - r.adjustCooldown *= (1 - adjustCooldownStep) - if r.adjustCooldown <= adjustRatio { - r.converged = true - } - } - + r.buckets[bucket].adjust(now, inside) + delete(r.buckets[bucket].lookupSent, targetHash) } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go index caaec28b7..625921e84 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -22,6 +22,8 @@ import ( "math" "math/rand" "time" + + "github.com/ethereum/go-ethereum/common/mclock" ) const ( @@ -37,7 +39,7 @@ type topicEntry struct { topic Topic fifoIdx uint64 node *Node - expire absTime + expire mclock.AbsTime } type topicInfo struct { @@ -61,7 +63,7 @@ type nodeInfo struct { entries map[Topic]*topicEntry lastIssuedTicket, lastUsedTicket uint32 // you can't register a ticket newer than lastUsedTicket before noRegUntil (absolute time) - noRegUntil absTime + noRegUntil mclock.AbsTime } type topicTable struct { @@ -72,7 +74,7 @@ type topicTable struct { globalEntries uint64 requested topicRequestQueue requestCnt uint64 - lastGarbageCollection absTime + lastGarbageCollection mclock.AbsTime } func newTopicTable(db *nodeDB, self *Node) *topicTable { @@ -134,7 +136,7 @@ func (t *topicTable) getOrNewNode(node *Node) *nodeInfo { } func (t *topicTable) checkDeleteNode(node *Node) { - if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < monotonicTime() { + if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() { //fmt.Printf("deleteNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) delete(t.nodes, node) } @@ -174,7 +176,7 @@ func (t *topicTable) addEntry(node *Node, topic Topic) { // *** n = t.getOrNewNode(node) - tm := monotonicTime() + tm := mclock.Now() te := t.getOrNewTopic(topic) if len(te.entries) == maxEntriesPerTopic { @@ -191,7 +193,7 @@ func (t *topicTable) addEntry(node *Node, topic Topic) { topic: topic, fifoIdx: fifoIdx, node: node, - expire: tm + absTime(fallbackRegistrationExpiry), + expire: tm + mclock.AbsTime(fallbackRegistrationExpiry), } if printTestImgLogs { fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8]) @@ -216,7 +218,7 @@ func (t *topicTable) leastRequested() *topicEntry { // entry should exist func (t *topicTable) deleteEntry(e *topicEntry) { if printTestImgLogs { - fmt.Printf("*- %d %v %016x %016x\n", monotonicTime()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8]) + fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8]) } ne := t.nodes[e.node].entries delete(ne, e.topic) @@ -233,7 +235,8 @@ func (t *topicTable) deleteEntry(e *topicEntry) { // It is assumed that topics and waitPeriods have the same length. func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) { - debugLog(fmt.Sprintf("useTicket", serialNo, topics, waitPeriods)) + debugLog(fmt.Sprintf("useTicket %v %v %v", serialNo, topics, waitPeriods)) + //fmt.Println("useTicket", serialNo, topics, waitPeriods) t.collectGarbage() n := t.getOrNewNode(node) @@ -241,22 +244,26 @@ func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx return false } - tm := monotonicTime() + tm := mclock.Now() if serialNo > n.lastUsedTicket && tm < n.noRegUntil { return false } if serialNo != n.lastUsedTicket { n.lastUsedTicket = serialNo - n.noRegUntil = tm + absTime(noRegTimeout()) + n.noRegUntil = tm + mclock.AbsTime(noRegTimeout()) t.storeTicketCounters(node) } - currTime := uint64(tm / absTime(time.Second)) + currTime := uint64(tm / mclock.AbsTime(time.Second)) regTime := issueTime + uint64(waitPeriods[idx]) relTime := int64(currTime - regTime) - if relTime >= -1 && relTime <= regTimeWindow+1 && // give clients a little security margin on both ends - n.entries[topics[idx]] == nil { // don't register again if there is an active entry - t.addEntry(node, topics[idx]) + if relTime >= -1 && relTime <= regTimeWindow+1 { // give clients a little security margin on both ends + if e := n.entries[topics[idx]]; e == nil { + t.addEntry(node, topics[idx]) + } else { + // if there is an active entry, don't move to the front of the FIFO but prolong expire time + e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry) + } return true } @@ -266,7 +273,7 @@ func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { topictab.collectGarbage() - now := monotonicTime() + now := mclock.Now() n := topictab.getOrNewNode(node) n.lastIssuedTicket++ topictab.storeTicketCounters(node) @@ -275,7 +282,7 @@ func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { issueTime: now, topics: topics, serial: n.lastIssuedTicket, - regTime: make([]absTime, len(topics)), + regTime: make([]mclock.AbsTime, len(topics)), } for i, topic := range topics { var waitPeriod time.Duration @@ -285,7 +292,7 @@ func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { waitPeriod = minWaitPeriod } - t.regTime[i] = now + absTime(waitPeriod) + t.regTime[i] = now + mclock.AbsTime(waitPeriod) } return t } @@ -293,7 +300,7 @@ func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { const gcInterval = time.Minute func (t *topicTable) collectGarbage() { - tm := monotonicTime() + tm := mclock.Now() if time.Duration(tm-t.lastGarbageCollection) < gcInterval { return } @@ -326,16 +333,16 @@ const ( // initialization is not required, will set to minWaitPeriod at first registration type waitControlLoop struct { - lastIncoming absTime + lastIncoming mclock.AbsTime waitPeriod time.Duration } -func (w *waitControlLoop) registered(tm absTime) { +func (w *waitControlLoop) registered(tm mclock.AbsTime) { w.waitPeriod = w.nextWaitPeriod(tm) w.lastIncoming = tm } -func (w *waitControlLoop) nextWaitPeriod(tm absTime) time.Duration { +func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration { period := tm - w.lastIncoming wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst))) if wp < minWaitPeriod { @@ -345,7 +352,7 @@ func (w *waitControlLoop) nextWaitPeriod(tm absTime) time.Duration { } func (w *waitControlLoop) hasMinimumWaitPeriod() bool { - return w.nextWaitPeriod(monotonicTime()) == minWaitPeriod + return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod } func noRegTimeout() time.Duration { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go index bc8a5dd7c..46d3200bf 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -116,7 +116,7 @@ type ( topicRegister struct { Topics []Topic - Idx int + Idx uint Pong []byte } @@ -258,7 +258,7 @@ func listenUDP(priv *ecdsa.PrivateKey, laddr string) (*udp, error) { if err != nil { return nil, err } - return &udp{conn: conn, priv: priv}, nil + return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(addr, uint16(addr.Port))}, nil } func (t *udp) localAddr() *net.UDPAddr { @@ -269,15 +269,16 @@ func (t *udp) Close() { t.conn.Close() } -func (t *udp) send(remote *Node, ptype nodeEvent, data interface{}) { - t.sendPacket(remote.ID, remote.addr(), byte(ptype), data) +func (t *udp) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { + hash, _ = t.sendPacket(remote.ID, remote.addr(), byte(ptype), data) + return hash } func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash []byte) { hash, _ = t.sendPacket(remote.ID, toaddr, byte(pingPacket), ping{ Version: Version, From: t.ourEndpoint, - To: makeEndpoint(toaddr, 0), // TODO: maybe use known TCP port from DB + To: makeEndpoint(toaddr, uint16(toaddr.Port)), // TODO: maybe use known TCP port from DB Expiration: uint64(time.Now().Add(expiration).Unix()), Topics: topics, }) @@ -314,31 +315,38 @@ func (t *udp) sendFindnodeHash(remote *Node, target common.Hash) { func (t *udp) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { t.sendPacket(remote.ID, remote.addr(), byte(topicRegisterPacket), topicRegister{ Topics: topics, - Idx: idx, + Idx: uint(idx), Pong: pong, }) } func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { p := topicNodes{Echo: queryHash} + if len(nodes) == 0 { + t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) + return + } for i, result := range nodes { p.Nodes = append(p.Nodes, nodeToRPC(result)) if len(p.Nodes) == maxTopicNodes || i == len(nodes)-1 { - t.sendPacket(remote.ID, remote.addr(), byte(neighborsPacket), p) + t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) p.Nodes = p.Nodes[:0] } } } func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req interface{}) (hash []byte, err error) { + //fmt.Println("sendPacket", nodeEvent(ptype), toaddr.String(), toid.String()) packet, hash, err := encodePacket(t.priv, ptype, req) if err != nil { + //fmt.Println(err) return hash, err } glog.V(logger.Detail).Infof(">>> %v to %x@%v\n", nodeEvent(ptype), toid[:8], toaddr) if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil { glog.V(logger.Detail).Infoln("UDP send failed:", err) } + //fmt.Println(err) return hash, err } @@ -401,16 +409,19 @@ func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { pkt := ingressPacket{remoteAddr: from} if err := decodePacket(buf, &pkt); err != nil { glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) + //fmt.Println("bad packet", err) return err } t.net.reqReadPacket(pkt) return nil } -func decodePacket(buf []byte, pkt *ingressPacket) error { - if len(buf) < headSize+1 { +func decodePacket(buffer []byte, pkt *ingressPacket) error { + if len(buffer) < headSize+1 { return errPacketTooSmall } + buf := make([]byte, len(buffer)) + copy(buf, buffer) hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Keccak256(buf[macSize:]) if !bytes.Equal(hash, shouldhash) { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server.go b/vendor/github.com/ethereum/go-ethereum/p2p/server.go index 8e874f533..7381127dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/server.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server.go @@ -73,16 +73,26 @@ type Config struct { // or not. Disabling is usually useful for protocol debugging (manual topology). Discovery bool + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. DiscoveryV5 bool + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + // Name sets the node name of this server. // Use common.MakeName to create a name that follows existing conventions. Name string - // Bootstrap nodes are used to establish connectivity + // BootstrapNodes are used to establish connectivity // with the rest of the network. BootstrapNodes []*discover.Node + // BootstrapNodesV5 are used to establish connectivity + // with the rest of the network using the V5 discovery + // protocol. + BootstrapNodesV5 []*discv5.Node + // Static nodes are used as pre-configured connections which are always // maintained and re-connected on disconnects. StaticNodes []*discover.Node @@ -108,8 +118,6 @@ type Config struct { // the server is started. ListenAddr string - ListenAddrV5 string - // If set to a non-nil value, the given NAT port mapper // is used to make the listening port available to the // Internet. @@ -140,6 +148,7 @@ type Server struct { listener net.Listener ourHandshake *protoHandshake lastLookup time.Time + DiscV5 *discv5.Network // These are for Peers, PeerCount (and nothing else). peerOp chan peerOpFunc @@ -358,14 +367,14 @@ func (srv *Server) Start() (err error) { } if srv.DiscoveryV5 { - ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.ListenAddrV5, srv.NAT, "") //srv.NodeDatabase) + ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "") //srv.NodeDatabase) if err != nil { return err } - if err := ntab.SetFallbackNodes(discv5.BootNodes); err != nil { + if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { return err } - //srv.ntab = ntab + srv.DiscV5 = ntab } dynPeers := (srv.MaxPeers + 1) / 2 @@ -543,6 +552,9 @@ running: if srv.ntab != nil { srv.ntab.Close() } + if srv.DiscV5 != nil { + srv.DiscV5.Close() + } // Disconnect all peers. for _, p := range peers { p.Disconnect(DiscQuitting) @@ -744,7 +756,7 @@ type NodeInfo struct { Protocols map[string]interface{} `json:"protocols"` } -// Info gathers and returns a collection of metadata known about the host. +// NodeInfo gathers and returns a collection of metadata known about the host. func (srv *Server) NodeInfo() *NodeInfo { node := srv.Self() diff --git a/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go new file mode 100644 index 000000000..830b309d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go @@ -0,0 +1,52 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" +) + +// MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on +// the main Ethereum network. +var MainnetBootnodes = []*discover.Node{ + // ETH/DEV Go Bootnodes + discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE + discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR + discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG + + // ETH/DEV Cpp Bootnodes + discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"), +} + +// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Morden test network. +var TestnetBootnodes = []*discover.Node{ + // ETH/DEV Go Bootnodes + discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"), + discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"), + + // ETH/DEV Cpp Bootnodes +} + +// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the +// experimental RLPx v5 topic-discovery network. +var DiscoveryV5Bootnodes = []*discv5.Node{ + discv5.MustParseNode("enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305"), + discv5.MustParseNode("enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308"), + discv5.MustParseNode("enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309"), +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/config.go b/vendor/github.com/ethereum/go-ethereum/params/config.go new file mode 100644 index 000000000..8c285781e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/config.go @@ -0,0 +1,150 @@ +// 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 . + +package params + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// MainnetChainConfig is the chain parameters to run a node on the main network. +var MainnetChainConfig = &ChainConfig{ + ChainId: MainNetChainID, + HomesteadBlock: MainNetHomesteadBlock, + DAOForkBlock: MainNetDAOForkBlock, + DAOForkSupport: true, + EIP150Block: MainNetHomesteadGasRepriceBlock, + EIP150Hash: MainNetHomesteadGasRepriceHash, + EIP155Block: MainNetSpuriousDragon, + EIP158Block: MainNetSpuriousDragon, +} + +// TestnetChainConfig is the chain parameters to run a node on the test network. +var TestnetChainConfig = &ChainConfig{ + ChainId: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), +} + +// ChainConfig is the core config which determines the blockchain settings. +// +// ChainConfig is stored in the database on a per block basis. This means +// that any network, identified by its genesis block, can have its own +// set of configuration options. +type ChainConfig struct { + ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection + + HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) + DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) + DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork + + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + EIP150Block *big.Int `json:"eip150Block"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"eip150Hash"` // EIP150 HF hash (fast sync aid) + + EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block + EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block +} + +// String implements the Stringer interface. +func (c *ChainConfig) String() string { + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}", + c.ChainId, + c.HomesteadBlock, + c.DAOForkBlock, + c.DAOForkSupport, + c.EIP150Block, + c.EIP155Block, + c.EIP158Block, + ) +} + +var ( + TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)} + TestRules = TestChainConfig.Rules(new(big.Int)) +) + +// IsHomestead returns whether num is either equal to the homestead block or greater. +func (c *ChainConfig) IsHomestead(num *big.Int) bool { + if c.HomesteadBlock == nil || num == nil { + return false + } + return num.Cmp(c.HomesteadBlock) >= 0 +} + +// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). +// +// The returned GasTable's fields shouldn't, under any circumstances, be changed. +func (c *ChainConfig) GasTable(num *big.Int) GasTable { + if num == nil { + return GasTableHomestead + } + + switch { + case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0: + return GasTableEIP158 + case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0: + return GasTableHomesteadGasRepriceFork + default: + return GasTableHomestead + } +} + +func (c *ChainConfig) IsEIP150(num *big.Int) bool { + if c.EIP150Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP150Block) >= 0 + +} + +func (c *ChainConfig) IsEIP155(num *big.Int) bool { + if c.EIP155Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP155Block) >= 0 + +} + +func (c *ChainConfig) IsEIP158(num *big.Int) bool { + if c.EIP158Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP158Block) >= 0 + +} + +// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions +// that do not have or require information about the block. +// +// Rules is a one time interface meaning that it shouldn't be used in between transition +// phases. +type Rules struct { + ChainId *big.Int + IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool +} + +func (c *ChainConfig) Rules(num *big.Int) Rules { + return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)} +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/gas_table.go b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go index 98f00a7ee..093dacc8b 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/gas_table.go +++ b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go @@ -26,6 +26,8 @@ type GasTable struct { Calls *big.Int Suicide *big.Int + ExpByte *big.Int + // CreateBySuicide occurs when the // refunded account is one that does // not exist. This logic is similar @@ -44,6 +46,7 @@ var ( SLoad: big.NewInt(50), Calls: big.NewInt(40), Suicide: big.NewInt(0), + ExpByte: big.NewInt(10), // explicitly set to nil to indicate // this rule does not apply to homestead. @@ -52,6 +55,8 @@ var ( // GasTableHomestead contain the gas re-prices for // the homestead phase. + // + // TODO rename to GasTableEIP150 GasTableHomesteadGasRepriceFork = GasTable{ ExtcodeSize: big.NewInt(700), ExtcodeCopy: big.NewInt(700), @@ -59,6 +64,19 @@ var ( SLoad: big.NewInt(200), Calls: big.NewInt(700), Suicide: big.NewInt(5000), + ExpByte: big.NewInt(10), + + CreateBySuicide: big.NewInt(25000), + } + + GasTableEIP158 = GasTable{ + ExtcodeSize: big.NewInt(700), + ExtcodeCopy: big.NewInt(700), + Balance: big.NewInt(400), + SLoad: big.NewInt(200), + Calls: big.NewInt(700), + Suicide: big.NewInt(5000), + ExpByte: big.NewInt(50), CreateBySuicide: big.NewInt(25000), } diff --git a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go index 5079eb111..e98925c2b 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go +++ b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go @@ -14,9 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// DO NOT EDIT!!! -// AUTOGENERATED FROM generators/defaults.go - package params import "math/big" @@ -72,4 +69,5 @@ var ( MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + MaxCodeSize = 24576 ) diff --git a/vendor/github.com/ethereum/go-ethereum/params/util.go b/vendor/github.com/ethereum/go-ethereum/params/util.go index 583cf03bb..546ebb35c 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/util.go +++ b/vendor/github.com/ethereum/go-ethereum/params/util.go @@ -16,11 +16,28 @@ package params -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) var ( - TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block - MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block - TestNetHomesteadGasRepriceBlock = big.NewInt(1783000) // Test net gas reprice block - MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Main net gas reprice block + TestNetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on + MainNetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on + + TestNetHomesteadBlock = big.NewInt(0) // Testnet homestead block + MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block + + TestNetHomesteadGasRepriceBlock = big.NewInt(0) // Testnet gas reprice block + MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Mainnet gas reprice block + + TestNetHomesteadGasRepriceHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet gas reprice block hash (used by fast sync) + MainNetHomesteadGasRepriceHash = common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0") // Mainnet gas reprice block hash (used by fast sync) + + TestNetSpuriousDragon = big.NewInt(10) + MainNetSpuriousDragon = big.NewInt(2675000) + + TestNetChainID = big.NewInt(3) // Test net default chain ID + MainNetChainID = big.NewInt(1) // main net default chain ID ) diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/http.go b/vendor/github.com/ethereum/go-ethereum/rpc/http.go index c923580bf..7d4fe5d47 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/http.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/http.go @@ -170,7 +170,7 @@ func newCorsHandler(srv *Server, corsString string) http.Handler { c := cors.New(cors.Options{ AllowedOrigins: allowedOrigins, AllowedMethods: []string{"POST", "GET"}, - MaxAge: 600, + MaxAge: 600, }) return c.Handler(srv) } diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go index 730755c43..c04a015ef 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go @@ -59,9 +59,8 @@ type Config struct { // config is agnostic to where private key is coming from // so managing accounts is outside swarm and left to wrappers func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) { - address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address - dirpath := filepath.Join(path, common.Bytes2Hex(address.Bytes())) + dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes())) err = os.MkdirAll(dirpath, os.ModePerm) if err != nil { return diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go index a35672687..9be60ef94 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go @@ -115,7 +115,11 @@ func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { switch { case r.Method == "POST" || r.Method == "PUT": - key, err := a.Store(r.Body, r.ContentLength, nil) + if r.Header.Get("content-length") == "" { + http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest) + return + } + key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil) if err == nil { glog.V(logger.Debug).Infof("Content for %v stored", key.Log()) } else { diff --git a/vendor/github.com/ethereum/go-ethereum/trie/hasher.go b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go index e395e00d7..e6261819c 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/hasher.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go @@ -58,8 +58,9 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) return hash, n, nil } if n.canUnload(h.cachegen, h.cachelimit) { - // Evict the node from cache. All of its subnodes will have a lower or equal + // Unload the node from cache. All of its subnodes will have a lower or equal // cache generation number. + cacheUnloadCounter.Inc(1) return hash, hash, nil } if !dirty { @@ -75,23 +76,20 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) if err != nil { return hashNode{}, n, err } - // Cache the hash of the ndoe for later reuse. - if hash, ok := hashed.(hashNode); ok && !force { - switch cached := cached.(type) { - case *shortNode: - cached = cached.copy() - cached.flags.hash = hash - if db != nil { - cached.flags.dirty = false - } - return hashed, cached, nil - case *fullNode: - cached = cached.copy() - cached.flags.hash = hash - if db != nil { - cached.flags.dirty = false - } - return hashed, cached, nil + // Cache the hash of the ndoe for later reuse and remove + // the dirty flag in commit mode. It's fine to assign these values directly + // without copying the node first because hashChildren copies it. + cachedHash, _ := hashed.(hashNode) + switch cn := cached.(type) { + case *shortNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false + } + case *fullNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false } } return hashed, cached, nil diff --git a/vendor/github.com/ethereum/go-ethereum/trie/node.go b/vendor/github.com/ethereum/go-ethereum/trie/node.go index de9752c93..4aa0cab65 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/node.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/node.go @@ -104,8 +104,8 @@ func (n valueNode) fstring(ind string) string { return fmt.Sprintf("%x ", []byte(n)) } -func mustDecodeNode(hash, buf []byte) node { - n, err := decodeNode(hash, buf) +func mustDecodeNode(hash, buf []byte, cachegen uint16) node { + n, err := decodeNode(hash, buf, cachegen) if err != nil { panic(fmt.Sprintf("node %x: %v", hash, err)) } @@ -113,7 +113,7 @@ func mustDecodeNode(hash, buf []byte) node { } // decodeNode parses the RLP encoding of a trie node. -func decodeNode(hash, buf []byte) (node, error) { +func decodeNode(hash, buf []byte, cachegen uint16) (node, error) { if len(buf) == 0 { return nil, io.ErrUnexpectedEOF } @@ -123,22 +123,22 @@ func decodeNode(hash, buf []byte) (node, error) { } switch c, _ := rlp.CountValues(elems); c { case 2: - n, err := decodeShort(hash, buf, elems) + n, err := decodeShort(hash, buf, elems, cachegen) return n, wrapError(err, "short") case 17: - n, err := decodeFull(hash, buf, elems) + n, err := decodeFull(hash, buf, elems, cachegen) return n, wrapError(err, "full") default: return nil, fmt.Errorf("invalid number of list elements: %v", c) } } -func decodeShort(hash, buf, elems []byte) (node, error) { +func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) { kbuf, rest, err := rlp.SplitString(elems) if err != nil { return nil, err } - flag := nodeFlag{hash: hash} + flag := nodeFlag{hash: hash, gen: cachegen} key := compactDecode(kbuf) if key[len(key)-1] == 16 { // value node @@ -148,17 +148,17 @@ func decodeShort(hash, buf, elems []byte) (node, error) { } return &shortNode{key, append(valueNode{}, val...), flag}, nil } - r, _, err := decodeRef(rest) + r, _, err := decodeRef(rest, cachegen) if err != nil { return nil, wrapError(err, "val") } return &shortNode{key, r, flag}, nil } -func decodeFull(hash, buf, elems []byte) (*fullNode, error) { - n := &fullNode{flags: nodeFlag{hash: hash}} +func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) { + n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}} for i := 0; i < 16; i++ { - cld, rest, err := decodeRef(elems) + cld, rest, err := decodeRef(elems, cachegen) if err != nil { return n, wrapError(err, fmt.Sprintf("[%d]", i)) } @@ -176,7 +176,7 @@ func decodeFull(hash, buf, elems []byte) (*fullNode, error) { const hashLen = len(common.Hash{}) -func decodeRef(buf []byte) (node, []byte, error) { +func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) { kind, val, rest, err := rlp.Split(buf) if err != nil { return nil, buf, err @@ -189,7 +189,7 @@ func decodeRef(buf []byte) (node, []byte, error) { err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen) return nil, buf, err } - n, err := decodeNode(nil, buf) + n, err := decodeNode(nil, buf, cachegen) return n, rest, err case kind == rlp.String && len(val) == 0: // empty node diff --git a/vendor/github.com/ethereum/go-ethereum/trie/proof.go b/vendor/github.com/ethereum/go-ethereum/trie/proof.go index f193b52df..bea5e5c09 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/proof.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/proof.go @@ -101,7 +101,7 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value if !bytes.Equal(sha.Sum(nil), wantHash) { return nil, fmt.Errorf("bad proof node %d: hash mismatch", i) } - n, err := decodeNode(wantHash, buf) + n, err := decodeNode(wantHash, buf, 0) if err != nil { return nil, fmt.Errorf("bad proof node %d: %v", i, err) } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/sync.go b/vendor/github.com/ethereum/go-ethereum/trie/sync.go index 400dff903..2158ab750 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/sync.go @@ -31,9 +31,9 @@ var ErrNotRequested = errors.New("not requested") // request represents a scheduled or already in-flight state retrieval request. type request struct { - hash common.Hash // Hash of the node data content to retrieve - data []byte // Data content of the node, cached until all subtrees complete - object *node // Target node to populate with retrieved data (hashnode originally) + hash common.Hash // Hash of the node data content to retrieve + data []byte // Data content of the node, cached until all subtrees complete + raw bool // Whether this is a raw entry (code) or a trie node parents []*request // Parent state nodes referencing this entry (notify all upon completion) depth int // Depth level within the trie the node is located to prioritise DFS @@ -82,13 +82,11 @@ func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, c } key := root.Bytes() blob, _ := s.database.Get(key) - if local, err := decodeNode(key, blob); local != nil && err == nil { + if local, err := decodeNode(key, blob, 0); local != nil && err == nil { return } // Assemble the new sub-trie sync request - node := node(hashNode(root.Bytes())) req := &request{ - object: &node, hash: root, depth: depth, callback: callback, @@ -120,6 +118,7 @@ func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) // Assemble the new sub-trie sync request req := &request{ hash: hash, + raw: true, depth: depth, } // If this sub-trie has a designated parent, link them together @@ -143,35 +142,40 @@ func (s *TrieSync) Missing(max int) []common.Hash { return requests } -// Process injects a batch of retrieved trie nodes data. -func (s *TrieSync) Process(results []SyncResult) (int, error) { +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *TrieSync) Process(results []SyncResult) (bool, int, error) { + committed := false + for i, item := range results { // If the item was not requested, bail out request := s.requests[item.Hash] if request == nil { - return i, ErrNotRequested + return committed, i, ErrNotRequested } // If the item is a raw entry request, commit directly - if request.object == nil { + if request.raw { request.data = item.Data s.commit(request, nil) + committed = true continue } // Decode the node data content and update the request - node, err := decodeNode(item.Hash[:], item.Data) + node, err := decodeNode(item.Hash[:], item.Data, 0) if err != nil { - return i, err + return committed, i, err } - *request.object = node request.data = item.Data // Create and schedule a request for all the children nodes - requests, err := s.children(request) + requests, err := s.children(request, node) if err != nil { - return i, err + return committed, i, err } if len(requests) == 0 && request.deps == 0 { s.commit(request, nil) + committed = true continue } request.deps += len(requests) @@ -179,7 +183,7 @@ func (s *TrieSync) Process(results []SyncResult) (int, error) { s.schedule(child) } } - return 0, nil + return committed, 0, nil } // Pending returns the number of state entries currently pending for download. @@ -203,27 +207,25 @@ func (s *TrieSync) schedule(req *request) { // children retrieves all the missing children of a state trie entry for future // retrieval scheduling. -func (s *TrieSync) children(req *request) ([]*request, error) { +func (s *TrieSync) children(req *request, object node) ([]*request, error) { // Gather all the children of the node, irrelevant whether known or not type child struct { - node *node + node node depth int } children := []child{} - switch node := (*req.object).(type) { + switch node := (object).(type) { case *shortNode: - node = node.copy() // Prevents linking all downloaded nodes together. children = []child{{ - node: &node.Val, + node: node.Val, depth: req.depth + len(node.Key), }} case *fullNode: - node = node.copy() for i := 0; i < 17; i++ { if node.Children[i] != nil { children = append(children, child{ - node: &node.Children[i], + node: node.Children[i], depth: req.depth + 1, }) } @@ -236,23 +238,21 @@ func (s *TrieSync) children(req *request) ([]*request, error) { for _, child := range children { // Notify any external watcher of a new key/value node if req.callback != nil { - if node, ok := (*child.node).(valueNode); ok { + if node, ok := (child.node).(valueNode); ok { if err := req.callback(node, req.hash); err != nil { return nil, err } } } // If the child references another node, resolve or schedule - if node, ok := (*child.node).(hashNode); ok { + if node, ok := (child.node).(hashNode); ok { // Try to resolve the node from the local database blob, _ := s.database.Get(node) - if local, err := decodeNode(node[:], blob); local != nil && err == nil { - *child.node = local + if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil { continue } // Locally unknown node, schedule for retrieval requests = append(requests, &request{ - object: child.node, hash: common.BytesToHash(node), parents: []*request{req}, depth: child.depth, diff --git a/vendor/github.com/ethereum/go-ethereum/trie/trie.go b/vendor/github.com/ethereum/go-ethereum/trie/trie.go index 65005bae8..035a80e74 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/trie.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/trie.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/rcrowley/go-metrics" ) var ( @@ -34,6 +35,25 @@ var ( emptyState common.Hash ) +var ( + cacheMissCounter = metrics.NewRegisteredCounter("trie/cachemiss", nil) + cacheUnloadCounter = metrics.NewRegisteredCounter("trie/cacheunload", nil) +) + +// CacheMisses retrieves a global counter measuring the number of cache misses +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheMisses() int64 { + return cacheMissCounter.Count() +} + +// CacheUnloads retrieves a global counter measuring the number of cache unloads +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheUnloads() int64 { + return cacheUnloadCounter.Count() +} + func init() { sha3.NewKeccak256().Sum(emptyState[:0]) } @@ -93,13 +113,11 @@ func New(root common.Hash, db Database) (*Trie, error) { if db == nil { panic("trie.New: cannot use existing root without a database") } - if v, _ := trie.db.Get(root[:]); len(v) == 0 { - return nil, &MissingNodeError{ - RootHash: root, - NodeHash: root, - } + rootnode, err := trie.resolveHash(root[:], nil, nil) + if err != nil { + return nil, err } - trie.root = hashNode(root.Bytes()) + trie.root = rootnode } return trie, nil } @@ -146,14 +164,15 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode if err == nil && didResolve { n = n.copy() n.Val = newnode + n.flags.gen = t.cachegen } return value, n, didResolve, err case *fullNode: value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1) if err == nil && didResolve { n = n.copy() + n.flags.gen = t.cachegen n.Children[key[pos]] = newnode - } return value, n, didResolve, err case hashNode: @@ -249,7 +268,8 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error return false, n, err } n = n.copy() - n.Children[key[0]], n.flags.hash, n.flags.dirty = nn, nil, true + n.flags = t.newFlag() + n.Children[key[0]] = nn return true, n, nil case nil: @@ -333,7 +353,8 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { return false, n, err } n = n.copy() - n.Children[key[0]], n.flags.hash, n.flags.dirty = nn, nil, true + n.flags = t.newFlag() + n.Children[key[0]] = nn // Check how many non-nil entries are left after deleting and // reduce the full node to a short node if only one entry is @@ -419,6 +440,8 @@ func (t *Trie) resolve(n node, prefix, suffix []byte) (node, error) { } func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { + cacheMissCounter.Inc(1) + enc, err := t.db.Get(n) if err != nil || enc == nil { return nil, &MissingNodeError{ @@ -429,7 +452,7 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { SuffixLen: len(suffix), } } - dec := mustDecodeNode(n, enc) + dec := mustDecodeNode(n, enc, t.cachegen) return dec, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor.conf b/vendor/github.com/ethereum/go-ethereum/vendor.conf new file mode 100644 index 000000000..30c96e81b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor.conf @@ -0,0 +1,42 @@ +# package +github.com/ethereum/go-ethereum + +# import +github.com/Azure/azure-sdk-for-go v5.0.0-beta-5-gbd73d95 +github.com/aristanetworks/goarista ockafka-v0.0.2-7-g306a19f +github.com/cespare/cp 165db2f +github.com/davecgh/go-spew v1.0.0-9-g346938d +github.com/ethereum/ethash v23.1-249-g214d4c0 +github.com/fatih/color v1.1.0-4-gbf82308 +github.com/gizak/termui d29684e +github.com/golang/snappy d9eb7a3 +github.com/hashicorp/golang-lru 0a025b7 +github.com/huin/goupnp 949b8a7 +github.com/jackpal/go-nat-pmp v1.0.1-4-g1fa385a +github.com/mattn/go-colorable v0.0.6-7-g6e26b35 +github.com/mattn/go-isatty 66b8e73 +github.com/mattn/go-runewidth v0.0.1-10-g737072b +github.com/mitchellh/go-wordwrap ad45545 +github.com/nsf/termbox-go b6acae5 +github.com/pborman/uuid v1.0-17-g3d4f2ba +github.com/peterh/liner 3c5f577 +github.com/rcrowley/go-metrics ab2277b +github.com/rjeczalik/notify 7e20c15 +github.com/robertkrimen/otto bf1c379 +github.com/rs/cors v1.0 +github.com/rs/xhandler v1.0-1-ged27b6f +github.com/syndtr/goleveldb 6b4daa5 +golang.org/x/crypto 9477e0b +golang.org/x/net 4bb47a1 +golang.org/x/sys c200b10 +golang.org/x/text a8b3843 +golang.org/x/tools 1529f88 +gopkg.in/check.v1 4f90aea +gopkg.in/fatih/set.v0 v0.1.0-3-g27c4092 +gopkg.in/karalabe/cookiejar.v2 8dcd6a7 +gopkg.in/natefinch/npipe.v2 c1b8fa8 +gopkg.in/sourcemap.v1 v1.0.3 +gopkg.in/urfave/cli.v1 v1.18.1 + +# exclude +-golang.org/x/net/context diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go new file mode 100644 index 000000000..50a8eb34a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go @@ -0,0 +1,504 @@ +// 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 . + +package shhapi + +import ( + "encoding/json" + "errors" + "fmt" + mathrand "math/rand" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/whisper/whisperv5" +) + +var whisperOffLineErr = errors.New("whisper is offline") + +// PublicWhisperAPI provides the whisper RPC service. +type PublicWhisperAPI struct { + whisper *whisperv5.Whisper +} + +// NewPublicWhisperAPI create a new RPC whisper service. +func NewPublicWhisperAPI() *PublicWhisperAPI { + w := whisperv5.NewWhisper(nil) + return &PublicWhisperAPI{whisper: w} +} + +// APIs returns the RPC descriptors the Whisper implementation offers +func APIs() []rpc.API { + return []rpc.API{ + { + Namespace: whisperv5.ProtocolName, + Version: whisperv5.ProtocolVersionStr, + Service: NewPublicWhisperAPI(), + Public: true, + }, + } +} + +// Version returns the Whisper version this node offers. +func (api *PublicWhisperAPI) Version() (*rpc.HexNumber, error) { + if api.whisper == nil { + return rpc.NewHexNumber(0), whisperOffLineErr + } + return rpc.NewHexNumber(api.whisper.Version()), nil +} + +// MarkPeerTrusted marks specific peer trusted, which will allow it +// to send historic (expired) messages. +func (api *PublicWhisperAPI) MarkPeerTrusted(peerID rpc.HexBytes) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.MarkPeerTrusted(peerID) +} + +// RequestHistoricMessages requests the peer to deliver the old (expired) messages. +// data contains parameters (time frame, payment details, etc.), required +// by the remote email-like server. Whisper is not aware about the data format, +// it will just forward the raw data to the server. +func (api *PublicWhisperAPI) RequestHistoricMessages(peerID rpc.HexBytes, data rpc.HexBytes) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.RequestHistoricMessages(peerID, data) +} + +// HasIdentity checks if the whisper node is configured with the private key +// of the specified public pair. +func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { + if api.whisper == nil { + return false, whisperOffLineErr + } + return api.whisper.HasIdentity(identity), nil +} + +// DeleteIdentity deletes the specifies key if it exists. +func (api *PublicWhisperAPI) DeleteIdentity(identity string) error { + if api.whisper == nil { + return whisperOffLineErr + } + api.whisper.DeleteIdentity(identity) + return nil +} + +// NewIdentity generates a new cryptographic identity for the client, and injects +// it into the known identities for message decryption. +func (api *PublicWhisperAPI) NewIdentity() (string, error) { + if api.whisper == nil { + return "", whisperOffLineErr + } + identity := api.whisper.NewIdentity() + return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil +} + +// GenerateSymKey generates a random symmetric key and stores it under +// the 'name' id. Will be used in the future for session key exchange. +func (api *PublicWhisperAPI) GenerateSymKey(name string) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.GenerateSymKey(name) +} + +// AddSymKey stores the key under the 'name' id. +func (api *PublicWhisperAPI) AddSymKey(name string, key []byte) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.AddSymKey(name, key) +} + +// HasSymKey returns true if there is a key associated with the name string. +// Otherwise returns false. +func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) { + if api.whisper == nil { + return false, whisperOffLineErr + } + res := api.whisper.HasSymKey(name) + return res, nil +} + +// DeleteSymKey deletes the key associated with the name string if it exists. +func (api *PublicWhisperAPI) DeleteSymKey(name string) error { + if api.whisper == nil { + return whisperOffLineErr + } + api.whisper.DeleteSymKey(name) + return nil +} + +// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. +// Returns the ID of the newly created Filter. +func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, error) { + if api.whisper == nil { + return nil, whisperOffLineErr + } + + filter := whisperv5.Filter{ + Src: crypto.ToECDSAPub(args.From), + KeySym: api.whisper.GetSymKey(args.KeyName), + PoW: args.PoW, + Messages: make(map[common.Hash]*whisperv5.ReceivedMessage), + AcceptP2P: args.AcceptP2P, + } + + if len(filter.KeySym) > 0 { + filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) + } + + for _, t := range args.Topics { + filter.Topics = append(filter.Topics, t) + } + + if len(args.Topics) == 0 { + info := "NewFilter: at least one topic must be specified" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { + info := "NewFilter: key was not found by name: " + args.KeyName + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) == 0 && len(filter.KeySym) == 0 { + info := "NewFilter: filter must contain either symmetric or asymmetric key" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) != 0 && len(filter.KeySym) != 0 { + info := "NewFilter: filter must not contain both symmetric and asymmetric key" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) > 0 { + dst := crypto.ToECDSAPub(args.To) + if !whisperv5.ValidatePublicKey(dst) { + info := "NewFilter: Invalid 'To' address" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) + if filter.KeyAsym == nil { + info := "NewFilter: non-existent identity provided" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + } + + if len(args.From) > 0 { + if !whisperv5.ValidatePublicKey(filter.Src) { + info := "NewFilter: Invalid 'From' address" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + } + + id := api.whisper.Watch(&filter) + return rpc.NewHexNumber(id), nil +} + +// UninstallFilter disables and removes an existing filter. +func (api *PublicWhisperAPI) UninstallFilter(filterId rpc.HexNumber) { + api.whisper.Unwatch(filterId.Int()) +} + +// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. +func (api *PublicWhisperAPI) GetFilterChanges(filterId rpc.HexNumber) []WhisperMessage { + f := api.whisper.GetFilter(filterId.Int()) + if f != nil { + newMail := f.Retrieve() + return toWhisperMessages(newMail) + } + return toWhisperMessages(nil) +} + +// GetMessages retrieves all the known messages that match a specific filter. +func (api *PublicWhisperAPI) GetMessages(filterId rpc.HexNumber) []WhisperMessage { + all := api.whisper.Messages(filterId.Int()) + return toWhisperMessages(all) +} + +// toWhisperMessages converts a Whisper message to a RPC whisper message. +func toWhisperMessages(messages []*whisperv5.ReceivedMessage) []WhisperMessage { + msgs := make([]WhisperMessage, len(messages)) + for i, msg := range messages { + msgs[i] = NewWhisperMessage(msg) + } + return msgs +} + +// Post creates a whisper message and injects it into the network for distribution. +func (api *PublicWhisperAPI) Post(args PostArgs) error { + if api.whisper == nil { + return whisperOffLineErr + } + + params := whisperv5.MessageParams{ + TTL: args.TTL, + Dst: crypto.ToECDSAPub(args.To), + KeySym: api.whisper.GetSymKey(args.KeyName), + Topic: args.Topic, + Payload: args.Payload, + Padding: args.Padding, + WorkTime: args.WorkTime, + PoW: args.PoW, + } + + if len(args.From) > 0 { + pub := crypto.ToECDSAPub(args.From) + if !whisperv5.ValidatePublicKey(pub) { + info := "Post: Invalid 'From' address" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + params.Src = api.whisper.GetIdentity(string(args.From)) + if params.Src == nil { + info := "Post: non-existent identity provided" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + } + + filter := api.whisper.GetFilter(args.FilterID) + if filter == nil && args.FilterID > -1 { + info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if filter != nil { + // get the missing fields from the filter + if params.KeySym == nil && filter.KeySym != nil { + params.KeySym = filter.KeySym + } + if params.Src == nil && filter.Src != nil { + params.Src = filter.KeyAsym + } + if (params.Topic == whisperv5.TopicType{}) { + sz := len(filter.Topics) + if sz < 1 { + info := fmt.Sprintf("Post: no topics in filter # %d", args.FilterID) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } else if sz == 1 { + params.Topic = filter.Topics[0] + } else { + // choose randomly + rnd := mathrand.Intn(sz) + params.Topic = filter.Topics[rnd] + } + } + } + + // validate + if len(args.KeyName) != 0 && len(params.KeySym) == 0 { + info := "Post: key was not found by name: " + args.KeyName + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) == 0 && len(args.KeyName) == 0 { + info := "Post: message must be encrypted either symmetrically or asymmetrically" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) != 0 && len(args.KeyName) != 0 { + info := "Post: ambigous encryption method requested" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) > 0 { + if !whisperv5.ValidatePublicKey(params.Dst) { + info := "Post: Invalid 'To' address" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + } + + // encrypt and send + message := whisperv5.NewSentMessage(¶ms) + envelope, err := message.Wrap(¶ms) + if err != nil { + glog.V(logger.Error).Infof(err.Error()) + return err + } + if len(envelope.Data) > whisperv5.MaxMessageLength { + info := "Post: message is too big" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + if (envelope.Topic == whisperv5.TopicType{} && envelope.IsSymmetric()) { + info := "Post: topic is missing for symmetric encryption" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if args.PeerID != nil { + return api.whisper.SendP2PMessage(args.PeerID, envelope) + } + + return api.whisper.Send(envelope) +} + +type PostArgs struct { + TTL uint32 `json:"ttl"` + From rpc.HexBytes `json:"from"` + To rpc.HexBytes `json:"to"` + KeyName string `json:"keyname"` + Topic whisperv5.TopicType `json:"topic"` + Padding rpc.HexBytes `json:"padding"` + Payload rpc.HexBytes `json:"payload"` + WorkTime uint32 `json:"worktime"` + PoW float64 `json:"pow"` + FilterID int `json:"filter"` + PeerID rpc.HexBytes `json:"directP2P"` +} + +func (args *PostArgs) UnmarshalJSON(data []byte) (err error) { + var obj struct { + TTL uint32 `json:"ttl"` + From rpc.HexBytes `json:"from"` + To rpc.HexBytes `json:"to"` + KeyName string `json:"keyname"` + Topic whisperv5.TopicType `json:"topic"` + Payload rpc.HexBytes `json:"payload"` + Padding rpc.HexBytes `json:"padding"` + WorkTime uint32 `json:"worktime"` + PoW float64 `json:"pow"` + FilterID rpc.HexBytes `json:"filter"` + PeerID rpc.HexBytes `json:"directP2P"` + } + + if err := json.Unmarshal(data, &obj); err != nil { + return err + } + + args.TTL = obj.TTL + args.From = obj.From + args.To = obj.To + args.KeyName = obj.KeyName + args.Topic = obj.Topic + args.Payload = obj.Payload + args.Padding = obj.Padding + args.WorkTime = obj.WorkTime + args.PoW = obj.PoW + args.FilterID = -1 + args.PeerID = obj.PeerID + + if obj.FilterID != nil { + x := whisperv5.BytesToIntBigEndian(obj.FilterID) + args.FilterID = int(x) + } + + return nil +} + +type WhisperFilterArgs struct { + To []byte + From []byte + KeyName string + PoW float64 + Topics []whisperv5.TopicType + AcceptP2P bool +} + +// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a +// JSON message blob into a WhisperFilterArgs structure. +func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { + // Unmarshal the JSON message and sanity check + var obj struct { + To rpc.HexBytes `json:"to"` + From rpc.HexBytes `json:"from"` + KeyName string `json:"keyname"` + PoW float64 `json:"pow"` + Topics []interface{} `json:"topics"` + AcceptP2P bool `json:"acceptP2P"` + } + if err := json.Unmarshal(b, &obj); err != nil { + return err + } + + args.To = obj.To + args.From = obj.From + args.KeyName = obj.KeyName + args.PoW = obj.PoW + args.AcceptP2P = obj.AcceptP2P + + // Construct the topic array + if obj.Topics != nil { + topics := make([]string, len(obj.Topics)) + for i, field := range obj.Topics { + switch value := field.(type) { + case string: + topics[i] = value + case nil: + return fmt.Errorf("topic[%d] is empty", i) + default: + return fmt.Errorf("topic[%d] is not a string", i) + } + } + topicsDecoded := make([]whisperv5.TopicType, len(topics)) + for j, s := range topics { + x := common.FromHex(s) + if x == nil || len(x) != whisperv5.TopicLength { + return fmt.Errorf("topic[%d] is invalid", j) + } + topicsDecoded[j] = whisperv5.BytesToTopic(x) + } + args.Topics = topicsDecoded + } + + return nil +} + +// WhisperMessage is the RPC representation of a whisper message. +type WhisperMessage struct { + Payload string `json:"payload"` + Padding string `json:"padding"` + From string `json:"from"` + To string `json:"to"` + Sent uint32 `json:"sent"` + TTL uint32 `json:"ttl"` + PoW float64 `json:"pow"` + Hash string `json:"hash"` +} + +// NewWhisperMessage converts an internal message into an API version. +func NewWhisperMessage(message *whisperv5.ReceivedMessage) WhisperMessage { + return WhisperMessage{ + Payload: common.ToHex(message.Payload), + Padding: common.ToHex(message.Padding), + From: common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())), + To: common.ToHex(crypto.FromECDSAPub(message.Dst)), + Sent: message.Sent, + TTL: message.TTL, + PoW: message.PoW, + Hash: common.ToHex(message.EnvelopeHash.Bytes()), + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/api.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go index bf94aff4b..c2b74c31f 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/api.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "encoding/json" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go similarity index 98% rename from vendor/github.com/ethereum/go-ethereum/whisper/doc.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go index cfb0b5117..7252f44b1 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/doc.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go @@ -29,4 +29,4 @@ Whisper is a pure identity-based messaging system. Whisper provides a low-level or prejudiced by the low-level hardware attributes and characteristics, particularly the notion of singular endpoints. */ -package whisper +package whisperv2 diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/envelope.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go index 97d489b96..7110ab457 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/envelope.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Envelope element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#envelopes. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/filter.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go index 9f6d6b781..8ce4a54fb 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go @@ -16,7 +16,7 @@ // Contains the message filter for fine grained subscriptions. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/main.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/main.go similarity index 100% rename from vendor/github.com/ethereum/go-ethereum/whisper/main.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/main.go diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/message.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/message.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go index f05b5d8b5..7ef9d0912 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/message.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Message element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/peer.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/peer.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go index ee10e66e7..404ebd513 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "fmt" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/topic.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/topic.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go index d37eb25ee..3e2b47bd3 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/topic.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Topic element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics. -package whisper +package whisperv2 import "github.com/ethereum/go-ethereum/crypto" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/whisper.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go index 91cfc9b6e..8ef584b84 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisper.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go new file mode 100644 index 000000000..ef3b93d12 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go @@ -0,0 +1,87 @@ +// 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 . + +/* +Package whisper implements the Whisper PoC-1. + +(https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec) + +Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). +As such it may be likened and compared to both, not dissimilar to the +matter/energy duality (apologies to physicists for the blatant abuse of a +fundamental and beautiful natural principle). + +Whisper is a pure identity-based messaging system. Whisper provides a low-level +(non-application-specific) but easily-accessible API without being based upon +or prejudiced by the low-level hardware attributes and characteristics, +particularly the notion of singular endpoints. +*/ +package whisperv5 + +import ( + "fmt" + "time" +) + +const ( + EnvelopeVersion = uint64(0) + ProtocolVersion = uint64(5) + ProtocolVersionStr = "5.0" + ProtocolName = "shh" + + statusCode = 0 + messagesCode = 1 + p2pCode = 2 + mailRequestCode = 3 + NumberOfMessageCodes = 4 + + paddingMask = byte(3) + signatureFlag = byte(4) + + TopicLength = 4 + signatureLength = 65 + aesKeyLength = 32 + saltLength = 12 + + MaxMessageLength = 0xFFFF // todo: remove this restriction after testing in morden and analizing stats. this should be regulated by MinimumPoW. + MinimumPoW = 10.0 // todo: review + + padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature + padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility + + expirationCycle = time.Second + transmissionCycle = 300 * time.Millisecond + + DefaultTTL = 50 // seconds + SynchAllowance = 10 // seconds +) + +type unknownVersionError uint64 + +func (e unknownVersionError) Error() string { + return fmt.Sprintf("invalid envelope version %d", uint64(e)) +} + +// MailServer represents a mail server, capable of +// archiving the old messages for subsequent delivery +// to the peers. Any implementation must ensure that both +// functions are thread-safe. Also, they must return ASAP. +// DeliverMail should use directMessagesCode for delivery, +// in order to bypass the expiry checks. +type MailServer interface { + Archive(env *Envelope) + DeliverMail(whisperPeer *Peer, data []byte) +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go new file mode 100644 index 000000000..57d454a08 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go @@ -0,0 +1,233 @@ +// 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 . + +// Contains the Whisper protocol Envelope element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#envelopes. + +package whisperv5 + +import ( + "crypto/ecdsa" + "encoding/binary" + "fmt" + "math" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/rlp" +) + +// Envelope represents a clear-text data packet to transmit through the Whisper +// network. Its contents may or may not be encrypted and signed. +type Envelope struct { + Version []byte + Expiry uint32 + TTL uint32 + Topic TopicType + Salt []byte + AESNonce []byte + Data []byte + EnvNonce uint64 + + pow float64 // Message-specific PoW as described in the Whisper specification. + hash common.Hash // Cached hash of the envelope to avoid rehashing every time. + // Don't access hash directly, use Hash() function instead. +} + +// NewEnvelope wraps a Whisper message with expiration and destination data +// included into an envelope for network forwarding. +func NewEnvelope(ttl uint32, topic TopicType, salt []byte, aesNonce []byte, msg *SentMessage) *Envelope { + env := Envelope{ + Version: make([]byte, 1), + Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), + TTL: ttl, + Topic: topic, + Salt: salt, + AESNonce: aesNonce, + Data: msg.Raw, + EnvNonce: 0, + } + + if EnvelopeVersion < 256 { + env.Version[0] = byte(EnvelopeVersion) + } else { + panic("please increase the size of Envelope.Version before releasing this version") + } + + return &env +} + +func (e *Envelope) IsSymmetric() bool { + return e.AESNonce != nil +} + +func (e *Envelope) isAsymmetric() bool { + return !e.IsSymmetric() +} + +func (e *Envelope) Ver() uint64 { + return bytesToIntLittleEndian(e.Version) +} + +// Seal closes the envelope by spending the requested amount of time as a proof +// of work on hashing the data. +func (e *Envelope) Seal(options *MessageParams) { + var target int + if options.PoW == 0 { + // adjust for the duration of Seal() execution only if execution time is predefined unconditionally + e.Expiry += options.WorkTime + } else { + target = e.powToFirstBit(options.PoW) + } + + buf := make([]byte, 64) + h := crypto.Keccak256(e.rlpWithoutNonce()) + copy(buf[:32], h) + + finish, bestBit := time.Now().Add(time.Duration(options.WorkTime)*time.Second).UnixNano(), 0 + for nonce := uint64(0); time.Now().UnixNano() < finish; { + for i := 0; i < 1024; i++ { + binary.BigEndian.PutUint64(buf[56:], nonce) + h = crypto.Keccak256(buf) + firstBit := common.FirstBitSet(common.BigD(h)) + if firstBit > bestBit { + e.EnvNonce, bestBit = nonce, firstBit + if target > 0 && bestBit >= target { + return + } + } + nonce++ + } + } +} + +func (e *Envelope) PoW() float64 { + if e.pow == 0 { + e.calculatePoW(0) + } + return e.pow +} + +func (e *Envelope) calculatePoW(diff uint32) { + buf := make([]byte, 64) + h := crypto.Keccak256(e.rlpWithoutNonce()) + copy(buf[:32], h) + binary.BigEndian.PutUint64(buf[56:], e.EnvNonce) + h = crypto.Keccak256(buf) + firstBit := common.FirstBitSet(common.BigD(h)) + x := math.Pow(2, float64(firstBit)) + x /= float64(len(e.Data)) + x /= float64(e.TTL + diff) + e.pow = x +} + +func (e *Envelope) powToFirstBit(pow float64) int { + x := pow + x *= float64(len(e.Data)) + x *= float64(e.TTL) + bits := math.Log2(x) + bits = math.Ceil(bits) + return int(bits) +} + +// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. +func (e *Envelope) rlpWithoutNonce() []byte { + res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Salt, e.AESNonce, e.Data}) + return res +} + +// Hash returns the SHA3 hash of the envelope, calculating it if not yet done. +func (e *Envelope) Hash() common.Hash { + if (e.hash == common.Hash{}) { + encoded, _ := rlp.EncodeToBytes(e) + e.hash = crypto.Keccak256Hash(encoded) + } + return e.hash +} + +// DecodeRLP decodes an Envelope from an RLP data stream. +func (e *Envelope) DecodeRLP(s *rlp.Stream) error { + raw, err := s.Raw() + if err != nil { + return err + } + // The decoding of Envelope uses the struct fields but also needs + // to compute the hash of the whole RLP-encoded envelope. This + // type has the same structure as Envelope but is not an + // rlp.Decoder (does not implement DecodeRLP function). + // Only public members will be encoded. + type rlpenv Envelope + if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { + return err + } + e.hash = crypto.Keccak256Hash(raw) + return nil +} + +// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. +func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { + message := &ReceivedMessage{Raw: e.Data} + err := message.decryptAsymmetric(key) + switch err { + case nil: + return message, nil + case ecies.ErrInvalidPublicKey: // addressed to somebody else + return nil, err + default: + return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) + } +} + +// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. +func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { + msg = &ReceivedMessage{Raw: e.Data} + err = msg.decryptSymmetric(key, e.Salt, e.AESNonce) + if err != nil { + msg = nil + } + return msg, err +} + +// Open tries to decrypt an envelope, and populates the message fields in case of success. +func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { + if e.isAsymmetric() { + msg, _ = e.OpenAsymmetric(watcher.KeyAsym) + if msg != nil { + msg.Dst = &watcher.KeyAsym.PublicKey + } + } else if e.IsSymmetric() { + msg, _ = e.OpenSymmetric(watcher.KeySym) + if msg != nil { + msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) + } + } + + if msg != nil { + ok := msg.Validate() + if !ok { + return nil + } + msg.Topic = e.Topic + msg.PoW = e.PoW() + msg.TTL = e.TTL + msg.Sent = e.Expiry - e.TTL + msg.EnvelopeHash = e.Hash() + msg.EnvelopeVersion = e.Ver() + } + return msg +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go new file mode 100644 index 000000000..5cc7be587 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go @@ -0,0 +1,197 @@ +// 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 . + +package whisperv5 + +import ( + "crypto/ecdsa" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +type Filter struct { + Src *ecdsa.PublicKey // Sender of the message + KeyAsym *ecdsa.PrivateKey // Private Key of recipient + KeySym []byte // Key associated with the Topic + Topics []TopicType // Topics to filter messages with + PoW float64 // Proof of work as described in the Whisper spec + AcceptP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages + SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization + + Messages map[common.Hash]*ReceivedMessage + mutex sync.RWMutex +} + +type Filters struct { + id int + watchers map[int]*Filter + whisper *Whisper + mutex sync.RWMutex +} + +func NewFilters(w *Whisper) *Filters { + return &Filters{ + watchers: make(map[int]*Filter), + whisper: w, + } +} + +func (fs *Filters) Install(watcher *Filter) int { + if watcher.Messages == nil { + watcher.Messages = make(map[common.Hash]*ReceivedMessage) + } + + fs.mutex.Lock() + defer fs.mutex.Unlock() + + fs.watchers[fs.id] = watcher + ret := fs.id + fs.id++ + return ret +} + +func (fs *Filters) Uninstall(id int) { + fs.mutex.Lock() + defer fs.mutex.Unlock() + delete(fs.watchers, id) +} + +func (fs *Filters) Get(i int) *Filter { + fs.mutex.RLock() + defer fs.mutex.RUnlock() + return fs.watchers[i] +} + +func (fs *Filters) NotifyWatchers(env *Envelope, messageCode uint64) { + fs.mutex.RLock() + var msg *ReceivedMessage + for _, watcher := range fs.watchers { + if messageCode == p2pCode && !watcher.AcceptP2P { + continue + } + + match := false + if msg != nil { + match = watcher.MatchMessage(msg) + } else { + match = watcher.MatchEnvelope(env) + if match { + msg = env.Open(watcher) + } + } + + if match && msg != nil { + watcher.Trigger(msg) + } + } + fs.mutex.RUnlock() // we need to unlock before calling addDecryptedMessage + + if msg != nil { + fs.whisper.addDecryptedMessage(msg) + } +} + +func (f *Filter) expectsAsymmetricEncryption() bool { + return f.KeyAsym != nil +} + +func (f *Filter) expectsSymmetricEncryption() bool { + return f.KeySym != nil +} + +func (f *Filter) Trigger(msg *ReceivedMessage) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if _, exist := f.Messages[msg.EnvelopeHash]; !exist { + f.Messages[msg.EnvelopeHash] = msg + } +} + +func (f *Filter) Retrieve() (all []*ReceivedMessage) { + f.mutex.Lock() + defer f.mutex.Unlock() + + all = make([]*ReceivedMessage, 0, len(f.Messages)) + for _, msg := range f.Messages { + all = append(all, msg) + } + f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages + return all +} + +func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { + if f.PoW > 0 && msg.PoW < f.PoW { + return false + } + if f.Src != nil && !isPubKeyEqual(msg.Src, f.Src) { + return false + } + + if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { + // if Dst match, ignore the topic + return isPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) + } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { + // check if that both the key and the topic match + if f.SymKeyHash == msg.SymKeyHash { + for _, t := range f.Topics { + if t == msg.Topic { + return true + } + } + return false + } + } + return false +} + +func (f *Filter) MatchEnvelope(envelope *Envelope) bool { + if f.PoW > 0 && envelope.pow < f.PoW { + return false + } + + encryptionMethodMatch := false + if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() { + encryptionMethodMatch = true + if f.Topics == nil { + // wildcard + return true + } + } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { + encryptionMethodMatch = true + } + + if encryptionMethodMatch { + for _, t := range f.Topics { + if t == envelope.Topic { + return true + } + } + } + + return false +} + +func isPubKeyEqual(a, b *ecdsa.PublicKey) bool { + if !ValidatePublicKey(a) { + return false + } else if !ValidatePublicKey(b) { + return false + } + // the Curve is always the same, just compare the points + return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go new file mode 100644 index 000000000..680d1f8a2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go @@ -0,0 +1,378 @@ +// 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 . + +// Contains the Whisper protocol Message element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. +// todo: fix the spec link, and move it to doc.go + +package whisperv5 + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + crand "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + mrand "math/rand" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/crypto/pbkdf2" +) + +// Options specifies the exact way a message should be wrapped into an Envelope. +type MessageParams struct { + TTL uint32 + Src *ecdsa.PrivateKey + Dst *ecdsa.PublicKey + KeySym []byte + Topic TopicType + WorkTime uint32 + PoW float64 + Payload []byte + Padding []byte +} + +// SentMessage represents an end-user data packet to transmit through the +// Whisper protocol. These are wrapped into Envelopes that need not be +// understood by intermediate nodes, just forwarded. +type SentMessage struct { + Raw []byte +} + +// ReceivedMessage represents a data packet to be received through the +// Whisper protocol. +type ReceivedMessage struct { + Raw []byte + + Payload []byte + Padding []byte + Signature []byte + + PoW float64 // Proof of work as described in the Whisper spec + Sent uint32 // Time when the message was posted into the network + TTL uint32 // Maximum time to live allowed for the message + Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) + Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) + Topic TopicType + + SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic + EnvelopeHash common.Hash // Message envelope hash to act as a unique id + EnvelopeVersion uint64 +} + +func isMessageSigned(flags byte) bool { + return (flags & signatureFlag) != 0 +} + +func (msg *ReceivedMessage) isSymmetricEncryption() bool { + return msg.SymKeyHash != common.Hash{} +} + +func (msg *ReceivedMessage) isAsymmetricEncryption() bool { + return msg.Dst != nil +} + +func DeriveOneTimeKey(key []byte, salt []byte, version uint64) ([]byte, error) { + if version == 0 { + derivedKey := pbkdf2.Key(key, salt, 8, aesKeyLength, sha256.New) + return derivedKey, nil + } else { + return nil, unknownVersionError(version) + } +} + +// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. +func NewSentMessage(params *MessageParams) *SentMessage { + msg := SentMessage{} + msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper) + msg.Raw[0] = 0 // set all the flags to zero + msg.appendPadding(params) + msg.Raw = append(msg.Raw, params.Payload...) + return &msg +} + +// appendPadding appends the pseudorandom padding bytes and sets the padding flag. +// The last byte contains the size of padding (thus, its size must not exceed 256). +func (msg *SentMessage) appendPadding(params *MessageParams) { + total := len(params.Payload) + 1 + if params.Src != nil { + total += signatureLength + } + padChunk := padSizeLimitUpper + if total <= padSizeLimitLower { + padChunk = padSizeLimitLower + } + odd := total % padChunk + if odd > 0 { + padSize := padChunk - odd + if padSize > 255 { + // this algorithm is only valid if padSizeLimitUpper <= 256. + // if padSizeLimitUpper will ever change, please fix the algorithm + // (for more information see ReceivedMessage.extractPadding() function). + panic("please fix the padding algorithm before releasing new version") + } + buf := make([]byte, padSize) + randomize(buf[1:]) // change to: err = mrand.Read(buf[1:]) + buf[0] = byte(padSize) + if params.Padding != nil { + copy(buf[1:], params.Padding) + } + msg.Raw = append(msg.Raw, buf...) + msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size + } +} + +// sign calculates and sets the cryptographic signature for the message, +// also setting the sign flag. +func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { + if isMessageSigned(msg.Raw[0]) { + // this should not happen, but no reason to panic + glog.V(logger.Error).Infof("Trying to sign a message which was already signed") + return nil + } + + msg.Raw[0] |= signatureFlag + hash := crypto.Keccak256(msg.Raw) + signature, err := crypto.Sign(hash, key) + if err != nil { + msg.Raw[0] &= ^signatureFlag // clear the flag + return err + } + msg.Raw = append(msg.Raw, signature...) + return nil +} + +// encryptAsymmetric encrypts a message with a public key. +func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { + if !ValidatePublicKey(key) { + return fmt.Errorf("Invalid public key provided for asymmetric encryption") + } + encrypted, err := crypto.Encrypt(key, msg.Raw) + if err == nil { + msg.Raw = encrypted + } + return err +} + +// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. +// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). +func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, err error) { + if !validateSymmetricKey(key) { + return nil, nil, errors.New("invalid key provided for symmetric encryption") + } + + salt = make([]byte, saltLength) + _, err = crand.Read(salt) + if err != nil { + return nil, nil, err + } else if !validateSymmetricKey(salt) { + return nil, nil, errors.New("crypto/rand failed to generate salt") + } + + derivedKey, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) + if err != nil { + return nil, nil, err + } + if !validateSymmetricKey(derivedKey) { + return nil, nil, errors.New("failed to derive one-time key") + } + block, err := aes.NewCipher(derivedKey) + if err != nil { + return nil, nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + // never use more than 2^32 random nonces with a given key + nonce = make([]byte, aesgcm.NonceSize()) + _, err = crand.Read(nonce) + if err != nil { + return nil, nil, err + } + msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) + return salt, nonce, nil +} + +// Wrap bundles the message into an Envelope to transmit over the network. +// +// pow (Proof Of Work) controls how much time to spend on hashing the message, +// inherently controlling its priority through the network (smaller hash, bigger +// priority). +// +// The user can control the amount of identity, privacy and encryption through +// the options parameter as follows: +// - options.From == nil && options.To == nil: anonymous broadcast +// - options.From != nil && options.To == nil: signed broadcast (known sender) +// - options.From == nil && options.To != nil: encrypted anonymous message +// - options.From != nil && options.To != nil: encrypted signed message +func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { + if options.TTL == 0 { + options.TTL = DefaultTTL + } + if options.Src != nil { + err = msg.sign(options.Src) + if err != nil { + return nil, err + } + } + if len(msg.Raw) > MaxMessageLength { + glog.V(logger.Error).Infof("Message size must not exceed %d bytes", MaxMessageLength) + return nil, errors.New("Oversized message") + } + var salt, nonce []byte + if options.Dst != nil { + err = msg.encryptAsymmetric(options.Dst) + } else if options.KeySym != nil { + salt, nonce, err = msg.encryptSymmetric(options.KeySym) + } else { + err = errors.New("Unable to encrypt the message: neither Dst nor Key") + } + + if err != nil { + return nil, err + } + + envelope = NewEnvelope(options.TTL, options.Topic, salt, nonce, msg) + envelope.Seal(options) + return envelope, nil +} + +// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. +// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). +func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []byte) error { + derivedKey, err := DeriveOneTimeKey(key, salt, msg.EnvelopeVersion) + if err != nil { + return err + } + + block, err := aes.NewCipher(derivedKey) + if err != nil { + return err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return err + } + if len(nonce) != aesgcm.NonceSize() { + info := fmt.Sprintf("Wrong AES nonce size - want: %d, got: %d", len(nonce), aesgcm.NonceSize()) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) + if err != nil { + return err + } + msg.Raw = decrypted + return nil +} + +// decryptAsymmetric decrypts an encrypted payload with a private key. +func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { + decrypted, err := crypto.Decrypt(key, msg.Raw) + if err == nil { + msg.Raw = decrypted + } + return err +} + +// Validate checks the validity and extracts the fields in case of success +func (msg *ReceivedMessage) Validate() bool { + end := len(msg.Raw) + if end < 1 { + return false + } + + if isMessageSigned(msg.Raw[0]) { + end -= signatureLength + if end <= 1 { + return false + } + msg.Signature = msg.Raw[end:] + msg.Src = msg.SigToPubKey() + if msg.Src == nil { + return false + } + } + + padSize, ok := msg.extractPadding(end) + if !ok { + return false + } + + msg.Payload = msg.Raw[1+padSize : end] + return true +} + +// extractPadding extracts the padding from raw message. +// although we don't support sending messages with padding size +// exceeding 255 bytes, such messages are perfectly valid, and +// can be successfully decrypted. +func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { + paddingSize := 0 + sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero + if sz != 0 { + paddingSize = int(bytesToIntLittleEndian(msg.Raw[1 : 1+sz])) + if paddingSize < sz || paddingSize+1 > end { + return 0, false + } + msg.Padding = msg.Raw[1+sz : 1+paddingSize] + } + return paddingSize, true +} + +// Recover retrieves the public key of the message signer. +func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { + defer func() { recover() }() // in case of invalid signature + + pub, err := crypto.SigToPub(msg.hash(), msg.Signature) + if err != nil { + glog.V(logger.Error).Infof("Could not get public key from signature: %v", err) + return nil + } + return pub +} + +// hash calculates the SHA3 checksum of the message flags, payload and padding. +func (msg *ReceivedMessage) hash() []byte { + if isMessageSigned(msg.Raw[0]) { + sz := len(msg.Raw) - signatureLength + return crypto.Keccak256(msg.Raw[:sz]) + } + return crypto.Keccak256(msg.Raw) +} + +// rand.Rand provides a Read method in Go 1.7 and later, +// but we can't use it yet. +func randomize(b []byte) { + cnt := 0 + val := mrand.Int63() + for n := 0; n < len(b); n++ { + b[n] = byte(val) + val >>= 8 + cnt++ + if cnt >= 7 { + cnt = 0 + val = mrand.Int63() + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go new file mode 100644 index 000000000..fc1afb6d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go @@ -0,0 +1,174 @@ +// 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 . + +package whisperv5 + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + set "gopkg.in/fatih/set.v0" +) + +// peer represents a whisper protocol peer connection. +type Peer struct { + host *Whisper + peer *p2p.Peer + ws p2p.MsgReadWriter + trusted bool + + known *set.Set // Messages already known by the peer to avoid wasting bandwidth + + quit chan struct{} +} + +// newPeer creates a new whisper peer object, but does not run the handshake itself. +func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { + return &Peer{ + host: host, + peer: remote, + ws: rw, + trusted: false, + known: set.New(), + quit: make(chan struct{}), + } +} + +// start initiates the peer updater, periodically broadcasting the whisper packets +// into the network. +func (p *Peer) start() { + go p.update() + glog.V(logger.Debug).Infof("%v: whisper started", p.peer) +} + +// stop terminates the peer updater, stopping message forwarding to it. +func (p *Peer) stop() { + close(p.quit) + glog.V(logger.Debug).Infof("%v: whisper stopped", p.peer) +} + +// handshake sends the protocol initiation status message to the remote peer and +// verifies the remote status too. +func (p *Peer) handshake() error { + // Send the handshake status message asynchronously + errc := make(chan error, 1) + go func() { + errc <- p2p.Send(p.ws, statusCode, ProtocolVersion) + }() + // Fetch the remote status packet and verify protocol match + packet, err := p.ws.ReadMsg() + if err != nil { + return err + } + if packet.Code != statusCode { + return fmt.Errorf("peer sent %x before status packet", packet.Code) + } + s := rlp.NewStream(packet.Payload, uint64(packet.Size)) + peerVersion, err := s.Uint() + if err != nil { + return fmt.Errorf("bad status message: %v", err) + } + if peerVersion != ProtocolVersion { + return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, ProtocolVersion) + } + // Wait until out own status is consumed too + if err := <-errc; err != nil { + return fmt.Errorf("failed to send status packet: %v", err) + } + return nil +} + +// update executes periodic operations on the peer, including message transmission +// and expiration. +func (p *Peer) update() { + // Start the tickers for the updates + expire := time.NewTicker(expirationCycle) + transmit := time.NewTicker(transmissionCycle) + + // Loop and transmit until termination is requested + for { + select { + case <-expire.C: + p.expire() + + case <-transmit.C: + if err := p.broadcast(); err != nil { + glog.V(logger.Info).Infof("%v: broadcast failed: %v", p.peer, err) + return + } + + case <-p.quit: + return + } + } +} + +// mark marks an envelope known to the peer so that it won't be sent back. +func (peer *Peer) mark(envelope *Envelope) { + peer.known.Add(envelope.Hash()) +} + +// marked checks if an envelope is already known to the remote peer. +func (peer *Peer) marked(envelope *Envelope) bool { + return peer.known.Has(envelope.Hash()) +} + +// expire iterates over all the known envelopes in the host and removes all +// expired (unknown) ones from the known list. +func (peer *Peer) expire() { + // Assemble the list of available envelopes + available := set.NewNonTS() + for _, envelope := range peer.host.Envelopes() { + available.Add(envelope.Hash()) + } + // Cross reference availability with known status + unmark := make(map[common.Hash]struct{}) + peer.known.Each(func(v interface{}) bool { + if !available.Has(v.(common.Hash)) { + unmark[v.(common.Hash)] = struct{}{} + } + return true + }) + // Dump all known but unavailable + for hash, _ := range unmark { + peer.known.Remove(hash) + } +} + +// broadcast iterates over the collection of envelopes and transmits yet unknown +// ones over the network. +func (p *Peer) broadcast() error { + // Fetch the envelopes and collect the unknown ones + envelopes := p.host.Envelopes() + transmit := make([]*Envelope, 0, len(envelopes)) + for _, envelope := range envelopes { + if !p.marked(envelope) { + transmit = append(transmit, envelope) + p.mark(envelope) + } + } + // Transmit the unknown batch (potentially empty) + if err := p2p.Send(p.ws, messagesCode, transmit); err != nil { + return err + } + glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)") + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go new file mode 100644 index 000000000..c29c344be --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go @@ -0,0 +1,70 @@ +// 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 . + +// Contains the Whisper protocol Topic element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics. + +package whisperv5 + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Topic represents a cryptographically secure, probabilistic partial +// classifications of a message, determined as the first (left) 4 bytes of the +// SHA3 hash of some arbitrary data given by the original author of the message. +type TopicType [TopicLength]byte + +func BytesToTopic(b []byte) (t TopicType) { + sz := TopicLength + if x := len(b); x < TopicLength { + sz = x + } + for i := 0; i < sz; i++ { + t[i] = b[i] + } + return t +} + +// String converts a topic byte array to a string representation. +func (topic *TopicType) String() string { + return string(common.ToHex(topic[:])) +} + +// UnmarshalJSON parses a hex representation to a topic. +func (t *TopicType) UnmarshalJSON(input []byte) error { + length := len(input) + if length >= 2 && input[0] == '"' && input[length-1] == '"' { + input = input[1 : length-1] + } + // strip "0x" for length check + if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" { + input = input[2:] + } + // validate the length of the input + if len(input) != TopicLength*2 { + return fmt.Errorf("unmarshalJSON failed: topic must be exactly %d bytes", TopicLength) + } + b := common.FromHex(string(input)) + if b == nil { + return fmt.Errorf("unmarshalJSON failed: wrong topic format") + } + *t = BytesToTopic(b) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go new file mode 100644 index 000000000..836810824 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go @@ -0,0 +1,585 @@ +// 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 . + +package whisperv5 + +import ( + "bytes" + "crypto/ecdsa" + crand "crypto/rand" + "crypto/sha256" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/pbkdf2" + set "gopkg.in/fatih/set.v0" +) + +// Whisper represents a dark communication interface through the Ethereum +// network, using its very own P2P communication layer. +type Whisper struct { + protocol p2p.Protocol + filters *Filters + + privateKeys map[string]*ecdsa.PrivateKey + symKeys map[string][]byte + keyMu sync.RWMutex + + envelopes map[common.Hash]*Envelope // Pool of messages currently tracked by this node + messages map[common.Hash]*ReceivedMessage // Pool of successfully decrypted messages, which are not expired yet + expirations map[uint32]*set.SetNonTS // Message expiration pool + poolMu sync.RWMutex // Mutex to sync the message and expiration pools + + peers map[*Peer]struct{} // Set of currently active peers + peerMu sync.RWMutex // Mutex to sync the active peer set + + mailServer MailServer + + quit chan struct{} + test bool +} + +// New creates a Whisper client ready to communicate through the Ethereum P2P network. +// Param s should be passed if you want to implement mail server, otherwise nil. +func NewWhisper(server MailServer) *Whisper { + whisper := &Whisper{ + privateKeys: make(map[string]*ecdsa.PrivateKey), + symKeys: make(map[string][]byte), + envelopes: make(map[common.Hash]*Envelope), + messages: make(map[common.Hash]*ReceivedMessage), + expirations: make(map[uint32]*set.SetNonTS), + peers: make(map[*Peer]struct{}), + mailServer: server, + quit: make(chan struct{}), + } + whisper.filters = NewFilters(whisper) + + // p2p whisper sub protocol handler + whisper.protocol = p2p.Protocol{ + Name: ProtocolName, + Version: uint(ProtocolVersion), + Length: NumberOfMessageCodes, + Run: whisper.HandlePeer, + } + + return whisper +} + +// Protocols returns the whisper sub-protocols ran by this particular client. +func (w *Whisper) Protocols() []p2p.Protocol { + return []p2p.Protocol{w.protocol} +} + +// Version returns the whisper sub-protocols version number. +func (w *Whisper) Version() uint { + return w.protocol.Version +} + +func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { + w.peerMu.Lock() + defer w.peerMu.Unlock() + for p, _ := range w.peers { + id := p.peer.ID() + if bytes.Equal(peerID, id[:]) { + return p, nil + } + } + return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) +} + +// MarkPeerTrusted marks specific peer trusted, which will allow it +// to send historic (expired) messages. +func (w *Whisper) MarkPeerTrusted(peerID []byte) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + p.trusted = true + return nil +} + +func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + p.trusted = true + return p2p.Send(p.ws, mailRequestCode, data) +} + +func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + return p2p.Send(p.ws, p2pCode, envelope) +} + +// NewIdentity generates a new cryptographic identity for the client, and injects +// it into the known identities for message decryption. +func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { + key, err := crypto.GenerateKey() + if err != nil || !validatePrivateKey(key) { + key, err = crypto.GenerateKey() // retry once + } + if err != nil { + panic(err) + } + if !validatePrivateKey(key) { + panic("Failed to generate valid key") + } + w.keyMu.Lock() + defer w.keyMu.Unlock() + w.privateKeys[common.ToHex(crypto.FromECDSAPub(&key.PublicKey))] = key + return key +} + +// DeleteIdentity deletes the specified key if it exists. +func (w *Whisper) DeleteIdentity(key string) { + w.keyMu.Lock() + defer w.keyMu.Unlock() + delete(w.privateKeys, key) +} + +// HasIdentity checks if the the whisper node is configured with the private key +// of the specified public pair. +func (w *Whisper) HasIdentity(pubKey string) bool { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.privateKeys[pubKey] != nil +} + +// GetIdentity retrieves the private key of the specified public identity. +func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.privateKeys[pubKey] +} + +func (w *Whisper) GenerateSymKey(name string) error { + buf := make([]byte, aesKeyLength*2) + _, err := crand.Read(buf) // todo: check how safe is this function + if err != nil { + return err + } else if !validateSymmetricKey(buf) { + return fmt.Errorf("crypto/rand failed to generate random data") + } + + key := buf[:aesKeyLength] + salt := buf[aesKeyLength:] + derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) + if err != nil { + return err + } else if !validateSymmetricKey(derived) { + return fmt.Errorf("failed to derive valid key") + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + if w.symKeys[name] != nil { + return fmt.Errorf("Key with name [%s] already exists", name) + } + w.symKeys[name] = derived + return nil +} + +func (w *Whisper) AddSymKey(name string, key []byte) error { + if w.HasSymKey(name) { + return fmt.Errorf("Key with name [%s] already exists", name) + } + + derived, err := deriveKeyMaterial(key, EnvelopeVersion) + if err != nil { + return err + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + // double check is necessary, because deriveKeyMaterial() is slow + if w.symKeys[name] != nil { + return fmt.Errorf("Key with name [%s] already exists", name) + } + w.symKeys[name] = derived + return nil +} + +func (w *Whisper) HasSymKey(name string) bool { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.symKeys[name] != nil +} + +func (w *Whisper) DeleteSymKey(name string) { + w.keyMu.Lock() + defer w.keyMu.Unlock() + delete(w.symKeys, name) +} + +func (w *Whisper) GetSymKey(name string) []byte { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.symKeys[name] +} + +// Watch installs a new message handler to run in case a matching packet arrives +// from the whisper network. +func (w *Whisper) Watch(f *Filter) int { + return w.filters.Install(f) +} + +func (w *Whisper) GetFilter(id int) *Filter { + return w.filters.Get(id) +} + +// Unwatch removes an installed message handler. +func (w *Whisper) Unwatch(id int) { + w.filters.Uninstall(id) +} + +// Send injects a message into the whisper send queue, to be distributed in the +// network in the coming cycles. +func (w *Whisper) Send(envelope *Envelope) error { + return w.add(envelope) +} + +// Start implements node.Service, starting the background data propagation thread +// of the Whisper protocol. +func (w *Whisper) Start(*p2p.Server) error { + glog.V(logger.Info).Infoln("Whisper started") + go w.update() + return nil +} + +// Stop implements node.Service, stopping the background data propagation thread +// of the Whisper protocol. +func (w *Whisper) Stop() error { + close(w.quit) + glog.V(logger.Info).Infoln("Whisper stopped") + return nil +} + +// handlePeer is called by the underlying P2P layer when the whisper sub-protocol +// connection is negotiated. +func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { + // Create the new peer and start tracking it + whisperPeer := newPeer(wh, peer, rw) + + wh.peerMu.Lock() + wh.peers[whisperPeer] = struct{}{} + wh.peerMu.Unlock() + + defer func() { + wh.peerMu.Lock() + delete(wh.peers, whisperPeer) + wh.peerMu.Unlock() + }() + + // Run the peer handshake and state updates + if err := whisperPeer.handshake(); err != nil { + return err + } + whisperPeer.start() + defer whisperPeer.stop() + + return wh.runMessageLoop(whisperPeer, rw) +} + +// runMessageLoop reads and processes inbound messages directly to merge into client-global state. +func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { + for { + // fetch the next packet + packet, err := rw.ReadMsg() + if err != nil { + return err + } + + switch packet.Code { + case statusCode: + // this should not happen, but no need to panic; just ignore this message. + glog.V(logger.Warn).Infof("%v: unxepected status message received", p.peer) + case messagesCode: + // decode the contained envelopes + var envelopes []*Envelope + if err := packet.Decode(&envelopes); err != nil { + glog.V(logger.Warn).Infof("%v: failed to decode envelope: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("garbage received") + } + // inject all envelopes into the internal pool + for _, envelope := range envelopes { + if err := wh.add(envelope); err != nil { + glog.V(logger.Warn).Infof("%v: bad envelope received: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("invalid envelope") + } + p.mark(envelope) + if wh.mailServer != nil { + wh.mailServer.Archive(envelope) + } + } + case p2pCode: + // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. + // this message is not supposed to be forwarded to other peers, and + // therefore might not satisfy the PoW, expiry and other requirements. + // these messages are only accepted from the trusted peer. + if p.trusted { + var envelopes []*Envelope + if err := packet.Decode(&envelopes); err != nil { + glog.V(logger.Warn).Infof("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("garbage received (directMessage)") + } + for _, envelope := range envelopes { + wh.postEvent(envelope, p2pCode) + } + } + case mailRequestCode: + // Must be processed if mail server is implemented. Otherwise ignore. + if wh.mailServer != nil { + s := rlp.NewStream(packet.Payload, uint64(packet.Size)) + data, err := s.Bytes() + if err == nil { + wh.mailServer.DeliverMail(p, data) + } else { + glog.V(logger.Error).Infof("%v: bad requestHistoricMessages received: [%v]", p.peer, err) + } + } + default: + // New message types might be implemented in the future versions of Whisper. + // For forward compatibility, just ignore. + } + + packet.Discard() + } +} + +// add inserts a new envelope into the message pool to be distributed within the +// whisper network. It also inserts the envelope into the expiration pool at the +// appropriate time-stamp. In case of error, connection should be dropped. +func (wh *Whisper) add(envelope *Envelope) error { + now := uint32(time.Now().Unix()) + sent := envelope.Expiry - envelope.TTL + + if sent > now { + if sent-SynchAllowance > now { + return fmt.Errorf("message created in the future") + } else { + // recalculate PoW, adjusted for the time difference, plus one second for latency + envelope.calculatePoW(sent - now + 1) + } + } + + if envelope.Expiry < now { + if envelope.Expiry+SynchAllowance*2 < now { + return fmt.Errorf("very old message") + } else { + return nil // drop envelope without error + } + } + + if len(envelope.Data) > MaxMessageLength { + return fmt.Errorf("huge messages are not allowed") + } + + if len(envelope.Version) > 4 { + return fmt.Errorf("oversized Version") + } + + if len(envelope.AESNonce) > 12 { + // the standard AES GSM nonce size is 12, + // but const gcmStandardNonceSize cannot be accessed directly + return fmt.Errorf("oversized AESNonce") + } + + if len(envelope.Salt) > saltLength { + return fmt.Errorf("oversized Salt") + } + + if envelope.PoW() < MinimumPoW && !wh.test { + glog.V(logger.Debug).Infof("envelope with low PoW dropped: %f", envelope.PoW()) + return nil // drop envelope without error + } + + hash := envelope.Hash() + + wh.poolMu.Lock() + _, alreadyCached := wh.envelopes[hash] + if !alreadyCached { + wh.envelopes[hash] = envelope + if wh.expirations[envelope.Expiry] == nil { + wh.expirations[envelope.Expiry] = set.NewNonTS() + } + if !wh.expirations[envelope.Expiry].Has(hash) { + wh.expirations[envelope.Expiry].Add(hash) + } + } + wh.poolMu.Unlock() + + if alreadyCached { + glog.V(logger.Detail).Infof("whisper envelope already cached: %x\n", envelope) + } else { + wh.postEvent(envelope, messagesCode) // notify the local node about the new message + glog.V(logger.Detail).Infof("cached whisper envelope %v\n", envelope) + } + return nil +} + +// postEvent delivers the message to the watchers. +func (w *Whisper) postEvent(envelope *Envelope, messageCode uint64) { + // if the version of incoming message is higher than + // currently supported version, we can not decrypt it, + // and therefore just ignore this message + if envelope.Ver() <= EnvelopeVersion { + // todo: review if you need an additional thread here + go w.filters.NotifyWatchers(envelope, messageCode) + } +} + +// update loops until the lifetime of the whisper node, updating its internal +// state by expiring stale messages from the pool. +func (w *Whisper) update() { + // Start a ticker to check for expirations + expire := time.NewTicker(expirationCycle) + + // Repeat updates until termination is requested + for { + select { + case <-expire.C: + w.expire() + + case <-w.quit: + return + } + } +} + +// expire iterates over all the expiration timestamps, removing all stale +// messages from the pools. +func (w *Whisper) expire() { + w.poolMu.Lock() + defer w.poolMu.Unlock() + + now := uint32(time.Now().Unix()) + for then, hashSet := range w.expirations { + // Short circuit if a future time + if then > now { + continue + } + // Dump all expired messages and remove timestamp + hashSet.Each(func(v interface{}) bool { + delete(w.envelopes, v.(common.Hash)) + delete(w.messages, v.(common.Hash)) + return true + }) + w.expirations[then].Clear() + } +} + +// envelopes retrieves all the messages currently pooled by the node. +func (w *Whisper) Envelopes() []*Envelope { + w.poolMu.RLock() + defer w.poolMu.RUnlock() + + all := make([]*Envelope, 0, len(w.envelopes)) + for _, envelope := range w.envelopes { + all = append(all, envelope) + } + return all +} + +// Messages retrieves all the decrypted messages matching a filter id. +func (w *Whisper) Messages(id int) []*ReceivedMessage { + result := make([]*ReceivedMessage, 0) + w.poolMu.RLock() + defer w.poolMu.RUnlock() + + if filter := w.filters.Get(id); filter != nil { + for _, msg := range w.messages { + if filter.MatchMessage(msg) { + result = append(result, msg) + } + } + } + return result +} + +func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { + w.poolMu.Lock() + defer w.poolMu.Unlock() + + w.messages[msg.EnvelopeHash] = msg +} + +func ValidatePublicKey(k *ecdsa.PublicKey) bool { + return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 +} + +func validatePrivateKey(k *ecdsa.PrivateKey) bool { + if k == nil || k.D == nil || k.D.Sign() == 0 { + return false + } + return ValidatePublicKey(&k.PublicKey) +} + +// validateSymmetricKey returns false if the key contains all zeros +func validateSymmetricKey(k []byte) bool { + return len(k) > 0 && !containsOnlyZeros(k) +} + +func containsOnlyZeros(data []byte) bool { + for _, b := range data { + if b != 0 { + return false + } + } + return true +} + +func bytesToIntLittleEndian(b []byte) (res uint64) { + mul := uint64(1) + for i := 0; i < len(b); i++ { + res += uint64(b[i]) * mul + mul *= 256 + } + return res +} + +func BytesToIntBigEndian(b []byte) (res uint64) { + for i := 0; i < len(b); i++ { + res *= 256 + res += uint64(b[i]) + } + return res +} + +// DeriveSymmetricKey derives symmetric key material from the key or password. +// pbkdf2 is used for security, in case people use password instead of randomly generated keys. +func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) { + if version == 0 { + // kdf should run no less than 0.1 seconds on average compute, + // because it's a once in a session experience + derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New) + return derivedKey, nil + } else { + return nil, unknownVersionError(version) + } +}