Rebased against the latest light-stable
This commit is contained in:
parent
53efc18c18
commit
009fab27fd
18
geth/node.go
18
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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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))
|
||||
}
|
256
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
256
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
|
|
68
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go
generated
vendored
Normal file
68
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
642
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go
generated
vendored
Normal file
642
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
541
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go
generated
vendored
Normal file
541
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go
generated
vendored
Normal file
|
@ -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...)
|
||||
}
|
45
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol
generated
vendored
Normal file
45
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
import "mortal";
|
||||
|
||||
/// @title Chequebook for Ethereum micropayments
|
||||
/// @author Daniel A. Nagy <daniel@ethdev.com>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
5
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go
generated
vendored
Normal file
5
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go
generated
vendored
Normal file
|
@ -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"
|
71
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go
generated
vendored
Normal file
71
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +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)
|
||||
}
|
||||
}
|
921
vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go
generated
vendored
Normal file
921
vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
226
vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol
generated
vendored
Normal file
226
vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol
generated
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
// Ethereum Name Service contracts by Nick Johnson <nick@ethereum.org>
|
||||
//
|
||||
// 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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: []
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
69
vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go
generated
vendored
Normal file
69
vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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]
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//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()
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
150
vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go
generated
vendored
Normal file
150
vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
173
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go
generated
vendored
Normal file
173
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
351
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go
generated
vendored
Normal file
351
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
429
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go
generated
vendored
Normal file
429
vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue