diff --git a/build/flags.sh b/build/flags.sh old mode 100644 new mode 100755 diff --git a/geth/node.go b/geth/node.go index ee47354a6..12aa7ecea 100644 --- a/geth/node.go +++ b/geth/node.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" @@ -37,8 +36,6 @@ const ( versionPatch = 0 // Patch version component of the current release versionMeta = "unstable" // Version metadata to append to the version string - versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle - RPCPort = 8545 // RPC port (replaced in unit tests) EventNodeStarted = "node.started" @@ -122,22 +119,11 @@ func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node { set.String("logdir", datadir, "log dir for glog") m.ctx = cli.NewContext(nil, set, nil) - // Construct the textual version string from the individual components - vString := fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) - - // Construct the version release oracle configuration - var rConfig release.Config - rConfig.Oracle = common.HexToAddress(versionOracle) - - rConfig.Major = uint32(versionMajor) - rConfig.Minor = uint32(versionMinor) - rConfig.Patch = uint32(versionPatch) - utils.DebugSetup(m.ctx) // create node and start requested protocols - m.currentNode = utils.MakeNode(m.ctx, clientIdentifier, vString) - utils.RegisterEthService(m.ctx, m.currentNode, rConfig, makeDefaultExtra()) + m.currentNode = utils.MakeNode(m.ctx, clientIdentifier, "") + utils.RegisterEthService(m.ctx, m.currentNode, makeDefaultExtra()) // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. shhEnabled := m.ctx.GlobalBool(utils.WhisperEnabledFlag.Name) diff --git a/geth/txqueue_test.go b/geth/txqueue_test.go index 4f3d229fd..c7979c695 100644 --- a/geth/txqueue_test.go +++ b/geth/txqueue_test.go @@ -101,7 +101,7 @@ func TestQueuedTransactions(t *testing.T) { } if !reflect.DeepEqual(txHash, txHashCheck) { - t.Error("Transaction hash returned from SendTransaction is invalid") + t.Errorf("Transaction hash returned from SendTransaction is invalid: expected %s, got %s", txHashCheck, txHash) return } 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 1b07b2f68..c127cd7a9 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go @@ -77,7 +77,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { return append(method.Id(), arguments...), nil } -// toGoSliceType prses the input and casts it to the proper slice defined by the ABI +// toGoSliceType parses the input and casts it to the proper slice defined by the ABI // argument in T. func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { index := i * 32 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 bec742c29..a4e90914f 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 @@ -20,28 +20,52 @@ import ( "errors" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "golang.org/x/net/context" ) -// ErrNoCode is returned by call and transact operations for which the requested -// recipient contract to operate on does not exist in the state db or does not -// have any code associated with it (i.e. suicided). -var ErrNoCode = errors.New("no contract code at given address") +var ( + // ErrNoCode is returned by call and transact operations for which the requested + // recipient contract to operate on does not exist in the state db or does not + // have any code associated with it (i.e. suicided). + ErrNoCode = errors.New("no contract code at given address") + + // This error is raised when attempting to perform a pending state action + // on a backend that doesn't implement PendingContractCaller. + ErrNoPendingState = errors.New("backend does not support pending state") + + // This error is returned by WaitDeployed if contract creation leaves an + // empty contract behind. + ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") +) // ContractCaller defines the methods needed to allow operating with contract on a read // only basis. type ContractCaller interface { - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) + // CodeAt returns the code of the given account. This is needed to differentiate + // between contract internal errors and the local chain being out of sync. + CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + // ContractCall executes an Ethereum contract call with the specified data as the + // input. + CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) +} - // ContractCall executes an Ethereum contract call with the specified data as - // the input. The pending flag requests execution against the pending block, not - // the stable head of the chain. - ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) +// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. +type DeployBackend interface { + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) +} + +// PendingContractCaller defines methods to perform contract calls on the pending state. +// Call will try to discover this interface when access to the pending state is requested. +// If the backend does not support the pending state, Call returns ErrNoPendingState. +type PendingContractCaller interface { + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) + // PendingCallContract executes an Ethereum contract call against the pending state. + PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) } // ContractTransactor defines the methods needed to allow operating with contract @@ -49,64 +73,25 @@ type ContractCaller interface { // used when the user does not provide some needed values, but rather leaves it up // to the transactor to decide. type ContractTransactor interface { - // PendingAccountNonce retrieves the current pending nonce associated with an - // account. - PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) - + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + // PendingNonceAt retrieves the current pending nonce associated with an account. + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) // SuggestGasPrice retrieves the currently suggested gas price to allow a timely // execution of a transaction. SuggestGasPrice(ctx context.Context) (*big.Int, error) - - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) - - // EstimateGasLimit tries to estimate the gas needed to execute a specific + // 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. - EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) - + EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) // SendTransaction injects the transaction into the pending pool for execution. SendTransaction(ctx context.Context, tx *types.Transaction) error } -// ContractBackend defines the methods needed to allow operating with contract -// on a read-write basis. -// -// This interface is essentially the union of ContractCaller and ContractTransactor -// but due to a bug in the Go compiler (https://github.com/golang/go/issues/6977), -// we cannot simply list it as the two interfaces. The other solution is to add a -// third interface containing the common methods, but that convolutes the user API -// as it introduces yet another parameter to require for initialization. +// ContractBackend defines the methods needed to work with contracts on a read-write basis. type ContractBackend interface { - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) - - // ContractCall executes an Ethereum contract call with the specified data as - // the input. The pending flag requests execution against the pending block, not - // the stable head of the chain. - ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) - - // PendingAccountNonce retrieves the current pending nonce associated with an - // account. - PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) - - // SuggestGasPrice retrieves the currently suggested gas price to allow a timely - // execution of a transaction. - SuggestGasPrice(ctx context.Context) (*big.Int, error) - - // EstimateGasLimit 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. - EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) - - // SendTransaction injects the transaction into the pending pool for execution. - SendTransaction(ctx context.Context, tx *types.Transaction) error + ContractCaller + ContractTransactor } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go deleted file mode 100644 index 54b222f1f..000000000 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go +++ /dev/null @@ -1,57 +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 backends - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "golang.org/x/net/context" -) - -// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend. -var _ bind.ContractBackend = (*nilBackend)(nil) - -// nilBackend implements bind.ContractBackend, but panics on any method call. -// Its sole purpose is to support the binding tests to construct the generated -// wrappers without calling any methods on them. -type nilBackend struct{} - -func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) { - panic("not implemented") -} -func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) { - panic("not implemented") -} -func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) { - panic("not implemented") -} -func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") } -func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) { - panic("not implemented") -} -func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error { - panic("not implemented") -} - -// NewNilBackend creates a new binding backend that can be used for instantiation -// but will panic on any invocation. Its sole purpose is to help testing. -func NewNilBackend() bind.ContractBackend { - return new(nilBackend) -} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go deleted file mode 100644 index 58edd791a..000000000 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go +++ /dev/null @@ -1,136 +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 backends - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" -) - -// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend. -var _ bind.ContractBackend = (*rpcBackend)(nil) - -// rpcBackend implements bind.ContractBackend, and acts as the data provider to -// Ethereum contracts bound to Go structs. It uses an RPC connection to delegate -// all its functionality. -type rpcBackend struct { - client *rpc.Client // RPC client connection to interact with an API server -} - -// NewRPCBackend creates a new binding backend to an RPC provider that can be -// used to interact with remote contracts. -func NewRPCBackend(client *rpc.Client) bind.ContractBackend { - return &rpcBackend{client: client} -} - -// HasCode implements ContractVerifier.HasCode by retrieving any code associated -// with the contract from the remote node, and checking its size. -func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - block := "latest" - if pending { - block = "pending" - } - var hex string - err := b.client.CallContext(ctx, &hex, "eth_getCode", contract, block) - if err != nil { - return false, err - } - return len(common.FromHex(hex)) > 0, nil -} - -// ContractCall implements ContractCaller.ContractCall, delegating the execution of -// a contract call to the remote node, returning the reply to for local processing. -func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - args := struct { - To common.Address `json:"to"` - Data string `json:"data"` - }{ - To: contract, - Data: common.ToHex(data), - } - block := "latest" - if pending { - block = "pending" - } - var hex string - err := b.client.CallContext(ctx, &hex, "eth_call", args, block) - if err != nil { - return nil, err - } - return common.FromHex(hex), nil - -} - -// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating -// the current account nonce retrieval to the remote node. -func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { - var hex rpc.HexNumber - err := b.client.CallContext(ctx, &hex, "eth_getTransactionCount", account.Hex(), "pending") - if err != nil { - return 0, err - } - return hex.Uint64(), nil -} - -// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the -// gas price oracle request to the remote node. -func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - var hex rpc.HexNumber - if err := b.client.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating -// the gas estimation to the remote node. -func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - args := struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - Value *rpc.HexNumber `json:"value"` - Data string `json:"data"` - }{ - From: sender, - To: contract, - Data: common.ToHex(data), - Value: rpc.NewHexNumber(value), - } - // Execute the RPC call and retrieve the response - var hex rpc.HexNumber - err := b.client.CallContext(ctx, &hex, "eth_estimateGas", args) - if err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -// SendTransaction implements ContractTransactor.SendTransaction, delegating the -// raw transaction injection to the remote node. -func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - data, err := rlp.EncodeToBytes(tx) - if err != nil { - return err - } - return b.client.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) -} 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 687a31bf1..7e09abb11 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 @@ -17,8 +17,12 @@ package backends import ( + "errors" + "fmt" "math/big" + "sync" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -36,12 +40,15 @@ var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) +var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") + // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. type SimulatedBackend struct { database ethdb.Database // In memory database to store our testing data blockchain *core.BlockChain // Ethereum blockchain to handle the consensus + 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 } @@ -52,85 +59,130 @@ func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { database, _ := ethdb.NewMemDatabase() core.WriteGenesisBlockForTesting(database, accounts...) blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux)) - - backend := &SimulatedBackend{ - database: database, - blockchain: blockchain, - } - backend.Rollback() - + backend := &SimulatedBackend{database: database, blockchain: blockchain} + backend.rollback() return backend } // Commit imports all the pending transactions as a single block and starts a // fresh new state. func (b *SimulatedBackend) Commit() { + b.mu.Lock() + defer b.mu.Unlock() + if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { panic(err) // This cannot happen unless the simulator is wrong, fail in that case } - b.Rollback() + b.rollback() } // Rollback aborts all pending transactions, reverting to the last committed state. func (b *SimulatedBackend) Rollback() { - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + b.mu.Lock() + defer b.mu.Unlock() + b.rollback() +} + +func (b *SimulatedBackend) rollback() { + blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } -// HasCode implements ContractVerifier.HasCode, checking whether there is any -// code associated with a certain account in the blockchain. -func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - if pending { - return len(b.pendingState.GetCode(contract)) > 0, nil +// CodeAt returns the code associated with a certain account in the blockchain. +func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported } statedb, _ := b.blockchain.State() - return len(statedb.GetCode(contract)) > 0, nil + return statedb.GetCode(contract), nil } -// ContractCall implements ContractCaller.ContractCall, executing the specified -// contract with the given input data. -func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - // Create a copy of the current state db to screw around with - var ( - block *types.Block - statedb *state.StateDB - ) - if pending { - block, statedb = b.pendingBlock, b.pendingState.Copy() - } else { - block = b.blockchain.CurrentBlock() - statedb, _ = b.blockchain.State() - } - // If there's no code to interact with, respond with an appropriate error - if code := statedb.GetCode(contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(common.Address{}) - from.SetBalance(common.MaxBig) +// BalanceAt returns the wei balance of a certain account in the blockchain. +func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: &contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: new(big.Int), - data: data, + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported } - // Execute the call and return - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - - out, _, err := core.ApplyMessage(vmenv, msg, gaspool) - return out, err + statedb, _ := b.blockchain.State() + return statedb.GetBalance(contract), nil } -// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving +// NonceAt returns the nonce of a certain account in the blockchain. +func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return 0, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + return statedb.GetNonce(contract), nil +} + +// StorageAt returns the value of key in the storage of an account in the blockchain. +func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + val := statedb.GetState(contract, key) + return val[:], nil +} + +// TransactionReceipt returns the receipt of a transaction. +func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + return core.GetReceipt(b.database, txHash), nil +} + +// PendingCodeAt returns the code associated with an account in the pending state. +func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.pendingState.GetCode(contract), nil +} + +// CallContract executes a contract call. +func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + state, err := b.blockchain.State() + if err != nil { + return nil, err + } + rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + return rval, err +} + +// PendingCallContract executes a contract call on the pending state. +func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) + return rval, err +} + +// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving // the nonce currently pending for the account. -func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { +func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + return b.pendingState.GetOrNewStateObject(account).Nonce(), nil } @@ -140,45 +192,55 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error return big.NewInt(1), nil } -// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the -// requested code against the currently pending block/state and returning the used -// gas. -func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - // Create a copy of the currently pending state db to screw around with - var ( - block = b.pendingBlock - statedb = b.pendingState.Copy() - ) - // If there's no code to interact with, respond with an appropriate error - if contract != nil { - if code := statedb.GetCode(*contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(sender) - from.SetBalance(common.MaxBig) +// EstimateGas executes the requested code against the currently pending block/state and +// returns the used amount of gas. +func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: value, - data: data, - } - // Execute the call and return - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - - _, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) return gas, err } -// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw -// transaction injection to the remote node. +// callContract implemens common code between normal and pending contract calls. +// state is modified during execution, make sure to copy it if necessary. +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) + } + if call.Value == nil { + call.Value = new(big.Int) + } + // Set infinite balance to the fake caller account. + from := statedb.GetOrNewStateObject(call.From) + from.SetBalance(common.MaxBig) + // Execute the call. + msg := callmsg{call} + vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + gaspool := new(core.GasPool).AddGas(common.MaxBig) + ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, err +} + +// SendTransaction updates the pending block to include the given transaction. +// It panics if the transaction is invalid. func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + b.mu.Lock() + defer b.mu.Unlock() + + sender, err := tx.From() + if err != nil { + panic(fmt.Errorf("invalid transaction: %v", err)) + } + nonce := b.pendingState.GetNonce(sender) + if tx.Nonce() != nonce { + 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) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) @@ -187,26 +249,20 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa }) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) - return nil } // callmsg implements core.Message to allow passing it as a transaction simulator. type callmsg struct { - from *state.StateObject - to *common.Address - gasLimit *big.Int - gasPrice *big.Int - value *big.Int - data []byte + ethereum.CallMsg } -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil } +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.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gasLimit } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } +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 80948d3f1..965f51e85 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 @@ -20,8 +20,8 @@ import ( "errors" "fmt" "math/big" - "sync/atomic" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -62,9 +62,6 @@ type BoundContract struct { abi abi.ABI // Reflect based ABI to access the correct Ethereum methods caller ContractCaller // Read interface to interact with the blockchain transactor ContractTransactor // Write interface to interact with the blockchain - - latestHasCode uint32 // Cached verification that the latest state contains code for this contract - pendingHasCode uint32 // Cached verification that the pending state contains code for this contract } // NewBoundContract creates a low level contract interface through which calls @@ -105,25 +102,42 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, if opts == nil { opts = new(CallOpts) } - // Make sure we have a contract to operate on, and bail out otherwise - if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) { - if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil { - return err - } else if !code { - return ErrNoCode - } - if opts.Pending { - atomic.StoreUint32(&c.pendingHasCode, 1) - } else { - atomic.StoreUint32(&c.latestHasCode, 1) - } - } // Pack the input, call and unpack the results input, err := c.abi.Pack(method, params...) if err != nil { return err } - output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending) + var ( + msg = ethereum.CallMsg{To: &c.address, Data: input} + ctx = ensureContext(opts.Context) + code []byte + output []byte + ) + if opts.Pending { + pb, ok := c.caller.(PendingContractCaller) + if !ok { + return ErrNoPendingState + } + output, err = pb.PendingCallContract(ctx, msg) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } else { + output, err = c.caller.CallContract(ctx, msg, nil) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } if err != nil { return err } @@ -158,7 +172,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } nonce := uint64(0) if opts.Nonce == nil { - nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From) + nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) if err != nil { return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) } @@ -168,7 +182,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i // Figure out the gas allowance and gas price values gasPrice := opts.GasPrice if gasPrice == nil { - gasPrice, err = c.transactor.SuggestGasPrice(opts.Context) + gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) if err != nil { return nil, fmt.Errorf("failed to suggest gas price: %v", err) } @@ -176,18 +190,18 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i gasLimit := opts.GasLimit if gasLimit == nil { // Gas estimation cannot succeed without code for method invocations - if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 { - if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil { + if contract != nil { + if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { return nil, err - } else if !code { + } else if len(code) == 0 { return nil, ErrNoCode } - atomic.StoreUint32(&c.pendingHasCode, 1) } // If the contract surely has code (or code is not needed), estimate the transaction - gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input) + msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} + gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) if err != nil { - return nil, fmt.Errorf("failed to exstimate gas needed: %v", err) + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) } } // Create the transaction, sign it and schedule it for execution @@ -204,8 +218,15 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i if err != nil { return nil, err } - if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil { + if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { return nil, err } return signedTx, nil } + +func ensureContext(ctx context.Context) context.Context { + if ctx == nil { + return context.TODO() + } + return ctx +} 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 72998bb6d..523122213 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 @@ -127,7 +127,7 @@ package {{.Package}} // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract. func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) { - contract, err := bind{{.Type}}(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + contract, err := bind{{.Type}}(address, backend, backend) if err != nil { return nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go new file mode 100644 index 000000000..bbb6d6a75 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go @@ -0,0 +1,76 @@ +// 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 bind + +import ( + "fmt" + "time" + + "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" + "golang.org/x/net/context" +) + +// WaitMined waits for tx to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + queryTicker := time.NewTicker(1 * time.Second) + defer queryTicker.Stop() + loghash := tx.Hash().Hex()[:8] + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if receipt != nil { + return receipt, nil + } + if err != nil { + glog.V(logger.Detail).Infof("tx %x error: %v", loghash, err) + } else { + glog.V(logger.Detail).Infof("tx %x not yet mined...", loghash) + } + // Wait for the next round. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} + +// WaitDeployed waits for a contract deployment transaction and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { + if tx.To() != nil { + return common.Address{}, fmt.Errorf("tx is not contract creation") + } + receipt, err := WaitMined(ctx, b, tx) + if err != nil { + return common.Address{}, err + } + if receipt.ContractAddress == (common.Address{}) { + return common.Address{}, fmt.Errorf("zero address") + } + // Check that code has indeed been deployed at the address. + // This matters on pre-Homestead chains: OOG in the constructor + // could leave an empty account behind. + code, err := b.CodeAt(ctx, receipt.ContractAddress, nil) + if err == nil && len(code) == 0 { + err = ErrNoCodeAfterDeploy + } + return receipt.ContractAddress, err +} 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 aa48f6ede..3f44e0f3c 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go @@ -35,8 +35,11 @@ import ( "gopkg.in/urfave/cli.v1" ) +var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) + var ( - app *cli.App + app = utils.NewApp(gitCommit, "the evm command line interface") + DebugFlag = cli.BoolFlag{ Name: "debug", Usage: "output full trace logs", @@ -91,7 +94,6 @@ var ( ) func init() { - app = utils.NewApp("0.2", "the evm command line interface") app.Flags = []cli.Flag{ CreateFlag, DebugFlag, @@ -117,10 +119,13 @@ func run(ctx *cli.Context) error { statedb, _ := state.New(common.Hash{}, db) sender := statedb.CreateAccount(common.StringToAddress("sender")) + logger := vm.NewStructLogger(nil) + vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{ Debug: ctx.GlobalBool(DebugFlag.Name), ForceJit: ctx.GlobalBool(ForceJitFlag.Name), EnableJit: !ctx.GlobalBool(DisableJitFlag.Name), + Tracer: logger, }) tstart := time.Now() @@ -157,7 +162,7 @@ func run(ctx *cli.Context) error { statedb.Commit() fmt.Println(string(statedb.Dump())) } - vm.StdErrFormat(vmenv.StructLogs()) + vm.StdErrFormat(logger.StructLogs()) if ctx.GlobalBool(SysStatFlag.Name) { var mem runtime.MemStats @@ -209,7 +214,6 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg value: value, time: big.NewInt(time.Now().Unix()), } - cfg.Logger.Collector = env env.evm = vm.New(env, cfg) return env @@ -242,12 +246,6 @@ func (self *VMEnv) GetHash(n uint64) common.Hash { } return common.Hash{} } -func (self *VMEnv) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} -func (self *VMEnv) StructLogs() []vm.StructLog { - return self.logs -} func (self *VMEnv) AddLog(log *vm.Log) { self.state.AddLog(log) } 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 b0a45dd41..bb0167964 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go @@ -168,7 +168,7 @@ nodes. ) func accountList(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) for i, acct := range stack.AccountManager().Accounts() { fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File) } @@ -278,7 +278,7 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro // accountCreate creates a new account into the keystore defined by the CLI flags. func accountCreate(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) whisper := getWhisperYesNo("You can also choose to enable your new account as a Whisper identity.") @@ -297,7 +297,7 @@ func accountUpdate(ctx *cli.Context) error { if len(ctx.Args()) == 0 { utils.Fatalf("No accounts specified to update") } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) account, oldPassword := unlockAccount(ctx, stack.AccountManager(), ctx.Args().First(), 0, nil) newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) if err := stack.AccountManager().Update(account, oldPassword, newPassword); err != nil { @@ -316,7 +316,7 @@ func importWallet(ctx *cli.Context) error { utils.Fatalf("Could not read wallet file: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) acct, err := stack.AccountManager().ImportPreSaleKey(keyJson, passphrase) if err != nil { @@ -335,7 +335,7 @@ func accountImport(ctx *cli.Context) error { if err != nil { utils.Fatalf("Failed to load the private key: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) acct, err := stack.AccountManager().ImportECDSA(key, passphrase) if err != nil { 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 fe63dfcca..293a6ffea 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go @@ -76,6 +76,9 @@ 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) + } chain, chainDb := utils.MakeChain(ctx) start := time.Now() err := utils.ImportChain(chain, ctx.Args().First()) 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 3ca79915f..f2cbd2414 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go @@ -32,7 +32,9 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "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/ethdb" "github.com/ethereum/go-ethereum/internal/debug" @@ -41,50 +43,24 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/release" - "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" ) const ( - clientIdentifier = "Geth" // Client identifier to advertise over the network - 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 - versionMeta = "unstable" // Version metadata to append to the version string - - versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle + clientIdentifier = "Geth" // Client identifier to advertise over the network ) var ( - gitCommit string // Git SHA1 commit hash of the release (set via linker flags) - verString string // Combined textual representation of all the version components - relConfig release.Config // Structured version information and release oracle config - app *cli.App + // Git SHA1 commit hash of the release (set via linker flags) + gitCommit = "" + // Ethereum address of the Geth release oracle. + relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf") + // The app that holds all commands and flags. + app = utils.NewApp(gitCommit, "the go-ethereum command line interface") ) func init() { - // Construct the textual version string from the individual components - verString = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) - if versionMeta != "" { - verString += "-" + versionMeta - } - if gitCommit != "" { - verString += "-" + gitCommit[:8] - } - // Construct the version release oracle configuration - relConfig.Oracle = common.HexToAddress(versionOracle) - - relConfig.Major = uint32(versionMajor) - relConfig.Minor = uint32(versionMinor) - relConfig.Patch = uint32(versionPatch) - - commit, _ := hex.DecodeString(gitCommit) - copy(relConfig.Commit[:], commit) - // Initialize the CLI app and start Geth - app = utils.NewApp(verString, "the go-ethereum command line interface") app.Action = geth app.HideVersion = true // we have a command to print the version app.Commands = []cli.Command{ @@ -144,6 +120,11 @@ 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", + }, } app.Flags = []cli.Flag{ @@ -153,7 +134,6 @@ participating. utils.BootnodesFlag, utils.DataDirFlag, utils.KeyStoreDirFlag, - utils.BlockchainVersionFlag, utils.OlympicFlag, utils.FastSyncFlag, utils.LightModeFlag, @@ -267,6 +247,10 @@ 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) + } + chainDb, err := ethdb.NewLDBDatabase(filepath.Join(utils.MustMakeDataDir(ctx), utils.ChainDbName(ctx)), 0, 0) if err != nil { utils.Fatalf("could not open database: %v", err) @@ -286,35 +270,32 @@ func initGenesis(ctx *cli.Context) error { } func makeFullNode(ctx *cli.Context) *node.Node { - node := utils.MakeNode(ctx, clientIdentifier, verString) - utils.RegisterEthService(ctx, node, relConfig, makeDefaultExtra()) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + utils.RegisterEthService(ctx, stack, utils.MakeDefaultExtraData(clientIdentifier)) + // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) if shhEnabled || shhAutoEnabled { - utils.RegisterShhService(node) - } - return node -} - -func makeDefaultExtra() []byte { - var clientInfo = struct { - Version uint - Name string - GoVersion string - Os string - }{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} - extra, err := rlp.EncodeToBytes(clientInfo) - if err != nil { - glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) + utils.RegisterShhService(stack) } - if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { - glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) - glog.V(logger.Debug).Infof("extra: %x\n", extra) - return nil + // Add the release oracle service so it boots along with node. + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + config := release.Config{ + Oracle: relOracle, + Major: uint32(utils.VersionMajor), + Minor: uint32(utils.VersionMinor), + Patch: uint32(utils.VersionPatch), + } + commit, _ := hex.DecodeString(gitCommit) + copy(config.Commit[:], commit) + return release.NewReleaseService(ctx, config) + }); err != nil { + utils.Fatalf("Failed to register the Geth release oracle service: %v", err) } - return extra + + return stack } // startNode boots up the system node and all registered protocols, after which @@ -322,7 +303,8 @@ func makeDefaultExtra() []byte { // miner. func startNode(ctx *cli.Context, stack *node.Node) { // Report geth version - glog.V(logger.Info).Infof("instance: Geth/%s/%s/%s\n", verString, runtime.Version(), runtime.GOOS) + glog.V(logger.Info).Infof("instance: Geth/%s/%s/%s\n", utils.Version, runtime.Version(), runtime.GOOS) + // Start up the node itself utils.StartNode(stack) @@ -335,18 +317,19 @@ func startNode(ctx *cli.Context, stack *node.Node) { } } - if ctx.GlobalBool(utils.OpposeDAOFork.Name) { - // Classic (Azure) - addPeer("enode://fc3d7b57e5d317946bf421411632ec98d5ffcbf94548cd7bc10088e4fef176670f8ec70280d301a9d0b22fe498203f62b323da15b3acc18b02a1fee2a06b7d3f@40.118.3.223:30305") - } else { - // MainNet (Azure) - addPeer("enode://feaf206a308a669a789be45f4dadcb351246051727f12415ad69e44f8080daf0569c10fe1d9944d245dd1f3e1c89cedda8ce03d7e3d5ed8975a35cad4b4f7ec1@40.118.3.223:30303") - // MainNet (John Gerryts @phonikg) - addPeer("enode://3cbd26f73513af0e789c55ea9efa6d259be2d5f6882bdb52740e21e01379287b652642a87207f1bc07c64aae3ab51ab566dede7588d6064022d40577fe59d5de@50.112.52.169:30300") - } if ctx.GlobalBool(utils.TestNetFlag.Name) { // TestNet (John Gerryts @phonikg) addPeer("enode://7d00e8c27b2328e2008a9fc86e81afba22681fdac675b99805fa62cc29ee8a2a9d83f916f7661da6a6bd78155a430bb2bd7cec733ca9e700e236ec9c71d97e24@50.112.52.169:30301") + } else { + if ctx.GlobalBool(utils.OpposeDAOFork.Name) { + // Classic (Azure) + addPeer("enode://fc3d7b57e5d317946bf421411632ec98d5ffcbf94548cd7bc10088e4fef176670f8ec70280d301a9d0b22fe498203f62b323da15b3acc18b02a1fee2a06b7d3f@40.118.3.223:30305") + } else { + // MainNet (Azure) + addPeer("enode://feaf206a308a669a789be45f4dadcb351246051727f12415ad69e44f8080daf0569c10fe1d9944d245dd1f3e1c89cedda8ce03d7e3d5ed8975a35cad4b4f7ec1@40.118.3.223:30303") + // MainNet (John Gerryts @phonikg) + addPeer("enode://3cbd26f73513af0e789c55ea9efa6d259be2d5f6882bdb52740e21e01379287b652642a87207f1bc07c64aae3ab51ab566dede7588d6064022d40577fe59d5de@50.112.52.169:30300") + } } } @@ -428,7 +411,10 @@ func gpubench(ctx *cli.Context) error { func version(c *cli.Context) error { fmt.Println(clientIdentifier) - fmt.Println("Version:", verString) + 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("Go Version:", runtime.Version()) @@ -438,3 +424,21 @@ func version(c *cli.Context) error { return nil } + +func license(c *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 +(at your option) any later version. + +Geth 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 geth. If not, see . +`) + + return nil +} 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 9d1b5c19a..5a730681d 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go @@ -30,6 +30,8 @@ import ( var AppHelpTemplate = `NAME: {{.App.Name}} - {{.App.Usage}} + Copyright 2013-2016 The go-ethereum Authors + USAGE: {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} {{if .App.Version}} @@ -76,7 +78,6 @@ var AppHelpFlagGroups = []flagGroup{ utils.LightPeersFlag, utils.LightKDFFlag, utils.CacheFlag, - utils.BlockchainVersionFlag, }, }, { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go index 3b521a0e1..584afc804 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go @@ -23,6 +23,7 @@ import ( "os" "os/signal" "regexp" + "runtime" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -52,10 +53,16 @@ func openLogFile(Datadir string, filename string) *os.File { // is redirected to a different file. func Fatalf(format string, args ...interface{}) { w := io.MultiWriter(os.Stdout, os.Stderr) - outf, _ := os.Stdout.Stat() - errf, _ := os.Stderr.Stat() - if outf != nil && errf != nil && os.SameFile(outf, errf) { - w = os.Stderr + if runtime.GOOS == "windows" { + // The SameFile check below doesn't work on Windows. + // stdout is unlikely to get redirected though, so just print there. + w = os.Stdout + } else { + outf, _ := os.Stdout.Stat() + errf, _ := os.Stderr.Stat() + if outf != nil && errf != nil && os.SameFile(outf, errf) { + w = os.Stderr + } } fmt.Fprintf(w, "Fatal: "+format+"\n", args...) logger.Flush() 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 9d2a23abf..59de43918 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -50,7 +50,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" "gopkg.in/urfave/cli.v1" @@ -83,13 +82,16 @@ OPTIONS: } // NewApp creates an app with sane defaults. -func NewApp(version, usage string) *cli.App { +func NewApp(gitCommit, usage string) *cli.App { app := cli.NewApp() app.Name = filepath.Base(os.Args[0]) app.Author = "" //app.Authors = nil app.Email = "" - app.Version = version + app.Version = Version + if gitCommit != "" { + app.Version += "-" + gitCommit[:8] + } app.Usage = usage return app } @@ -147,11 +149,6 @@ var ( Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", Value: 128, } - BlockchainVersionFlag = cli.IntFlag{ - Name: "blockchainversion", - Usage: "Blockchain version (integer)", - Value: core.BlockChainVersion, - } FastSyncFlag = cli.BoolFlag{ Name: "fast", Usage: "Enable fast syncing through state downloads", @@ -640,13 +637,18 @@ func MakePasswordList(ctx *cli.Context) []string { } // MakeNode configures a node with no services from command line flags. -func MakeNode(ctx *cli.Context, name, version string) *node.Node { +func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { + vsn := Version + if gitCommit != "" { + vsn += "-" + gitCommit[:8] + } + config := &node.Config{ DataDir: MustMakeDataDir(ctx), KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name), UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name), PrivateKey: MakeNodeKey(ctx), - Name: MakeNodeName(name, version, ctx), + Name: MakeNodeName(name, vsn, ctx), NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name), BootstrapNodes: MakeBootstrapNodes(ctx), ListenAddr: MakeListenAddress(ctx), @@ -680,7 +682,7 @@ func MakeNode(ctx *cli.Context, name, version string) *node.Node { // RegisterEthService configures eth.Ethereum from command line flags and adds it to the // given node. -func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Config, extra []byte) { +func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { ethDisabled := ctx.GlobalBool(NoEthFlag.Name) if ethDisabled { glog.V(logger.Info).Infof("Ethereum service registration by-passed (--%s flag used)\n", NoEthFlag.Name) @@ -716,7 +718,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf NoDefSrv: ctx.GlobalBool(NoDefSrvFlag.Name), LightServ: ctx.GlobalInt(LightServFlag.Name), LightPeers: ctx.GlobalInt(LightPeersFlag.Name), - BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), DatabaseCache: ctx.GlobalInt(CacheFlag.Name), DatabaseHandles: MakeDatabaseHandles(), NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), @@ -779,11 +780,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf Fatalf("Failed to register the Ethereum full node service: %v", err) } } - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return release.NewReleaseService(ctx, relconf) - }); err != nil { - Fatalf("Failed to register the Geth release oracle service: %v", err) - } } // RegisterShhService configures whisper and adds it to the given node. @@ -856,23 +852,6 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC case ctx.GlobalBool(OpposeDAOFork.Name): config.DAOForkSupport = false } - // Temporarilly display a proper message so the user knows which fork its on - if !ctx.GlobalBool(TestNetFlag.Name) && (genesis == nil || genesis.Hash() == common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")) { - choice := "SUPPORT" - if !config.DAOForkSupport { - choice = "OPPOSE" - } - current := fmt.Sprintf("Geth is currently configured to %s the DAO hard-fork!", choice) - howtoswap := fmt.Sprintf("You can change your choice prior to block #%v with --support-dao-fork or --oppose-dao-fork.", config.DAOForkBlock) - howtosync := fmt.Sprintf("After the hard-fork block #%v passed, changing chains requires a resync from scratch!", config.DAOForkBlock) - separator := strings.Repeat("-", len(howtoswap)) - - glog.V(logger.Warn).Info(separator) - glog.V(logger.Warn).Info(current) - glog.V(logger.Warn).Info(howtoswap) - glog.V(logger.Warn).Info(howtosync) - glog.V(logger.Warn).Info(separator) - } return config } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go new file mode 100644 index 000000000..03633d694 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go @@ -0,0 +1,64 @@ +// Copyright 2014 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 contains internal helper functions for go-ethereum commands. +package utils + +import ( + "fmt" + "runtime" + + "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" +) + +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 + VersionMeta = "unstable" // Version metadata to append to the version string +) + +// Version holds the textual version string. +var Version = func() string { + v := fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) + if VersionMeta != "" { + v += "-" + VersionMeta + } + return v +}() + +// MakeDefaultExtraData returns the default Ethereum block extra data blob. +func MakeDefaultExtraData(clientIdentifier string) []byte { + var clientInfo = struct { + Version uint + Name string + GoVersion string + Os string + }{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} + extra, err := rlp.EncodeToBytes(clientInfo) + if err != nil { + glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) + } + if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { + glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) + glog.V(logger.Debug).Infof("extra: %x\n", extra) + return nil + } + return extra +} 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 index 2b30d31d3..0375bb4d5 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go +++ b/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go @@ -787,7 +787,7 @@ var findIndex = function (array, callback) { }; /** - * Should be called to get sting from it's hex representation + * Should be called to get sting from its hex representation * * @method toAscii * @param {String} string in hex @@ -865,7 +865,7 @@ var extractTypeName = function (name) { }; /** - * Converts value to it's decimal representation in string + * Converts value to its decimal representation in string * * @method toDecimal * @param {String|Number|BigNumber} @@ -876,7 +876,7 @@ var toDecimal = function (value) { }; /** - * Converts value to it's hex representation + * Converts value to its hex representation * * @method fromDecimal * @param {String|Number|BigNumber} @@ -890,7 +890,7 @@ var fromDecimal = function (value) { }; /** - * Auto converts any given value into it's hex representation. + * Auto converts any given value into its hex representation. * * And even stringifys objects before. * diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go new file mode 100644 index 000000000..b2b2365f3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go @@ -0,0 +1,68 @@ +// 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 chequebook + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +const Version = "1.0" + +var errNoChequebook = errors.New("no chequebook") + +type Api struct { + chequebookf func() *Chequebook +} + +func NewApi(ch func() *Chequebook) *Api { + return &Api{ch} +} + +func (self *Api) Balance() (string, error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Balance().String(), nil +} + +func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) { + ch := self.chequebookf() + if ch == nil { + return nil, errNoChequebook + } + return ch.Issue(beneficiary, amount) +} + +func (self *Api) Cash(cheque *Cheque) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Cash(cheque) +} + +func (self *Api) Deposit(amount *big.Int) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Deposit(amount) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go new file mode 100644 index 000000000..5ece1391b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go @@ -0,0 +1,642 @@ +// 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 chequebook package wraps the 'chequebook' Ethereum smart contract. +// +// The functions in this package allow using chequebook for +// issuing, receiving, verifying cheques in ether; (auto)cashing cheques in ether +// as well as (auto)depositing ether to the chequebook contract. +package chequebook + +//go:generate abigen --sol contract/chequebook.sol --pkg contract --out contract/chequebook.go +//go:generate go run ./gencode.go + +import ( + "bytes" + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// TODO(zelig): watch peer solvency and notify of bouncing cheques +// TODO(zelig): enable paying with cheque by signing off + +// Some functionality require interacting with the blockchain: +// * setting current balance on peer's chequebook +// * sending the transaction to cash the cheque +// * depositing ether to the chequebook +// * watching incoming ether + +var ( + gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook + // gasToDeploy = big.NewInt(3000000) +) + +// Backend wraps all methods required for chequebook operation. +type Backend interface { + bind.ContractBackend + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error) +} + +// Cheque represents a payment promise to a single beneficiary. +type Cheque struct { + Contract common.Address // address of chequebook, needed to avoid cross-contract submission + Beneficiary common.Address + Amount *big.Int // cumulative amount of all funds sent + Sig []byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey) +} + +func (self *Cheque) String() string { + return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig) +} + +type Params struct { + ContractCode, ContractAbi string +} + +var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI} + +// Chequebook can create and sign cheques from a single contract to multiple beneficiaries. +// It is the outgoing payment handler for peer to peer micropayments. +type Chequebook struct { + path string // path to chequebook file + prvKey *ecdsa.PrivateKey // private key to sign cheque with + lock sync.Mutex // + backend Backend // blockchain API + quit chan bool // when closed causes autodeposit to stop + owner common.Address // owner address (derived from pubkey) + contract *contract.Chequebook // abigen binding + session *contract.ChequebookSession // abigen binding with Tx Opts + + // persisted fields + balance *big.Int // not synced with blockchain + contractAddr common.Address // contract address + sent map[common.Address]*big.Int //tallies for beneficiarys + + txhash string // tx hash of last deposit tx + threshold *big.Int // threshold that triggers autodeposit if not nil + buffer *big.Int // buffer to keep on top of balance for fork protection +} + +func (self *Chequebook) String() string { + return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey) +} + +// NewChequebook creates a new Chequebook. +func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) { + balance := new(big.Int) + sent := make(map[common.Address]*big.Int) + + chbook, err := contract.NewChequebook(contractAddr, backend) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + + self = &Chequebook{ + prvKey: prvKey, + balance: balance, + contractAddr: contractAddr, + sent: sent, + path: path, + backend: backend, + owner: transactOpts.From, + contract: chbook, + session: session, + } + + if (contractAddr != common.Address{}) { + self.setBalanceFromBlockChain() + glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String()) + } + return +} + +func (self *Chequebook) setBalanceFromBlockChain() { + balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil) + if err != nil { + glog.V(logger.Error).Infof("can't get balance: %v", err) + } else { + self.balance.Set(balance) + } +} + +// LoadChequebook loads a chequebook from disk (file path). +func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) { + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + self, _ = NewChequebook(path, common.Address{}, prvKey, backend) + + err = json.Unmarshal(data, self) + if err != nil { + return nil, err + } + if checkBalance { + self.setBalanceFromBlockChain() + } + + glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path) + + return +} + +// chequebookFile is the JSON representation of a chequebook. +type chequebookFile struct { + Balance string + Contract string + Owner string + Sent map[string]string +} + +// UnmarshalJSON deserialises a chequebook. +func (self *Chequebook) UnmarshalJSON(data []byte) error { + var file chequebookFile + err := json.Unmarshal(data, &file) + if err != nil { + return err + } + _, ok := self.balance.SetString(file.Balance, 10) + if !ok { + return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance) + } + self.contractAddr = common.HexToAddress(file.Contract) + for addr, sent := range file.Sent { + self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10) + if !ok { + return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent) + } + } + return nil +} + +// MarshalJSON serialises a chequebook. +func (self *Chequebook) MarshalJSON() ([]byte, error) { + var file = &chequebookFile{ + Balance: self.balance.String(), + Contract: self.contractAddr.Hex(), + Owner: self.owner.Hex(), + Sent: make(map[string]string), + } + for addr, sent := range self.sent { + file.Sent[addr.Hex()] = sent.String() + } + return json.Marshal(file) +} + +// Save persists the chequebook on disk, remembering balance, contract address and +// cumulative amount of funds sent for each beneficiary. +func (self *Chequebook) Save() (err error) { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path) + + return ioutil.WriteFile(self.path, data, os.ModePerm) +} + +// Stop quits the autodeposit go routine to terminate +func (self *Chequebook) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Issue creates a cheque signed by the chequebook owner's private key. The +// signer commits to a contract (one that they own), a beneficiary and amount. +func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) { + defer self.lock.Unlock() + self.lock.Lock() + + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("amount must be greater than zero (%v)", amount) + } + if self.balance.Cmp(amount) < 0 { + err = fmt.Errorf("insufficent funds to issue cheque for amount: %v. balance: %v", amount, self.balance) + } else { + var sig []byte + sent, found := self.sent[beneficiary] + if !found { + sent = new(big.Int) + self.sent[beneficiary] = sent + } + sum := new(big.Int).Set(sent) + sum.Add(sum, amount) + + sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey) + if err == nil { + ch = &Cheque{ + Contract: self.contractAddr, + Beneficiary: beneficiary, + Amount: sum, + Sig: sig, + } + sent.Set(sum) + self.balance.Sub(self.balance, amount) // subtract amount from balance + } + } + + // auto deposit if threshold is set and balance is less then threshold + // note this is called even if issueing cheque fails + // so we reattempt depositing + if self.threshold != nil { + if self.balance.Cmp(self.threshold) < 0 { + send := new(big.Int).Sub(self.buffer, self.balance) + self.deposit(send) + } + } + + return +} + +// Cash is a convenience method to cash any cheque. +func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) { + return ch.Cash(self.session) +} + +// data to sign: contract address, beneficiary, cumulative amount of funds ever sent +func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte { + bigamount := sum.Bytes() + if len(bigamount) > 32 { + return nil + } + var amount32 [32]byte + copy(amount32[32-len(bigamount):32], bigamount) + input := append(contract.Bytes(), beneficiary.Bytes()...) + input = append(input, amount32[:]...) + return crypto.Keccak256(input) +} + +// Balance returns the current balance of the chequebook. +func (self *Chequebook) Balance() *big.Int { + defer self.lock.Unlock() + self.lock.Lock() + return new(big.Int).Set(self.balance) +} + +// Owner returns the owner account of the chequebook. +func (self *Chequebook) Owner() common.Address { + return self.owner +} + +// Address returns the on-chain contract address of the chequebook. +func (self *Chequebook) Address() common.Address { + return self.contractAddr +} + +// Deposit deposits money to the chequebook account. +func (self *Chequebook) Deposit(amount *big.Int) (string, error) { + defer self.lock.Unlock() + self.lock.Lock() + return self.deposit(amount) +} + +// deposit deposits amount to the chequebook account. +// The caller must hold self.lock. +func (self *Chequebook) deposit(amount *big.Int) (string, error) { + // since the amount is variable here, we do not use sessions + depositTransactor := bind.NewKeyedTransactor(self.prvKey) + depositTransactor.Value = amount + chbookRaw := &contract.ChequebookRaw{Contract: self.contract} + tx, err := chbookRaw.Transfer(depositTransactor) + if err != nil { + glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err) + return "", err + } + // assume that transaction is actually successful, we add the amount to balance right away + self.balance.Add(self.balance, amount) + glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer) + return tx.Hash().Hex(), nil +} + +// AutoDeposit (re)sets interval time and amount which triggers sending funds to the +// chequebook. Contract backend needs to be set if threshold is not less than buffer, then +// deposit will be triggered on every new cheque issued. +func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.threshold = threshold + self.buffer = buffer + self.autoDeposit(interval) +} + +// autoDeposit starts a goroutine that periodically sends funds to the chequebook +// contract caller holds the lock the go routine terminates if Chequebook.quit is closed. +func (self *Chequebook) autoDeposit(interval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if threshold >= balance autodeposit after every cheque issued + if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 { + return + } + + ticker := time.NewTicker(interval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.balance.Cmp(self.buffer) < 0 { + amount := new(big.Int).Sub(self.buffer, self.balance) + txhash, err := self.deposit(amount) + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Outbox can issue cheques from a single contract to a single beneficiary. +type Outbox struct { + chequeBook *Chequebook + beneficiary common.Address +} + +// NewOutbox creates an outbox. +func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox { + return &Outbox{chbook, beneficiary} +} + +// Issue creates cheque. +func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) { + return self.chequeBook.Issue(self.beneficiary, amount) +} + +// AutoDeposit enables auto-deposits on the underlying chequebook. +func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + self.chequeBook.AutoDeposit(interval, threshold, buffer) +} + +// Stop helps satisfy the swap.OutPayment interface. +func (self *Outbox) Stop() {} + +// String implements fmt.Stringer. +func (self *Outbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance()) +} + +// Inbox can deposit, verify and cash cheques from a single contract to a single +// beneficiary. It is the incoming payment handler for peer to peer micropayments. +type Inbox struct { + lock sync.Mutex + contract common.Address // peer's chequebook contract + beneficiary common.Address // local peer's receiving address + sender common.Address // local peer's address to send cashing tx from + signer *ecdsa.PublicKey // peer's public key + txhash string // tx hash of last cashing tx + abigen bind.ContractBackend // blockchain API + session *contract.ChequebookSession // abi contract backend with tx opts + quit chan bool // when closed causes autocash to stop + maxUncashed *big.Int // threshold that triggers autocashing + cashed *big.Int // cumulative amount cashed + cheque *Cheque // last cheque, nil if none yet received +} + +// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated +// from blockchain when first cheque is received. +func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) { + if signer == nil { + return nil, fmt.Errorf("signer is null") + } + chbook, err := contract.NewChequebook(contractAddr, abigen) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + transactOpts.GasLimit = gasToCash + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + sender := transactOpts.From + + self = &Inbox{ + contract: contractAddr, + beneficiary: beneficiary, + sender: sender, + signer: signer, + session: session, + cashed: new(big.Int).Set(common.Big0), + } + glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer)) + return +} + +func (self *Inbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount) +} + +// Stop quits the autocash goroutine. +func (self *Inbox) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Cash attempts to cash the current cheque. +func (self *Inbox) Cash() (txhash string, err error) { + if self.cheque != nil { + txhash, err = self.cheque.Cash(self.session) + glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex()) + self.cashed = self.cheque.Amount + } + return +} + +// AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed +// cheque if maxUncashed is set to 0, then autocash on receipt. +func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.maxUncashed = maxUncashed + self.autoCash(cashInterval) +} + +// autoCash starts a loop that periodically clears the last check +// if the peer is trusted. Clearing period could be 24h or a week. +// +// The caller must hold self.lock. +func (self *Inbox) autoCash(cashInterval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if maxUncashed is set to 0, then autocash on receipt + if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 { + return + } + + ticker := time.NewTicker(cashInterval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 { + txhash, err := self.Cash() + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Receive is called to deposit the latest cheque to the incoming Inbox. +// The given promise must be a *Cheque. +func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) { + ch := promise.(*Cheque) + + defer self.lock.Unlock() + self.lock.Lock() + + var sum *big.Int + if self.cheque == nil { + // the sum is checked against the blockchain once a check is received + tally, err := self.session.Sent(self.beneficiary) + if err != nil { + return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err) + } + sum = tally + } else { + sum = self.cheque.Amount + } + + amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum) + var uncashed *big.Int + if err == nil { + self.cheque = ch + + if self.maxUncashed != nil { + uncashed = new(big.Int).Sub(ch.Amount, self.cashed) + if self.maxUncashed.Cmp(uncashed) < 0 { + self.Cash() + } + } + glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed) + } + + return amount, err +} + +// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature. +func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) { + glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum) + if sum == nil { + return nil, fmt.Errorf("invalid amount") + } + + if self.Beneficiary != beneficiary { + return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex()) + } + if self.Contract != contract { + return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex()) + } + + amount := new(big.Int).Set(self.Amount) + if sum != nil { + amount.Sub(amount, sum) + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("incorrect amount: %v <= 0", amount) + } + } + + pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig) + if err != nil { + return nil, fmt.Errorf("invalid signature: %v", err) + } + if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) { + return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) + } + return amount, nil +} + +// v/r/s representation of signature +func sig2vrs(sig []byte) (v byte, r, s [32]byte) { + v = sig[64] + 27 + copy(r[:], sig[:32]) + copy(s[:], sig[32:64]) + return +} + +// Cash cashes the cheque by sending an Ethereum transaction. +func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) { + v, r, s := sig2vrs(self.Sig) + tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s) + if err != nil { + return "", err + } + return tx.Hash().Hex(), nil +} + +// ValidateCode checks that the on-chain code at address matches the expected chequebook +// contract code. This is used to detect suicided chequebooks. +func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) { + code, err := b.CodeAt(ctx, address, nil) + if err != nil { + return false, err + } + return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go new file mode 100644 index 000000000..47090152c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go @@ -0,0 +1,541 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +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" +) + +// ChequebookABI is the input ABI used to generate the binding from. +const ChequebookABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"sent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]` + +// ChequebookBin is the compiled bytecode used for deploying new contracts. +const ChequebookBin = `0x606060405260008054600160a060020a031916331790556101ff806100246000396000f3606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff` + +// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it. +func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// Chequebook is an auto generated Go binding around an Ethereum contract. +type Chequebook struct { + ChequebookCaller // Read-only binding to the contract + ChequebookTransactor // Write-only binding to the contract +} + +// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract. +type ChequebookCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ChequebookTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ChequebookSession struct { + Contract *Chequebook // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ChequebookCallerSession struct { + Contract *ChequebookCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ChequebookTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ChequebookTransactorSession struct { + Contract *ChequebookTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookRaw is an auto generated low-level Go binding around an Ethereum contract. +type ChequebookRaw struct { + Contract *Chequebook // Generic contract binding to access the raw methods on +} + +// ChequebookCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ChequebookCallerRaw struct { + Contract *ChequebookCaller // Generic read-only contract binding to access the raw methods on +} + +// ChequebookTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ChequebookTransactorRaw struct { + Contract *ChequebookTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract. +func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) { + contract, err := bindChequebook(address, backend, backend) + if err != nil { + return nil, err + } + return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) { + contract, err := bindChequebook(address, caller, nil) + if err != nil { + return nil, err + } + return &ChequebookCaller{contract: contract}, nil +} + +// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) { + contract, err := bindChequebook(address, nil, transactor) + if err != nil { + return nil, err + } + return &ChequebookTransactor{contract: contract}, nil +} + +// bindChequebook binds a generic wrapper to an already deployed contract. +func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.ChequebookCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transact(opts, method, params...) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCaller) Sent(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _Chequebook.contract.Call(opts, out, "sent", arg0) + return *ret0, err +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// MortalABI is the input ABI used to generate the binding from. +const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]` + +// MortalBin is the compiled bytecode used for deploying new contracts. +const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56` + +// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it. +func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// Mortal is an auto generated Go binding around an Ethereum contract. +type Mortal struct { + MortalCaller // Read-only binding to the contract + MortalTransactor // Write-only binding to the contract +} + +// MortalCaller is an auto generated read-only Go binding around an Ethereum contract. +type MortalCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract. +type MortalTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MortalSession struct { + Contract *Mortal // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MortalCallerSession struct { + Contract *MortalCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MortalTransactorSession struct { + Contract *MortalTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalRaw is an auto generated low-level Go binding around an Ethereum contract. +type MortalRaw struct { + Contract *Mortal // Generic contract binding to access the raw methods on +} + +// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MortalCallerRaw struct { + Contract *MortalCaller // Generic read-only contract binding to access the raw methods on +} + +// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MortalTransactorRaw struct { + Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewMortal creates a new instance of Mortal, bound to a specific deployed contract. +func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) { + contract, err := bindMortal(address, backend, backend) + if err != nil { + return nil, err + } + return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract. +func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) { + contract, err := bindMortal(address, caller, nil) + if err != nil { + return nil, err + } + return &MortalCaller{contract: contract}, nil +} + +// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract. +func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) { + contract, err := bindMortal(address, nil, transactor) + if err != nil { + return nil, err + } + return &MortalTransactor{contract: contract}, nil +} + +// bindMortal binds a generic wrapper to an already deployed contract. +func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transact(opts, method, params...) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// OwnedABI is the input ABI used to generate the binding from. +const OwnedABI = `[{"inputs":[],"type":"constructor"}]` + +// OwnedBin is the compiled bytecode used for deploying new contracts. +const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200` + +// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it. +func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// Owned is an auto generated Go binding around an Ethereum contract. +type Owned struct { + OwnedCaller // Read-only binding to the contract + OwnedTransactor // Write-only binding to the contract +} + +// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract. +type OwnedCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract. +type OwnedTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type OwnedSession struct { + Contract *Owned // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type OwnedCallerSession struct { + Contract *OwnedCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type OwnedTransactorSession struct { + Contract *OwnedTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract. +type OwnedRaw struct { + Contract *Owned // Generic contract binding to access the raw methods on +} + +// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type OwnedCallerRaw struct { + Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on +} + +// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type OwnedTransactorRaw struct { + Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewOwned creates a new instance of Owned, bound to a specific deployed contract. +func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) { + contract, err := bindOwned(address, backend, backend) + if err != nil { + return nil, err + } + return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract. +func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) { + contract, err := bindOwned(address, caller, nil) + if err != nil { + return nil, err + } + return &OwnedCaller{contract: contract}, nil +} + +// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract. +func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) { + contract, err := bindOwned(address, nil, transactor) + if err != nil { + return nil, err + } + return &OwnedTransactor{contract: contract}, nil +} + +// bindOwned binds a generic wrapper to an already deployed contract. +func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.contract.Transact(opts, method, params...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol new file mode 100644 index 000000000..eefe6c063 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol @@ -0,0 +1,45 @@ +import "mortal"; + +/// @title Chequebook for Ethereum micropayments +/// @author Daniel A. Nagy +contract chequebook is mortal { + // Cumulative paid amount in wei to each beneficiary + mapping (address => uint256) public sent; + + /// @notice Overdraft event + event Overdraft(address deadbeat); + + /// @notice Cash cheque + /// + /// @param beneficiary beneficiary address + /// @param amount cumulative amount in wei + /// @param sig_v signature parameter v + /// @param sig_r signature parameter r + /// @param sig_s signature parameter s + /// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount + function cash(address beneficiary, uint256 amount, + uint8 sig_v, bytes32 sig_r, bytes32 sig_s) { + // Check if the cheque is old. + // Only cheques that are more recent than the last cashed one are considered. + if(amount <= sent[beneficiary]) return; + // Check the digital signature of the cheque. + bytes32 hash = sha3(address(this), beneficiary, amount); + if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return; + // Attempt sending the difference between the cumulative amount on the cheque + // and the cumulative amount on the last cashed cheque to beneficiary. + if (amount - sent[beneficiary] >= this.balance) { + // update the cumulative amount before sending + sent[beneficiary] = amount; + if (!beneficiary.send(amount - sent[beneficiary])) { + // Upon failure to execute send, revert everything + throw; + } + } else { + // Upon failure, punish owner for writing a bounced cheque. + // owner.sendToDebtorsPrison(); + Overdraft(owner); + // Compensate beneficiary. + suicide(beneficiary); + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go new file mode 100644 index 000000000..b08e04e71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go @@ -0,0 +1,5 @@ +package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "0x606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff" diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go new file mode 100644 index 000000000..faa927279 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go @@ -0,0 +1,71 @@ +// 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 none + +// This program generates contract/code.go, which contains the chequebook code +// after deployment. +package main + +import ( + "fmt" + "io/ioutil" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAccount = core.GenesisAccount{ + Address: crypto.PubkeyToAddress(testKey.PublicKey), + Balance: big.NewInt(500000000000), + } +) + +func main() { + backend := backends.NewSimulatedBackend(testAccount) + auth := bind.NewKeyedTransactor(testKey) + + // Deploy the contract, get the code. + addr, _, _, err := contract.DeployChequebook(auth, backend) + if err != nil { + panic(err) + } + backend.Commit() + code, err := backend.CodeAt(nil, addr, nil) + if err != nil { + panic(err) + } + if len(code) == 0 { + panic("empty code") + } + + // Write the output file. + content := fmt.Sprintf(`package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "%#x" +`, code) + if err := ioutil.WriteFile("contract/code.go", []byte(content), 0644); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go new file mode 100644 index 000000000..aca16de50 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go @@ -0,0 +1,921 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +import ( + "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" +) + +// ENSABI is the input ABI used to generate the binding from. +const ENSABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},{"inputs":[{"name":"owner","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"}]` + +// ENSBin is the compiled bytecode used for deploying new contracts. +const ENSBin = `0x606060405260405160208061032683395060806040525160008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a03191682179055506102c88061005e6000396000f3606060405260e060020a60003504630178b8bf811461004757806302571be31461006e57806306ab5923146100915780631896f70a146100c85780635b0fc9c3146100fc575b005b610130600435600081815260208190526040902060010154600160a060020a03165b919050565b610130600435600081815260208190526040902054600160a060020a0316610069565b6100456004356024356044356000838152602081905260408120548490600160a060020a0390811633919091161461014d57610002565b6100456004356024356000828152602081905260409020548290600160a060020a039081163391909116146101e757610002565b6100456004356024356000828152602081905260409020548290600160a060020a0390811633919091161461025957610002565b60408051600160a060020a03929092168252519081900360200190f35b60408051868152602081810187905282519182900383018220600160a060020a03871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a382600060005060008460001916815260200190815260200160002060005060000160006101000a815481600160a060020a03021916908302179055505050505050565b60408051600160a060020a0384168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a2506000828152602081905260409020600101805473ffffffffffffffffffffffffffffffffffffffff1916821790555050565b60408051600160a060020a0384168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a2506000828152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191682179055505056` + +// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it. +func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.Address) (common.Address, *types.Transaction, *ENS, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend, owner) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// ENS is an auto generated Go binding around an Ethereum contract. +type ENS struct { + ENSCaller // Read-only binding to the contract + ENSTransactor // Write-only binding to the contract +} + +// ENSCaller is an auto generated read-only Go binding around an Ethereum contract. +type ENSCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ENSTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ENSSession struct { + Contract *ENS // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ENSCallerSession struct { + Contract *ENSCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ENSTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ENSTransactorSession struct { + Contract *ENSTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSRaw is an auto generated low-level Go binding around an Ethereum contract. +type ENSRaw struct { + Contract *ENS // Generic contract binding to access the raw methods on +} + +// ENSCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ENSCallerRaw struct { + Contract *ENSCaller // Generic read-only contract binding to access the raw methods on +} + +// ENSTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ENSTransactorRaw struct { + Contract *ENSTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewENS creates a new instance of ENS, bound to a specific deployed contract. +func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) { + contract, err := bindENS(address, backend, backend) + if err != nil { + return nil, err + } + return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract. +func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) { + contract, err := bindENS(address, caller, nil) + if err != nil { + return nil, err + } + return &ENSCaller{contract: contract}, nil +} + +// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract. +func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) { + contract, err := bindENS(address, nil, transactor) + if err != nil { + return nil, err + } + return &ENSTransactor{contract: contract}, nil +} + +// bindENS binds a generic wrapper to an already deployed contract. +func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.ENSCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.contract.Transact(opts, method, params...) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Owner(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "owner", node) + return *ret0, err +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Resolver(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "resolver", node) + return *ret0, err +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetOwner(opts *bind.TransactOpts, node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setOwner", node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactor) SetResolver(opts *bind.TransactOpts, node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setResolver", node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactorSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetSubnodeOwner(opts *bind.TransactOpts, node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setSubnodeOwner", node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// FIFSRegistrarABI is the input ABI used to generate the binding from. +const FIFSRegistrarABI = `[{"constant":false,"inputs":[{"name":"subnode","type":"bytes32"},{"name":"owner","type":"address"}],"name":"register","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"node","type":"bytes32"}],"type":"constructor"}]` + +// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts. +const FIFSRegistrarBin = `0x6060604081815280610620833960a090525160805160008054600160a060020a031916831790558160a0610367806100878339018082600160a060020a03168152602001915050604051809103906000f0600160006101000a815481600160a060020a0302191690830217905550806002600050819055505050610232806103ee6000396000f3606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056606060405260e060020a6000350463d22057a9811461001b575b005b61001960043560243560025460408051918252602082810185905260008054835194859003840185207f02571be300000000000000000000000000000000000000000000000000000000865260048601819052935193949193600160a060020a03909116926302571be39260248181019391829003018187876161da5a03f11561000257505060405151915050600160a060020a0381166000148015906100d4575033600160a060020a031681600160a060020a031614155b156100de57610002565b60408051600080546002547f06ab592300000000000000000000000000000000000000000000000000000000845260048401526024830188905230600160a060020a03908116604485015293519316926306ab5923926064818101939291829003018183876161da5a03f11561000257505060008054600154604080517f1896f70a00000000000000000000000000000000000000000000000000000000815260048101889052600160a060020a0392831660248201529051929091169350631896f70a926044828101939192829003018183876161da5a03f11561000257505060008054604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260048101879052600160a060020a0388811660248301529151929091169350635b0fc9c3926044828101939192829003018183876161da5a03f115610002575050505050505056` + +// DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it. +func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(FIFSRegistrarBin), backend, ensAddr, node) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// FIFSRegistrar is an auto generated Go binding around an Ethereum contract. +type FIFSRegistrar struct { + FIFSRegistrarCaller // Read-only binding to the contract + FIFSRegistrarTransactor // Write-only binding to the contract +} + +// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract. +type FIFSRegistrarCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarTransactor is an auto generated write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type FIFSRegistrarSession struct { + Contract *FIFSRegistrar // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type FIFSRegistrarCallerSession struct { + Contract *FIFSRegistrarCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// FIFSRegistrarTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type FIFSRegistrarTransactorSession struct { + Contract *FIFSRegistrarTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarRaw is an auto generated low-level Go binding around an Ethereum contract. +type FIFSRegistrarRaw struct { + Contract *FIFSRegistrar // Generic contract binding to access the raw methods on +} + +// FIFSRegistrarCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type FIFSRegistrarCallerRaw struct { + Contract *FIFSRegistrarCaller // Generic read-only contract binding to access the raw methods on +} + +// FIFSRegistrarTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactorRaw struct { + Contract *FIFSRegistrarTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) { + contract, err := bindFIFSRegistrar(address, backend, backend) + if err != nil { + return nil, err + } + return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) { + contract, err := bindFIFSRegistrar(address, caller, nil) + if err != nil { + return nil, err + } + return &FIFSRegistrarCaller{contract: contract}, nil +} + +// NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) { + contract, err := bindFIFSRegistrar(address, nil, transactor) + if err != nil { + return nil, err + } + return &FIFSRegistrarTransactor{contract: contract}, nil +} + +// bindFIFSRegistrar binds a generic wrapper to an already deployed contract. +func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.FIFSRegistrarCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transact(opts, method, params...) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactor) Register(opts *bind.TransactOpts, subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.contract.Transact(opts, "register", subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// PublicResolverABI is the input ABI used to generate the binding from. +const PublicResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// PublicResolverBin is the compiled bytecode used for deploying new contracts. +const PublicResolverBin = `0x606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056` + +// DeployPublicResolver deploys a new Ethereum contract, binding an instance of PublicResolver to it. +func DeployPublicResolver(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address) (common.Address, *types.Transaction, *PublicResolver, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(PublicResolverBin), backend, ensAddr) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// PublicResolver is an auto generated Go binding around an Ethereum contract. +type PublicResolver struct { + PublicResolverCaller // Read-only binding to the contract + PublicResolverTransactor // Write-only binding to the contract +} + +// PublicResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type PublicResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PublicResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PublicResolverSession struct { + Contract *PublicResolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PublicResolverCallerSession struct { + Contract *PublicResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PublicResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PublicResolverTransactorSession struct { + Contract *PublicResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type PublicResolverRaw struct { + Contract *PublicResolver // Generic contract binding to access the raw methods on +} + +// PublicResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PublicResolverCallerRaw struct { + Contract *PublicResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// PublicResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PublicResolverTransactorRaw struct { + Contract *PublicResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPublicResolver creates a new instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolver(address common.Address, backend bind.ContractBackend) (*PublicResolver, error) { + contract, err := bindPublicResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// NewPublicResolverCaller creates a new read-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverCaller(address common.Address, caller bind.ContractCaller) (*PublicResolverCaller, error) { + contract, err := bindPublicResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &PublicResolverCaller{contract: contract}, nil +} + +// NewPublicResolverTransactor creates a new write-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*PublicResolverTransactor, error) { + contract, err := bindPublicResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &PublicResolverTransactor{contract: contract}, nil +} + +// bindPublicResolver binds a generic wrapper to an already deployed contract. +func bindPublicResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.PublicResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactor) SetAddr(opts *bind.TransactOpts, node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setAddr", node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactor) SetContent(opts *bind.TransactOpts, node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setContent", node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// ResolverABI is the input ABI used to generate the binding from. +const ResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// ResolverBin is the compiled bytecode used for deploying new contracts. +const ResolverBin = `0x` + +// DeployResolver deploys a new Ethereum contract, binding an instance of Resolver to it. +func DeployResolver(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Resolver, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ResolverBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// Resolver is an auto generated Go binding around an Ethereum contract. +type Resolver struct { + ResolverCaller // Read-only binding to the contract + ResolverTransactor // Write-only binding to the contract +} + +// ResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type ResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ResolverSession struct { + Contract *Resolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ResolverCallerSession struct { + Contract *ResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ResolverTransactorSession struct { + Contract *ResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type ResolverRaw struct { + Contract *Resolver // Generic contract binding to access the raw methods on +} + +// ResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ResolverCallerRaw struct { + Contract *ResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// ResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ResolverTransactorRaw struct { + Contract *ResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewResolver creates a new instance of Resolver, bound to a specific deployed contract. +func NewResolver(address common.Address, backend bind.ContractBackend) (*Resolver, error) { + contract, err := bindResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// NewResolverCaller creates a new read-only instance of Resolver, bound to a specific deployed contract. +func NewResolverCaller(address common.Address, caller bind.ContractCaller) (*ResolverCaller, error) { + contract, err := bindResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &ResolverCaller{contract: contract}, nil +} + +// NewResolverTransactor creates a new write-only instance of Resolver, bound to a specific deployed contract. +func NewResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*ResolverTransactor, error) { + contract, err := bindResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &ResolverTransactor{contract: contract}, nil +} + +// bindResolver binds a generic wrapper to an already deployed contract. +func bindResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.ResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol new file mode 100644 index 000000000..114cd7319 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol @@ -0,0 +1,226 @@ +// Ethereum Name Service contracts by Nick Johnson +// +// To the extent possible under law, the person who associated CC0 with +// ENS contracts has waived all copyright and related or neighboring rights +// to ENS. +// +// You should have received a copy of the CC0 legalcode along with this +// work. If not, see . + +/** + * The ENS registry contract. + */ +contract ENS { + struct Record { + address owner; + address resolver; + } + + mapping(bytes32=>Record) records; + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the owner of a node changes the resolver for that node. + event NewResolver(bytes32 indexed node, address resolver); + + // Permits modifications only by the owner of the specified node. + modifier only_owner(bytes32 node) { + if(records[node].owner != msg.sender) throw; + _ + } + + /** + * Constructs a new ENS registrar, with the provided address as the owner of the root node. + */ + function ENS(address owner) { + records[0].owner = owner; + } + + /** + * Returns the address that owns the specified node. + */ + function owner(bytes32 node) constant returns (address) { + return records[node].owner; + } + + /** + * Returns the address of the resolver for the specified node. + */ + function resolver(bytes32 node) constant returns (address) { + return records[node].resolver; + } + + /** + * Transfers ownership of a node to a new address. May only be called by the current + * owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) only_owner(node) { + Transfer(node, owner); + records[node].owner = owner; + } + + /** + * Transfers ownership of a subnode sha3(node, label) to a new address. May only be + * called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) { + var subnode = sha3(node, label); + NewOwner(node, label, owner); + records[subnode].owner = owner; + } + + /** + * Sets the resolver address for the specified node. + * @param node The node to update. + * @param resolver The address of the resolver. + */ + function setResolver(bytes32 node, address resolver) only_owner(node) { + NewResolver(node, resolver); + records[node].resolver = resolver; + } +} + +/** + * A registrar that allocates subdomains to the first person to claim them. It also deploys + * a simple resolver contract and sets that as the default resolver on new names for + * convenience. + */ +contract FIFSRegistrar { + ENS ens; + PublicResolver defaultResolver; + bytes32 rootNode; + + /** + * Constructor. + * @param ensAddr The address of the ENS registry. + * @param node The node that this registrar administers. + */ + function FIFSRegistrar(address ensAddr, bytes32 node) { + ens = ENS(ensAddr); + defaultResolver = new PublicResolver(ensAddr); + rootNode = node; + } + + /** + * Register a name, or change the owner of an existing registration. + * @param subnode The hash of the label to register. + * @param owner The address of the new owner. + */ + function register(bytes32 subnode, address owner) { + var node = sha3(rootNode, subnode); + var currentOwner = ens.owner(node); + if(currentOwner != 0 && currentOwner != msg.sender) + throw; + + // Temporarily set ourselves as the owner + ens.setSubnodeOwner(rootNode, subnode, this); + // Set up the default resolver + ens.setResolver(node, defaultResolver); + // Set the owner to the real owner + ens.setOwner(node, owner); + } +} + +contract Resolver { + event AddrChanged(bytes32 indexed node, address a); + event ContentChanged(bytes32 indexed node, bytes32 hash); + + function has(bytes32 node, bytes32 kind) returns (bool); + function addr(bytes32 node) constant returns (address ret); + function content(bytes32 node) constant returns (bytes32 ret); +} + +/** + * A simple resolver anyone can use; only allows the owner of a node to set its + * address. + */ +contract PublicResolver is Resolver { + ENS ens; + mapping(bytes32=>address) addresses; + mapping(bytes32=>bytes32) contents; + + modifier only_owner(bytes32 node) { + if(ens.owner(node) != msg.sender) throw; + _ + } + + /** + * Constructor. + * @param ensAddr The ENS registrar contract. + */ + function PublicResolver(address ensAddr) { + ens = ENS(ensAddr); + } + + /** + * Fallback function. + */ + function() { + throw; + } + + /** + * Returns true if the specified node has the specified record type. + * @param node The ENS node to query. + * @param kind The record type name, as specified in EIP137. + * @return True if this resolver has a record of the provided type on the + * provided node. + */ + function has(bytes32 node, bytes32 kind) returns (bool) { + return (kind == "addr" && addresses[node] != 0) || + (kind == "content" && contents[node] != 0); + } + + /** + * Returns the address associated with an ENS node. + * @param node The ENS node to query. + * @return The associated address. + */ + function addr(bytes32 node) constant returns (address ret) { + ret = addresses[node]; + if(ret == 0) + throw; + } + + /** + * Returns the content hash associated with an ENS node. + * @param node The ENS node to query. + * @return The associated content hash. + */ + function content(bytes32 node) constant returns (bytes32 ret) { + ret = contents[node]; + if(ret == 0) + throw; + } + + /** + * Sets the address associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param addr The address to set. + */ + function setAddr(bytes32 node, address addr) only_owner(node) { + addresses[node] = addr; + AddrChanged(node, addr); + } + + /** + * Sets the content hash associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The content hash to set. + */ + function setContent(bytes32 node, bytes32 hash) only_owner(node) { + contents[node] = hash; + ContentChanged(node, hash); + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go new file mode 100644 index 000000000..7806742cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go @@ -0,0 +1,178 @@ +// 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 ens + +//go:generate abigen --sol contract/ens.sol --pkg contract --out contract/ens.go + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/ens/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// swarm domain name registry and resolver +type ENS struct { + *contract.ENSSession + contractBackend bind.ContractBackend +} + +// NewENS creates a struct exposing convenient high-level operations for interacting with +// the Ethereum Name Service. +func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) (*ENS, error) { + ens, err := contract.NewENS(contractAddr, contractBackend) + if err != nil { + return nil, err + } + + return &ENS{ + &contract.ENSSession{ + Contract: ens, + TransactOpts: *transactOpts, + }, + contractBackend, + }, nil +} + +// DeployENS deploys an instance of the ENS nameservice, with a 'first in first served' root registrar. +func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (*ENS, error) { + // Deploy the ENS registry + ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend, transactOpts.From) + if err != nil { + return nil, err + } + + ens, err := NewENS(transactOpts, ensAddr, contractBackend) + if err != nil { + return nil, err + } + + // Deploy the registrar + regAddr, _, _, err := contract.DeployFIFSRegistrar(transactOpts, contractBackend, ensAddr, [32]byte{}) + if err != nil { + return nil, err + } + + // Set the registrar as owner of the ENS root + _, err = ens.SetOwner([32]byte{}, regAddr) + if err != nil { + return nil, err + } + + return ens, nil +} + +func ensParentNode(name string) (common.Hash, common.Hash) { + parts := strings.SplitN(name, ".", 2) + label := crypto.Keccak256Hash([]byte(parts[0])) + if len(parts) == 1 { + return [32]byte{}, label + } else { + parentNode, parentLabel := ensParentNode(parts[1]) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]), label + } +} + +func ensNode(name string) common.Hash { + parentNode, parentLabel := ensParentNode(name) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]) +} + +func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) { + resolverAddr, err := self.Resolver(node) + if err != nil { + return nil, err + } + + resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.PublicResolverSession{ + Contract: resolver, + TransactOpts: self.TransactOpts, + }, nil +} + +func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) { + registrarAddr, err := self.Owner(node) + if err != nil { + return nil, err + } + + registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.FIFSRegistrarSession{ + Contract: registrar, + TransactOpts: self.TransactOpts, + }, nil +} + +// Resolve is a non-transactional call that returns the content hash associated with a name. +func (self *ENS) Resolve(name string) (common.Hash, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return common.Hash{}, err + } + + ret, err := resolver.Content(node) + if err != nil { + return common.Hash{}, err + } + + return common.BytesToHash(ret[:]), nil +} + +// Register registers a new domain name for the caller, making them the owner of the new name. +// Only works if the registrar for the parent domain implements the FIFS registrar protocol. +func (self *ENS) Register(name string) (*types.Transaction, error) { + parentNode, label := ensParentNode(name) + + registrar, err := self.getRegistrar(parentNode) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return registrar.Contract.Register(&opts, label, self.TransactOpts.From) +} + +// SetContentHash sets the content hash associated with a name. Only works if the caller +// owns the name, and the associated resolver implements a `setContent` function. +func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return resolver.Contract.SetContent(&opts, node, hash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/release/contract.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go similarity index 61% rename from vendor/github.com/ethereum/go-ethereum/release/contract.go rename to vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go index 51c1443d4..6a0b09931 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/contract.go +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go @@ -14,10 +14,10 @@ import ( ) // ReleaseOracleABI is the input ABI used to generate the binding from. -const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` +const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` // ReleaseOracleBin is the compiled bytecode used for deploying new contracts. -const ReleaseOracleBin = `0x60606040526040516114033803806114038339810160405280510160008151600014156100ce57600160a060020a0333168152602081905260408120805460ff191660019081179091558054808201808355828183801582901161015e5781836000526020600020918201910161015e91905b8082111561019957600081558401610072565b505050919090600052602060002090016000848481518110156100025790602001906020020151909190916101000a815481600160a060020a0302191690830217905550506001015b815181101561018957600160006000506000848481518110156100025790602001906020020151600160a060020a0316815260200190815260200160002060006101000a81548160ff0219169083021790555060016000508054806001018281815481835581811511610085576000839052610085906000805160206113e3833981519152908101908301610072565b5050506000929092526000805160206113e3833981519152018054600160a060020a03191633179055505b50506112458061019e6000396000f35b50905600606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fb578063d0e0813a14610467578063d67cbec914610478575b005b610495604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b6100776004356106d78160005b600160a060020a033316600090815260208190526040812054819060ff16156106df57600160a060020a038416815260026020526040812091505b81548110156106e7578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610732576106df565b6105926004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b6106176000600060006000600060006008600050805490506000141561064e576106cf565b6100776106e56000808080805b600160a060020a033316600090815260208190526040812054819060ff161561121c57821580156103f1575060065481145b15610ca55761121c565b6040805160208181018352600082526003805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b6100776004356106d7816001610217565b6100776004356024356044356064356106df8484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526060830152608082019290925290519081900360a00190f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b600182015481101561073a5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610784576106df565b600101610252565b8154600014801561074f575060018201546000145b156107ac576003805460018101808355828183801582901161078c5781836000526020600020918201910161078c9190610834565b6001016106eb565b5050506000928352506020909120018054600160a060020a031916851790555b821561084c578154600181018084558391908281838015829011610881578183600052602060002091820191016108819190610834565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b5c91908101905b808211156108485760008155600101610834565b5090565b816001016000508054806001018281815481835581811511610933578183600052602060002091820191016109339190610834565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108b5576106df565b8280156108db5750600160a060020a03841660009081526020819052604090205460ff16155b1561096a57600160a060020a0384166000908152602081905260409020805460ff19166001908117909155805480820180835582818380158290116107e3578183600052602060002091820191016107e39190610834565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108b5576106df565b821580156109905750600160a060020a03841660009081526020819052604090205460ff165b156108035750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108035783600160a060020a03166001600050828154811015610002576000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60154600160a060020a03161415610ad057600180546000198101908110156100025760009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601909054906101000a9004600160a060020a03166001600050828154811015610002577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6018054600160a060020a03191690921790915580546000198101808355909190828015829011610ad857818360005260206000209182019101610ad89190610834565b6001016109b7565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b32907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5060018201805460008083559182526020909120610b5291810190610834565b5050505050610803565b5060018201805460008083559182526020909120610b7c91810190610834565b506000925050505b6003548110156106df5783600160a060020a03166003600050828154811015610002576000919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154600160a060020a03161415610c9d57600380546000198101908110156100025760009182527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01909054906101000a9004600160a060020a03166003600050828154811015610002577fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a031916909217909155805460001981018083559091908280158290116106da578183600052602060002091820191016106da9190610834565b600101610b84565b60065460001415610d03576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d6d575060045463ffffffff8881169116141580610d395750600454640100000000900463ffffffff90811690871614155b80610d56575060045463ffffffff868116604060020a9092041614155b80610d6d5750600454606060020a90819004028414155b15610d775761121c565b506006905060005b8154811015610dc0578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610e0b5761121c565b5060005b6001820154811015610e135733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e485761121c565b600101610d7f565b8215610e50578154600181018084558391908281838015829011610e8557600083815260209020610e85918101908301610834565b600101610dc4565b816001016000508054806001018281815481835581811511610f0857818360005260206000209182019101610f089190610834565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610eb95761121c565b8215610f3f576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f9457600402816004028360005260206000209182019101610f9491906110ae565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610eb95761121c565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611225907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9290921691909117905560068054600284018054828255600082815260209020949594919283929182019185821561110d5760005260206000209182015b8281111561110d57825482559160010191906001019061108b565b505050506004015b8082111561084857600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ed9190810190610834565b50600182018054600080835591825260209091206110a691810190610834565b506111339291505b80821115610848578054600160a060020a0319168155600101611115565b50506001818101805491840180548083556000838152602090209293830192909182156111815760005260206000209182015b82811115611181578254825591600101919060010190611166565b5061118d929150611115565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111ec91507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b506001820180546000808355918252602090912061120c91810190610834565b505050505061121c565b50505050505b50505050505050565b50600182018054600080835591825260209091206112169181019061083456b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` +const ReleaseOracleBin = `0x606060405260405161135338038061135383398101604052805101600081516000141561008457600160a060020a0333168152602081905260408120805460ff19166001908117909155805480820180835582818380158290116100ff576000838152602090206100ff9181019083015b8082111561012f5760008155600101610070565b5060005b815181101561011f5760016000600050600084848151811015610002576020908102909101810151600160a060020a03168252810191909152604001600020805460ff1916909117905560018054808201808355828183801582901161013357600083815260209020610133918101908301610070565b5050506000928352506020909120018054600160a060020a031916331790555b50506111df806101746000396000f35b5090565b50505091909060005260206000209001600084848151811015610002575050506020838102850101518154600160a060020a0319161790555060010161008856606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fc578063d0e0813a14610468578063d67cbec914610479575b005b610496604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b61007760043561066d8160005b600160a060020a033316600090815260208190526040812054819060ff161561070057600160a060020a038416815260026020526040812091505b8154811015610706578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a0316141561075157610700565b6105a26004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b61062760006000600060006000600060086000508054905060001415610670576106f1565b6100776106f96000808080805b600160a060020a033316600090815260208190526040812054819060ff16156111b657821580156103f257506006546000145b15610c2e576111b6565b6040805160208181018352600082526003805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b61007760043561066d816001610217565b6100776004356024356044356064356107008484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff168152602001846bffffffffffffffffffffffff1916815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526bffffffffffffffffffffffff19166060830152608082019290925290519081900360a00190f35b50565b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b565b505050505b50505050565b5060005b60018201548110156107595733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a031614156107a357610700565b600101610252565b8154600014801561076e575060018201546000145b156107cb57600380546001810180835582818380158290116107ab578183600052602060002091820191016107ab9190610851565b60010161070a565b5050506000928352506020909120018054600160a060020a031916851790555b821561086957815460018101808455839190828183801582901161089e5760008381526020902061089e918101908301610851565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b2f91908101905b808211156108655760008155600101610851565b5090565b816001016000508054806001018281815481835581811511610950578183600052602060002091820191016109509190610851565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108d257610700565b8280156108f85750600160a060020a03841660009081526020819052604090205460ff16155b1561098757600160a060020a0384166000908152602081905260409020805460ff1916600190811790915580548082018083558281838015829011610800578183600052602060002091820191016108009190610851565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108d257610700565b821580156109ad5750600160a060020a03841660009081526020819052604090205460ff165b156108205750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108205783600160a060020a0316600160005082815481101561000257600091825260209091200154600160a060020a03161415610aa357600180546000198101908110156100025760206000908120929052600180549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a030219169083021790555060016000508054809190600190039090815481835581811511610aab57600083815260209020610aab918101908301610851565b6001016109d4565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b05907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5060018201805460008083559182526020909120610b2591810190610851565b5050505050610820565b5060018201805460008083559182526020909120610b4f91810190610851565b506000925050505b6003548110156107005783600160a060020a0316600360005082815481101561000257600091825260209091200154600160a060020a03161415610c2657600380546000198101908110156100025760206000908120929052600380549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a0302191690830217905550600360005080548091906001900390908154818355818115116106fb576000838152602090206106fb918101908301610851565b600101610b57565b60065460001415610c8c576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d08575060045463ffffffff8881169116141580610cc1575060045463ffffffff8781166401000000009092041614155b80610cde575060045463ffffffff868116604060020a9092041614155b80610d085750600454606060020a90819004026bffffffffffffffffffffffff1990811690851614155b15610d12576111b6565b506006905060005b8154811015610d5b578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610da6576111b6565b5060005b6001820154811015610dae5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610de3576111b6565b600101610d1a565b8215610deb578154600181018084558391908281838015829011610e2057600083815260209020610e20918101908301610851565b600101610d5f565b816001016000508054806001018281815481835581811511610ea357818360005260206000209182019101610ea39190610851565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610e54576111b6565b8215610eda576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f2f57600402816004028360005260206000209182019101610f2f9190611048565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610e54576111b6565b600060048181556005805467ffffffffffffffff191690556006805483825581845291929182906111bf907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9092169190911790556006805460028401805482825560008281526020902094959491928392918201918582156110a75760005260206000209182015b828111156110a7578254825591600101919060010190611025565b505050506004015b8082111561086557600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110879190810190610851565b506001820180546000808355918252602090912061104091810190610851565b506110cd9291505b80821115610865578054600160a060020a03191681556001016110af565b505060018181018054918401805480835560008381526020902092938301929091821561111b5760005260206000209182015b8281111561111b578254825591600101919060010190611100565b506111279291506110af565b5050600060048181556005805467ffffffffffffffff191690556006805483825581845291975091955090935084925061118691507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b50600182018054600080835591825260209091206111a691810190610851565b50505050506111b6565b50505050505b50505050505050565b50600182018054600080835591825260209091206111b09181019061085156` // DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend, signers []common.Address) (common.Address, *types.Transaction, *ReleaseOracle, error) { @@ -87,7 +87,7 @@ type ReleaseOracleTransactorRaw struct { // NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { - contract, err := bindReleaseOracle(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + contract, err := bindReleaseOracle(address, backend, backend) if err != nil { return nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/release/contract.sol b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol similarity index 100% rename from vendor/github.com/ethereum/go-ethereum/release/contract.sol rename to vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol diff --git a/vendor/github.com/ethereum/go-ethereum/release/release.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go similarity index 98% rename from vendor/github.com/ethereum/go-ethereum/release/release.go rename to vendor/github.com/ethereum/go-ethereum/contracts/release/release.go index 7c358aece..8d1710197 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/release.go +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go @@ -17,6 +17,8 @@ // Package release contains the node service that tracks client releases. package release +//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go + import ( "fmt" "strings" diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go index edb12b1f4..fecda3f59 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -93,10 +93,11 @@ type BlockChain struct { currentBlock *types.Block // Current head of the block chain currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks - futureBlocks *lru.Cache // future blocks are blocks added for later processing + stateCache *state.StateDB // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing quit chan struct{} // blockchain quit channel running int32 // running must be called atomically @@ -196,7 +197,15 @@ func (self *BlockChain) loadLastState() error { self.currentFastBlock = block } } - // Issue a status log and return + // Initialize a statedb cache to ensure singleton account bloom filter generation + statedb, err := state.New(self.currentBlock.Root(), self.chainDb) + if err != nil { + return err + } + self.stateCache = statedb + self.stateCache.GetAccount(common.Address{}) + + // Issue a status log for the user headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()) blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64()) fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()) @@ -348,7 +357,12 @@ func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } // State returns a new mutable state based on the current HEAD block. func (self *BlockChain) State() (*state.StateDB, error) { - return state.New(self.CurrentBlock().Root(), self.chainDb) + return self.StateAt(self.CurrentBlock().Root()) +} + +// StateAt returns a new mutable state based on a particular point in time. +func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { + return self.stateCache.New(root) } // Reset purges the entire blockchain, restoring it to its genesis state. @@ -832,7 +846,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { tstart = time.Now() nonceChecked = make([]bool, len(chain)) - statedb *state.StateDB ) // Start the parallel nonce verifier. @@ -899,29 +912,30 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // Create a new statedb using the parent block and report an // error if it fails. - if statedb == nil { - statedb, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb) - } else { - err = statedb.Reset(chain[i-1].Root()) + switch { + case i == 0: + err = self.stateCache.Reset(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) + default: + err = self.stateCache.Reset(chain[i-1].Root()) } if err != nil { reportBlock(block, err) return i, err } // Process block using the parent state as reference point. - receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig) + receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.config.VmConfig) if err != nil { reportBlock(block, err) return i, err } // Validate the state using the default validator - err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas) + err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas) if err != nil { reportBlock(block, err) return i, err } // Write state changes to database - _, err = statedb.Commit() + _, err = self.stateCache.Commit() if err != nil { return i, err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/dump.go b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go index a328b0537..58ecd852b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/dump.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go @@ -21,9 +21,10 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" ) -type Account struct { +type DumpAccount struct { Balance string `json:"balance"` Nonce uint64 `json:"nonce"` Root string `json:"root"` @@ -32,40 +33,41 @@ type Account struct { Storage map[string]string `json:"storage"` } -type World struct { - Root string `json:"root"` - Accounts map[string]Account `json:"accounts"` +type Dump struct { + Root string `json:"root"` + Accounts map[string]DumpAccount `json:"accounts"` } -func (self *StateDB) RawDump() World { - world := World{ +func (self *StateDB) RawDump() Dump { + dump := Dump{ Root: common.Bytes2Hex(self.trie.Root()), - Accounts: make(map[string]Account), + Accounts: make(map[string]DumpAccount), } it := self.trie.Iterator() for it.Next() { addr := self.trie.GetKey(it.Key) - stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value) - if err != nil { + var data Account + if err := rlp.DecodeBytes(it.Value, &data); err != nil { panic(err) } - account := Account{ - Balance: stateObject.balance.String(), - Nonce: stateObject.nonce, - Root: common.Bytes2Hex(stateObject.Root()), - CodeHash: common.Bytes2Hex(stateObject.codeHash), - Code: common.Bytes2Hex(stateObject.Code()), + obj := NewObject(common.BytesToAddress(addr), data, nil) + account := DumpAccount{ + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: common.Bytes2Hex(data.Root[:]), + CodeHash: common.Bytes2Hex(data.CodeHash), + Code: common.Bytes2Hex(obj.Code(self.db)), Storage: make(map[string]string), } - storageIt := stateObject.trie.Iterator() + storageIt := obj.getTrie(self.db).Iterator() for storageIt.Next() { account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) } - world.Accounts[common.Bytes2Hex(addr)] = account + dump.Accounts[common.Bytes2Hex(addr)] = account } - return world + return dump } func (self *StateDB) Dump() []byte { @@ -76,12 +78,3 @@ func (self *StateDB) Dump() []byte { return json } - -// Debug stuff -func (self *StateObject) CreateOutputForDiff() { - fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce) - it := self.trie.Iterator() - for it.Next() { - fmt.Printf("%x %x\n", it.Key, it.Value) - } -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go index 9d8a69b7c..14265b277 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go @@ -76,7 +76,7 @@ func (it *NodeIterator) step() error { } // Initialize the iterator if we've just started if it.stateIt == nil { - it.stateIt = trie.NewNodeIterator(it.state.trie.Trie) + it.stateIt = it.state.trie.NodeIterator() } // If we had data nodes previously, we surely have at least state nodes if it.dataIt != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go index f8e2f2b87..ad73dc0dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go @@ -33,14 +33,14 @@ type ManagedState struct { mu sync.RWMutex - accounts map[string]*account + accounts map[common.Address]*account } // ManagedState returns a new managed state with the statedb as it's backing layer func ManageState(statedb *StateDB) *ManagedState { return &ManagedState{ StateDB: statedb.Copy(), - accounts: make(map[string]*account), + accounts: make(map[common.Address]*account), } } @@ -103,7 +103,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) { so := ms.GetOrNewStateObject(addr) so.SetNonce(nonce) - ms.accounts[addr.Str()] = newAccount(so) + ms.accounts[addr] = newAccount(so) } // HasAccount returns whether the given address is managed or not @@ -114,29 +114,28 @@ func (ms *ManagedState) HasAccount(addr common.Address) bool { } func (ms *ManagedState) hasAccount(addr common.Address) bool { - _, ok := ms.accounts[addr.Str()] + _, ok := ms.accounts[addr] return ok } // populate the managed state func (ms *ManagedState) getAccount(addr common.Address) *account { - straddr := addr.Str() - if account, ok := ms.accounts[straddr]; !ok { + if account, ok := ms.accounts[addr]; !ok { so := ms.GetOrNewStateObject(addr) - ms.accounts[straddr] = newAccount(so) + ms.accounts[addr] = newAccount(so) } else { // Always make sure the state account nonce isn't actually higher // than the tracked one. so := ms.StateDB.GetStateObject(addr) - if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce { - ms.accounts[straddr] = newAccount(so) + if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() { + ms.accounts[addr] = newAccount(so) } } - return ms.accounts[straddr] + return ms.accounts[addr] } func newAccount(so *StateObject) *account { - return &account{so, so.nonce, nil} + return &account{so, so.Nonce(), nil} } 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 769c63d42..a54620d55 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 @@ -57,143 +57,194 @@ func (self Storage) Copy() Storage { return cpy } +// StateObject represents an Ethereum account which is being modified. +// +// The usage pattern is as follows: +// First you need to obtain a state object. +// Account values can be accessed and modified through the object. +// Finally, call CommitTrie to write the modified storage trie into a database. type StateObject struct { - db trie.Database // State database for storing state changes - trie *trie.SecureTrie + address common.Address // Ethereum address of this account + data Account - // Address belonging to this account - address common.Address - // The balance of the account - balance *big.Int - // The nonce of the account - nonce uint64 - // The code hash if code is present (i.e. a contract) - codeHash []byte - // The code for this account - code Code - // Temporarily initialisation code - initCode Code - // Cached storage (flushed when updated) - storage Storage + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error - // Mark for deletion + // Write caches. + trie *trie.SecureTrie // storage trie, which becomes non-nil on first access + code Code // contract bytecode, which gets set when code is loaded + storage Storage // Cached storage (flushed when updated) + + // Cache flags. // When an object is marked for deletion it will be delete from the trie // during the "update" phase of the state transition - remove bool - deleted bool - dirty bool + dirtyCode bool // true if the code was updated + remove bool + deleted bool + onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } -func NewStateObject(address common.Address, db trie.Database) *StateObject { - object := &StateObject{ - db: db, - address: address, - balance: new(big.Int), - dirty: true, - codeHash: emptyCodeHash, - storage: make(Storage), +// Account is the Ethereum consensus representation of accounts. +// These objects are stored in the main account trie. +type Account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash // merkle root of the storage trie + CodeHash []byte +} + +// NewObject creates a state object. +func NewObject(address common.Address, data Account, onDirty func(addr common.Address)) *StateObject { + if data.Balance == nil { + data.Balance = new(big.Int) + } + if data.CodeHash == nil { + data.CodeHash = emptyCodeHash + } + return &StateObject{address: address, data: data, storage: make(Storage), onDirty: onDirty} +} + +// EncodeRLP implements rlp.Encoder. +func (c *StateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, c.data) +} + +// setError remembers the first non-nil error it is called with. +func (self *StateObject) setError(err error) { + if self.dbErr == nil { + self.dbErr = err } - object.trie, _ = trie.NewSecure(common.Hash{}, db) - return object } func (self *StateObject) MarkForDeletion() { self.remove = true - self.dirty = true - + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } if glog.V(logger.Core) { - glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) + glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance()) } } -func (c *StateObject) getAddr(addr common.Hash) common.Hash { - var ret []byte - rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) - return common.BytesToHash(ret) -} - -func (c *StateObject) setAddr(addr, value common.Hash) { - v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) - if err != nil { - // if RLPing failed we better panic and not fail silently. This would be considered a consensus issue - panic(err) - } - c.trie.Update(addr[:], v) -} - -func (self *StateObject) Storage() Storage { - return self.storage -} - -func (self *StateObject) GetState(key common.Hash) common.Hash { - value, exists := self.storage[key] - if !exists { - value = self.getAddr(key) - if (value != common.Hash{}) { - self.storage[key] = value +func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { + if c.trie == nil { + var err error + c.trie, err = trie.NewSecure(c.data.Root, db) + if err != nil { + c.trie, _ = trie.NewSecure(common.Hash{}, db) + c.setError(fmt.Errorf("can't create storage trie: %v", err)) } } + return c.trie +} +// GetState returns a value in account storage. +func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash { + value, exists := self.storage[key] + if exists { + return value + } + // Load from DB in case it is missing. + tr := self.getTrie(db) + var ret []byte + rlp.DecodeBytes(tr.Get(key[:]), &ret) + value = common.BytesToHash(ret) + if (value != common.Hash{}) { + self.storage[key] = value + } return value } +// SetState updates a value in account storage. func (self *StateObject) SetState(key, value common.Hash) { self.storage[key] = value - self.dirty = true -} - -// Update updates the current cached storage to the trie -func (self *StateObject) Update() { - for key, value := range self.storage { - if (value == common.Hash{}) { - self.trie.Delete(key[:]) - continue - } - self.setAddr(key, value) + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil } } +// updateTrie writes cached storage modifications into the object's storage trie. +func (self *StateObject) updateTrie(db trie.Database) { + tr := self.getTrie(db) + for key, value := range self.storage { + if (value == common.Hash{}) { + tr.Delete(key[:]) + continue + } + // Encoding []byte cannot fail, ok to ignore the error. + v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + tr.Update(key[:], v) + } +} + +// UpdateRoot sets the trie root to the current root hash of +func (self *StateObject) UpdateRoot(db trie.Database) { + self.updateTrie(db) + self.data.Root = self.trie.Hash() +} + +// CommitTrie the storage trie of the object to dwb. +// This updates the trie root. +func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error { + self.updateTrie(db) + if self.dbErr != nil { + fmt.Println("dbErr:", self.dbErr) + return self.dbErr + } + root, err := self.trie.CommitTo(dbw) + if err == nil { + self.data.Root = root + } + return err +} + func (c *StateObject) AddBalance(amount *big.Int) { - c.SetBalance(new(big.Int).Add(c.balance, amount)) + if amount.Cmp(common.Big0) == 0 { + return + } + c.SetBalance(new(big.Int).Add(c.Balance(), amount)) if glog.V(logger.Core) { - glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) + glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) } } func (c *StateObject) SubBalance(amount *big.Int) { - c.SetBalance(new(big.Int).Sub(c.balance, amount)) + if amount.Cmp(common.Big0) == 0 { + return + } + c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) if glog.V(logger.Core) { - glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) + glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) } } -func (c *StateObject) SetBalance(amount *big.Int) { - c.balance = amount - c.dirty = true -} - -func (c *StateObject) St() Storage { - return c.storage +func (self *StateObject) SetBalance(amount *big.Int) { + self.data.Balance = amount + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } } // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(gas, price *big.Int) {} -func (self *StateObject) Copy() *StateObject { - stateObject := NewStateObject(self.Address(), self.db) - stateObject.balance.Set(self.balance) - stateObject.codeHash = common.CopyBytes(self.codeHash) - stateObject.nonce = self.nonce +func (self *StateObject) Copy(db trie.Database, onDirty func(addr common.Address)) *StateObject { + stateObject := NewObject(self.address, self.data, onDirty) stateObject.trie = self.trie - stateObject.code = common.CopyBytes(self.code) - stateObject.initCode = common.CopyBytes(self.initCode) + stateObject.code = self.code stateObject.storage = self.storage.Copy() stateObject.remove = self.remove - stateObject.dirty = self.dirty + stateObject.dirtyCode = self.dirtyCode stateObject.deleted = self.deleted - return stateObject } @@ -201,40 +252,55 @@ func (self *StateObject) Copy() *StateObject { // Attribute accessors // -func (self *StateObject) Balance() *big.Int { - return self.balance -} - // Returns the address of the contract/account func (c *StateObject) Address() common.Address { return c.address } -func (self *StateObject) Trie() *trie.SecureTrie { - return self.trie -} - -func (self *StateObject) Root() []byte { - return self.trie.Root() -} - -func (self *StateObject) Code() []byte { - return self.code +// Code returns the contract code associated with this object, if any. +func (self *StateObject) Code(db trie.Database) []byte { + if self.code != nil { + return self.code + } + if bytes.Equal(self.CodeHash(), emptyCodeHash) { + return nil + } + code, err := db.Get(self.CodeHash()) + if err != nil { + self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err)) + } + self.code = code + return code } func (self *StateObject) SetCode(code []byte) { self.code = code - self.codeHash = crypto.Keccak256(code) - self.dirty = true + self.data.CodeHash = crypto.Keccak256(code) + self.dirtyCode = true + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } } func (self *StateObject) SetNonce(nonce uint64) { - self.nonce = nonce - self.dirty = true + self.data.Nonce = nonce + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +func (self *StateObject) CodeHash() []byte { + return self.data.CodeHash +} + +func (self *StateObject) Balance() *big.Int { + return self.data.Balance } func (self *StateObject) Nonce() uint64 { - return self.nonce + return self.data.Nonce } // Never called, but must be present to allow StateObject to be used @@ -259,39 +325,3 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { } } } - -type extStateObject struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte -} - -// EncodeRLP implements rlp.Encoder. -func (c *StateObject) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash}) -} - -// DecodeObject decodes an RLP-encoded state object. -func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) { - var ( - obj = &StateObject{address: address, db: db, storage: make(Storage)} - ext extStateObject - err error - ) - if err = rlp.DecodeBytes(data, &ext); err != nil { - return nil, err - } - if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil { - return nil, err - } - if !bytes.Equal(ext.CodeHash, emptyCodeHash) { - if obj.code, err = db.Get(ext.CodeHash); err != nil { - return nil, fmt.Errorf("can't get code for hash %x: %v", ext.CodeHash, err) - } - } - obj.nonce = ext.Nonce - obj.balance = ext.Balance - obj.codeHash = ext.CodeHash - return obj, nil -} 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 3e25e0c16..5c51e3b59 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -20,6 +20,7 @@ package state import ( "fmt" "math/big" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" @@ -28,29 +29,46 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" ) // The starting nonce determines the default nonce when new accounts are being // created. var StartingNonce uint64 +const ( + // Number of past tries to keep. The arbitrarily chosen value here + // is max uncle depth + 1. + maxJournalLength = 8 + + // Number of codehash->size associations to keep. + codeSizeCacheSize = 100000 +) + // StateDBs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: // * Contracts // * Accounts type StateDB struct { - db ethdb.Database - trie *trie.SecureTrie + db ethdb.Database + trie *trie.SecureTrie + pastTries []*trie.SecureTrie + codeSizeCache *lru.Cache - stateObjects map[string]*StateObject + // This map holds 'live' objects, which will get modified while processing a state transition. + stateObjects map[common.Address]*StateObject + stateObjectsDirty map[common.Address]struct{} + // The refund counter, also used by state transitioning. refund *big.Int thash, bhash common.Hash txIndex int logs map[common.Hash]vm.Logs logSize uint + + lock sync.Mutex } // Create a new state from a given trie @@ -59,35 +77,84 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) { if err != nil { return nil, err } + csc, _ := lru.New(codeSizeCacheSize) return &StateDB{ - db: db, - trie: tr, - stateObjects: make(map[string]*StateObject), - refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), + db: db, + trie: tr, + codeSizeCache: csc, + stateObjects: make(map[common.Address]*StateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash]vm.Logs), + }, nil +} + +// New creates a new statedb by reusing any journalled tries to avoid costly +// disk io. +func (self *StateDB) New(root common.Hash) (*StateDB, error) { + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) + if err != nil { + return nil, err + } + return &StateDB{ + db: self.db, + trie: tr, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*StateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash]vm.Logs), }, nil } // Reset clears out all emphemeral state objects from the state db, but keeps // the underlying state trie to avoid reloading data for the next operations. func (self *StateDB) Reset(root common.Hash) error { - var ( - err error - tr = self.trie - ) - if self.trie.Hash() != root { - if tr, err = trie.NewSecure(root, self.db); err != nil { - return err + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) + if err != nil { + return err + } + self.trie = tr + self.stateObjects = make(map[common.Address]*StateObject) + self.stateObjectsDirty = make(map[common.Address]struct{}) + self.refund = new(big.Int) + self.thash = common.Hash{} + self.bhash = common.Hash{} + self.txIndex = 0 + self.logs = make(map[common.Hash]vm.Logs) + self.logSize = 0 + + return nil +} + +// openTrie creates a trie. It uses an existing trie if one is available +// from the journal if available. +func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { + for i := len(self.pastTries) - 1; i >= 0; i-- { + if self.pastTries[i].Hash() == root { + tr := *self.pastTries[i] + return &tr, nil } } - *self = StateDB{ - db: self.db, - trie: tr, - stateObjects: make(map[string]*StateObject), - refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), + return trie.NewSecure(root, self.db) +} + +func (self *StateDB) pushTrie(t *trie.SecureTrie) { + self.lock.Lock() + defer self.lock.Unlock() + + if len(self.pastTries) >= maxJournalLength { + copy(self.pastTries, self.pastTries[1:]) + self.pastTries[len(self.pastTries)-1] = t + } else { + self.pastTries = append(self.pastTries, t) } - return nil } func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { @@ -137,7 +204,7 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account { func (self *StateDB) GetBalance(addr common.Address) *big.Int { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.balance + return stateObject.Balance() } return common.Big0 @@ -146,7 +213,7 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int { func (self *StateDB) GetNonce(addr common.Address) uint64 { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.nonce + return stateObject.Nonce() } return StartingNonce @@ -155,18 +222,35 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 { func (self *StateDB) GetCode(addr common.Address) []byte { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.code + code := stateObject.Code(self.db) + key := common.BytesToHash(stateObject.CodeHash()) + self.codeSizeCache.Add(key, len(code)) + return code } - return nil } +func (self *StateDB) GetCodeSize(addr common.Address) int { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + return 0 + } + key := common.BytesToHash(stateObject.CodeHash()) + if cached, ok := self.codeSizeCache.Get(key); ok { + return cached.(int) + } + size := len(stateObject.Code(self.db)) + if stateObject.dbErr == nil { + self.codeSizeCache.Add(key, size) + } + return size +} + func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { stateObject := self.GetStateObject(a) if stateObject != nil { - return stateObject.GetState(b) + return stateObject.GetState(self.db, b) } - return common.Hash{} } @@ -214,8 +298,7 @@ func (self *StateDB) Delete(addr common.Address) bool { stateObject := self.GetStateObject(addr) if stateObject != nil { stateObject.MarkForDeletion() - stateObject.balance = new(big.Int) - + stateObject.data.Balance = new(big.Int) return true } @@ -242,35 +325,36 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) { addr := stateObject.Address() self.trie.Delete(addr[:]) - //delete(self.stateObjects, addr.Str()) } -// Retrieve a state object given my the address. Nil if not found +// Retrieve a state object given my the address. Returns nil if not found. func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) { - stateObject = self.stateObjects[addr.Str()] - if stateObject != nil { - if stateObject.deleted { - stateObject = nil + // Prefer 'live' objects. + if obj := self.stateObjects[addr]; obj != nil { + if obj.deleted { + return nil } - - return stateObject + return obj } - data := self.trie.Get(addr[:]) - if len(data) == 0 { + // Load the object from the database. + enc := self.trie.Get(addr[:]) + if len(enc) == 0 { return nil } - stateObject, err := DecodeObject(addr, self.db, data) - if err != nil { + var data Account + if err := rlp.DecodeBytes(enc, &data); err != nil { glog.Errorf("can't decode object at %x: %v", addr[:], err) return nil } - self.SetStateObject(stateObject) - return stateObject + // Insert into the live set. + obj := NewObject(addr, data, self.MarkStateObjectDirty) + self.SetStateObject(obj) + return obj } func (self *StateDB) SetStateObject(object *StateObject) { - self.stateObjects[object.Address().Str()] = object + self.stateObjects[object.Address()] = object } // Retrieve a state object or create a new state object if nil @@ -288,15 +372,19 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject { if glog.V(logger.Core) { glog.Infof("(+) %x\n", addr) } - - stateObject := NewStateObject(addr, self.db) - stateObject.SetNonce(StartingNonce) - self.stateObjects[addr.Str()] = stateObject - - return stateObject + obj := NewObject(addr, Account{}, self.MarkStateObjectDirty) + obj.SetNonce(StartingNonce) // sets the object to dirty + self.stateObjects[addr] = obj + return obj } -// Creates creates a new state object and takes ownership. This is different from "NewStateObject" +// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly +// state object cache iteration to find a handful of modified ones. +func (self *StateDB) MarkStateObjectDirty(addr common.Address) { + self.stateObjectsDirty[addr] = struct{}{} +} + +// Creates creates a new state object and takes ownership. func (self *StateDB) CreateStateObject(addr common.Address) *StateObject { // Get previous (if any) so := self.GetStateObject(addr) @@ -305,7 +393,7 @@ func (self *StateDB) CreateStateObject(addr common.Address) *StateObject { // If it existed set the balance to the new account if so != nil { - newSo.balance = so.balance + newSo.data.Balance = so.data.Balance } return newSo @@ -320,28 +408,43 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account { // func (self *StateDB) Copy() *StateDB { - // ignore error - we assume state-to-be-copied always exists - state, _ := New(common.Hash{}, self.db) - state.trie = self.trie - for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() + self.lock.Lock() + defer self.lock.Unlock() + + // Copy all the basic fields, initialize the memory ones + state := &StateDB{ + db: self.db, + trie: self.trie, + pastTries: self.pastTries, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + refund: new(big.Int).Set(self.refund), + logs: make(map[common.Hash]vm.Logs, len(self.logs)), + logSize: self.logSize, + } + // Copy the dirty states and logs + for addr, _ := range self.stateObjectsDirty { + state.stateObjects[addr] = self.stateObjects[addr].Copy(self.db, state.MarkStateObjectDirty) + state.stateObjectsDirty[addr] = struct{}{} } - - state.refund.Set(self.refund) - for hash, logs := range self.logs { state.logs[hash] = make(vm.Logs, len(logs)) copy(state.logs[hash], logs) } - state.logSize = self.logSize - return state } func (self *StateDB) Set(state *StateDB) { - self.trie = state.trie - self.stateObjects = state.stateObjects + self.lock.Lock() + defer self.lock.Unlock() + self.db = state.db + self.trie = state.trie + self.pastTries = state.pastTries + self.stateObjects = state.stateObjects + self.stateObjectsDirty = state.stateObjectsDirty + self.codeSizeCache = state.codeSizeCache self.refund = state.refund self.logs = state.logs self.logSize = state.logSize @@ -356,15 +459,13 @@ func (self *StateDB) GetRefund() *big.Int { // goes into transaction receipts. func (s *StateDB) IntermediateRoot() common.Hash { s.refund = new(big.Int) - for _, stateObject := range s.stateObjects { - if stateObject.dirty { - if stateObject.remove { - s.DeleteStateObject(stateObject) - } else { - stateObject.Update() - s.UpdateStateObject(stateObject) - } - stateObject.dirty = false + for addr, _ := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + if stateObject.remove { + s.DeleteStateObject(stateObject) + } else { + stateObject.UpdateRoot(s.db) + s.UpdateStateObject(stateObject) } } return s.trie.Hash() @@ -379,15 +480,15 @@ func (s *StateDB) DeleteSuicides() { // Reset refund so that any used-gas calculations can use // this method. s.refund = new(big.Int) - for _, stateObject := range s.stateObjects { - if stateObject.dirty { - // If the object has been removed by a suicide - // flag the object as deleted. - if stateObject.remove { - stateObject.deleted = true - } - stateObject.dirty = false + for addr, _ := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + + // If the object has been removed by a suicide + // flag the object as deleted. + if stateObject.remove { + stateObject.deleted = true } + delete(s.stateObjectsDirty, addr) } } @@ -406,46 +507,40 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { return root, batch } -func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) { +func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) { s.refund = new(big.Int) - for _, stateObject := range s.stateObjects { + // Commit objects to the trie. + for addr, stateObject := range s.stateObjects { if stateObject.remove { // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. s.DeleteStateObject(stateObject) - } else { + } else if _, ok := s.stateObjectsDirty[addr]; ok { // Write any contract code associated with the state object - if len(stateObject.code) > 0 { - if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { + if stateObject.code != nil && stateObject.dirtyCode { + if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { return common.Hash{}, err } + stateObject.dirtyCode = false } - // Write any storage changes in the state object to its trie. - stateObject.Update() - - // Commit the trie of the object to the batch. - // This updates the trie root internally, so - // getting the root hash of the storage trie - // through UpdateStateObject is fast. - if _, err := stateObject.trie.CommitTo(db); err != nil { + // Write any storage changes in the state object to its storage trie. + if err := stateObject.CommitTrie(s.db, dbw); err != nil { return common.Hash{}, err } - // Update the object in the account trie. + // Update the object in the main account trie. s.UpdateStateObject(stateObject) } - stateObject.dirty = false + delete(s.stateObjectsDirty, addr) } - return s.trie.CommitTo(db) + // Write trie changes. + root, err = s.trie.CommitTo(dbw) + if err == nil { + s.pushTrie(s.trie) + } + return root, err } func (self *StateDB) Refunds() *big.Int { return self.refund } - -// Debug stuff -func (self *StateDB) CreateOutputForDiff() { - for _, stateObject := range self.stateObjects { - stateObject.CreateOutputForDiff() - } -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_list.go b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go new file mode 100644 index 000000000..c3ddf3148 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go @@ -0,0 +1,342 @@ +// 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 ( + "container/heap" + "math" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/core/types" +) + +// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for +// retrieving sorted transactions from the possibly gapped future queue. +type nonceHeap []uint64 + +func (h nonceHeap) Len() int { return len(h) } +func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *nonceHeap) Push(x interface{}) { + *h = append(*h, x.(uint64)) +} + +func (h *nonceHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// txSortedMap is a nonce->transaction hash map with a heap based index to allow +// iterating over the contents in a nonce-incrementing way. +type txSortedMap struct { + items map[uint64]*types.Transaction // Hash map storing the transaction data + index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) + cache types.Transactions // Cache of the transactions already sorted +} + +// newTxSortedMap creates a new sorted transaction map. +func newTxSortedMap() *txSortedMap { + return &txSortedMap{ + items: make(map[uint64]*types.Transaction), + index: &nonceHeap{}, + } +} + +// Get retrieves the current transactions associated with the given nonce. +func (m *txSortedMap) Get(nonce uint64) *types.Transaction { + return m.items[nonce] +} + +// Put inserts a new transaction into the map, also updating the map's nonce +// index. If a transaction already exists with the same nonce, it's overwritten. +func (m *txSortedMap) Put(tx *types.Transaction) { + nonce := tx.Nonce() + if m.items[nonce] == nil { + heap.Push(m.index, nonce) + } + m.items[nonce], m.cache = tx, nil +} + +// Forward removes all transactions from the map with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (m *txSortedMap) Forward(threshold uint64) types.Transactions { + var removed types.Transactions + + // Pop off heap items until the threshold is reached + for m.index.Len() > 0 && (*m.index)[0] < threshold { + nonce := heap.Pop(m.index).(uint64) + removed = append(removed, m.items[nonce]) + delete(m.items, nonce) + } + // If we had a cached order, shift the front + if m.cache != nil { + m.cache = m.cache[len(removed):] + } + return removed +} + +// Filter iterates over the list of transactions and removes all of them for which +// the specified function evaluates to true. +func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { + var removed types.Transactions + + // Collect all the transactions to filter out + for nonce, tx := range m.items { + if filter(tx) { + removed = append(removed, tx) + delete(m.items, nonce) + } + } + // If transactions were removed, the heap and cache are ruined + if len(removed) > 0 { + *m.index = make([]uint64, 0, len(m.items)) + for nonce, _ := range m.items { + *m.index = append(*m.index, nonce) + } + heap.Init(m.index) + + m.cache = nil + } + return removed +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (m *txSortedMap) Cap(threshold int) types.Transactions { + // Short circuit if the number of items is under the limit + if len(m.items) <= threshold { + return nil + } + // Otherwise gather and drop the highest nonce'd transactions + var drops types.Transactions + + sort.Sort(*m.index) + for size := len(m.items); size > threshold; size-- { + drops = append(drops, m.items[(*m.index)[size-1]]) + delete(m.items, (*m.index)[size-1]) + } + *m.index = (*m.index)[:threshold] + heap.Init(m.index) + + // If we had a cache, shift the back + if m.cache != nil { + m.cache = m.cache[:len(m.cache)-len(drops)] + } + return drops +} + +// Remove deletes a transaction from the maintained map, returning whether the +// transaction was found. +func (m *txSortedMap) Remove(nonce uint64) bool { + // Short circuit if no transaction is present + _, ok := m.items[nonce] + if !ok { + return false + } + // Otherwise delete the transaction and fix the heap index + for i := 0; i < m.index.Len(); i++ { + if (*m.index)[i] == nonce { + heap.Remove(m.index, i) + break + } + } + delete(m.items, nonce) + m.cache = nil + + return true +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (m *txSortedMap) Ready(start uint64) types.Transactions { + // Short circuit if no transactions are available + if m.index.Len() == 0 || (*m.index)[0] > start { + return nil + } + // Otherwise start accumulating incremental transactions + var ready types.Transactions + for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { + ready = append(ready, m.items[next]) + delete(m.items, next) + heap.Pop(m.index) + } + m.cache = nil + + return ready +} + +// Len returns the length of the transaction map. +func (m *txSortedMap) Len() int { + return len(m.items) +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (m *txSortedMap) Flatten() types.Transactions { + // If the sorting was not cached yet, create and cache it + if m.cache == nil { + m.cache = make(types.Transactions, 0, len(m.items)) + for _, tx := range m.items { + m.cache = append(m.cache, tx) + } + sort.Sort(types.TxByNonce(m.cache)) + } + // Copy the cache to prevent accidental modifications + txs := make(types.Transactions, len(m.cache)) + copy(txs, m.cache) + return txs +} + +// txList is a "list" of transactions belonging to an account, sorted by account +// nonce. The same type can be used both for storing contiguous transactions for +// the executable/pending queue; and for storing gapped transactions for the non- +// executable/future queue, with minor behavoiral changes. +type txList struct { + strict bool // Whether nonces are strictly continuous or not + txs *txSortedMap // Heap indexed sorted hash map of the transactions + costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) +} + +// newTxList create a new transaction list for maintaining nonce-indexable fast, +// gapped, sortable transaction lists. +func newTxList(strict bool) *txList { + return &txList{ + strict: strict, + txs: newTxSortedMap(), + costcap: new(big.Int), + } +} + +// Add tries to insert a new transaction into the list, returning whether the +// transaction was accepted, and if yes, any previous transaction it replaced. +// +// If the new transaction is accepted into the list, the lists' cost threshold +// is also potentially updated. +func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) { + // If there's an older better transaction, abort + old := l.txs.Get(tx.Nonce()) + if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 { + return false, nil + } + // Otherwise overwrite the old transaction with the current one + l.txs.Put(tx) + if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { + l.costcap = cost + } + return true, old +} + +// Forward removes all transactions from the list with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (l *txList) Forward(threshold uint64) types.Transactions { + return l.txs.Forward(threshold) +} + +// Filter removes all transactions from the list with a cost higher than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. Strict-mode invalidated transactions are also returned. +// +// This method uses the cached costcap to quickly decide if there's even a point +// in calculating all the costs or if the balance covers all. If the threshold is +// lower than the costcap, the costcap will be reset to a new high after removing +// expensive the too transactions. +func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) { + // If all transactions are below the threshold, short circuit + if l.costcap.Cmp(threshold) <= 0 { + return nil, nil + } + l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold + + // Filter out all the transactions above the account's funds + removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 }) + + // If the list was strict, filter anything above the lowest nonce + var invalids types.Transactions + if l.strict && len(removed) > 0 { + lowest := uint64(math.MaxUint64) + for _, tx := range removed { + if nonce := tx.Nonce(); lowest > nonce { + lowest = nonce + } + } + invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) + } + return removed, invalids +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (l *txList) Cap(threshold int) types.Transactions { + return l.txs.Cap(threshold) +} + +// Remove deletes a transaction from the maintained list, returning whether the +// transaction was found, and also returning any transaction invalidated due to +// the deletion (strict mode only). +func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { + // Remove the transaction from the set + nonce := tx.Nonce() + if removed := l.txs.Remove(nonce); !removed { + return false, nil + } + // In strict mode, filter out non-executable transactions + if l.strict { + return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) + } + return true, nil +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (l *txList) Ready(start uint64) types.Transactions { + return l.txs.Ready(start) +} + +// Len returns the length of the transaction list. +func (l *txList) Len() int { + return l.txs.Len() +} + +// Empty returns whether the list of transactions is empty or not. +func (l *txList) Empty() bool { + return l.Len() == 0 +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (l *txList) Flatten() types.Transactions { + return l.txs.Flatten() +} 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 596356377..f8b11a7ce 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go @@ -45,8 +45,11 @@ var ( ErrNegativeValue = errors.New("Negative value") ) -const ( - maxQueued = 64 // max limit of queued txs per address +var ( + maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address + maxQueuedInTotal = uint64(65536) // Max limit of queued transactions from all accounts + maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued + evictionInterval = time.Minute // Time interval to check for evictable transactions ) type stateFn func() (*state.StateDB, error) @@ -68,10 +71,14 @@ type TxPool struct { events event.Subscription localTx *txSet mu sync.RWMutex - pending map[common.Hash]*types.Transaction // processable transactions - queue map[common.Address]map[common.Hash]*types.Transaction - wg sync.WaitGroup // for shutdown sync + pending map[common.Address]*txList // All currently processable transactions + queue map[common.Address]*txList // Queued but non-processable transactions + all map[common.Hash]*types.Transaction // All transactions to allow lookups + beats map[common.Address]time.Time // Last heartbeat from each known account + + wg sync.WaitGroup // for shutdown sync + quit chan struct{} homestead bool } @@ -79,8 +86,10 @@ type TxPool struct { func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { pool := &TxPool{ config: config, - pending: make(map[common.Hash]*types.Transaction), - queue: make(map[common.Address]map[common.Hash]*types.Transaction), + pending: make(map[common.Address]*txList), + queue: make(map[common.Address]*txList), + all: make(map[common.Hash]*types.Transaction), + beats: make(map[common.Address]time.Time), eventMux: eventMux, currentState: currentStateFn, gasLimit: gasLimitFn, @@ -88,10 +97,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat pendingState: nil, localTx: newTxSet(), events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), + quit: make(chan struct{}), } - pool.wg.Add(1) + pool.wg.Add(2) go pool.eventLoop() + go pool.expirationLoop() return pool } @@ -117,7 +128,7 @@ func (pool *TxPool) eventLoop() { pool.minGasPrice = ev.Price pool.mu.Unlock() case RemovedTransactionEvent: - pool.AddTransactions(ev.Txs) + pool.AddBatch(ev.Txs) } } } @@ -125,12 +136,12 @@ func (pool *TxPool) eventLoop() { func (pool *TxPool) resetState() { currentState, err := pool.currentState() if err != nil { - glog.V(logger.Info).Infoln("failed to get current state: %v", err) + glog.V(logger.Error).Infof("Failed to get current state: %v", err) return } managedState := state.ManageState(currentState) if err != nil { - glog.V(logger.Info).Infoln("failed to get managed state: %v", err) + glog.V(logger.Error).Infof("Failed to get managed state: %v", err) return } pool.pendingState = managedState @@ -139,26 +150,21 @@ func (pool *TxPool) resetState() { // any transactions that have been included in the block or // have been invalidated because of another transaction (e.g. // higher gas price) - pool.validatePool() + pool.demoteUnexecutables() - // Loop over the pending transactions and base the nonce of the new - // pending transaction set. - for _, tx := range pool.pending { - if addr, err := tx.From(); err == nil { - // Set the nonce. Transaction nonce can never be lower - // than the state nonce; validatePool took care of that. - if pool.pendingState.GetNonce(addr) <= tx.Nonce() { - pool.pendingState.SetNonce(addr, tx.Nonce()+1) - } - } + // Update all accounts to the latest known pending nonce + for addr, list := range pool.pending { + txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway + pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1) } // Check the queue and move transactions over to the pending if possible // or remove those that have become invalid - pool.checkQueue() + pool.promoteExecutables() } func (pool *TxPool) Stop() { pool.events.Unsubscribe() + close(pool.quit) pool.wg.Wait() glog.V(logger.Info).Infoln("Transaction pool stopped") } @@ -170,47 +176,58 @@ func (pool *TxPool) State() *state.ManagedState { return pool.pendingState } +// Stats retrieves the current pool stats, namely the number of pending and the +// number of queued (non-executable) transactions. func (pool *TxPool) Stats() (pending int, queued int) { pool.mu.RLock() defer pool.mu.RUnlock() - pending = len(pool.pending) - for _, txs := range pool.queue { - queued += len(txs) + for _, list := range pool.pending { + pending += list.Len() + } + for _, list := range pool.queue { + queued += list.Len() } return } // Content retrieves the data content of the transaction pool, returning all the -// pending as well as queued transactions, grouped by account and nonce. -func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +// pending as well as queued transactions, grouped by account and sorted by nonce. +func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { pool.mu.RLock() defer pool.mu.RUnlock() - // Retrieve all the pending transactions and sort by account and by nonce - pending := make(map[common.Address]map[uint64][]*types.Transaction) - for _, tx := range pool.pending { - account, _ := tx.From() - - owned, ok := pending[account] - if !ok { - owned = make(map[uint64][]*types.Transaction) - pending[account] = owned - } - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() } - // Retrieve all the queued transactions and sort by account and by nonce - queued := make(map[common.Address]map[uint64][]*types.Transaction) - for account, txs := range pool.queue { - owned := make(map[uint64][]*types.Transaction) - for _, tx := range txs { - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) - } - queued[account] = owned + queued := make(map[common.Address]types.Transactions) + for addr, list := range pool.queue { + queued[addr] = list.Flatten() } return pending, queued } +// Pending retrieves all currently processable transactions, groupped by origin +// account and sorted by nonce. The returned transaction set is a copy and can be +// freely modified by calling code. +func (pool *TxPool) Pending() map[common.Address]types.Transactions { + pool.mu.Lock() + defer pool.mu.Unlock() + + // check queue first + pool.promoteExecutables() + + // invalidate any txs + pool.demoteUnexecutables() + + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() + } + return pending +} + // SetLocal marks a transaction as local, skipping gas price // check against local miner minimum in the future func (pool *TxPool) SetLocal(tx *types.Transaction) { @@ -276,312 +293,348 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return nil } -// validate and queue transactions. -func (self *TxPool) add(tx *types.Transaction) error { +// add validates a transaction and inserts it into the non-executable queue for +// later pending promotion and execution. +func (pool *TxPool) add(tx *types.Transaction) error { + // If the transaction is alreayd known, discard it hash := tx.Hash() - - if self.pending[hash] != nil { - return fmt.Errorf("Known transaction (%x)", hash[:4]) + if pool.all[hash] != nil { + return fmt.Errorf("Known transaction: %x", hash[:4]) } - err := self.validateTx(tx) - if err != nil { + // Otherwise ensure basic validation passes and queue it up + if err := pool.validateTx(tx); err != nil { return err } - self.queueTx(hash, tx) + pool.enqueueTx(hash, tx) + // Print a log message if low enough level is set if glog.V(logger.Debug) { - var toname string + rcpt := "[NEW_CONTRACT]" if to := tx.To(); to != nil { - toname = common.Bytes2Hex(to[:4]) - } else { - toname = "[NEW_CONTRACT]" + rcpt = common.Bytes2Hex(to[:4]) } - // we can ignore the error here because From is - // verified in ValidateTransaction. - f, _ := tx.From() - from := common.Bytes2Hex(f[:4]) - glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) + from, _ := tx.From() // from already verified during tx validation + glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash) } - return nil } -// queueTx will queue an unknown transaction -func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { +// enqueueTx inserts a new transaction into the non-executable transaction queue. +// +// 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 - if self.queue[from] == nil { - self.queue[from] = make(map[common.Hash]*types.Transaction) + if pool.queue[from] == nil { + pool.queue[from] = newTxList(false) } - self.queue[from][hash] = tx + inserted, old := pool.queue[from].Add(tx) + if !inserted { + return // An older transaction was better, discard this + } + // Discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + } + pool.all[hash] = tx } -// addTx will add a transaction to the pending (processable queue) list of transactions -func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { - // init delayed since tx pool could have been started before any state sync +// promoteTx adds a transaction to the pending (processable) list of transactions. +// +// Note, this method assumes the pool lock is held! +func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) { + // Init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } - - if _, ok := pool.pending[hash]; !ok { - pool.pending[hash] = tx - - // Increment the nonce on the pending state. This can only happen if - // the nonce is +1 to the previous one. - pool.pendingState.SetNonce(addr, tx.Nonce()+1) - // 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 pool.eventMux.Post(TxPreEvent{tx}) + // Try to insert the transaction into the pending queue + if pool.pending[addr] == nil { + pool.pending[addr] = newTxList(true) } + list := pool.pending[addr] + + inserted, old := list.Add(tx) + if !inserted { + // An older transaction was better, discard this + delete(pool.all, hash) + return + } + // Otherwise discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + } + pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests) + + // Set the potentially new pending nonce and notify any subsystems of the new tx + pool.beats[addr] = time.Now() + pool.pendingState.SetNonce(addr, tx.Nonce()+1) + go pool.eventMux.Post(TxPreEvent{tx}) } // Add queues a single transaction in the pool if it is valid. -func (self *TxPool) Add(tx *types.Transaction) error { - self.mu.Lock() - defer self.mu.Unlock() - - if err := self.add(tx); err != nil { - return err - } - self.checkQueue() - return nil -} - -// AddTransactions attempts to queue all valid transactions in txs. -func (self *TxPool) AddTransactions(txs []*types.Transaction) { - self.mu.Lock() - defer self.mu.Unlock() - - for _, tx := range txs { - if err := self.add(tx); err != nil { - glog.V(logger.Debug).Infoln("tx error:", err) - } else { - h := tx.Hash() - glog.V(logger.Debug).Infof("tx %x\n", h[:4]) - } - } - - // check and validate the queue - self.checkQueue() -} - -// GetTransaction returns a transaction if it is contained in the pool -// and nil otherwise. -func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { - tp.mu.RLock() - defer tp.mu.RUnlock() - - // check the txs first - if tx, ok := tp.pending[hash]; ok { - return tx - } - // check queue - for _, txs := range tp.queue { - if tx, ok := txs[hash]; ok { - return tx - } - } - return nil -} - -// GetTransactions returns all currently processable transactions. -// The returned slice may be modified by the caller. -func (self *TxPool) GetTransactions() (txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() - - // check queue first - self.checkQueue() - // invalidate any txs - self.validatePool() - - txs = make(types.Transactions, len(self.pending)) - i := 0 - for _, tx := range self.pending { - txs[i] = tx - i++ - } - return txs -} - -// GetQueuedTransactions returns all non-processable transactions. -func (self *TxPool) GetQueuedTransactions() types.Transactions { - self.mu.RLock() - defer self.mu.RUnlock() - - var ret types.Transactions - for _, txs := range self.queue { - for _, tx := range txs { - ret = append(ret, tx) - } - } - sort.Sort(types.TxByNonce(ret)) - return ret -} - -// RemoveTransactions removes all given transactions from the pool. -func (self *TxPool) RemoveTransactions(txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() - for _, tx := range txs { - self.removeTx(tx.Hash()) - } -} - -// RemoveTx removes the transaction with the given hash from the pool. -func (pool *TxPool) RemoveTx(hash common.Hash) { +func (pool *TxPool) Add(tx *types.Transaction) error { pool.mu.Lock() defer pool.mu.Unlock() + + if err := pool.add(tx); err != nil { + return err + } + pool.promoteExecutables() + + return nil +} + +// AddBatch attempts to queue a batch of transactions. +func (pool *TxPool) AddBatch(txs []*types.Transaction) { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + if err := pool.add(tx); err != nil { + glog.V(logger.Debug).Infoln("tx error:", err) + } + } + pool.promoteExecutables() +} + +// Get returns a transaction if it is contained in the pool +// and nil otherwise. +func (pool *TxPool) Get(hash common.Hash) *types.Transaction { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.all[hash] +} + +// Remove removes the transaction with the given hash from the pool. +func (pool *TxPool) Remove(hash common.Hash) { + pool.mu.Lock() + defer pool.mu.Unlock() + pool.removeTx(hash) } +// RemoveBatch removes all given transactions from the pool. +func (pool *TxPool) RemoveBatch(txs types.Transactions) { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + pool.removeTx(tx.Hash()) + } +} + +// removeTx removes a single transaction from the queue, moving all subsequent +// transactions back to the future queue. func (pool *TxPool) removeTx(hash common.Hash) { - // delete from pending pool - delete(pool.pending, hash) - // delete from queue - for address, txs := range pool.queue { - if _, ok := txs[hash]; ok { - if len(txs) == 1 { - // if only one tx, remove entire address entry. - delete(pool.queue, address) + // Fetch the transaction we wish to delete + tx, ok := pool.all[hash] + if !ok { + return + } + addr, _ := tx.From() // already validated during insertion + + // Remove it from the list of known transactions + delete(pool.all, hash) + + // Remove the transaction from the pending lists and reset the account nonce + if pending := pool.pending[addr]; pending != nil { + if removed, invalids := pending.Remove(tx); removed { + // If no more transactions are left, remove the list + if pending.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) } else { - delete(txs, hash) + // Otherwise postpone any invalidated transactions + for _, tx := range invalids { + pool.enqueueTx(tx.Hash(), tx) + } } - break + // Update the account nonce if needed + if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce { + pool.pendingState.SetNonce(addr, tx.Nonce()) + } + } + } + // Transaction is in the future queue + if future := pool.queue[addr]; future != nil { + future.Remove(tx) + if future.Empty() { + delete(pool.queue, addr) } } } -// checkQueue moves transactions that have become processable to main pool. -func (pool *TxPool) checkQueue() { - // init delayed since tx pool could have been started before any state sync +// promoteExecutables moves transactions that have become processable from the +// future queue to the set of pending transactions. During this process, all +// invalidated transactions (low nonce, low balance) are deleted. +func (pool *TxPool) promoteExecutables() { + // Init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } + // Retrieve the current state to allow nonce and balance checking + state, err := pool.currentState() + if err != nil { + glog.Errorf("Could not get current state: %v", err) + return + } + // Iterate over all accounts and promote any executable transactions + queued := uint64(0) - var promote txQueue - for address, txs := range pool.queue { - currentState, err := pool.currentState() - if err != nil { - glog.Errorf("could not get current state: %v", err) - return + for addr, list := range pool.queue { + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(state.GetNonce(addr)) { + if glog.V(logger.Core) { + glog.Infof("Removed old queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) } - balance := currentState.GetBalance(address) + // Drop all transactions that are too costly (low balance) + drops, _ := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Core) { + glog.Infof("Removed unpayable queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + } + // Gather all executable transactions and promote them + for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) { + if glog.V(logger.Core) { + glog.Infof("Promoting queued transaction: %v", tx) + } + pool.promoteTx(addr, tx.Hash(), tx) + } + // Drop all transactions over the allowed limit + for _, tx := range list.Cap(int(maxQueuedPerAccount)) { + if glog.V(logger.Core) { + glog.Infof("Removed cap-exceeding queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + } + queued += uint64(list.Len()) - var ( - guessedNonce = pool.pendingState.GetNonce(address) // nonce currently kept by the tx pool (pending state) - trueNonce = currentState.GetNonce(address) // nonce known by the last state - ) - promote = promote[:0] - for hash, tx := range txs { - // Drop processed or out of fund transactions - if tx.Nonce() < trueNonce || balance.Cmp(tx.Cost()) < 0 { - if glog.V(logger.Core) { - glog.Infof("removed tx (%v) from pool queue: low tx nonce or out of funds\n", tx) + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.queue, addr) + } + } + // If we've queued more transactions than the hard limit, drop oldest ones + if queued > maxQueuedInTotal { + // Sort all accounts with queued transactions by heartbeat + addresses := make(addresssByHeartbeat, 0, len(pool.queue)) + for addr, _ := range pool.queue { + addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) + } + sort.Sort(addresses) + + // Drop transactions until the total is below the limit + for drop := queued - maxQueuedInTotal; drop > 0; { + addr := addresses[len(addresses)-1] + list := pool.queue[addr.address] + + addresses = addresses[:len(addresses)-1] + + // Drop all transactions if they are less than the overflow + if size := uint64(list.Len()); size <= drop { + for _, tx := range list.Flatten() { + pool.removeTx(tx.Hash()) } - delete(txs, hash) + drop -= size continue } - // Collect the remaining transactions for the next pass. - promote = append(promote, txQueueEntry{hash, address, tx}) - } - // Find the next consecutive nonce range starting at the current account nonce, - // pushing the guessed nonce forward if we add consecutive transactions. - sort.Sort(promote) - for i, entry := range promote { - // If we reached a gap in the nonces, enforce transaction limit and stop - if entry.Nonce() > guessedNonce { - if len(promote)-i > maxQueued { - if glog.V(logger.Debug) { - glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:])) - } - for _, drop := range promote[i+maxQueued:] { - delete(txs, drop.hash) - } - } - break + // Otherwise drop only last few transactions + txs := list.Flatten() + for i := len(txs) - 1; i >= 0 && drop > 0; i-- { + pool.removeTx(txs[i].Hash()) + drop-- } - // Otherwise promote the transaction and move the guess nonce if needed - pool.addTx(entry.hash, address, entry.Transaction) - delete(txs, entry.hash) - - if entry.Nonce() == guessedNonce { - guessedNonce++ - } - } - // Delete the entire queue entry if it became empty. - if len(txs) == 0 { - delete(pool.queue, address) } } } -// validatePool removes invalid and processed transactions from the main pool. -// If a transaction is removed for being invalid (e.g. out of funds), all sub- -// sequent (Still valid) transactions are moved back into the future queue. This -// is important to prevent a drained account from DOSing the network with non -// executable transactions. -func (pool *TxPool) validatePool() { +// demoteUnexecutables removes invalid and processed transactions from the pools +// executable/pending queue and any subsequent transactions that become unexecutable +// are moved back into the future queue. +func (pool *TxPool) demoteUnexecutables() { + // Retrieve the current state to allow nonce and balance checking state, err := pool.currentState() if err != nil { glog.V(logger.Info).Infoln("failed to get current state: %v", err) return } - balanceCache := make(map[common.Address]*big.Int) + // Iterate over all accounts and demote any non-executable transactions + for addr, list := range pool.pending { + nonce := state.GetNonce(addr) - // Clean up the pending pool, accumulating invalid nonces - gaps := make(map[common.Address]uint64) - - for hash, tx := range pool.pending { - sender, _ := tx.From() // err already checked - - // Perform light nonce and balance validation - balance := balanceCache[sender] - if balance == nil { - balance = state.GetBalance(sender) - balanceCache[sender] = balance - } - if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 { - // Remove an already past it invalidated transaction + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(nonce) { if glog.V(logger.Core) { - glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx) - } - delete(pool.pending, hash) - - // Track the smallest invalid nonce to postpone subsequent transactions - if !past { - if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev { - gaps[sender] = tx.Nonce() - } + glog.Infof("Removed old pending transaction: %v", tx) } + delete(pool.all, tx.Hash()) } - } - // Move all transactions after a gap back to the future queue - if len(gaps) > 0 { - for hash, tx := range pool.pending { - sender, _ := tx.From() - if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap { - if glog.V(logger.Core) { - glog.Infof("postponed tx (%v) due to introduced gap\n", tx) - } - pool.queueTx(hash, tx) - delete(pool.pending, hash) + // Drop all transactions that are too costly (low balance), and queue any invalids back for later + drops, invalids := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Core) { + glog.Infof("Removed unpayable pending transaction: %v", tx) } + delete(pool.all, tx.Hash()) + } + for _, tx := range invalids { + if glog.V(logger.Core) { + glog.Infof("Demoting pending transaction: %v", tx) + } + pool.enqueueTx(tx.Hash(), tx) + } + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) } } } -type txQueue []txQueueEntry +// expirationLoop is a loop that periodically iterates over all accounts with +// queued transactions and drop all that have been inactive for a prolonged amount +// of time. +func (pool *TxPool) expirationLoop() { + defer pool.wg.Done() -type txQueueEntry struct { - hash common.Hash - addr common.Address - *types.Transaction + evict := time.NewTicker(evictionInterval) + defer evict.Stop() + + for { + select { + case <-evict.C: + pool.mu.Lock() + for addr := range pool.queue { + if time.Since(pool.beats[addr]) > maxQueuedLifetime { + for _, tx := range pool.queue[addr].Flatten() { + pool.removeTx(tx.Hash()) + } + } + } + pool.mu.Unlock() + + case <-pool.quit: + return + } + } } -func (q txQueue) Len() int { return len(q) } -func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } -func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() } +// addressByHeartbeat is an account address tagged with its last activity timestamp. +type addressByHeartbeat struct { + address common.Address + heartbeat time.Time +} + +type addresssByHeartbeat []addressByHeartbeat + +func (a addresssByHeartbeat) Len() int { return len(a) } +func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) } +func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // txSet represents a set of transaction hashes in which entries // are automatically dropped after txSetDuration time 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 12fcb6cdc..9562c8b9b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/block.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block.go @@ -18,9 +18,9 @@ package types import ( - "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "io" "math/big" @@ -33,25 +33,53 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ( + EmptyRootHash = DeriveSha(Transactions{}) + EmptyUncleHash = CalcUncleHash(nil) +) + +var ( + errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header") + errMissingHeaderFields = errors.New("missing required JSON block header fields") + errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes") +) + // A BlockNonce 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 BlockNonce [8]byte +// EncodeNonce converts the given integer to a block nonce. func EncodeNonce(i uint64) BlockNonce { var n BlockNonce binary.BigEndian.PutUint64(n[:], i) return n } +// Uint64 returns the integer value of a block nonce. func (n BlockNonce) Uint64() uint64 { return binary.BigEndian.Uint64(n[:]) } +// MarshalJSON implements json.Marshaler func (n BlockNonce) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"0x%x"`, n)), nil } +// UnmarshalJSON implements json.Unmarshaler +func (n *BlockNonce) UnmarshalJSON(input []byte) error { + var b hexBytes + if err := b.UnmarshalJSON(input); err != nil { + return err + } + if len(b) != 8 { + return errBadNonceSize + } + copy((*n)[:], b) + return nil +} + +// Header represents Ethereum block headers. type Header struct { ParentHash common.Hash // Hash to the previous block UncleHash common.Hash // Uncles of this block @@ -70,6 +98,24 @@ type Header struct { Nonce BlockNonce } +type jsonHeader struct { + ParentHash *common.Hash `json:"parentHash"` + UncleHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptRoot"` + Bloom *Bloom `json:"logsBloom"` + Difficulty *hexBig `json:"difficulty"` + Number *hexBig `json:"number"` + GasLimit *hexBig `json:"gasLimit"` + GasUsed *hexBig `json:"gasUsed"` + Time *hexBig `json:"timestamp"` + Extra *hexBytes `json:"extraData"` + MixDigest *common.Hash `json:"mixHash"` + 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) } @@ -79,10 +125,13 @@ 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 { return rlpHash(h) } +// HashNoNonce returns the hash which is used as input for the proof-of-work search. func (h *Header) HashNoNonce() common.Hash { return rlpHash([]interface{}{ h.ParentHash, @@ -101,48 +150,63 @@ func (h *Header) HashNoNonce() common.Hash { }) } -func (h *Header) UnmarshalJSON(data []byte) error { - var ext struct { - ParentHash string - Coinbase string - Difficulty string - GasLimit string - Time *big.Int - Extra string - } - dec := json.NewDecoder(bytes.NewReader(data)) - if err := dec.Decode(&ext); err != nil { - return err - } - - h.ParentHash = common.HexToHash(ext.ParentHash) - h.Coinbase = common.HexToAddress(ext.Coinbase) - h.Difficulty = common.String2Big(ext.Difficulty) - h.Time = ext.Time - h.Extra = []byte(ext.Extra) - return nil +// MarshalJSON encodes headers into the web3 RPC response block format. +func (h *Header) MarshalJSON() ([]byte, error) { + return json.Marshal(&jsonHeader{ + ParentHash: &h.ParentHash, + UncleHash: &h.UncleHash, + Coinbase: &h.Coinbase, + Root: &h.Root, + TxHash: &h.TxHash, + ReceiptHash: &h.ReceiptHash, + Bloom: &h.Bloom, + Difficulty: (*hexBig)(h.Difficulty), + Number: (*hexBig)(h.Number), + GasLimit: (*hexBig)(h.GasLimit), + GasUsed: (*hexBig)(h.GasUsed), + Time: (*hexBig)(h.Time), + Extra: (*hexBytes)(&h.Extra), + MixDigest: &h.MixDigest, + Nonce: &h.Nonce, + }) } -func (h *Header) MarshalJSON() ([]byte, error) { - fields := map[string]interface{}{ - "hash": h.Hash(), - "parentHash": h.ParentHash, - "number": fmt.Sprintf("%#x", h.Number), - "nonce": h.Nonce, - "receiptRoot": h.ReceiptHash, - "logsBloom": h.Bloom, - "sha3Uncles": h.UncleHash, - "stateRoot": h.Root, - "miner": h.Coinbase, - "difficulty": fmt.Sprintf("%#x", h.Difficulty), - "extraData": fmt.Sprintf("0x%x", h.Extra), - "gasLimit": fmt.Sprintf("%#x", h.GasLimit), - "gasUsed": fmt.Sprintf("%#x", h.GasUsed), - "timestamp": fmt.Sprintf("%#x", h.Time), - "transactionsRoot": h.TxHash, +// UnmarshalJSON decodes headers from the web3 RPC response block format. +func (h *Header) UnmarshalJSON(input []byte) error { + var dec jsonHeader + if err := json.Unmarshal(input, &dec); err != nil { + return err } - - return json.Marshal(fields) + // Ensure that all fields are set. MixDigest is checked separately because + // it is a recent addition to the spec (as of August 2016) and older RPC server + // implementations might not provide it. + if dec.MixDigest == nil { + return errMissingHeaderMixDigest + } + if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil || + dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil || + dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil || + dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil || + dec.Extra == nil || dec.Nonce == nil { + return errMissingHeaderFields + } + // Assign all values. + h.ParentHash = *dec.ParentHash + h.UncleHash = *dec.UncleHash + h.Coinbase = *dec.Coinbase + h.Root = *dec.Root + h.TxHash = *dec.TxHash + h.ReceiptHash = *dec.ReceiptHash + h.Bloom = *dec.Bloom + h.Difficulty = (*big.Int)(dec.Difficulty) + h.Number = (*big.Int)(dec.Number) + h.GasLimit = (*big.Int)(dec.GasLimit) + h.GasUsed = (*big.Int)(dec.GasUsed) + h.Time = (*big.Int)(dec.Time) + h.Extra = *dec.Extra + h.MixDigest = *dec.MixDigest + h.Nonce = *dec.Nonce + return nil } func rlpHash(x interface{}) (h common.Hash) { @@ -159,6 +223,7 @@ type Body struct { Uncles []*Header } +// Block represents a block in the Ethereum blockchain. type Block struct { header *Header uncles []*Header @@ -207,11 +272,6 @@ type storageblock struct { TD *big.Int } -var ( - EmptyRootHash = DeriveSha(Transactions{}) - EmptyUncleHash = CalcUncleHash(nil) -) - // NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. @@ -284,23 +344,7 @@ func CopyHeader(h *Header) *Header { return &cpy } -func (b *Block) ValidateFields() error { - if b.header == nil { - return fmt.Errorf("header is nil") - } - for i, transaction := range b.transactions { - if transaction == nil { - return fmt.Errorf("transaction %d is nil", i) - } - } - for i, uncle := range b.uncles { - if uncle == nil { - return fmt.Errorf("uncle %d is nil", i) - } - } - return nil -} - +// DecodeRLP decodes the Ethereum func (b *Block) DecodeRLP(s *rlp.Stream) error { var eb extblock _, size, _ := s.Kind() @@ -312,6 +356,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { return nil } +// EncodeRLP serializes b into the Ethereum RLP block format. func (b *Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, extblock{ Header: b.header, @@ -331,6 +376,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { } // TODO: copies + func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } @@ -418,8 +464,8 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { return block } -// Implement pow.Block - +// Hash returns the keccak256 hash of b's header. +// The hash is computed on the first call and cached thereafter. func (b *Block) Hash() common.Hash { if hash := b.hash.Load(); hash != nil { return hash.(common.Hash) diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go index ecf2bffc2..d3945a734 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go @@ -31,28 +31,34 @@ type bytesBacked interface { const bloomLength = 256 +// Bloom represents a 256 bit bloom filter. type Bloom [bloomLength]byte +// BytesToBloom converts a byte slice to a bloom filter. +// It panics if b is not of suitable size. func BytesToBloom(b []byte) Bloom { var bloom Bloom bloom.SetBytes(b) return bloom } +// SetBytes sets the content of b to the given bytes. +// It panics if d is not of suitable size. func (b *Bloom) SetBytes(d []byte) { if len(b) < len(d) { panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) } - copy(b[bloomLength-len(d):], d) } +// Add adds d to the filter. Future calls of Test(d) will return true. func (b *Bloom) Add(d *big.Int) { bin := new(big.Int).SetBytes(b[:]) bin.Or(bin, bloom9(d.Bytes())) b.SetBytes(bin.Bytes()) } +// Big converts b to a big integer. func (b Bloom) Big() *big.Int { return common.Bytes2Big(b[:]) } @@ -69,8 +75,22 @@ func (b Bloom) TestBytes(test []byte) bool { return b.Test(common.BytesToBig(test)) } +// MarshalJSON encodes b as a hex string with 0x prefix. func (b Bloom) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%#x"`, b.Bytes())), nil + return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil +} + +// UnmarshalJSON b as a hex string with 0x prefix. +func (b *Bloom) UnmarshalJSON(input []byte) error { + var dec hexBytes + if err := dec.UnmarshalJSON(input); err != nil { + return err + } + if len(dec) != bloomLength { + return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength) + } + copy((*b)[:], dec) + return nil } func CreateBloom(receipts Receipts) Bloom { diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/json.go b/vendor/github.com/ethereum/go-ethereum/core/types/json.go new file mode 100644 index 000000000..d2718a96d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/json.go @@ -0,0 +1,108 @@ +// 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 ( + "encoding/hex" + "fmt" + "math/big" +) + +// JSON unmarshaling utilities. + +type hexBytes []byte + +func (b *hexBytes) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil + } + return nil, nil +} + +func (b *hexBytes) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + return fmt.Errorf("cannot unmarshal non-string into hexBytes") + } + input = input[1 : len(input)-1] + if len(input) < 2 || input[0] != '0' || input[1] != 'x' { + return fmt.Errorf("missing 0x prefix in hexBytes input %q", input) + } + dec := make(hexBytes, (len(input)-2)/2) + if _, err := hex.Decode(dec, input[2:]); err != nil { + return err + } + *b = dec + return nil +} + +type hexBig big.Int + +func (b *hexBig) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil + } + return nil, nil +} + +func (b *hexBig) UnmarshalJSON(input []byte) error { + raw, err := checkHexNumber(input) + if err != nil { + return err + } + dec, ok := new(big.Int).SetString(string(raw), 16) + if !ok { + return fmt.Errorf("invalid hex number") + } + *b = (hexBig)(*dec) + return nil +} + +type hexUint64 uint64 + +func (b *hexUint64) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil + } + return nil, nil +} + +func (b *hexUint64) UnmarshalJSON(input []byte) error { + raw, err := checkHexNumber(input) + if err != nil { + return err + } + _, err = fmt.Sscanf(string(raw), "%x", b) + return err +} + +func checkHexNumber(input []byte) (raw []byte, err error) { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + return nil, fmt.Errorf("cannot unmarshal non-string into hex number") + } + input = input[1 : len(input)-1] + if len(input) < 2 || input[0] != '0' || input[1] != 'x' { + return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input) + } + if len(input) == 2 { + return nil, fmt.Errorf("empty hex number") + } + raw = input[2:] + if len(raw)%2 != 0 { + raw = append([]byte{'0'}, raw...) + } + return raw, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go index 5f847fc5c..b00fdabff 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go @@ -17,6 +17,8 @@ package types import ( + "encoding/json" + "errors" "fmt" "io" "math/big" @@ -26,6 +28,11 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ( + errMissingReceiptPostState = errors.New("missing post state root in JSON receipt") + errMissingReceiptFields = errors.New("missing required JSON receipt fields") +) + // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields @@ -34,12 +41,22 @@ type Receipt struct { Bloom Bloom Logs vm.Logs - // Implementation fields + // Implementation fields (don't reorder!) TxHash common.Hash ContractAddress common.Address GasUsed *big.Int } +type jsonReceipt struct { + PostState *common.Hash `json:"root"` + CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"` + Bloom *Bloom `json:"logsBloom"` + Logs *vm.Logs `json:"logs"` + TxHash *common.Hash `json:"transactionHash"` + ContractAddress *common.Address `json:"contractAddress"` + GasUsed *hexBig `json:"gasUsed"` +} + // NewReceipt creates a barebone transaction receipt, copying the init fields. func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt { return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)} @@ -67,13 +84,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return nil } -// RlpEncode implements common.RlpEncode required for SHA3 derivation. -func (r *Receipt) RlpEncode() []byte { - bytes, err := rlp.EncodeToBytes(r) - if err != nil { - panic(err) +// MarshalJSON encodes receipts into the web3 RPC response block format. +func (r *Receipt) MarshalJSON() ([]byte, error) { + root := common.BytesToHash(r.PostState) + + return json.Marshal(&jsonReceipt{ + PostState: &root, + CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed), + Bloom: &r.Bloom, + Logs: &r.Logs, + TxHash: &r.TxHash, + ContractAddress: &r.ContractAddress, + GasUsed: (*hexBig)(r.GasUsed), + }) +} + +// UnmarshalJSON decodes the web3 RPC receipt format. +func (r *Receipt) UnmarshalJSON(input []byte) error { + var dec jsonReceipt + if err := json.Unmarshal(input, &dec); err != nil { + return err } - return bytes + // Ensure that all fields are set. PostState is checked separately because it is a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide it. Note that ContractAddress is not checked because it can be null. + if dec.PostState == nil { + return errMissingReceiptPostState + } + if dec.CumulativeGasUsed == nil || dec.Bloom == nil || + dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil { + return errMissingReceiptFields + } + *r = Receipt{ + PostState: (*dec.PostState)[:], + CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed), + Bloom: *dec.Bloom, + Logs: *dec.Logs, + TxHash: *dec.TxHash, + GasUsed: (*big.Int)(dec.GasUsed), + } + if dec.ContractAddress != nil { + r.ContractAddress = *dec.ContractAddress + } + return nil } // String implements the Stringer interface. @@ -122,7 +175,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { return nil } -// Receipts is a wrapper around a Receipt array to implement types.DerivableList. +// Receipts is a wrapper around a Receipt array to implement DerivableList. type Receipts []*Receipt // Len returns the number of receipts in this list. 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 c71c98aa7..f0512ae7e 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go @@ -19,21 +19,24 @@ package types import ( "container/heap" "crypto/ecdsa" + "encoding/json" "errors" "fmt" "io" "math/big" - "sort" "sync/atomic" "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/rlp" ) -var ErrInvalidSig = errors.New("invalid v, r, s values") +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") +) type Transaction struct { data txdata @@ -53,6 +56,20 @@ type txdata struct { R, S *big.Int // signature } +type jsonTransaction struct { + Hash *common.Hash `json:"hash"` + AccountNonce *hexUint64 `json:"nonce"` + Price *hexBig `json:"gasPrice"` + GasLimit *hexBig `json:"gas"` + Recipient *common.Address `json:"to"` + Amount *hexBig `json:"value"` + Payload *hexBytes `json:"input"` + V *hexUint64 `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) @@ -69,6 +86,7 @@ func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, 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 { if len(data) > 0 { data = common.CopyBytes(data) @@ -95,10 +113,12 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice return &Transaction{data: d} } +// DecodeRLP implements rlp.Encoder func (tx *Transaction) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &tx.data) } +// DecodeRLP implements rlp.Decoder func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { _, size, _ := s.Kind() err := s.Decode(&tx.data) @@ -108,6 +128,60 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { 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) + + return json.Marshal(&jsonTransaction{ + Hash: &hash, + AccountNonce: (*hexUint64)(&tx.data.AccountNonce), + Price: (*hexBig)(tx.data.Price), + GasLimit: (*hexBig)(tx.data.GasLimit), + Recipient: tx.data.Recipient, + Amount: (*hexBig)(tx.data.Amount), + Payload: (*hexBytes)(&tx.data.Payload), + V: (*hexUint64)(&v), + R: (*hexBig)(tx.data.R), + S: (*hexBig)(tx.data.S), + }) +} + +// UnmarshalJSON decodes the web3 RPC transaction format. +func (tx *Transaction) UnmarshalJSON(input []byte) error { + var dec jsonTransaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + // Ensure that all fields are set. V, R, S are checked separately because they're a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide them. Note that Recipient is not checked because it can be missing for + // contract creations. + 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) { + return ErrInvalidSig + } + if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil { + return errMissingTxFields + } + // Assign the fields. This is not atomic but reusing transactions + // for decoding isn't thread safe anyway. + *tx = Transaction{} + tx.data = txdata{ + AccountNonce: uint64(*dec.AccountNonce), + Recipient: dec.Recipient, + Amount: (*big.Int)(dec.Amount), + GasLimit: (*big.Int)(dec.GasLimit), + Price: (*big.Int)(dec.Price), + Payload: *dec.Payload, + V: byte(*dec.V), + R: (*big.Int)(dec.R), + S: (*big.Int)(dec.S), + } + return nil +} + func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) } func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } @@ -215,6 +289,7 @@ 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) } @@ -235,7 +310,6 @@ func (tx *Transaction) publicKey(homestead bool) ([]byte, error) { hash := tx.SigHash() pub, err := crypto.Ecrecover(hash[:], sig) if err != nil { - glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) return nil, err } if len(pub) == 0 || pub[0] != 4 { @@ -370,49 +444,58 @@ func (s *TxByPrice) Pop() interface{} { return x } -// SortByPriceAndNonce sorts the transactions by price in such a way that the -// nonce orderings within a single account are maintained. +// TransactionsByPriceAndNonce represents a set of transactions that can return +// transactions in a profit-maximising sorted order, while supporting removing +// entire batches of transactions for non-executable accounts. +type TransactionsByPriceAndNonce struct { + txs map[common.Address]Transactions // Per account nonce-sorted list of transactions + heads TxByPrice // Next transaction for each unique account (price heap) +} + +// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve +// price sorted transactions in a nonce-honouring way. // -// Note, this is not as trivial as it seems from the first look as there are three -// different criteria that need to be taken into account (price, nonce, account -// match), which cannot be done with any plain sorting method, as certain items -// cannot be compared without context. -// -// This method first sorts the separates the list of transactions into individual -// sender accounts and sorts them by nonce. After the account nonce ordering is -// satisfied, the results are merged back together by price, always comparing only -// the head transaction from each account. This is done via a heap to keep it fast. -func SortByPriceAndNonce(txs []*Transaction) { - // Separate the transactions by account and sort by nonce - byNonce := make(map[common.Address][]*Transaction) - for _, tx := range txs { - acc, _ := tx.From() // we only sort valid txs so this cannot fail - byNonce[acc] = append(byNonce[acc], tx) - } - for _, accTxs := range byNonce { - sort.Sort(TxByNonce(accTxs)) - } +// Note, the input map is reowned so the caller should not interact any more with +// if after providng it to the constructor. +func NewTransactionsByPriceAndNonce(txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { // Initialize a price based heap with the head transactions - byPrice := make(TxByPrice, 0, len(byNonce)) - for acc, accTxs := range byNonce { - byPrice = append(byPrice, accTxs[0]) - byNonce[acc] = accTxs[1:] + heads := make(TxByPrice, 0, len(txs)) + for acc, accTxs := range txs { + heads = append(heads, accTxs[0]) + txs[acc] = accTxs[1:] } - heap.Init(&byPrice) + heap.Init(&heads) - // Merge by replacing the best with the next from the same account - txs = txs[:0] - for len(byPrice) > 0 { - // Retrieve the next best transaction by price - best := heap.Pop(&byPrice).(*Transaction) - - // Push in its place the next transaction from the same account - acc, _ := best.From() // we only sort valid txs so this cannot fail - if accTxs, ok := byNonce[acc]; ok && len(accTxs) > 0 { - heap.Push(&byPrice, accTxs[0]) - byNonce[acc] = accTxs[1:] - } - // Accumulate the best priced transaction - txs = append(txs, best) + // Assemble and return the transaction set + return &TransactionsByPriceAndNonce{ + txs: txs, + heads: heads, } } + +// Peek returns the next transaction by price. +func (t *TransactionsByPriceAndNonce) Peek() *Transaction { + if len(t.heads) == 0 { + return nil + } + return t.heads[0] +} + +// 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 + + 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) + } else { + heap.Pop(&t.heads) + } +} + +// Pop removes the best transaction, *not* replacing it with the next one from +// the same account. This should be used when a transaction cannot be executed +// and hence all subsequent ones should be discarded from the same account. +func (t *TransactionsByPriceAndNonce) Pop() { + heap.Pop(&t.heads) +} 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 664887454..4bd03de7e 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go @@ -73,8 +73,6 @@ type Environment interface { DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) // Create a new contract Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) - - StructLogs() []StructLog } // Vm is the basic interface for an implementation of the EVM. @@ -96,6 +94,7 @@ type Database interface { GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) + GetCodeSize(common.Address) int GetCode(common.Address) []byte SetCode(common.Address, []byte) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go index 09feddd7d..eb2c16346 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go @@ -38,7 +38,7 @@ var ( ) // baseCheck checks for any stack error underflows -func baseCheck(op OpCode, stack *stack, gas *big.Int) error { +func baseCheck(op OpCode, stack *Stack, gas *big.Int) error { // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit // PUSH is also allowed to calculate the same price for all PUSHes // DUP requirements are handled elsewhere (except for the stack limit check) 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 e2fc5ee0f..849a8463c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -27,14 +27,14 @@ import ( type programInstruction interface { // executes the program instruction and allows the instruction to modify the state of the program - do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) + do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) // returns whether the program instruction halts the execution of the JIT halts() bool // Returns the current op code (debugging purposes) Op() OpCode } -type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) +type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) type instruction struct { op OpCode @@ -58,7 +58,7 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract return mapping[to.Uint64()], nil } -func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) { +func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // calculate the new memory size and gas price for the current executing opcode newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack) if err != nil { @@ -114,26 +114,26 @@ func (instr instruction) Op() OpCode { return instr.op } -func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *Stack) { ret.Set(instr.data) } -func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Add(x, y))) } -func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Sub(x, y))) } -func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Mul(x, y))) } -func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) != 0 { stack.push(U256(x.Div(x, y))) @@ -142,7 +142,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -162,7 +162,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -171,7 +171,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { @@ -191,12 +191,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +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))) } -func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -213,12 +213,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont } } -func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x := stack.pop() stack.push(U256(x.Not(x))) } -func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) < 0 { stack.push(big.NewInt(1)) @@ -227,7 +227,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -236,7 +236,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(S256(y)) < 0 { stack.push(big.NewInt(1)) @@ -245,7 +245,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -254,7 +254,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) == 0 { stack.push(big.NewInt(1)) @@ -263,7 +263,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x := stack.pop() if x.Cmp(common.Big0) > 0 { stack.push(new(big.Int)) @@ -272,19 +272,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) } -func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.Or(x, y)) } -func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.Xor(x, y)) } -func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) @@ -293,7 +293,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, stack.push(new(big.Int)) } } -func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { add := x.Add(x, y) @@ -303,7 +303,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract stack.push(new(big.Int)) } } -func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { mul := x.Mul(x, y) @@ -314,45 +314,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { offset, size := stack.pop(), stack.pop() hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64())) stack.push(common.BytesToBig(hash)) } -func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(common.Bytes2Big(contract.Address().Bytes())) } -func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { addr := common.BigToAddress(stack.pop()) balance := env.Db().GetBalance(addr) stack.push(new(big.Int).Set(balance)) } -func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(env.Origin().Big()) } -func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(contract.Caller().Big()) } -func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.value)) } -func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) } -func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(big.NewInt(int64(len(contract.Input)))) } -func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { var ( mOff = stack.pop() cOff = stack.pop() @@ -361,18 +361,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) } -func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { addr := common.BigToAddress(stack.pop()) - l := big.NewInt(int64(len(env.Db().GetCode(addr)))) + l := big.NewInt(int64(env.Db().GetCodeSize(addr))) stack.push(l) } -func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { l := big.NewInt(int64(len(contract.Code))) stack.push(l) } -func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { var ( mOff = stack.pop() cOff = stack.pop() @@ -383,7 +383,7 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { var ( addr = common.BigToAddress(stack.pop()) mOff = stack.pop() @@ -395,11 +395,11 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.Price)) } -func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { num := stack.pop() n := new(big.Int).Sub(env.BlockNumber(), common.Big257) @@ -410,43 +410,43 @@ func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contr } } -func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(env.Coinbase().Big()) } -func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(U256(new(big.Int).Set(env.Time()))) } -func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(U256(new(big.Int).Set(env.BlockNumber()))) } -func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(U256(new(big.Int).Set(env.Difficulty()))) } -func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(U256(new(big.Int).Set(env.GasLimit()))) } -func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.pop() } -func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(instr.data)) } -func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.dup(int(instr.data.Int64())) } -func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.swap(int(instr.data.Int64())) } -func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { n := int(instr.data.Int64()) topics := make([]common.Hash, n) mStart, mSize := stack.pop(), stack.pop() @@ -459,55 +459,55 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m env.AddLog(log) } -func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { offset := stack.pop() val := common.BigD(memory.Get(offset.Int64(), 32)) stack.push(val) } -func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { // pop value of the stack mStart, val := stack.pop(), stack.pop() memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) } -func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) } -func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { loc := common.BigToHash(stack.pop()) val := env.Db().GetState(contract.Address(), loc).Big() stack.push(val) } -func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { loc := common.BigToHash(stack.pop()) val := stack.pop() env.Db().SetState(contract.Address(), loc, common.BigToHash(val)) } -func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(instr.data)) } -func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(big.NewInt(int64(memory.Len()))) } -func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.Gas)) } -func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() @@ -529,7 +529,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -560,7 +560,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -591,7 +591,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra } } -func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) @@ -605,12 +605,12 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co } } -func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { balance := env.Db().GetBalance(contract.Address()) env.Db().AddBalance(common.BigToAddress(stack.pop()), balance) @@ -621,7 +621,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac // make log instruction function func makeLog(size int) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { @@ -636,7 +636,7 @@ func makeLog(size int) instrFn { // make push instruction function func makePush(size uint64, bsize *big.Int) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize) stack.push(common.Bytes2Big(byts)) *pc += size @@ -645,7 +645,7 @@ func makePush(size uint64, bsize *big.Int) instrFn { // make push instruction function func makeDup(size int64) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.dup(int(size)) } } @@ -654,7 +654,7 @@ func makeDup(size int64) instrFn { func makeSwap(size int64) instrFn { // switch n + 1 otherwise n would be swapped with n size += 1 - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.swap(int(size)) } } 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 e2374df42..460a68ddd 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go @@ -303,7 +303,7 @@ func RunProgram(program *Program, env Environment, contract *Contract, input []b return runProgram(program, 0, NewMemory(), newstack(), env, contract, input) } -func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, contract *Contract, input []byte) ([]byte, error) { +func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env Environment, contract *Contract, input []byte) ([]byte, error) { contract.Input = input var ( @@ -357,7 +357,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool { // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { +func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -491,7 +491,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi // jitBaseCheck is the same as baseCheck except it doesn't do the look up in the // gas table. This is done during compilation instead. -func jitBaseCheck(instr instruction, stack *stack, gas *big.Int) error { +func jitBaseCheck(instr instruction, stack *Stack, gas *big.Int) error { err := stack.require(instr.spop) if err != nil { return err diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go index e4cc6021b..b292f5f43 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go @@ -18,6 +18,7 @@ package vm import ( "encoding/json" + "errors" "fmt" "io" @@ -25,18 +26,33 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -type Log struct { - // Consensus fields - Address common.Address - Topics []common.Hash - Data []byte +var errMissingLogFields = errors.New("missing required JSON log fields") - // Derived fields (don't reorder!) - BlockNumber uint64 - TxHash common.Hash - TxIndex uint - BlockHash common.Hash - Index uint +// Log represents a contract log event. These events are generated by the LOG +// opcode and stored/indexed by the node. +type Log struct { + // Consensus fields. + Address common.Address // address of the contract that generated the event + Topics []common.Hash // list of topics provided by the contract. + Data []byte // supplied by the contract, usually ABI-encoded + + // Derived fields (don't reorder!). + BlockNumber uint64 // block in which the transaction was included + TxHash common.Hash // hash of the transaction + TxIndex uint // index of the transaction in the block + BlockHash common.Hash // hash of the block in which the transaction was included + Index uint // index of the log in the receipt +} + +type jsonLog struct { + Address *common.Address `json:"address"` + Topics *[]common.Hash `json:"topics"` + Data string `json:"data"` + BlockNumber string `json:"blockNumber"` + TxIndex string `json:"transactionIndex"` + TxHash *common.Hash `json:"transactionHash"` + BlockHash *common.Hash `json:"blockHash"` + Index string `json:"logIndex"` } func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log { @@ -64,19 +80,50 @@ func (l *Log) String() string { return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) } +// MarshalJSON implements json.Marshaler. func (r *Log) MarshalJSON() ([]byte, error) { - fields := map[string]interface{}{ - "address": r.Address, - "data": fmt.Sprintf("%#x", r.Data), - "blockNumber": fmt.Sprintf("%#x", r.BlockNumber), - "logIndex": fmt.Sprintf("%#x", r.Index), - "blockHash": r.BlockHash, - "transactionHash": r.TxHash, - "transactionIndex": fmt.Sprintf("%#x", r.TxIndex), - "topics": r.Topics, - } + return json.Marshal(&jsonLog{ + Address: &r.Address, + Topics: &r.Topics, + Data: fmt.Sprintf("0x%x", r.Data), + BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber), + TxIndex: fmt.Sprintf("0x%x", r.TxIndex), + TxHash: &r.TxHash, + BlockHash: &r.BlockHash, + Index: fmt.Sprintf("0x%x", r.Index), + }) +} - return json.Marshal(fields) +// UnmarshalJSON implements json.Umarshaler. +func (r *Log) UnmarshalJSON(input []byte) error { + var dec jsonLog + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" || + dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" { + return errMissingLogFields + } + declog := Log{ + Address: *dec.Address, + Topics: *dec.Topics, + TxHash: *dec.TxHash, + BlockHash: *dec.BlockHash, + } + if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil { + return fmt.Errorf("invalid hex log data") + } + if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil { + return fmt.Errorf("invalid hex log block number") + } + if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil { + return fmt.Errorf("invalid hex log tx index") + } + if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil { + return fmt.Errorf("invalid hex log index") + } + *r = declog + return nil } type Logs []*Log 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 cbdc8a744..ae62b6b57 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go @@ -36,19 +36,12 @@ func (self Storage) Copy() Storage { return cpy } -// StructLogCollector is the basic interface to capture emited logs by the EVM logger. -type StructLogCollector interface { - // Adds the structured log to the collector. - AddStructLog(StructLog) -} - // LogConfig are the configuration options for structured logger the EVM type LogConfig struct { - DisableMemory bool // disable memory capture - DisableStack bool // disable stack capture - DisableStorage bool // disable storage capture - FullStorage bool // show full storage (slow) - Collector StructLogCollector // the log collector + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + FullStorage bool // show full storage (slow) } // StructLog is emitted to the Environment each cycle and lists information about the current internal state @@ -65,36 +58,42 @@ type StructLog struct { Err error } -// Logger is an EVM state logger and implements VmLogger. +// Tracer is used to collect execution traces from an EVM transaction +// execution. CaptureState is called for each step of the VM with the +// current VM state. +// 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) +} + +// StructLogger is an EVM state logger and implements Tracer. // -// Logger can capture state based on the given Log configuration and also keeps +// StructLogger can capture state based on the given Log configuration and also keeps // a track record of modified storage which is used in reporting snapshots of the // contract their storage. -type Logger struct { +type StructLogger struct { cfg LogConfig - env Environment + logs []StructLog changedValues map[common.Address]Storage } -// newLogger returns a new logger -func newLogger(cfg LogConfig, env Environment) *Logger { - return &Logger{ - cfg: cfg, - env: env, +// NewLogger returns a new logger +func NewStructLogger(cfg *LogConfig) *StructLogger { + logger := &StructLogger{ changedValues: make(map[common.Address]Storage), } + if cfg != nil { + logger.cfg = *cfg + } + return logger } // 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 *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, depth int, err error) { - // short circuit if no log collector is present - if l.cfg.Collector == nil { - return - } - +func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) { // initialise new changed values storage container for this contract // if not present. if l.changedValues[contract.Address()] == nil { @@ -139,7 +138,7 @@ func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory * storage = make(Storage) // Get the contract account and loop over each storage entry. This may involve looping over // the trie and is a very expensive process. - l.env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { + env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { storage[key] = value // Return true, indicating we'd like to continue. return true @@ -150,9 +149,14 @@ func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory * } } // create a new snaptshot of the EVM. - log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, l.env.Depth(), err} - // Add the log to the collector - l.cfg.Collector.AddStructLog(log) + log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err} + + l.logs = append(l.logs, log) +} + +// StructLogs returns a list of captured log entries +func (l *StructLogger) StructLogs() []StructLog { + return l.logs } // StdErrFormat formats a slice of StructLogs to human readable format 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 d8c98e545..a4793c98f 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 @@ -39,8 +39,6 @@ type Env struct { difficulty *big.Int gasLimit *big.Int - logs []vm.StructLog - getHashFn func(uint64) common.Hash evm *vm.EVM @@ -62,23 +60,11 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { Debug: cfg.Debug, EnableJit: !cfg.DisableJit, ForceJit: !cfg.DisableJit, - - Logger: vm.LogConfig{ - Collector: env, - }, }) return env } -func (self *Env) StructLogs() []vm.StructLog { - return self.logs -} - -func (self *Env) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} - 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 } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go index c69ceddf4..648d8a04a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go @@ -24,7 +24,7 @@ type jumpSeg struct { gas *big.Int } -func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) { +func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { if !contract.UseGas(j.gas) { return nil, OutOfGasError } @@ -42,7 +42,7 @@ type pushSeg struct { gas *big.Int } -func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) { +func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error if !contract.UseGas(s.gas) { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go index 0046edec2..f6c1f76e4 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go @@ -24,58 +24,58 @@ import ( // stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly // initialised objects. -type stack struct { +type Stack struct { data []*big.Int } -func newstack() *stack { - return &stack{} +func newstack() *Stack { + return &Stack{} } -func (st *stack) Data() []*big.Int { +func (st *Stack) Data() []*big.Int { return st.data } -func (st *stack) push(d *big.Int) { +func (st *Stack) push(d *big.Int) { // NOTE push limit (1024) is checked in baseCheck //stackItem := new(big.Int).Set(d) //st.data = append(st.data, stackItem) st.data = append(st.data, d) } -func (st *stack) pushN(ds ...*big.Int) { +func (st *Stack) pushN(ds ...*big.Int) { st.data = append(st.data, ds...) } -func (st *stack) pop() (ret *big.Int) { +func (st *Stack) pop() (ret *big.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return } -func (st *stack) len() int { +func (st *Stack) len() int { return len(st.data) } -func (st *stack) swap(n int) { +func (st *Stack) swap(n int) { st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *stack) dup(n int) { +func (st *Stack) dup(n int) { st.push(new(big.Int).Set(st.data[st.len()-n])) } -func (st *stack) peek() *big.Int { +func (st *Stack) peek() *big.Int { return st.data[st.len()-1] } -func (st *stack) require(n int) error { +func (st *Stack) require(n int) error { if st.len() < n { return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n) } return nil } -func (st *stack) Print() { +func (st *Stack) Print() { fmt.Println("### stack ###") if len(st.data) > 0 { for i, val := range st.data { 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 52e782b23..9d7b55058 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go @@ -33,7 +33,7 @@ type Config struct { Debug bool EnableJit bool ForceJit bool - Logger LogConfig + Tracer Tracer } // EVM is used to run Ethereum based contracts and will utilise the @@ -44,22 +44,14 @@ type EVM struct { env Environment jumpTable vmJumpTable cfg Config - - logger *Logger } // New returns a new instance of the EVM. func New(env Environment, cfg Config) *EVM { - var logger *Logger - if cfg.Debug { - logger = newLogger(cfg.Logger, env) - } - return &EVM{ env: env, jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), cfg: cfg, - logger: logger, } } @@ -149,7 +141,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil && evm.cfg.Debug { - evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) } }() @@ -191,7 +183,7 @@ 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.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) } if opPtr := evm.jumpTable[op]; opPtr.valid { @@ -241,7 +233,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { +func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) 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 599672382..e541eaef4 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go @@ -49,7 +49,6 @@ type VMEnv struct { header *types.Header // Header information chain *BlockChain // Blockchain handle - logs []vm.StructLog // Logs for the custom structured logger getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes } @@ -63,11 +62,6 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m getHashFn: GetHashFn(header.ParentHash, chain), } - // if no log collector is present set self as the collector - if cfg.Logger.Collector == nil { - cfg.Logger.Collector = env - } - env.evm = vm.New(env, cfg) return env } @@ -121,11 +115,3 @@ func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []b func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { return Create(self, me, data, gas, price, value) } - -func (self *VMEnv) StructLogs() []vm.StructLog { - return self.logs -} - -func (self *VMEnv) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go index 3b7abb69a..c2fdbe99c 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -25,6 +25,7 @@ import ( "math/big" "os" "runtime" + "time" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/common" @@ -38,8 +39,11 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) +const defaultTraceTimeout = 5 * time.Second + // PublicEthereumAPI provides an API to access Ethereum full node-related // information. type PublicEthereumAPI struct { @@ -284,14 +288,14 @@ func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI { } // DumpBlock retrieves the entire state of the database at a given block. -func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) { +func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { - return state.World{}, fmt.Errorf("block #%d not found", number) + return state.Dump{}, fmt.Errorf("block #%d not found", number) } - stateDb, err := state.New(block.Root(), api.eth.ChainDb()) + stateDb, err := api.eth.BlockChain().StateAt(block.Root()) if err != nil { - return state.World{}, err + return state.Dump{}, err } return stateDb.RawDump(), nil } @@ -317,9 +321,16 @@ type BlockTraceResult struct { Error string `json:"error"` } +// TraceArgs holds extra parameters to trace functions +type TraceArgs struct { + *vm.LogConfig + Tracer *string + Timeout *string +} + // TraceBlock processes the given block's RLP but does not import the block in to // the chain. -func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult { var block types.Block err := rlp.Decode(bytes.NewReader(blockRlp), &block) if err != nil { @@ -336,7 +347,7 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) Block // TraceBlockFromFile loads the block's RLP from the given file name and attempts to // process it but does not import the block in to the chain. -func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult { blockRlp, err := ioutil.ReadFile(file) if err != nil { return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)} @@ -345,7 +356,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B } // TraceBlockByNumber processes the block by canonical block number. -func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { @@ -361,7 +372,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) } // TraceBlockByHash processes the block by hash. -func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByHash(hash) if block == nil { @@ -376,49 +387,38 @@ func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config } } -// TraceCollector collects EVM structered logs. -// -// TraceCollector implements vm.Collector -type TraceCollector struct { - traces []vm.StructLog -} - -// AddStructLog adds a structered log. -func (t *TraceCollector) AddStructLog(slog vm.StructLog) { - t.traces = append(t.traces, slog) -} - // traceBlock processes the given block but does not save the state. -func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) { +func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConfig) (bool, []vm.StructLog, error) { // Validate and reprocess the block var ( blockchain = api.eth.BlockChain() validator = blockchain.Validator() processor = blockchain.Processor() - collector = &TraceCollector{} ) - if config == nil { - config = new(vm.Config) + + structLogger := vm.NewStructLogger(logConfig) + + config := vm.Config{ + Debug: true, + Tracer: structLogger, } - config.Debug = true // make sure debug is set. - config.Logger.Collector = collector if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - statedb, err := state.New(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), api.eth.ChainDb()) + statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - receipts, _, usedGas, err := processor.Process(block, statedb, *config) + receipts, _, usedGas, err := processor.Process(block, statedb, config) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - return true, collector.traces, nil + return true, structLogger.StructLogs(), nil } // callmsg is the message type used for call transations. @@ -450,12 +450,43 @@ func formatError(err error) string { return err.Error() } +type timeoutError struct{} + +func (t *timeoutError) Error() string { + return "Execution time exceeded" +} + // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) { - if logger == nil { - logger = new(vm.LogConfig) +func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.Hash, config *TraceArgs) (interface{}, error) { + var tracer vm.Tracer + if config != nil && config.Tracer != nil { + timeout := defaultTraceTimeout + if config.Timeout != nil { + var err error + if timeout, err = time.ParseDuration(*config.Timeout); err != nil { + return nil, err + } + } + + var err error + if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil { + return nil, err + } + + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { + <-deadlineCtx.Done() + tracer.(*ethapi.JavascriptTracer).Stop(&timeoutError{}) + }() + defer cancel() + } else if config == nil { + tracer = vm.NewStructLogger(nil) + } else { + tracer = vm.NewStructLogger(config.LogConfig) } + // Retrieve the tx from the chain and the containing block tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash) if tx == nil { @@ -470,7 +501,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC if parent == nil { return nil, fmt.Errorf("block parent %x not found", block.ParentHash()) } - stateDb, err := state.New(parent.Root(), api.eth.ChainDb()) + stateDb, err := api.eth.BlockChain().StateAt(parent.Root()) if err != nil { return nil, err } @@ -500,16 +531,22 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC continue } // Otherwise trace the transaction and return - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger}) + vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer}) ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } - return ðapi.ExecutionResult{ - Gas: gas, - ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: ethapi.FormatLogs(vmenv.StructLogs()), - }, nil + + switch tracer := tracer.(type) { + case *vm.StructLogger: + return ðapi.ExecutionResult{ + Gas: gas, + ReturnValue: fmt.Sprintf("%x", ret), + StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + }, nil + case *ethapi.JavascriptTracer: + return tracer.GetResult() + } } return nil, errors.New("database inconsistency") } 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 aa4c75e20..58dd70d54 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -81,7 +81,7 @@ func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. if header == nil || err != nil { return nil, nil, err } - stateDb, err := state.New(header.Root, b.eth.chainDb) + stateDb, err := b.eth.BlockChain().StateAt(header.Root) return EthApiState{stateDb}, header, err } @@ -118,21 +118,25 @@ func (b *EthApiBackend) RemoveTx(txHash common.Hash) { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - b.eth.txPool.RemoveTx(txHash) + b.eth.txPool.Remove(txHash) } func (b *EthApiBackend) GetPoolTransactions() types.Transactions { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - return b.eth.txPool.GetTransactions() + var txs types.Transactions + for _, batch := range b.eth.txPool.Pending() { + txs = append(txs, batch...) + } + return txs } -func (b *EthApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { +func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - return b.eth.txPool.GetTransaction(txHash) + return b.eth.txPool.Get(hash) } func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { @@ -149,7 +153,7 @@ func (b *EthApiBackend) Stats() (pending int, queued int) { return b.eth.txPool.Stats() } -func (b *EthApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go index 38b4eb60e..3736588d1 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -74,7 +74,6 @@ type Config struct { LightServ int // Maximum percentage of time allowed for serving LES requests LightPeers int // Maximum number of LES client peers - BlockChainVersion int SkipBcVersionCheck bool // e.g. blockchain export DatabaseCache int DatabaseHandles int @@ -195,10 +194,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if !config.SkipBcVersionCheck { bcVersion := core.GetBlockChainVersion(chainDb) - if bcVersion != config.BlockChainVersion && bcVersion != 0 { - return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, config.BlockChainVersion) + if bcVersion != core.BlockChainVersion && bcVersion != 0 { + return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion) } - core.WriteBlockChainVersion(chainDb, config.BlockChainVersion) + core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) } // load the genesis block or write a new one if no genesis diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bind.go b/vendor/github.com/ethereum/go-ethereum/eth/bind.go index 26fc02860..0931c5f3b 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/bind.go @@ -19,6 +19,7 @@ package eth import ( "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -50,47 +51,62 @@ func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { } } -// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated -// with the contract from the local API, and checking its size. -func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - if ctx == nil { - ctx = context.Background() - } - block := rpc.LatestBlockNumber - if pending { - block = rpc.PendingBlockNumber - } - out, err := b.bcapi.GetCode(ctx, contract, block) - return len(common.FromHex(out)) > 0, err +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) + return common.FromHex(out), err +} + +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) + return common.FromHex(out), err } // ContractCall implements bind.ContractCaller executing an Ethereum contract // call with the specified data as the input. The pending flag requests execution // against the pending block, not the stable head of the chain. -func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - if ctx == nil { - ctx = context.Background() - } - // Convert the input args to the API spec - args := ethapi.CallArgs{ - To: &contract, - Data: common.ToHex(data), - } - block := rpc.LatestBlockNumber - if pending { - block = rpc.PendingBlockNumber - } - // Execute the call and convert the output back to Go types - out, err := b.bcapi.Call(ctx, args, block) +func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) return common.FromHex(out), err } +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) + return common.FromHex(out), err +} + +func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { + args := ethapi.CallArgs{ + To: msg.To, + From: msg.From, + Data: common.ToHex(msg.Data), + } + if msg.Gas != nil { + args.Gas = *rpc.NewHexNumber(msg.Gas) + } + if msg.GasPrice != nil { + args.GasPrice = *rpc.NewHexNumber(msg.GasPrice) + } + if msg.Value != nil { + args.Value = *rpc.NewHexNumber(msg.Value) + } + return args +} + +func toBlockNumber(num *big.Int) rpc.BlockNumber { + if num == nil { + return rpc.LatestBlockNumber + } + return rpc.BlockNumber(num.Int64()) +} + // PendingAccountNonce implements bind.ContractTransactor retrieving the current // pending nonce associated with an account. -func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { - if ctx == nil { - ctx = context.Background() - } +func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) return out.Uint64(), err } @@ -98,9 +114,6 @@ func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account commo // SuggestGasPrice implements bind.ContractTransactor retrieving the currently // suggested gas price to allow a timely execution of a transaction. func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - if ctx == nil { - ctx = context.Background() - } return b.eapi.GasPrice(ctx) } @@ -109,25 +122,14 @@ func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) // 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 (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - if ctx == nil { - ctx = context.Background() - } - out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{ - From: sender, - To: contract, - Value: *rpc.NewHexNumber(value), - Data: common.ToHex(data), - }) +func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { + out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) return out.BigInt(), err } // SendTransaction implements bind.ContractTransactor injects the transaction // into the pending pool for execution. func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - if ctx == nil { - ctx = context.Background() - } raw, _ := rlp.EncodeToBytes(tx) _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) return err diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go index c36dfb7e0..e41376810 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go @@ -19,6 +19,7 @@ package downloader import ( "sync" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" @@ -73,9 +74,10 @@ func (api *PublicDownloaderAPI) eventLoop() { var notification interface{} switch event.Data.(type) { case StartEvent: - result := &SyncingResult{Syncing: true} - result.Status.Origin, result.Status.Current, result.Status.Height, result.Status.Pulled, result.Status.Known = api.d.Progress() - notification = result + notification = &SyncingResult{ + Syncing: true, + Status: api.d.Progress(), + } case DoneEvent, FailedEvent: notification = false } @@ -117,19 +119,10 @@ func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, return rpcSub, nil } -// Progress gives progress indications when the node is synchronising with the Ethereum network. -type Progress struct { - Origin uint64 `json:"startingBlock"` - Current uint64 `json:"currentBlock"` - Height uint64 `json:"highestBlock"` - Pulled uint64 `json:"pulledStates"` - Known uint64 `json:"knownStates"` -} - // SyncingResult provides information about the current synchronisation status for this node. type SyncingResult struct { - Syncing bool `json:"syncing"` - Status Progress `json:"status"` + Syncing bool `json:"syncing"` + Status ethereum.SyncProgress `json:"status"` } // uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop. 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 d9a344da7..1952d57ab 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go @@ -28,6 +28,7 @@ import ( "sync/atomic" "time" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" @@ -211,7 +212,7 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader he // In addition, during the state download phase of fast synchronisation the number // of processed and the total number of known states are also returned. Otherwise // these are zero. -func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { +func (d *Downloader) Progress() ethereum.SyncProgress { // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks pendingStates := uint64(d.queue.PendingNodeData()) @@ -228,7 +229,13 @@ func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { case LightSync: current = d.headHeader().Number.Uint64() } - return d.syncStatsChainOrigin, current, d.syncStatsChainHeight, d.syncStatsStateDone, d.syncStatsStateDone + pendingStates + return ethereum.SyncProgress{ + StartingBlock: d.syncStatsChainOrigin, + CurrentBlock: current, + HighestBlock: d.syncStatsChainHeight, + PulledStates: d.syncStatsStateDone, + KnownStates: d.syncStatsStateDone + pendingStates, + } } // Synchronising returns whether the downloader is currently retrieving blocks. diff --git a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go index bd235bb9e..e225b4996 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go @@ -38,7 +38,7 @@ const ( maxUncleDist = 7 // Maximum allowed backward distance from the chain head maxQueueDist = 32 // Maximum allowed distance from the chain head to queue hashLimit = 256 // Maximum number of unique blocks a peer may have announced - blockLimit = 64 // Maximum number of unique blocks a per may have delivered + blockLimit = 64 // Maximum number of unique blocks a peer may have delivered ) var ( @@ -661,7 +661,7 @@ func (f *Fetcher) insert(peer string, block *types.Block) { // If the parent's unknown, abort insertion parent := f.getBlock(block.ParentHash()) if parent == nil { - glog.V(logger.Debug).Infof("Peer %s: parent []%x] of block #%d [%x…] unknown", block.ParentHash().Bytes()[:4], peer, block.NumberU64(), hash[:4]) + glog.V(logger.Debug).Infof("Peer %s: parent [%x…] of block #%d [%x…] unknown", peer, block.ParentHash().Bytes()[:4], block.NumberU64(), hash[:4]) return } // Quickly validate the header and propagate the block if it passes diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler.go b/vendor/github.com/ethereum/go-ethereum/eth/handler.go index 751408788..cad9075db 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler.go @@ -638,9 +638,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } - if err := request.Block.ValidateFields(); err != nil { - return errResp(ErrDecode, "block validation %v: %v", msg, err) - } request.Block.ReceivedAt = msg.ReceivedAt request.Block.ReceivedFrom = p @@ -684,7 +681,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkTransaction(tx.Hash()) } - pm.txpool.AddTransactions(txs) + pm.txpool.AddBatch(txs) default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go index 69b3be578..3f65c204b 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go @@ -97,12 +97,12 @@ var errorToString = map[int]string{ } type txPool interface { - // AddTransactions should add the given transactions to the pool. - AddTransactions([]*types.Transaction) + // AddBatch should add the given transactions to the pool. + AddBatch([]*types.Transaction) - // GetTransactions should return pending transactions. + // Pending should return pending transactions. // The slice should be modifiable by the caller. - GetTransactions() types.Transactions + Pending() map[common.Address]types.Transactions } // statusData is the network packet for the status message. diff --git a/vendor/github.com/ethereum/go-ethereum/eth/sync.go b/vendor/github.com/ethereum/go-ethereum/eth/sync.go index e1946edda..6584bb1e2 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/sync.go @@ -45,7 +45,10 @@ type txsync struct { // syncTransactions starts sending all currently pending transactions to the given peer. func (pm *ProtocolManager) syncTransactions(p *peer) { - txs := pm.txpool.GetTransactions() + var txs types.Transactions + for _, batch := range pm.txpool.Pending() { + txs = append(txs, batch...) + } if len(txs) == 0 { return } diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go new file mode 100644 index 000000000..aa7796f32 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go @@ -0,0 +1,415 @@ +// 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 ethclient provides a client for the Ethereum RPC API. +package ethclient + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Client defines typed wrappers for the Ethereum RPC API. +type Client struct { + c *rpc.Client +} + +// Dial connects a client to the given URL. +func Dial(rawurl string) (*Client, error) { + c, err := rpc.Dial(rawurl) + if err != nil { + return nil, err + } + return NewClient(c), nil +} + +// NewClient creates a client that uses the given RPC client. +func NewClient(c *rpc.Client) *Client { + return &Client{c} +} + +// Blockchain Access + +// BlockByHash returns the given full block. +// +// Note that loading full blocks requires two requests. Use HeaderByHash +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) +} + +// BlockByNumber returns a block from the current canonical chain. If number is nil, the +// latest known block is returned. +// +// Note that loading full blocks requires two requests. Use HeaderByNumber +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) +} + +type rpcBlock struct { + Hash common.Hash `json:"hash"` + Transactions []*types.Transaction `json:"transactions"` + UncleHashes []common.Hash `json:"uncles"` +} + +func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { + var raw json.RawMessage + err := ec.c.CallContext(ctx, &raw, method, args...) + if err != nil { + return nil, err + } + // Decode header and transactions. + var head *types.Header + var body rpcBlock + if err := json.Unmarshal(raw, &head); err != nil { + return nil, err + } + if err := json.Unmarshal(raw, &body); err != nil { + return nil, err + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. + if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { + return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles") + } + if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { + return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles") + } + if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { + return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") + } + if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { + return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") + } + // Load uncles because they are not included in the block response. + var uncles []*types.Header + if len(body.UncleHashes) > 0 { + uncles = make([]*types.Header, len(body.UncleHashes)) + reqs := make([]rpc.BatchElem, len(body.UncleHashes)) + for i := range reqs { + reqs[i] = rpc.BatchElem{ + Method: "eth_getUncleByBlockHashAndIndex", + Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)}, + Result: &uncles[i], + } + } + if err := ec.c.BatchCallContext(ctx, reqs); err != nil { + return nil, err + } + for i := range reqs { + if reqs[i].Error != nil { + return nil, reqs[i].Error + } + } + } + return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil +} + +// HeaderByHash returns the block header with the given hash. +func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + var head *types.Header + err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) + return head, err +} + +// HeaderByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + var head *types.Header + err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) + return head, err +} + +// 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) + if err == nil { + if _, r, _ := tx.SignatureValues(); r == nil { + return nil, fmt.Errorf("server returned transaction without signature") + } + } + return tx, err +} + +// TransactionCount returns the total number of transactions in the given block. +func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { + var num rpc.HexNumber + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) + return num.Uint(), err +} + +// TransactionInBlock returns a single transaction at index in the given block. +func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { + var tx *types.Transaction + err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index) + if err == nil { + if _, r, _ := tx.SignatureValues(); r == nil { + return nil, fmt.Errorf("server returned transaction without signature") + } + } + return tx, err +} + +// TransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + var r *types.Receipt + err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) + if err == nil && r != nil && len(r.PostState) == 0 { + return nil, fmt.Errorf("server returned receipt without post state") + } + return r, err +} + +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + return fmt.Sprintf("%#x", number) +} + +type rpcProgress struct { + StartingBlock rpc.HexNumber + CurrentBlock rpc.HexNumber + HighestBlock rpc.HexNumber + PulledStates rpc.HexNumber + KnownStates rpc.HexNumber +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + var raw json.RawMessage + if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil { + return nil, err + } + // Handle the possible response types + var syncing bool + if err := json.Unmarshal(raw, &syncing); err == nil { + return nil, nil // Not syncing (always false) + } + var progress *rpcProgress + if err := json.Unmarshal(raw, &progress); err != nil { + return nil, err + } + return ðereum.SyncProgress{ + StartingBlock: progress.StartingBlock.Uint64(), + CurrentBlock: progress.CurrentBlock.Uint64(), + HighestBlock: progress.HighestBlock.Uint64(), + PulledStates: progress.PulledStates.Uint64(), + KnownStates: progress.KnownStates.Uint64(), + }, nil +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "newHeads", map[string]struct{}{}) +} + +// State Access + +// BalanceAt returns the wei balance of the given account. +// The block number can be nil, in which case the balance is taken from the latest known block. +func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + var result rpc.HexNumber + err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) + return (*big.Int)(&result), err +} + +// StorageAt returns the value of key in the contract storage of the given account. +// The block number can be nil, in which case the value is taken from the latest known block. +func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + var result rpc.HexBytes + err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) + return result, err +} + +// CodeAt returns the contract code of the given account. +// The block number can be nil, in which case the code is taken from the latest known block. +func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + var result rpc.HexBytes + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) + return result, err +} + +// NonceAt returns the account nonce of the given account. +// The block number can be nil, in which case the nonce is taken from the latest known block. +func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + var result rpc.HexNumber + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) + return result.Uint64(), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]vm.Log, error) { + var result []vm.Log + err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q)) + return result, err +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- vm.Log) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q)) +} + +func toFilterArg(q ethereum.FilterQuery) interface{} { + arg := map[string]interface{}{ + "fromBlock": toBlockNumArg(q.FromBlock), + "toBlock": toBlockNumArg(q.ToBlock), + "addresses": q.Addresses, + "topics": q.Topics, + } + if q.FromBlock == nil { + arg["fromBlock"] = "0x0" + } + return arg +} + +// Pending State + +// PendingBalanceAt returns the wei balance of the given account in the pending state. +func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { + var result rpc.HexNumber + err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending") + return (*big.Int)(&result), err +} + +// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. +func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { + var result rpc.HexBytes + err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending") + return result, err +} + +// PendingCodeAt returns the contract code of the given account in the pending state. +func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + var result rpc.HexBytes + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") + return result, err +} + +// PendingNonceAt 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 *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + var result rpc.HexNumber + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") + return result.Uint64(), err +} + +// PendingTransactionCount returns the total number of transactions in the pending state. +func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { + var num rpc.HexNumber + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") + return num.Uint(), err +} + +// TODO: SubscribePendingTransactions (needs server side) + +// 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 nil, 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 *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + var hex string + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) + if err != nil { + return nil, err + } + return common.FromHex(hex), nil +} + +// PendingCallContract executes a message call transaction using the EVM. +// The state seen by the contract call is the pending state. +func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + var hex string + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") + if err != nil { + return nil, err + } + return common.FromHex(hex), nil +} + +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + var hex rpc.HexNumber + if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// 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 *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { + var hex rpc.HexNumber + err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) + if err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// 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 *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) +} + +func toCallArg(msg ethereum.CallMsg) interface{} { + arg := map[string]interface{}{ + "from": msg.From, + "to": msg.To, + } + if len(msg.Data) > 0 { + arg["data"] = fmt.Sprintf("%#x", msg.Data) + } + if msg.Value != nil { + arg["value"] = fmt.Sprintf("%#x", msg.Value) + } + if msg.Gas != nil { + arg["gas"] = fmt.Sprintf("%#x", msg.Gas) + } + if msg.GasPrice != nil { + arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice) + } + return arg +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/database.go b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go index a12ec4ceb..a0a6efbad 100644 --- a/vendor/github.com/ethereum/go-ethereum/ethdb/database.go +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" @@ -86,6 +87,7 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { OpenFilesCacheCapacity: handles, BlockCacheCapacity: cache / 2 * opt.MiB, WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally + Filter: filter.NewBloomFilter(10), }) if _, corrupted := err.(*errors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) diff --git a/vendor/github.com/ethereum/go-ethereum/interfaces.go b/vendor/github.com/ethereum/go-ethereum/interfaces.go new file mode 100644 index 000000000..aab0e2029 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/interfaces.go @@ -0,0 +1,184 @@ +// 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 ethereum defines interfaces for interacting with Ethereum. +package ethereum + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "golang.org/x/net/context" +) + +// TODO: move subscription to package event + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription interface { + // Unsubscribe cancels the sending of events to the data channel + // and closes the error channel. + Unsubscribe() + // Err returns the subscription error channel. The error channel receives + // a value if there is an issue with the subscription (e.g. the network connection + // delivering the events has been closed). Only one value will ever be sent. + // The error channel is closed by Unsubscribe. + Err() <-chan error +} + +// ChainReader provides access to the blockchain. The methods in this interface access raw +// data from either the canonical chain (when requesting by block number) or any +// blockchain fork that was previously downloaded and processed by the node. The block +// number argument can be nil to select the latest canonical block. Reading block headers +// should be preferred over full blocks whenever possible. +type ChainReader interface { + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) + TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) + TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) +} + +// ChainStateReader wraps access to the state trie of the canonical blockchain. Note that +// implementations of the interface may be unable to return state values for old blocks. +// In many cases, using CallContract can be preferable to reading raw contract storage. +type ChainStateReader interface { + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + StartingBlock uint64 // Block number where sync began + CurrentBlock uint64 // Current block number where sync is at + HighestBlock uint64 // Highest alleged block number in the chain + PulledStates uint64 // Number of state trie entries already downloaded + KnownStates uint64 // Total number os state trie entries known about +} + +// ChainSyncReader wraps access to the node's current sync status. If there's no +// sync currently running, it returns nil. +type ChainSyncReader interface { + SyncProgress(ctx context.Context) (*SyncProgress, error) +} + +// A ChainHeadEventer returns notifications whenever the canonical head block is updated. +type ChainHeadEventer interface { + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error) +} + +// CallMsg contains parameters for contract calls. +type CallMsg struct { + From common.Address // the sender of the 'transaction' + To *common.Address // the destination contract (nil for contract creation) + Gas *big.Int // if nil, the call executes with near-infinite gas + GasPrice *big.Int // wei <-> gas exchange ratio + Value *big.Int // amount of wei sent along with the call + Data []byte // input data, usually an ABI-encoded contract method invocation +} + +// A ContractCaller provides contract calls, essentially transactions that are executed by +// the EVM but not mined into the blockchain. ContractCall is a low-level method to +// execute such calls. For applications which are structured around specific contracts, +// the abigen tool provides a nicer, properly typed way to perform calls. +type ContractCaller interface { + CallContract(ctx context.Context, call CallMsg, blockNumber *big.Int) ([]byte, error) +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + FromBlock *big.Int // beginning of the queried range, nil means genesis block + ToBlock *big.Int // end of the range, nil means latest block + Addresses []common.Address // restricts matches to events created by specific contracts + + // The Topic list restricts matches to particular event topics. Each event has a list + // of topics. Topics matches a prefix of that list. An empty element slice matches any + // topic. Non-empty elements represent an alternative that matches any of the + // contained topics. + // + // Examples: + // {} or nil matches any topic list + // {{A}} matches topic A in first position + // {{}, {B}} matches any topic in first position, B in second position + // {{A}}, {B}} matches topic A in first position, B in second position + // {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position + Topics [][]common.Hash +} + +// LogFilterer provides access to contract log events using a one-off query or continuous +// event subscription. +type LogFilterer interface { + FilterLogs(ctx context.Context, q FilterQuery) ([]vm.Log, error) + SubscribeFilterLogs(ctx context.Context, q FilterQuery, ch chan<- vm.Log) (Subscription, error) +} + +// TransactionSender wraps transaction sending. The SendTransaction method injects a +// signed transaction into the pending transaction pool for execution. If the transaction +// was a contract creation, the TransactionReceipt method can be used to retrieve the +// contract address after the transaction has been mined. +// +// The transaction must be signed and have a valid nonce to be included. Consumers of the +// API can use package accounts to maintain local private keys and need can retrieve the +// next available nonce using PendingNonceAt. +type TransactionSender interface { + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +// GasPricer wraps the gas price oracle, which monitors the blockchain to determine the +// optimal gas price given current fee market conditions. +type GasPricer interface { + SuggestGasPrice(ctx context.Context) (*big.Int, error) +} + +// A PendingStateReader provides access to the pending state, which is the result of all +// known executable transactions which have not yet been included in the blockchain. It is +// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value +// transfers) initiated by the user. The PendingNonceAt operation is a good way to +// retrieve the next available transaction nonce for a specific account. +type PendingStateReader interface { + PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) + PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + PendingTransactionCount(ctx context.Context) (uint, error) +} + +// PendingContractCaller can be used to perform calls against the pending state. +type PendingContractCaller interface { + PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) +} + +// GasEstimator wraps EstimateGas, which tries to estimate the gas needed to execute a +// specific transaction based on the pending state. 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. +type GasEstimator interface { + EstimateGas(ctx context.Context, call CallMsg) (usedGas *big.Int, err error) +} + +// A PendingStateEventer provides access to real time notifications about changes to the +// pending state. +type PendingStateEventer interface { + SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error) +} 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 24b0d2482..53237f4e4 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -77,19 +77,19 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled func (s *PublicEthereumAPI) Syncing() (interface{}, error) { - origin, current, height, pulled, known := s.b.Downloader().Progress() + progress := s.b.Downloader().Progress() // Return not syncing if the synchronisation already completed - if current >= height { + if progress.CurrentBlock >= progress.HighestBlock { return false, nil } // Otherwise gather the block sync stats return map[string]interface{}{ - "startingBlock": rpc.NewHexNumber(origin), - "currentBlock": rpc.NewHexNumber(current), - "highestBlock": rpc.NewHexNumber(height), - "pulledStates": rpc.NewHexNumber(pulled), - "knownStates": rpc.NewHexNumber(known), + "startingBlock": rpc.NewHexNumber(progress.StartingBlock), + "currentBlock": rpc.NewHexNumber(progress.CurrentBlock), + "highestBlock": rpc.NewHexNumber(progress.HighestBlock), + "pulledStates": rpc.NewHexNumber(progress.PulledStates), + "knownStates": rpc.NewHexNumber(progress.KnownStates), }, nil } @@ -104,32 +104,26 @@ func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { } // Content returns the transactions contained within the transaction pool. -func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTransaction { - content := map[string]map[string]map[string][]*RPCTransaction{ - "pending": make(map[string]map[string][]*RPCTransaction), - "queued": make(map[string]map[string][]*RPCTransaction), +func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { + content := map[string]map[string]map[string]*RPCTransaction{ + "pending": make(map[string]map[string]*RPCTransaction), + "queued": make(map[string]map[string]*RPCTransaction), } pending, queue := s.b.TxPoolContent() // Flatten the pending transactions - for account, batches := range pending { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) - } + for account, txs := range pending { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) } content["pending"][account.Hex()] = dump } // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) - } + for account, txs := range queue { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) } content["queued"][account.Hex()] = dump } @@ -147,10 +141,10 @@ func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { // Inspect retrieves the content of the transaction pool and flattens it into an // easily inspectable list. -func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { - content := map[string]map[string]map[string][]string{ - "pending": make(map[string]map[string][]string), - "queued": make(map[string]map[string][]string), +func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), } pending, queue := s.b.TxPoolContent() @@ -162,24 +156,18 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) } // Flatten the pending transactions - for account, batches := range pending { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], format(tx)) - } + for account, txs := range pending { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) } content["pending"][account.Hex()] = dump } // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], format(tx)) - } + for account, txs := range queue { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) } content["queued"][account.Hex()] = dump } @@ -483,6 +471,8 @@ type CallArgs struct { } func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { + defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) + state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { return "0x", common.Big0, err @@ -601,82 +591,30 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes { return formattedStructLogs } -// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values. -func (s *PublicBlockChainAPI) TraceCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) { - state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err - } - - var addr common.Address - if args.From == (common.Address{}) { - accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { - addr = accounts[0].Address - } - } else { - addr = args.From - } - - // 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), - } - - 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) - } - - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return nil, err - } - gp := new(core.GasPool).AddGas(common.MaxBig) - ret, gas, err := core.ApplyMessage(vmenv, msg, gp) - if err := vmError(); err != nil { - return nil, err - } - return &ExecutionResult{ - Gas: gas, - ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: FormatLogs(vmenv.StructLogs()), - }, nil -} - // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { + head := b.Header() // copies the header once fields := map[string]interface{}{ - "number": rpc.NewHexNumber(b.Number()), + "number": rpc.NewHexNumber(head.Number), "hash": b.Hash(), - "parentHash": b.ParentHash(), - "nonce": b.Header().Nonce, - "sha3Uncles": b.UncleHash(), - "logsBloom": b.Bloom(), - "stateRoot": b.Root(), - "miner": b.Coinbase(), - "difficulty": rpc.NewHexNumber(b.Difficulty()), + "parentHash": head.ParentHash, + "nonce": head.Nonce, + "mixHash": head.MixDigest, + "sha3Uncles": head.UncleHash, + "logsBloom": head.Bloom, + "stateRoot": head.Root, + "miner": head.Coinbase, + "difficulty": rpc.NewHexNumber(head.Difficulty), "totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), - "extraData": fmt.Sprintf("0x%x", b.Extra()), + "extraData": rpc.HexBytes(head.Extra), "size": rpc.NewHexNumber(b.Size().Int64()), - "gasLimit": rpc.NewHexNumber(b.GasLimit()), - "gasUsed": rpc.NewHexNumber(b.GasUsed()), - "timestamp": rpc.NewHexNumber(b.Time()), - "transactionsRoot": b.TxHash(), - "receiptRoot": b.ReceiptHash(), + "gasLimit": rpc.NewHexNumber(head.GasLimit), + "gasUsed": rpc.NewHexNumber(head.GasUsed), + "timestamp": rpc.NewHexNumber(head.Time), + "transactionsRoot": head.TxHash, + "receiptRoot": head.ReceiptHash, } if inclTx { @@ -719,26 +657,32 @@ type RPCTransaction struct { Gas *rpc.HexNumber `json:"gas"` GasPrice *rpc.HexNumber `json:"gasPrice"` Hash common.Hash `json:"hash"` - Input string `json:"input"` + Input rpc.HexBytes `json:"input"` Nonce *rpc.HexNumber `json:"nonce"` To *common.Address `json:"to"` TransactionIndex *rpc.HexNumber `json:"transactionIndex"` Value *rpc.HexNumber `json:"value"` + V *rpc.HexNumber `json:"v"` + R *rpc.HexNumber `json:"r"` + S *rpc.HexNumber `json:"s"` } // 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() return &RPCTransaction{ From: from, Gas: rpc.NewHexNumber(tx.Gas()), GasPrice: rpc.NewHexNumber(tx.GasPrice()), Hash: tx.Hash(), - Input: fmt.Sprintf("0x%x", tx.Data()), + Input: rpc.HexBytes(tx.Data()), Nonce: rpc.NewHexNumber(tx.Nonce()), To: tx.To(), Value: rpc.NewHexNumber(tx.Value()), + V: rpc.NewHexNumber(v), + R: rpc.NewHexNumber(r), + S: rpc.NewHexNumber(s), } } @@ -750,7 +694,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti if err != nil { return nil, err } - + v, r, s := tx.SignatureValues() return &RPCTransaction{ BlockHash: b.Hash(), BlockNumber: rpc.NewHexNumber(b.Number()), @@ -758,11 +702,14 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti Gas: rpc.NewHexNumber(tx.Gas()), GasPrice: rpc.NewHexNumber(tx.GasPrice()), Hash: tx.Hash(), - Input: fmt.Sprintf("0x%x", tx.Data()), + Input: rpc.HexBytes(tx.Data()), Nonce: rpc.NewHexNumber(tx.Nonce()), To: tx.To(), TransactionIndex: rpc.NewHexNumber(txIndex), Value: rpc.NewHexNumber(tx.Value()), + V: rpc.NewHexNumber(v), + R: rpc.NewHexNumber(r), + S: rpc.NewHexNumber(s), }, nil } @@ -949,7 +896,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma } fields := map[string]interface{}{ - "root": common.Bytes2Hex(receipt.PostState), + "root": rpc.HexBytes(receipt.PostState), "blockHash": txBlock, "blockNumber": rpc.NewHexNumber(blockIndex), "transactionHash": txHash, @@ -960,17 +907,15 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma "cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed), "contractAddress": nil, "logs": receipt.Logs, + "logsBloom": receipt.Bloom, } - if receipt.Logs == nil { fields["logs"] = []vm.Logs{} } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { + if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } - return fields, nil } @@ -1405,8 +1350,8 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { } // SetHead rewinds the head of the blockchain to a previous block. -func (api *PrivateDebugAPI) SetHead(number uint64) { - api.b.SetHead(number) +func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { + api.b.SetHead(uint64(number.Int64())) } // PublicNetAPI offers network related RPC methods 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 8427b3e03..ec5a84c5c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -58,7 +58,7 @@ type Backend interface { GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) - TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) + TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) } 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 new file mode 100644 index 000000000..16ec6ebf0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go @@ -0,0 +1,317 @@ +// 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 ethapi + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/robertkrimen/otto" +) + +// fakeBig is used to provide an interface to Javascript for 'big.NewInt' +type fakeBig struct{} + +// NewInt creates a new big.Int with the specified int64 value. +func (fb *fakeBig) NewInt(x int64) *big.Int { + return big.NewInt(x) +} + +// OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it +// as an object, instead of a number. +type opCodeWrapper struct { + op vm.OpCode +} + +// toNumber returns the ID of this opcode as an integer +func (ocw *opCodeWrapper) toNumber() int { + return int(ocw.op) +} + +// toString returns the string representation of the opcode +func (ocw *opCodeWrapper) toString() string { + return ocw.op.String() +} + +// isPush returns true if the op is a Push +func (ocw *opCodeWrapper) isPush() bool { + return ocw.op.IsPush() +} + +// MarshalJSON serializes the opcode as JSON +func (ocw *opCodeWrapper) MarshalJSON() ([]byte, error) { + return json.Marshal(ocw.op.String()) +} + +// toValue returns an otto.Value for the opCodeWrapper +func (ocw *opCodeWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(ocw) + obj := value.Object() + obj.Set("toNumber", ocw.toNumber) + obj.Set("toString", ocw.toString) + obj.Set("isPush", ocw.isPush) + return value +} + +// memoryWrapper provides a JS wrapper around vm.Memory +type memoryWrapper struct { + memory *vm.Memory +} + +// slice returns the requested range of memory as a byte slice +func (mw *memoryWrapper) slice(begin, end int64) []byte { + return mw.memory.Get(begin, end-begin) +} + +// getUint returns the 32 bytes at the specified address interpreted +// as an unsigned integer +func (mw *memoryWrapper) getUint(addr int64) *big.Int { + ret := big.NewInt(0) + ret.SetBytes(mw.memory.GetPtr(addr, 32)) + return ret +} + +// toValue returns an otto.Value for the memoryWrapper +func (mw *memoryWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(mw) + obj := value.Object() + obj.Set("slice", mw.slice) + obj.Set("getUint", mw.getUint) + return value +} + +// stackWrapper provides a JS wrapper around vm.Stack +type stackWrapper struct { + stack *vm.Stack +} + +// peek returns the nth-from-the-top element of the stack. +func (sw *stackWrapper) peek(idx int) *big.Int { + return sw.stack.Data()[len(sw.stack.Data())-idx-1] +} + +// length returns the length of the stack +func (sw *stackWrapper) length() int { + return len(sw.stack.Data()) +} + +// toValue returns an otto.Value for the stackWrapper +func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(sw) + obj := value.Object() + obj.Set("peek", sw.peek) + obj.Set("length", sw.length) + return value +} + +// dbWrapper provides a JS wrapper around vm.Database +type dbWrapper struct { + db vm.Database +} + +// getBalance retrieves an account's balance +func (dw *dbWrapper) getBalance(addr common.Address) *big.Int { + return dw.db.GetBalance(addr) +} + +// getNonce retrieves an account's nonce +func (dw *dbWrapper) getNonce(addr common.Address) uint64 { + return dw.db.GetNonce(addr) +} + +// getCode retrieves an account's code +func (dw *dbWrapper) getCode(addr common.Address) []byte { + return dw.db.GetCode(addr) +} + +// getState retrieves an account's state data for the given hash +func (dw *dbWrapper) getState(addr common.Address, hash common.Hash) common.Hash { + return dw.db.GetState(addr, hash) +} + +// exists returns true iff the account exists +func (dw *dbWrapper) exists(addr common.Address) bool { + return dw.db.Exist(addr) +} + +// toValue returns an otto.Value for the dbWrapper +func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(dw) + obj := value.Object() + obj.Set("getBalance", dw.getBalance) + obj.Set("getNonce", dw.getNonce) + obj.Set("getCode", dw.getCode) + obj.Set("getState", dw.getState) + obj.Set("exists", dw.exists) + return 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 +} + +// NewJavascriptTracer instantiates a new JavascriptTracer instance. +// code specifies a Javascript snippet, which must evaluate to an expression +// returning an object with 'step' and 'result' functions. +func NewJavascriptTracer(code string) (*JavascriptTracer, error) { + vm := otto.New() + vm.Interrupt = make(chan func(), 1) + + // Set up builtins for this environment + vm.Set("big", &fakeBig{}) + + jstracer, err := vm.Object("(" + code + ")") + if err != nil { + return nil, err + } + + // Check the required functions exist + step, err := jstracer.Get("step") + if err != nil { + return nil, err + } + if !step.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function step()") + } + + result, err := jstracer.Get("result") + if err != nil { + return nil, err + } + if !result.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function result()") + } + + // Create the persistent log object + log := make(map[string]interface{}) + logvalue, _ := vm.ToValue(log) + + // Create persistent wrappers for memory and stack + mem := &memoryWrapper{} + stack := &stackWrapper{} + 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, + }, nil +} + +// Stop terminates execution of any JavaScript +func (jst *JavascriptTracer) Stop(err error) { + jst.vm.Interrupt <- func() { + panic(err) + } +} + +// callSafely executes a method on a JS object, catching any panics and +// returning them as error objects. +func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) { + defer func() { + if caught := recover(); caught != nil { + switch caught := caught.(type) { + case error: + err = caught + case string: + err = errors.New(caught) + case fmt.Stringer: + err = errors.New(caught.String()) + default: + panic(caught) + } + } + }() + + value, err := jst.traceobj.Call(method, argumentList...) + ret, _ = value.Export() + return ret, err +} + +func wrapError(context string, err error) error { + var message string + switch err := err.(type) { + case *otto.Error: + message = err.String() + default: + message = err.Error() + } + return fmt.Errorf("%v in server-side tracer function '%v'", message, context) +} + +// 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) { + if jst.err == nil { + jst.memory.memory = memory + jst.stack.stack = stack + jst.db.db = env.Db() + + ocw := &opCodeWrapper{op} + + jst.log["pc"] = pc + jst.log["op"] = ocw.toValue(jst.vm) + jst.log["gas"] = gas.Int64() + jst.log["gasPrice"] = cost.Int64() + jst.log["memory"] = jst.memvalue + jst.log["stack"] = jst.stackvalue + jst.log["depth"] = depth + jst.log["account"] = contract.Address() + jst.log["err"] = err + + _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) + if err != nil { + jst.err = wrapError("step", err) + } + } +} + +// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error +func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { + if jst.err != nil { + return nil, jst.err + } + + result, err = jst.callSafely("result") + if err != nil { + err = wrapError("result", err) + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go index daa19b6bd..17fae5647 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go @@ -1987,7 +1987,7 @@ var padRight = function (string, chars, sign) { }; /** - * Should be called to get utf8 from it's hex representation + * Should be called to get utf8 from its hex representation * * @method toUtf8 * @param {String} string in hex @@ -2011,7 +2011,7 @@ var toUtf8 = function(hex) { }; /** - * Should be called to get ascii from it's hex representation + * Should be called to get ascii from its hex representation * * @method toAscii * @param {String} string in hex @@ -2109,7 +2109,7 @@ var extractTypeName = function (name) { }; /** - * Converts value to it's decimal representation in string + * Converts value to its decimal representation in string * * @method toDecimal * @param {String|Number|BigNumber} @@ -2120,7 +2120,7 @@ var toDecimal = function (value) { }; /** - * Converts value to it's hex representation + * Converts value to its hex representation * * @method fromDecimal * @param {String|Number|BigNumber} @@ -2134,7 +2134,7 @@ var fromDecimal = function (value) { }; /** - * Auto converts any given value into it's hex representation. + * Auto converts any given value into its hex representation. * * And even stringifys objects before. * 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 e76e15177..04b13e483 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -18,17 +18,159 @@ package web3ext var Modules = map[string]string{ - "admin": Admin_JS, - "debug": Debug_JS, - "eth": Eth_JS, - "miner": Miner_JS, - "net": Net_JS, - "personal": Personal_JS, - "rpc": RPC_JS, - "shh": Shh_JS, - "txpool": TxPool_JS, + "admin": Admin_JS, + "bzz": Bzz_JS, + "chequebook": Chequebook_JS, + "debug": Debug_JS, + "ens": ENS_JS, + "eth": Eth_JS, + "miner": Miner_JS, + "net": Net_JS, + "personal": Personal_JS, + "rpc": RPC_JS, + "shh": Shh_JS, + "txpool": TxPool_JS, } +const Bzz_JS = ` +web3._extend({ + property: 'bzz', + methods: + [ + new web3._extend.Method({ + name: 'blockNetworkRead', + call: 'bzz_blockNetworkRead', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'syncEnabled', + call: 'bzz_syncEnabled', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'swapEnabled', + call: 'bzz_swapEnabled', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'download', + call: 'bzz_download', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'upload', + call: 'bzz_upload', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'retrieve', + call: 'bzz_retrieve', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'store', + call: 'bzz_store', + params: 2, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'get', + call: 'bzz_get', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'put', + call: 'bzz_put', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'modify', + call: 'bzz_modify', + params: 4, + inputFormatter: [null, null, null, null] + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'hive', + getter: 'bzz_hive' + }), + new web3._extend.Property({ + name: 'info', + getter: 'bzz_info', + }), + ] +}); +` + +const ENS_JS = ` +web3._extend({ + property: 'ens', + methods: + [ + new web3._extend.Method({ + name: 'register', + call: 'ens_register', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'setContentHash', + call: 'ens_setContentHash', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'resolve', + call: 'ens_resolve', + params: 1, + inputFormatter: [null] + }), + ] +}) +` + +const Chequebook_JS = ` +web3._extend({ + property: 'chequebook', + methods: + [ + new web3._extend.Method({ + name: 'deposit', + call: 'chequebook_deposit', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Property({ + name: 'balance', + getter: 'chequebook_balance', + outputFormatter: web3._extend.utils.toDecimal + }), + new web3._extend.Method({ + name: 'cash', + call: 'chequebook_cash', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'issue', + call: 'chequebook_issue', + params: 2, + inputFormatter: [null, null] + }), + ] +}); +` + const Admin_JS = ` web3._extend({ property: 'admin', @@ -284,7 +426,8 @@ web3._extend({ new web3._extend.Method({ name: 'traceTransaction', call: 'debug_traceTransaction', - params: 1 + params: 2, + inputFormatter: [null, null] }) ], properties: [] 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 f17e0cc91..d50b3ea33 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -115,7 +115,7 @@ func (b *LesApiBackend) Stats() (pending int, queued int) { return b.eth.txPool.Stats(), 0 } -func (b *LesApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { return b.eth.txPool.Content() } diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler.go b/vendor/github.com/ethereum/go-ethereum/les/handler.go index ef64e25fb..1130e6ab4 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/les/handler.go @@ -85,7 +85,7 @@ type BlockChain interface { type txPool interface { // AddTransactions should add the given transactions to the pool. - AddTransactions([]*types.Transaction) + AddBatch([]*types.Transaction) } type ProtocolManager struct { @@ -588,8 +588,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { if trie, _ := trie.New(header.Root, pm.chainDb); trie != nil { sdata := trie.Get(req.AccKey) - if so, err := state.DecodeObject(common.Address{}, pm.chainDb, sdata); err == nil { - entry := so.Code() + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + entry, _ := pm.chainDb.Get(acc.CodeHash) if bytes+len(entry) >= softResponseLimit { break } @@ -714,10 +715,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { if tr, _ := trie.New(header.Root, pm.chainDb); tr != nil { if len(req.AccKey) > 0 { - data := tr.Get(req.AccKey) + sdata := tr.Get(req.AccKey) tr = nil - if so, err := state.DecodeObject(common.Address{}, pm.chainDb, data); err == nil { - tr, _ = trie.New(common.BytesToHash(so.Root()), pm.chainDb) + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + tr, _ = trie.New(acc.Root, pm.chainDb) } } if tr != nil { @@ -826,7 +828,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if reqCnt > maxReqs || reqCnt > MaxTxSend { return errResp(ErrRequestRejected, "") } - pm.txpool.AddTransactions(txs) + pm.txpool.AddBatch(txs) _, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go index 8e45d54d3..88a25ba08 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go @@ -106,8 +106,8 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e // add trusted CHT if config.DAOForkSupport { WriteTrustedCht(bc.chainDb, TrustedCht{ - Number: 523, - Root: common.HexToHash("c9c3203ca7e58bf0ceaae50ed00c7ae234e7b3cc054aee086a1d0873f843df4e"), + Number: 564, + Root: common.HexToHash("ee31f7fc21f627dc2b8d3ed8fed5b74dbc393d146a67249a656e163148e39016"), }) } else { WriteTrustedCht(bc.chainDb, TrustedCht{ diff --git a/vendor/github.com/ethereum/go-ethereum/light/state.go b/vendor/github.com/ethereum/go-ethereum/light/state.go index 08bb8e5ba..a2faf0ca3 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state.go @@ -273,7 +273,9 @@ func (self *LightState) Copy() *LightState { state.trie = self.trie state.id = self.id for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() + if stateObject.dirty { + state.stateObjects[k] = stateObject.Copy() + } } state.refund.Set(self.refund) diff --git a/vendor/github.com/ethereum/go-ethereum/light/state_object.go b/vendor/github.com/ethereum/go-ethereum/light/state_object.go index 0eb7d68b2..9cb677487 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state_object.go @@ -79,8 +79,6 @@ type StateObject struct { codeHash []byte // The code for this account code Code - // Temporarily initialisation code - initCode Code // Cached storage (flushed when updated) storage Storage @@ -190,8 +188,7 @@ func (self *StateObject) Copy() *StateObject { stateObject.codeHash = common.CopyBytes(self.codeHash) stateObject.nonce = self.nonce stateObject.trie = self.trie - stateObject.code = common.CopyBytes(self.code) - stateObject.initCode = common.CopyBytes(self.initCode) + stateObject.code = self.code stateObject.storage = self.storage.Copy() stateObject.remove = self.remove stateObject.dirty = self.dirty diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool.go b/vendor/github.com/ethereum/go-ethereum/light/txpool.go index e94b2da95..c49d4e74a 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/txpool.go +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool.go @@ -463,7 +463,7 @@ func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error { // AddTransactions adds all valid transactions to the pool and passes them to // the tx relay backend -func (self *TxPool) AddTransactions(ctx context.Context, txs []*types.Transaction) { +func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { self.mu.Lock() defer self.mu.Unlock() var sendTx types.Transactions @@ -510,24 +510,18 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { // Content retrieves the data content of the transaction pool, returning all the // pending as well as queued transactions, grouped by account and nonce. -func (self *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { self.mu.RLock() defer self.mu.RUnlock() // Retrieve all the pending transactions and sort by account and by nonce - pending := make(map[common.Address]map[uint64][]*types.Transaction) + pending := make(map[common.Address]types.Transactions) for _, tx := range self.pending { account, _ := tx.From() - - owned, ok := pending[account] - if !ok { - owned = make(map[uint64][]*types.Transaction) - pending[account] = owned - } - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + pending[account] = append(pending[account], tx) } // There are no queued transactions in a light pool, just return an empty map - queued := make(map[common.Address]map[uint64][]*types.Transaction) + queued := make(map[common.Address]types.Transactions) return pending, queued } 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 d3c55eb82..596143795 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go @@ -57,11 +57,6 @@ func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfi } env.state = &VMState{ctx: ctx, state: state, env: env} - // if no log collector is present set self as the collector - if cfg.Logger.Collector == nil { - cfg.Logger.Collector = env - } - env.evm = vm.New(env, cfg) return env } diff --git a/vendor/github.com/ethereum/go-ethereum/miner/agent.go b/vendor/github.com/ethereum/go-ethereum/miner/agent.go index 4a4683bc6..16f2a7723 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/agent.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/agent.go @@ -43,8 +43,10 @@ type CpuAgent struct { func NewCpuAgent(index int, pow pow.PoW) *CpuAgent { miner := &CpuAgent{ - pow: pow, - index: index, + pow: pow, + index: index, + quit: make(chan struct{}), + workCh: make(chan *Work, 1), } return miner @@ -55,25 +57,15 @@ func (self *CpuAgent) Pow() pow.PoW { return self.pow } func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } func (self *CpuAgent) Stop() { - self.mu.Lock() - defer self.mu.Unlock() - close(self.quit) } func (self *CpuAgent) Start() { - self.mu.Lock() - defer self.mu.Unlock() if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) { return // agent already started } - self.quit = make(chan struct{}) - // creating current op ch makes sure we're not closing a nil ch - // later on - self.workCh = make(chan *Work, 1) - go self.update() } diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go index 59406bf4e..ac1ef5ba3 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -63,18 +63,16 @@ type uint64RingBuffer struct { // Work is the workers current environment and holds // all of the current state information type Work struct { - config *core.ChainConfig - 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) - uncles *set.Set // uncle set - remove *set.Set // tx which will be removed - tcount int // tx count in cycle - ignoredTransactors *set.Set - lowGasTransactors *set.Set - ownedAccounts *set.Set - lowGasTxs types.Transactions - localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) + config *core.ChainConfig + 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) + uncles *set.Set // uncle set + tcount int // tx count in cycle + ownedAccounts *set.Set + lowGasTxs types.Transactions + failedTxs types.Transactions + localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) Block *types.Block // the new block @@ -236,7 +234,12 @@ func (self *worker) update() { // Apply transaction to the pending state if we're not mining if atomic.LoadInt32(&self.mining) == 0 { self.currentMu.Lock() - self.current.commitTransactions(self.mux, types.Transactions{ev.Tx}, self.gasPrice, self.chain) + + acc, _ := ev.Tx.From() + txs := map[common.Address]types.Transactions{acc: types.Transactions{ev.Tx}} + txset := types.NewTransactionsByPriceAndNonce(txs) + + self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain) self.currentMu.Unlock() } } @@ -358,7 +361,7 @@ func (self *worker) push(work *Work) { // makeCurrent creates a new environment for the current cycle. func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { - state, err := state.New(parent.Root(), self.eth.ChainDb()) + state, err := self.chain.StateAt(parent.Root()) if err != nil { return err } @@ -383,10 +386,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error accounts := self.eth.AccountManager().Accounts() // Keep track of transactions which return errors so they can be removed - work.remove = set.New() work.tcount = 0 - work.ignoredTransactors = set.New() - work.lowGasTransactors = set.New() work.ownedAccounts = accountAddressesSet(accounts) if self.current != nil { work.localMinedBlocks = self.current.localMinedBlocks @@ -495,45 +495,11 @@ func (self *worker) commitNewWork() { if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { core.ApplyDAOHardFork(work.state) } + txs := types.NewTransactionsByPriceAndNonce(self.eth.TxPool().Pending()) + work.commitTransactions(self.mux, txs, self.gasPrice, self.chain) - /* //approach 1 - transactions := self.eth.TxPool().GetTransactions() - sort.Sort(types.TxByNonce(transactions)) - */ - - //approach 2 - transactions := self.eth.TxPool().GetTransactions() - types.SortByPriceAndNonce(transactions) - - /* // approach 3 - // commit transactions for this run. - txPerOwner := make(map[common.Address]types.Transactions) - // Sort transactions by owner - for _, tx := range self.eth.TxPool().GetTransactions() { - from, _ := tx.From() // we can ignore the sender error - txPerOwner[from] = append(txPerOwner[from], tx) - } - var ( - singleTxOwner types.Transactions - multiTxOwner types.Transactions - ) - // Categorise transactions by - // 1. 1 owner tx per block - // 2. multi txs owner per block - for _, txs := range txPerOwner { - if len(txs) == 1 { - singleTxOwner = append(singleTxOwner, txs[0]) - } else { - multiTxOwner = append(multiTxOwner, txs...) - } - } - sort.Sort(types.TxByPrice(singleTxOwner)) - sort.Sort(types.TxByNonce(multiTxOwner)) - transactions := append(singleTxOwner, multiTxOwner...) - */ - - work.commitTransactions(self.mux, transactions, self.gasPrice, self.chain) - self.eth.TxPool().RemoveTransactions(work.lowGasTxs) + self.eth.TxPool().RemoveBatch(work.lowGasTxs) + self.eth.TxPool().RemoveBatch(work.failedTxs) // compute uncles for the new block. var ( @@ -591,65 +557,52 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { return nil } -func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { +func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) { gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs vm.Logs - for _, tx := range transactions { + + for { + // Retrieve the next transaction and abort if all done + tx := txs.Peek() + if tx == nil { + break + } // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := tx.From() - // Check if it falls within margin. Txs from owned accounts are always processed. + // Ignore any transactions (and accounts subsequently) with low gas limits if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { - // ignore the transaction and transactor. We ignore the transactor - // because nonce will fail after ignoring this transaction so there's - // no point - env.lowGasTransactors.Add(from) + // Pop the current low-priced transaction without shifting in the next from the account + glog.V(logger.Info).Infof("Transaction (%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) - glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) - } + env.lowGasTxs = append(env.lowGasTxs, tx) + txs.Pop() - // Continue with the next transaction if the transaction sender is included in - // the low gas tx set. This will also remove the tx and all sequential transaction - // from this transactor - if env.lowGasTransactors.Has(from) { - // add tx to the low gas set. This will be removed at the end of the run - // owned accounts are ignored - if !env.ownedAccounts.Has(from) { - env.lowGasTxs = append(env.lowGasTxs, tx) - } continue } - - // Move on to the next transaction when the transactor is in ignored transactions set - // This may occur when a transaction hits the gas limit. When a gas limit is hit and - // the transaction is processed (that could potentially be included in the block) it - // will throw a nonce error because the previous transaction hasn't been processed. - // Therefor we need to ignore any transaction after the ignored one. - if env.ignoredTransactors.Has(from) { - continue - } - - env.state.StartRecord(tx.Hash(), common.Hash{}, 0) + // Start executing the transaction + env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) err, logs := env.commitTransaction(tx, bc, gp) switch { case core.IsGasLimitErr(err): - // ignore the transactor so no nonce errors will be thrown for this account - // next time the worker is run, they'll be picked up again. - env.ignoredTransactors.Add(from) - + // Pop the current out-of-gas transaction without shifting in the next from the account glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) - case err != nil: - env.remove.Add(tx.Hash()) + txs.Pop() + + case err != nil: + // Pop the current failed transaction without shifting in the next from the account + glog.V(logger.Detail).Infof("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) + env.failedTxs = append(env.failedTxs, tx) + txs.Pop() - if glog.V(logger.Detail) { - glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) - } default: - env.tcount++ + // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + txs.Shift() } } if len(coalescedLogs) > 0 || env.tcount > 0 { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go index 505a1fc77..377dc406b 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go @@ -105,7 +105,7 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na glog.V(logger.Debug).Infof("deleting port mapping: %s %d -> %d (%s) using %s\n", protocol, extport, intport, name, m) m.DeleteMapping(protocol, extport, intport) }() - if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) } else { glog.V(logger.Info).Infof("mapped network port %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) @@ -118,7 +118,7 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na } case <-refresh.C: glog.V(logger.Detail).Infof("refresh port mapping %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) - if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) } refresh.Reset(mapUpdateInterval) diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/types.go b/vendor/github.com/ethereum/go-ethereum/rpc/types.go index e313bcda0..f435b0eca 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/types.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/types.go @@ -17,6 +17,8 @@ package rpc import ( + "bytes" + "encoding/hex" "fmt" "math" "math/big" @@ -273,6 +275,34 @@ func (bn BlockNumber) Int64() int64 { return (int64)(bn) } +// HexBytes JSON-encodes as hex with 0x prefix. +type HexBytes []byte + +func (b HexBytes) MarshalJSON() ([]byte, error) { + result := make([]byte, len(b)*2+4) + copy(result, `"0x`) + hex.Encode(result[3:], b) + result[len(result)-1] = '"' + return result, nil +} + +func (b *HexBytes) UnmarshalJSON(input []byte) error { + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + if !bytes.HasPrefix(input, []byte("0x")) { + return fmt.Errorf("missing 0x prefix for hex byte array") + } + input = input[2:] + if len(input) == 0 { + *b = nil + return nil + } + *b = make([]byte, len(input)/2) + _, err := hex.Decode(*b, input) + return err +} + type ClientRestartWrapper struct { client *Client newClientFn func() *Client @@ -300,3 +330,4 @@ func (rw *ClientRestartWrapper) Restart() { rw.client.Close() rw.client = rw.newClientFn() } + diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/utils.go b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go index b590ba62f..c249e9b4a 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/utils.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go @@ -24,6 +24,7 @@ import ( "math/big" "math/rand" "reflect" + "strings" "sync" "time" "unicode" @@ -250,5 +251,13 @@ func NewID() ID { val >>= 8 } } - return ID("0x" + hex.EncodeToString(id)) + + rpcId := hex.EncodeToString(id) + // rpc ID's are RPC quantities, no leading zero's and 0 is 0x0 + rpcId = strings.TrimLeft(rpcId, "0") + if rpcId == "" { + rpcId = "0" + } + + return ID("0x" + rpcId) } diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go new file mode 100644 index 000000000..673cff350 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.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 . + +package api + +import ( + "fmt" + "io" + "regexp" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +var ( + hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") + slashes = regexp.MustCompile("/+") + domainAndVersion = regexp.MustCompile("[@:;,]+") +) + +type Resolver interface { + Resolve(string) (common.Hash, error) +} + +/* +Api implements webserver/file system related content storage and retrieval +on top of the dpa +it is the public interface of the dpa which is included in the ethereum stack +*/ +type Api struct { + dpa *storage.DPA + dns Resolver +} + +//the api constructor initialises +func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { + self = &Api{ + dpa: dpa, + dns: dns, + } + return +} + +// DPA reader API +func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { + return self.dpa.Retrieve(key) +} + +func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { + return self.dpa.Store(data, size, wg, nil) +} + +type ErrResolve error + +// DNS Resolver +func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { + if hashMatcher.MatchString(hostPort) || self.dns == nil { + glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort) + return storage.Key(common.Hex2Bytes(hostPort)), nil + } + if !nameresolver { + return nil, fmt.Errorf("'%s' is not a content hash value.", hostPort) + } + contentHash, err := self.dns.Resolve(hostPort) + if err != nil { + err = ErrResolve(err) + glog.V(logger.Warn).Infof("DNS error : %v", err) + } + glog.V(logger.Detail).Infof("host lookup: %v -> %v", err) + return contentHash[:], err +} + +func parse(uri string) (hostPort, path string) { + parts := slashes.Split(uri, 3) + var i int + if len(parts) == 0 { + return + } + // beginning with slash is now optional + for len(parts[i]) == 0 { + i++ + } + hostPort = parts[i] + for i < len(parts)-1 { + i++ + if len(path) > 0 { + path = path + "/" + parts[i] + } else { + path = parts[i] + } + } + glog.V(logger.Debug).Infof("host: '%s', path '%s' requested.", hostPort, path) + return +} + +func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { + hostPort, path = parse(uri) + //resolving host and port + contentHash, err := self.Resolve(hostPort, nameresolver) + glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path) + return contentHash[:], hostPort, path, err +} + +// Put provides singleton manifest creation on top of dpa store +func (self *Api) Put(content, contentType string) (string, error) { + r := strings.NewReader(content) + wg := &sync.WaitGroup{} + key, err := self.dpa.Store(r, int64(len(content)), wg, nil) + if err != nil { + return "", err + } + manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) + r = strings.NewReader(manifest) + key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) + if err != nil { + return "", err + } + wg.Wait() + return key.String(), nil +} + +// Get uses iterative manifest retrieval and prefix matching +// to resolve path to content using dpa retrieve +// it returns a section reader, mimeType, status and an error +func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionReader, mimeType string, status int, err error) { + + key, _, path, err := self.parseAndResolve(uri, nameresolver) + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("loadManifestTrie error: %v", err) + return + } + + glog.V(logger.Detail).Infof("getEntry(%s)", path) + entry, _ := trie.getEntry(path) + if entry != nil { + key = common.Hex2Bytes(entry.Hash) + status = entry.Status + mimeType = entry.ContentType + glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType) + reader = self.dpa.Retrieve(key) + } else { + err = fmt.Errorf("manifest entry for '%s' not found", path) + glog.V(logger.Warn).Infof("%v", err) + } + return +} + +func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) { + root, _, path, err := self.parseAndResolve(uri, nameresolver) + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, root, quitC) + if err != nil { + return + } + + if contentHash != "" { + entry := &manifestTrieEntry{ + Path: path, + Hash: contentHash, + ContentType: contentType, + } + trie.addEntry(entry, quitC) + } else { + trie.deleteEntry(path, quitC) + } + + err = trie.recalcAndStore() + if err != nil { + return + } + return trie.hash.String(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go new file mode 100644 index 000000000..730755c43 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go @@ -0,0 +1,132 @@ +// 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 api + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + port = "8500" +) + +// by default ens root is north internal +var ( + toyNetEnsRoot = common.HexToAddress("0xd344889e0be3e9ef6c26b0f60ef66a32e83c1b69") +) + +// separate bzz directories +// allow several bzz nodes running in parallel +type Config struct { + // serialised/persisted fields + *storage.StoreParams + *storage.ChunkerParams + *network.HiveParams + Swap *swap.SwapParams + *network.SyncParams + Path string + Port string + PublicKey string + BzzKey string + EnsRoot common.Address +} + +// 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())) + err = os.MkdirAll(dirpath, os.ModePerm) + if err != nil { + return + } + confpath := filepath.Join(dirpath, "config.json") + var data []byte + pubkey := crypto.FromECDSAPub(&prvKey.PublicKey) + pubkeyhex := common.ToHex(pubkey) + keyhex := crypto.Sha3Hash(pubkey).Hex() + + self = &Config{ + SyncParams: network.NewSyncParams(dirpath), + HiveParams: network.NewHiveParams(dirpath), + ChunkerParams: storage.NewChunkerParams(), + StoreParams: storage.NewStoreParams(dirpath), + Port: port, + Path: dirpath, + Swap: swap.DefaultSwapParams(contract, prvKey), + PublicKey: pubkeyhex, + BzzKey: keyhex, + EnsRoot: toyNetEnsRoot, + } + data, err = ioutil.ReadFile(confpath) + if err != nil { + if !os.IsNotExist(err) { + return + } + // file does not exist + // write out config file + err = self.Save() + if err != nil { + err = fmt.Errorf("error writing config: %v", err) + } + return + } + // file exists, deserialise + err = json.Unmarshal(data, self) + if err != nil { + return nil, fmt.Errorf("unable to parse config: %v", err) + } + // check public key + if pubkeyhex != self.PublicKey { + return nil, fmt.Errorf("public key does not match the one in the config file %v != %v", pubkeyhex, self.PublicKey) + } + if keyhex != self.BzzKey { + return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey) + } + self.Swap.SetKey(prvKey) + + if (self.EnsRoot == common.Address{}) { + self.EnsRoot = toyNetEnsRoot + } + + return +} + +func (self *Config) Save() error { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = os.MkdirAll(self.Path, os.ModePerm) + if err != nil { + return err + } + confpath := filepath.Join(self.Path, "config.json") + return ioutil.WriteFile(confpath, data, os.ModePerm) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go new file mode 100644 index 000000000..428f3e3ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go @@ -0,0 +1,283 @@ +// 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 api + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const maxParallelFiles = 5 + +type FileSystem struct { + api *Api +} + +func NewFileSystem(api *Api) *FileSystem { + return &FileSystem{api} +} + +// Upload replicates a local directory as a manifest file and uploads it +// using dpa store +// TODO: localpath should point to a manifest +func (self *FileSystem) Upload(lpath, index string) (string, error) { + var list []*manifestTrieEntry + localpath, err := filepath.Abs(filepath.Clean(lpath)) + if err != nil { + return "", err + } + + f, err := os.Open(localpath) + if err != nil { + return "", err + } + stat, err := f.Stat() + if err != nil { + return "", err + } + + var start int + if stat.IsDir() { + start = len(localpath) + glog.V(logger.Debug).Infof("uploading '%s'", localpath) + err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { + if (err == nil) && !info.IsDir() { + //fmt.Printf("lp %s path %s\n", localpath, path) + if len(path) <= start { + return fmt.Errorf("Path is too short") + } + if path[:start] != localpath { + return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(path), + } + list = append(list, entry) + } + return err + }) + if err != nil { + return "", err + } + } else { + dir := filepath.Dir(localpath) + start = len(dir) + if len(localpath) <= start { + return "", fmt.Errorf("Path is too short") + } + if localpath[:start] != dir { + return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(localpath), + } + list = append(list, entry) + } + + cnt := len(list) + errors := make([]error, cnt) + done := make(chan bool, maxParallelFiles) + dcnt := 0 + awg := &sync.WaitGroup{} + + for i, entry := range list { + if i >= dcnt+maxParallelFiles { + <-done + dcnt++ + } + awg.Add(1) + go func(i int, entry *manifestTrieEntry, done chan bool) { + f, err := os.Open(entry.Path) + if err == nil { + stat, _ := f.Stat() + var hash storage.Key + wg := &sync.WaitGroup{} + hash, err = self.api.dpa.Store(f, stat.Size(), wg, nil) + if hash != nil { + list[i].Hash = hash.String() + } + wg.Wait() + awg.Done() + if err == nil { + first512 := make([]byte, 512) + fread, _ := f.ReadAt(first512, 0) + if fread > 0 { + mimeType := http.DetectContentType(first512[:fread]) + if filepath.Ext(entry.Path) == ".css" { + mimeType = "text/css" + } + list[i].ContentType = mimeType + } + } + f.Close() + } + errors[i] = err + done <- true + }(i, entry, done) + } + for dcnt < cnt { + <-done + dcnt++ + } + + trie := &manifestTrie{ + dpa: self.api.dpa, + } + quitC := make(chan bool) + for i, entry := range list { + if errors[i] != nil { + return "", errors[i] + } + entry.Path = RegularSlashes(entry.Path[start:]) + if entry.Path == index { + ientry := &manifestTrieEntry{ + Path: "", + Hash: entry.Hash, + ContentType: entry.ContentType, + } + trie.addEntry(ientry, quitC) + } + trie.addEntry(entry, quitC) + } + + err2 := trie.recalcAndStore() + var hs string + if err2 == nil { + hs = trie.hash.String() + } + awg.Wait() + return hs, err2 +} + +// Download replicates the manifest path structure on the local filesystem +// under localpath +func (self *FileSystem) Download(bzzpath, localpath string) error { + lpath, err := filepath.Abs(filepath.Clean(localpath)) + if err != nil { + return err + } + err = os.MkdirAll(lpath, os.ModePerm) + if err != nil { + return err + } + + //resolving host and port + key, _, path, err := self.api.parseAndResolve(bzzpath, true) + if err != nil { + return err + } + + if len(path) > 0 { + path += "/" + } + + quitC := make(chan bool) + trie, err := loadManifest(self.api.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("fs.Download: loadManifestTrie error: %v", err) + return err + } + + type downloadListEntry struct { + key storage.Key + path string + } + + var list []*downloadListEntry + var mde error + + prevPath := lpath + err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { + glog.V(logger.Detail).Infof("fs.Download: %#v", entry) + + key = common.Hex2Bytes(entry.Hash) + path := lpath + "/" + suffix + dir := filepath.Dir(path) + if dir != prevPath { + mde = os.MkdirAll(dir, os.ModePerm) + prevPath = dir + } + if (mde == nil) && (path != dir+"/") { + list = append(list, &downloadListEntry{key: key, path: path}) + } + }) + if err != nil { + return err + } + + wg := sync.WaitGroup{} + errC := make(chan error) + done := make(chan bool, maxParallelFiles) + for i, entry := range list { + select { + case done <- true: + wg.Add(1) + case <-quitC: + return fmt.Errorf("aborted") + } + go func(i int, entry *downloadListEntry) { + defer wg.Done() + f, err := os.Create(entry.path) // TODO: path separators + if err == nil { + + reader := self.api.dpa.Retrieve(entry.key) + writer := bufio.NewWriter(f) + size, err := reader.Size(quitC) + if err == nil { + _, err = io.CopyN(writer, reader, size) // TODO: handle errors + err2 := writer.Flush() + if err == nil { + err = err2 + } + err2 = f.Close() + if err == nil { + err = err2 + } + } + } + if err != nil { + select { + case errC <- err: + case <-quitC: + } + return + } + <-done + }(i, entry) + } + go func() { + wg.Wait() + close(errC) + }() + select { + case err = <-errC: + return err + case <-quitC: + return fmt.Errorf("aborted") + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go new file mode 100644 index 000000000..a3a644b73 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go @@ -0,0 +1,69 @@ +// 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 http + +import ( + "fmt" + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +http roundtripper to register for bzz url scheme +see https://github.com/ethereum/go-ethereum/issues/2040 +Usage: + +import ( + "github.com/ethereum/go-ethereum/common/httpclient" + "github.com/ethereum/go-ethereum/swarm/api/http" +) +client := httpclient.New() +// for (private) swarm proxy running locally +client.RegisterScheme("bzz", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzi", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzr", &http.RoundTripper{Port: port}) + +The port you give the Roundtripper is the port the swarm proxy is listening on. +If Host is left empty, localhost is assumed. + +Using a public gateway, the above few lines gives you the leanest +bzz-scheme aware read-only http client. You really only ever need this +if you need go-native swarm access to bzz addresses, e.g., +github.com/ethereum/go-ethereum/common/natspec + +*/ + +type RoundTripper struct { + Host string + Port string +} + +func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { + host := self.Host + if len(host) == 0 { + host = "localhost" + } + url := fmt.Sprintf("http://%s:%s/%s:/%s/%s", host, self.Port, req.Proto, req.URL.Host, req.URL.Path) + glog.V(logger.Info).Infof("roundtripper: proxying request '%s' to '%s'", req.RequestURI, url) + reqProxy, err := http.NewRequest(req.Method, url, req.Body) + if err != nil { + return nil, err + } + return http.DefaultClient.Do(reqProxy) +} 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 new file mode 100644 index 000000000..a35672687 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go @@ -0,0 +1,286 @@ +// 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 . + +/* +A simple http server interface to Swarm +*/ +package http + +import ( + "bytes" + "io" + "net/http" + "regexp" + "sync" + "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/swarm/api" +) + +const ( + rawType = "application/octet-stream" +) + +var ( + // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw) + bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+") + trailingSlashes = regexp.MustCompile("/+$") + rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$") + // forever = func() time.Time { return time.Unix(0, 0) } + forever = time.Now +) + +type sequentialReader struct { + reader io.Reader + pos int64 + ahead map[int64](chan bool) + lock sync.Mutex +} + +// browser API for registering bzz url scheme handlers: +// https://developer.mozilla.org/en/docs/Web-based_protocol_handlers +// electron (chromium) api for registering bzz url scheme handlers: +// https://github.com/atom/electron/blob/master/docs/api/protocol.md + +// starts up http server +func StartHttpServer(api *api.Api, port string) { + serveMux := http.NewServeMux() + serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + handler(w, r, api) + }) + go http.ListenAndServe(":"+port, serveMux) + glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", port) +} + +func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { + requestURL := r.URL + // This is wrong + // if requestURL.Host == "" { + // var err error + // requestURL, err = url.Parse(r.Referer() + requestURL.String()) + // if err != nil { + // http.Error(w, err.Error(), http.StatusBadRequest) + // return + // } + // } + glog.V(logger.Debug).Infof("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept")) + uri := requestURL.Path + var raw, nameresolver bool + var proto string + + // HTTP-based URL protocol handler + glog.V(logger.Debug).Infof("BZZ request URI: '%s'", uri) + + path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string { + proto = p + return "" + }) + + // protocol identification (ugly) + if proto == "" { + if glog.V(logger.Error) { + glog.Errorf( + "[BZZ] Swarm: Protocol error in request `%s`.", + uri, + ) + http.Error(w, "BZZ protocol error", http.StatusBadRequest) + return + } + } + if len(proto) > 4 { + raw = proto[1:5] == "bzzr" + nameresolver = proto[1:5] != "bzzi" + } + + glog.V(logger.Debug).Infof( + "[BZZ] Swarm: %s request over protocol %s '%s' received.", + r.Method, proto, path, + ) + + switch { + case r.Method == "POST" || r.Method == "PUT": + key, err := a.Store(r.Body, r.ContentLength, nil) + if err == nil { + glog.V(logger.Debug).Infof("Content for %v stored", key.Log()) + } else { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if r.Method == "POST" { + if raw { + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key)))) + } else { + http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest) + return + } + } else { + // PUT + if raw { + http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + mime := r.Header.Get("Content-Type") + // TODO proper root hash separation + glog.V(logger.Debug).Infof("Modify '%s' to store %v as '%s'.", path, key.Log(), mime) + newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest) + return + } + } + } + case r.Method == "DELETE": + if raw { + http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + glog.V(logger.Debug).Infof("Delete '%s'.", path) + newKey, err := a.Modify(path, "", "", nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest) + return + } + } + case r.Method == "GET" || r.Method == "HEAD": + path = trailingSlashes.ReplaceAllString(path, "") + if raw { + // resolving host + key, err := a.Resolve(path, nameresolver) + if err != nil { + glog.V(logger.Error).Infof("%v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // retrieving content + reader := a.Retrieve(key) + quitC := make(chan bool) + size, err := reader.Size(quitC) + glog.V(logger.Debug).Infof("Reading %d bytes.", size) + + // setting mime type + qv := requestURL.Query() + mimeType := qv.Get("content_type") + if mimeType == "" { + mimeType = rawType + } + + w.Header().Set("Content-Type", mimeType) + http.ServeContent(w, r, uri, forever(), reader) + glog.V(logger.Debug).Infof("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType) + + // retrieve path via manifest + } else { + glog.V(logger.Debug).Infof("Structured GET request '%s' received.", uri) + // add trailing slash, if missing + if rootDocumentUri.MatchString(uri) { + http.Redirect(w, r, path+"/", http.StatusFound) + return + } + reader, mimeType, status, err := a.Get(path, nameresolver) + if err != nil { + if _, ok := err.(api.ErrResolve); ok { + glog.V(logger.Debug).Infof("%v", err) + status = http.StatusBadRequest + } else { + glog.V(logger.Debug).Infof("error retrieving '%s': %v", uri, err) + status = http.StatusNotFound + } + http.Error(w, err.Error(), status) + return + } + // set mime type and status headers + w.Header().Set("Content-Type", mimeType) + if status > 0 { + w.WriteHeader(status) + } else { + status = 200 + } + quitC := make(chan bool) + size, err := reader.Size(quitC) + glog.V(logger.Debug).Infof("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status) + + http.ServeContent(w, r, path, forever(), reader) + + } + default: + http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + } +} + +func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) { + self.lock.Lock() + // assert self.pos <= off + if self.pos > off { + glog.V(logger.Error).Infof("non-sequential read attempted from sequentialReader; %d > %d", + self.pos, off) + panic("Non-sequential read attempt") + } + if self.pos != off { + glog.V(logger.Debug).Infof("deferred read in POST at position %d, offset %d.", + self.pos, off) + wait := make(chan bool) + self.ahead[off] = wait + self.lock.Unlock() + if <-wait { + // failed read behind + n = 0 + err = io.ErrUnexpectedEOF + return + } + self.lock.Lock() + } + localPos := 0 + for localPos < len(target) { + n, err = self.reader.Read(target[localPos:]) + localPos += n + glog.V(logger.Debug).Infof("Read %d bytes into buffer size %d from POST, error %v.", + n, len(target), err) + if err != nil { + glog.V(logger.Debug).Infof("POST stream's reading terminated with %v.", err) + for i := range self.ahead { + self.ahead[i] <- true + delete(self.ahead, i) + } + self.lock.Unlock() + return localPos, err + } + self.pos += int64(n) + } + wait := self.ahead[self.pos] + if wait != nil { + glog.V(logger.Debug).Infof("deferred read in POST at position %d triggered.", + self.pos) + delete(self.ahead, self.pos) + close(wait) + } + self.lock.Unlock() + return localPos, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go new file mode 100644 index 000000000..a289c01f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go @@ -0,0 +1,336 @@ +// 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 api + +import ( + "bytes" + "encoding/json" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + manifestType = "application/bzz-manifest+json" +) + +type manifestTrie struct { + dpa *storage.DPA + entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry + hash storage.Key // if hash != nil, it is stored +} + +type manifestJSON struct { + Entries []*manifestTrieEntry `json:"entries"` +} + +type manifestTrieEntry struct { + Path string `json:"path"` + Hash string `json:"hash"` // for manifest content type, empty until subtrie is evaluated + ContentType string `json:"contentType"` + Status int `json:"status"` + subtrie *manifestTrie +} + +func loadManifest(dpa *storage.DPA, hash storage.Key, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + glog.V(logger.Detail).Infof("manifest lookup key: '%v'.", hash.Log()) + // retrieve manifest via DPA + manifestReader := dpa.Retrieve(hash) + return readManifest(manifestReader, hash, dpa, quitC) +} + +func readManifest(manifestReader storage.LazySectionReader, hash storage.Key, dpa *storage.DPA, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + // TODO check size for oversized manifests + size, err := manifestReader.Size(quitC) + manifestData := make([]byte, size) + read, err := manifestReader.Read(manifestData) + if int64(read) < size { + glog.V(logger.Detail).Infof("Manifest %v not found.", hash.Log()) + if err == nil { + err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) + } + return + } + + glog.V(logger.Detail).Infof("Manifest %v retrieved", hash.Log()) + man := manifestJSON{} + err = json.Unmarshal(manifestData, &man) + if err != nil { + err = fmt.Errorf("Manifest %v is malformed: %v", hash.Log(), err) + glog.V(logger.Detail).Infof("%v", err) + return + } + + glog.V(logger.Detail).Infof("Manifest %v has %d entries.", hash.Log(), len(man.Entries)) + + trie = &manifestTrie{ + dpa: dpa, + } + for _, entry := range man.Entries { + trie.addEntry(entry, quitC) + } + return +} + +func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(entry.Path) == 0 { + self.entries[256] = entry + return + } + + b := byte(entry.Path[0]) + if (self.entries[b] == nil) || (self.entries[b].Path == entry.Path) { + self.entries[b] = entry + return + } + + oldentry := self.entries[b] + cpl := 0 + for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { + cpl++ + } + + if (oldentry.ContentType == manifestType) && (cpl == len(oldentry.Path)) { + if self.loadSubTrie(oldentry, quitC) != nil { + return + } + entry.Path = entry.Path[cpl:] + oldentry.subtrie.addEntry(entry, quitC) + oldentry.Hash = "" + return + } + + commonPrefix := entry.Path[:cpl] + + subtrie := &manifestTrie{ + dpa: self.dpa, + } + entry.Path = entry.Path[cpl:] + oldentry.Path = oldentry.Path[cpl:] + subtrie.addEntry(entry, quitC) + subtrie.addEntry(oldentry, quitC) + + self.entries[b] = &manifestTrieEntry{ + Path: commonPrefix, + Hash: "", + ContentType: manifestType, + subtrie: subtrie, + } +} + +func (self *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { + for _, e := range self.entries { + if e != nil { + cnt++ + entry = e + } + } + return +} + +func (self *manifestTrie) deleteEntry(path string, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(path) == 0 { + self.entries[256] = nil + return + } + + b := byte(path[0]) + entry := self.entries[b] + if entry == nil { + return + } + if entry.Path == path { + self.entries[b] = nil + return + } + + epl := len(entry.Path) + if (entry.ContentType == manifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { + if self.loadSubTrie(entry, quitC) != nil { + return + } + entry.subtrie.deleteEntry(path[epl:], quitC) + entry.Hash = "" + // remove subtree if it has less than 2 elements + cnt, lastentry := entry.subtrie.getCountLast() + if cnt < 2 { + if lastentry != nil { + lastentry.Path = entry.Path + lastentry.Path + } + self.entries[b] = lastentry + } + } +} + +func (self *manifestTrie) recalcAndStore() error { + if self.hash != nil { + return nil + } + + var buffer bytes.Buffer + buffer.WriteString(`{"entries":[`) + + list := &manifestJSON{} + for _, entry := range self.entries { + if entry != nil { + if entry.Hash == "" { // TODO: paralellize + err := entry.subtrie.recalcAndStore() + if err != nil { + return err + } + entry.Hash = entry.subtrie.hash.String() + } + list.Entries = append(list.Entries, entry) + } + } + + manifest, err := json.Marshal(list) + if err != nil { + return err + } + + sr := bytes.NewReader(manifest) + wg := &sync.WaitGroup{} + key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg, nil) + wg.Wait() + self.hash = key + return err2 +} + +func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { + if entry.subtrie == nil { + hash := common.Hex2Bytes(entry.Hash) + entry.subtrie, err = loadManifest(self.dpa, hash, quitC) + entry.Hash = "" // might not match, should be recalculated + } + return +} + +func (self *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { + plen := len(prefix) + var start, stop int + if plen == 0 { + start = 0 + stop = 256 + } else { + start = int(prefix[0]) + stop = start + } + + for i := start; i <= stop; i++ { + select { + case <-quitC: + return fmt.Errorf("aborted") + default: + } + entry := self.entries[i] + if entry != nil { + epl := len(entry.Path) + if entry.ContentType == manifestType { + l := plen + if epl < l { + l = epl + } + if prefix[:l] == entry.Path[:l] { + err := self.loadSubTrie(entry, quitC) + if err != nil { + return err + } + err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) + if err != nil { + return err + } + } + } else { + if (epl >= plen) && (prefix == entry.Path[:plen]) { + cb(entry, rp+entry.Path[plen:]) + } + } + } + } + return nil +} + +func (self *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { + return self.listWithPrefixInt(prefix, "", quitC, cb) +} + +func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { + + glog.V(logger.Detail).Infof("findPrefixOf(%s)", path) + + if len(path) == 0 { + return self.entries[256], 0 + } + + b := byte(path[0]) + entry = self.entries[b] + if entry == nil { + return self.entries[256], 0 + } + epl := len(entry.Path) + glog.V(logger.Detail).Infof("path = %v entry.Path = %v epl = %v", path, entry.Path, epl) + if (len(path) >= epl) && (path[:epl] == entry.Path) { + glog.V(logger.Detail).Infof("entry.ContentType = %v", entry.ContentType) + if entry.ContentType == manifestType { + if self.loadSubTrie(entry, quitC) != nil { + return nil, 0 + } + entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC) + if entry != nil { + pos += epl + } + } else { + pos = epl + } + } else { + entry = nil + } + return +} + +// file system manifest always contains regularized paths +// no leading or trailing slashes, only single slashes inside +func RegularSlashes(path string) (res string) { + for i := 0; i < len(path); i++ { + if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { + res = res + path[i:i+1] + } + } + if (len(res) > 0) && (res[len(res)-1] == '/') { + res = res[:len(res)-1] + } + return +} + +func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { + path := RegularSlashes(spath) + var pos int + quitC := make(chan bool) + entry, pos = self.findPrefixOf(path, quitC) + return entry, path[:pos] +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go new file mode 100644 index 000000000..31b484675 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.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 . + +package api + +type Response struct { + MimeType string + Status int + Size int64 + // Content []byte + Content string +} + +// implements a service +type Storage struct { + api *Api +} + +func NewStorage(api *Api) *Storage { + return &Storage{api} +} + +// Put uploads the content to the swarm with a simple manifest speficying +// its content type +func (self *Storage) Put(content, contentType string) (string, error) { + return self.api.Put(content, contentType) +} + +// Get retrieves the content from bzzpath and reads the response in full +// It returns the Response object, which serialises containing the +// response body as the value of the Content field +// NOTE: if error is non-nil, sResponse may still have partial content +// the actual size of which is given in len(resp.Content), while the expected +// size is resp.Size +func (self *Storage) Get(bzzpath string) (*Response, error) { + reader, mimeType, status, err := self.api.Get(bzzpath, true) + if err != nil { + return nil, err + } + quitC := make(chan bool) + expsize, err := reader.Size(quitC) + if err != nil { + return nil, err + } + body := make([]byte, expsize) + size, err := reader.Read(body) + if int64(size) == expsize { + err = nil + } + return &Response{mimeType, status, expsize, string(body[:size])}, err +} + +// Modify(rootHash, path, contentHash, contentType) takes th e manifest trie rooted in rootHash, +// and merge on to it. creating an entry w conentType (mime) +func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) { + return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true) +} diff --git a/vendor/github.com/ethereum/go-ethereum/release/generator.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go similarity index 60% rename from vendor/github.com/ethereum/go-ethereum/release/generator.go rename to vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go index 1553e0612..6631196c1 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/generator.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go @@ -14,6 +14,33 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go +package api -package release +import ( + "github.com/ethereum/go-ethereum/swarm/network" +) + +type Control struct { + api *Api + hive *network.Hive +} + +func NewControl(api *Api, hive *network.Hive) *Control { + return &Control{api, hive} +} + +func (self *Control) BlockNetworkRead(on bool) { + self.hive.BlockNetworkRead(on) +} + +func (self *Control) SyncEnabled(on bool) { + self.hive.SyncEnabled(on) +} + +func (self *Control) SwapEnabled(on bool) { + self.hive.SwapEnabled(on) +} + +func (self *Control) Hive() string { + return self.hive.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go new file mode 100644 index 000000000..79987cc6b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go @@ -0,0 +1,211 @@ +// 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 network + +import ( + "bytes" + "encoding/binary" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Handler for storage/retrieval related protocol requests +// implements the StorageHandler interface used by the bzz protocol +type Depo struct { + hashfunc storage.Hasher + localStore storage.ChunkStore + netStore storage.ChunkStore +} + +func NewDepo(hash storage.Hasher, localStore, remoteStore storage.ChunkStore) *Depo { + return &Depo{ + hashfunc: hash, + localStore: localStore, + netStore: remoteStore, // entrypoint internal + } +} + +// Handles UnsyncedKeysMsg after msg decoding - unsynced hashes upto sync state +// * the remote sync state is just stored and handled in protocol +// * filters through the new syncRequests and send the ones missing +// * back immediately as a deliveryRequest message +// * empty message just pings back for more (is this needed?) +// * strict signed sync states may be needed. +func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error { + unsynced := req.Unsynced + var missing []*syncRequest + var chunk *storage.Chunk + var err error + for _, req := range unsynced { + // skip keys that are found, + chunk, err = self.localStore.Get(storage.Key(req.Key[:])) + if err != nil || chunk.SData == nil { + missing = append(missing, req) + } + } + glog.V(logger.Debug).Infof("Depo.HandleUnsyncedKeysMsg: received %v unsynced keys: %v missing. new state: %v", len(unsynced), len(missing), req.State) + glog.V(logger.Detail).Infof("Depo.HandleUnsyncedKeysMsg: received %v", unsynced) + // send delivery request with missing keys + err = p.deliveryRequest(missing) + if err != nil { + return err + } + // set peers state to persist + p.syncState = req.State + return nil +} + +// Handles deliveryRequestMsg +// * serves actual chunks asked by the remote peer +// by pushing to the delivery queue (sync db) of the correct priority +// (remote peer is free to reprioritize) +// * the message implies remote peer wants more, so trigger for +// * new outgoing unsynced keys message is fired +func (self *Depo) HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error { + deliver := req.Deliver + // queue the actual delivery of a chunk () + glog.V(logger.Detail).Infof("Depo.HandleDeliveryRequestMsg: received %v delivery requests: %v", len(deliver), deliver) + for _, sreq := range deliver { + // TODO: look up in cache here or in deliveries + // priorities are taken from the message so the remote party can + // reprioritise to at their leisure + // r = self.pullCached(sreq.Key) // pulls and deletes from cache + Push(p, sreq.Key, sreq.Priority) + } + + // sends it out as unsyncedKeysMsg + p.syncer.sendUnsyncedKeys() + return nil +} + +// the entrypoint for store requests coming from the bzz wire protocol +// if key found locally, return. otherwise +// remote is untrusted, so hash is verified and chunk passed on to NetStore +func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) { + req.from = p + chunk, err := self.localStore.Get(req.Key) + switch { + case err != nil: + glog.V(logger.Detail).Infof("Depo.handleStoreRequest: %v not found locally. create new chunk/request", req.Key) + // not found in memory cache, ie., a genuine store request + // create chunk + chunk = storage.NewChunk(req.Key, nil) + + case chunk.SData == nil: + // found chunk in memory store, needs the data, validate now + hasher := self.hashfunc() + hasher.Write(req.SData) + if !bytes.Equal(hasher.Sum(nil), req.Key) { + // data does not validate, ignore + // TODO: peer should be penalised/dropped? + glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req) + return + } + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v. request entry found", req) + + default: + // data is found, store request ignored + // this should update access count? + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v found locally. ignore.", req) + return + } + + // update chunk with size and data + chunk.SData = req.SData // protocol validates that SData is minimum 9 bytes long (int64 size + at least one byte of data) + chunk.Size = int64(binary.LittleEndian.Uint64(req.SData[0:8])) + glog.V(logger.Detail).Infof("delivery of %p from %v", chunk, p) + chunk.Source = p + self.netStore.Put(chunk) +} + +// entrypoint for retrieve requests coming from the bzz wire protocol +// checks swap balance - return if peer has no credit +func (self *Depo) HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) { + req.from = p + // swap - record credit for 1 request + // note that only charge actual reqsearches + var err error + if p.swap != nil { + err = p.swap.Add(1) + } + if err != nil { + glog.V(logger.Warn).Infof("Depo.HandleRetrieveRequest: %v - cannot process request: %v", req.Key.Log(), err) + return + } + + // call storage.NetStore#Get which + // blocks until local retrieval finished + // launches cloud retrieval + chunk, _ := self.netStore.Get(req.Key) + req = self.strategyUpdateRequest(chunk.Req, req) + // check if we can immediately deliver + if chunk.SData != nil { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, delivering...", req.Key.Log()) + + if req.MaxSize == 0 || int64(req.MaxSize) >= chunk.Size { + sreq := &storeRequestMsgData{ + Id: req.Id, + Key: chunk.Key, + SData: chunk.SData, + requestTimeout: req.timeout, // + } + p.syncer.addRequest(sreq, DeliverReq) + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, not wanted", req.Key.Log()) + } + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content not found locally. asked swarm for help. will get back", req.Key.Log()) + } +} + +// add peer request the chunk and decides the timeout for the response if still searching +func (self *Depo) strategyUpdateRequest(rs *storage.RequestStatus, origReq *retrieveRequestMsgData) (req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.strategyUpdateRequest: key %v", origReq.Key.Log()) + // we do not create an alternative one + req = origReq + if rs != nil { + self.addRequester(rs, req) + req.setTimeout(self.searchTimeout(rs, req)) + } + return +} + +// decides the timeout promise sent with the immediate peers response to a retrieve request +// if timeout is explicitly set and expired +func (self *Depo) searchTimeout(rs *storage.RequestStatus, req *retrieveRequestMsgData) (timeout *time.Time) { + reqt := req.getTimeout() + t := time.Now().Add(searchTimeout) + if reqt != nil && reqt.Before(t) { + return reqt + } else { + return &t + } +} + +/* +adds a new peer to an existing open request +only add if less than requesterCount peers forwarded the same request id so far +note this is done irrespective of status (searching or found) +*/ +func (self *Depo) addRequester(rs *storage.RequestStatus, req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.addRequester: key %v - add peer to req.Id %v", req.Key.Log(), req.from, req.Id) + list := rs.Requesters[req.Id] + rs.Requesters[req.Id] = append(list, req) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go new file mode 100644 index 000000000..fef79c70b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.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 network + +import ( + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const requesterCount = 3 + +/* +forwarder implements the CloudStore interface (use by storage.NetStore) +and serves as the cloud store backend orchestrating storage/retrieval/delivery +via the native bzz protocol +which uses an MSB logarithmic distance-based semi-permanent Kademlia table for +* recursive forwarding style routing for retrieval +* smart syncronisation +*/ + +type forwarder struct { + hive *Hive +} + +func NewForwarder(hive *Hive) *forwarder { + return &forwarder{hive: hive} +} + +// generate a unique id uint64 +func generateId() uint64 { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + return uint64(r.Int63()) +} + +var searchTimeout = 3 * time.Second + +// forwarding logic +// logic propagating retrieve requests to peers given by the kademlia hive +func (self *forwarder) Retrieve(chunk *storage.Chunk) { + peers := self.hive.getPeers(chunk.Key, 0) + glog.V(logger.Detail).Infof("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers)) +OUT: + for _, p := range peers { + glog.V(logger.Detail).Infof("forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p) + for _, recipients := range chunk.Req.Requesters { + for _, recipient := range recipients { + req := recipient.(*retrieveRequestMsgData) + if req.from.Addr() == p.Addr() { + continue OUT + } + } + } + req := &retrieveRequestMsgData{ + Key: chunk.Key, + Id: generateId(), + } + var err error + if p.swap != nil { + err = p.swap.Add(-1) + } + if err == nil { + p.retrieve(req) + break OUT + } + glog.V(logger.Warn).Infof("forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err) + } +} + +// requests to specific peers given by the kademlia hive +// except for peers that the store request came from (if any) +// delivery queueing taken care of by syncer +func (self *forwarder) Store(chunk *storage.Chunk) { + var n int + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var source *peer + if chunk.Source != nil { + source = chunk.Source.(*peer) + } + for _, p := range self.hive.getPeers(chunk.Key, 0) { + glog.V(logger.Detail).Infof("forwarder.Store: %v %v", p, chunk) + + if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) { + n++ + Deliver(p, msg, PropagateReq) + } + } + glog.V(logger.Detail).Infof("forwarder.Store: sent to %v peers (chunk = %v)", n, chunk) +} + +// once a chunk is found deliver it to its requesters unless timed out +func (self *forwarder) Deliver(chunk *storage.Chunk) { + // iterate over request entries + for id, requesters := range chunk.Req.Requesters { + counter := requesterCount + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var n int + var req *retrieveRequestMsgData + // iterate over requesters with the same id + for id, r := range requesters { + req = r.(*retrieveRequestMsgData) + if req.timeout == nil || req.timeout.After(time.Now()) { + glog.V(logger.Detail).Infof("forwarder.Deliver: %v -> %v", req.Id, req.from) + msg.Id = uint64(id) + Deliver(req.from, msg, DeliverReq) + n++ + counter-- + if counter <= 0 { + break + } + } + } + glog.V(logger.Detail).Infof("forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n) + } +} + +// initiate delivery of a chunk to a particular peer via syncer#addRequest +// depending on syncer mode and priority settings and sync request type +// this either goes via confirmation roundtrip or queued or pushed directly +func Deliver(p *peer, req interface{}, ty int) { + p.syncer.addRequest(req, ty) +} + +// push chunk over to peer +func Push(p *peer, key storage.Key, priority uint) { + p.syncer.doDelivery(key, priority, p.syncer.quit) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go new file mode 100644 index 000000000..f5ebdd008 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go @@ -0,0 +1,383 @@ +// 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 network + +import ( + "fmt" + "math/rand" + "path/filepath" + "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/discover" + "github.com/ethereum/go-ethereum/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Hive is the logistic manager of the swarm +// it uses a generic kademlia nodetable to find best peer list +// for any target +// this is used by the netstore to search for content in the swarm +// the bzz protocol peersMsgData exchange is relayed to Kademlia +// for db storage and filtering +// connections and disconnections are reported and relayed +// to keep the nodetable uptodate + +type Hive struct { + listenAddr func() string + callInterval uint64 + id discover.NodeID + addr kademlia.Address + kad *kademlia.Kademlia + path string + quit chan bool + toggle chan bool + more chan bool + + // for testing only + swapEnabled bool + syncEnabled bool + blockRead bool + blockWrite bool +} + +const ( + callInterval = 3000000000 + // bucketSize = 3 + // maxProx = 8 + // proxBinSize = 4 +) + +type HiveParams struct { + CallInterval uint64 + KadDbPath string + *kademlia.KadParams +} + +func NewHiveParams(path string) *HiveParams { + kad := kademlia.NewKadParams() + // kad.BucketSize = bucketSize + // kad.MaxProx = maxProx + // kad.ProxBinSize = proxBinSize + + return &HiveParams{ + CallInterval: callInterval, + KadDbPath: filepath.Join(path, "bzz-peers.json"), + KadParams: kad, + } +} + +func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { + kad := kademlia.New(kademlia.Address(addr), params.KadParams) + return &Hive{ + callInterval: params.CallInterval, + kad: kad, + addr: kad.Addr(), + path: params.KadDbPath, + swapEnabled: swapEnabled, + syncEnabled: syncEnabled, + } +} + +func (self *Hive) SyncEnabled(on bool) { + self.syncEnabled = on +} + +func (self *Hive) SwapEnabled(on bool) { + self.swapEnabled = on +} + +func (self *Hive) BlockNetworkRead(on bool) { + self.blockRead = on +} + +func (self *Hive) BlockNetworkWrite(on bool) { + self.blockWrite = on +} + +// public accessor to the hive base address +func (self *Hive) Addr() kademlia.Address { + return self.addr +} + +// Start receives network info only at startup +// listedAddr is a function to retrieve listening address to advertise to peers +// connectPeer is a function to connect to a peer based on its NodeID or enode URL +// there are called on the p2p.Server which runs on the node +func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) { + self.toggle = make(chan bool) + self.more = make(chan bool) + self.quit = make(chan bool) + self.id = id + self.listenAddr = listenAddr + err = self.kad.Load(self.path, nil) + if err != nil { + glog.V(logger.Warn).Infof("Warning: error reading kaddb '%s' (skipping): %v", self.path, err) + err = nil + } + // this loop is doing bootstrapping and maintains a healthy table + go self.keepAlive() + go func() { + // whenever toggled ask kademlia about most preferred peer + for alive := range self.more { + if !alive { + // receiving false closes the loop while allowing parallel routines + // to attempt to write to more (remove Peer when shutting down) + return + } + node, need, proxLimit := self.kad.Suggest() + + if node != nil && len(node.Url) > 0 { + glog.V(logger.Detail).Infof("call known bee %v", node.Url) + // enode or any lower level connection address is unnecessary in future + // discovery table is used to look it up. + connectPeer(node.Url) + } + if need { + // a random peer is taken from the table + peers := self.kad.FindClosest(kademlia.RandomAddressAt(self.addr, rand.Intn(self.kad.MaxProx)), 1) + if len(peers) > 0 { + // a random address at prox bin 0 is sent for lookup + randAddr := kademlia.RandomAddressAt(self.addr, proxLimit) + req := &retrieveRequestMsgData{ + Key: storage.Key(randAddr[:]), + } + glog.V(logger.Detail).Infof("call any bee near %v (PO%03d) - messenger bee: %v", randAddr, proxLimit, peers[0]) + peers[0].(*peer).retrieve(req) + } else { + glog.V(logger.Warn).Infof("no peer") + } + glog.V(logger.Detail).Infof("buzz kept alive") + } else { + glog.V(logger.Info).Infof("no need for more bees") + } + select { + case self.toggle <- need: + case <-self.quit: + return + } + glog.V(logger.Debug).Infof("queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount()) + } + }() + return +} + +// keepAlive is a forever loop +// in its awake state it periodically triggers connection attempts +// by writing to self.more until Kademlia Table is saturated +// wake state is toggled by writing to self.toggle +// it restarts if the table becomes non-full again due to disconnections +func (self *Hive) keepAlive() { + alarm := time.NewTicker(time.Duration(self.callInterval)).C + for { + select { + case <-alarm: + if self.kad.DBCount() > 0 { + select { + case self.more <- true: + glog.V(logger.Debug).Infof("buzz wakeup") + default: + } + } + case need := <-self.toggle: + if alarm == nil && need { + alarm = time.NewTicker(time.Duration(self.callInterval)).C + } + if alarm != nil && !need { + alarm = nil + + } + case <-self.quit: + return + } + } +} + +func (self *Hive) Stop() error { + // closing toggle channel quits the updateloop + close(self.quit) + return self.kad.Save(self.path, saveSync) +} + +// called at the end of a successful protocol handshake +func (self *Hive) addPeer(p *peer) error { + defer func() { + select { + case self.more <- true: + default: + } + }() + glog.V(logger.Detail).Infof("hi new bee %v", p) + err := self.kad.On(p, loadSync) + if err != nil { + return err + } + // self lookup (can be encoded as nil/zero key since peers addr known) + no id () + // the most common way of saying hi in bzz is initiation of gossip + // let me know about anyone new from my hood , here is the storageradius + // to send the 6 byte self lookup + // we do not record as request or forward it, just reply with peers + p.retrieve(&retrieveRequestMsgData{}) + glog.V(logger.Detail).Infof("'whatsup wheresdaparty' sent to %v", p) + + return nil +} + +// called after peer disconnected +func (self *Hive) removePeer(p *peer) { + glog.V(logger.Debug).Infof("bee %v removed", p) + self.kad.Off(p, saveSync) + select { + case self.more <- true: + default: + } + if self.kad.Count() == 0 { + glog.V(logger.Debug).Infof("empty, all bees gone") + } +} + +// Retrieve a list of live peers that are closer to target than us +func (self *Hive) getPeers(target storage.Key, max int) (peers []*peer) { + var addr kademlia.Address + copy(addr[:], target[:]) + for _, node := range self.kad.FindClosest(addr, max) { + peers = append(peers, node.(*peer)) + } + return +} + +// disconnects all the peers +func (self *Hive) DropAll() { + glog.V(logger.Info).Infof("dropping all bees") + for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) { + node.Drop() + } +} + +// contructor for kademlia.NodeRecord based on peer address alone +// TODO: should go away and only addr passed to kademlia +func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord { + now := time.Now() + return &kademlia.NodeRecord{ + Addr: addr.Addr, + Url: addr.String(), + Seen: now, + After: now, + } +} + +// called by the protocol when receiving peerset (for target address) +// peersMsgData is converted to a slice of NodeRecords for Kademlia +// this is to store all thats needed +func (self *Hive) HandlePeersMsg(req *peersMsgData, from *peer) { + var nrs []*kademlia.NodeRecord + for _, p := range req.Peers { + nrs = append(nrs, newNodeRecord(p)) + } + self.kad.Add(nrs) +} + +// peer wraps the protocol instance to represent a connected peer +// it implements kademlia.Node interface +type peer struct { + *bzz // protocol instance running on peer connection +} + +// protocol instance implements kademlia.Node interface (embedded peer) +func (self *peer) Addr() kademlia.Address { + return self.remoteAddr.Addr +} + +func (self *peer) Url() string { + return self.remoteAddr.String() +} + +// TODO take into account traffic +func (self *peer) LastActive() time.Time { + return self.lastActive +} + +// reads the serialised form of sync state persisted as the 'Meta' attribute +// and sets the decoded syncState on the online node +func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error { + p, ok := node.(*peer) + if !ok { + return fmt.Errorf("invalid type") + } + if record.Meta == nil { + glog.V(logger.Debug).Infof("no sync state for node record %v setting default", record) + p.syncState = &syncState{DbSyncState: &storage.DbSyncState{}} + return nil + } + state, err := decodeSync(record.Meta) + if err != nil { + return fmt.Errorf("error decoding kddb record meta info into a sync state: %v", err) + } + glog.V(logger.Detail).Infof("sync state for node record %v read from Meta: %s", record, string(*(record.Meta))) + p.syncState = state + return err +} + +// callback when saving a sync state +func saveSync(record *kademlia.NodeRecord, node kademlia.Node) { + if p, ok := node.(*peer); ok { + meta, err := encodeSync(p.syncState) + if err != nil { + glog.V(logger.Warn).Infof("error saving sync state for %v: %v", node, err) + return + } + glog.V(logger.Detail).Infof("saved sync state for %v: %s", node, string(*meta)) + record.Meta = meta + } +} + +// the immediate response to a retrieve request, +// sends relevant peer data given by the kademlia hive to the requester +// TODO: remember peers sent for duration of the session, only new peers sent +func (self *Hive) peers(req *retrieveRequestMsgData) { + if req != nil && req.MaxPeers >= 0 { + var addrs []*peerAddr + if req.timeout == nil || time.Now().Before(*(req.timeout)) { + key := req.Key + // self lookup from remote peer + if storage.IsZeroKey(key) { + addr := req.from.Addr() + key = storage.Key(addr[:]) + req.Key = nil + } + // get peer addresses from hive + for _, peer := range self.getPeers(key, int(req.MaxPeers)) { + addrs = append(addrs, peer.remoteAddr) + } + glog.V(logger.Debug).Infof("Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %v", len(addrs), req.from, req.Id, req.Key.Log()) + + peersData := &peersMsgData{ + Peers: addrs, + Key: req.Key, + Id: req.Id, + } + peersData.setTimeout(req.timeout) + req.from.peers(peersData) + } + } +} + +func (self *Hive) String() string { + return self.kad.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go new file mode 100644 index 000000000..16c5ce532 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go @@ -0,0 +1,173 @@ +// 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 kademlia + +import ( + "fmt" + "math/rand" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +type Address common.Hash + +func (a Address) String() string { + return fmt.Sprintf("%x", a[:]) +} + +func (a *Address) MarshalJSON() (out []byte, err error) { + return []byte(`"` + a.String() + `"`), nil +} + +func (a *Address) UnmarshalJSON(value []byte) error { + *a = Address(common.HexToHash(string(value[1 : len(value)-1]))) + return nil +} + +// the string form of the binary representation of an address (only first 8 bits) +func (a Address) Bin() string { + var bs []string + for _, b := range a[:] { + bs = append(bs, fmt.Sprintf("%08b", b)) + } + return strings.Join(bs, "") +} + +/* +Proximity(x, y) returns the proximity order of the MSB distance between x and y + +The distance metric MSB(x, y) of two equal length byte sequences x an y is the +value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed. +the binary cast is big endian: most significant bit first (=MSB). + +Proximity(x, y) is a discrete logarithmic scaling of the MSB distance. +It is defined as the reverse rank of the integer part of the base 2 +logarithm of the distance. +It is calculated by counting the number of common leading zeros in the (MSB) +binary representation of the x^y. + +(0 farthest, 255 closest, 256 self) +*/ +func proximity(one, other Address) (ret int) { + for i := 0; i < len(one); i++ { + oxo := one[i] ^ other[i] + for j := 0; j < 8; j++ { + if (uint8(oxo)>>uint8(7-j))&0x01 != 0 { + return i*8 + j + } + } + } + return len(one) * 8 +} + +// Address.ProxCmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func (target Address) ProxCmp(a, b Address) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// randomAddressAt(address, prox) generates a random address +// at proximity order prox relative to address +// if prox is negative a random address is generated +func RandomAddressAt(self Address, prox int) (addr Address) { + addr = self + var pos int + if prox >= 0 { + pos = prox / 8 + trans := prox % 8 + transbytea := byte(0) + for j := 0; j <= trans; j++ { + transbytea |= 1 << uint8(7-j) + } + flipbyte := byte(1 << uint8(7-trans)) + transbyteb := transbytea ^ byte(255) + randbyte := byte(rand.Intn(255)) + addr[pos] = ((addr[pos] & transbytea) ^ flipbyte) | randbyte&transbyteb + } + for i := pos + 1; i < len(addr); i++ { + addr[i] = byte(rand.Intn(255)) + } + + return +} + +// KeyRange(a0, a1, proxLimit) returns the address inclusive address +// range that contain addresses closer to one than other +func KeyRange(one, other Address, proxLimit int) (start, stop Address) { + prox := proximity(one, other) + if prox >= proxLimit { + prox = proxLimit + } + start = CommonBitsAddrByte(one, other, byte(0x00), prox) + stop = CommonBitsAddrByte(one, other, byte(0xff), prox) + return +} + +func CommonBitsAddrF(self, other Address, f func() byte, p int) (addr Address) { + prox := proximity(self, other) + var pos int + if p <= prox { + prox = p + } + pos = prox / 8 + addr = self + trans := byte(prox % 8) + var transbytea byte + if p > prox { + transbytea = byte(0x7f) + } else { + transbytea = byte(0xff) + } + transbytea >>= trans + transbyteb := transbytea ^ byte(0xff) + addrpos := addr[pos] + addrpos &= transbyteb + if p > prox { + addrpos ^= byte(0x80 >> trans) + } + addrpos |= transbytea & f() + addr[pos] = addrpos + for i := pos + 1; i < len(addr); i++ { + addr[i] = f() + } + + return +} + +func CommonBitsAddr(self, other Address, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return byte(rand.Intn(255)) }, prox) +} + +func CommonBitsAddrByte(self, other Address, b byte, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return b }, prox) +} + +// randomAddressAt() generates a random address +func RandomAddress() Address { + return RandomAddressAt(Address{}, -1) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go new file mode 100644 index 000000000..53a7db451 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go @@ -0,0 +1,351 @@ +// 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 kademlia + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +type NodeData interface { + json.Marshaler + json.Unmarshaler +} + +// allow inactive peers under +type NodeRecord struct { + Addr Address // address of node + Url string // Url, used to connect to node + After time.Time // next call after time + Seen time.Time // last connected at time + Meta *json.RawMessage // arbitrary metadata saved for a peer + + node Node +} + +func (self *NodeRecord) setSeen() { + t := time.Now() + self.Seen = t + self.After = t +} + +func (self *NodeRecord) String() string { + return fmt.Sprintf("<%v>", self.Addr) +} + +// persisted node record database () +type KadDb struct { + Address Address + Nodes [][]*NodeRecord + index map[Address]*NodeRecord + cursors []int + lock sync.RWMutex + purgeInterval time.Duration + initialRetryInterval time.Duration + connRetryExp int +} + +func newKadDb(addr Address, params *KadParams) *KadDb { + return &KadDb{ + Address: addr, + Nodes: make([][]*NodeRecord, params.MaxProx+1), // overwritten by load + cursors: make([]int, params.MaxProx+1), + index: make(map[Address]*NodeRecord), + purgeInterval: params.PurgeInterval, + initialRetryInterval: params.InitialRetryInterval, + connRetryExp: params.ConnRetryExp, + } +} + +func (self *KadDb) findOrCreate(index int, a Address, url string) *NodeRecord { + defer self.lock.Unlock() + self.lock.Lock() + + record, found := self.index[a] + if !found { + record = &NodeRecord{ + Addr: a, + Url: url, + } + glog.V(logger.Info).Infof("add new record %v to kaddb", record) + // insert in kaddb + self.index[a] = record + self.Nodes[index] = append(self.Nodes[index], record) + } else { + glog.V(logger.Info).Infof("found record %v in kaddb", record) + } + // update last seen time + record.setSeen() + // update with url in case IP/port changes + record.Url = url + return record +} + +// add adds node records to kaddb (persisted node record db) +func (self *KadDb) add(nrs []*NodeRecord, proximityBin func(Address) int) { + defer self.lock.Unlock() + self.lock.Lock() + var n int + var nodes []*NodeRecord + for _, node := range nrs { + _, found := self.index[node.Addr] + if !found && node.Addr != self.Address { + node.setSeen() + self.index[node.Addr] = node + index := proximityBin(node.Addr) + dbcursor := self.cursors[index] + nodes = self.Nodes[index] + // this is inefficient for allocation, need to just append then shift + newnodes := make([]*NodeRecord, len(nodes)+1) + copy(newnodes[:], nodes[:dbcursor]) + newnodes[dbcursor] = node + copy(newnodes[dbcursor+1:], nodes[dbcursor:]) + glog.V(logger.Detail).Infof("new nodes: %v (keys: %v)\nnodes: %v", newnodes, nodes) + self.Nodes[index] = newnodes + n++ + } + } + if n > 0 { + glog.V(logger.Debug).Infof("%d/%d node records (new/known)", n, len(nrs)) + } +} + +/* +next return one node record with the highest priority for desired +connection. +This is used to pick candidates for live nodes that are most wanted for +a higly connected low centrality network structure for Swarm which best suits +for a Kademlia-style routing. + +* Starting as naive node with empty db, this implements Kademlia bootstrapping +* As a mature node, it fills short lines. All on demand. + +The candidate is chosen using the following strategy: +We check for missing online nodes in the buckets for 1 upto Max BucketSize rounds. +On each round we proceed from the low to high proximity order buckets. +If the number of active nodes (=connected peers) is < rounds, then start looking +for a known candidate. To determine if there is a candidate to recommend the +kaddb node record database row corresponding to the bucket is checked. + +If the row cursor is on position i, the ith element in the row is chosen. +If the record is scheduled not to be retried before NOW, the next element is taken. +If the record is scheduled to be retried, it is set as checked, scheduled for +checking and is returned. The time of the next check is in X (duration) such that +X = ConnRetryExp * delta where delta is the time past since the last check and +ConnRetryExp is constant obsoletion factor. (Note that when node records are added +from peer messages, they are marked as checked and placed at the cursor, ie. +given priority over older entries). Entries which were checked more than +purgeInterval ago are deleted from the kaddb row. If no candidate is found after +a full round of checking the next bucket up is considered. If no candidate is +found when we reach the maximum-proximity bucket, the next round starts. + +node record a is more favoured to b a > b iff a is a passive node (record of +offline past peer) +|proxBin(a)| < |proxBin(b)| +|| (proxBin(a) < proxBin(b) && |proxBin(a)| == |proxBin(b)|) +|| (proxBin(a) == proxBin(b) && lastChecked(a) < lastChecked(b)) + + +The second argument returned names the first missing slot found +*/ +func (self *KadDb) findBest(maxBinSize int, binSize func(int) int) (node *NodeRecord, need bool, proxLimit int) { + // return nil, proxLimit indicates that all buckets are filled + defer self.lock.Unlock() + self.lock.Lock() + + var interval time.Duration + var found bool + var purge []bool + var delta time.Duration + var cursor int + var count int + var after time.Time + + // iterate over columns maximum bucketsize times + for rounds := 1; rounds <= maxBinSize; rounds++ { + ROUND: + // iterate over rows from PO 0 upto MaxProx + for po, dbrow := range self.Nodes { + // if row has rounds connected peers, then take the next + if binSize(po) >= rounds { + continue ROUND + } + if !need { + // set proxlimit to the PO where the first missing slot is found + proxLimit = po + need = true + } + purge = make([]bool, len(dbrow)) + + // there is a missing slot - finding a node to connect to + // select a node record from the relavant kaddb row (of identical prox order) + ROW: + for cursor = self.cursors[po]; !found && count < len(dbrow); cursor = (cursor + 1) % len(dbrow) { + count++ + node = dbrow[cursor] + + // skip already connected nodes + if node.node != nil { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d/%d) already connected", node.Addr, po, cursor, len(dbrow)) + continue ROW + } + + // if node is scheduled to connect + if time.Time(node.After).After(time.Now()) { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) skipped. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + continue ROW + } + + delta = time.Since(time.Time(node.Seen)) + if delta < self.initialRetryInterval { + delta = self.initialRetryInterval + } + if delta > self.purgeInterval { + // remove node + purge[cursor] = true + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) unreachable since %v. Removed", node.Addr, po, cursor, node.Seen) + continue ROW + } + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) ready to be tried. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + + // scheduling next check + interval = time.Duration(delta * time.Duration(self.connRetryExp)) + after = time.Now().Add(interval) + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) selected as candidate connection %v. seen at %v (%v ago), selectable since %v, retry after %v (in %v)", node.Addr, po, cursor, rounds, node.Seen, delta, node.After, after, interval) + node.After = after + found = true + } // ROW + self.cursors[po] = cursor + self.delete(po, purge) + if found { + return node, need, proxLimit + } + } // ROUND + } // ROUNDS + + return nil, need, proxLimit +} + +// deletes the noderecords of a kaddb row corresponding to the indexes +// caller must hold the dblock +// the call is unsafe, no index checks +func (self *KadDb) delete(row int, purge []bool) { + var nodes []*NodeRecord + dbrow := self.Nodes[row] + for i, del := range purge { + if i == self.cursors[row] { + //reset cursor + self.cursors[row] = len(nodes) + } + // delete the entry to be purged + if del { + delete(self.index, dbrow[i].Addr) + continue + } + // otherwise append to new list + nodes = append(nodes, dbrow[i]) + } + self.Nodes[row] = nodes +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *KadDb) save(path string, cb func(*NodeRecord, Node)) error { + defer self.lock.Unlock() + self.lock.Lock() + + var n int + + for _, b := range self.Nodes { + for _, node := range b { + n++ + node.After = time.Now() + node.Seen = time.Now() + if cb != nil { + cb(node, node.node) + } + } + } + + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = ioutil.WriteFile(path, data, os.ModePerm) + if err != nil { + glog.V(logger.Warn).Infof("unable to save kaddb with %v nodes to %v: err", n, path, err) + } else { + glog.V(logger.Info).Infof("saved kaddb with %v nodes to %v", n, path) + } + return err +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err error) { + defer self.lock.Unlock() + self.lock.Lock() + + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + err = json.Unmarshal(data, self) + if err != nil { + return + } + var n int + var purge []bool + for po, b := range self.Nodes { + purge = make([]bool, len(b)) + ROW: + for i, node := range b { + if cb != nil { + err = cb(node, node.node) + if err != nil { + purge[i] = true + continue ROW + } + } + n++ + if (node.After == time.Time{}) { + node.After = time.Now() + } + self.index[node.Addr] = node + } + self.delete(po, purge) + } + glog.V(logger.Info).Infof("loaded kaddb with %v nodes from %v", n, path) + + return +} + +// accessor for KAD offline db count +func (self *KadDb) count() int { + defer self.lock.Unlock() + self.lock.Lock() + return len(self.index) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go new file mode 100644 index 000000000..87c57cefe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go @@ -0,0 +1,429 @@ +// 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 kademlia + +import ( + "fmt" + "sort" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + bucketSize = 4 + proxBinSize = 2 + maxProx = 8 + connRetryExp = 2 + maxPeers = 100 +) + +var ( + purgeInterval = 42 * time.Hour + initialRetryInterval = 42 * time.Millisecond + maxIdleInterval = 42 * 1000 * time.Millisecond + // maxIdleInterval = 42 * 10 0 * time.Millisecond +) + +type KadParams struct { + // adjustable parameters + MaxProx int + ProxBinSize int + BucketSize int + PurgeInterval time.Duration + InitialRetryInterval time.Duration + MaxIdleInterval time.Duration + ConnRetryExp int +} + +func NewKadParams() *KadParams { + return &KadParams{ + MaxProx: maxProx, + ProxBinSize: proxBinSize, + BucketSize: bucketSize, + PurgeInterval: purgeInterval, + InitialRetryInterval: initialRetryInterval, + MaxIdleInterval: maxIdleInterval, + ConnRetryExp: connRetryExp, + } +} + +// Kademlia is a table of active nodes +type Kademlia struct { + addr Address // immutable baseaddress of the table + *KadParams // Kademlia configuration parameters + proxLimit int // state, the PO of the first row of the most proximate bin + proxSize int // state, the number of peers in the most proximate bin + count int // number of active peers (w live connection) + buckets [][]Node // the actual bins + db *KadDb // kaddb, node record database + lock sync.RWMutex // mutex to access buckets +} + +type Node interface { + Addr() Address + Url() string + LastActive() time.Time + Drop() +} + +// public constructor +// add is the base address of the table +// params is KadParams configuration +func New(addr Address, params *KadParams) *Kademlia { + buckets := make([][]Node, params.MaxProx+1) + return &Kademlia{ + addr: addr, + KadParams: params, + buckets: buckets, + db: newKadDb(addr, params), + } +} + +// accessor for KAD base address +func (self *Kademlia) Addr() Address { + return self.addr +} + +// accessor for KAD active node count +func (self *Kademlia) Count() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.count +} + +// accessor for KAD active node count +func (self *Kademlia) DBCount() int { + return self.db.count() +} + +// On is the entry point called when a new nodes is added +// unsafe in that node is not checked to be already active node (to be called once) +func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) { + glog.V(logger.Warn).Infof("%v", self) + defer self.lock.Unlock() + self.lock.Lock() + + index := self.proximityBin(node.Addr()) + record := self.db.findOrCreate(index, node.Addr(), node.Url()) + + if cb != nil { + err = cb(record, node) + glog.V(logger.Detail).Infof("cb(%v, %v) ->%v", record, node, err) + if err != nil { + return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err) + } + glog.V(logger.Debug).Infof("add node record %v with node %v", record, node) + } + + // insert in kademlia table of active nodes + bucket := self.buckets[index] + // if bucket is full insertion replaces the worst node + // TODO: give priority to peers with active traffic + if len(bucket) < self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation + self.buckets[index] = append(bucket, node) + glog.V(logger.Debug).Infof("add node %v to table", node) + self.setProxLimit(index, true) + record.node = node + self.count++ + return nil + } + + // always rotate peers + idle := self.MaxIdleInterval + var pos int + var replaced Node + for i, p := range bucket { + idleInt := time.Since(p.LastActive()) + if idleInt > idle { + idle = idleInt + pos = i + replaced = p + } + } + if replaced == nil { + glog.V(logger.Debug).Infof("all peers wanted, PO%03d bucket full", index) + return fmt.Errorf("bucket full") + } + glog.V(logger.Debug).Infof("node %v replaced by %v (idle for %v > %v)", replaced, node, idle, self.MaxIdleInterval) + replaced.Drop() + // actually replace in the row. When off(node) is called, the peer is no longer in the row + bucket[pos] = node + // there is no change in bucket cardinalities so no prox limit adjustment is needed + record.node = node + self.count++ + return nil + +} + +// Off is the called when a node is taken offline (from the protocol main loop exit) +func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) { + self.lock.Lock() + defer self.lock.Unlock() + + index := self.proximityBin(node.Addr()) + bucket := self.buckets[index] + for i := 0; i < len(bucket); i++ { + if node.Addr() == bucket[i].Addr() { + self.buckets[index] = append(bucket[:i], bucket[(i+1):]...) + self.setProxLimit(index, false) + break + } + } + + record := self.db.index[node.Addr()] + // callback on remove + if cb != nil { + cb(record, record.node) + } + record.node = nil + self.count-- + glog.V(logger.Debug).Infof("remove node %v from table, population now is %v", node, self.count) + + return +} + +// proxLimit is dynamically adjusted so that +// 1) there is no empty buckets in bin < proxLimit and +// 2) the sum of all items are the minimum possible but higher than ProxBinSize +// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes) +// caller holds the lock +func (self *Kademlia) setProxLimit(r int, on bool) { + // if the change is outside the core (PO lower) + // and the change does not leave a bucket empty then + // no adjustment needed + if r < self.proxLimit && len(self.buckets[r]) > 0 { + return + } + // if on=a node was added, then r must be within prox limit so increment cardinality + if on { + self.proxSize++ + curr := len(self.buckets[self.proxLimit]) + // if now core is big enough without the furthest bucket, then contract + // this can result in more than one bucket change + for self.proxSize >= self.ProxBinSize+curr && curr > 0 { + self.proxSize -= curr + self.proxLimit++ + curr = len(self.buckets[self.proxLimit]) + + glog.V(logger.Detail).Infof("proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } + return + } + // otherwise + if r >= self.proxLimit { + self.proxSize-- + } + // expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality + for (self.proxSize < self.ProxBinSize || r < self.proxLimit) && + self.proxLimit > 0 { + // + self.proxLimit-- + self.proxSize += len(self.buckets[self.proxLimit]) + glog.V(logger.Detail).Infof("proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } +} + +/* +returns the list of nodes belonging to the same proximity bin +as the target. The most proximate bin will be the union of the bins between +proxLimit and MaxProx. +*/ +func (self *Kademlia) FindClosest(target Address, max int) []Node { + self.lock.Lock() + defer self.lock.Unlock() + + r := nodesByDistance{ + target: target, + } + + po := self.proximityBin(target) + index := po + step := 1 + glog.V(logger.Detail).Infof("serving %v nodes at %v (PO%02d)", max, index, po) + + // if max is set to 0, just want a full bucket, dynamic number + min := max + // set limit to max + limit := max + if max == 0 { + min = 1 + limit = maxPeers + } + + var n int + for index >= 0 { + // add entire bucket + for _, p := range self.buckets[index] { + r.push(p, limit) + n++ + } + // terminate if index reached the bottom or enough peers > min + glog.V(logger.Detail).Infof("add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po) + if n >= min && (step < 0 || max == 0) { + break + } + // reach top most non-empty PO bucket, turn around + if index == self.MaxProx { + index = po + step = -1 + } + index += step + } + glog.V(logger.Detail).Infof("serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po) + return r.nodes +} + +func (self *Kademlia) Suggest() (*NodeRecord, bool, int) { + defer self.lock.RUnlock() + self.lock.RLock() + return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) }) +} + +// adds node records to kaddb (persisted node record db) +func (self *Kademlia) Add(nrs []*NodeRecord) { + self.db.add(nrs, self.proximityBin) +} + +// nodesByDistance is a list of nodes, ordered by distance to target. +type nodesByDistance struct { + nodes []Node + target Address +} + +func sortedByDistanceTo(target Address, slice []Node) bool { + var last Address + for i, node := range slice { + if i > 0 { + if target.ProxCmp(node.Addr(), last) < 0 { + return false + } + } + last = node.Addr() + } + return true +} + +// push(node, max) adds the given node to the list, keeping the total size +// below max elements. +func (h *nodesByDistance) push(node Node, max int) { + // returns the firt index ix such that func(i) returns true + ix := sort.Search(len(h.nodes), func(i int) bool { + return h.target.ProxCmp(h.nodes[i].Addr(), node.Addr()) >= 0 + }) + + if len(h.nodes) < max { + h.nodes = append(h.nodes, node) + } + if ix < len(h.nodes) { + copy(h.nodes[ix+1:], h.nodes[ix:]) + h.nodes[ix] = node + } +} + +/* +Taking the proximity order relative to a fix point x classifies the points in +the space (n byte long byte sequences) into bins. Items in each are at +most half as distant from x as items in the previous bin. Given a sample of +uniformly distributed items (a hash function over arbitrary sequence) the +proximity scale maps onto series of subsets with cardinalities on a negative +exponential scale. + +It also has the property that any two item belonging to the same bin are at +most half as distant from each other as they are from x. + +If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local +decisions for graph traversal where the task is to find a route between two +points. Since in every hop, the finite distance halves, there is +a guaranteed constant maximum limit on the number of hops needed to reach one +node from the other. +*/ + +func (self *Kademlia) proximityBin(other Address) (ret int) { + ret = proximity(self.addr, other) + if ret > self.MaxProx { + ret = self.MaxProx + } + return +} + +// provides keyrange for chunk db iteration +func (self *Kademlia) KeyRange(other Address) (start, stop Address) { + defer self.lock.RUnlock() + self.lock.RLock() + return KeyRange(self.addr, other, self.proxLimit) +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *Kademlia) Save(path string, cb func(*NodeRecord, Node)) error { + return self.db.save(path, cb) +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err error) { + return self.db.load(path, cb) +} + +// kademlia table + kaddb table displayed with ascii +func (self *Kademlia) String() string { + defer self.lock.RUnlock() + self.lock.RLock() + defer self.db.lock.RUnlock() + self.db.lock.RLock() + + var rows []string + rows = append(rows, "=========================================================================") + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6])) + rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize)) + rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize)) + + for i, bucket := range self.buckets { + + if i == self.proxLimit { + rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) + } + row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))} + var k int + c := self.db.cursors[i] + for ; k < len(bucket); k++ { + p := bucket[(c+k)%len(bucket)] + row = append(row, p.Addr().String()[:6]) + if k == 4 { + break + } + } + for ; k < 4; k++ { + row = append(row, " ") + } + row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i])) + + for j, p := range self.db.Nodes[i] { + row = append(row, p.Addr.String()[:6]) + if j == 3 { + break + } + } + rows = append(rows, strings.Join(row, " ")) + if i == self.MaxProx { + } + } + rows = append(rows, "=========================================================================") + return strings.Join(rows, "\n") +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go new file mode 100644 index 000000000..d3858c424 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go @@ -0,0 +1,317 @@ +// 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 network + +import ( + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +/* +BZZ protocol Message Types and Message Data Types +*/ + +// bzz protocol message codes +const ( + statusMsg = iota // 0x01 + storeRequestMsg // 0x02 + retrieveRequestMsg // 0x03 + peersMsg // 0x04 + syncRequestMsg // 0x05 + deliveryRequestMsg // 0x06 + unsyncedKeysMsg // 0x07 + paymentMsg // 0x08 +) + +/* + Handshake + +* Version: 8 byte integer version of the protocol +* ID: arbitrary byte sequence client identifier human readable +* Addr: the address advertised by the node, format similar to DEVp2p wire protocol +* Swap: info for the swarm accounting protocol +* NetworkID: 8 byte integer network identifier +* Caps: swarm-specific capabilities, format identical to devp2p +* SyncState: syncronisation state (db iterator key and address space etc) persisted about the peer + +*/ +type statusMsgData struct { + Version uint64 + ID string + Addr *peerAddr + Swap *swap.SwapProfile + NetworkId uint64 +} + +func (self *statusMsgData) String() string { + return fmt.Sprintf("Status: Version: %v, ID: %v, Addr: %v, Swap: %v, NetworkId: %v", self.Version, self.ID, self.Addr, self.Swap, self.NetworkId) +} + +/* + store requests are forwarded to the peers in their kademlia proximity bin + if they are distant + if they are within our storage radius or have any incentive to store it + then attach your nodeID to the metadata + if the storage request is sufficiently close (within our proxLimit, i. e., the + last row of the routing table) +*/ +type storeRequestMsgData struct { + Key storage.Key // hash of datasize | data + SData []byte // the actual chunk Data + // optional + Id uint64 // request ID. if delivery, the ID is retrieve request ID + requestTimeout *time.Time // expiry for forwarding - [not serialised][not currently used] + storageTimeout *time.Time // expiry of content - [not serialised][not currently used] + from *peer // [not serialised] protocol registers the requester +} + +func (self storeRequestMsgData) String() string { + var from string + if self.from == nil { + from = "self" + } else { + from = self.from.Addr().String() + } + end := len(self.SData) + if len(self.SData) > 10 { + end = 10 + } + return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:end]) +} + +/* +Retrieve request + +Timeout in milliseconds. Note that zero timeout retrieval requests do not request forwarding, but prompt for a peers message response. therefore they serve also +as messages to retrieve peers. + +MaxSize specifies the maximum size that the peer will accept. This is useful in +particular if we allow storage and delivery of multichunk payload representing +the entire or partial subtree unfolding from the requested root key. +So when only interested in limited part of a stream (infinite trees) or only +testing chunk availability etc etc, we can indicate it by limiting the size here. + +Request ID can be newly generated or kept from the request originator. +If request ID Is missing or zero, the request is handled as a lookup only +prompting a peers response but not launching a search. Lookup requests are meant +to be used to bootstrap kademlia tables. + +In the special case that the key is the zero value as well, the remote peer's +address is assumed (the message is to be handled as a self lookup request). +The response is a PeersMsg with the peers in the kademlia proximity bin +corresponding to the address. +*/ + +type retrieveRequestMsgData struct { + Key storage.Key // target Key address of chunk to be retrieved + Id uint64 // request id, request is a lookup if missing or zero + MaxSize uint64 // maximum size of delivery accepted + MaxPeers uint64 // maximum number of peers returned + Timeout uint64 // the longest time we are expecting a response + timeout *time.Time // [not serialied] + from *peer // +} + +func (self retrieveRequestMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, MaxSize: %v, MaxPeers: %d", from, target, self.Id, self.MaxSize, self.MaxPeers) +} + +// lookups are encoded by missing request ID +func (self retrieveRequestMsgData) isLookup() bool { + return self.Id == 0 +} + +// sets timeout fields +func (self retrieveRequestMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self retrieveRequestMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +// peerAddr is sent in StatusMsg as part of the handshake +type peerAddr struct { + IP net.IP + Port uint16 + ID []byte // the 64 byte NodeID (ECDSA Public Key) + Addr kademlia.Address +} + +// peerAddr pretty prints as enode +func (self peerAddr) String() string { + var nodeid discover.NodeID + copy(nodeid[:], self.ID) + return discover.NewNode(nodeid, self.IP, 0, self.Port).String() +} + +/* +peers Msg is one response to retrieval; it is always encouraged after a retrieval +request to respond with a list of peers in the same kademlia proximity bin. +The encoding of a peer is identical to that in the devp2p base protocol peers +messages: [IP, Port, NodeID] +note that a node's DPA address is not the NodeID but the hash of the NodeID. + +Timeout serves to indicate whether the responder is forwarding the query within +the timeout or not. + +NodeID serves as the owner of payment contracts and signer of proofs of transfer. + +The Key is the target (if response to a retrieval request) or missing (zero value) +peers address (hash of NodeID) if retrieval request was a self lookup. + +Peers message is requested by retrieval requests with a missing or zero value request ID +*/ +type peersMsgData struct { + Peers []*peerAddr // + Timeout uint64 // + timeout *time.Time // indicate whether responder is expected to deliver content + Key storage.Key // present if a response to a retrieval request + Id uint64 // present if a response to a retrieval request + from *peer +} + +// peers msg pretty printer +func (self peersMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, Peers: %v", from, target, self.Id, self.Peers) +} + +func (self peersMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self peersMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +/* +syncRequest + +is sent after the handshake to initiate syncing +the syncState of the remote node is persisted in kaddb and set on the +peer/protocol instance when the node is registered by hive as online{ +*/ + +type syncRequestMsgData struct { + SyncState *syncState `rlp:"nil"` +} + +func (self *syncRequestMsgData) String() string { + return fmt.Sprintf("%v", self.SyncState) +} + +/* +deliveryRequest + +is sent once a batch of sync keys is filtered. The ones not found are +sent as a list of syncReuest (hash, priority) in the Deliver field. +When the source receives the sync request it continues to iterate +and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type deliveryRequestMsgData struct { + Deliver []*syncRequest +} + +func (self *deliveryRequestMsgData) String() string { + return fmt.Sprintf("sync request for new chunks\ndelivery request for %v chunks", len(self.Deliver)) +} + +/* +unsyncedKeys + +is sent first after the handshake if SyncState iterator brings up hundreds, thousands? +and subsequently sent as a response to deliveryRequestMsgData. + +Syncing is the iterative process of exchanging unsyncedKeys and deliveryRequestMsgs +both ways. + +State contains the sync state sent by the source. When the source receives the +sync state it continues to iterate and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type unsyncedKeysMsgData struct { + Unsynced []*syncRequest + State *syncState +} + +func (self *unsyncedKeysMsgData) String() string { + return fmt.Sprintf("sync: keys of %d new chunks (state %v) => synced: %v", len(self.Unsynced), self.State, self.State.Synced) +} + +/* +payment + +is sent when the swap balance is tilted in favour of the remote peer +and in absolute units exceeds the PayAt parameter in the remote peer's profile +*/ + +type paymentMsgData struct { + Units uint // units actually paid for (checked against amount by swap) + Promise *chequebook.Cheque // payment with cheque +} + +func (self *paymentMsgData) String() string { + return fmt.Sprintf("payment for %d units: %v", self.Units, self.Promise) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go new file mode 100644 index 000000000..5e65108d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go @@ -0,0 +1,554 @@ +// 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 network + +/* +bzz implements the swarm wire protocol [bzz] (sister of eth and shh) +the protocol instance is launched on each peer by the network layer if the +bzz protocol handler is registered on the p2p server. + +The bzz protocol component speaks the bzz protocol +* handle the protocol handshake +* register peers in the KΛÐΞMLIΛ table via the hive logistic manager +* dispatch to hive for handling the DHT logic +* encode and decode requests for storage and retrieval +* handle sync protocol messages via the syncer +* talks the SWAP payment protocol (swap accounting is done within NetStore) +*/ + +import ( + "fmt" + "net" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/errs" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + Version = 0 + ProtocolLength = uint64(8) + ProtocolMaxMsgSize = 10 * 1024 * 1024 + NetworkId = 322 +) + +const ( + ErrMsgTooLarge = iota + ErrDecode + ErrInvalidMsgCode + ErrVersionMismatch + ErrNetworkIdMismatch + ErrNoStatusMsg + ErrExtraStatusMsg + ErrSwap + ErrSync + ErrUnwanted +) + +var errorToString = map[int]string{ + ErrMsgTooLarge: "Message too long", + ErrDecode: "Invalid message", + ErrInvalidMsgCode: "Invalid message code", + ErrVersionMismatch: "Protocol version mismatch", + ErrNetworkIdMismatch: "NetworkId mismatch", + ErrNoStatusMsg: "No status message", + ErrExtraStatusMsg: "Extra status message", + ErrSwap: "SWAP error", + ErrSync: "Sync error", + ErrUnwanted: "Unwanted peer", +} + +// bzz represents the swarm wire protocol +// an instance is running on each peer +type bzz struct { + selfID discover.NodeID // peer's node id used in peer advertising in handshake + key storage.Key // baseaddress as storage.Key + storage StorageHandler // handler storage/retrieval related requests coming via the bzz wire protocol + hive *Hive // the logistic manager, peerPool, routing service and peer handler + dbAccess *DbAccess // access to db storage counter and iterator for syncing + requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing + remoteAddr *peerAddr // remote peers address + peer *p2p.Peer // the p2p peer object + rw p2p.MsgReadWriter // messageReadWriter to send messages to + errors *errs.Errors // errors table + backend chequebook.Backend + lastActive time.Time + + swap *swap.Swap // swap instance for the peer connection + swapParams *bzzswap.SwapParams // swap settings both local and remote + swapEnabled bool // flag to enable SWAP (will be set via Caps in handshake) + syncEnabled bool // flag to enable SYNC (will be set via Caps in handshake) + syncer *syncer // syncer instance for the peer connection + syncParams *SyncParams // syncer params + syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter) +} + +// interface type for handler of storage/retrieval related requests coming +// via the bzz wire protocol +// messages: UnsyncedKeys, DeliveryRequest, StoreRequest, RetrieveRequest +type StorageHandler interface { + HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error + HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error + HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) + HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) +} + +/* +main entrypoint, wrappers starting a server that will run the bzz protocol +use this constructor to attach the protocol ("class") to server caps +This is done by node.Node#Register(func(node.ServiceContext) (Service, error)) +Service implements Protocols() which is an array of protocol constructors +at node startup the protocols are initialised +the Dev p2p layer then calls Run(p *p2p.Peer, rw p2p.MsgReadWriter) error +on each peer connection +The Run function of the Bzz protocol class creates a bzz instance +which will represent the peer for the swarm hive and all peer-aware components +*/ +func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) { + + // a single global request db is created for all peer connections + // this is to persist delivery backlog and aid syncronisation + requestDb, err := storage.NewLDBDatabase(sy.RequestDbPath) + if err != nil { + return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err) + } + + return p2p.Protocol{ + Name: "bzz", + Version: Version, + Length: ProtocolLength, + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, p, rw) + }, + }, nil +} + +/* +the main protocol loop that + * does the handshake by exchanging statusMsg + * if peer is valid and accepted, registers with the hive + * then enters into a forever loop handling incoming messages + * storage and retrieval related queries coming via bzz are dispatched to StorageHandler + * peer-related messages are dispatched to the hive + * payment related messages are relayed to SWAP service + * on disconnect, unregister the peer in the hive (note RemovePeer in the post-disconnect hook) + * whenever the loop terminates, the peer will disconnect with Subprotocol error + * whenever handlers return an error the loop terminates +*/ +func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { + + self := &bzz{ + storage: depo, + backend: backend, + hive: hive, + dbAccess: dbaccess, + requestDb: requestDb, + peer: p, + rw: rw, + errors: &errs.Errors{ + Package: "BZZ", + Errors: errorToString, + }, + swapParams: sp, + syncParams: sy, + swapEnabled: hive.swapEnabled, + syncEnabled: true, + } + + // handle handshake + err = self.handleStatus() + if err != nil { + return err + } + defer func() { + // if the handler loop exits, the peer is disconnecting + // deregister the peer in the hive + self.hive.removePeer(&peer{bzz: self}) + if self.syncer != nil { + self.syncer.stop() // quits request db and delivery loops, save requests + } + if self.swap != nil { + self.swap.Stop() // quits chequebox autocash etc + } + }() + + // the main forever loop that handles incoming requests + for { + if self.hive.blockRead { + glog.V(logger.Warn).Infof("Cannot read network") + time.Sleep(100 * time.Millisecond) + continue + } + err = self.handle() + if err != nil { + return + } + } +} + +// TODO: may need to implement protocol drop only? don't want to kick off the peer +// if they are useful for other protocols +func (self *bzz) Drop() { + self.peer.Disconnect(p2p.DiscSubprotocolError) +} + +// one cycle of the main forever loop that handles and dispatches incoming messages +func (self *bzz) handle() error { + msg, err := self.rw.ReadMsg() + glog.V(logger.Debug).Infof("<- %v", msg) + if err != nil { + return err + } + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + // make sure that the payload has been fully consumed + defer msg.Discard() + + switch msg.Code { + + case statusMsg: + // no extra status message allowed. The one needed already handled by + // handleStatus + glog.V(logger.Debug).Infof("Status message: %v", msg) + return self.protoError(ErrExtraStatusMsg, "") + + case storeRequestMsg: + // store requests are dispatched to netStore + var req storeRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + if len(req.SData) < 9 { + return self.protoError(ErrDecode, "<- %v: Data too short (%v)", msg) + } + // last Active time is set only when receiving chunks + self.lastActive = time.Now() + glog.V(logger.Detail).Infof("incoming store request: %s", req.String()) + // swap accounting is done within forwarding + self.storage.HandleStoreRequestMsg(&req, &peer{bzz: self}) + + case retrieveRequestMsg: + // retrieve Requests are dispatched to netStore + var req retrieveRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + // if request is lookup and not to be delivered + if req.isLookup() { + glog.V(logger.Detail).Infof("self lookup for %v: responding with peers only...", req.from) + } else if req.Key == nil { + return self.protoError(ErrDecode, "protocol handler: req.Key == nil || req.Timeout == nil") + } else { + // swap accounting is done within netStore + self.storage.HandleRetrieveRequestMsg(&req, &peer{bzz: self}) + } + // direct response with peers, TODO: sort this out + self.hive.peers(&req) + + case peersMsg: + // response to lookups and immediate response to retrieve requests + // dispatches new peer data to the hive that adds them to KADDB + var req peersMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + glog.V(logger.Detail).Infof("<- peer addresses: %v", req) + self.hive.HandlePeersMsg(&req, &peer{bzz: self}) + + case syncRequestMsg: + var req syncRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- sync request: %v", req) + self.lastActive = time.Now() + self.sync(req.SyncState) + + case unsyncedKeysMsg: + // coming from parent node offering + var req unsyncedKeysMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- unsynced keys : %s", req.String()) + err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case deliveryRequestMsg: + // response to syncKeysMsg hashes filtered not existing in db + // also relays the last synced state to the source + var req deliveryRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<-msg %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- delivery request: %s", req.String()) + err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case paymentMsg: + // swap protocol message for payment, Units paid for, Cheque paid with + if self.swapEnabled { + var req paymentMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- payment: %s", req.String()) + self.swap.Receive(int(req.Units), req.Promise) + } + + default: + // no other message is allowed + return self.protoError(ErrInvalidMsgCode, "%v", msg.Code) + } + return nil +} + +func (self *bzz) handleStatus() (err error) { + + handshake := &statusMsgData{ + Version: uint64(Version), + ID: "honey", + Addr: self.selfAddr(), + NetworkId: uint64(NetworkId), + Swap: &bzzswap.SwapProfile{ + Profile: self.swapParams.Profile, + PayProfile: self.swapParams.PayProfile, + }, + } + + err = p2p.Send(self.rw, statusMsg, handshake) + if err != nil { + self.protoError(ErrNoStatusMsg, err.Error()) + } + + // read and handle remote status + var msg p2p.Msg + msg, err = self.rw.ReadMsg() + if err != nil { + return err + } + + if msg.Code != statusMsg { + self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, statusMsg) + } + + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + + var status statusMsgData + if err := msg.Decode(&status); err != nil { + return self.protoError(ErrDecode, " %v: %v", msg, err) + } + + if status.NetworkId != NetworkId { + return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId) + } + + if Version != status.Version { + return self.protoError(ErrVersionMismatch, "%d (!= %d)", status.Version, Version) + } + + self.remoteAddr = self.peerAddr(status.Addr) + glog.V(logger.Detail).Infof("self: advertised IP: %v, peer advertised: %v, local address: %v\npeer: advertised IP: %v, remote address: %v\n", self.selfAddr(), self.remoteAddr, self.peer.LocalAddr(), status.Addr.IP, self.peer.RemoteAddr()) + + if self.swapEnabled { + // set remote profile for accounting + self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self.backend, self) + if err != nil { + return self.protoError(ErrSwap, "%v", err) + } + } + + glog.V(logger.Info).Infof("Peer %08x is capable (%d/%d)", self.remoteAddr.Addr[:4], status.Version, status.NetworkId) + err = self.hive.addPeer(&peer{bzz: self}) + if err != nil { + return self.protoError(ErrUnwanted, "%v", err) + } + + // hive sets syncstate so sync should start after node added + glog.V(logger.Info).Infof("syncronisation request sent with %v", self.syncState) + self.syncRequest() + + return nil +} + +func (self *bzz) sync(state *syncState) error { + // syncer setup + if self.syncer != nil { + return self.protoError(ErrSync, "sync request can only be sent once") + } + + cnt := self.dbAccess.counter() + remoteaddr := self.remoteAddr.Addr + start, stop := self.hive.kad.KeyRange(remoteaddr) + + // an explicitly received nil syncstate disables syncronisation + if state == nil { + self.syncEnabled = false + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v", self) + state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true} + } else { + state.synced = make(chan bool) + state.SessionAt = cnt + if storage.IsZeroKey(state.Stop) && state.Synced { + state.Start = storage.Key(start[:]) + state.Stop = storage.Key(stop[:]) + } + glog.V(logger.Debug).Infof("syncronisation requested by peer %v at state %v", self, state) + } + var err error + self.syncer, err = newSyncer( + self.requestDb, + storage.Key(remoteaddr[:]), + self.dbAccess, + self.unsyncedKeys, self.store, + self.syncParams, state, func() bool { return self.syncEnabled }, + ) + if err != nil { + return self.protoError(ErrSync, "%v", err) + } + glog.V(logger.Detail).Infof("syncer set for peer %v", self) + return nil +} + +func (self *bzz) String() string { + return self.remoteAddr.String() +} + +// repair reported address if IP missing +func (self *bzz) peerAddr(base *peerAddr) *peerAddr { + if base.IP.IsUnspecified() { + host, _, _ := net.SplitHostPort(self.peer.RemoteAddr().String()) + base.IP = net.ParseIP(host) + } + return base +} + +// returns self advertised node connection info (listening address w enodes) +// IP will get repaired on the other end if missing +// or resolved via ID by discovery at dialout +func (self *bzz) selfAddr() *peerAddr { + id := self.hive.id + host, port, _ := net.SplitHostPort(self.hive.listenAddr()) + intport, _ := strconv.Atoi(port) + addr := &peerAddr{ + Addr: self.hive.addr, + ID: id[:], + IP: net.ParseIP(host), + Port: uint16(intport), + } + return addr +} + +// outgoing messages +// send retrieveRequestMsg +func (self *bzz) retrieve(req *retrieveRequestMsgData) error { + return self.send(retrieveRequestMsg, req) +} + +// send storeRequestMsg +func (self *bzz) store(req *storeRequestMsgData) error { + return self.send(storeRequestMsg, req) +} + +func (self *bzz) syncRequest() error { + req := &syncRequestMsgData{} + if self.hive.syncEnabled { + glog.V(logger.Debug).Infof("syncronisation request to peer %v at state %v", self, self.syncState) + req.SyncState = self.syncState + } + if self.syncState == nil { + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v at state %v", self, self.syncState) + } + return self.send(syncRequestMsg, req) +} + +// queue storeRequestMsg in request db +func (self *bzz) deliveryRequest(reqs []*syncRequest) error { + req := &deliveryRequestMsgData{ + Deliver: reqs, + } + return self.send(deliveryRequestMsg, req) +} + +// batch of syncRequests to send off +func (self *bzz) unsyncedKeys(reqs []*syncRequest, state *syncState) error { + req := &unsyncedKeysMsgData{ + Unsynced: reqs, + State: state, + } + return self.send(unsyncedKeysMsg, req) +} + +// send paymentMsg +func (self *bzz) Pay(units int, promise swap.Promise) { + req := &paymentMsgData{uint(units), promise.(*chequebook.Cheque)} + self.payment(req) +} + +// send paymentMsg +func (self *bzz) payment(req *paymentMsgData) error { + return self.send(paymentMsg, req) +} + +// sends peersMsg +func (self *bzz) peers(req *peersMsgData) error { + return self.send(peersMsg, req) +} + +func (self *bzz) protoError(code int, format string, params ...interface{}) (err *errs.Error) { + err = self.errors.New(code, format, params...) + err.Log(glog.V(logger.Info)) + return +} + +func (self *bzz) protoErrorDisconnect(err *errs.Error) { + err.Log(glog.V(logger.Info)) + if err.Fatal() { + self.peer.Disconnect(p2p.DiscSubprotocolError) + } +} + +func (self *bzz) send(msg uint64, data interface{}) error { + if self.hive.blockWrite { + return fmt.Errorf("network write blocked") + } + glog.V(logger.Detail).Infof("-> %v: %v (%T) to %v", msg, data, data, self) + err := p2p.Send(self.rw, msg, data) + if err != nil { + self.Drop() + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go new file mode 100644 index 000000000..cef32610f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go @@ -0,0 +1,390 @@ +// 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 network + +import ( + "encoding/binary" + "fmt" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const counterKeyPrefix = 0x01 + +/* +syncDb is a queueing service for outgoing deliveries. +One instance per priority queue for each peer + +a syncDb instance maintains an in-memory buffer (of capacity bufferSize) +once its in-memory buffer is full it switches to persisting in db +and dbRead iterator iterates through the items keeping their order +once the db read catches up (there is no more items in the db) then +it switches back to in-memory buffer. + +when syncdb is stopped all items in the buffer are saved to the db +*/ +type syncDb struct { + start []byte // this syncdb starting index in requestdb + key storage.Key // remote peers address key + counterKey []byte // db key to persist counter + priority uint // priotity High|Medium|Low + buffer chan interface{} // incoming request channel + db *storage.LDBDatabase // underlying db (TODO should be interface) + done chan bool // chan to signal goroutines finished quitting + quit chan bool // chan to signal quitting to goroutines + total, dbTotal int // counts for one session + batch chan chan int // channel for batch requests + dbBatchSize uint // number of items before batch is saved +} + +// constructor needs a shared request db (leveldb) +// priority is used in the index key +// uses a buffer and a leveldb for persistent storage +// bufferSize, dbBatchSize are config parameters +func newSyncDb(db *storage.LDBDatabase, key storage.Key, priority uint, bufferSize, dbBatchSize uint, deliver func(interface{}, chan bool) bool) *syncDb { + start := make([]byte, 42) + start[1] = byte(priorities - priority) + copy(start[2:34], key) + + counterKey := make([]byte, 34) + counterKey[0] = counterKeyPrefix + copy(counterKey[1:], start[1:34]) + + syncdb := &syncDb{ + start: start, + key: key, + counterKey: counterKey, + priority: priority, + buffer: make(chan interface{}, bufferSize), + db: db, + done: make(chan bool), + quit: make(chan bool), + batch: make(chan chan int), + dbBatchSize: dbBatchSize, + } + glog.V(logger.Detail).Infof("syncDb[peer: %v, priority: %v] - initialised", key.Log(), priority) + + // starts the main forever loop reading from buffer + go syncdb.bufferRead(deliver) + return syncdb +} + +/* +bufferRead is a forever iterator loop that takes care of delivering +outgoing store requests reads from incoming buffer + +its argument is the deliver function taking the item as first argument +and a quit channel as second. +Closing of this channel is supposed to abort all waiting for delivery +(typically network write) + +The iteration switches between 2 modes, +* buffer mode reads the in-memory buffer and delivers the items directly +* db mode reads from the buffer and writes to the db, parallelly another +routine is started that reads from the db and delivers items + +If there is buffer contention in buffer mode (slow network, high upload volume) +syncdb switches to db mode and starts dbRead +Once db backlog is delivered, it reverts back to in-memory buffer + +It is automatically started when syncdb is initialised. + +It saves the buffer to db upon receiving quit signal. syncDb#stop() +*/ +func (self *syncDb) bufferRead(deliver func(interface{}, chan bool) bool) { + var buffer, db chan interface{} // channels representing the two read modes + var more bool + var req interface{} + var entry *syncDbEntry + var inBatch, inDb int + batch := new(leveldb.Batch) + var dbSize chan int + quit := self.quit + counterValue := make([]byte, 8) + + // counter is used for keeping the items in order, persisted to db + // start counter where db was at, 0 if not found + data, err := self.db.Get(self.counterKey) + var counter uint64 + if err == nil { + counter = binary.BigEndian.Uint64(data) + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter read from db at %v", self.key.Log(), self.priority, counter) + } else { + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter starts at %v", self.key.Log(), self.priority, counter) + } + +LOOP: + for { + // waiting for item next in the buffer, or quit signal or batch request + select { + // buffer only closes when writing to db + case req = <-buffer: + // deliver request : this is blocking on network write so + // it is passed the quit channel as argument, so that it returns + // if syncdb is stopped. In this case we need to save the item to the db + more = deliver(req, self.quit) + if !more { + glog.V(logger.Debug).Infof("syncDb[%v/%v] quit: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // received quit signal, save request currently waiting delivery + // by switching to db mode and closing the buffer + buffer = nil + db = self.buffer + close(db) + quit = nil // needs to block the quit case in select + break // break from select, this item will be written to the db + } + self.total++ + glog.V(logger.Detail).Infof("syncDb[%v/%v] deliver (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // by the time deliver returns, there were new writes to the buffer + // if buffer contention is detected, switch to db mode which drains + // the buffer so no process will block on pushing store requests + if len(buffer) == cap(buffer) { + glog.V(logger.Debug).Infof("syncDb[%v/%v] buffer full %v: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, cap(buffer), self.dbTotal, self.total) + buffer = nil + db = self.buffer + } + continue LOOP + + // incoming entry to put into db + case req, more = <-db: + if !more { + // only if quit is called, saved all the buffer + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) // persist counter in batch + self.writeSyncBatch(batch) // save batch + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save current batch to db", self.key.Log(), self.priority) + break LOOP + } + self.dbTotal++ + self.total++ + // otherwise break after select + case dbSize = <-self.batch: + // explicit request for batch + if inBatch == 0 && quit != nil { + // there was no writes since the last batch so db depleted + // switch to buffer mode + glog.V(logger.Debug).Infof("syncDb[%v/%v] empty db: switching to buffer", self.key.Log(), self.priority) + db = nil + buffer = self.buffer + dbSize <- 0 // indicates to 'caller' that batch has been written + inDb = 0 + continue LOOP + } + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) + glog.V(logger.Debug).Infof("syncDb[%v/%v] write batch %v/%v - %x - %x", self.key.Log(), self.priority, inBatch, counter, self.counterKey, counterValue) + batch = self.writeSyncBatch(batch) + dbSize <- inBatch // indicates to 'caller' that batch has been written + inBatch = 0 + continue LOOP + + // closing syncDb#quit channel is used to signal to all goroutines to quit + case <-quit: + // need to save backlog, so switch to db mode + db = self.buffer + buffer = nil + quit = nil + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save buffer to db", self.key.Log(), self.priority) + close(db) + continue LOOP + } + + // only get here if we put req into db + entry, err = self.newSyncDbEntry(req, counter) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving request %v (#%v/%v) failed: %v", self.key.Log(), self.priority, req, inBatch, inDb, err) + continue LOOP + } + batch.Put(entry.key, entry.val) + glog.V(logger.Detail).Infof("syncDb[%v/%v] to batch %v '%v' (#%v/%v/%v)", self.key.Log(), self.priority, req, entry, inBatch, inDb, counter) + // if just switched to db mode and not quitting, then launch dbRead + // in a parallel go routine to send deliveries from db + if inDb == 0 && quit != nil { + glog.V(logger.Detail).Infof("syncDb[%v/%v] start dbRead") + go self.dbRead(true, counter, deliver) + } + inDb++ + inBatch++ + counter++ + // need to save the batch if it gets too large (== dbBatchSize) + if inBatch%int(self.dbBatchSize) == 0 { + batch = self.writeSyncBatch(batch) + } + } + glog.V(logger.Info).Infof("syncDb[%v:%v]: saved %v keys (saved counter at %v)", self.key.Log(), self.priority, inBatch, counter) + close(self.done) +} + +// writes the batch to the db and returns a new batch object +func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch { + err := self.db.Write(batch) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving batch to db failed: %v", self.key.Log(), self.priority, err) + return batch + } + return new(leveldb.Batch) +} + +// abstract type for db entries (TODO could be a feature of Receipts) +type syncDbEntry struct { + key, val []byte +} + +func (self syncDbEntry) String() string { + return fmt.Sprintf("key: %x, value: %x", self.key, self.val) +} + +/* + dbRead is iterating over store requests to be sent over to the peer + this is mainly to prevent crashes due to network output buffer contention (???) + as well as to make syncronisation resilient to disconnects + the messages are supposed to be sent in the p2p priority queue. + + the request DB is shared between peers, but domains for each syncdb + are disjoint. dbkeys (42 bytes) are structured: + * 0: 0x00 (0x01 reserved for counter key) + * 1: priorities - priority (so that high priority can be replayed first) + * 2-33: peers address + * 34-41: syncdb counter to preserve order (this field is missing for the counter key) + + values (40 bytes) are: + * 0-31: key + * 32-39: request id + +dbRead needs a boolean to indicate if on first round all the historical +record is synced. Second argument to indicate current db counter +The third is the function to apply +*/ +func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}, chan bool) bool) { + key := make([]byte, 42) + copy(key, self.start) + binary.BigEndian.PutUint64(key[34:], counter) + var batches, n, cnt, total int + var more bool + var entry *syncDbEntry + var it iterator.Iterator + var del *leveldb.Batch + batchSizes := make(chan int) + + for { + // if useBatches is false, cnt is not set + if useBatches { + // this could be called before all cnt items sent out + // so that loop is not blocking while delivering + // only relevant if cnt is large + select { + case self.batch <- batchSizes: + case <-self.quit: + return + } + // wait for the write to finish and get the item count in the next batch + cnt = <-batchSizes + batches++ + if cnt == 0 { + // empty + return + } + } + it = self.db.NewIterator() + it.Seek(key) + if !it.Valid() { + copy(key, self.start) + useBatches = true + continue + } + del = new(leveldb.Batch) + glog.V(logger.Detail).Infof("syncDb[%v/%v]: new iterator: %x (batch %v, count %v)", self.key.Log(), self.priority, key, batches, cnt) + + for n = 0; !useBatches || n < cnt; it.Next() { + copy(key, it.Key()) + if len(key) == 0 || key[0] != 0 { + copy(key, self.start) + useBatches = true + break + } + val := make([]byte, 40) + copy(val, it.Value()) + entry = &syncDbEntry{key, val} + // glog.V(logger.Detail).Infof("syncDb[%v/%v] - %v, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, self.key.Log(), batches, total, self.dbTotal, self.total) + more = fun(entry, self.quit) + if !more { + // quit received when waiting to deliver entry, the entry will not be deleted + glog.V(logger.Detail).Infof("syncDb[%v/%v] batch %v quit after %v/%v items", self.key.Log(), self.priority, batches, n, cnt) + break + } + // since subsequent batches of the same db session are indexed incrementally + // deleting earlier batches can be delayed and parallelised + // this could be batch delete when db is idle (but added complexity esp when quitting) + del.Delete(key) + n++ + total++ + } + glog.V(logger.Debug).Infof("syncDb[%v/%v] - db session closed, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, batches, total, self.dbTotal, self.total) + self.db.Write(del) // this could be async called only when db is idle + it.Release() + } +} + +// +func (self *syncDb) stop() { + close(self.quit) + <-self.done +} + +// calculate a dbkey for the request, for the db to work +// see syncdb for db key structure +// polimorphic: accepted types, see syncer#addRequest +func (self *syncDb) newSyncDbEntry(req interface{}, counter uint64) (entry *syncDbEntry, err error) { + var key storage.Key + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + + if key, ok = req.(storage.Key); ok { + id = generateId() + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + id = sreq.Id + } else if entry, ok = req.(*syncDbEntry); !ok { + return nil, fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + // order by peer > priority > seqid + // value is request id if exists + if entry == nil { + dbkey := make([]byte, 42) + dbval := make([]byte, 40) + + // encode key + copy(dbkey[:], self.start[:34]) // db peer + binary.BigEndian.PutUint64(dbkey[34:], counter) + // encode value + copy(dbval, key[:]) + binary.BigEndian.PutUint64(dbval[32:], id) + + entry = &syncDbEntry{dbkey, dbval} + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go new file mode 100644 index 000000000..e871666bd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go @@ -0,0 +1,778 @@ +// 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 network + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "path/filepath" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// syncer parameters (global, not peer specific) default values +const ( + requestDbBatchSize = 512 // size of batch before written to request db + keyBufferSize = 1024 // size of buffer for unsynced keys + syncBatchSize = 128 // maximum batchsize for outgoing requests + syncBufferSize = 128 // size of buffer for delivery requests + syncCacheSize = 1024 // cache capacity to store request queue in memory +) + +// priorities +const ( + Low = iota // 0 + Medium // 1 + High // 2 + priorities // 3 number of priority levels +) + +// request types +const ( + DeliverReq = iota // 0 + PushReq // 1 + PropagateReq // 2 + HistoryReq // 3 + BacklogReq // 4 +) + +// json serialisable struct to record the syncronisation state between 2 peers +type syncState struct { + *storage.DbSyncState // embeds the following 4 fields: + // Start Key // lower limit of address space + // Stop Key // upper limit of address space + // First uint64 // counter taken from last sync state + // Last uint64 // counter of remote peer dbStore at the time of last connection + SessionAt uint64 // set at the time of connection + LastSeenAt uint64 // set at the time of connection + Latest storage.Key // cursor of dbstore when last (continuously set by syncer) + Synced bool // true iff Sync is done up to the last disconnect + synced chan bool // signal that sync stage finished +} + +// wrapper of db-s to provide mockable custom local chunk store access to syncer +type DbAccess struct { + db *storage.DbStore + loc *storage.LocalStore +} + +func NewDbAccess(loc *storage.LocalStore) *DbAccess { + return &DbAccess{loc.DbStore.(*storage.DbStore), loc} +} + +// to obtain the chunks from key or request db entry only +func (self *DbAccess) get(key storage.Key) (*storage.Chunk, error) { + return self.loc.Get(key) +} + +// current storage counter of chunk db +func (self *DbAccess) counter() uint64 { + return self.db.Counter() +} + +// implemented by dbStoreSyncIterator +type keyIterator interface { + Next() storage.Key +} + +// generator function for iteration by address range and storage counter +func (self *DbAccess) iterator(s *syncState) keyIterator { + it, err := self.db.NewSyncIterator(*(s.DbSyncState)) + if err != nil { + return nil + } + return keyIterator(it) +} + +func (self syncState) String() string { + if self.Synced { + return fmt.Sprintf( + "session started at: %v, last seen at: %v, latest key: %v", + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } else { + return fmt.Sprintf( + "address: %v-%v, index: %v-%v, session started at: %v, last seen at: %v, latest key: %v", + self.Start.Log(), self.Stop.Log(), + self.First, self.Last, + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } +} + +// syncer parameters (global, not peer specific) +type SyncParams struct { + RequestDbPath string // path for request db (leveldb) + RequestDbBatchSize uint // nuber of items before batch is saved to requestdb + KeyBufferSize uint // size of key buffer + SyncBatchSize uint // maximum batchsize for outgoing requests + SyncBufferSize uint // size of buffer for + SyncCacheSize uint // cache capacity to store request queue in memory + SyncPriorities []uint // list of priority levels for req types 0-3 + SyncModes []bool // list of sync modes for for req types 0-3 +} + +// constructor with default values +func NewSyncParams(bzzdir string) *SyncParams { + return &SyncParams{ + RequestDbPath: filepath.Join(bzzdir, "requests"), + RequestDbBatchSize: requestDbBatchSize, + KeyBufferSize: keyBufferSize, + SyncBufferSize: syncBufferSize, + SyncBatchSize: syncBatchSize, + SyncCacheSize: syncCacheSize, + SyncPriorities: []uint{High, Medium, Medium, Low, Low}, + SyncModes: []bool{true, true, true, true, false}, + } +} + +// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding +type syncer struct { + *SyncParams // sync parameters + syncF func() bool // if syncing is needed + key storage.Key // remote peers address key + state *syncState // sync state for our dbStore + syncStates chan *syncState // different stages of sync + deliveryRequest chan bool // one of two triggers needed to send unsyncedKeys + newUnsyncedKeys chan bool // one of two triggers needed to send unsynced keys + quit chan bool // signal to quit loops + + // DB related fields + dbAccess *DbAccess // access to dbStore + db *storage.LDBDatabase // delivery msg db + + // native fields + queues [priorities]*syncDb // in-memory cache / queues for sync reqs + keys [priorities]chan interface{} // buffer for unsynced keys + deliveries [priorities]chan *storeRequestMsgData // delivery + + // bzz protocol instance outgoing message callbacks (mockable for testing) + unsyncedKeys func([]*syncRequest, *syncState) error // send unsyncedKeysMsg + store func(*storeRequestMsgData) error // send storeRequestMsg +} + +// a syncer instance is linked to each peer connection +// constructor is called from protocol after successful handshake +// the returned instance is attached to the peer and can be called +// by the forwarder +func newSyncer( + db *storage.LDBDatabase, remotekey storage.Key, + dbAccess *DbAccess, + unsyncedKeys func([]*syncRequest, *syncState) error, + store func(*storeRequestMsgData) error, + params *SyncParams, + state *syncState, + syncF func() bool, +) (*syncer, error) { + + syncBufferSize := params.SyncBufferSize + keyBufferSize := params.KeyBufferSize + dbBatchSize := params.RequestDbBatchSize + + self := &syncer{ + syncF: syncF, + key: remotekey, + dbAccess: dbAccess, + syncStates: make(chan *syncState, 20), + deliveryRequest: make(chan bool, 1), + newUnsyncedKeys: make(chan bool, 1), + SyncParams: params, + state: state, + quit: make(chan bool), + unsyncedKeys: unsyncedKeys, + store: store, + } + + // initialising + for i := 0; i < priorities; i++ { + self.keys[i] = make(chan interface{}, keyBufferSize) + self.deliveries[i] = make(chan *storeRequestMsgData) + // initialise a syncdb instance for each priority queue + self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i))) + } + glog.V(logger.Info).Infof("syncer started: %v", state) + // launch chunk delivery service + go self.syncDeliveries() + // launch sync task manager + if self.syncF() { + go self.sync() + } + // process unsynced keys to broadcast + go self.syncUnsyncedKeys() + + return self, nil +} + +// metadata serialisation +func encodeSync(state *syncState) (*json.RawMessage, error) { + data, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + meta := json.RawMessage(data) + return &meta, nil +} + +func decodeSync(meta *json.RawMessage) (*syncState, error) { + if meta == nil { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + data := []byte(*(meta)) + if len(data) == 0 { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + state := &syncState{DbSyncState: &storage.DbSyncState{}} + err := json.Unmarshal(data, state) + return state, err +} + +/* + sync implements the syncing script + * first all items left in the request Db are replayed + * type = StaleSync + * Mode: by default once again via confirmation roundtrip + * Priority: the items are replayed as the proirity specified for StaleSync + * but within the order respects earlier priority level of request + * after all items are consumed for a priority level, the the respective + queue for delivery requests is open (this way new reqs not written to db) + (TODO: this should be checked) + * the sync state provided by the remote peer is used to sync history + * all the backlog from earlier (aborted) syncing is completed starting from latest + * if Last < LastSeenAt then all items in between then process all + backlog from upto last disconnect + * if Last > 0 && + + sync is called from the syncer constructor and is not supposed to be used externally +*/ +func (self *syncer) sync() { + state := self.state + // sync finished + defer close(self.syncStates) + + // 0. first replay stale requests from request db + if state.SessionAt == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: nothing to sync", self.key.Log()) + return + } + glog.V(logger.Debug).Infof("syncer[%v]: start replaying stale requests from request db", self.key.Log()) + for p := priorities - 1; p >= 0; p-- { + self.queues[p].dbRead(false, 0, self.replay()) + } + glog.V(logger.Debug).Infof("syncer[%v]: done replaying stale requests from request db", self.key.Log()) + + // unless peer is synced sync unfinished history beginning on + if !state.Synced { + start := state.Start + + if !storage.IsZeroKey(state.Latest) { + // 1. there is unfinished earlier sync + state.Start = state.Latest + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising backlog (unfinished sync: %v)", self.key.Log(), state) + // blocks while the entire history upto state is synced + self.syncState(state) + if state.Last < state.SessionAt { + state.First = state.Last + 1 + } + } + state.Latest = storage.ZeroKey + state.Start = start + // 2. sync up to last disconnect1 + if state.First < state.LastSeenAt { + state.Last = state.LastSeenAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history upto last disconnect at %v: %v", self.key.Log(), state.LastSeenAt, state) + self.syncState(state) + state.First = state.LastSeenAt + } + state.Latest = storage.ZeroKey + + } else { + // synchronisation starts at end of last session + state.First = state.LastSeenAt + } + + // 3. sync up to current session start + // if there have been new chunks since last session + if state.LastSeenAt < state.SessionAt { + state.Last = state.SessionAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history since last disconnect at %v up until session start at %v: %v", self.key.Log(), state.LastSeenAt, state.SessionAt, state) + // blocks until state syncing is finished + self.syncState(state) + } + glog.V(logger.Info).Infof("syncer[%v]: syncing all history complete", self.key.Log()) + +} + +// wait till syncronised block uptil state is synced +func (self *syncer) syncState(state *syncState) { + self.syncStates <- state + select { + case <-state.synced: + case <-self.quit: + } +} + +// stop quits both request processor and saves the request cache to disk +func (self *syncer) stop() { + close(self.quit) + glog.V(logger.Detail).Infof("syncer[%v]: stop and save sync request db backlog", self.key.Log()) + for _, db := range self.queues { + db.stop() + } +} + +// rlp serialisable sync request +type syncRequest struct { + Key storage.Key + Priority uint +} + +func (self *syncRequest) String() string { + return fmt.Sprintf("", self.Key.Log(), self.Priority) +} + +func (self *syncer) newSyncRequest(req interface{}, p int) (*syncRequest, error) { + key, _, _, _, err := parseRequest(req) + // TODO: if req has chunk, it should be put in a cache + // create + if err != nil { + return nil, err + } + return &syncRequest{key, uint(p)}, nil +} + +// serves historical items from the DB +// * read is on demand, blocking unless history channel is read +// * accepts sync requests (syncStates) to create new db iterator +// * closes the channel one iteration finishes +func (self *syncer) syncHistory(state *syncState) chan interface{} { + var n uint + history := make(chan interface{}) + glog.V(logger.Debug).Infof("syncer[%v]: syncing history between %v - %v for chunk addresses %v - %v", self.key.Log(), state.First, state.Last, state.Start, state.Stop) + it := self.dbAccess.iterator(state) + if it != nil { + go func() { + // signal end of the iteration ended + defer close(history) + IT: + for { + key := it.Next() + if key == nil { + break IT + } + select { + // blocking until history channel is read from + case history <- storage.Key(key): + n++ + glog.V(logger.Detail).Infof("syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n) + state.Latest = key + case <-self.quit: + return + } + } + glog.V(logger.Debug).Infof("syncer[%v]: finished syncing history between %v - %v for chunk addresses %v - %v (at %v) (chunks = %v)", self.key.Log(), state.First, state.Last, state.Start, state.Stop, state.Latest, n) + }() + } + return history +} + +// triggers key syncronisation +func (self *syncer) sendUnsyncedKeys() { + select { + case self.deliveryRequest <- true: + default: + } +} + +// assembles a new batch of unsynced keys +// * keys are drawn from the key buffers in order of priority queue +// * if the queues of priority for History (HistoryReq) or higher are depleted, +// historical data is used so historical items are lower priority within +// their priority group. +// * Order of historical data is unspecified +func (self *syncer) syncUnsyncedKeys() { + // send out new + var unsynced []*syncRequest + var more, justSynced bool + var keyCount, historyCnt int + var history chan interface{} + + priority := High + keys := self.keys[priority] + var newUnsyncedKeys, deliveryRequest chan bool + keyCounts := make([]int, priorities) + histPrior := self.SyncPriorities[HistoryReq] + syncStates := self.syncStates + state := self.state + +LOOP: + for { + + var req interface{} + // select the highest priority channel to read from + // keys channels are buffered so the highest priority ones + // are checked first - integrity can only be guaranteed if writing + // is locked while selecting + if priority != High || len(keys) == 0 { + // selection is not needed if the High priority queue has items + keys = nil + PRIORITIES: + for priority = High; priority >= 0; priority-- { + // the first priority channel that is non-empty will be assigned to keys + if len(self.keys[priority]) > 0 { + glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority) + keys = self.keys[priority] + break PRIORITIES + } + glog.V(logger.Detail).Infof("syncer[%v/%v]: queue: [%v, %v, %v]", self.key.Log(), priority, len(self.keys[High]), len(self.keys[Medium]), len(self.keys[Low])) + // if the input queue is empty on this level, resort to history if there is any + if uint(priority) == histPrior && history != nil { + glog.V(logger.Detail).Infof("syncer[%v]: reading history for %v", self.key.Log(), self.key) + keys = history + break PRIORITIES + } + } + } + + // if peer ready to receive but nothing to send + if keys == nil && deliveryRequest == nil { + // if no items left and switch to waiting mode + glog.V(logger.Detail).Infof("syncer[%v]: buffers consumed. Waiting", self.key.Log()) + newUnsyncedKeys = self.newUnsyncedKeys + } + + // send msg iff + // * peer is ready to receive keys AND ( + // * all queues and history are depleted OR + // * batch full OR + // * all history have been consumed, synced) + if deliveryRequest == nil && + (justSynced || + len(unsynced) > 0 && keys == nil || + len(unsynced) == int(self.SyncBatchSize)) { + justSynced = false + // listen to requests + deliveryRequest = self.deliveryRequest + newUnsyncedKeys = nil // not care about data until next req comes in + // set sync to current counter + // (all nonhistorical outgoing traffic sheduled and persisted + state.LastSeenAt = self.dbAccess.counter() + state.Latest = storage.ZeroKey + glog.V(logger.Detail).Infof("syncer[%v]: sending %v", self.key.Log(), unsynced) + // send the unsynced keys + stateCopy := *state + err := self.unsyncedKeys(unsynced, &stateCopy) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: unable to send unsynced keys: %v", err) + } + self.state = state + glog.V(logger.Debug).Infof("syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy) + unsynced = nil + keys = nil + } + + // process item and add it to the batch + select { + case <-self.quit: + break LOOP + case req, more = <-keys: + if keys == history && !more { + glog.V(logger.Detail).Infof("syncer[%v]: syncing history segment complete", self.key.Log()) + // history channel is closed, waiting for new state (called from sync()) + syncStates = self.syncStates + state.Synced = true // this signals that the current segment is complete + select { + case state.synced <- false: + case <-self.quit: + break LOOP + } + justSynced = true + history = nil + } + case <-deliveryRequest: + glog.V(logger.Detail).Infof("syncer[%v]: peer ready to receive", self.key.Log()) + + // this 1 cap channel can wake up the loop + // signaling that peer is ready to receive unsynced Keys + // the channel is set to nil any further writes will be ignored + deliveryRequest = nil + + case <-newUnsyncedKeys: + glog.V(logger.Detail).Infof("syncer[%v]: new unsynced keys available", self.key.Log()) + // this 1 cap channel can wake up the loop + // signals that data is available to send if peer is ready to receive + newUnsyncedKeys = nil + keys = self.keys[High] + + case state, more = <-syncStates: + // this resets the state + if !more { + state = self.state + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing complete upto %v)", self.key.Log(), priority, state) + state.Synced = true + syncStates = nil + } else { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing history upto %v priority %v)", self.key.Log(), priority, state, histPrior) + state.Synced = false + history = self.syncHistory(state) + // only one history at a time, only allow another one once the + // history channel is closed + syncStates = nil + } + } + if req == nil { + continue LOOP + } + + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) added to unsynced keys: %v", self.key.Log(), priority, req) + keyCounts[priority]++ + keyCount++ + if keys == history { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) history item %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + historyCnt++ + } + if sreq, err := self.newSyncRequest(req, priority); err == nil { + // extract key from req + glog.V(logger.Detail).Infof("syncer(priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + unsynced = append(unsynced, sreq) + } else { + glog.V(logger.Warn).Infof("syncer(priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err) + } + + } +} + +// delivery loop +// takes into account priority, send store Requests with chunk (delivery) +// idle blocking if no new deliveries in any of the queues +func (self *syncer) syncDeliveries() { + var req *storeRequestMsgData + p := High + var deliveries chan *storeRequestMsgData + var msg *storeRequestMsgData + var err error + var c = [priorities]int{} + var n = [priorities]int{} + var total, success uint + + for { + deliveries = self.deliveries[p] + select { + case req = <-deliveries: + n[p]++ + c[p]++ + default: + if p == Low { + // blocking, depletion on all channels, no preference for priority + select { + case req = <-self.deliveries[High]: + n[High]++ + case req = <-self.deliveries[Medium]: + n[Medium]++ + case req = <-self.deliveries[Low]: + n[Low]++ + case <-self.quit: + return + } + p = High + } else { + p-- + continue + } + } + total++ + msg, err = self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to create store request for %v: %v", self.key.Log(), req, err) + } else { + err = self.store(msg) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to deliver %v: %v", self.key.Log(), req, err) + } else { + success++ + glog.V(logger.Detail).Infof("syncer[%v]: %v successfully delivered", self.key.Log(), req) + } + } + if total%self.SyncBatchSize == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: deliver Total: %v, Success: %v, High: %v/%v, Medium: %v/%v, Low %v/%v", self.key.Log(), total, success, c[High], n[High], c[Medium], n[Medium], c[Low], n[Low]) + } + } +} + +/* + addRequest handles requests for delivery + it accepts 4 types: + + * storeRequestMsgData: coming from netstore propagate response + * chunk: coming from forwarding (questionable: id?) + * key: from incoming syncRequest + * syncDbEntry: key,id encoded in db + + If sync mode is on for the type of request, then + it sends the request to the keys queue of the correct priority + channel buffered with capacity (SyncBufferSize) + + If sync mode is off then, requests are directly sent to deliveries +*/ +func (self *syncer) addRequest(req interface{}, ty int) { + // retrieve priority for request type name int8 + + priority := self.SyncPriorities[ty] + // sync mode for this type ON + if self.syncF() || ty == DeliverReq { + if self.SyncModes[ty] { + self.addKey(req, priority, self.quit) + } else { + self.addDelivery(req, priority, self.quit) + } + } +} + +// addKey queues sync request for sync confirmation with given priority +// ie the key will go out in an unsyncedKeys message +func (self *syncer) addKey(req interface{}, priority uint, quit chan bool) bool { + select { + case self.keys[priority] <- req: + // this wakes up the unsynced keys loop if idle + select { + case self.newUnsyncedKeys <- true: + default: + } + return true + case <-quit: + return false + } +} + +// addDelivery queues delivery request for with given priority +// ie the chunk will be delivered ASAP mod priority queueing handled by syncdb +// requests are persisted across sessions for correct sync +func (self *syncer) addDelivery(req interface{}, priority uint, quit chan bool) bool { + select { + case self.queues[priority].buffer <- req: + return true + case <-quit: + return false + } +} + +// doDelivery delivers the chunk for the request with given priority +// without queuing +func (self *syncer) doDelivery(req interface{}, priority uint, quit chan bool) bool { + msgdata, err := self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("unable to deliver request %v: %v", msgdata, err) + return false + } + select { + case self.deliveries[priority] <- msgdata: + return true + case <-quit: + return false + } +} + +// returns the delivery function for given priority +// passed on to syncDb +func (self *syncer) deliver(priority uint) func(req interface{}, quit chan bool) bool { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } +} + +// returns the replay function passed on to syncDb +// depending on sync mode settings for BacklogReq, +// re play of request db backlog sends items via confirmation +// or directly delivers +func (self *syncer) replay() func(req interface{}, quit chan bool) bool { + sync := self.SyncModes[BacklogReq] + priority := self.SyncPriorities[BacklogReq] + // sync mode for this type ON + if sync { + return func(req interface{}, quit chan bool) bool { + return self.addKey(req, priority, quit) + } + } else { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } + + } +} + +// given a request, extends it to a full storeRequestMsgData +// polimorphic: see addRequest for the types accepted +func (self *syncer) newStoreRequestMsgData(req interface{}) (*storeRequestMsgData, error) { + + key, id, chunk, sreq, err := parseRequest(req) + if err != nil { + return nil, err + } + + if sreq == nil { + if chunk == nil { + var err error + chunk, err = self.dbAccess.get(key) + if err != nil { + return nil, err + } + } + + sreq = &storeRequestMsgData{ + Id: id, + Key: chunk.Key, + SData: chunk.SData, + } + } + + return sreq, nil +} + +// parse request types and extracts, key, id, chunk, request if available +// does not do chunk lookup ! +func parseRequest(req interface{}) (storage.Key, uint64, *storage.Chunk, *storeRequestMsgData, error) { + var key storage.Key + var entry *syncDbEntry + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + var err error + + if key, ok = req.(storage.Key); ok { + id = generateId() + + } else if entry, ok = req.(*syncDbEntry); ok { + id = binary.BigEndian.Uint64(entry.val[32:]) + key = storage.Key(entry.val[:32]) + + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + } else { + err = fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + return key, id, chunk, sreq, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go new file mode 100644 index 000000000..f72036d72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go @@ -0,0 +1,283 @@ +// 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 swap + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "os" + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// SwAP Swarm Accounting Protocol with +// SWAP^2 Strategies of Withholding Automatic Payments +// SWAP^3 Accreditation: payment via credit SWAP +// using chequebook pkg for delayed payments +// default parameters + +var ( + autoCashInterval = 300 * time.Second // default interval for autocash + autoCashThreshold = big.NewInt(50000000000000) // threshold that triggers autocash (wei) + autoDepositInterval = 300 * time.Second // default interval for autocash + autoDepositThreshold = big.NewInt(50000000000000) // threshold that triggers autodeposit (wei) + autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei) + buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei) + sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei) + payAt = 100 // threshold that triggers payment {request} (units) + dropAt = 10000 // threshold that triggers disconnect (units) +) + +const ( + chequebookDeployRetries = 5 + chequebookDeployDelay = 1 * time.Second // delay between retries +) + +type SwapParams struct { + *swap.Params + *PayProfile +} + +type SwapProfile struct { + *swap.Profile + *PayProfile +} + +type PayProfile struct { + PublicKey string // check against signature of promise + Contract common.Address // address of chequebook contract + Beneficiary common.Address // recipient address for swarm sales revenue + privateKey *ecdsa.PrivateKey + publicKey *ecdsa.PublicKey + owner common.Address + chbook *chequebook.Chequebook + lock sync.RWMutex +} + +func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams { + pubkey := &prvkey.PublicKey + return &SwapParams{ + PayProfile: &PayProfile{ + PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), + Contract: contract, + Beneficiary: crypto.PubkeyToAddress(*pubkey), + privateKey: prvkey, + publicKey: pubkey, + owner: crypto.PubkeyToAddress(*pubkey), + }, + Params: &swap.Params{ + Profile: &swap.Profile{ + BuyAt: buyAt, + SellAt: sellAt, + PayAt: uint(payAt), + DropAt: uint(dropAt), + }, + Strategy: &swap.Strategy{ + AutoCashInterval: autoCashInterval, + AutoCashThreshold: autoCashThreshold, + AutoDepositInterval: autoDepositInterval, + AutoDepositThreshold: autoDepositThreshold, + AutoDepositBuffer: autoDepositBuffer, + }, + }, + } +} + +// swap constructor, parameters +// * global chequebook, assume deployed service and +// * the balance is at buffer. +// swap.Add(n) called in netstore +// n > 0 called when sending chunks = receiving retrieve requests +// OR sending cheques. +// n < 0 called when receiving chunks = receiving delivery responses +// OR receiving cheques. + +func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend, proto swap.Protocol) (self *swap.Swap, err error) { + var ( + ctx = context.TODO() + ok bool + in *chequebook.Inbox + out *chequebook.Outbox + ) + + // check if remote chequebook is valid + // insolvent chequebooks suicide so will signal as invalid + // TODO: monitoring a chequebooks events + ok, err = chequebook.ValidateCode(ctx, backend, remote.Contract) + if !ok { + glog.V(logger.Info).Infof("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } else { + // remote contract valid, create inbox + in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } + } + + // check if local chequebook contract is valid + ok, err = chequebook.ValidateCode(ctx, backend, local.Contract) + if !ok { + glog.V(logger.Warn).Infof("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err) + } else { + out = chequebook.NewOutbox(local.Chequebook(), remote.Beneficiary) + } + + pm := swap.Payment{ + In: in, + Out: out, + Buys: out != nil, + Sells: in != nil, + } + self, err = swap.New(local.Params, pm, proto) + if err != nil { + return + } + // remote profile given (first) in handshake + self.SetRemote(remote.Profile) + var buy, sell string + if self.Buys { + buy = "purchase from peer enabled at " + remote.SellAt.String() + " wei/chunk" + } else { + buy = "purchase from peer disabled" + } + if self.Sells { + sell = "selling to peer enabled at " + local.SellAt.String() + " wei/chunk" + } else { + sell = "selling to peer disabled" + } + glog.V(logger.Warn).Infof("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell) + + return +} + +func (self *SwapParams) Chequebook() *chequebook.Chequebook { + defer self.lock.Unlock() + self.lock.Lock() + return self.chbook +} + +func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey { + return self.privateKey +} + +// func (self *SwapParams) PublicKey() *ecdsa.PublicKey { +// return self.publicKey +// } + +func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) { + self.privateKey = prvkey + self.publicKey = &prvkey.PublicKey +} + +// setChequebook(path, backend) wraps the +// chequebook initialiser and sets up autoDeposit to cover spending. +func (self *SwapParams) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + self.lock.Lock() + contract := self.Contract + self.lock.Unlock() + + valid, err := chequebook.ValidateCode(ctx, backend, contract) + if err != nil { + return err + } else if valid { + return self.newChequebookFromContract(path, backend) + } + return self.deployChequebook(ctx, backend, path) +} + +func (self *SwapParams) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + opts := bind.NewKeyedTransactor(self.privateKey) + opts.Value = self.AutoDepositBuffer + opts.Context = ctx + + glog.V(logger.Info).Infof("Deploying new chequebook (owner: %v)", opts.From.Hex()) + contract, err := deployChequebookLoop(opts, backend) + if err != nil { + glog.V(logger.Error).Infof("unable to deploy new chequebook: %v", err) + return err + } + glog.V(logger.Info).Infof("new chequebook deployed at %v (owner: %v)", contract.Hex(), opts.From.Hex()) + + // need to save config at this point + self.lock.Lock() + self.Contract = contract + err = self.newChequebookFromContract(path, backend) + self.lock.Unlock() + if err != nil { + glog.V(logger.Warn).Infof("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err) + } + return err +} + +// repeatedly tries to deploy a chequebook. +func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { + var tx *types.Transaction + for try := 0; try < chequebookDeployRetries; try++ { + if try > 0 { + time.Sleep(chequebookDeployDelay) + } + if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { + glog.V(logger.Warn).Infof("can't send chequebook deploy tx (try %d): %v", try, err) + continue + } + if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { + glog.V(logger.Warn).Infof("chequebook deploy error (try %d): %v", try, err) + continue + } + return addr, nil + } + return addr, err +} + +// initialise the chequebook from a persisted json file or create a new one +// caller holds the lock +func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error { + hexkey := common.Bytes2Hex(self.Contract.Bytes()) + err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) + if err != nil { + return fmt.Errorf("unable to create directory for chequebooks: %v", err) + } + + chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") + self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) + + if err != nil { + self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + } + } + + self.chbook.AutoDeposit(self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + glog.V(logger.Info).Infof("auto deposit ON for %v -> %v: interval = %v, threshold = %v, buffer = %v)", crypto.PubkeyToAddress(*(self.publicKey)).Hex()[:8], self.Contract.Hex()[:8], self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go new file mode 100644 index 000000000..9d5da7c3a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go @@ -0,0 +1,254 @@ +// 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 swap + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// SwAP Swarm Accounting Protocol with +// Swift Automatic Payments +// a peer to peer micropayment system + +// public swap profile +// public parameters for SWAP, serializable config struct passed in handshake +type Profile struct { + BuyAt *big.Int // accepted max price for chunk + SellAt *big.Int // offered sale price for chunk + PayAt uint // threshold that triggers payment request + DropAt uint // threshold that triggers disconnect +} + +// Strategy encapsulates parameters relating to +// automatic deposit and automatic cashing +type Strategy struct { + AutoCashInterval time.Duration // default interval for autocash + AutoCashThreshold *big.Int // threshold that triggers autocash (wei) + AutoDepositInterval time.Duration // default interval for autocash + AutoDepositThreshold *big.Int // threshold that triggers autodeposit (wei) + AutoDepositBuffer *big.Int // buffer that is surplus for fork protection etc (wei) +} + +// Params extends the public profile with private parameters relating to +// automatic deposit and automatic cashing +type Params struct { + *Profile + *Strategy +} + +// Promise +// 3rd party Provable Promise of Payment +// issued by outPayment +// serialisable to send with Protocol +type Promise interface{} + +// interface for the peer protocol for testing or external alternative payment +type Protocol interface { + Pay(int, Promise) // units, payment proof + Drop() + String() string +} + +// interface for the (delayed) ougoing payment system with autodeposit +type OutPayment interface { + Issue(amount *big.Int) (promise Promise, err error) + AutoDeposit(interval time.Duration, threshold, buffer *big.Int) + Stop() +} + +// interface for the (delayed) incoming payment system with autocash +type InPayment interface { + Receive(promise Promise) (*big.Int, error) + AutoCash(cashInterval time.Duration, maxUncashed *big.Int) + Stop() +} + +// swap is the swarm accounting protocol instance +// * pairwise accounting and payments +type Swap struct { + lock sync.Mutex // mutex for balance access + balance int // units of chunk/retrieval request + local *Params // local peer's swap parameters + remote *Profile // remote peer's swap profile + proto Protocol // peer communication protocol + Payment +} + +type Payment struct { + Out OutPayment // outgoing payment handler + In InPayment // incoming payment handler + Buys, Sells bool +} + +// swap constructor +func New(local *Params, pm Payment, proto Protocol) (self *Swap, err error) { + + self = &Swap{ + local: local, + Payment: pm, + proto: proto, + } + + self.SetParams(local) + + return +} + +// entry point for setting remote swap profile (e.g from handshake or other message) +func (self *Swap) SetRemote(remote *Profile) { + defer self.lock.Unlock() + self.lock.Lock() + + self.remote = remote + if self.Sells && (remote.BuyAt.Cmp(common.Big0) <= 0 || self.local.SellAt.Cmp(common.Big0) <= 0 || remote.BuyAt.Cmp(self.local.SellAt) < 0) { + self.Out.Stop() + self.Sells = false + } + if self.Buys && (remote.SellAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(self.remote.SellAt) < 0) { + self.In.Stop() + self.Buys = false + } + + glog.V(logger.Debug).Infof("<%v> remote profile set: pay at: %v, drop at: %v, buy at: %v, sell at: %v", self.proto, remote.PayAt, remote.DropAt, remote.BuyAt, remote.SellAt) + +} + +// to set strategy dynamically +func (self *Swap) SetParams(local *Params) { + defer self.lock.Unlock() + self.lock.Lock() + self.local = local + self.setParams(local) +} + +// caller holds the lock + +func (self *Swap) setParams(local *Params) { + + if self.Sells { + self.In.AutoCash(local.AutoCashInterval, local.AutoCashThreshold) + glog.V(logger.Info).Infof("<%v> set autocash to every %v, max uncashed limit: %v", self.proto, local.AutoCashInterval, local.AutoCashThreshold) + } else { + glog.V(logger.Info).Infof("<%v> autocash off (not selling)", self.proto) + } + if self.Buys { + self.Out.AutoDeposit(local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + glog.V(logger.Info).Infof("<%v> set autodeposit to every %v, pay at: %v, buffer: %v", self.proto, local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + } else { + glog.V(logger.Info).Infof("<%v> autodeposit off (not buying)", self.proto) + } +} + +// Add(n) +// n > 0 called when promised/provided n units of service +// n < 0 called when used/requested n units of service +func (self *Swap) Add(n int) error { + defer self.lock.Unlock() + self.lock.Lock() + self.balance += n + if !self.Sells && self.balance > 0 { + glog.V(logger.Detail).Infof("<%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + } + if !self.Buys && self.balance < 0 { + glog.V(logger.Detail).Infof("<%v> we cannot have debt (balance: %v)", self.proto, self.balance) + return fmt.Errorf("[SWAP] <%v> we cannot have debt (balance: %v)", self.proto, self.balance) + } + if self.balance >= int(self.local.DropAt) { + glog.V(logger.Detail).Infof("<%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + } else if self.balance <= -int(self.remote.PayAt) { + self.send() + } + return nil +} + +func (self *Swap) Balance() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.balance +} + +// send(units) is called when payment is due +// In case of insolvency no promise is issued and sent, safe against fraud +// No return value: no error = payment is opportunistic = hang in till dropped +func (self *Swap) send() { + if self.local.BuyAt != nil && self.balance < 0 { + amount := big.NewInt(int64(-self.balance)) + amount.Mul(amount, self.remote.SellAt) + promise, err := self.Out.Issue(amount) + if err != nil { + glog.V(logger.Warn).Infof("<%v> cannot issue cheque (amount: %v, channel: %v): %v", self.proto, amount, self.Out, err) + } else { + glog.V(logger.Warn).Infof("<%v> cheque issued (amount: %v, channel: %v)", self.proto, amount, self.Out) + self.proto.Pay(-self.balance, promise) + self.balance = 0 + } + } +} + +// receive(units, promise) is called by the protocol when a payment msg is received +// returns error if promise is invalid. +func (self *Swap) Receive(units int, promise Promise) error { + if units <= 0 { + return fmt.Errorf("invalid units: %v <= 0", units) + } + + price := new(big.Int).SetInt64(int64(units)) + price.Mul(price, self.local.SellAt) + + amount, err := self.In.Receive(promise) + + if err != nil { + err = fmt.Errorf("invalid promise: %v", err) + } else if price.Cmp(amount) != 0 { + // verify amount = units * unit sale price + return fmt.Errorf("invalid amount: %v = %v * %v (units sent in msg * agreed sale unit price) != %v (signed in cheque)", price, units, self.local.SellAt, amount) + } + if err != nil { + glog.V(logger.Detail).Infof("<%v> invalid promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, err) + return err + } + + // credit remote peer with units + self.Add(-units) + glog.V(logger.Detail).Infof("<%v> received promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, promise) + + return nil +} + +// stop() causes autocash loop to terminate. +// Called after protocol handle loop terminates. +func (self *Swap) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.Buys { + self.Out.Stop() + } + if self.Sells { + self.In.Stop() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go new file mode 100644 index 000000000..4c8551da9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go @@ -0,0 +1,509 @@ +// 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 storage + +import ( + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "sync" + // "github.com/ethereum/go-ethereum/logger" + // "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +The distributed storage implemented in this package requires fix sized chunks of content. + +Chunker is the interface to a component that is responsible for disassembling and assembling larger data. + +TreeChunker implements a Chunker based on a tree structure defined as follows: + +1 each node in the tree including the root and other branching nodes are stored as a chunk. + +2 branching nodes encode data contents that includes the size of the dataslice covered by its entire subtree under the node as well as the hash keys of all its children : +data_{i} := size(subtree_{i}) || key_{j} || key_{j+1} .... || key_{j+n-1} + +3 Leaf nodes encode an actual subslice of the input data. + +4 if data size is not more than maximum chunksize, the data is stored in a single chunk + key = hash(int64(size) + data) + +5 if data size is more than chunksize*branches^l, but no more than chunksize* + branches^(l+1), the data vector is split into slices of chunksize* + branches^l length (except the last one). + key = hash(int64(size) + key(slice0) + key(slice1) + ...) + + The underlying hash function is configurable +*/ + +const ( + defaultHash = "SHA3" // http://golang.org/pkg/hash/#Hash + // defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash + defaultBranches int64 = 128 + // hashSize int64 = hasherfunc.New().Size() // hasher knows about its own length in bytes + // chunksize int64 = branches * hashSize // chunk is defined as this +) + +/* +Tree chunker is a concrete implementation of data chunking. +This chunker works in a simple way, it builds a tree out of the document so that each node either represents a chunk of real data or a chunk of data representing an branching non-leaf node of the tree. In particular each such non-leaf chunk will represent is a concatenation of the hash of its respective children. This scheme simultaneously guarantees data integrity as well as self addressing. Abstract nodes are transparent since their represented size component is strictly greater than their maximum data size, since they encode a subtree. + +If all is well it is possible to implement this by simply composing readers so that no extra allocation or buffering is necessary for the data splitting and joining. This means that in principle there can be direct IO between : memory, file system, network socket (bzz peers storage request is read from the socket). In practice there may be need for several stages of internal buffering. +The hashing itself does use extra copies and allocation though, since it does need it. +*/ + +type ChunkerParams struct { + Branches int64 + Hash string +} + +func NewChunkerParams() *ChunkerParams { + return &ChunkerParams{ + Branches: defaultBranches, + Hash: defaultHash, + } +} + +type TreeChunker struct { + branches int64 + hashFunc Hasher + // calculated + hashSize int64 // self.hashFunc.New().Size() + chunkSize int64 // hashSize* branches + workerCount int +} + +func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) { + self = &TreeChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +// func (self *TreeChunker) KeySize() int64 { +// return self.hashSize +// } + +// String() for pretty printing +func (self *Chunk) String() string { + return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData)) +} + +type hashJob struct { + key Key + chunk []byte + size int64 + parentWg *sync.WaitGroup +} + +func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + if self.chunkSize <= 0 { + panic("chunker must be initialised") + } + + jobC := make(chan *hashJob, 2*processors) + wg := &sync.WaitGroup{} + errC := make(chan error) + + // wwg = workers waitgroup keeps track of hashworkers spawned by this split call + if wwg != nil { + wwg.Add(1) + } + go self.hashWorker(jobC, chunkC, errC, swg, wwg) + + depth := 0 + treeSize := self.chunkSize + + // takes lowest depth such that chunksize*HashCount^(depth+1) > size + // power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree. + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + + key := make([]byte, self.hashFunc().Size()) + // glog.V(logger.Detail).Infof("split request received for data (%v bytes, depth: %v)", size, depth) + // this waitgroup member is released after the root hash is calculated + wg.Add(1) + //launch actual recursive function passing the waitgroups + go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, wg, swg, wwg) + + // closes internal error channel if all subprocesses in the workgroup finished + go func() { + // waiting for all threads to finish + wg.Wait() + // if storage waitgroup is non-nil, we wait for storage to finish too + if swg != nil { + // glog.V(logger.Detail).Infof("Waiting for storage to finish") + swg.Wait() + } + close(errC) + }() + + select { + case err := <-errC: + if err != nil { + return nil, err + } + // + } + return key, nil +} + +func (self *TreeChunker) split(depth int, treeSize int64, key Key, data io.Reader, size int64, jobC chan *hashJob, chunkC chan *Chunk, errC chan error, parentWg, swg, wwg *sync.WaitGroup) { + + for depth > 0 && size < treeSize { + treeSize /= self.branches + depth-- + } + + if depth == 0 { + // leaf nodes -> content chunks + chunkData := make([]byte, size+8) + binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size)) + data.Read(chunkData[8:]) + select { + case jobC <- &hashJob{key, chunkData, size, parentWg}: + case <-errC: + } + // glog.V(logger.Detail).Infof("read %v", size) + return + } + // intermediate chunk containing child nodes hashes + branchCnt := int64((size + treeSize - 1) / treeSize) + // glog.V(logger.Detail).Infof("intermediate node: setting branches: %v, depth: %v, max subtree size: %v, data size: %v", branches, depth, treeSize, size) + + var chunk []byte = make([]byte, branchCnt*self.hashSize+8) + var pos, i int64 + + binary.LittleEndian.PutUint64(chunk[0:8], uint64(size)) + + childrenWg := &sync.WaitGroup{} + var secSize int64 + for i < branchCnt { + // the last item can have shorter data + if size-pos < treeSize { + secSize = size - pos + } else { + secSize = treeSize + } + // the hash of that data + subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize] + + childrenWg.Add(1) + self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, childrenWg, swg, wwg) + + i++ + pos += treeSize + } + // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk + // parentWg.Add(1) + // go func() { + childrenWg.Wait() + if len(jobC) > self.workerCount && self.workerCount < processors { + if wwg != nil { + wwg.Add(1) + } + self.workerCount++ + go self.hashWorker(jobC, chunkC, errC, swg, wwg) + } + select { + case jobC <- &hashJob{key, chunk, size, parentWg}: + case <-errC: + } +} + +func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, swg, wwg *sync.WaitGroup) { + hasher := self.hashFunc() + if wwg != nil { + defer wwg.Done() + } + for { + select { + + case job, ok := <-jobC: + if !ok { + return + } + // now we got the hashes in the chunk, then hash the chunks + hasher.Reset() + self.hashChunk(hasher, job, chunkC, swg) + // glog.V(logger.Detail).Infof("hash chunk (%v)", job.size) + case <-errC: + return + } + } +} + +// The treeChunkers own Hash hashes together +// - the size (of the subtree encoded in the Chunk) +// - the Chunk, ie. the contents read from the input reader +func (self *TreeChunker) hashChunk(hasher hash.Hash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) { + hasher.Write(job.chunk) + h := hasher.Sum(nil) + newChunk := &Chunk{ + Key: h, + SData: job.chunk, + Size: job.size, + wg: swg, + } + + // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk) + copy(job.key, h) + // send off new chunk to storage + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + } + job.parentWg.Done() + if chunkC != nil { + chunkC <- newChunk + } +} + +// LazyChunkReader implements LazySectionReader +type LazyChunkReader struct { + key Key // root key + chunkC chan *Chunk // chunk channel to send retrieve requests on + chunk *Chunk // size of the entire subtree + off int64 // offset + chunkSize int64 // inherit from chunker + branches int64 // inherit from chunker + hashSize int64 // inherit from chunker +} + +// implements the Joiner interface +func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader { + + return &LazyChunkReader{ + key: key, + chunkC: chunkC, + chunkSize: self.chunkSize, + branches: self.branches, + hashSize: self.hashSize, + } +} + +// Size is meant to be called on the LazySectionReader +func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) { + if self.chunk != nil { + return self.chunk.Size, nil + } + chunk := retrieve(self.key, self.chunkC, quitC) + if chunk == nil { + select { + case <-quitC: + return 0, errors.New("aborted") + default: + return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex()) + } + } + self.chunk = chunk + return chunk.Size, nil +} + +// read at can be called numerous times +// concurrent reads are allowed +// Size() needs to be called synchronously on the LazyChunkReader first +func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) { + // this is correct, a swarm doc cannot be zero length, so no EOF is expected + if len(b) == 0 { + // glog.V(logger.Detail).Infof("Size query for %v", chunk.Key.Log()) + return 0, nil + } + quitC := make(chan bool) + size, err := self.Size(quitC) + if err != nil { + return 0, err + } + // glog.V(logger.Detail).Infof("readAt: len(b): %v, off: %v, size: %v ", len(b), off, size) + + errC := make(chan error) + // glog.V(logger.Detail).Infof("readAt: reading %v into %d bytes at offset %d.", self.chunk.Key.Log(), len(b), off) + + // } + // glog.V(logger.Detail).Infof("-> want: %v, off: %v size: %v ", want, off, self.size) + var treeSize int64 + var depth int + // calculate depth and max treeSize + treeSize = self.chunkSize + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + wg := sync.WaitGroup{} + wg.Add(1) + go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC) + go func() { + wg.Wait() + close(errC) + }() + + err = <-errC + if err != nil { + close(quitC) + + return 0, err + } + // glog.V(logger.Detail).Infof("ReadAt received %v", err) + // glog.V(logger.Detail).Infof("end: len(b): %v, off: %v, size: %v ", len(b), off, size) + if off+int64(len(b)) >= size { + // glog.V(logger.Detail).Infof(" len(b): %v EOF", len(b)) + return len(b), io.EOF + } + // glog.V(logger.Detail).Infof("ReadAt returning at %d: %v", read, err) + return len(b), nil +} + +func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) { + defer parentWg.Done() + // return NewDPA(&LocalStore{}) + // glog.V(logger.Detail).Infof("inh len(b): %v, off: %v eoff: %v ", len(b), off, eoff) + + // glog.V(logger.Detail).Infof("depth: %v, loff: %v, eoff: %v, chunk.Size: %v, treeSize: %v", depth, off, eoff, chunk.Size, treeSize) + + // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + // find appropriate block level + for chunk.Size < treeSize && depth > 0 { + treeSize /= self.branches + depth-- + } + + // leaf chunk found + if depth == 0 { + // glog.V(logger.Detail).Infof("depth: %v, len(b): %v, off: %v, eoff: %v, chunk.Size: %v %v, treeSize: %v", depth, len(b), off, eoff, chunk.Size, len(chunk.SData), treeSize) + extra := 8 + eoff - int64(len(chunk.SData)) + if extra > 0 { + eoff -= extra + } + copy(b, chunk.SData[8+off:8+eoff]) + return // simply give back the chunks reader for content chunks + } + + // subtree + start := off / treeSize + end := (eoff + treeSize - 1) / treeSize + + wg := &sync.WaitGroup{} + defer wg.Wait() + // glog.V(logger.Detail).Infof("start %v,end %v", start, end) + + for i := start; i < end; i++ { + soff := i * treeSize + roff := soff + seoff := soff + treeSize + + if soff < off { + soff = off + } + if seoff > eoff { + seoff = eoff + } + if depth > 1 { + wg.Wait() + } + wg.Add(1) + go func(j int64) { + childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize] + // glog.V(logger.Detail).Infof("subtree ind.ex: %v -> %v", j, childKey.Log()) + chunk := retrieve(childKey, self.chunkC, quitC) + if chunk == nil { + select { + case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize): + case <-quitC: + } + return + } + if soff < off { + soff = off + } + self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC) + }(i) + } //for +} + +// the helper method submits chunks for a key to a oueue (DPA) and +// block until they time out or arrive +// abort if quitC is readable +func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk { + chunk := &Chunk{ + Key: key, + C: make(chan bool), // close channel to signal data delivery + } + // glog.V(logger.Detail).Infof("chunk data sent for %v (key interval in chunk %v-%v)", ch.Key.Log(), j*self.chunker.hashSize, (j+1)*self.chunker.hashSize) + // submit chunk for retrieval + select { + case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally) + case <-quitC: + return nil + } + // waiting for the chunk retrieval + select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + case <-quitC: + // this is how we control process leakage (quitC is closed once join is finished (after timeout)) + return nil + case <-chunk.C: // bells are ringing, data have been delivered + // glog.V(logger.Detail).Infof("chunk data received") + } + if len(chunk.SData) == 0 { + return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + } + return chunk +} + +// Read keeps a cursor so cannot be called simulateously, see ReadAt +func (self *LazyChunkReader) Read(b []byte) (read int, err error) { + read, err = self.ReadAt(b, self.off) + // glog.V(logger.Detail).Infof("read: %v, off: %v, error: %v", read, self.off, err) + + self.off += int64(read) + return +} + +// completely analogous to standard SectionReader implementation +var errWhence = errors.New("Seek: invalid whence") +var errOffset = errors.New("Seek: invalid offset") + +func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case 0: + offset += 0 + case 1: + offset += s.off + case 2: + if s.chunk == nil { + return 0, fmt.Errorf("seek from the end requires rootchunk for size. call Size first") + } + offset += s.chunk.Size + } + + if offset < 0 { + return 0, errOffset + } + s.off = offset + return offset, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go new file mode 100644 index 000000000..2532490cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go @@ -0,0 +1,99 @@ +// 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 storage + +// this is a clone of an earlier state of the ethereum ethdb/database +// no need for queueing/caching + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/compression/rle" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +const openFileLimit = 128 + +type LDBDatabase struct { + db *leveldb.DB + comp bool +} + +func NewLDBDatabase(file string) (*LDBDatabase, error) { + // Open the db + db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit}) + if err != nil { + return nil, err + } + + database := &LDBDatabase{db: db, comp: false} + + return database, nil +} + +func (self *LDBDatabase) Put(key []byte, value []byte) { + if self.comp { + value = rle.Compress(value) + } + + err := self.db.Put(key, value, nil) + if err != nil { + fmt.Println("Error put", err) + } +} + +func (self *LDBDatabase) Get(key []byte) ([]byte, error) { + dat, err := self.db.Get(key, nil) + if err != nil { + return nil, err + } + + if self.comp { + return rle.Decompress(dat) + } + + return dat, nil +} + +func (self *LDBDatabase) Delete(key []byte) error { + return self.db.Delete(key, nil) +} + +func (self *LDBDatabase) LastKnownTD() []byte { + data, _ := self.Get([]byte("LTD")) + + if len(data) == 0 { + data = []byte{0x0} + } + + return data +} + +func (self *LDBDatabase) NewIterator() iterator.Iterator { + return self.db.NewIterator(nil, nil) +} + +func (self *LDBDatabase) Write(batch *leveldb.Batch) error { + return self.db.Write(batch, nil) +} + +func (self *LDBDatabase) Close() { + // Close the leveldb database + self.db.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go new file mode 100644 index 000000000..5ecc5c500 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go @@ -0,0 +1,473 @@ +// 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 . + +// disk storage layer for the package bzz +// DbStore implements the ChunkStore interface and is used by the DPA as +// persistent storage of chunks +// it implements purging based on access count allowing for external control of +// max capacity + +package storage + +import ( + "bytes" + "encoding/binary" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const ( + defaultDbCapacity = 5000000 + defaultRadius = 0 // not yet used + + gcArraySize = 10000 + gcArrayFreeRatio = 0.1 + + // key prefixes for leveldb storage + kpIndex = 0 + kpData = 1 +) + +var ( + keyAccessCnt = []byte{2} + keyEntryCnt = []byte{3} + keyDataIdx = []byte{4} + keyGCPos = []byte{5} +) + +type gcItem struct { + idx uint64 + value uint64 + idxKey []byte +} + +type DbStore struct { + db *LDBDatabase + + // this should be stored in db, accessed transactionally + entryCnt, accessCnt, dataIdx, capacity uint64 + + gcPos, gcStartPos []byte + gcArray []*gcItem + + hashfunc Hasher + + lock sync.Mutex +} + +func NewDbStore(path string, hash Hasher, capacity uint64, radius int) (s *DbStore, err error) { + s = new(DbStore) + + s.hashfunc = hash + + s.db, err = NewLDBDatabase(path) + if err != nil { + return + } + + s.setCapacity(capacity) + + s.gcStartPos = make([]byte, 1) + s.gcStartPos[0] = kpIndex + s.gcArray = make([]*gcItem, gcArraySize) + + data, _ := s.db.Get(keyEntryCnt) + s.entryCnt = BytesToU64(data) + data, _ = s.db.Get(keyAccessCnt) + s.accessCnt = BytesToU64(data) + data, _ = s.db.Get(keyDataIdx) + s.dataIdx = BytesToU64(data) + s.gcPos, _ = s.db.Get(keyGCPos) + if s.gcPos == nil { + s.gcPos = s.gcStartPos + } + return +} + +type dpaDBIndex struct { + Idx uint64 + Access uint64 +} + +func BytesToU64(data []byte) uint64 { + if len(data) < 8 { + return 0 + } + return binary.LittleEndian.Uint64(data) +} + +func U64ToBytes(val uint64) []byte { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, val) + return data +} + +func getIndexGCValue(index *dpaDBIndex) uint64 { + return index.Access +} + +func (s *DbStore) updateIndexAccess(index *dpaDBIndex) { + index.Access = s.accessCnt +} + +func getIndexKey(hash Key) []byte { + HashSize := len(hash) + key := make([]byte, HashSize+1) + key[0] = 0 + copy(key[1:], hash[:]) + return key +} + +func getDataKey(idx uint64) []byte { + key := make([]byte, 9) + key[0] = 1 + binary.BigEndian.PutUint64(key[1:9], idx) + + return key +} + +func encodeIndex(index *dpaDBIndex) []byte { + data, _ := rlp.EncodeToBytes(index) + return data +} + +func encodeData(chunk *Chunk) []byte { + return chunk.SData +} + +func decodeIndex(data []byte, index *dpaDBIndex) { + dec := rlp.NewStream(bytes.NewReader(data), 0) + dec.Decode(index) +} + +func decodeData(data []byte, chunk *Chunk) { + chunk.SData = data + chunk.Size = int64(binary.LittleEndian.Uint64(data[0:8])) +} + +func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int { + pivotValue := list[pivotIndex].value + dd := list[pivotIndex] + list[pivotIndex] = list[right] + list[right] = dd + storeIndex := left + for i := left; i < right; i++ { + if list[i].value < pivotValue { + dd = list[storeIndex] + list[storeIndex] = list[i] + list[i] = dd + storeIndex++ + } + } + dd = list[storeIndex] + list[storeIndex] = list[right] + list[right] = dd + return storeIndex +} + +func gcListSelect(list []*gcItem, left int, right int, n int) int { + if left == right { + return left + } + pivotIndex := (left + right) / 2 + pivotIndex = gcListPartition(list, left, right, pivotIndex) + if n == pivotIndex { + return n + } else { + if n < pivotIndex { + return gcListSelect(list, left, pivotIndex-1, n) + } else { + return gcListSelect(list, pivotIndex+1, right, n) + } + } +} + +func (s *DbStore) collectGarbage(ratio float32) { + it := s.db.NewIterator() + it.Seek(s.gcPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + gcnt := 0 + + for (gcnt < gcArraySize) && (uint64(gcnt) < s.entryCnt) { + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + it.Seek(s.gcStartPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + break + } + + gci := new(gcItem) + gci.idxKey = s.gcPos + var index dpaDBIndex + decodeIndex(it.Value(), &index) + gci.idx = index.Idx + // the smaller, the more likely to be gc'd + gci.value = getIndexGCValue(&index) + s.gcArray[gcnt] = gci + gcnt++ + it.Next() + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + it.Release() + + cutidx := gcListSelect(s.gcArray, 0, gcnt-1, int(float32(gcnt)*ratio)) + cutval := s.gcArray[cutidx].value + + // fmt.Print(gcnt, " ", s.entryCnt, " ") + + // actual gc + for i := 0; i < gcnt; i++ { + if s.gcArray[i].value <= cutval { + batch := new(leveldb.Batch) + batch.Delete(s.gcArray[i].idxKey) + batch.Delete(getDataKey(s.gcArray[i].idx)) + s.entryCnt-- + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.db.Write(batch) + } + } + + // fmt.Println(s.entryCnt) + + s.db.Put(keyGCPos, s.gcPos) +} + +func (s *DbStore) Counter() uint64 { + s.lock.Lock() + defer s.lock.Unlock() + return s.dataIdx +} + +func (s *DbStore) Put(chunk *Chunk) { + s.lock.Lock() + defer s.lock.Unlock() + + ikey := getIndexKey(chunk.Key) + var index dpaDBIndex + + if s.tryAccessIdx(ikey, &index) { + if chunk.dbStored != nil { + close(chunk.dbStored) + } + return // already exists, only update access + } + + data := encodeData(chunk) + //data := ethutil.Encode([]interface{}{entry}) + + if s.entryCnt >= s.capacity { + s.collectGarbage(gcArrayFreeRatio) + } + + batch := new(leveldb.Batch) + + batch.Put(getDataKey(s.dataIdx), data) + + index.Idx = s.dataIdx + s.updateIndexAccess(&index) + + idata := encodeIndex(&index) + batch.Put(ikey, idata) + + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.entryCnt++ + batch.Put(keyDataIdx, U64ToBytes(s.dataIdx)) + s.dataIdx++ + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + + s.db.Write(batch) + if chunk.dbStored != nil { + close(chunk.dbStored) + } + glog.V(logger.Detail).Infof("DbStore.Put: %v. db storage counter: %v ", chunk.Key.Log(), s.dataIdx) +} + +// try to find index; if found, update access cnt and return true +func (s *DbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool { + idata, err := s.db.Get(ikey) + if err != nil { + return false + } + decodeIndex(idata, index) + + batch := new(leveldb.Batch) + + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + s.updateIndexAccess(index) + idata = encodeIndex(index) + batch.Put(ikey, idata) + + s.db.Write(batch) + + return true +} + +func (s *DbStore) Get(key Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + + if s.tryAccessIdx(getIndexKey(key), &index) { + var data []byte + data, err = s.db.Get(getDataKey(index.Idx)) + if err != nil { + return + } + + hasher := s.hashfunc() + hasher.Write(data) + hash := hasher.Sum(nil) + if bytes.Compare(hash, key) != 0 { + s.db.Delete(getDataKey(index.Idx)) + err = fmt.Errorf("invalid chunk. hash=%x, key=%v", hash, key[:]) + return + } + + chunk = &Chunk{ + Key: key, + } + decodeData(data, chunk) + } else { + err = notFound + } + + return + +} + +func (s *DbStore) updateAccessCnt(key Key) { + + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt + +} + +func (s *DbStore) setCapacity(c uint64) { + + s.lock.Lock() + defer s.lock.Unlock() + + s.capacity = c + + if s.entryCnt > c { + var ratio float32 + ratio = float32(1.01) - float32(c)/float32(s.entryCnt) + if ratio < gcArrayFreeRatio { + ratio = gcArrayFreeRatio + } + if ratio > 1 { + ratio = 1 + } + for s.entryCnt > c { + s.collectGarbage(ratio) + } + } +} + +func (s *DbStore) getEntryCnt() uint64 { + return s.entryCnt +} + +func (s *DbStore) close() { + s.db.Close() +} + +// describes a section of the DbStore representing the unsynced +// domain relevant to a peer +// Start - Stop designate a continuous area Keys in an address space +// typically the addresses closer to us than to the peer but not closer +// another closer peer in between +// From - To designates a time interval typically from the last disconnect +// till the latest connection (real time traffic is relayed) +type DbSyncState struct { + Start, Stop Key + First, Last uint64 +} + +// implements the syncer iterator interface +// iterates by storage index (~ time of storage = first entry to db) +type dbSyncIterator struct { + it iterator.Iterator + DbSyncState +} + +// initialises a sync iterator from a syncToken (passed in with the handshake) +func (self *DbStore) NewSyncIterator(state DbSyncState) (si *dbSyncIterator, err error) { + if state.First > state.Last { + return nil, fmt.Errorf("no entries found") + } + si = &dbSyncIterator{ + it: self.db.NewIterator(), + DbSyncState: state, + } + si.it.Seek(getIndexKey(state.Start)) + return si, nil +} + +// walk the area from Start to Stop and returns items within time interval +// First to Last +func (self *dbSyncIterator) Next() (key Key) { + for self.it.Valid() { + dbkey := self.it.Key() + if dbkey[0] != 0 { + break + } + key = Key(make([]byte, len(dbkey)-1)) + copy(key[:], dbkey[1:]) + if bytes.Compare(key[:], self.Start) <= 0 { + self.it.Next() + continue + } + if bytes.Compare(key[:], self.Stop) > 0 { + break + } + var index dpaDBIndex + decodeIndex(self.it.Value(), &index) + self.it.Next() + if (index.Idx >= self.First) && (index.Idx < self.Last) { + return + } + } + self.it.Release() + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go new file mode 100644 index 000000000..31b6c54ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go @@ -0,0 +1,239 @@ +// 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 storage + +import ( + "errors" + "io" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +DPA provides the client API entrypoints Store and Retrieve to store and retrieve +It can store anything that has a byte slice representation, so files or serialised objects etc. + +Storage: DPA calls the Chunker to segment the input datastream of any size to a merkle hashed tree of chunks. The key of the root block is returned to the client. + +Retrieval: given the key of the root block, the DPA retrieves the block chunks and reconstructs the original data and passes it back as a lazy reader. A lazy reader is a reader with on-demand delayed processing, i.e. the chunks needed to reconstruct a large file are only fetched and processed if that particular part of the document is actually read. + +As the chunker produces chunks, DPA dispatches them to its own chunk store +implementation for storage or retrieval. +*/ + +const ( + storeChanCapacity = 100 + retrieveChanCapacity = 100 + singletonSwarmDbCapacity = 50000 + singletonSwarmCacheCapacity = 500 + maxStoreProcesses = 8 + maxRetrieveProcesses = 8 +) + +var ( + notFound = errors.New("not found") +) + +type DPA struct { + ChunkStore + storeC chan *Chunk + retrieveC chan *Chunk + Chunker Chunker + + lock sync.Mutex + running bool + wg *sync.WaitGroup + quitC chan bool +} + +// for testing locally +func NewLocalDPA(datadir string) (*DPA, error) { + + hash := MakeHashFunc("SHA256") + + dbStore, err := NewDbStore(datadir, hash, singletonSwarmDbCapacity, 0) + if err != nil { + return nil, err + } + + return NewDPA(&LocalStore{ + NewMemStore(dbStore, singletonSwarmCacheCapacity), + dbStore, + }, NewChunkerParams()), nil +} + +func NewDPA(store ChunkStore, params *ChunkerParams) *DPA { + chunker := NewTreeChunker(params) + return &DPA{ + Chunker: chunker, + ChunkStore: store, + } +} + +// Public API. Main entry point for document retrieval directly. Used by the +// FS-aware API and httpaccess +// Chunk retrieval blocks on netStore requests with a timeout so reader will +// report error if retrieval of chunks within requested range time out. +func (self *DPA) Retrieve(key Key) LazySectionReader { + return self.Chunker.Join(key, self.retrieveC) +} + +// Public API. Main entry point for document storage directly. Used by the +// FS-aware API and httpaccess +func (self *DPA) Store(data io.Reader, size int64, swg *sync.WaitGroup, wwg *sync.WaitGroup) (key Key, err error) { + return self.Chunker.Split(data, size, self.storeC, swg, wwg) +} + +func (self *DPA) Start() { + self.lock.Lock() + defer self.lock.Unlock() + if self.running { + return + } + self.running = true + self.retrieveC = make(chan *Chunk, retrieveChanCapacity) + self.storeC = make(chan *Chunk, storeChanCapacity) + self.quitC = make(chan bool) + self.storeLoop() + self.retrieveLoop() +} + +func (self *DPA) Stop() { + self.lock.Lock() + defer self.lock.Unlock() + if !self.running { + return + } + self.running = false + close(self.quitC) +} + +// retrieveLoop dispatches the parallel chunk retrieval requests received on the +// retrieve channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) retrieveLoop() { + for i := 0; i < maxRetrieveProcesses; i++ { + go self.retrieveWorker() + } + glog.V(logger.Detail).Infof("dpa: retrieve loop spawning %v workers", maxRetrieveProcesses) +} + +func (self *DPA) retrieveWorker() { + for chunk := range self.retrieveC { + glog.V(logger.Detail).Infof("dpa: retrieve loop : chunk %v", chunk.Key.Log()) + storedChunk, err := self.Get(chunk.Key) + if err == notFound { + glog.V(logger.Detail).Infof("chunk %v not found", chunk.Key.Log()) + } else if err != nil { + glog.V(logger.Detail).Infof("error retrieving chunk %v: %v", chunk.Key.Log(), err) + } else { + chunk.SData = storedChunk.SData + chunk.Size = storedChunk.Size + } + close(chunk.C) + + select { + case <-self.quitC: + return + default: + } + } +} + +// storeLoop dispatches the parallel chunk store request processors +// received on the store channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) storeLoop() { + for i := 0; i < maxStoreProcesses; i++ { + go self.storeWorker() + } + glog.V(logger.Detail).Infof("dpa: store spawning %v workers", maxStoreProcesses) +} + +func (self *DPA) storeWorker() { + + for chunk := range self.storeC { + self.Put(chunk) + if chunk.wg != nil { + glog.V(logger.Detail).Infof("dpa: store processor %v", chunk.Key.Log()) + chunk.wg.Done() + + } + select { + case <-self.quitC: + return + default: + } + } +} + +// DpaChunkStore implements the ChunkStore interface, +// this chunk access layer assumed 2 chunk stores +// local storage eg. LocalStore and network storage eg., NetStore +// access by calling network is blocking with a timeout + +type dpaChunkStore struct { + n int + localStore ChunkStore + netStore ChunkStore +} + +func NewDpaChunkStore(localStore, netStore ChunkStore) *dpaChunkStore { + return &dpaChunkStore{0, localStore, netStore} +} + +// Get is the entrypoint for local retrieve requests +// waits for response or times out +func (self *dpaChunkStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.netStore.Get(key) + // timeout := time.Now().Add(searchTimeout) + if chunk.SData != nil { + glog.V(logger.Detail).Infof("DPA.Get: %v found locally, %d bytes", key.Log(), len(chunk.SData)) + return + } + // TODO: use self.timer time.Timer and reset with defer disableTimer + timer := time.After(searchTimeout) + select { + case <-timer: + glog.V(logger.Detail).Infof("DPA.Get: %v request time out ", key.Log()) + err = notFound + case <-chunk.Req.C: + glog.V(logger.Detail).Infof("DPA.Get: %v retrieved, %d bytes (%p)", key.Log(), len(chunk.SData), chunk) + } + return +} + +// Put is the entrypoint for local store requests coming from storeLoop +func (self *dpaChunkStore) Put(entry *Chunk) { + chunk, err := self.localStore.Get(entry.Key) + if err != nil { + glog.V(logger.Detail).Infof("DPA.Put: %v new chunk. call netStore.Put", entry.Key.Log()) + chunk = entry + } else if chunk.SData == nil { + glog.V(logger.Detail).Infof("DPA.Put: %v request entry found", entry.Key.Log()) + chunk.SData = entry.SData + chunk.Size = entry.Size + } else { + glog.V(logger.Detail).Infof("DPA.Put: %v chunk already known", entry.Key.Log()) + return + } + // from this point on the storage logic is the same with network storage requests + glog.V(logger.Detail).Infof("DPA.Put %v: %v", self.n, chunk.Key.Log()) + self.n++ + self.netStore.Put(chunk) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go new file mode 100644 index 000000000..541462f0c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.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 a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" +) + +// LocalStore is a combination of inmemory db over a disk persisted db +// implements a Get/Put with fallback (caching) logic using any 2 ChunkStores +type LocalStore struct { + memStore ChunkStore + DbStore ChunkStore +} + +// This constructor uses MemStore and DbStore as components +func NewLocalStore(hash Hasher, params *StoreParams) (*LocalStore, error) { + dbStore, err := NewDbStore(params.ChunkDbPath, hash, params.DbCapacity, params.Radius) + if err != nil { + return nil, err + } + return &LocalStore{ + memStore: NewMemStore(dbStore, params.CacheCapacity), + DbStore: dbStore, + }, nil +} + +// LocalStore is itself a chunk store +// unsafe, in that the data is not integrity checked +func (self *LocalStore) Put(chunk *Chunk) { + chunk.dbStored = make(chan bool) + self.memStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Add(1) + } + go func() { + self.DbStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Done() + } + }() +} + +// Get(chunk *Chunk) looks up a chunk in the local stores +// This method is blocking until the chunk is retrieved +// so additional timeout may be needed to wrap this call if +// ChunkStores are remote and can have long latency +func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.memStore.Get(key) + if err == nil { + return + } + chunk, err = self.DbStore.Get(key) + if err != nil { + return + } + chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + self.memStore.Put(chunk) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go new file mode 100644 index 000000000..f133bd7d3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go @@ -0,0 +1,316 @@ +// 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 . + +// memory storage layer for the package blockhash + +package storage + +import ( + "sync" +) + +const ( + memTreeLW = 2 // log2(subtree count) of the subtrees + memTreeFLW = 14 // log2(subtree count) of the root layer + dbForceUpdateAccessCnt = 1000 + defaultCacheCapacity = 5000 +) + +type MemStore struct { + memtree *memTree + entryCnt, capacity uint // stored entries + accessCnt uint64 // access counter; oldest is thrown away when full + dbAccessCnt uint64 + dbStore *DbStore + lock sync.Mutex +} + +/* +a hash prefix subtree containing subtrees or one storage entry (but never both) + +- access[0] stores the smallest (oldest) access count value in this subtree +- if it contains more subtrees and its subtree count is at least 4, access[1:2] + stores the smallest access count in the first and second halves of subtrees + (so that access[0] = min(access[1], access[2]) +- likewise, if subtree count is at least 8, + access[1] = min(access[3], access[4]) + access[2] = min(access[5], access[6]) + (access[] is a binary tree inside the multi-bit leveled hash tree) +*/ + +func NewMemStore(d *DbStore, capacity uint) (m *MemStore) { + m = &MemStore{} + m.memtree = newMemTree(memTreeFLW, nil, 0) + m.dbStore = d + m.setCapacity(capacity) + return +} + +type memTree struct { + subtree []*memTree + parent *memTree + parentIdx uint + + bits uint // log2(subtree count) + width uint // subtree count + + entry *Chunk // if subtrees are present, entry should be nil + lastDBaccess uint64 + access []uint64 +} + +func newMemTree(b uint, parent *memTree, pidx uint) (node *memTree) { + node = new(memTree) + node.bits = b + node.width = 1 << uint(b) + node.subtree = make([]*memTree, node.width) + node.access = make([]uint64, node.width-1) + node.parent = parent + node.parentIdx = pidx + if parent != nil { + parent.subtree[pidx] = node + } + + return node +} + +func (node *memTree) updateAccess(a uint64) { + aidx := uint(0) + var aa uint64 + oa := node.access[0] + for node.access[aidx] == oa { + node.access[aidx] = a + if aidx > 0 { + aa = node.access[((aidx-1)^1)+1] + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + nn := node.subtree[pidx^1] + if nn != nil { + aa = nn.access[0] + } else { + aa = 0 + } + aidx = (node.width + pidx - 2) >> 1 + } + + if (aa != 0) && (aa < a) { + a = aa + } + } +} + +func (s *MemStore) setCapacity(c uint) { + s.lock.Lock() + defer s.lock.Unlock() + + for c < s.entryCnt { + s.removeOldest() + } + s.capacity = c +} + +func (s *MemStore) getEntryCnt() uint { + return s.entryCnt +} + +// entry (not its copy) is going to be in MemStore +func (s *MemStore) Put(entry *Chunk) { + if s.capacity == 0 { + return + } + + s.lock.Lock() + defer s.lock.Unlock() + + if s.entryCnt >= s.capacity { + s.removeOldest() + } + + s.accessCnt++ + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + bitpos += node.bits + node = st + break + } + bitpos += node.bits + node = st + } + + if node.entry != nil { + + if node.entry.Key.isEqual(entry.Key) { + node.updateAccess(s.accessCnt) + if entry.SData == nil { + entry.Size = node.entry.Size + entry.SData = node.entry.SData + } + if entry.Req == nil { + entry.Req = node.entry.Req + } + entry.C = node.entry.C + node.entry = entry + return + } + + for node.entry != nil { + + l := node.entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + st.entry = node.entry + node.entry = nil + st.updateAccess(node.access[0]) + + l = entry.Key.bits(bitpos, node.bits) + st = node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + bitpos += node.bits + node = st + + } + } + + node.entry = entry + node.lastDBaccess = s.dbAccessCnt + node.updateAccess(s.accessCnt) + s.entryCnt++ + + return +} + +func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := hash.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + return nil, notFound + } + bitpos += node.bits + node = st + } + + if node.entry.Key.isEqual(hash) { + s.accessCnt++ + node.updateAccess(s.accessCnt) + chunk = node.entry + if s.dbAccessCnt-node.lastDBaccess > dbForceUpdateAccessCnt { + s.dbAccessCnt++ + node.lastDBaccess = s.dbAccessCnt + if s.dbStore != nil { + s.dbStore.updateAccessCnt(hash) + } + } + } else { + err = notFound + } + + return +} + +func (s *MemStore) removeOldest() { + node := s.memtree + + for node.entry == nil { + + aidx := uint(0) + av := node.access[aidx] + + for aidx < node.width/2-1 { + if av == node.access[aidx*2+1] { + node.access[aidx] = node.access[aidx*2+2] + aidx = aidx*2 + 1 + } else if av == node.access[aidx*2+2] { + node.access[aidx] = node.access[aidx*2+1] + aidx = aidx*2 + 2 + } else { + panic(nil) + } + } + pidx := aidx*2 + 2 - node.width + if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) { + if node.subtree[pidx+1] != nil { + node.access[aidx] = node.subtree[pidx+1].access[0] + } else { + node.access[aidx] = 0 + } + } else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) { + if node.subtree[pidx] != nil { + node.access[aidx] = node.subtree[pidx].access[0] + } else { + node.access[aidx] = 0 + } + pidx++ + } else { + panic(nil) + } + + //fmt.Println(pidx) + node = node.subtree[pidx] + + } + + if node.entry.dbStored != nil { + <-node.entry.dbStored + } + + if node.entry.SData != nil { + node.entry = nil + s.entryCnt-- + } + + node.access[0] = 0 + + //--- + + aidx := uint(0) + for { + aa := node.access[aidx] + if aidx > 0 { + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + aidx = (node.width + pidx - 2) >> 1 + } + if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) { + node.access[aidx] = aa + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go new file mode 100644 index 000000000..334229aed --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go @@ -0,0 +1,134 @@ +// 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 storage + +import ( + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +NetStore is a cloud storage access abstaction layer for swarm +it contains the shared logic of network served chunk store/retrieval requests +both local (coming from DPA api) and remote (coming from peers via bzz protocol) +it implements the ChunkStore interface and embeds LocalStore + +It is called by the bzz protocol instances via Depo (the store/retrieve request handler) +a protocol instance is running on each peer, so this is heavily parallelised. +NetStore falls back to a backend (CloudStorage interface) +implemented by bzz/network/forwarder. forwarder or IPFS or IPΞS +*/ +type NetStore struct { + hashfunc Hasher + localStore *LocalStore + cloud CloudStore + lock sync.Mutex +} + +// backend engine for cloud store +// It can be aggregate dispatching to several parallel implementations: +// bzz/network/forwarder. forwarder or IPFS or IPΞS +type CloudStore interface { + Store(*Chunk) + Deliver(*Chunk) + Retrieve(*Chunk) +} + +type StoreParams struct { + ChunkDbPath string + DbCapacity uint64 + CacheCapacity uint + Radius int +} + +func NewStoreParams(path string) (self *StoreParams) { + return &StoreParams{ + ChunkDbPath: filepath.Join(path, "chunks"), + DbCapacity: defaultDbCapacity, + CacheCapacity: defaultCacheCapacity, + Radius: defaultRadius, + } +} + +// netstore contructor, takes path argument that is used to initialise dbStore, +// the persistent (disk) storage component of LocalStore +// the second argument is the hive, the connection/logistics manager for the node +func NewNetStore(hash Hasher, lstore *LocalStore, cloud CloudStore, params *StoreParams) *NetStore { + return &NetStore{ + hashfunc: hash, + localStore: lstore, + cloud: cloud, + } +} + +const ( + // maximum number of peers that a retrieved message is delivered to + requesterCount = 3 +) + +var ( + // timeout interval before retrieval is timed out + searchTimeout = 3 * time.Second +) + +// store logic common to local and network chunk store requests +// ~ unsafe put in localdb no check if exists no extra copy no hash validation +// the chunk is forced to propagate (Cloud.Store) even if locally found! +// caller needs to make sure if that is wanted +func (self *NetStore) Put(entry *Chunk) { + self.localStore.Put(entry) + + // handle deliveries + if entry.Req != nil { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v hit existing request...delivering", entry.Key.Log()) + // closing C singals to other routines (local requests) + // that the chunk is has been retrieved + close(entry.Req.C) + // deliver the chunk to requesters upstream + go self.cloud.Deliver(entry) + } else { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v stored locally", entry.Key.Log()) + // handle propagating store requests + // go self.cloud.Store(entry) + go self.cloud.Store(entry) + } +} + +// retrieve logic common for local and network chunk retrieval requests +func (self *NetStore) Get(key Key) (*Chunk, error) { + var err error + chunk, err := self.localStore.Get(key) + if err == nil { + if chunk.Req == nil { + glog.V(logger.Detail).Infof("NetStore.Get: %v found locally", key) + } else { + glog.V(logger.Detail).Infof("NetStore.Get: %v hit on an existing request", key) + // no need to launch again + } + return chunk, err + } + // no data and no request status + glog.V(logger.Detail).Infof("NetStore.Get: %v not found locally. open new request", key) + chunk = NewChunk(key, newRequestStatus(key)) + self.localStore.memStore.Put(chunk) + go self.cloud.Retrieve(chunk) + return chunk, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go new file mode 100644 index 000000000..3c1ef17a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go @@ -0,0 +1,211 @@ +// 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 storage + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + processors = 8 +) + +type Tree struct { + Chunks int64 + Levels []map[int64]*Node + Lock sync.RWMutex +} + +type Node struct { + Pending int64 + Size uint64 + Children []common.Hash + Last bool +} + +func (self *Node) String() string { + var children []string + for _, node := range self.Children { + children = append(children, node.Hex()) + } + return fmt.Sprintf("pending: %v, size: %v, last :%v, children: %v", self.Pending, self.Size, self.Last, strings.Join(children, ", ")) +} + +type Task struct { + Index int64 // Index of the chunk being processed + Size uint64 + Data []byte // Binary blob of the chunk + Last bool +} + +type PyramidChunker struct { + hashFunc Hasher + chunkSize int64 + hashSize int64 + branches int64 + workerCount int +} + +func NewPyramidChunker(params *ChunkerParams) (self *PyramidChunker) { + self = &PyramidChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +func (self *PyramidChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + chunks := (size + self.chunkSize - 1) / self.chunkSize + depth := int(math.Ceil(math.Log(float64(chunks))/math.Log(float64(self.branches)))) + 1 + // glog.V(logger.Detail).Infof("chunks: %v, depth: %v", chunks, depth) + + results := Tree{ + Chunks: chunks, + Levels: make([]map[int64]*Node, depth), + } + for i := 0; i < depth; i++ { + results.Levels[i] = make(map[int64]*Node) + } + // Create a pool of workers to crunch through the file + tasks := make(chan *Task, 2*processors) + pend := new(sync.WaitGroup) + abortC := make(chan bool) + for i := 0; i < processors; i++ { + pend.Add(1) + go self.processor(pend, swg, tasks, chunkC, &results) + } + // Feed the chunks into the task pool + for index := 0; ; index++ { + buffer := make([]byte, self.chunkSize+8) + n, err := data.Read(buffer[8:]) + last := err == io.ErrUnexpectedEOF || err == io.EOF + // glog.V(logger.Detail).Infof("n: %v, index: %v, depth: %v", n, index, depth) + if err != nil && !last { + // glog.V(logger.Info).Infof("error: %v", err) + close(abortC) + break + } + binary.LittleEndian.PutUint64(buffer[:8], uint64(n)) + pend.Add(1) + // glog.V(logger.Info).Infof("-> task %v (%v)", index, n) + select { + case tasks <- &Task{Index: int64(index), Size: uint64(n), Data: buffer[:n+8], Last: last}: + case <-abortC: + return nil, err + } + if last { + // glog.V(logger.Info).Infof("last task %v (%v)", index, n) + break + } + } + // Wait for the workers and return + close(tasks) + pend.Wait() + + // glog.V(logger.Info).Infof("len: %v", results.Levels[0][0]) + key := results.Levels[0][0].Children[0][:] + return key, nil +} + +func (self *PyramidChunker) processor(pend, swg *sync.WaitGroup, tasks chan *Task, chunkC chan *Chunk, results *Tree) { + defer pend.Done() + + // glog.V(logger.Info).Infof("processor started") + // Start processing leaf chunks ad infinitum + hasher := self.hashFunc() + for task := range tasks { + depth, pow := len(results.Levels)-1, self.branches + // glog.V(logger.Info).Infof("task: %v, last: %v", task.Index, task.Last) + size := task.Size + data := task.Data + var node *Node + for depth >= 0 { + // New chunk received, reset the hasher and start processing + hasher.Reset() + if node == nil { // Leaf node, hash the data chunk + hasher.Write(task.Data) + } else { // Internal node, hash the children + size = node.Size + data = make([]byte, hasher.Size()*len(node.Children)+8) + binary.LittleEndian.PutUint64(data[:8], size) + + hasher.Write(data[:8]) + for i, hash := range node.Children { + copy(data[i*hasher.Size()+8:], hash[:]) + hasher.Write(hash[:]) + } + } + hash := hasher.Sum(nil) + last := task.Last || (node != nil) && node.Last + // Insert the subresult into the memoization tree + results.Lock.Lock() + if node = results.Levels[depth][task.Index/pow]; node == nil { + // Figure out the pending tasks + pending := self.branches + if task.Index/pow == results.Chunks/pow { + pending = (results.Chunks + pow/self.branches - 1) / (pow / self.branches) % self.branches + } + node = &Node{pending, 0, make([]common.Hash, pending), last} + results.Levels[depth][task.Index/pow] = node + // glog.V(logger.Info).Infof("create node %v, %v (%v children, all pending)", depth, task.Index/pow, pending) + } + node.Pending-- + // glog.V(logger.Info).Infof("pending now: %v", node.Pending) + i := task.Index / (pow / self.branches) % self.branches + if last { + node.Last = true + } + copy(node.Children[i][:], hash) + node.Size += size + left := node.Pending + // glog.V(logger.Info).Infof("left pending now: %v, node size: %v", left, node.Size) + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + select { + case chunkC <- &Chunk{Key: hash, SData: data, wg: swg}: + // case <- self.quitC + } + } + if depth+1 < len(results.Levels) { + delete(results.Levels[depth+1], task.Index/(pow/self.branches)) + } + + results.Lock.Unlock() + // If there's more work to be done, leave for others + // glog.V(logger.Info).Infof("left %v", left) + if left > 0 { + break + } + // We're the last ones in this batch, merge the children together + depth-- + pow *= self.branches + } + pend.Done() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go new file mode 100644 index 000000000..0dcbc0100 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go @@ -0,0 +1,232 @@ +// 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 storage + +import ( + "bytes" + "crypto" + "fmt" + "hash" + "io" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" +) + +type Hasher func() hash.Hash + +// Peer is the recorded as Source on the chunk +// should probably not be here? but network should wrap chunk object +type Peer interface{} + +type Key []byte + +func (x Key) Size() uint { + return uint(len(x)) +} + +func (x Key) isEqual(y Key) bool { + return bytes.Compare(x, y) == 0 +} + +func (h Key) bits(i, j uint) uint { + ii := i >> 3 + jj := i & 7 + if ii >= h.Size() { + return 0 + } + + if jj+j <= 8 { + return uint((h[ii] >> jj) & ((1 << j) - 1)) + } + + res := uint(h[ii] >> jj) + jj = 8 - jj + j -= jj + for j != 0 { + ii++ + if j < 8 { + res += uint(h[ii]&((1<. + +package swarm + +import ( + "bytes" + "crypto/ecdsa" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/ens" + "github.com/ethereum/go-ethereum/crypto" + "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/rpc" + "github.com/ethereum/go-ethereum/swarm/api" + httpapi "github.com/ethereum/go-ethereum/swarm/api/http" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/storage" + "golang.org/x/net/context" +) + +// the swarm stack +type Swarm struct { + config *api.Config // swarm configuration + api *api.Api // high level api layer (fs/manifest) + dns api.Resolver // DNS registrar + dbAccess *network.DbAccess // access to local chunk db iterator and storage counter + storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends + dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support + depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage + cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud) + hive *network.Hive // the logistic manager + backend chequebook.Backend // simple blockchain Backend + privateKey *ecdsa.PrivateKey + swapEnabled bool +} + +type SwarmAPI struct { + Api *api.Api + Backend chequebook.Backend + PrvKey *ecdsa.PrivateKey +} + +func (self *Swarm) API() *SwarmAPI { + return &SwarmAPI{ + Api: self.api, + Backend: self.backend, + PrvKey: self.privateKey, + } +} + +// creates a new swarm service instance +// implements node.Service +func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool) (self *Swarm, err error) { + if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty public key") + } + if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty bzz key") + } + + self = &Swarm{ + config: config, + swapEnabled: swapEnabled, + backend: backend, + privateKey: config.Swap.PrivateKey(), + } + glog.V(logger.Debug).Infof("Setting up Swarm service components") + + hash := storage.MakeHashFunc(config.ChunkerParams.Hash) + lstore, err := storage.NewLocalStore(hash, config.StoreParams) + if err != nil { + return + } + + // setup local store + glog.V(logger.Debug).Infof("Set up local storage") + + self.dbAccess = network.NewDbAccess(lstore) + glog.V(logger.Debug).Infof("Set up local db access (iterator/counter)") + + // set up the kademlia hive + self.hive = network.NewHive( + common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address) + config.HiveParams, // configuration parameters + swapEnabled, // SWAP enabled + syncEnabled, // syncronisation enabled + ) + glog.V(logger.Debug).Infof("Set up swarm network with Kademlia hive") + + // setup cloud storage backend + cloud := network.NewForwarder(self.hive) + glog.V(logger.Debug).Infof("-> set swarm forwarder as cloud storage backend") + // setup cloud storage internal access layer + + self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams) + glog.V(logger.Debug).Infof("-> swarm net store shared access layer to Swarm Chunk Store") + + // set up Depo (storage handler = cloud storage access layer for incoming remote requests) + self.depo = network.NewDepo(hash, lstore, self.storage) + glog.V(logger.Debug).Infof("-> REmote Access to CHunks") + + // set up DPA, the cloud storage local access layer + dpaChunkStore := storage.NewDpaChunkStore(lstore, self.storage) + glog.V(logger.Debug).Infof("-> Local Access to Swarm") + // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage + self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams) + glog.V(logger.Debug).Infof("-> Content Store API") + + // set up high level api + transactOpts := bind.NewKeyedTransactor(self.privateKey) + + self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) + if err != nil { + return nil, err + } + glog.V(logger.Debug).Infof("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex()) + + self.api = api.NewApi(self.dpa, self.dns) + // Manifests for Smart Hosting + glog.V(logger.Debug).Infof("-> Web3 virtual server API") + + return self, nil +} + +/* +Start is called when the stack is started +* starts the network kademlia hive peer management +* (starts netStore level 0 api) +* starts DPA level 1 api (chunking -> store/retrieve requests) +* (starts level 2 api) +* starts http proxy server +* registers url scheme handlers for bzz, etc +* TODO: start subservices like sword, swear, swarmdns +*/ +// implements the node.Service interface +func (self *Swarm) Start(net *p2p.Server) error { + connectPeer := func(url string) error { + node, err := discover.ParseNode(url) + if err != nil { + return fmt.Errorf("invalid node URL: %v", err) + } + net.AddPeer(node) + return nil + } + // set chequebook + if self.swapEnabled { + ctx := context.Background() // The initial setup has no deadline. + err := self.SetChequebook(ctx) + if err != nil { + return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) + } + glog.V(logger.Debug).Infof("-> cheque book for SWAP: %v", self.config.Swap.Chequebook()) + } else { + glog.V(logger.Debug).Infof("SWAP disabled: no cheque book set") + } + + glog.V(logger.Warn).Infof("Starting Swarm service") + self.hive.Start( + discover.PubkeyID(&net.PrivateKey.PublicKey), + func() string { return net.ListenAddr }, + connectPeer, + ) + glog.V(logger.Info).Infof("Swarm network started on bzz address: %v", self.hive.Addr()) + + self.dpa.Start() + glog.V(logger.Debug).Infof("Swarm DPA started") + + // start swarm http proxy server + if self.config.Port != "" { + go httpapi.StartHttpServer(self.api, self.config.Port) + } + glog.V(logger.Debug).Infof("Swarm http proxy started on port: %v", self.config.Port) + + return nil +} + +// implements the node.Service interface +// stops all component services. +func (self *Swarm) Stop() error { + self.dpa.Stop() + self.hive.Stop() + if ch := self.config.Swap.Chequebook(); ch != nil { + ch.Stop() + ch.Save() + } + return self.config.Save() +} + +// implements the node.Service interface +func (self *Swarm) Protocols() []p2p.Protocol { + proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams) + if err != nil { + return nil + } + return []p2p.Protocol{proto} +} + +// implements node.Service +// Apis returns the RPC Api descriptors the Swarm implementation offers +func (self *Swarm) APIs() []rpc.API { + return []rpc.API{ + // public APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewStorage(self.api), + Public: true, + }, + { + Namespace: "bzz", + Version: "0.1", + Service: &Info{self.config, chequebook.ContractParams}, + Public: true, + }, + // admin APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewFileSystem(self.api), + Public: false}, + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewControl(self.api, self.hive), + Public: false, + }, + { + Namespace: "chequebook", + Version: chequebook.Version, + Service: chequebook.NewApi(self.config.Swap.Chequebook), + Public: false, + }, + // {Namespace, Version, api.NewAdmin(self), false}, + } +} + +func (self *Swarm) Api() *api.Api { + return self.api +} + +// SetChequebook ensures that the local checquebook is set up on chain. +func (self *Swarm) SetChequebook(ctx context.Context) error { + err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) + if err != nil { + return err + } + glog.V(logger.Info).Infof("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex()) + self.config.Save() + self.hive.DropAll() + return nil +} + +// Local swarm without netStore +func NewLocalSwarm(datadir, port string) (self *Swarm, err error) { + + prvKey, err := crypto.GenerateKey() + if err != nil { + return + } + + config, err := api.NewConfig(datadir, common.Address{}, prvKey) + if err != nil { + return + } + config.Port = port + + dpa, err := storage.NewLocalDPA(datadir) + if err != nil { + return + } + + self = &Swarm{ + api: api.NewApi(dpa, nil), + config: config, + } + + return +} + +// serialisable info about swarm +type Info struct { + *api.Config + *chequebook.Params +} + +func (self *Info) Info() *Info { + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/arc.go b/vendor/github.com/ethereum/go-ethereum/trie/arc.go deleted file mode 100644 index fc7a3259f..000000000 --- a/vendor/github.com/ethereum/go-ethereum/trie/arc.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2015 Hans Alexander Gugel -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This file contains a modified version of package arc from -// https://github.com/alexanderGugel/arc -// -// It implements the ARC (Adaptive Replacement Cache) algorithm as detailed in -// https://www.usenix.org/legacy/event/fast03/tech/full_papers/megiddo/megiddo.pdf - -package trie - -import ( - "container/list" - "sync" -) - -type arc struct { - p int - c int - t1 *list.List - b1 *list.List - t2 *list.List - b2 *list.List - cache map[string]*entry - mutex sync.Mutex -} - -type entry struct { - key hashNode - value node - ll *list.List - el *list.Element -} - -// newARC returns a new Adaptive Replacement Cache with the -// given capacity. -func newARC(c int) *arc { - return &arc{ - c: c, - t1: list.New(), - b1: list.New(), - t2: list.New(), - b2: list.New(), - cache: make(map[string]*entry, c), - } -} - -// Clear clears the cache -func (a *arc) Clear() { - a.mutex.Lock() - defer a.mutex.Unlock() - a.p = 0 - a.t1 = list.New() - a.b1 = list.New() - a.t2 = list.New() - a.b2 = list.New() - a.cache = make(map[string]*entry, a.c) -} - -// Put inserts a new key-value pair into the cache. -// This optimizes future access to this entry (side effect). -func (a *arc) Put(key hashNode, value node) bool { - a.mutex.Lock() - defer a.mutex.Unlock() - ent, ok := a.cache[string(key)] - if ok != true { - ent = &entry{key: key, value: value} - a.req(ent) - a.cache[string(key)] = ent - } else { - ent.value = value - a.req(ent) - } - return ok -} - -// Get retrieves a previously via Set inserted entry. -// This optimizes future access to this entry (side effect). -func (a *arc) Get(key hashNode) (value node, ok bool) { - a.mutex.Lock() - defer a.mutex.Unlock() - ent, ok := a.cache[string(key)] - if ok { - a.req(ent) - return ent.value, ent.value != nil - } - return nil, false -} - -func (a *arc) req(ent *entry) { - if ent.ll == a.t1 || ent.ll == a.t2 { - // Case I - ent.setMRU(a.t2) - } else if ent.ll == a.b1 { - // Case II - // Cache Miss in t1 and t2 - - // Adaptation - var d int - if a.b1.Len() >= a.b2.Len() { - d = 1 - } else { - d = a.b2.Len() / a.b1.Len() - } - a.p = a.p + d - if a.p > a.c { - a.p = a.c - } - - a.replace(ent) - ent.setMRU(a.t2) - } else if ent.ll == a.b2 { - // Case III - // Cache Miss in t1 and t2 - - // Adaptation - var d int - if a.b2.Len() >= a.b1.Len() { - d = 1 - } else { - d = a.b1.Len() / a.b2.Len() - } - a.p = a.p - d - if a.p < 0 { - a.p = 0 - } - - a.replace(ent) - ent.setMRU(a.t2) - } else if ent.ll == nil { - // Case IV - - if a.t1.Len()+a.b1.Len() == a.c { - // Case A - if a.t1.Len() < a.c { - a.delLRU(a.b1) - a.replace(ent) - } else { - a.delLRU(a.t1) - } - } else if a.t1.Len()+a.b1.Len() < a.c { - // Case B - if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c { - if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c { - a.delLRU(a.b2) - } - a.replace(ent) - } - } - - ent.setMRU(a.t1) - } -} - -func (a *arc) delLRU(list *list.List) { - lru := list.Back() - list.Remove(lru) - delete(a.cache, string(lru.Value.(*entry).key)) -} - -func (a *arc) replace(ent *entry) { - if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) { - lru := a.t1.Back().Value.(*entry) - lru.value = nil - lru.setMRU(a.b1) - } else { - lru := a.t2.Back().Value.(*entry) - lru.value = nil - lru.setMRU(a.b2) - } -} - -func (e *entry) setLRU(list *list.List) { - e.detach() - e.ll = list - e.el = e.ll.PushBack(e) -} - -func (e *entry) setMRU(list *list.List) { - e.detach() - e.ll = list - e.el = e.ll.PushFront(e) -} - -func (e *entry) detach() { - if e.ll != nil { - e.ll.Remove(e.el) - } -} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/hasher.go b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go new file mode 100644 index 000000000..87e02fb85 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go @@ -0,0 +1,157 @@ +// 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 trie + +import ( + "bytes" + "hash" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +type hasher struct { + tmp *bytes.Buffer + sha hash.Hash +} + +// hashers live in a global pool. +var hasherPool = sync.Pool{ + New: func() interface{} { + return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} + }, +} + +func newHasher() *hasher { + return hasherPool.Get().(*hasher) +} + +func returnHasherToPool(h *hasher) { + hasherPool.Put(h) +} + +// hash collapses a node down into a hash node, also returning a copy of the +// original node initialzied with the computed hash to replace the original one. +func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { + // If we're not storing the node, just hashing, use avaialble cached data + if hash, dirty := n.cache(); hash != nil && (db == nil || !dirty) { + return hash, n, nil + } + // Trie not processed yet or needs storage, walk the children + collapsed, cached, err := h.hashChildren(n, db) + if err != nil { + return hashNode{}, n, err + } + hashed, err := h.store(collapsed, db, force) + if err != nil { + return hashNode{}, n, err + } + // Cache the hash and RLP blob of the ndoe for later reuse + if hash, ok := hashed.(hashNode); ok && !force { + switch cached := cached.(type) { + case shortNode: + cached.hash = hash + if db != nil { + cached.dirty = false + } + return hashed, cached, nil + case fullNode: + cached.hash = hash + if db != nil { + cached.dirty = false + } + return hashed, cached, nil + } + } + return hashed, cached, nil +} + +// hashChildren replaces the children of a node with their hashes if the encoded +// size of the child is larger than a hash, returning the collapsed node as well +// as a replacement for the original node with the child hashes cached in. +func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { + var err error + + switch n := original.(type) { + case shortNode: + // Hash the short node's child, caching the newly hashed subtree + cached := n + cached.Key = common.CopyBytes(cached.Key) + + n.Key = compactEncode(n.Key) + if _, ok := n.Val.(valueNode); !ok { + if n.Val, cached.Val, err = h.hash(n.Val, db, false); err != nil { + return n, original, err + } + } + if n.Val == nil { + n.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + return n, cached, nil + + case fullNode: + // Hash the full node's children, caching the newly hashed subtrees + cached := fullNode{dirty: n.dirty} + + for i := 0; i < 16; i++ { + if n.Children[i] != nil { + if n.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false); err != nil { + return n, original, err + } + } else { + n.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + } + cached.Children[16] = n.Children[16] + if n.Children[16] == nil { + n.Children[16] = valueNode(nil) + } + return n, cached, nil + + default: + // Value and hash nodes don't have children so they're left as were + return n, original, nil + } +} + +func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { + // Don't store hashes or empty nodes. + if _, isHash := n.(hashNode); n == nil || isHash { + return n, nil + } + // Generate the RLP encoding of the node + h.tmp.Reset() + if err := rlp.Encode(h.tmp, n); err != nil { + panic("encode error: " + err.Error()) + } + if h.tmp.Len() < 32 && !force { + return n, nil // Nodes smaller than 32 bytes are stored inside their parent + } + // Larger nodes are replaced by their hash and stored in the database. + hash, _ := n.cache() + if hash == nil { + h.sha.Reset() + h.sha.Write(h.tmp.Bytes()) + hash = hashNode(h.sha.Sum(nil)) + } + if db != nil { + return hash, db.Put(hash, h.tmp.Bytes()) + } + return hash, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/iterator.go b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go index 88c4cee7f..8cad51aff 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/iterator.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go @@ -16,18 +16,13 @@ package trie -import ( - "bytes" - "fmt" +import "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -// Iterator is a key-value trie iterator to traverse the data contents. +// Iterator is a key-value trie iterator that traverses a Trie. type Iterator struct { - trie *Trie + trie *Trie + nodeIt *NodeIterator + keyBuf []byte Key []byte // Current data key on which the iterator is positioned on Value []byte // Current data value on which the iterator is positioned on @@ -35,119 +30,45 @@ type Iterator struct { // NewIterator creates a new key-value iterator. func NewIterator(trie *Trie) *Iterator { - return &Iterator{trie: trie, Key: nil} + return &Iterator{ + trie: trie, + nodeIt: NewNodeIterator(trie), + keyBuf: make([]byte, 0, 64), + Key: nil, + } } -// Next moves the iterator forward with one key-value entry. -func (self *Iterator) Next() bool { - isIterStart := false - if self.Key == nil { - isIterStart = true - self.Key = make([]byte, 32) +// Next moves the iterator forward one key-value entry. +func (it *Iterator) Next() bool { + for it.nodeIt.Next() { + if it.nodeIt.Leaf { + it.Key = it.makeKey() + it.Value = it.nodeIt.LeafBlob + return true + } } - - key := remTerm(compactHexDecode(self.Key)) - k := self.next(self.trie.root, key, isIterStart) - - self.Key = []byte(decodeCompact(k)) - - return len(k) > 0 + it.Key = nil + it.Value = nil + return false } -func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byte { - if node == nil { - return nil +func (it *Iterator) makeKey() []byte { + key := it.keyBuf[:0] + for _, se := range it.nodeIt.stack { + switch node := se.node.(type) { + case fullNode: + if se.child <= 16 { + key = append(key, byte(se.child)) + } + case shortNode: + if hasTerm(node.Key) { + key = append(key, node.Key[:len(node.Key)-1]...) + } else { + key = append(key, node.Key...) + } + } } - - switch node := node.(type) { - case fullNode: - if len(key) > 0 { - k := self.next(node.Children[key[0]], key[1:], isIterStart) - if k != nil { - return append([]byte{key[0]}, k...) - } - } - - var r byte - if len(key) > 0 { - r = key[0] + 1 - } - - for i := r; i < 16; i++ { - k := self.key(node.Children[i]) - if k != nil { - return append([]byte{i}, k...) - } - } - - case shortNode: - k := remTerm(node.Key) - if vnode, ok := node.Val.(valueNode); ok { - switch bytes.Compare([]byte(k), key) { - case 0: - if isIterStart { - self.Value = vnode - return k - } - case 1: - self.Value = vnode - return k - } - } else { - cnode := node.Val - - var ret []byte - skey := key[len(k):] - if bytes.HasPrefix(key, k) { - ret = self.next(cnode, skey, isIterStart) - } else if bytes.Compare(k, key[:len(k)]) > 0 { - return self.key(node) - } - - if ret != nil { - return append(k, ret...) - } - } - - case hashNode: - rn, err := self.trie.resolveHash(node, nil, nil) - if err != nil && glog.V(logger.Error) { - glog.Errorf("Unhandled trie error: %v", err) - } - return self.next(rn, key, isIterStart) - } - return nil -} - -func (self *Iterator) key(node interface{}) []byte { - switch node := node.(type) { - case shortNode: - // Leaf node - k := remTerm(node.Key) - if vnode, ok := node.Val.(valueNode); ok { - self.Value = vnode - return k - } - return append(k, self.key(node.Val)...) - case fullNode: - if node.Children[16] != nil { - self.Value = node.Children[16].(valueNode) - return []byte{16} - } - for i := 0; i < 16; i++ { - k := self.key(node.Children[i]) - if k != nil { - return append([]byte{byte(i)}, k...) - } - } - case hashNode: - rn, err := self.trie.resolveHash(node, nil, nil) - if err != nil && glog.V(logger.Error) { - glog.Errorf("Unhandled trie error: %v", err) - } - return self.key(rn) - } - return nil + return decodeCompact(key) } // nodeIteratorState represents the iteration state at one particular node of the @@ -199,25 +120,27 @@ func (it *NodeIterator) Next() bool { // step moves the iterator to the next node of the trie. func (it *NodeIterator) step() error { - // Abort if we reached the end of the iteration if it.trie == nil { + // Abort if we reached the end of the iteration return nil } - // Initialize the iterator if we've just started, or pop off the old node otherwise if len(it.stack) == 0 { - // Always start with a collapsed root + // Initialize the iterator if we've just started. root := it.trie.Hash() - it.stack = append(it.stack, &nodeIteratorState{node: hashNode(root[:]), child: -1}) - if it.stack[0].node == nil { - return fmt.Errorf("root node missing: %x", it.trie.Hash()) + state := &nodeIteratorState{node: it.trie.root, child: -1} + if root != emptyRoot { + state.hash = root } + it.stack = append(it.stack, state) } else { + // Continue iterating at the previous node otherwise. it.stack = it.stack[:len(it.stack)-1] if len(it.stack) == 0 { it.trie = nil return nil } } + // Continue iteration to the next child for { parent := it.stack[len(it.stack)-1] @@ -232,7 +155,12 @@ func (it *NodeIterator) step() error { } for parent.child++; parent.child < len(node.Children); parent.child++ { if current := node.Children[parent.child]; current != nil { - it.stack = append(it.stack, &nodeIteratorState{node: current, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(node.hash), + node: current, + parent: ancestor, + child: -1, + }) break } } @@ -242,7 +170,12 @@ func (it *NodeIterator) step() error { break } parent.child++ - it.stack = append(it.stack, &nodeIteratorState{node: node.Val, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(node.hash), + node: node.Val, + parent: ancestor, + child: -1, + }) } else if hash, ok := parent.node.(hashNode); ok { // Hash node, resolve the hash child from the database, then the node itself if parent.child >= 0 { @@ -254,7 +187,12 @@ func (it *NodeIterator) step() error { if err != nil { return err } - it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(hash), + node: node, + parent: ancestor, + child: -1, + }) } else { break } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/proof.go b/vendor/github.com/ethereum/go-ethereum/trie/proof.go index 5135de047..116c13a1b 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/proof.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/proof.go @@ -70,15 +70,13 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) } } - if t.hasher == nil { - t.hasher = newHasher() - } + hasher := newHasher() proof := make([]rlp.RawValue, 0, len(nodes)) for i, n := range nodes { // Don't bother checking for errors here since hasher panics // if encoding doesn't work and we're not writing to any database. - n, _, _ = t.hasher.hashChildren(n, nil) - hn, _ := t.hasher.store(n, nil, false) + n, _, _ = hasher.hashChildren(n, nil) + hn, _ := hasher.store(n, nil, false) if _, ok := hn.(hashNode); ok || i == 0 { // If the node's database encoding is a hash (or is the // root node), it becomes a proof element. diff --git a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go index 1d027c102..2a8b57214 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go @@ -17,16 +17,15 @@ package trie import ( - "hash" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) var secureKeyPrefix = []byte("secure-key-") +const secureKeyLength = 11 + 32 // Length of the above prefix + 32byte hash + // SecureTrie wraps a trie with key hashing. In a secure trie, all // access operations hash the key using keccak256. This prevents // calling code from creating long chains of nodes that @@ -38,12 +37,11 @@ var secureKeyPrefix = []byte("secure-key-") // // SecureTrie is not safe for concurrent use. type SecureTrie struct { - *Trie - - hash hash.Hash - hashKeyBuf []byte - secKeyBuf []byte - secKeyCache map[string][]byte + trie Trie + hashKeyBuf [secureKeyLength]byte + secKeyBuf [200]byte + secKeyCache map[string][]byte + secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch } // NewSecure creates a trie with an existing root node from db. @@ -61,8 +59,7 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { return nil, err } return &SecureTrie{ - Trie: trie, - secKeyCache: make(map[string][]byte), + trie: *trie, }, nil } @@ -80,7 +77,7 @@ func (t *SecureTrie) Get(key []byte) []byte { // The value bytes must not be modified by the caller. // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryGet(key []byte) ([]byte, error) { - return t.Trie.TryGet(t.hashKey(key)) + return t.trie.TryGet(t.hashKey(key)) } // Update associates key with value in the trie. Subsequent calls to @@ -105,11 +102,11 @@ func (t *SecureTrie) Update(key, value []byte) { // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryUpdate(key, value []byte) error { hk := t.hashKey(key) - err := t.Trie.TryUpdate(hk, value) + err := t.trie.TryUpdate(hk, value) if err != nil { return err } - t.secKeyCache[string(hk)] = common.CopyBytes(key) + t.getSecKeyCache()[string(hk)] = common.CopyBytes(key) return nil } @@ -124,17 +121,17 @@ func (t *SecureTrie) Delete(key []byte) { // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryDelete(key []byte) error { hk := t.hashKey(key) - delete(t.secKeyCache, string(hk)) - return t.Trie.TryDelete(hk) + delete(t.getSecKeyCache(), string(hk)) + return t.trie.TryDelete(hk) } // GetKey returns the sha3 preimage of a hashed key that was // previously used to store a value. func (t *SecureTrie) GetKey(shaKey []byte) []byte { - if key, ok := t.secKeyCache[string(shaKey)]; ok { + if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { return key } - key, _ := t.Trie.db.Get(t.secKey(shaKey)) + key, _ := t.trie.db.Get(t.secKey(shaKey)) return key } @@ -144,7 +141,23 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte { // Committing flushes nodes from memory. Subsequent Get calls will load nodes // from the database. func (t *SecureTrie) Commit() (root common.Hash, err error) { - return t.CommitTo(t.db) + return t.CommitTo(t.trie.db) +} + +func (t *SecureTrie) Hash() common.Hash { + return t.trie.Hash() +} + +func (t *SecureTrie) Root() []byte { + return t.trie.Root() +} + +func (t *SecureTrie) Iterator() *Iterator { + return t.trie.Iterator() +} + +func (t *SecureTrie) NodeIterator() *NodeIterator { + return NewNodeIterator(&t.trie) } // CommitTo writes all nodes and the secure hash pre-images to the given database. @@ -154,7 +167,7 @@ func (t *SecureTrie) Commit() (root common.Hash, err error) { // the trie's database. Calling code must ensure that the changes made to db are // written back to the trie's attached database before using the trie. func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { - if len(t.secKeyCache) > 0 { + if len(t.getSecKeyCache()) > 0 { for hk, key := range t.secKeyCache { if err := db.Put(t.secKey([]byte(hk)), key); err != nil { return common.Hash{}, err @@ -162,27 +175,37 @@ func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { } t.secKeyCache = make(map[string][]byte) } - n, clean, err := t.hashRoot(db) - if err != nil { - return (common.Hash{}), err - } - t.root = clean - return common.BytesToHash(n.(hashNode)), nil + return t.trie.CommitTo(db) } +// secKey returns the database key for the preimage of key, as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. func (t *SecureTrie) secKey(key []byte) []byte { - t.secKeyBuf = append(t.secKeyBuf[:0], secureKeyPrefix...) - t.secKeyBuf = append(t.secKeyBuf, key...) - return t.secKeyBuf + buf := append(t.secKeyBuf[:0], secureKeyPrefix...) + buf = append(buf, key...) + return buf } +// hashKey returns the hash of key as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. func (t *SecureTrie) hashKey(key []byte) []byte { - if t.hash == nil { - t.hash = sha3.NewKeccak256() - t.hashKeyBuf = make([]byte, 32) - } - t.hash.Reset() - t.hash.Write(key) - t.hashKeyBuf = t.hash.Sum(t.hashKeyBuf[:0]) - return t.hashKeyBuf + h := newHasher() + h.sha.Reset() + h.sha.Write(key) + buf := h.sha.Sum(t.hashKeyBuf[:0]) + returnHasherToPool(h) + return buf +} + +// getSecKeyCache returns the current secure key cache, creating a new one if +// ownership changed (i.e. the current secure trie is a copy of another owning +// the actual cache). +func (t *SecureTrie) getSecKeyCache() map[string][]byte { + if t != t.secKeyCacheOwner { + t.secKeyCacheOwner = t + t.secKeyCache = make(map[string][]byte) + } + return t.secKeyCache } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/trie.go b/vendor/github.com/ethereum/go-ethereum/trie/trie.go index a530e7b2a..93e189e2e 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/trie.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/trie.go @@ -20,22 +20,14 @@ package trie import ( "bytes" "fmt" - "hash" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rlp" ) -const defaultCacheCapacity = 800 - var ( - // The global cache stores decoded trie nodes by hash as they get loaded. - globalCache = newARC(defaultCacheCapacity) - // This is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") @@ -43,11 +35,6 @@ var ( emptyState = crypto.Keccak256Hash(nil) ) -// ClearGlobalCache clears the global trie cache -func ClearGlobalCache() { - globalCache.Clear() -} - // Database must be implemented by backing stores for the trie. type Database interface { DatabaseWriter @@ -72,7 +59,6 @@ type Trie struct { root node db Database originalRoot common.Hash - *hasher } // New creates a trie with an existing root node from db. @@ -118,32 +104,50 @@ func (t *Trie) Get(key []byte) []byte { // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryGet(key []byte) ([]byte, error) { key = compactHexDecode(key) - pos := 0 - tn := t.root - for pos < len(key) { - switch n := tn.(type) { - case shortNode: - if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { - return nil, nil - } - tn = n.Val - pos += len(n.Key) - case fullNode: - tn = n.Children[key[pos]] - pos++ - case nil: - return nil, nil - case hashNode: - var err error - tn, err = t.resolveHash(n, key[:pos], key[pos:]) - if err != nil { - return nil, err - } - default: - panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) - } + value, newroot, didResolve, err := t.tryGet(t.root, key, 0) + if err == nil && didResolve { + t.root = newroot + } + return value, err +} + +func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) { + switch n := (origNode).(type) { + case nil: + return nil, nil, false, nil + case valueNode: + return n, n, false, nil + case shortNode: + if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { + // key not found in trie + return nil, n, false, nil + } + value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key)) + if err == nil && didResolve { + n.Val = newnode + return value, n, didResolve, err + } else { + return value, origNode, didResolve, err + } + case fullNode: + child := n.Children[key[pos]] + value, newnode, didResolve, err = t.tryGet(child, key, pos+1) + if err == nil && didResolve { + n.Children[key[pos]] = newnode + return value, n, didResolve, err + } else { + return value, origNode, didResolve, err + } + case hashNode: + child, err := t.resolveHash(n, key[:pos], key[pos:]) + if err != nil { + return nil, n, true, err + } + value, newnode, _, err := t.tryGet(child, key, pos) + return value, newnode, true, err + default: + panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode)) } - return tn.(valueNode), nil } // Update associates key with value in the trie. Subsequent calls to @@ -410,9 +414,6 @@ func (t *Trie) resolve(n node, prefix, suffix []byte) (node, error) { } func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { - if v, ok := globalCache.Get(n); ok { - return v, nil - } enc, err := t.db.Get(n) if err != nil || enc == nil { return nil, &MissingNodeError{ @@ -424,9 +425,6 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { } } dec := mustDecodeNode(n, enc) - if dec != nil { - globalCache.Put(n, dec) - } return dec, nil } @@ -474,127 +472,7 @@ func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) { if t.root == nil { return hashNode(emptyRoot.Bytes()), nil, nil } - if t.hasher == nil { - t.hasher = newHasher() - } - return t.hasher.hash(t.root, db, true) -} - -type hasher struct { - tmp *bytes.Buffer - sha hash.Hash -} - -func newHasher() *hasher { - return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} -} - -// hash collapses a node down into a hash node, also returning a copy of the -// original node initialzied with the computed hash to replace the original one. -func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { - // If we're not storing the node, just hashing, use avaialble cached data - if hash, dirty := n.cache(); hash != nil && (db == nil || !dirty) { - return hash, n, nil - } - // Trie not processed yet or needs storage, walk the children - collapsed, cached, err := h.hashChildren(n, db) - if err != nil { - return hashNode{}, n, err - } - hashed, err := h.store(collapsed, db, force) - if err != nil { - return hashNode{}, n, err - } - // Cache the hash and RLP blob of the ndoe for later reuse - if hash, ok := hashed.(hashNode); ok && !force { - switch cached := cached.(type) { - case shortNode: - cached.hash = hash - if db != nil { - cached.dirty = false - } - return hashed, cached, nil - case fullNode: - cached.hash = hash - if db != nil { - cached.dirty = false - } - return hashed, cached, nil - } - } - return hashed, cached, nil -} - -// hashChildren replaces the children of a node with their hashes if the encoded -// size of the child is larger than a hash, returning the collapsed node as well -// as a replacement for the original node with the child hashes cached in. -func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { - var err error - - switch n := original.(type) { - case shortNode: - // Hash the short node's child, caching the newly hashed subtree - cached := n - cached.Key = common.CopyBytes(cached.Key) - - n.Key = compactEncode(n.Key) - if _, ok := n.Val.(valueNode); !ok { - if n.Val, cached.Val, err = h.hash(n.Val, db, false); err != nil { - return n, original, err - } - } - if n.Val == nil { - n.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } - return n, cached, nil - - case fullNode: - // Hash the full node's children, caching the newly hashed subtrees - cached := fullNode{dirty: n.dirty} - - for i := 0; i < 16; i++ { - if n.Children[i] != nil { - if n.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false); err != nil { - return n, original, err - } - } else { - n.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } - } - cached.Children[16] = n.Children[16] - if n.Children[16] == nil { - n.Children[16] = valueNode(nil) - } - return n, cached, nil - - default: - // Value and hash nodes don't have children so they're left as were - return n, original, nil - } -} - -func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { - // Don't store hashes or empty nodes. - if _, isHash := n.(hashNode); n == nil || isHash { - return n, nil - } - // Generate the RLP encoding of the node - h.tmp.Reset() - if err := rlp.Encode(h.tmp, n); err != nil { - panic("encode error: " + err.Error()) - } - if h.tmp.Len() < 32 && !force { - return n, nil // Nodes smaller than 32 bytes are stored inside their parent - } - // Larger nodes are replaced by their hash and stored in the database. - hash, _ := n.cache() - if hash == nil { - h.sha.Reset() - h.sha.Write(h.tmp.Bytes()) - hash = hashNode(h.sha.Sum(nil)) - } - if db != nil { - return hash, db.Put(hash, h.tmp.Bytes()) - } - return hash, nil + h := newHasher() + defer returnHasherToPool(h) + return h.hash(t.root, db, true) }