Merge pull request #2159 from zsfelfoldi/light-backend
eth: separate common and full node-specific API and backend service
This commit is contained in:
		| @@ -22,6 +22,7 @@ import ( | ||||
|  | ||||
| 	"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 | ||||
| @@ -35,12 +36,12 @@ 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(contract common.Address, pending bool) (bool, error) | ||||
| 	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(contract common.Address, data []byte, pending bool) ([]byte, error) | ||||
| 	ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) | ||||
| } | ||||
|  | ||||
| // ContractTransactor defines the methods needed to allow operating with contract | ||||
| @@ -50,26 +51,26 @@ type ContractCaller interface { | ||||
| type ContractTransactor interface { | ||||
| 	// PendingAccountNonce retrieves the current pending nonce associated with an | ||||
| 	// account. | ||||
| 	PendingAccountNonce(account common.Address) (uint64, error) | ||||
| 	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() (*big.Int, error) | ||||
| 	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(contract common.Address, pending bool) (bool, error) | ||||
| 	HasCode(ctx context.Context, contract common.Address, pending bool) (bool, 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(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) | ||||
| 	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(tx *types.Transaction) error | ||||
| 	SendTransaction(ctx context.Context, tx *types.Transaction) error | ||||
| } | ||||
|  | ||||
| // ContractBackend defines the methods needed to allow operating with contract | ||||
| @@ -84,28 +85,28 @@ 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(contract common.Address, pending bool) (bool, error) | ||||
| 	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(contract common.Address, data []byte, pending bool) ([]byte, error) | ||||
| 	ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) | ||||
|  | ||||
| 	// PendingAccountNonce retrieves the current pending nonce associated with an | ||||
| 	// account. | ||||
| 	PendingAccountNonce(account common.Address) (uint64, error) | ||||
| 	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() (*big.Int, error) | ||||
| 	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(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) | ||||
| 	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(tx *types.Transaction) error | ||||
| 	SendTransaction(ctx context.Context, tx *types.Transaction) error | ||||
| } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import ( | ||||
| 	"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. | ||||
| @@ -32,16 +33,22 @@ var _ bind.ContractBackend = (*nilBackend)(nil) | ||||
| // wrappers without calling any methods on them. | ||||
| type nilBackend struct{} | ||||
|  | ||||
| func (*nilBackend) ContractCall(common.Address, []byte, bool) ([]byte, error) { | ||||
| func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) { | ||||
| 	panic("not implemented") | ||||
| } | ||||
| func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) { | ||||
| 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") | ||||
| } | ||||
| func (*nilBackend) HasCode(common.Address, bool) (bool, error)         { panic("not implemented") } | ||||
| func (*nilBackend) SuggestGasPrice() (*big.Int, error)                 { panic("not implemented") } | ||||
| func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") } | ||||
| func (*nilBackend) SendTransaction(*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. | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import ( | ||||
| 	"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. | ||||
| @@ -80,18 +81,23 @@ type failure struct { | ||||
| // | ||||
| // This is currently painfully non-concurrent, but it will have to do until we | ||||
| // find the time for niceties like this :P | ||||
| func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessage, error) { | ||||
| func (b *rpcBackend) request(ctx context.Context, method string, params []interface{}) (json.RawMessage, error) { | ||||
| 	b.lock.Lock() | ||||
| 	defer b.lock.Unlock() | ||||
|  | ||||
| 	if ctx == nil { | ||||
| 		ctx = context.Background() | ||||
| 	} | ||||
|  | ||||
| 	// Ugly hack to serialize an empty list properly | ||||
| 	if params == nil { | ||||
| 		params = []interface{}{} | ||||
| 	} | ||||
| 	// Assemble the request object | ||||
| 	reqID := int(atomic.AddUint32(&b.autoid, 1)) | ||||
| 	req := &request{ | ||||
| 		JSONRPC: "2.0", | ||||
| 		ID:      int(atomic.AddUint32(&b.autoid, 1)), | ||||
| 		ID:      reqID, | ||||
| 		Method:  method, | ||||
| 		Params:  params, | ||||
| 	} | ||||
| @@ -99,9 +105,18 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	res := new(response) | ||||
| 	if err := b.client.Recv(res); err != nil { | ||||
| 	errc := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		errc <- b.client.Recv(res) | ||||
| 	}() | ||||
| 	select { | ||||
| 	case err := <-errc: | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, ctx.Err() | ||||
| 	} | ||||
| 	if res.Error != nil { | ||||
| 		if res.Error.Message == bind.ErrNoCode.Error() { | ||||
| 			return nil, bind.ErrNoCode | ||||
| @@ -113,13 +128,13 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa | ||||
|  | ||||
| // HasCode implements ContractVerifier.HasCode by retrieving any code associated | ||||
| // with the contract from the remote node, and checking its size. | ||||
| func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error) { | ||||
| func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { | ||||
| 	// Execute the RPC code retrieval | ||||
| 	block := "latest" | ||||
| 	if pending { | ||||
| 		block = "pending" | ||||
| 	} | ||||
| 	res, err := b.request("eth_getCode", []interface{}{contract.Hex(), block}) | ||||
| 	res, err := b.request(ctx, "eth_getCode", []interface{}{contract.Hex(), block}) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| @@ -133,7 +148,7 @@ func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error | ||||
|  | ||||
| // 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(contract common.Address, data []byte, pending bool) ([]byte, error) { | ||||
| func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { | ||||
| 	// Pack up the request into an RPC argument | ||||
| 	args := struct { | ||||
| 		To   common.Address `json:"to"` | ||||
| @@ -147,7 +162,7 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending | ||||
| 	if pending { | ||||
| 		block = "pending" | ||||
| 	} | ||||
| 	res, err := b.request("eth_call", []interface{}{args, block}) | ||||
| 	res, err := b.request(ctx, "eth_call", []interface{}{args, block}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -161,8 +176,8 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending | ||||
|  | ||||
| // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating | ||||
| // the current account nonce retrieval to the remote node. | ||||
| func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) { | ||||
| 	res, err := b.request("eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) | ||||
| func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { | ||||
| 	res, err := b.request(ctx, "eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| @@ -179,8 +194,8 @@ func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) | ||||
|  | ||||
| // SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the | ||||
| // gas price oracle request to the remote node. | ||||
| func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { | ||||
| 	res, err := b.request("eth_gasPrice", nil) | ||||
| func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { | ||||
| 	res, err := b.request(ctx, "eth_gasPrice", nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -197,7 +212,7 @@ func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { | ||||
|  | ||||
| // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating | ||||
| // the gas estimation to the remote node. | ||||
| func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { | ||||
| func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { | ||||
| 	// Pack up the request into an RPC argument | ||||
| 	args := struct { | ||||
| 		From  common.Address  `json:"from"` | ||||
| @@ -211,7 +226,7 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad | ||||
| 		Value: rpc.NewHexNumber(value), | ||||
| 	} | ||||
| 	// Execute the RPC call and retrieve the response | ||||
| 	res, err := b.request("eth_estimateGas", []interface{}{args}) | ||||
| 	res, err := b.request(ctx, "eth_estimateGas", []interface{}{args}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -228,12 +243,12 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad | ||||
|  | ||||
| // SendTransaction implements ContractTransactor.SendTransaction, delegating the | ||||
| // raw transaction injection to the remote node. | ||||
| func (b *rpcBackend) SendTransaction(tx *types.Transaction) error { | ||||
| func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { | ||||
| 	data, err := rlp.EncodeToBytes(tx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	res, err := b.request("eth_sendRawTransaction", []interface{}{common.ToHex(data)}) | ||||
| 	res, err := b.request(ctx, "eth_sendRawTransaction", []interface{}{common.ToHex(data)}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
| @@ -27,6 +27,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) | ||||
| @@ -80,7 +81,7 @@ func (b *SimulatedBackend) Rollback() { | ||||
|  | ||||
| // HasCode implements ContractVerifier.HasCode, checking whether there is any | ||||
| // code associated with a certain account in the blockchain. | ||||
| func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, error) { | ||||
| func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { | ||||
| 	if pending { | ||||
| 		return len(b.pendingState.GetCode(contract)) > 0, nil | ||||
| 	} | ||||
| @@ -90,7 +91,7 @@ func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, | ||||
|  | ||||
| // ContractCall implements ContractCaller.ContractCall, executing the specified | ||||
| // contract with the given input data. | ||||
| func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) { | ||||
| 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 | ||||
| @@ -129,20 +130,20 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe | ||||
|  | ||||
| // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving | ||||
| // the nonce currently pending for the account. | ||||
| func (b *SimulatedBackend) PendingAccountNonce(account common.Address) (uint64, error) { | ||||
| func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { | ||||
| 	return b.pendingState.GetOrNewStateObject(account).Nonce(), nil | ||||
| } | ||||
|  | ||||
| // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated | ||||
| // chain doens't have miners, we just return a gas price of 1 for any call. | ||||
| func (b *SimulatedBackend) SuggestGasPrice() (*big.Int, error) { | ||||
| 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(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { | ||||
| 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 | ||||
| @@ -177,7 +178,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com | ||||
|  | ||||
| // SendTransaction implements ContractTransactor.SendTransaction, delegating the raw | ||||
| // transaction injection to the remote node. | ||||
| func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error { | ||||
| func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { | ||||
| 	blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { | ||||
| 		for _, tx := range b.pendingBlock.Transactions() { | ||||
| 			block.AddTx(tx) | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // SignerFn is a signer function callback when a contract requires a method to | ||||
| @@ -35,6 +36,8 @@ type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, erro | ||||
| // CallOpts is the collection of options to fine tune a contract call request. | ||||
| type CallOpts struct { | ||||
| 	Pending bool // Whether to operate on the pending state or the last known one | ||||
|  | ||||
| 	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) | ||||
| } | ||||
|  | ||||
| // TransactOpts is the collection of authorization data required to create a | ||||
| @@ -47,6 +50,8 @@ type TransactOpts struct { | ||||
| 	Value    *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) | ||||
| 	GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) | ||||
| 	GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%) | ||||
|  | ||||
| 	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) | ||||
| } | ||||
|  | ||||
| // BoundContract is the base wrapper object that reflects a contract on the | ||||
| @@ -102,7 +107,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, | ||||
| 	} | ||||
| 	// 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(c.address, opts.Pending); err != nil { | ||||
| 		if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil { | ||||
| 			return err | ||||
| 		} else if !code { | ||||
| 			return ErrNoCode | ||||
| @@ -118,7 +123,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	output, err := c.caller.ContractCall(c.address, input, opts.Pending) | ||||
| 	output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -153,7 +158,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i | ||||
| 	} | ||||
| 	nonce := uint64(0) | ||||
| 	if opts.Nonce == nil { | ||||
| 		nonce, err = c.transactor.PendingAccountNonce(opts.From) | ||||
| 		nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) | ||||
| 		} | ||||
| @@ -163,7 +168,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() | ||||
| 		gasPrice, err = c.transactor.SuggestGasPrice(opts.Context) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to suggest gas price: %v", err) | ||||
| 		} | ||||
| @@ -172,7 +177,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i | ||||
| 	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(c.address, true); err != nil { | ||||
| 			if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil { | ||||
| 				return nil, err | ||||
| 			} else if !code { | ||||
| 				return nil, ErrNoCode | ||||
| @@ -180,7 +185,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i | ||||
| 			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.From, contract, value, input) | ||||
| 		gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to exstimate gas needed: %v", err) | ||||
| 		} | ||||
| @@ -199,7 +204,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := c.transactor.SendTransaction(signedTx); err != nil { | ||||
| 	if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return signedTx, nil | ||||
|   | ||||
| @@ -34,6 +34,8 @@ import ( | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/p2p" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -340,3 +342,23 @@ func zeroKey(k *ecdsa.PrivateKey) { | ||||
| 		b[i] = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // APIs implements node.Service | ||||
| func (am *Manager) APIs() []rpc.API { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Protocols implements node.Service | ||||
| func (am *Manager) Protocols() []p2p.Protocol { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Start implements node.Service | ||||
| func (am *Manager) Start(srvr *p2p.Server) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Stop implements node.Service | ||||
| func (am *Manager) Stop() error { | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/ethereum/ethash" | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/cmd/utils" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/console" | ||||
| @@ -313,11 +314,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { | ||||
| 	utils.StartNode(stack) | ||||
|  | ||||
| 	// Unlock any account specifically requested | ||||
| 	var ethereum *eth.Ethereum | ||||
| 	if err := stack.Service(ðereum); err != nil { | ||||
| 	var accman *accounts.Manager | ||||
| 	if err := stack.Service(&accman); err != nil { | ||||
| 		utils.Fatalf("ethereum service not running: %v", err) | ||||
| 	} | ||||
| 	accman := ethereum.AccountManager() | ||||
| 	passwords := utils.MakePasswordList(ctx) | ||||
|  | ||||
| 	accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") | ||||
| @@ -328,6 +328,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { | ||||
| 	} | ||||
| 	// Start auxiliary services if enabled | ||||
| 	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { | ||||
| 		var ethereum *eth.FullNodeService | ||||
| 		if err := stack.Service(ðereum); err != nil { | ||||
| 			utils.Fatalf("ethereum service not running: %v", err) | ||||
| 		} | ||||
| 		if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { | ||||
| 			utils.Fatalf("Failed to start mining: %v", err) | ||||
| 		} | ||||
|   | ||||
| @@ -146,7 +146,7 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node | ||||
| // RunTest executes the specified test against an already pre-configured protocol | ||||
| // stack to ensure basic checks pass before running RPC tests. | ||||
| func RunTest(stack *node.Node, test *tests.BlockTest) error { | ||||
| 	var ethereum *eth.Ethereum | ||||
| 	var ethereum *eth.FullNodeService | ||||
| 	stack.Service(ðereum) | ||||
| 	blockchain := ethereum.BlockChain() | ||||
|  | ||||
|   | ||||
| @@ -763,6 +763,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte, | ||||
| 	if err != nil { | ||||
| 		Fatalf("Failed to create the protocol stack: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { | ||||
| 		return accman, nil | ||||
| 	}); err != nil { | ||||
| 		Fatalf("Failed to register the account manager service: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { | ||||
| 		return eth.New(ctx, ethConf) | ||||
| 	}); err != nil { | ||||
|   | ||||
| @@ -99,7 +99,7 @@ const ( | ||||
|  | ||||
| type testFrontend struct { | ||||
| 	t           *testing.T | ||||
| 	ethereum    *eth.Ethereum | ||||
| 	ethereum    *eth.FullNodeService | ||||
| 	xeth        *xe.XEth | ||||
| 	wait        chan *big.Int | ||||
| 	lastConfirm string | ||||
| @@ -123,7 +123,7 @@ func (self *testFrontend) ConfirmTransaction(tx string) bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { | ||||
| func testEth(t *testing.T) (ethereum *eth.FullNodeService, err error) { | ||||
|  | ||||
| 	tmp, err := ioutil.TempDir("", "natspec-test") | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -76,7 +76,7 @@ func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} | ||||
| type tester struct { | ||||
| 	workspace string | ||||
| 	stack     *node.Node | ||||
| 	ethereum  *eth.Ethereum | ||||
| 	ethereum  *eth.FullNodeService | ||||
| 	console   *Console | ||||
| 	input     *hookedPrompter | ||||
| 	output    *bytes.Buffer | ||||
| @@ -134,7 +134,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { | ||||
| 		t.Fatalf("failed to create JavaScript console: %v", err) | ||||
| 	} | ||||
| 	// Create the final tester and return | ||||
| 	var ethereum *eth.Ethereum | ||||
| 	var ethereum *eth.FullNodeService | ||||
| 	stack.Service(ðereum) | ||||
|  | ||||
| 	return &tester{ | ||||
|   | ||||
| @@ -73,6 +73,8 @@ 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. | ||||
|   | ||||
							
								
								
									
										1603
									
								
								eth/api.go
									
									
									
									
									
								
							
							
						
						
									
										1603
									
								
								eth/api.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										201
									
								
								eth/api_backend.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								eth/api_backend.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| // Copyright 2015 The go-ethereum Authors | ||||
| // This file is part of go-ethereum. | ||||
| // | ||||
| // go-ethereum is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // go-ethereum is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| package eth | ||||
|  | ||||
| import ( | ||||
| 	"math/big" | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/state" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| 	"github.com/ethereum/go-ethereum/eth/downloader" | ||||
| 	"github.com/ethereum/go-ethereum/eth/gasprice" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/internal/ethapi" | ||||
| 	rpc "github.com/ethereum/go-ethereum/rpc" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // EthApiBackend implements ethapi.Backend for full nodes | ||||
| type EthApiBackend struct { | ||||
| 	eth *FullNodeService | ||||
| 	gpo *gasprice.GasPriceOracle | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) SetHead(number uint64) { | ||||
| 	b.eth.blockchain.SetHead(number) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) HeaderByNumber(blockNr rpc.BlockNumber) *types.Header { | ||||
| 	// Pending block is only known by the miner | ||||
| 	if blockNr == rpc.PendingBlockNumber { | ||||
| 		block, _ := b.eth.miner.Pending() | ||||
| 		return block.Header() | ||||
| 	} | ||||
| 	// Otherwise resolve and return the block | ||||
| 	if blockNr == rpc.LatestBlockNumber { | ||||
| 		return b.eth.blockchain.CurrentBlock().Header() | ||||
| 	} | ||||
| 	return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { | ||||
| 	// Pending block is only known by the miner | ||||
| 	if blockNr == rpc.PendingBlockNumber { | ||||
| 		block, _ := b.eth.miner.Pending() | ||||
| 		return block, nil | ||||
| 	} | ||||
| 	// Otherwise resolve and return the block | ||||
| 	if blockNr == rpc.LatestBlockNumber { | ||||
| 		return b.eth.blockchain.CurrentBlock(), nil | ||||
| 	} | ||||
| 	return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) StateAndHeaderByNumber(blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { | ||||
| 	// Pending state is only known by the miner | ||||
| 	if blockNr == rpc.PendingBlockNumber { | ||||
| 		block, state := b.eth.miner.Pending() | ||||
| 		return EthApiState{state}, block.Header(), nil | ||||
| 	} | ||||
| 	// Otherwise resolve the block number and return its state | ||||
| 	header := b.HeaderByNumber(blockNr) | ||||
| 	if header == nil { | ||||
| 		return nil, nil, nil | ||||
| 	} | ||||
| 	stateDb, err := state.New(header.Root, b.eth.chainDb) | ||||
| 	return EthApiState{stateDb}, header, err | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { | ||||
| 	return b.eth.blockchain.GetBlockByHash(blockHash), nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { | ||||
| 	return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { | ||||
| 	return b.eth.blockchain.GetTdByHash(blockHash) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { | ||||
| 	stateDb := state.(EthApiState).state.Copy() | ||||
| 	addr, _ := msg.From() | ||||
| 	from := stateDb.GetOrNewStateObject(addr) | ||||
| 	from.SetBalance(common.MaxBig) | ||||
| 	vmError := func() error { return nil } | ||||
| 	return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	b.eth.txPool.SetLocal(signedTx) | ||||
| 	return b.eth.txPool.Add(signedTx) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) RemoveTx(txHash common.Hash) { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	b.eth.txPool.RemoveTx(txHash) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetPoolTransactions() types.Transactions { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	return b.eth.txPool.GetTransactions() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	return b.eth.txPool.GetTransaction(txHash) | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	return b.eth.txPool.State().GetNonce(addr), nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) Stats() (pending int, queued int) { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	return b.eth.txPool.Stats() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { | ||||
| 	b.eth.txMu.Lock() | ||||
| 	defer b.eth.txMu.Unlock() | ||||
|  | ||||
| 	return b.eth.TxPool().Content() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) Downloader() *downloader.Downloader { | ||||
| 	return b.eth.Downloader() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) ProtocolVersion() int { | ||||
| 	return b.eth.EthVersion() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { | ||||
| 	return b.gpo.SuggestPrice(), nil | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) ChainDb() ethdb.Database { | ||||
| 	return b.eth.ChainDb() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) EventMux() *event.TypeMux { | ||||
| 	return b.eth.EventMux() | ||||
| } | ||||
|  | ||||
| func (b *EthApiBackend) AccountManager() *accounts.Manager { | ||||
| 	return b.eth.AccountManager() | ||||
| } | ||||
|  | ||||
| type EthApiState struct { | ||||
| 	state *state.StateDB | ||||
| } | ||||
|  | ||||
| func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { | ||||
| 	return s.state.GetBalance(addr), nil | ||||
| } | ||||
|  | ||||
| func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { | ||||
| 	return s.state.GetCode(addr), nil | ||||
| } | ||||
|  | ||||
| func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { | ||||
| 	return s.state.GetState(a, b), nil | ||||
| } | ||||
|  | ||||
| func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { | ||||
| 	return s.state.GetNonce(addr), nil | ||||
| } | ||||
							
								
								
									
										322
									
								
								eth/backend.go
									
									
									
									
									
								
							
							
						
						
									
										322
									
								
								eth/backend.go
									
									
									
									
									
								
							| @@ -39,8 +39,10 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| 	"github.com/ethereum/go-ethereum/eth/downloader" | ||||
| 	"github.com/ethereum/go-ethereum/eth/filters" | ||||
| 	"github.com/ethereum/go-ethereum/eth/gasprice" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/internal/ethapi" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | ||||
| 	"github.com/ethereum/go-ethereum/miner" | ||||
| @@ -101,95 +103,86 @@ type Config struct { | ||||
| 	TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) | ||||
| } | ||||
|  | ||||
| type Ethereum struct { | ||||
| // FullNodeService implements the Ethereum full node service. | ||||
| type FullNodeService struct { | ||||
| 	chainConfig *core.ChainConfig | ||||
| 	// Channel for shutting down the service | ||||
| 	shutdownChan  chan bool // Channel for shutting down the ethereum | ||||
| 	stopDbUpgrade func()    // stop chain db sequential key upgrade | ||||
|  | ||||
| 	// DB interfaces | ||||
| 	chainDb ethdb.Database // Block chain database | ||||
| 	dappDb  ethdb.Database // Dapp database | ||||
|  | ||||
| 	// Handlers | ||||
| 	txPool          *core.TxPool | ||||
| 	txMu            sync.Mutex | ||||
| 	blockchain      *core.BlockChain | ||||
| 	accountManager  *accounts.Manager | ||||
| 	pow             *ethash.Ethash | ||||
| 	protocolManager *ProtocolManager | ||||
| 	SolcPath        string | ||||
| 	solc            *compiler.Solidity | ||||
| 	gpo             *GasPriceOracle | ||||
|  | ||||
| 	GpoMinGasPrice          *big.Int | ||||
| 	GpoMaxGasPrice          *big.Int | ||||
| 	GpoFullBlockRatio       int | ||||
| 	GpobaseStepDown         int | ||||
| 	GpobaseStepUp           int | ||||
| 	GpobaseCorrectionFactor int | ||||
|  | ||||
| 	httpclient *httpclient.HTTPClient | ||||
| 	// DB interfaces | ||||
| 	chainDb ethdb.Database // Block chain database | ||||
| 	dappDb  ethdb.Database // Dapp database | ||||
|  | ||||
| 	eventMux       *event.TypeMux | ||||
| 	miner    *miner.Miner | ||||
| 	pow            *ethash.Ethash | ||||
| 	httpclient     *httpclient.HTTPClient | ||||
| 	accountManager *accounts.Manager | ||||
|  | ||||
| 	apiBackend *EthApiBackend | ||||
|  | ||||
| 	miner        *miner.Miner | ||||
| 	Mining       bool | ||||
| 	MinerThreads int | ||||
| 	NatSpec       bool | ||||
| 	AutoDAG      bool | ||||
| 	PowTest       bool | ||||
| 	autodagquit  chan bool | ||||
| 	etherbase    common.Address | ||||
| 	solcPath     string | ||||
| 	solc         *compiler.Solidity | ||||
|  | ||||
| 	NatSpec       bool | ||||
| 	PowTest       bool | ||||
| 	netVersionId  int | ||||
| 	netRPCService *PublicNetAPI | ||||
| 	netRPCService *ethapi.PublicNetAPI | ||||
| } | ||||
|  | ||||
| func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | ||||
| 	// Open the chain database and perform any upgrades needed | ||||
| 	chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles) | ||||
| // New creates a new FullNodeService object (including the | ||||
| // initialisation of the common Ethereum object) | ||||
| func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) { | ||||
| 	chainDb, dappDb, err := CreateDBs(ctx, config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if db, ok := chainDb.(*ethdb.LDBDatabase); ok { | ||||
| 		db.Meter("eth/db/chaindata/") | ||||
| 	stopDbUpgrade := upgradeSequentialKeys(chainDb) | ||||
| 	if err := SetupGenesisBlock(&chainDb, config); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	pow, err := CreatePoW(config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	eth := &FullNodeService{ | ||||
| 		chainDb:        chainDb, | ||||
| 		dappDb:         dappDb, | ||||
| 		eventMux:       ctx.EventMux, | ||||
| 		accountManager: config.AccountManager, | ||||
| 		pow:            pow, | ||||
| 		shutdownChan:   make(chan bool), | ||||
| 		stopDbUpgrade:  stopDbUpgrade, | ||||
| 		httpclient:     httpclient.New(config.DocRoot), | ||||
| 		netVersionId:   config.NetworkId, | ||||
| 		NatSpec:        config.NatSpec, | ||||
| 		PowTest:        config.PowTest, | ||||
| 		etherbase:      config.Etherbase, | ||||
| 		MinerThreads:   config.MinerThreads, | ||||
| 		AutoDAG:        config.AutoDAG, | ||||
| 		solcPath:       config.SolcPath, | ||||
| 	} | ||||
|  | ||||
| 	if err := upgradeChainDatabase(chainDb); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := addMipmapBloomBins(chainDb); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	stopDbUpgrade := upgradeSequentialKeys(chainDb) | ||||
|  | ||||
| 	dappDb, err := ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if db, ok := dappDb.(*ethdb.LDBDatabase); ok { | ||||
| 		db.Meter("eth/db/dapp/") | ||||
| 	} | ||||
| 	glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId) | ||||
|  | ||||
| 	// Load up any custom genesis block if requested | ||||
| 	if len(config.Genesis) > 0 { | ||||
| 		block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis)) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) | ||||
| 	} | ||||
|  | ||||
| 	// Load up a test setup if directly injected | ||||
| 	if config.TestGenesisState != nil { | ||||
| 		chainDb = config.TestGenesisState | ||||
| 	} | ||||
| 	if config.TestGenesisBlock != nil { | ||||
| 		core.WriteTd(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) | ||||
| 		core.WriteBlock(chainDb, config.TestGenesisBlock) | ||||
| 		core.WriteCanonicalHash(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) | ||||
| 		core.WriteHeadBlockHash(chainDb, config.TestGenesisBlock.Hash()) | ||||
| 	} | ||||
|  | ||||
| 	if !config.SkipBcVersionCheck { | ||||
| 		bcVersion := core.GetBlockChainVersion(chainDb) | ||||
| 		if bcVersion != config.BlockChainVersion && bcVersion != 0 { | ||||
| @@ -197,44 +190,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | ||||
| 		} | ||||
| 		core.WriteBlockChainVersion(chainDb, config.BlockChainVersion) | ||||
| 	} | ||||
| 	glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion) | ||||
|  | ||||
| 	eth := &Ethereum{ | ||||
| 		shutdownChan:            make(chan bool), | ||||
| 		stopDbUpgrade:           stopDbUpgrade, | ||||
| 		chainDb:                 chainDb, | ||||
| 		dappDb:                  dappDb, | ||||
| 		eventMux:                ctx.EventMux, | ||||
| 		accountManager:          config.AccountManager, | ||||
| 		etherbase:               config.Etherbase, | ||||
| 		netVersionId:            config.NetworkId, | ||||
| 		NatSpec:                 config.NatSpec, | ||||
| 		MinerThreads:            config.MinerThreads, | ||||
| 		SolcPath:                config.SolcPath, | ||||
| 		AutoDAG:                 config.AutoDAG, | ||||
| 		PowTest:                 config.PowTest, | ||||
| 		GpoMinGasPrice:          config.GpoMinGasPrice, | ||||
| 		GpoMaxGasPrice:          config.GpoMaxGasPrice, | ||||
| 		GpoFullBlockRatio:       config.GpoFullBlockRatio, | ||||
| 		GpobaseStepDown:         config.GpobaseStepDown, | ||||
| 		GpobaseStepUp:           config.GpobaseStepUp, | ||||
| 		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, | ||||
| 		httpclient:              httpclient.New(config.DocRoot), | ||||
| 	} | ||||
| 	switch { | ||||
| 	case config.PowTest: | ||||
| 		glog.V(logger.Info).Infof("ethash used in test mode") | ||||
| 		eth.pow, err = ethash.NewForTesting() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	case config.PowShared: | ||||
| 		glog.V(logger.Info).Infof("ethash used in shared mode") | ||||
| 		eth.pow = ethash.NewShared() | ||||
|  | ||||
| 	default: | ||||
| 		eth.pow = ethash.New() | ||||
| 	} | ||||
|  | ||||
| 	// load the genesis block or write a new one if no genesis | ||||
| 	// block is prenent in the database. | ||||
| @@ -263,8 +218,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	eth.gpo = NewGasPriceOracle(eth) | ||||
|  | ||||
| 	newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) | ||||
| 	eth.txPool = newPool | ||||
|  | ||||
| @@ -275,37 +228,87 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | ||||
| 	eth.miner.SetGasPrice(config.GasPrice) | ||||
| 	eth.miner.SetExtra(config.ExtraData) | ||||
|  | ||||
| 	gpoParams := &gasprice.GpoParams{ | ||||
| 		GpoMinGasPrice:          config.GpoMinGasPrice, | ||||
| 		GpoMaxGasPrice:          config.GpoMaxGasPrice, | ||||
| 		GpoFullBlockRatio:       config.GpoFullBlockRatio, | ||||
| 		GpobaseStepDown:         config.GpobaseStepDown, | ||||
| 		GpobaseStepUp:           config.GpobaseStepUp, | ||||
| 		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, | ||||
| 	} | ||||
| 	gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) | ||||
| 	eth.apiBackend = &EthApiBackend{eth, gpo} | ||||
|  | ||||
| 	return eth, nil | ||||
| } | ||||
|  | ||||
| // CreateDBs creates the chain and dapp databases for an Ethereum service | ||||
| func CreateDBs(ctx *node.ServiceContext, config *Config) (chainDb, dappDb ethdb.Database, err error) { | ||||
| 	// Open the chain database and perform any upgrades needed | ||||
| 	chainDb, err = ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if db, ok := chainDb.(*ethdb.LDBDatabase); ok { | ||||
| 		db.Meter("eth/db/chaindata/") | ||||
| 	} | ||||
|  | ||||
| 	dappDb, err = ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if db, ok := dappDb.(*ethdb.LDBDatabase); ok { | ||||
| 		db.Meter("eth/db/dapp/") | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // SetupGenesisBlock initializes the genesis block for an Ethereum service | ||||
| func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error { | ||||
| 	// Load up any custom genesis block if requested | ||||
| 	if len(config.Genesis) > 0 { | ||||
| 		block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) | ||||
| 	} | ||||
| 	// Load up a test setup if directly injected | ||||
| 	if config.TestGenesisState != nil { | ||||
| 		*chainDb = config.TestGenesisState | ||||
| 	} | ||||
| 	if config.TestGenesisBlock != nil { | ||||
| 		core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) | ||||
| 		core.WriteBlock(*chainDb, config.TestGenesisBlock) | ||||
| 		core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) | ||||
| 		core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash()) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CreatePoW creates the required type of PoW instance for an Ethereum service | ||||
| func CreatePoW(config *Config) (*ethash.Ethash, error) { | ||||
| 	switch { | ||||
| 	case config.PowTest: | ||||
| 		glog.V(logger.Info).Infof("ethash used in test mode") | ||||
| 		return ethash.NewForTesting() | ||||
| 	case config.PowShared: | ||||
| 		glog.V(logger.Info).Infof("ethash used in shared mode") | ||||
| 		return ethash.NewShared(), nil | ||||
|  | ||||
| 	default: | ||||
| 		return ethash.New(), nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // APIs returns the collection of RPC services the ethereum package offers. | ||||
| // NOTE, some of these services probably need to be moved to somewhere else. | ||||
| func (s *Ethereum) APIs() []rpc.API { | ||||
| 	return []rpc.API{ | ||||
| func (s *FullNodeService) APIs() []rpc.API { | ||||
| 	return append(ethapi.GetAPIs(s.apiBackend, &s.solcPath, &s.solc), []rpc.API{ | ||||
| 		{ | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicEthereumAPI(s), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicAccountAPI(s.accountManager), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "personal", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateAccountAPI(s), | ||||
| 			Public:    false, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicBlockChainAPI(s.chainConfig, s.blockchain, s.miner, s.chainDb, s.gpo, s.eventMux, s.accountManager), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicTransactionPoolAPI(s), | ||||
| 			Service:   NewPublicFullEthereumAPI(s), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| @@ -322,11 +325,6 @@ func (s *Ethereum) APIs() []rpc.API { | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateMinerAPI(s), | ||||
| 			Public:    false, | ||||
| 		}, { | ||||
| 			Namespace: "txpool", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicTxPoolAPI(s), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| @@ -335,16 +333,16 @@ func (s *Ethereum) APIs() []rpc.API { | ||||
| 		}, { | ||||
| 			Namespace: "admin", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateAdminAPI(s), | ||||
| 			Service:   NewPrivateFullAdminAPI(s), | ||||
| 		}, { | ||||
| 			Namespace: "debug", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicDebugAPI(s), | ||||
| 			Service:   NewPublicFullDebugAPI(s), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "debug", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateDebugAPI(s.chainConfig, s), | ||||
| 			Service:   NewPrivateFullDebugAPI(s.chainConfig, s), | ||||
| 		}, { | ||||
| 			Namespace: "net", | ||||
| 			Version:   "1.0", | ||||
| @@ -355,14 +353,14 @@ func (s *Ethereum) APIs() []rpc.API { | ||||
| 			Version:   "1.0", | ||||
| 			Service:   ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), | ||||
| 		}, | ||||
| 	} | ||||
| 	}...) | ||||
| } | ||||
|  | ||||
| func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { | ||||
| func (s *FullNodeService) ResetWithGenesisBlock(gb *types.Block) { | ||||
| 	s.blockchain.ResetWithGenesisBlock(gb) | ||||
| } | ||||
|  | ||||
| func (s *Ethereum) Etherbase() (eb common.Address, err error) { | ||||
| func (s *FullNodeService) Etherbase() (eb common.Address, err error) { | ||||
| 	eb = s.etherbase | ||||
| 	if (eb == common.Address{}) { | ||||
| 		firstAccount, err := s.AccountManager().AccountByIndex(0) | ||||
| @@ -375,46 +373,47 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { | ||||
| } | ||||
|  | ||||
| // set in js console via admin interface or wrapper from cli flags | ||||
| func (self *Ethereum) SetEtherbase(etherbase common.Address) { | ||||
| func (self *FullNodeService) SetEtherbase(etherbase common.Address) { | ||||
| 	self.etherbase = etherbase | ||||
| 	self.miner.SetEtherbase(etherbase) | ||||
| } | ||||
|  | ||||
| func (s *Ethereum) StopMining()         { s.miner.Stop() } | ||||
| func (s *Ethereum) IsMining() bool      { return s.miner.Mining() } | ||||
| func (s *Ethereum) Miner() *miner.Miner { return s.miner } | ||||
| func (s *FullNodeService) StopMining()         { s.miner.Stop() } | ||||
| func (s *FullNodeService) IsMining() bool      { return s.miner.Mining() } | ||||
| func (s *FullNodeService) Miner() *miner.Miner { return s.miner } | ||||
|  | ||||
| func (s *Ethereum) AccountManager() *accounts.Manager  { return s.accountManager } | ||||
| func (s *Ethereum) BlockChain() *core.BlockChain       { return s.blockchain } | ||||
| func (s *Ethereum) TxPool() *core.TxPool               { return s.txPool } | ||||
| func (s *Ethereum) EventMux() *event.TypeMux           { return s.eventMux } | ||||
| func (s *Ethereum) ChainDb() ethdb.Database            { return s.chainDb } | ||||
| func (s *Ethereum) DappDb() ethdb.Database             { return s.dappDb } | ||||
| func (s *Ethereum) IsListening() bool                  { return true } // Always listening | ||||
| func (s *Ethereum) EthVersion() int                    { return int(s.protocolManager.SubProtocols[0].Version) } | ||||
| func (s *Ethereum) NetVersion() int                    { return s.netVersionId } | ||||
| func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } | ||||
| func (s *FullNodeService) AccountManager() *accounts.Manager  { return s.accountManager } | ||||
| func (s *FullNodeService) BlockChain() *core.BlockChain       { return s.blockchain } | ||||
| func (s *FullNodeService) TxPool() *core.TxPool               { return s.txPool } | ||||
| func (s *FullNodeService) EventMux() *event.TypeMux           { return s.eventMux } | ||||
| func (s *FullNodeService) Pow() *ethash.Ethash                { return s.pow } | ||||
| func (s *FullNodeService) ChainDb() ethdb.Database            { return s.chainDb } | ||||
| func (s *FullNodeService) DappDb() ethdb.Database             { return s.dappDb } | ||||
| func (s *FullNodeService) IsListening() bool                  { return true } // Always listening | ||||
| func (s *FullNodeService) EthVersion() int                    { return int(s.protocolManager.SubProtocols[0].Version) } | ||||
| func (s *FullNodeService) NetVersion() int                    { return s.netVersionId } | ||||
| func (s *FullNodeService) Downloader() *downloader.Downloader { return s.protocolManager.downloader } | ||||
|  | ||||
| // Protocols implements node.Service, returning all the currently configured | ||||
| // network protocols to start. | ||||
| func (s *Ethereum) Protocols() []p2p.Protocol { | ||||
| func (s *FullNodeService) Protocols() []p2p.Protocol { | ||||
| 	return s.protocolManager.SubProtocols | ||||
| } | ||||
|  | ||||
| // Start implements node.Service, starting all internal goroutines needed by the | ||||
| // Ethereum protocol implementation. | ||||
| func (s *Ethereum) Start(srvr *p2p.Server) error { | ||||
| // FullNodeService protocol implementation. | ||||
| func (s *FullNodeService) Start(srvr *p2p.Server) error { | ||||
| 	s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) | ||||
| 	if s.AutoDAG { | ||||
| 		s.StartAutoDAG() | ||||
| 	} | ||||
| 	s.protocolManager.Start() | ||||
| 	s.netRPCService = NewPublicNetAPI(srvr, s.NetVersion()) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Stop implements node.Service, terminating all internal goroutines used by the | ||||
| // Ethereum protocol. | ||||
| func (s *Ethereum) Stop() error { | ||||
| func (s *FullNodeService) Stop() error { | ||||
| 	if s.stopDbUpgrade != nil { | ||||
| 		s.stopDbUpgrade() | ||||
| 	} | ||||
| @@ -434,7 +433,7 @@ func (s *Ethereum) Stop() error { | ||||
| } | ||||
|  | ||||
| // This function will wait for a shutdown and resumes main thread execution | ||||
| func (s *Ethereum) WaitForShutdown() { | ||||
| func (s *FullNodeService) WaitForShutdown() { | ||||
| 	<-s.shutdownChan | ||||
| } | ||||
|  | ||||
| @@ -447,7 +446,7 @@ func (s *Ethereum) WaitForShutdown() { | ||||
| // stop any number of times. | ||||
| // For any more sophisticated pattern of DAG generation, use CLI subcommand | ||||
| // makedag | ||||
| func (self *Ethereum) StartAutoDAG() { | ||||
| func (self *FullNodeService) StartAutoDAG() { | ||||
| 	if self.autodagquit != nil { | ||||
| 		return // already started | ||||
| 	} | ||||
| @@ -493,7 +492,7 @@ func (self *Ethereum) StartAutoDAG() { | ||||
| } | ||||
|  | ||||
| // stopAutoDAG stops automatic DAG pregeneration by quitting the loop | ||||
| func (self *Ethereum) StopAutoDAG() { | ||||
| func (self *FullNodeService) StopAutoDAG() { | ||||
| 	if self.autodagquit != nil { | ||||
| 		close(self.autodagquit) | ||||
| 		self.autodagquit = nil | ||||
| @@ -503,25 +502,10 @@ func (self *Ethereum) StopAutoDAG() { | ||||
|  | ||||
| // HTTPClient returns the light http client used for fetching offchain docs | ||||
| // (natspec, source for verification) | ||||
| func (self *Ethereum) HTTPClient() *httpclient.HTTPClient { | ||||
| func (self *FullNodeService) HTTPClient() *httpclient.HTTPClient { | ||||
| 	return self.httpclient | ||||
| } | ||||
|  | ||||
| func (self *Ethereum) Solc() (*compiler.Solidity, error) { | ||||
| 	var err error | ||||
| 	if self.solc == nil { | ||||
| 		self.solc, err = compiler.New(self.SolcPath) | ||||
| 	} | ||||
| 	return self.solc, err | ||||
| } | ||||
|  | ||||
| // set in js console via admin interface or wrapper from cli flags | ||||
| func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) { | ||||
| 	self.SolcPath = solcPath | ||||
| 	self.solc = nil | ||||
| 	return self.Solc() | ||||
| } | ||||
|  | ||||
| // dagFiles(epoch) returns the two alternative DAG filenames (not a path) | ||||
| // 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])> | ||||
| func dagFiles(epoch uint64) (string, string) { | ||||
|   | ||||
							
								
								
									
										60
									
								
								eth/bind.go
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								eth/bind.go
									
									
									
									
									
								
							| @@ -21,8 +21,10 @@ import ( | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/internal/ethapi" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // ContractBackend implements bind.ContractBackend with direct calls to Ethereum | ||||
| @@ -33,38 +35,44 @@ import ( | ||||
| // object. These should be rewritten to internal Go method calls when the Go API | ||||
| // is refactored to support a clean library use. | ||||
| type ContractBackend struct { | ||||
| 	eapi  *PublicEthereumAPI        // Wrapper around the Ethereum object to access metadata | ||||
| 	bcapi *PublicBlockChainAPI      // Wrapper around the blockchain to access chain data | ||||
| 	txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data | ||||
| 	eapi  *ethapi.PublicEthereumAPI        // Wrapper around the Ethereum object to access metadata | ||||
| 	bcapi *ethapi.PublicBlockChainAPI      // Wrapper around the blockchain to access chain data | ||||
| 	txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data | ||||
| } | ||||
|  | ||||
| // NewContractBackend creates a new native contract backend using an existing | ||||
| // Etheruem object. | ||||
| func NewContractBackend(eth *Ethereum) *ContractBackend { | ||||
| func NewContractBackend(eth *FullNodeService) *ContractBackend { | ||||
| 	return &ContractBackend{ | ||||
| 		eapi:  NewPublicEthereumAPI(eth), | ||||
| 		bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager), | ||||
| 		txapi: NewPublicTransactionPoolAPI(eth), | ||||
| 		eapi:  ethapi.NewPublicEthereumAPI(eth.apiBackend, nil, nil), | ||||
| 		bcapi: ethapi.NewPublicBlockChainAPI(eth.apiBackend), | ||||
| 		txapi: ethapi.NewPublicTransactionPoolAPI(eth.apiBackend), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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(contract common.Address, pending bool) (bool, error) { | ||||
| 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(contract, block) | ||||
| 	out, err := b.bcapi.GetCode(ctx, contract, block) | ||||
| 	return len(common.FromHex(out)) > 0, 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(contract common.Address, data []byte, pending bool) ([]byte, error) { | ||||
| 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 := CallArgs{ | ||||
| 	args := ethapi.CallArgs{ | ||||
| 		To:   &contract, | ||||
| 		Data: common.ToHex(data), | ||||
| 	} | ||||
| @@ -73,21 +81,27 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen | ||||
| 		block = rpc.PendingBlockNumber | ||||
| 	} | ||||
| 	// Execute the call and convert the output back to Go types | ||||
| 	out, err := b.bcapi.Call(args, block) | ||||
| 	out, err := b.bcapi.Call(ctx, args, block) | ||||
| 	return common.FromHex(out), err | ||||
| } | ||||
|  | ||||
| // PendingAccountNonce implements bind.ContractTransactor retrieving the current | ||||
| // pending nonce associated with an account. | ||||
| func (b *ContractBackend) PendingAccountNonce(account common.Address) (uint64, error) { | ||||
| 	out, err := b.txapi.GetTransactionCount(account, rpc.PendingBlockNumber) | ||||
| func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { | ||||
| 	if ctx == nil { | ||||
| 		ctx = context.Background() | ||||
| 	} | ||||
| 	out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) | ||||
| 	return out.Uint64(), err | ||||
| } | ||||
|  | ||||
| // SuggestGasPrice implements bind.ContractTransactor retrieving the currently | ||||
| // suggested gas price to allow a timely execution of a transaction. | ||||
| func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) { | ||||
| 	return b.eapi.GasPrice(), nil | ||||
| func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { | ||||
| 	if ctx == nil { | ||||
| 		ctx = context.Background() | ||||
| 	} | ||||
| 	return b.eapi.GasPrice(ctx) | ||||
| } | ||||
|  | ||||
| // EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas | ||||
| @@ -95,8 +109,11 @@ func (b *ContractBackend) SuggestGasPrice() (*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(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { | ||||
| 	out, err := b.bcapi.EstimateGas(CallArgs{ | ||||
| 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), | ||||
| @@ -107,8 +124,11 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm | ||||
|  | ||||
| // SendTransaction implements bind.ContractTransactor injects the transaction | ||||
| // into the pending pool for execution. | ||||
| func (b *ContractBackend) SendTransaction(tx *types.Transaction) error { | ||||
| 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(common.ToHex(raw)) | ||||
| 	_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) | ||||
| 	return err | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ import ( | ||||
|  | ||||
| const disabledInfo = "Set GO_OPENCL and re-build to enable." | ||||
|  | ||||
| func (s *Ethereum) StartMining(threads int, gpus string) error { | ||||
| func (s *FullNodeService) StartMining(threads int, gpus string) error { | ||||
| 	eb, err := s.Etherbase() | ||||
| 	if err != nil { | ||||
| 		err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
| // You should have received a copy of the GNU Lesser General Public License | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package eth | ||||
| package gasprice | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| @@ -23,6 +23,8 @@ import ( | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | ||||
| ) | ||||
| @@ -39,10 +41,22 @@ type blockPriceInfo struct { | ||||
| 	baseGasPrice *big.Int | ||||
| } | ||||
| 
 | ||||
| type GpoParams struct { | ||||
| 	GpoMinGasPrice          *big.Int | ||||
| 	GpoMaxGasPrice          *big.Int | ||||
| 	GpoFullBlockRatio       int | ||||
| 	GpobaseStepDown         int | ||||
| 	GpobaseStepUp           int | ||||
| 	GpobaseCorrectionFactor int | ||||
| } | ||||
| 
 | ||||
| // GasPriceOracle recommends gas prices based on the content of recent | ||||
| // blocks. | ||||
| type GasPriceOracle struct { | ||||
| 	eth           *Ethereum | ||||
| 	chain         *core.BlockChain | ||||
| 	db            ethdb.Database | ||||
| 	evmux         *event.TypeMux | ||||
| 	params        *GpoParams | ||||
| 	initOnce      sync.Once | ||||
| 	minPrice      *big.Int | ||||
| 	lastBaseMutex sync.Mutex | ||||
| @@ -55,17 +69,20 @@ type GasPriceOracle struct { | ||||
| } | ||||
| 
 | ||||
| // NewGasPriceOracle returns a new oracle. | ||||
| func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { | ||||
| 	minprice := eth.GpoMinGasPrice | ||||
| func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle { | ||||
| 	minprice := params.GpoMinGasPrice | ||||
| 	if minprice == nil { | ||||
| 		minprice = big.NewInt(gpoDefaultMinGasPrice) | ||||
| 	} | ||||
| 	minbase := new(big.Int).Mul(minprice, big.NewInt(100)) | ||||
| 	if eth.GpobaseCorrectionFactor > 0 { | ||||
| 		minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor))) | ||||
| 	if params.GpobaseCorrectionFactor > 0 { | ||||
| 		minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor))) | ||||
| 	} | ||||
| 	return &GasPriceOracle{ | ||||
| 		eth:      eth, | ||||
| 		chain:    chain, | ||||
| 		db:       db, | ||||
| 		evmux:    evmux, | ||||
| 		params:   params, | ||||
| 		blocks:   make(map[uint64]*blockPriceInfo), | ||||
| 		minBase:  minbase, | ||||
| 		minPrice: minprice, | ||||
| @@ -75,14 +92,14 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { | ||||
| 
 | ||||
| func (gpo *GasPriceOracle) init() { | ||||
| 	gpo.initOnce.Do(func() { | ||||
| 		gpo.processPastBlocks(gpo.eth.BlockChain()) | ||||
| 		gpo.processPastBlocks() | ||||
| 		go gpo.listenLoop() | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { | ||||
| func (self *GasPriceOracle) processPastBlocks() { | ||||
| 	last := int64(-1) | ||||
| 	cblock := chain.CurrentBlock() | ||||
| 	cblock := self.chain.CurrentBlock() | ||||
| 	if cblock != nil { | ||||
| 		last = int64(cblock.NumberU64()) | ||||
| 	} | ||||
| @@ -92,7 +109,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { | ||||
| 	} | ||||
| 	self.firstProcessed = uint64(first) | ||||
| 	for i := first; i <= last; i++ { | ||||
| 		block := chain.GetBlockByNumber(uint64(i)) | ||||
| 		block := self.chain.GetBlockByNumber(uint64(i)) | ||||
| 		if block != nil { | ||||
| 			self.processBlock(block) | ||||
| 		} | ||||
| @@ -101,7 +118,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { | ||||
| } | ||||
| 
 | ||||
| func (self *GasPriceOracle) listenLoop() { | ||||
| 	events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) | ||||
| 	events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) | ||||
| 	defer events.Unsubscribe() | ||||
| 
 | ||||
| 	for event := range events.Chan() { | ||||
| @@ -136,9 +153,9 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { | ||||
| 	} | ||||
| 
 | ||||
| 	if lastBase.Cmp(lp) < 0 { | ||||
| 		corr = self.eth.GpobaseStepUp | ||||
| 		corr = self.params.GpobaseStepUp | ||||
| 	} else { | ||||
| 		corr = -self.eth.GpobaseStepDown | ||||
| 		corr = -self.params.GpobaseStepDown | ||||
| 	} | ||||
| 
 | ||||
| 	crand := int64(corr * (900 + rand.Intn(201))) | ||||
| @@ -159,14 +176,14 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { | ||||
| 	self.lastBase = newBase | ||||
| 	self.lastBaseMutex.Unlock() | ||||
| 
 | ||||
| 	glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", block.NumberU64(), newBase.Int64()) | ||||
| 	glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", i, newBase.Int64()) | ||||
| } | ||||
| 
 | ||||
| // returns the lowers possible price with which a tx was or could have been included | ||||
| func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { | ||||
| 	gasUsed := big.NewInt(0) | ||||
| 
 | ||||
| 	receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash(), block.NumberU64()) | ||||
| 	receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64()) | ||||
| 	if len(receipts) > 0 { | ||||
| 		if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { | ||||
| 			gasUsed = receipts[len(receipts)-1].CumulativeGasUsed | ||||
| @@ -174,7 +191,7 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { | ||||
| 	} | ||||
| 
 | ||||
| 	if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(), | ||||
| 		big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 { | ||||
| 		big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 { | ||||
| 		// block is not full, could have posted a tx with MinGasPrice | ||||
| 		return big.NewInt(0) | ||||
| 	} | ||||
| @@ -201,12 +218,12 @@ func (self *GasPriceOracle) SuggestPrice() *big.Int { | ||||
| 	price := new(big.Int).Set(self.lastBase) | ||||
| 	self.lastBaseMutex.Unlock() | ||||
| 
 | ||||
| 	price.Mul(price, big.NewInt(int64(self.eth.GpobaseCorrectionFactor))) | ||||
| 	price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor))) | ||||
| 	price.Div(price, big.NewInt(100)) | ||||
| 	if price.Cmp(self.minPrice) < 0 { | ||||
| 		price.Set(self.minPrice) | ||||
| 	} else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 { | ||||
| 		price.Set(self.eth.GpoMaxGasPrice) | ||||
| 	} else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 { | ||||
| 		price.Set(self.params.GpoMaxGasPrice) | ||||
| 	} | ||||
| 	return price | ||||
| } | ||||
| @@ -33,7 +33,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/miner" | ||||
| ) | ||||
|  | ||||
| func (s *Ethereum) StartMining(threads int, gpus string) error { | ||||
| func (s *FullNodeService) StartMining(threads int, gpus string) error { | ||||
| 	eb, err := s.Etherbase() | ||||
| 	if err != nil { | ||||
| 		err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) | ||||
|   | ||||
							
								
								
									
										1542
									
								
								internal/ethapi/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1542
									
								
								internal/ethapi/api.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										119
									
								
								internal/ethapi/backend.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								internal/ethapi/backend.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| // 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 implements the general Ethereum API functions. | ||||
| package ethapi | ||||
|  | ||||
| import ( | ||||
| 	"math/big" | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/common/compiler" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| 	"github.com/ethereum/go-ethereum/eth/downloader" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // Backend interface provides the common API services (that are provided by | ||||
| // both full and light clients) with access to necessary functions. | ||||
| type Backend interface { | ||||
| 	// general Ethereum API | ||||
| 	Downloader() *downloader.Downloader | ||||
| 	ProtocolVersion() int | ||||
| 	SuggestPrice(ctx context.Context) (*big.Int, error) | ||||
| 	ChainDb() ethdb.Database | ||||
| 	EventMux() *event.TypeMux | ||||
| 	AccountManager() *accounts.Manager | ||||
| 	// BlockChain API | ||||
| 	SetHead(number uint64) | ||||
| 	HeaderByNumber(blockNr rpc.BlockNumber) *types.Header | ||||
| 	BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) | ||||
| 	StateAndHeaderByNumber(blockNr rpc.BlockNumber) (State, *types.Header, error) | ||||
| 	GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) | ||||
| 	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) | ||||
| 	GetTd(blockHash common.Hash) *big.Int | ||||
| 	GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error) | ||||
| 	// TxPool API | ||||
| 	SendTx(ctx context.Context, signedTx *types.Transaction) error | ||||
| 	RemoveTx(txHash common.Hash) | ||||
| 	GetPoolTransactions() types.Transactions | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| type State interface { | ||||
| 	GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) | ||||
| 	GetCode(ctx context.Context, addr common.Address) ([]byte, error) | ||||
| 	GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) | ||||
| 	GetNonce(ctx context.Context, addr common.Address) (uint64, error) | ||||
| } | ||||
|  | ||||
| func GetAPIs(apiBackend Backend, solcPath *string, solc **compiler.Solidity) []rpc.API { | ||||
| 	return []rpc.API{ | ||||
| 		{ | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicEthereumAPI(apiBackend, solcPath, solc), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicBlockChainAPI(apiBackend), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicTransactionPoolAPI(apiBackend), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "txpool", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicTxPoolAPI(apiBackend), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "admin", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateAdminAPI(apiBackend, solcPath, solc), | ||||
| 		}, { | ||||
| 			Namespace: "debug", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicDebugAPI(apiBackend), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "debug", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateDebugAPI(apiBackend), | ||||
| 		}, { | ||||
| 			Namespace: "eth", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPublicAccountAPI(apiBackend.AccountManager()), | ||||
| 			Public:    true, | ||||
| 		}, { | ||||
| 			Namespace: "personal", | ||||
| 			Version:   "1.0", | ||||
| 			Service:   NewPrivateAccountAPI(apiBackend), | ||||
| 			Public:    false, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @@ -30,6 +30,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/node" | ||||
| 	"github.com/ethereum/go-ethereum/p2p" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // Interval to check for new releases | ||||
| @@ -57,7 +58,7 @@ type ReleaseService struct { | ||||
| // releases and notify the user of such. | ||||
| func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { | ||||
| 	// Retrieve the Ethereum service dependency to access the blockchain | ||||
| 	var ethereum *eth.Ethereum | ||||
| 	var ethereum *eth.FullNodeService | ||||
| 	if err := ctx.Service(ðereum); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -110,7 +111,9 @@ func (r *ReleaseService) checker() { | ||||
| 			timer.Reset(releaseRecheckInterval) | ||||
|  | ||||
| 			// Retrieve the current version, and handle missing contracts gracefully | ||||
| 			version, err := r.oracle.CurrentVersion(nil) | ||||
| 			ctx, _ := context.WithTimeout(context.Background(), time.Second*5) | ||||
| 			opts := &bind.CallOpts{Context: ctx} | ||||
| 			version, err := r.oracle.CurrentVersion(opts) | ||||
| 			if err != nil { | ||||
| 				if err == bind.ErrNoCode { | ||||
| 					glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user