mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
Update Go Ethereum
This commit is contained in:
commit
12a4c86011
189 changed files with 16639 additions and 4217 deletions
|
|
@ -97,7 +97,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||
Type string
|
||||
Name string
|
||||
Constant bool
|
||||
Indexed bool
|
||||
Anonymous bool
|
||||
Inputs []Argument
|
||||
Outputs []Argument
|
||||
|
|
|
|||
|
|
@ -52,12 +52,6 @@ type ContractCaller interface {
|
|||
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]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.
|
||||
|
|
@ -90,8 +84,29 @@ type ContractTransactor interface {
|
|||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||
}
|
||||
|
||||
// ContractFilterer defines the methods needed to access log events using one-off
|
||||
// queries or continuous event subscriptions.
|
||||
type ContractFilterer interface {
|
||||
// FilterLogs executes a log filter operation, blocking during execution and
|
||||
// returning all the results in one batch.
|
||||
//
|
||||
// TODO(karalabe): Deprecate when the subscription one can return past data too.
|
||||
FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
|
||||
|
||||
// SubscribeFilterLogs creates a background log filtering operation, returning
|
||||
// a subscription immediately, which can be used to stream the found events.
|
||||
SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, 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)
|
||||
}
|
||||
|
||||
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
||||
type ContractBackend interface {
|
||||
ContractCaller
|
||||
ContractTransactor
|
||||
ContractFilterer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,15 @@ import (
|
|||
"github.com/EthereumCommonwealth/go-callisto/common/math"
|
||||
"github.com/EthereumCommonwealth/go-callisto/consensus/ethash"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/bloombits"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/state"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/vm"
|
||||
"github.com/EthereumCommonwealth/go-callisto/eth/filters"
|
||||
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
||||
"github.com/EthereumCommonwealth/go-callisto/event"
|
||||
"github.com/EthereumCommonwealth/go-callisto/params"
|
||||
"github.com/EthereumCommonwealth/go-callisto/rpc"
|
||||
)
|
||||
|
||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||
|
|
@ -53,6 +57,8 @@ type SimulatedBackend struct {
|
|||
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
|
||||
|
||||
events *filters.EventSystem // Event system for filtering log events live
|
||||
|
||||
config *params.ChainConfig
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +69,13 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
|||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), vm.Config{})
|
||||
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
|
||||
|
||||
backend := &SimulatedBackend{
|
||||
database: database,
|
||||
blockchain: blockchain,
|
||||
config: genesis.Config,
|
||||
events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
|
||||
}
|
||||
backend.rollback()
|
||||
return backend
|
||||
}
|
||||
|
|
@ -248,7 +260,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
return hi, nil
|
||||
}
|
||||
|
||||
// callContract implemens common code between normal and pending contract calls.
|
||||
// callContract implements 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, uint64, bool, error) {
|
||||
// Ensure message is initialized properly.
|
||||
|
|
@ -302,7 +314,69 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||
return nil
|
||||
}
|
||||
|
||||
// JumpTimeInSeconds adds skip seconds to the clock
|
||||
// FilterLogs executes a log filter operation, blocking during execution and
|
||||
// returning all the results in one batch.
|
||||
//
|
||||
// TODO(karalabe): Deprecate when the subscription one can return past data too.
|
||||
func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
|
||||
// Initialize unset filter boundaried to run from genesis to chain head
|
||||
from := int64(0)
|
||||
if query.FromBlock != nil {
|
||||
from = query.FromBlock.Int64()
|
||||
}
|
||||
to := int64(-1)
|
||||
if query.ToBlock != nil {
|
||||
to = query.ToBlock.Int64()
|
||||
}
|
||||
// Construct and execute the filter
|
||||
filter := filters.New(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
|
||||
|
||||
logs, err := filter.Logs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := make([]types.Log, len(logs))
|
||||
for i, log := range logs {
|
||||
res[i] = *log
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// SubscribeFilterLogs creates a background log filtering operation, returning a
|
||||
// subscription immediately, which can be used to stream the found events.
|
||||
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||
// Subscribe to contract events
|
||||
sink := make(chan []*types.Log)
|
||||
|
||||
sub, err := b.events.SubscribeLogs(query, sink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Since we're getting logs in batches, we need to flatten them into a plain stream
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case logs := <-sink:
|
||||
for _, log := range logs {
|
||||
select {
|
||||
case ch <- *log:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// AdjustTime adds a time shift to the simulated clock.
|
||||
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
|
@ -331,3 +405,44 @@ func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
|||
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
||||
|
||||
// filterBackend implements filters.Backend to support filtering for logs without
|
||||
// taking bloom-bits acceleration structures into account.
|
||||
type filterBackend struct {
|
||||
db ethdb.Database
|
||||
bc *core.BlockChain
|
||||
}
|
||||
|
||||
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
||||
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
||||
|
||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
|
||||
if block == rpc.LatestBlockNumber {
|
||||
return fb.bc.CurrentHeader(), nil
|
||||
}
|
||||
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
|
||||
}
|
||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
<-quit
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||
return fb.bc.SubscribeChainEvent(ch)
|
||||
}
|
||||
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
||||
}
|
||||
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||
return fb.bc.SubscribeLogsEvent(ch)
|
||||
}
|
||||
|
||||
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
||||
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/event"
|
||||
)
|
||||
|
||||
// SignerFn is a signer function callback when a contract requires a method to
|
||||
|
|
@ -55,6 +56,22 @@ type TransactOpts struct {
|
|||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
}
|
||||
|
||||
// FilterOpts is the collection of options to fine tune filtering for events
|
||||
// within a bound contract.
|
||||
type FilterOpts struct {
|
||||
Start uint64 // Start of the queried range
|
||||
End *uint64 // End of the range (nil = latest)
|
||||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
}
|
||||
|
||||
// WatchOpts is the collection of options to fine tune subscribing for events
|
||||
// within a bound contract.
|
||||
type WatchOpts struct {
|
||||
Start *uint64 // Start of the queried range (nil = latest)
|
||||
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
|
||||
// Ethereum network. It contains a collection of methods that are used by the
|
||||
// higher level contract bindings to operate.
|
||||
|
|
@ -63,16 +80,18 @@ 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
|
||||
filterer ContractFilterer // Event filtering to interact with the blockchain
|
||||
}
|
||||
|
||||
// NewBoundContract creates a low level contract interface through which calls
|
||||
// and transactions may be made through.
|
||||
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract {
|
||||
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
|
||||
return &BoundContract{
|
||||
address: address,
|
||||
abi: abi,
|
||||
caller: caller,
|
||||
transactor: transactor,
|
||||
filterer: filterer,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +99,7 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller
|
|||
// deployment address with a Go wrapper.
|
||||
func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
|
||||
// Otherwise try to deploy the contract
|
||||
c := NewBoundContract(common.Address{}, abi, backend, backend)
|
||||
c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
|
||||
|
||||
input, err := c.abi.Pack("", params...)
|
||||
if err != nil {
|
||||
|
|
@ -225,6 +244,104 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
return signedTx, nil
|
||||
}
|
||||
|
||||
// FilterLogs filters contract logs for past blocks, returning the necessary
|
||||
// channels to construct a strongly typed bound iterator on top of them.
|
||||
func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
|
||||
// Don't crash on a lazy user
|
||||
if opts == nil {
|
||||
opts = new(FilterOpts)
|
||||
}
|
||||
// Append the event selector to the query parameters and construct the topic set
|
||||
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
||||
|
||||
topics, err := makeTopics(query...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Start the background filtering
|
||||
logs := make(chan types.Log, 128)
|
||||
|
||||
config := ethereum.FilterQuery{
|
||||
Addresses: []common.Address{c.address},
|
||||
Topics: topics,
|
||||
FromBlock: new(big.Int).SetUint64(opts.Start),
|
||||
}
|
||||
if opts.End != nil {
|
||||
config.ToBlock = new(big.Int).SetUint64(*opts.End)
|
||||
}
|
||||
/* TODO(karalabe): Replace the rest of the method below with this when supported
|
||||
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
|
||||
*/
|
||||
buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
for _, log := range buff {
|
||||
select {
|
||||
case logs <- log:
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}), nil
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return logs, sub, nil
|
||||
}
|
||||
|
||||
// WatchLogs filters subscribes to contract logs for future blocks, returning a
|
||||
// subscription object that can be used to tear down the watcher.
|
||||
func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
|
||||
// Don't crash on a lazy user
|
||||
if opts == nil {
|
||||
opts = new(WatchOpts)
|
||||
}
|
||||
// Append the event selector to the query parameters and construct the topic set
|
||||
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
||||
|
||||
topics, err := makeTopics(query...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Start the background filtering
|
||||
logs := make(chan types.Log, 128)
|
||||
|
||||
config := ethereum.FilterQuery{
|
||||
Addresses: []common.Address{c.address},
|
||||
Topics: topics,
|
||||
}
|
||||
if opts.Start != nil {
|
||||
config.FromBlock = new(big.Int).SetUint64(*opts.Start)
|
||||
}
|
||||
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return logs, sub, nil
|
||||
}
|
||||
|
||||
// UnpackLog unpacks a retrieved log into the provided output structure.
|
||||
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
||||
if len(log.Data) > 0 {
|
||||
if err := c.abi.Unpack(out, event, log.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var indexed abi.Arguments
|
||||
for _, arg := range c.abi.Events[event].Inputs {
|
||||
if arg.Indexed {
|
||||
indexed = append(indexed, arg)
|
||||
}
|
||||
}
|
||||
return parseTopics(out, indexed, log.Topics[1:])
|
||||
}
|
||||
|
||||
// ensureContext is a helper method to ensure a context is not nil, even if the
|
||||
// user specified it as such.
|
||||
func ensureContext(ctx context.Context) context.Context {
|
||||
if ctx == nil {
|
||||
return context.TODO()
|
||||
|
|
|
|||
|
|
@ -63,10 +63,11 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
return r
|
||||
}, abis[i])
|
||||
|
||||
// Extract the call and transact methods, and sort them alphabetically
|
||||
// Extract the call and transact methods; events; and sort them alphabetically
|
||||
var (
|
||||
calls = make(map[string]*tmplMethod)
|
||||
transacts = make(map[string]*tmplMethod)
|
||||
events = make(map[string]*tmplEvent)
|
||||
)
|
||||
for _, original := range evmABI.Methods {
|
||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||
|
|
@ -89,11 +90,33 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
}
|
||||
// Append the methods to the call or transact lists
|
||||
if original.Const {
|
||||
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
|
||||
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||
} else {
|
||||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
|
||||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||
}
|
||||
}
|
||||
for _, original := range evmABI.Events {
|
||||
// Skip anonymous events as they don't support explicit filtering
|
||||
if original.Anonymous {
|
||||
continue
|
||||
}
|
||||
// Normalize the event for capital cases and non-anonymous outputs
|
||||
normalized := original
|
||||
normalized.Name = methodNormalizer[lang](original.Name)
|
||||
|
||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||
copy(normalized.Inputs, original.Inputs)
|
||||
for j, input := range normalized.Inputs {
|
||||
// Indexed fields are input, non-indexed ones are outputs
|
||||
if input.Indexed {
|
||||
if input.Name == "" {
|
||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Append the event to the accumulator list
|
||||
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
||||
}
|
||||
contracts[types[i]] = &tmplContract{
|
||||
Type: capitalise(types[i]),
|
||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||
|
|
@ -101,6 +124,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
Constructor: evmABI.Constructor,
|
||||
Calls: calls,
|
||||
Transacts: transacts,
|
||||
Events: events,
|
||||
}
|
||||
}
|
||||
// Generate the contract template data content and render it
|
||||
|
|
@ -111,10 +135,11 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
buffer := new(bytes.Buffer)
|
||||
|
||||
funcs := map[string]interface{}{
|
||||
"bindtype": bindType[lang],
|
||||
"namedtype": namedType[lang],
|
||||
"capitalise": capitalise,
|
||||
"decapitalise": decapitalise,
|
||||
"bindtype": bindType[lang],
|
||||
"bindtopictype": bindTopicType[lang],
|
||||
"namedtype": namedType[lang],
|
||||
"capitalise": capitalise,
|
||||
"decapitalise": decapitalise,
|
||||
}
|
||||
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
|
||||
if err := tmpl.Execute(buffer, data); err != nil {
|
||||
|
|
@ -133,7 +158,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
}
|
||||
|
||||
// bindType is a set of type binders that convert Solidity types to some supported
|
||||
// programming language.
|
||||
// programming language types.
|
||||
var bindType = map[Lang]func(kind abi.Type) string{
|
||||
LangGo: bindTypeGo,
|
||||
LangJava: bindTypeJava,
|
||||
|
|
@ -254,6 +279,33 @@ func bindTypeJava(kind abi.Type) string {
|
|||
}
|
||||
}
|
||||
|
||||
// bindTopicType is a set of type binders that convert Solidity types to some
|
||||
// supported programming language topic types.
|
||||
var bindTopicType = map[Lang]func(kind abi.Type) string{
|
||||
LangGo: bindTopicTypeGo,
|
||||
LangJava: bindTopicTypeJava,
|
||||
}
|
||||
|
||||
// bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
||||
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||
func bindTopicTypeGo(kind abi.Type) string {
|
||||
bound := bindTypeGo(kind)
|
||||
if bound == "string" || bound == "[]byte" {
|
||||
bound = "common.Hash"
|
||||
}
|
||||
return bound
|
||||
}
|
||||
|
||||
// bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
|
||||
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||
func bindTopicTypeJava(kind abi.Type) string {
|
||||
bound := bindTypeJava(kind)
|
||||
if bound == "String" || bound == "Bytes" {
|
||||
bound = "Hash"
|
||||
}
|
||||
return bound
|
||||
}
|
||||
|
||||
// namedType is a set of functions that transform language specific types to
|
||||
// named versions that my be used inside method names.
|
||||
var namedType = map[Lang]func(string, abi.Type) string{
|
||||
|
|
@ -321,14 +373,14 @@ func decapitalise(input string) string {
|
|||
return strings.ToLower(input[:1]) + input[1:]
|
||||
}
|
||||
|
||||
// structured checks whether a method has enough information to return a proper
|
||||
// Go struct or if flat returns are needed.
|
||||
func structured(method abi.Method) bool {
|
||||
if len(method.Outputs) < 2 {
|
||||
// structured checks whether a list of ABI data types has enough information to
|
||||
// operate through a proper Go struct or if flat returns are needed.
|
||||
func structured(args abi.Arguments) bool {
|
||||
if len(args) < 2 {
|
||||
return false
|
||||
}
|
||||
exists := make(map[string]bool)
|
||||
for _, out := range method.Outputs {
|
||||
for _, out := range args {
|
||||
// If the name is anonymous, we can't organize into a struct
|
||||
if out.Name == "" {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -148,6 +148,64 @@ var bindTests = []struct {
|
|||
fmt.Println(str1, str2, res.Str1, res.Str2, err)
|
||||
}`,
|
||||
},
|
||||
// Tests that named, anonymous and indexed events are handled correctly
|
||||
{
|
||||
`EventChecker`, ``, ``,
|
||||
`
|
||||
[
|
||||
{"type":"event","name":"empty","inputs":[]},
|
||||
{"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]},
|
||||
{"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256"}]},
|
||||
{"type":"event","name":"anonymous","anonymous":true,"inputs":[]},
|
||||
{"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]}
|
||||
]
|
||||
`,
|
||||
`if e, err := NewEventChecker(common.Address{}, nil); e == nil || err != nil {
|
||||
t.Fatalf("binding (%v) nil or error (%v) not nil", e, nil)
|
||||
} else if false { // Don't run, just compile and test types
|
||||
var (
|
||||
err error
|
||||
res bool
|
||||
str string
|
||||
dat []byte
|
||||
hash common.Hash
|
||||
)
|
||||
_, err = e.FilterEmpty(nil)
|
||||
_, err = e.FilterIndexed(nil, []common.Address{}, []*big.Int{})
|
||||
|
||||
mit, err := e.FilterMixed(nil, []common.Address{})
|
||||
|
||||
res = mit.Next() // Make sure the iterator has a Next method
|
||||
err = mit.Error() // Make sure the iterator has an Error method
|
||||
err = mit.Close() // Make sure the iterator has a Close method
|
||||
|
||||
fmt.Println(mit.Event.Raw.BlockHash) // Make sure the raw log is contained within the results
|
||||
fmt.Println(mit.Event.Num) // Make sure the unpacked non-indexed fields are present
|
||||
fmt.Println(mit.Event.Addr) // Make sure the reconstructed indexed fields are present
|
||||
|
||||
dit, err := e.FilterDynamic(nil, []string{}, [][]byte{})
|
||||
|
||||
str = dit.Event.Str // Make sure non-indexed strings retain their type
|
||||
dat = dit.Event.Dat // Make sure non-indexed bytes retain their type
|
||||
hash = dit.Event.IdxStr // Make sure indexed strings turn into hashes
|
||||
hash = dit.Event.IdxDat // Make sure indexed bytes turn into hashes
|
||||
|
||||
sink := make(chan *EventCheckerMixed)
|
||||
sub, err := e.WatchMixed(nil, sink, []common.Address{})
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
event := <-sink
|
||||
fmt.Println(event.Raw.BlockHash) // Make sure the raw log is contained within the results
|
||||
fmt.Println(event.Num) // Make sure the unpacked non-indexed fields are present
|
||||
fmt.Println(event.Addr) // Make sure the reconstructed indexed fields are present
|
||||
|
||||
fmt.Println(res, str, dat, hash, err)
|
||||
}
|
||||
// Run a tiny reflection test to ensure disallowed methods don't appear
|
||||
if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok {
|
||||
t.Errorf("binding has disallowed method (FilterAnonymous)")
|
||||
}`,
|
||||
},
|
||||
// Test that contract interactions (deploy, transact and call) generate working code
|
||||
{
|
||||
`Interactor`,
|
||||
|
|
@ -508,6 +566,177 @@ var bindTests = []struct {
|
|||
fmt.Println(a, b, err)
|
||||
`,
|
||||
},
|
||||
// Tests that logs can be successfully filtered and decoded.
|
||||
{
|
||||
`Eventer`,
|
||||
`
|
||||
contract Eventer {
|
||||
event SimpleEvent (
|
||||
address indexed Addr,
|
||||
bytes32 indexed Id,
|
||||
bool indexed Flag,
|
||||
uint Value
|
||||
);
|
||||
function raiseSimpleEvent(address addr, bytes32 id, bool flag, uint value) {
|
||||
SimpleEvent(addr, id, flag, value);
|
||||
}
|
||||
|
||||
event NodataEvent (
|
||||
uint indexed Number,
|
||||
int16 indexed Short,
|
||||
uint32 indexed Long
|
||||
);
|
||||
function raiseNodataEvent(uint number, int16 short, uint32 long) {
|
||||
NodataEvent(number, short, long);
|
||||
}
|
||||
|
||||
event DynamicEvent (
|
||||
string indexed IndexedString,
|
||||
bytes indexed IndexedBytes,
|
||||
string NonIndexedString,
|
||||
bytes NonIndexedBytes
|
||||
);
|
||||
function raiseDynamicEvent(string str, bytes blob) {
|
||||
DynamicEvent(str, blob, str, blob);
|
||||
}
|
||||
}
|
||||
`,
|
||||
`6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`,
|
||||
`[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`,
|
||||
`
|
||||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy an eventer contract
|
||||
_, _, eventer, err := DeployEventer(auth, sim)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deploy eventer contract: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
// Inject a few events into the contract, gradually more in each block
|
||||
for i := 1; i <= 3; i++ {
|
||||
for j := 1; j <= i; j++ {
|
||||
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil {
|
||||
t.Fatalf("block %d, event %d: raise failed: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
sim.Commit()
|
||||
}
|
||||
// Test filtering for certain events and ensure they can be found
|
||||
sit, err := eventer.FilterSimpleEvent(nil, []common.Address{common.Address{1}, common.Address{3}}, [][32]byte{{byte(1)}, {byte(2)}, {byte(3)}}, []bool{true})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to filter for simple events: %v", err)
|
||||
}
|
||||
defer sit.Close()
|
||||
|
||||
sit.Next()
|
||||
if sit.Event.Value.Uint64() != 11 || !sit.Event.Flag {
|
||||
t.Errorf("simple log content mismatch: have %v, want {11, true}", sit.Event)
|
||||
}
|
||||
sit.Next()
|
||||
if sit.Event.Value.Uint64() != 21 || !sit.Event.Flag {
|
||||
t.Errorf("simple log content mismatch: have %v, want {21, true}", sit.Event)
|
||||
}
|
||||
sit.Next()
|
||||
if sit.Event.Value.Uint64() != 31 || !sit.Event.Flag {
|
||||
t.Errorf("simple log content mismatch: have %v, want {31, true}", sit.Event)
|
||||
}
|
||||
sit.Next()
|
||||
if sit.Event.Value.Uint64() != 33 || !sit.Event.Flag {
|
||||
t.Errorf("simple log content mismatch: have %v, want {33, true}", sit.Event)
|
||||
}
|
||||
|
||||
if sit.Next() {
|
||||
t.Errorf("unexpected simple event found: %+v", sit.Event)
|
||||
}
|
||||
if err = sit.Error(); err != nil {
|
||||
t.Fatalf("simple event iteration failed: %v", err)
|
||||
}
|
||||
// Test raising and filtering for an event with no data component
|
||||
if _, err := eventer.RaiseNodataEvent(auth, big.NewInt(314), 141, 271); err != nil {
|
||||
t.Fatalf("failed to raise nodata event: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
nit, err := eventer.FilterNodataEvent(nil, []*big.Int{big.NewInt(314)}, []int16{140, 141, 142}, []uint32{271})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to filter for nodata events: %v", err)
|
||||
}
|
||||
defer nit.Close()
|
||||
|
||||
if !nit.Next() {
|
||||
t.Fatalf("nodata log not found: %v", nit.Error())
|
||||
}
|
||||
if nit.Event.Number.Uint64() != 314 {
|
||||
t.Errorf("nodata log content mismatch: have %v, want 314", nit.Event.Number)
|
||||
}
|
||||
if nit.Next() {
|
||||
t.Errorf("unexpected nodata event found: %+v", nit.Event)
|
||||
}
|
||||
if err = nit.Error(); err != nil {
|
||||
t.Fatalf("nodata event iteration failed: %v", err)
|
||||
}
|
||||
// Test raising and filtering for events with dynamic indexed components
|
||||
if _, err := eventer.RaiseDynamicEvent(auth, "Hello", []byte("World")); err != nil {
|
||||
t.Fatalf("failed to raise dynamic event: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
dit, err := eventer.FilterDynamicEvent(nil, []string{"Hi", "Hello", "Bye"}, [][]byte{[]byte("World")})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to filter for dynamic events: %v", err)
|
||||
}
|
||||
defer dit.Close()
|
||||
|
||||
if !dit.Next() {
|
||||
t.Fatalf("dynamic log not found: %v", dit.Error())
|
||||
}
|
||||
if dit.Event.NonIndexedString != "Hello" || string(dit.Event.NonIndexedBytes) != "World" || dit.Event.IndexedString != common.HexToHash("0x06b3dfaec148fb1bb2b066f10ec285e7c9bf402ab32aa78a5d38e34566810cd2") || dit.Event.IndexedBytes != common.HexToHash("0xf2208c967df089f60420785795c0a9ba8896b0f6f1867fa7f1f12ad6f79c1a18") {
|
||||
t.Errorf("dynamic log content mismatch: have %v, want {'0x06b3dfaec148fb1bb2b066f10ec285e7c9bf402ab32aa78a5d38e34566810cd2, '0xf2208c967df089f60420785795c0a9ba8896b0f6f1867fa7f1f12ad6f79c1a18', 'Hello', 'World'}", dit.Event)
|
||||
}
|
||||
if dit.Next() {
|
||||
t.Errorf("unexpected dynamic event found: %+v", dit.Event)
|
||||
}
|
||||
if err = dit.Error(); err != nil {
|
||||
t.Fatalf("dynamic event iteration failed: %v", err)
|
||||
}
|
||||
// Test subscribing to an event and raising it afterwards
|
||||
ch := make(chan *EventerSimpleEvent, 16)
|
||||
sub, err := eventer.WatchSimpleEvent(nil, ch, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to subscribe to simple events: %v", err)
|
||||
}
|
||||
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{255}, [32]byte{255}, true, big.NewInt(255)); err != nil {
|
||||
t.Fatalf("failed to raise subscribed simple event: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
select {
|
||||
case event := <-ch:
|
||||
if event.Value.Uint64() != 255 {
|
||||
t.Errorf("simple log content mismatch: have %v, want 255", event)
|
||||
}
|
||||
case <-time.After(250 * time.Millisecond):
|
||||
t.Fatalf("subscribed simple event didn't arrive")
|
||||
}
|
||||
// Unsubscribe from the event and make sure we're not delivered more
|
||||
sub.Unsubscribe()
|
||||
|
||||
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{254}, [32]byte{254}, true, big.NewInt(254)); err != nil {
|
||||
t.Fatalf("failed to raise subscribed simple event: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
select {
|
||||
case event := <-ch:
|
||||
t.Fatalf("unsubscribed simple event arrived: %v", event)
|
||||
case <-time.After(250 * time.Millisecond):
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
// Tests that packages generated by the binder can be successfully compiled and
|
||||
|
|
@ -559,7 +788,7 @@ func TestBindings(t *testing.T) {
|
|||
}
|
||||
}
|
||||
// Test the entire package and report any failures
|
||||
cmd := exec.Command(gocmd, "test", "-v")
|
||||
cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
|
||||
cmd.Dir = pkg
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("failed to run binding test: %v\n%s", err, out)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ type tmplContract struct {
|
|||
Constructor abi.Method // Contract constructor for deploy parametrization
|
||||
Calls map[string]*tmplMethod // Contract calls that only read state data
|
||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||
Events map[string]*tmplEvent // Contract events accessors
|
||||
}
|
||||
|
||||
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
||||
|
|
@ -39,7 +40,13 @@ type tmplContract struct {
|
|||
type tmplMethod struct {
|
||||
Original abi.Method // Original method as parsed by the abi package
|
||||
Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
|
||||
Structured bool // Whether the returns should be accumulated into a contract
|
||||
Structured bool // Whether the returns should be accumulated into a struct
|
||||
}
|
||||
|
||||
// tmplEvent is a wrapper around an a
|
||||
type tmplEvent struct {
|
||||
Original abi.Event // Original event as parsed by the abi package
|
||||
Normalized abi.Event // Normalized version of the parsed fields
|
||||
}
|
||||
|
||||
// tmplSource is language to template mapping containing all the supported
|
||||
|
|
@ -75,7 +82,7 @@ package {{.Package}}
|
|||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil
|
||||
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
||||
}
|
||||
{{end}}
|
||||
|
||||
|
|
@ -83,6 +90,7 @@ package {{.Package}}
|
|||
type {{.Type}} struct {
|
||||
{{.Type}}Caller // Read-only binding to the contract
|
||||
{{.Type}}Transactor // Write-only binding to the contract
|
||||
{{.Type}}Filterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
|
|
@ -95,6 +103,11 @@ package {{.Package}}
|
|||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type {{.Type}}Filterer struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
|
||||
// with pre-set call and transact options.
|
||||
type {{.Type}}Session struct {
|
||||
|
|
@ -134,16 +147,16 @@ 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, backend)
|
||||
contract, err := bind{{.Type}}(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil
|
||||
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
||||
}
|
||||
|
||||
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
|
||||
contract, err := bind{{.Type}}(address, caller, nil)
|
||||
contract, err := bind{{.Type}}(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -152,20 +165,29 @@ package {{.Package}}
|
|||
|
||||
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
|
||||
contract, err := bind{{.Type}}(address, nil, transactor)
|
||||
contract, err := bind{{.Type}}(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{.Type}}Transactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
|
||||
contract, err := bind{{.Type}}(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{.Type}}Filterer{contract: contract}, nil
|
||||
}
|
||||
|
||||
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
|
|
@ -263,6 +285,137 @@ package {{.Package}}
|
|||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{range .Events}}
|
||||
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
|
||||
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
|
||||
Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if (it.fail != nil) {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if (it.done) {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
// Error returns any retrieval or parsing error occurred during filtering.
|
||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
|
||||
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
|
||||
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type}}{{else}}{{bindtype .Type}}{{end}}; {{end}}
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
||||
//
|
||||
// Solidity: {{.Original.String}}
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
||||
{{range .Normalized.Inputs}}
|
||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||
for _, {{.Name}}Item := range {{.Name}} {
|
||||
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
||||
}{{end}}{{end}}
|
||||
|
||||
logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
||||
//
|
||||
// Solidity: {{.Original.String}}
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (event.Subscription, error) {
|
||||
{{range .Normalized.Inputs}}
|
||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||
for _, {{.Name}}Item := range {{.Name}} {
|
||||
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
||||
}{{end}}{{end}}
|
||||
|
||||
logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new({{$contract.Type}}{{.Normalized.Name}})
|
||||
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
|
|
|
|||
189
accounts/abi/bind/topics.go
Normal file
189
accounts/abi/bind/topics.go
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright 2018 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/EthereumCommonwealth/go-callisto/accounts/abi"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||
)
|
||||
|
||||
// makeTopics converts a filter query argument list into a filter topic set.
|
||||
func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||
topics := make([][]common.Hash, len(query))
|
||||
for i, filter := range query {
|
||||
for _, rule := range filter {
|
||||
var topic common.Hash
|
||||
|
||||
// Try to generate the topic based on simple types
|
||||
switch rule := rule.(type) {
|
||||
case common.Hash:
|
||||
copy(topic[:], rule[:])
|
||||
case common.Address:
|
||||
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
||||
case *big.Int:
|
||||
blob := rule.Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case bool:
|
||||
if rule {
|
||||
topic[common.HashLength-1] = 1
|
||||
}
|
||||
case int8:
|
||||
blob := big.NewInt(int64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case int16:
|
||||
blob := big.NewInt(int64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case int32:
|
||||
blob := big.NewInt(int64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case int64:
|
||||
blob := big.NewInt(rule).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case uint8:
|
||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case uint16:
|
||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case uint32:
|
||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case uint64:
|
||||
blob := new(big.Int).SetUint64(rule).Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
case string:
|
||||
hash := crypto.Keccak256Hash([]byte(rule))
|
||||
copy(topic[:], hash[:])
|
||||
case []byte:
|
||||
hash := crypto.Keccak256Hash(rule)
|
||||
copy(topic[:], hash[:])
|
||||
|
||||
default:
|
||||
// Attempt to generate the topic from funky types
|
||||
val := reflect.ValueOf(rule)
|
||||
|
||||
switch {
|
||||
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
||||
reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||
}
|
||||
}
|
||||
topics[i] = append(topics[i], topic)
|
||||
}
|
||||
}
|
||||
return topics, nil
|
||||
}
|
||||
|
||||
// Big batch of reflect types for topic reconstruction.
|
||||
var (
|
||||
reflectHash = reflect.TypeOf(common.Hash{})
|
||||
reflectAddress = reflect.TypeOf(common.Address{})
|
||||
reflectBigInt = reflect.TypeOf(new(big.Int))
|
||||
)
|
||||
|
||||
// parseTopics converts the indexed topic fields into actual log field values.
|
||||
//
|
||||
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
||||
// hashes as the topic value!
|
||||
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
|
||||
// Sanity check that the fields and topics match up
|
||||
if len(fields) != len(topics) {
|
||||
return errors.New("topic/field count mismatch")
|
||||
}
|
||||
// Iterate over all the fields and reconstruct them from topics
|
||||
for _, arg := range fields {
|
||||
if !arg.Indexed {
|
||||
return errors.New("non-indexed field in topic reconstruction")
|
||||
}
|
||||
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
|
||||
|
||||
// Try to parse the topic back into the fields based on primitive types
|
||||
switch field.Kind() {
|
||||
case reflect.Bool:
|
||||
if topics[0][common.HashLength-1] == 1 {
|
||||
field.Set(reflect.ValueOf(true))
|
||||
}
|
||||
case reflect.Int8:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(int8(num.Int64())))
|
||||
|
||||
case reflect.Int16:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(int16(num.Int64())))
|
||||
|
||||
case reflect.Int32:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(int32(num.Int64())))
|
||||
|
||||
case reflect.Int64:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(num.Int64()))
|
||||
|
||||
case reflect.Uint8:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(uint8(num.Uint64())))
|
||||
|
||||
case reflect.Uint16:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(uint16(num.Uint64())))
|
||||
|
||||
case reflect.Uint32:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(uint32(num.Uint64())))
|
||||
|
||||
case reflect.Uint64:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(num.Uint64()))
|
||||
|
||||
default:
|
||||
// Ran out of plain primitive types, try custom types
|
||||
switch field.Type() {
|
||||
case reflectHash: // Also covers all dynamic types
|
||||
field.Set(reflect.ValueOf(topics[0]))
|
||||
|
||||
case reflectAddress:
|
||||
var addr common.Address
|
||||
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
|
||||
field.Set(reflect.ValueOf(addr))
|
||||
|
||||
case reflectBigInt:
|
||||
num := new(big.Int).SetBytes(topics[0][:])
|
||||
field.Set(reflect.ValueOf(num))
|
||||
|
||||
default:
|
||||
// Ran out of custom types, try the crazies
|
||||
switch {
|
||||
case arg.Type.T == abi.FixedBytesTy:
|
||||
reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:]))
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
topics = topics[1:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -33,6 +33,17 @@ type Event struct {
|
|||
Inputs Arguments
|
||||
}
|
||||
|
||||
func (event Event) String() string {
|
||||
inputs := make([]string, len(event.Inputs))
|
||||
for i, input := range event.Inputs {
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
|
||||
if input.Indexed {
|
||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", "))
|
||||
}
|
||||
|
||||
// Id returns the canonical representation of the event's signature used by the
|
||||
// abi definition to identify event names and types.
|
||||
func (e Event) Id() common.Hash {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func NewAuthNeededError(needed string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Error implements the standard error interfacel.
|
||||
// Error implements the standard error interface.
|
||||
func (err *AuthNeededError) Error() string {
|
||||
return fmt.Sprintf("authentication needed: %s", err.Needed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/EthereumCommonwealth/go-callisto/cmd/utils"
|
||||
|
|
@ -96,12 +97,32 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
addr, err := net.ResolveUDPAddr("udp", *listenAddr)
|
||||
if err != nil {
|
||||
utils.Fatalf("-ResolveUDPAddr: %v", err)
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
utils.Fatalf("-ListenUDP: %v", err)
|
||||
}
|
||||
|
||||
realaddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
if natm != nil {
|
||||
if !realaddr.IP.IsLoopback() {
|
||||
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||
}
|
||||
// TODO: react to external IP changes over time.
|
||||
if ext, err := natm.ExternalIP(); err == nil {
|
||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||
}
|
||||
}
|
||||
|
||||
if *runv5 {
|
||||
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
if _, err := discv5.ListenUDP(nodeKey, conn, realaddr, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
if _, err := discover.ListenUDP(nodeKey, conn, realaddr, nil, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,6 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
|
|||
NoDiscovery: true,
|
||||
DiscoveryV5: true,
|
||||
ListenAddr: fmt.Sprintf(":%d", port),
|
||||
DiscoveryV5Addr: fmt.Sprintf(":%d", port+1),
|
||||
MaxPeers: 25,
|
||||
BootstrapNodesV5: enodes,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -29,7 +28,6 @@ import (
|
|||
cli "gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/EthereumCommonwealth/go-callisto/cmd/utils"
|
||||
"github.com/EthereumCommonwealth/go-callisto/contracts/release"
|
||||
"github.com/EthereumCommonwealth/go-callisto/dashboard"
|
||||
"github.com/EthereumCommonwealth/go-callisto/eth"
|
||||
"github.com/EthereumCommonwealth/go-callisto/node"
|
||||
|
|
@ -177,21 +175,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
|||
if cfg.Ethstats.URL != "" {
|
||||
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
||||
}
|
||||
|
||||
// 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(params.VersionMajor),
|
||||
Minor: uint32(params.VersionMinor),
|
||||
Patch: uint32(params.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 stack
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
|||
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
||||
}
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyV5Bootnodes
|
||||
urls = params.RinkebyBootnodes
|
||||
case cfg.BootstrapNodesV5 != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
|
|
@ -636,14 +636,6 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
// setDiscoveryV5Address creates a UDP listening address string from set command
|
||||
// line flags for the V5 discovery protocol.
|
||||
func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) {
|
||||
if ctx.GlobalIsSet(ListenPortFlag.Name) {
|
||||
cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
|
||||
}
|
||||
}
|
||||
|
||||
// setNAT creates a port mapper from command line flags.
|
||||
func setNAT(ctx *cli.Context, cfg *p2p.Config) {
|
||||
if ctx.GlobalIsSet(NATFlag.Name) {
|
||||
|
|
@ -794,7 +786,6 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
|||
setNodeKey(ctx, cfg)
|
||||
setNAT(ctx, cfg)
|
||||
setListenAddress(ctx, cfg)
|
||||
setDiscoveryV5Address(ctx, cfg)
|
||||
setBootstrapNodes(ctx, cfg)
|
||||
setBootstrapNodesV5(ctx, cfg)
|
||||
|
||||
|
|
@ -830,7 +821,6 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
|||
// --dev mode can't use p2p networking.
|
||||
cfg.MaxPeers = 0
|
||||
cfg.ListenAddr = ":0"
|
||||
cfg.DiscoveryV5Addr = ":0"
|
||||
cfg.NoDiscovery = true
|
||||
cfg.DiscoveryV5 = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ func requestExpiredMessagesLoop() {
|
|||
if err != nil {
|
||||
utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
|
||||
}
|
||||
peerID = extractIdFromEnode(*argEnode)
|
||||
peerID = extractIDFromEnode(*argEnode)
|
||||
shh.AllowP2PMessagesFromPeer(peerID)
|
||||
|
||||
for {
|
||||
|
|
@ -652,7 +652,7 @@ func requestExpiredMessagesLoop() {
|
|||
}
|
||||
}
|
||||
|
||||
func extractIdFromEnode(s string) []byte {
|
||||
func extractIDFromEnode(s string) []byte {
|
||||
n, err := discover.ParseNode(s)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse enode: %s", err)
|
||||
|
|
|
|||
|
|
@ -355,9 +355,11 @@ func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte)
|
|||
return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup)
|
||||
}
|
||||
|
||||
const maxEpoch = 2048
|
||||
|
||||
// datasetSizes is a lookup table for the ethash dataset size for the first 2048
|
||||
// epochs (i.e. 61440000 blocks).
|
||||
var datasetSizes = []uint64{
|
||||
var datasetSizes = [maxEpoch]uint64{
|
||||
1073739904, 1082130304, 1090514816, 1098906752, 1107293056,
|
||||
1115684224, 1124070016, 1132461952, 1140849536, 1149232768,
|
||||
1157627776, 1166013824, 1174404736, 1182786944, 1191180416,
|
||||
|
|
@ -771,7 +773,7 @@ var datasetSizes = []uint64{
|
|||
|
||||
// cacheSizes is a lookup table for the ethash verification cache size for the
|
||||
// first 2048 epochs (i.e. 61440000 blocks).
|
||||
var cacheSizes = []uint64{
|
||||
var cacheSizes = [maxEpoch]uint64{
|
||||
16776896, 16907456, 17039296, 17170112, 17301056, 17432512, 17563072,
|
||||
17693888, 17824192, 17955904, 18087488, 18218176, 18349504, 18481088,
|
||||
18611392, 18742336, 18874304, 19004224, 19135936, 19267264, 19398208,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ package ethash
|
|||
func cacheSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(cacheSizes) {
|
||||
if epoch < maxEpoch {
|
||||
return cacheSizes[epoch]
|
||||
}
|
||||
// We don't have a way to verify primes fast before Go 1.8
|
||||
|
|
@ -39,7 +39,7 @@ func cacheSize(block uint64) uint64 {
|
|||
func datasetSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(datasetSizes) {
|
||||
if epoch < maxEpoch {
|
||||
return datasetSizes[epoch]
|
||||
}
|
||||
// We don't have a way to verify primes fast before Go 1.8
|
||||
|
|
|
|||
|
|
@ -20,17 +20,20 @@ package ethash
|
|||
|
||||
import "math/big"
|
||||
|
||||
// cacheSize calculates and returns the size of the ethash verification cache that
|
||||
// belongs to a certain block number. The cache size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
// cacheSize returns the size of the ethash verification cache that belongs to a certain
|
||||
// block number.
|
||||
func cacheSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(cacheSizes) {
|
||||
if epoch < maxEpoch {
|
||||
return cacheSizes[epoch]
|
||||
}
|
||||
// No known cache size, calculate manually (sanity branch only)
|
||||
return calcCacheSize(epoch)
|
||||
}
|
||||
|
||||
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
|
||||
// however, we always take the highest prime below the linearly growing threshold in order
|
||||
// to reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func calcCacheSize(epoch int) uint64 {
|
||||
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
|
||||
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
|
||||
size -= 2 * hashBytes
|
||||
|
|
@ -38,17 +41,20 @@ func cacheSize(block uint64) uint64 {
|
|||
return size
|
||||
}
|
||||
|
||||
// datasetSize calculates and returns the size of the ethash mining dataset that
|
||||
// belongs to a certain block number. The dataset size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
// datasetSize returns the size of the ethash mining dataset that belongs to a certain
|
||||
// block number.
|
||||
func datasetSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(datasetSizes) {
|
||||
if epoch < maxEpoch {
|
||||
return datasetSizes[epoch]
|
||||
}
|
||||
// No known dataset size, calculate manually (sanity branch only)
|
||||
return calcDatasetSize(epoch)
|
||||
}
|
||||
|
||||
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
|
||||
// however, we always take the highest prime below the linearly growing threshold in order
|
||||
// to reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func calcDatasetSize(epoch int) uint64 {
|
||||
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
|
||||
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
|
||||
size -= 2 * mixBytes
|
||||
|
|
|
|||
|
|
@ -23,24 +23,15 @@ import "testing"
|
|||
// Tests whether the dataset size calculator works correctly by cross checking the
|
||||
// hard coded lookup table with the value generated by it.
|
||||
func TestSizeCalculations(t *testing.T) {
|
||||
var tests []uint64
|
||||
|
||||
// Verify all the cache sizes from the lookup table
|
||||
defer func(sizes []uint64) { cacheSizes = sizes }(cacheSizes)
|
||||
tests, cacheSizes = cacheSizes, []uint64{}
|
||||
|
||||
for i, test := range tests {
|
||||
if size := cacheSize(uint64(i*epochLength) + 1); size != test {
|
||||
t.Errorf("cache %d: cache size mismatch: have %d, want %d", i, size, test)
|
||||
// Verify all the cache and dataset sizes from the lookup table.
|
||||
for epoch, want := range cacheSizes {
|
||||
if size := calcCacheSize(epoch); size != want {
|
||||
t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want)
|
||||
}
|
||||
}
|
||||
// Verify all the dataset sizes from the lookup table
|
||||
defer func(sizes []uint64) { datasetSizes = sizes }(datasetSizes)
|
||||
tests, datasetSizes = datasetSizes, []uint64{}
|
||||
|
||||
for i, test := range tests {
|
||||
if size := datasetSize(uint64(i*epochLength) + 1); size != test {
|
||||
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", i, size, test)
|
||||
for epoch, want := range datasetSizes {
|
||||
if size := calcDatasetSize(epoch); size != want {
|
||||
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -529,7 +529,7 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
|
|||
}
|
||||
// Sanity check that the block number is below the lookup table size (60M blocks)
|
||||
number := header.Number.Uint64()
|
||||
if number/epochLength >= uint64(len(cacheSizes)) {
|
||||
if number/epochLength >= maxEpoch {
|
||||
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
|
||||
return errNonceOutOfRange
|
||||
}
|
||||
|
|
@ -537,14 +537,18 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
|
|||
if header.Difficulty.Sign() <= 0 {
|
||||
return errInvalidDifficulty
|
||||
}
|
||||
|
||||
// Recompute the digest and PoW value and verify against the header
|
||||
cache := ethash.cache(number)
|
||||
|
||||
size := datasetSize(number)
|
||||
if ethash.config.PowMode == ModeTest {
|
||||
size = 32 * 1024
|
||||
}
|
||||
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
|
||||
digest, result := hashimotoLight(size, cache.cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
|
||||
// Caches are unmapped in a finalizer. Ensure that the cache stays live
|
||||
// until after the call to hashimotoLight so it's not unmapped while being used.
|
||||
runtime.KeepAlive(cache)
|
||||
|
||||
if !bytes.Equal(header.MixDigest[:], digest) {
|
||||
return errInvalidMixDigest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,15 +26,17 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
mmap "github.com/edsrzf/mmap-go"
|
||||
"github.com/EthereumCommonwealth/go-callisto/consensus"
|
||||
"github.com/EthereumCommonwealth/go-callisto/log"
|
||||
"github.com/EthereumCommonwealth/go-callisto/rpc"
|
||||
mmap "github.com/edsrzf/mmap-go"
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
metrics "github.com/rcrowley/go-metrics"
|
||||
)
|
||||
|
||||
|
|
@ -142,32 +144,82 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
|
|||
return memoryMap(path)
|
||||
}
|
||||
|
||||
// lru tracks caches or datasets by their last use time, keeping at most N of them.
|
||||
type lru struct {
|
||||
what string
|
||||
new func(epoch uint64) interface{}
|
||||
mu sync.Mutex
|
||||
// Items are kept in a LRU cache, but there is a special case:
|
||||
// We always keep an item for (highest seen epoch) + 1 as the 'future item'.
|
||||
cache *simplelru.LRU
|
||||
future uint64
|
||||
futureItem interface{}
|
||||
}
|
||||
|
||||
// newlru create a new least-recently-used cache for ither the verification caches
|
||||
// or the mining datasets.
|
||||
func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru {
|
||||
if maxItems <= 0 {
|
||||
maxItems = 1
|
||||
}
|
||||
cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) {
|
||||
log.Trace("Evicted ethash "+what, "epoch", key)
|
||||
})
|
||||
return &lru{what: what, new: new, cache: cache}
|
||||
}
|
||||
|
||||
// get retrieves or creates an item for the given epoch. The first return value is always
|
||||
// non-nil. The second return value is non-nil if lru thinks that an item will be useful in
|
||||
// the near future.
|
||||
func (lru *lru) get(epoch uint64) (item, future interface{}) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
// Get or create the item for the requested epoch.
|
||||
item, ok := lru.cache.Get(epoch)
|
||||
if !ok {
|
||||
if lru.future > 0 && lru.future == epoch {
|
||||
item = lru.futureItem
|
||||
} else {
|
||||
log.Trace("Requiring new ethash "+lru.what, "epoch", epoch)
|
||||
item = lru.new(epoch)
|
||||
}
|
||||
lru.cache.Add(epoch, item)
|
||||
}
|
||||
// Update the 'future item' if epoch is larger than previously seen.
|
||||
if epoch < maxEpoch-1 && lru.future < epoch+1 {
|
||||
log.Trace("Requiring new future ethash "+lru.what, "epoch", epoch+1)
|
||||
future = lru.new(epoch + 1)
|
||||
lru.future = epoch + 1
|
||||
lru.futureItem = future
|
||||
}
|
||||
return item, future
|
||||
}
|
||||
|
||||
// cache wraps an ethash cache with some metadata to allow easier concurrent use.
|
||||
type cache struct {
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
cache []uint32 // The actual cache data content (may be memory mapped)
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
}
|
||||
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
|
||||
cache []uint32 // The actual cache data content (may be memory mapped)
|
||||
used time.Time // Timestamp of the last use for smarter eviction
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
lock sync.Mutex // Ensures thread safety for updating the usage time
|
||||
// newCache creates a new ethash verification cache and returns it as a plain Go
|
||||
// interface to be usable in an LRU cache.
|
||||
func newCache(epoch uint64) interface{} {
|
||||
return &cache{epoch: epoch}
|
||||
}
|
||||
|
||||
// generate ensures that the cache content is generated before use.
|
||||
func (c *cache) generate(dir string, limit int, test bool) {
|
||||
c.once.Do(func() {
|
||||
// If we have a testing cache, generate and return
|
||||
if test {
|
||||
c.cache = make([]uint32, 1024/4)
|
||||
generateCache(c.cache, c.epoch, seedHash(c.epoch*epochLength+1))
|
||||
return
|
||||
}
|
||||
// If we don't store anything on disk, generate and return
|
||||
size := cacheSize(c.epoch*epochLength + 1)
|
||||
seed := seedHash(c.epoch*epochLength + 1)
|
||||
|
||||
if test {
|
||||
size = 1024
|
||||
}
|
||||
// If we don't store anything on disk, generate and return.
|
||||
if dir == "" {
|
||||
c.cache = make([]uint32, size/4)
|
||||
generateCache(c.cache, c.epoch, seed)
|
||||
|
|
@ -181,6 +233,10 @@ func (c *cache) generate(dir string, limit int, test bool) {
|
|||
path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
logger := log.New("epoch", c.epoch)
|
||||
|
||||
// We're about to mmap the file, ensure that the mapping is cleaned up when the
|
||||
// cache becomes unused.
|
||||
runtime.SetFinalizer(c, (*cache).finalizer)
|
||||
|
||||
// Try to load the file from disk and memory map it
|
||||
var err error
|
||||
c.dump, c.mmap, c.cache, err = memoryMap(path)
|
||||
|
|
@ -207,49 +263,41 @@ func (c *cache) generate(dir string, limit int, test bool) {
|
|||
})
|
||||
}
|
||||
|
||||
// release closes any file handlers and memory maps open.
|
||||
func (c *cache) release() {
|
||||
// finalizer unmaps the memory and closes the file.
|
||||
func (c *cache) finalizer() {
|
||||
if c.mmap != nil {
|
||||
c.mmap.Unmap()
|
||||
c.mmap = nil
|
||||
}
|
||||
if c.dump != nil {
|
||||
c.dump.Close()
|
||||
c.dump = nil
|
||||
c.mmap, c.dump = nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// dataset wraps an ethash dataset with some metadata to allow easier concurrent use.
|
||||
type dataset struct {
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
dataset []uint32 // The actual cache data content
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
}
|
||||
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
|
||||
dataset []uint32 // The actual cache data content
|
||||
used time.Time // Timestamp of the last use for smarter eviction
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
lock sync.Mutex // Ensures thread safety for updating the usage time
|
||||
// newDataset creates a new ethash mining dataset and returns it as a plain Go
|
||||
// interface to be usable in an LRU cache.
|
||||
func newDataset(epoch uint64) interface{} {
|
||||
return &dataset{epoch: epoch}
|
||||
}
|
||||
|
||||
// generate ensures that the dataset content is generated before use.
|
||||
func (d *dataset) generate(dir string, limit int, test bool) {
|
||||
d.once.Do(func() {
|
||||
// If we have a testing dataset, generate and return
|
||||
if test {
|
||||
cache := make([]uint32, 1024/4)
|
||||
generateCache(cache, d.epoch, seedHash(d.epoch*epochLength+1))
|
||||
|
||||
d.dataset = make([]uint32, 32*1024/4)
|
||||
generateDataset(d.dataset, d.epoch, cache)
|
||||
|
||||
return
|
||||
}
|
||||
// If we don't store anything on disk, generate and return
|
||||
csize := cacheSize(d.epoch*epochLength + 1)
|
||||
dsize := datasetSize(d.epoch*epochLength + 1)
|
||||
seed := seedHash(d.epoch*epochLength + 1)
|
||||
|
||||
if test {
|
||||
csize = 1024
|
||||
dsize = 32 * 1024
|
||||
}
|
||||
// If we don't store anything on disk, generate and return
|
||||
if dir == "" {
|
||||
cache := make([]uint32, csize/4)
|
||||
generateCache(cache, d.epoch, seed)
|
||||
|
|
@ -265,6 +313,10 @@ func (d *dataset) generate(dir string, limit int, test bool) {
|
|||
path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
logger := log.New("epoch", d.epoch)
|
||||
|
||||
// We're about to mmap the file, ensure that the mapping is cleaned up when the
|
||||
// cache becomes unused.
|
||||
runtime.SetFinalizer(d, (*dataset).finalizer)
|
||||
|
||||
// Try to load the file from disk and memory map it
|
||||
var err error
|
||||
d.dump, d.mmap, d.dataset, err = memoryMap(path)
|
||||
|
|
@ -294,15 +346,12 @@ func (d *dataset) generate(dir string, limit int, test bool) {
|
|||
})
|
||||
}
|
||||
|
||||
// release closes any file handlers and memory maps open.
|
||||
func (d *dataset) release() {
|
||||
// finalizer closes any file handlers and memory maps open.
|
||||
func (d *dataset) finalizer() {
|
||||
if d.mmap != nil {
|
||||
d.mmap.Unmap()
|
||||
d.mmap = nil
|
||||
}
|
||||
if d.dump != nil {
|
||||
d.dump.Close()
|
||||
d.dump = nil
|
||||
d.mmap, d.dump = nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,14 +359,12 @@ func (d *dataset) release() {
|
|||
func MakeCache(block uint64, dir string) {
|
||||
c := cache{epoch: block / epochLength}
|
||||
c.generate(dir, math.MaxInt32, false)
|
||||
c.release()
|
||||
}
|
||||
|
||||
// MakeDataset generates a new ethash dataset and optionally stores it to disk.
|
||||
func MakeDataset(block uint64, dir string) {
|
||||
d := dataset{epoch: block / epochLength}
|
||||
d.generate(dir, math.MaxInt32, false)
|
||||
d.release()
|
||||
}
|
||||
|
||||
// Mode defines the type and amount of PoW verification an ethash engine makes.
|
||||
|
|
@ -347,10 +394,8 @@ type Config struct {
|
|||
type Ethash struct {
|
||||
config Config
|
||||
|
||||
caches map[uint64]*cache // In memory caches to avoid regenerating too often
|
||||
fcache *cache // Pre-generated cache for the estimated future epoch
|
||||
datasets map[uint64]*dataset // In memory datasets to avoid regenerating too often
|
||||
fdataset *dataset // Pre-generated dataset for the estimated future epoch
|
||||
caches *lru // In memory caches to avoid regenerating too often
|
||||
datasets *lru // In memory datasets to avoid regenerating too often
|
||||
|
||||
// Mining related fields
|
||||
rand *rand.Rand // Properly seeded random source for nonces
|
||||
|
|
@ -380,8 +425,8 @@ func New(config Config) *Ethash {
|
|||
}
|
||||
return &Ethash{
|
||||
config: config,
|
||||
caches: make(map[uint64]*cache),
|
||||
datasets: make(map[uint64]*dataset),
|
||||
caches: newlru("cache", config.CachesInMem, newCache),
|
||||
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
|
||||
update: make(chan struct{}),
|
||||
hashrate: metrics.NewMeter(),
|
||||
}
|
||||
|
|
@ -390,16 +435,7 @@ func New(config Config) *Ethash {
|
|||
// NewTester creates a small sized ethash PoW scheme useful only for testing
|
||||
// purposes.
|
||||
func NewTester() *Ethash {
|
||||
return &Ethash{
|
||||
config: Config{
|
||||
CachesInMem: 1,
|
||||
PowMode: ModeTest,
|
||||
},
|
||||
caches: make(map[uint64]*cache),
|
||||
datasets: make(map[uint64]*dataset),
|
||||
update: make(chan struct{}),
|
||||
hashrate: metrics.NewMeter(),
|
||||
}
|
||||
return New(Config{CachesInMem: 1, PowMode: ModeTest})
|
||||
}
|
||||
|
||||
// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts
|
||||
|
|
@ -456,126 +492,40 @@ func NewShared() *Ethash {
|
|||
// cache tries to retrieve a verification cache for the specified block number
|
||||
// by first checking against a list of in-memory caches, then against caches
|
||||
// stored on disk, and finally generating one if none can be found.
|
||||
func (ethash *Ethash) cache(block uint64) []uint32 {
|
||||
func (ethash *Ethash) cache(block uint64) *cache {
|
||||
epoch := block / epochLength
|
||||
currentI, futureI := ethash.caches.get(epoch)
|
||||
current := currentI.(*cache)
|
||||
|
||||
// If we have a PoW for that epoch, use that
|
||||
ethash.lock.Lock()
|
||||
|
||||
current, future := ethash.caches[epoch], (*cache)(nil)
|
||||
if current == nil {
|
||||
// No in-memory cache, evict the oldest if the cache limit was reached
|
||||
for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.config.CachesInMem {
|
||||
var evict *cache
|
||||
for _, cache := range ethash.caches {
|
||||
if evict == nil || evict.used.After(cache.used) {
|
||||
evict = cache
|
||||
}
|
||||
}
|
||||
delete(ethash.caches, evict.epoch)
|
||||
evict.release()
|
||||
|
||||
log.Trace("Evicted ethash cache", "epoch", evict.epoch, "used", evict.used)
|
||||
}
|
||||
// If we have the new cache pre-generated, use that, otherwise create a new one
|
||||
if ethash.fcache != nil && ethash.fcache.epoch == epoch {
|
||||
log.Trace("Using pre-generated cache", "epoch", epoch)
|
||||
current, ethash.fcache = ethash.fcache, nil
|
||||
} else {
|
||||
log.Trace("Requiring new ethash cache", "epoch", epoch)
|
||||
current = &cache{epoch: epoch}
|
||||
}
|
||||
ethash.caches[epoch] = current
|
||||
|
||||
// If we just used up the future cache, or need a refresh, regenerate
|
||||
if ethash.fcache == nil || ethash.fcache.epoch <= epoch {
|
||||
if ethash.fcache != nil {
|
||||
ethash.fcache.release()
|
||||
}
|
||||
log.Trace("Requiring new future ethash cache", "epoch", epoch+1)
|
||||
future = &cache{epoch: epoch + 1}
|
||||
ethash.fcache = future
|
||||
}
|
||||
// New current cache, set its initial timestamp
|
||||
current.used = time.Now()
|
||||
}
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
// Wait for generation finish.
|
||||
current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
|
||||
|
||||
current.lock.Lock()
|
||||
current.used = time.Now()
|
||||
current.lock.Unlock()
|
||||
|
||||
// If we exhausted the future cache, now's a good time to regenerate it
|
||||
if future != nil {
|
||||
// If we need a new future cache, now's a good time to regenerate it.
|
||||
if futureI != nil {
|
||||
future := futureI.(*cache)
|
||||
go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
|
||||
}
|
||||
return current.cache
|
||||
return current
|
||||
}
|
||||
|
||||
// dataset tries to retrieve a mining dataset for the specified block number
|
||||
// by first checking against a list of in-memory datasets, then against DAGs
|
||||
// stored on disk, and finally generating one if none can be found.
|
||||
func (ethash *Ethash) dataset(block uint64) []uint32 {
|
||||
func (ethash *Ethash) dataset(block uint64) *dataset {
|
||||
epoch := block / epochLength
|
||||
currentI, futureI := ethash.datasets.get(epoch)
|
||||
current := currentI.(*dataset)
|
||||
|
||||
// If we have a PoW for that epoch, use that
|
||||
ethash.lock.Lock()
|
||||
|
||||
current, future := ethash.datasets[epoch], (*dataset)(nil)
|
||||
if current == nil {
|
||||
// No in-memory dataset, evict the oldest if the dataset limit was reached
|
||||
for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.config.DatasetsInMem {
|
||||
var evict *dataset
|
||||
for _, dataset := range ethash.datasets {
|
||||
if evict == nil || evict.used.After(dataset.used) {
|
||||
evict = dataset
|
||||
}
|
||||
}
|
||||
delete(ethash.datasets, evict.epoch)
|
||||
evict.release()
|
||||
|
||||
log.Trace("Evicted ethash dataset", "epoch", evict.epoch, "used", evict.used)
|
||||
}
|
||||
// If we have the new cache pre-generated, use that, otherwise create a new one
|
||||
if ethash.fdataset != nil && ethash.fdataset.epoch == epoch {
|
||||
log.Trace("Using pre-generated dataset", "epoch", epoch)
|
||||
current = &dataset{epoch: ethash.fdataset.epoch} // Reload from disk
|
||||
ethash.fdataset = nil
|
||||
} else {
|
||||
log.Trace("Requiring new ethash dataset", "epoch", epoch)
|
||||
current = &dataset{epoch: epoch}
|
||||
}
|
||||
ethash.datasets[epoch] = current
|
||||
|
||||
// If we just used up the future dataset, or need a refresh, regenerate
|
||||
if ethash.fdataset == nil || ethash.fdataset.epoch <= epoch {
|
||||
if ethash.fdataset != nil {
|
||||
ethash.fdataset.release()
|
||||
}
|
||||
log.Trace("Requiring new future ethash dataset", "epoch", epoch+1)
|
||||
future = &dataset{epoch: epoch + 1}
|
||||
ethash.fdataset = future
|
||||
}
|
||||
// New current dataset, set its initial timestamp
|
||||
current.used = time.Now()
|
||||
}
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
// Wait for generation finish.
|
||||
current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
|
||||
|
||||
current.lock.Lock()
|
||||
current.used = time.Now()
|
||||
current.lock.Unlock()
|
||||
|
||||
// If we exhausted the future dataset, now's a good time to regenerate it
|
||||
if future != nil {
|
||||
// If we need a new future dataset, now's a good time to regenerate it.
|
||||
if futureI != nil {
|
||||
future := futureI.(*dataset)
|
||||
go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
|
||||
}
|
||||
return current.dataset
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
// Threads returns the number of mining threads currently enabled. This doesn't
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@
|
|||
package ethash
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
|
|
@ -38,3 +42,38 @@ func TestTestMode(t *testing.T) {
|
|||
t.Fatalf("unexpected verification error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks that cache lru logic doesn't crash under load.
|
||||
// It reproduces https://github.com/EthereumCommonwealth/go-callisto/issues/14943
|
||||
func TestCacheFileEvict(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "ethash-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
e := New(Config{CachesInMem: 3, CachesOnDisk: 10, CacheDir: tmpdir, PowMode: ModeTest})
|
||||
|
||||
workers := 8
|
||||
epochs := 100
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(workers)
|
||||
for i := 0; i < workers; i++ {
|
||||
go verifyTest(&wg, e, i, epochs)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) {
|
||||
defer wg.Done()
|
||||
|
||||
const wiggle = 4 * epochLength
|
||||
r := rand.New(rand.NewSource(int64(workerIndex)))
|
||||
for epoch := 0; epoch < epochs; epoch++ {
|
||||
block := int64(epoch)*epochLength - wiggle/2 + r.Int63n(wiggle)
|
||||
if block < 0 {
|
||||
block = 0
|
||||
}
|
||||
head := &types.Header{Number: big.NewInt(block), Difficulty: big.NewInt(100)}
|
||||
e.VerifySeal(nil, head)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,10 +97,9 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
|
|||
func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
|
||||
// Extract some data from the header
|
||||
var (
|
||||
header = block.Header()
|
||||
hash = header.HashNoNonce().Bytes()
|
||||
target = new(big.Int).Div(maxUint256, header.Difficulty)
|
||||
|
||||
header = block.Header()
|
||||
hash = header.HashNoNonce().Bytes()
|
||||
target = new(big.Int).Div(maxUint256, header.Difficulty)
|
||||
number = header.Number.Uint64()
|
||||
dataset = ethash.dataset(number)
|
||||
)
|
||||
|
|
@ -111,13 +110,14 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
|
|||
)
|
||||
logger := log.New("miner", id)
|
||||
logger.Trace("Started ethash search for new nonces", "seed", seed)
|
||||
search:
|
||||
for {
|
||||
select {
|
||||
case <-abort:
|
||||
// Mining terminated, update stats and abort
|
||||
logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed)
|
||||
ethash.hashrate.Mark(attempts)
|
||||
return
|
||||
break search
|
||||
|
||||
default:
|
||||
// We don't have to update hash rate on every nonce, so update after after 2^X nonces
|
||||
|
|
@ -127,7 +127,7 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
|
|||
attempts = 0
|
||||
}
|
||||
// Compute the PoW value of this nonce
|
||||
digest, result := hashimotoFull(dataset, hash, nonce)
|
||||
digest, result := hashimotoFull(dataset.dataset, hash, nonce)
|
||||
if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
|
||||
// Correct nonce found, create a new header with it
|
||||
header = types.CopyHeader(header)
|
||||
|
|
@ -141,9 +141,12 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
|
|||
case <-abort:
|
||||
logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
|
||||
}
|
||||
return
|
||||
break search
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
// Datasets are unmapped in a finalizer. Ensure that the dataset stays live
|
||||
// during sealing so it's not unmapped while being read.
|
||||
runtime.KeepAlive(dataset)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,19 @@ import (
|
|||
"math/big"
|
||||
"strings"
|
||||
|
||||
ethereum "github.com/EthereumCommonwealth/go-callisto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/accounts/abi"
|
||||
"github.com/EthereumCommonwealth/go-callisto/accounts/abi/bind"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
"github.com/EthereumCommonwealth/go-callisto/event"
|
||||
)
|
||||
|
||||
// ChequebookABI is the input ABI used to generate the binding from.
|
||||
const ChequebookABI = "[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"sent\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"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\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"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 = `0x606060405260008054600160a060020a033316600160a060020a03199091161790556102ec806100306000396000f3006060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a7230582014e927522ca5cd8f68529ac4d3b9cdf36d40e09d8a33b70008248d1abebf79680029`
|
||||
const ChequebookBin = `0x606060405260008054600160a060020a033316600160a060020a03199091161790556102ec806100306000396000f3006060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a72305820533e856fc37e3d64d1706bcc7dfb6b1d490c8d566ea498d9d01ec08965a896ca0029`
|
||||
|
||||
// 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) {
|
||||
|
|
@ -29,13 +31,14 @@ func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (co
|
|||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil
|
||||
return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}, ChequebookFilterer: ChequebookFilterer{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
|
||||
ChequebookFilterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
|
|
@ -48,6 +51,11 @@ type ChequebookTransactor struct {
|
|||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// ChequebookFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type ChequebookFilterer 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 {
|
||||
|
|
@ -87,16 +95,16 @@ type ChequebookTransactorRaw struct {
|
|||
|
||||
// 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)
|
||||
contract, err := bindChequebook(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil
|
||||
return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}, ChequebookFilterer: ChequebookFilterer{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)
|
||||
contract, err := bindChequebook(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -105,20 +113,29 @@ func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*C
|
|||
|
||||
// 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)
|
||||
contract, err := bindChequebook(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ChequebookTransactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewChequebookFilterer creates a new log filterer instance of Chequebook, bound to a specific deployed contract.
|
||||
func NewChequebookFilterer(address common.Address, filterer bind.ContractFilterer) (*ChequebookFilterer, error) {
|
||||
contract, err := bindChequebook(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ChequebookFilterer{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) {
|
||||
func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(ChequebookABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
|
|
@ -226,3 +243,125 @@ func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) {
|
|||
func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) {
|
||||
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts)
|
||||
}
|
||||
|
||||
// ChequebookOverdraftIterator is returned from FilterOverdraft and is used to iterate over the raw logs and unpacked data for Overdraft events raised by the Chequebook contract.
|
||||
type ChequebookOverdraftIterator struct {
|
||||
Event *ChequebookOverdraft // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *ChequebookOverdraftIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ChequebookOverdraft)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ChequebookOverdraft)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error retruned any retrieval or parsing error occurred during filtering.
|
||||
func (it *ChequebookOverdraftIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *ChequebookOverdraftIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChequebookOverdraft represents a Overdraft event raised by the Chequebook contract.
|
||||
type ChequebookOverdraft struct {
|
||||
Deadbeat common.Address
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterOverdraft is a free log retrieval operation binding the contract event 0x2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f978.
|
||||
//
|
||||
// Solidity: event Overdraft(deadbeat address)
|
||||
func (_Chequebook *ChequebookFilterer) FilterOverdraft(opts *bind.FilterOpts) (*ChequebookOverdraftIterator, error) {
|
||||
|
||||
logs, sub, err := _Chequebook.contract.FilterLogs(opts, "Overdraft")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ChequebookOverdraftIterator{contract: _Chequebook.contract, event: "Overdraft", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchOverdraft is a free log subscription operation binding the contract event 0x2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f978.
|
||||
//
|
||||
// Solidity: event Overdraft(deadbeat address)
|
||||
func (_Chequebook *ChequebookFilterer) WatchOverdraft(opts *bind.WatchOpts, sink chan<- *ChequebookOverdraft) (event.Subscription, error) {
|
||||
|
||||
logs, sub, err := _Chequebook.contract.WatchLogs(opts, "Overdraft")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(ChequebookOverdraft)
|
||||
if err := _Chequebook.contract.UnpackLog(event, "Overdraft", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ package contract
|
|||
|
||||
// ContractDeployedCode is used to detect suicides. This constant needs to be
|
||||
// updated when the contract code is changed.
|
||||
const ContractDeployedCode = "0x6060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a7230582014e927522ca5cd8f68529ac4d3b9cdf36d40e09d8a33b70008248d1abebf79680029"
|
||||
const ContractDeployedCode = "0x6060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a72305820533e856fc37e3d64d1706bcc7dfb6b1d490c8d566ea498d9d01ec08965a896ca0029"
|
||||
|
|
|
|||
|
|
@ -6,17 +6,19 @@ package contract
|
|||
import (
|
||||
"strings"
|
||||
|
||||
ethereum "github.com/EthereumCommonwealth/go-callisto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/accounts/abi"
|
||||
"github.com/EthereumCommonwealth/go-callisto/accounts/abi/bind"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
"github.com/EthereumCommonwealth/go-callisto/event"
|
||||
)
|
||||
|
||||
// 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\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"label\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setSubnodeOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"setTTL\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"setResolver\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"NewTTL\",\"type\":\"event\"}]"
|
||||
|
||||
// ENSBin is the compiled bytecode used for deploying new contracts.
|
||||
const ENSBin = `0x6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610503806100626000396000f3006060604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100b957806306ab5923146100cf57806314ab9038146100f657806316a25cbd146101195780631896f70a1461014c5780635b0fc9c31461016e575b600080fd5b341561009257600080fd5b61009d600435610190565b604051600160a060020a03909116815260200160405180910390f35b34156100c457600080fd5b61009d6004356101ae565b34156100da57600080fd5b6100f4600435602435600160a060020a03604435166101c9565b005b341561010157600080fd5b6100f460043567ffffffffffffffff6024351661028b565b341561012457600080fd5b61012f600435610357565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015757600080fd5b6100f4600435600160a060020a036024351661038e565b341561017957600080fd5b6100f4600435600160a060020a0360243516610434565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f257600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b457600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b757600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045d57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582087c335a130f7bd19015451f7e1dc0e44cdeb5b64393f51a105ee00160711fcff0029`
|
||||
const ENSBin = `0x6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610503806100626000396000f3006060604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100b957806306ab5923146100cf57806314ab9038146100f657806316a25cbd146101195780631896f70a1461014c5780635b0fc9c31461016e575b600080fd5b341561009257600080fd5b61009d600435610190565b604051600160a060020a03909116815260200160405180910390f35b34156100c457600080fd5b61009d6004356101ae565b34156100da57600080fd5b6100f4600435602435600160a060020a03604435166101c9565b005b341561010157600080fd5b6100f460043567ffffffffffffffff6024351661028b565b341561012457600080fd5b61012f600435610357565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015757600080fd5b6100f4600435600160a060020a036024351661038e565b341561017957600080fd5b6100f4600435600160a060020a0360243516610434565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f257600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b457600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b757600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045d57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a72305820f4c798d4c84c9912f389f64631e85e8d16c3e6644f8c2e1579936015c7d5f6660029`
|
||||
|
||||
// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it.
|
||||
func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) {
|
||||
|
|
@ -28,13 +30,14 @@ func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Ad
|
|||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
|
||||
return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}, ENSFilterer: ENSFilterer{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
|
||||
ENSFilterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// ENSCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
|
|
@ -47,6 +50,11 @@ type ENSTransactor struct {
|
|||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// ENSFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type ENSFilterer 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 {
|
||||
|
|
@ -86,16 +94,16 @@ type ENSTransactorRaw struct {
|
|||
|
||||
// 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)
|
||||
contract, err := bindENS(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
|
||||
return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}, ENSFilterer: ENSFilterer{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)
|
||||
contract, err := bindENS(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -104,20 +112,29 @@ func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCalle
|
|||
|
||||
// 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)
|
||||
contract, err := bindENS(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSTransactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewENSFilterer creates a new log filterer instance of ENS, bound to a specific deployed contract.
|
||||
func NewENSFilterer(address common.Address, filterer bind.ContractFilterer) (*ENSFilterer, error) {
|
||||
contract, err := bindENS(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSFilterer{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) {
|
||||
func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(ENSABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
|
|
@ -319,3 +336,544 @@ func (_ENS *ENSSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, e
|
|||
func (_ENS *ENSTransactorSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, error) {
|
||||
return _ENS.Contract.SetTTL(&_ENS.TransactOpts, node, ttl)
|
||||
}
|
||||
|
||||
// ENSNewOwnerIterator is returned from FilterNewOwner and is used to iterate over the raw logs and unpacked data for NewOwner events raised by the ENS contract.
|
||||
type ENSNewOwnerIterator struct {
|
||||
Event *ENSNewOwner // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *ENSNewOwnerIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewOwner)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewOwner)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error retruned any retrieval or parsing error occurred during filtering.
|
||||
func (it *ENSNewOwnerIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *ENSNewOwnerIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENSNewOwner represents a NewOwner event raised by the ENS contract.
|
||||
type ENSNewOwner struct {
|
||||
Node [32]byte
|
||||
Label [32]byte
|
||||
Owner common.Address
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterNewOwner is a free log retrieval operation binding the contract event 0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82.
|
||||
//
|
||||
// Solidity: event NewOwner(node indexed bytes32, label indexed bytes32, owner address)
|
||||
func (_ENS *ENSFilterer) FilterNewOwner(opts *bind.FilterOpts, node [][32]byte, label [][32]byte) (*ENSNewOwnerIterator, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
var labelRule []interface{}
|
||||
for _, labelItem := range label {
|
||||
labelRule = append(labelRule, labelItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewOwner", nodeRule, labelRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSNewOwnerIterator{contract: _ENS.contract, event: "NewOwner", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchNewOwner is a free log subscription operation binding the contract event 0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82.
|
||||
//
|
||||
// Solidity: event NewOwner(node indexed bytes32, label indexed bytes32, owner address)
|
||||
func (_ENS *ENSFilterer) WatchNewOwner(opts *bind.WatchOpts, sink chan<- *ENSNewOwner, node [][32]byte, label [][32]byte) (event.Subscription, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
var labelRule []interface{}
|
||||
for _, labelItem := range label {
|
||||
labelRule = append(labelRule, labelItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewOwner", nodeRule, labelRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(ENSNewOwner)
|
||||
if err := _ENS.contract.UnpackLog(event, "NewOwner", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ENSNewResolverIterator is returned from FilterNewResolver and is used to iterate over the raw logs and unpacked data for NewResolver events raised by the ENS contract.
|
||||
type ENSNewResolverIterator struct {
|
||||
Event *ENSNewResolver // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *ENSNewResolverIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewResolver)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewResolver)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error retruned any retrieval or parsing error occurred during filtering.
|
||||
func (it *ENSNewResolverIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *ENSNewResolverIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENSNewResolver represents a NewResolver event raised by the ENS contract.
|
||||
type ENSNewResolver struct {
|
||||
Node [32]byte
|
||||
Resolver common.Address
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterNewResolver is a free log retrieval operation binding the contract event 0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0.
|
||||
//
|
||||
// Solidity: event NewResolver(node indexed bytes32, resolver address)
|
||||
func (_ENS *ENSFilterer) FilterNewResolver(opts *bind.FilterOpts, node [][32]byte) (*ENSNewResolverIterator, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewResolver", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSNewResolverIterator{contract: _ENS.contract, event: "NewResolver", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchNewResolver is a free log subscription operation binding the contract event 0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0.
|
||||
//
|
||||
// Solidity: event NewResolver(node indexed bytes32, resolver address)
|
||||
func (_ENS *ENSFilterer) WatchNewResolver(opts *bind.WatchOpts, sink chan<- *ENSNewResolver, node [][32]byte) (event.Subscription, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewResolver", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(ENSNewResolver)
|
||||
if err := _ENS.contract.UnpackLog(event, "NewResolver", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ENSNewTTLIterator is returned from FilterNewTTL and is used to iterate over the raw logs and unpacked data for NewTTL events raised by the ENS contract.
|
||||
type ENSNewTTLIterator struct {
|
||||
Event *ENSNewTTL // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *ENSNewTTLIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewTTL)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSNewTTL)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error retruned any retrieval or parsing error occurred during filtering.
|
||||
func (it *ENSNewTTLIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *ENSNewTTLIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENSNewTTL represents a NewTTL event raised by the ENS contract.
|
||||
type ENSNewTTL struct {
|
||||
Node [32]byte
|
||||
Ttl uint64
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterNewTTL is a free log retrieval operation binding the contract event 0x1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68.
|
||||
//
|
||||
// Solidity: event NewTTL(node indexed bytes32, ttl uint64)
|
||||
func (_ENS *ENSFilterer) FilterNewTTL(opts *bind.FilterOpts, node [][32]byte) (*ENSNewTTLIterator, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewTTL", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSNewTTLIterator{contract: _ENS.contract, event: "NewTTL", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchNewTTL is a free log subscription operation binding the contract event 0x1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68.
|
||||
//
|
||||
// Solidity: event NewTTL(node indexed bytes32, ttl uint64)
|
||||
func (_ENS *ENSFilterer) WatchNewTTL(opts *bind.WatchOpts, sink chan<- *ENSNewTTL, node [][32]byte) (event.Subscription, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewTTL", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(ENSNewTTL)
|
||||
if err := _ENS.contract.UnpackLog(event, "NewTTL", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ENSTransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the ENS contract.
|
||||
type ENSTransferIterator struct {
|
||||
Event *ENSTransfer // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *ENSTransferIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSTransfer)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(ENSTransfer)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error retruned any retrieval or parsing error occurred during filtering.
|
||||
func (it *ENSTransferIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *ENSTransferIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENSTransfer represents a Transfer event raised by the ENS contract.
|
||||
type ENSTransfer struct {
|
||||
Node [32]byte
|
||||
Owner common.Address
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterTransfer is a free log retrieval operation binding the contract event 0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266.
|
||||
//
|
||||
// Solidity: event Transfer(node indexed bytes32, owner address)
|
||||
func (_ENS *ENSFilterer) FilterTransfer(opts *bind.FilterOpts, node [][32]byte) (*ENSTransferIterator, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.FilterLogs(opts, "Transfer", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ENSTransferIterator{contract: _ENS.contract, event: "Transfer", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchTransfer is a free log subscription operation binding the contract event 0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266.
|
||||
//
|
||||
// Solidity: event Transfer(node indexed bytes32, owner address)
|
||||
func (_ENS *ENSFilterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ENSTransfer, node [][32]byte) (event.Subscription, error) {
|
||||
|
||||
var nodeRule []interface{}
|
||||
for _, nodeItem := range node {
|
||||
nodeRule = append(nodeRule, nodeItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _ENS.contract.WatchLogs(opts, "Transfer", nodeRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(ENSTransfer)
|
||||
if err := _ENS.contract.UnpackLog(event, "Transfer", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"subnode\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
|
||||
|
||||
// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts.
|
||||
const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058209b0c0f4ed76e4fe49a71d4b838ab3d00d6bad29021172db7ced9f36abcafbf510029`
|
||||
const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058206fb963cb168d5e3a51af12cd6bb23e324dbd32dd4954f43653ba27e66b68ea650029`
|
||||
|
||||
// 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) {
|
||||
|
|
@ -28,13 +28,14 @@ func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend,
|
|||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil
|
||||
return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{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
|
||||
FIFSRegistrarFilterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
|
|
@ -47,6 +48,11 @@ type FIFSRegistrarTransactor struct {
|
|||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// FIFSRegistrarFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type FIFSRegistrarFilterer 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 {
|
||||
|
|
@ -86,16 +92,16 @@ type FIFSRegistrarTransactorRaw struct {
|
|||
|
||||
// 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)
|
||||
contract, err := bindFIFSRegistrar(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil
|
||||
return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{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)
|
||||
contract, err := bindFIFSRegistrar(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -104,20 +110,29 @@ func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller)
|
|||
|
||||
// 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)
|
||||
contract, err := bindFIFSRegistrar(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FIFSRegistrarTransactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewFIFSRegistrarFilterer creates a new log filterer instance of FIFSRegistrar, bound to a specific deployed contract.
|
||||
func NewFIFSRegistrarFilterer(address common.Address, filterer bind.ContractFilterer) (*FIFSRegistrarFilterer, error) {
|
||||
contract, err := bindFIFSRegistrar(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FIFSRegistrarFilterer{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) {
|
||||
func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,251 +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/>.
|
||||
|
||||
pragma solidity ^0.4.18;
|
||||
|
||||
// ReleaseOracle is an Ethereum contract to store the current and previous
|
||||
// versions of the go-ethereum implementation. Its goal is to allow Geth to
|
||||
// check for new releases automatically without the need to consult a central
|
||||
// repository.
|
||||
//
|
||||
// The contract takes a vote based approach on both assigning authorised signers
|
||||
// as well as signing off on new Geth releases.
|
||||
//
|
||||
// Note, when a signer is demoted, the currently pending release is auto-nuked.
|
||||
// The reason is to prevent suprises where a demotion actually tilts the votes
|
||||
// in favor of one voter party and pushing out a new release as a consequence of
|
||||
// a simple demotion.
|
||||
contract ReleaseOracle {
|
||||
// Votes is an internal data structure to count votes on a specific proposal
|
||||
struct Votes {
|
||||
address[] pass; // List of signers voting to pass a proposal
|
||||
address[] fail; // List of signers voting to fail a proposal
|
||||
}
|
||||
|
||||
// Version is the version details of a particular Geth release
|
||||
struct Version {
|
||||
uint32 major; // Major version component of the release
|
||||
uint32 minor; // Minor version component of the release
|
||||
uint32 patch; // Patch version component of the release
|
||||
bytes20 commit; // Git SHA1 commit hash of the release
|
||||
|
||||
uint64 time; // Timestamp of the release approval
|
||||
Votes votes; // Votes that passed this release
|
||||
}
|
||||
|
||||
// Oracle authorization details
|
||||
mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract
|
||||
address[] voters; // List of addresses currently accepted as signers
|
||||
|
||||
// Various proposals being voted on
|
||||
mapping(address => Votes) authProps; // Currently running user authorization proposals
|
||||
address[] authPend; // List of addresses being voted on (map indexes)
|
||||
|
||||
Version verProp; // Currently proposed release being voted on
|
||||
Version[] releases; // All the positively voted releases
|
||||
|
||||
// isSigner is a modifier to authorize contract transactions.
|
||||
modifier isSigner() {
|
||||
if (authorised[msg.sender]) {
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor to assign the initial set of signers.
|
||||
function ReleaseOracle(address[] signers) {
|
||||
// If no signers were specified, assign the creator as the sole signer
|
||||
if (signers.length == 0) {
|
||||
authorised[msg.sender] = true;
|
||||
voters.push(msg.sender);
|
||||
return;
|
||||
}
|
||||
// Otherwise assign the individual signers one by one
|
||||
for (uint i = 0; i < signers.length; i++) {
|
||||
authorised[signers[i]] = true;
|
||||
voters.push(signers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// signers is an accessor method to retrieve all the signers (public accessor
|
||||
// generates an indexed one, not a retrieve-all version).
|
||||
function signers() constant returns(address[]) {
|
||||
return voters;
|
||||
}
|
||||
|
||||
// authProposals retrieves the list of addresses that authorization proposals
|
||||
// are currently being voted on.
|
||||
function authProposals() constant returns(address[]) {
|
||||
return authPend;
|
||||
}
|
||||
|
||||
// authVotes retrieves the current authorization votes for a particular user
|
||||
// to promote him into the list of signers, or demote him from there.
|
||||
function authVotes(address user) constant returns(address[] promote, address[] demote) {
|
||||
return (authProps[user].pass, authProps[user].fail);
|
||||
}
|
||||
|
||||
// currentVersion retrieves the semantic version, commit hash and release time
|
||||
// of the currently votec active release.
|
||||
function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) {
|
||||
if (releases.length == 0) {
|
||||
return (0, 0, 0, 0, 0);
|
||||
}
|
||||
var release = releases[releases.length - 1];
|
||||
|
||||
return (release.major, release.minor, release.patch, release.commit, release.time);
|
||||
}
|
||||
|
||||
// proposedVersion retrieves the semantic version, commit hash and the current
|
||||
// votes for the next proposed release.
|
||||
function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) {
|
||||
return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail);
|
||||
}
|
||||
|
||||
// promote pitches in on a voting campaign to promote a new user to a signer
|
||||
// position.
|
||||
function promote(address user) {
|
||||
updateSigner(user, true);
|
||||
}
|
||||
|
||||
// demote pitches in on a voting campaign to demote an authorised user from
|
||||
// its signer position.
|
||||
function demote(address user) {
|
||||
updateSigner(user, false);
|
||||
}
|
||||
|
||||
// release votes for a particular version to be included as the next release.
|
||||
function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) {
|
||||
updateRelease(major, minor, patch, commit, true);
|
||||
}
|
||||
|
||||
// nuke votes for the currently proposed version to not be included as the next
|
||||
// release. Nuking doesn't require a specific version number for simplicity.
|
||||
function nuke() {
|
||||
updateRelease(0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
// updateSigner marks a vote for changing the status of an Ethereum user, either
|
||||
// for or against the user being an authorised signer.
|
||||
function updateSigner(address user, bool authorize) internal isSigner {
|
||||
// Gather the current votes and ensure we don't double vote
|
||||
Votes votes = authProps[user];
|
||||
for (uint i = 0; i < votes.pass.length; i++) {
|
||||
if (votes.pass[i] == msg.sender) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < votes.fail.length; i++) {
|
||||
if (votes.fail[i] == msg.sender) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If no authorization proposal is open, add the user to the index for later lookups
|
||||
if (votes.pass.length == 0 && votes.fail.length == 0) {
|
||||
authPend.push(user);
|
||||
}
|
||||
// Cast the vote and return if the proposal cannot be resolved yet
|
||||
if (authorize) {
|
||||
votes.pass.push(msg.sender);
|
||||
if (votes.pass.length <= voters.length / 2) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
votes.fail.push(msg.sender);
|
||||
if (votes.fail.length <= voters.length / 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Proposal resolved in our favor, execute whatever we voted on
|
||||
if (authorize && !authorised[user]) {
|
||||
authorised[user] = true;
|
||||
voters.push(user);
|
||||
} else if (!authorize && authorised[user]) {
|
||||
authorised[user] = false;
|
||||
|
||||
for (i = 0; i < voters.length; i++) {
|
||||
if (voters[i] == user) {
|
||||
voters[i] = voters[voters.length - 1];
|
||||
voters.length--;
|
||||
|
||||
delete verProp; // Nuke any version proposal (no surprise releases!)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally delete the resolved proposal, index and garbage collect
|
||||
delete authProps[user];
|
||||
|
||||
for (i = 0; i < authPend.length; i++) {
|
||||
if (authPend[i] == user) {
|
||||
authPend[i] = authPend[authPend.length - 1];
|
||||
authPend.length--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updateRelease votes for a particular version to be included as the next release,
|
||||
// or for the currently proposed release to be nuked out.
|
||||
function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner {
|
||||
// Skip nuke votes if no proposal is pending
|
||||
if (!release && verProp.votes.pass.length == 0) {
|
||||
return;
|
||||
}
|
||||
// Mark a new release if no proposal is pending
|
||||
if (verProp.votes.pass.length == 0) {
|
||||
verProp.major = major;
|
||||
verProp.minor = minor;
|
||||
verProp.patch = patch;
|
||||
verProp.commit = commit;
|
||||
}
|
||||
// Make sure positive votes match the current proposal
|
||||
if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) {
|
||||
return;
|
||||
}
|
||||
// Gather the current votes and ensure we don't double vote
|
||||
Votes votes = verProp.votes;
|
||||
for (uint i = 0; i < votes.pass.length; i++) {
|
||||
if (votes.pass[i] == msg.sender) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < votes.fail.length; i++) {
|
||||
if (votes.fail[i] == msg.sender) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Cast the vote and return if the proposal cannot be resolved yet
|
||||
if (release) {
|
||||
votes.pass.push(msg.sender);
|
||||
if (votes.pass.length <= voters.length / 2) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
votes.fail.push(msg.sender);
|
||||
if (votes.fail.length <= voters.length / 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Proposal resolved in our favor, execute whatever we voted on
|
||||
if (release) {
|
||||
verProp.time = uint64(now);
|
||||
releases.push(verProp);
|
||||
delete verProp;
|
||||
} else {
|
||||
delete verProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -465,7 +465,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
|||
}
|
||||
bc.currentBlock = block
|
||||
|
||||
// If the block is better than out head or is on a different chain, force update heads
|
||||
// If the block is better than our head or is on a different chain, force update heads
|
||||
if updateHeads {
|
||||
bc.hc.SetCurrentHeader(block.Header())
|
||||
|
||||
|
|
@ -1140,18 +1140,17 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
} else {
|
||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
||||
}
|
||||
// Insert the new chain, taking care of the proper incremental order
|
||||
var addedTxs types.Transactions
|
||||
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
||||
for _, block := range newChain {
|
||||
for i := len(newChain) - 1; i >= 0; i-- {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
bc.insert(block)
|
||||
bc.insert(newChain[i])
|
||||
// write lookup entries for hash based transaction/receipt searches
|
||||
if err := WriteTxLookupEntries(bc.chainDb, block); err != nil {
|
||||
if err := WriteTxLookupEntries(bc.chainDb, newChain[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
addedTxs = append(addedTxs, block.Transactions()...)
|
||||
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
||||
}
|
||||
|
||||
// calculate the difference between deleted and added transactions
|
||||
diff := types.TxDifference(deletedTxs, addedTxs)
|
||||
// When transactions get deleted from the database that means the
|
||||
|
|
|
|||
|
|
@ -1197,3 +1197,51 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
t.Error("account should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
// This is a regression test (i.e. as weird as it is, don't delete it ever), which
|
||||
// tests that under weird reorg conditions the blockchain and its internal header-
|
||||
// chain return the same latest block/header.
|
||||
//
|
||||
// https://github.com/EthereumCommonwealth/go-callisto/pull/15941
|
||||
func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
forks := make([]*types.Block, len(blocks))
|
||||
for i := 0; i < len(forks); i++ {
|
||||
parent := genesis
|
||||
if i > 0 {
|
||||
parent = blocks[i-1]
|
||||
}
|
||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
||||
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5392
dashboard/assets.go
5392
dashboard/assets.go
File diff suppressed because one or more lines are too long
|
|
@ -40,6 +40,9 @@
|
|||
'react/jsx-indent': ['error', 'tab'],
|
||||
'react/jsx-indent-props': ['error', 'tab'],
|
||||
'react/prefer-stateless-function': 'off',
|
||||
'jsx-quotes': ['error', 'prefer-single'],
|
||||
'no-plusplus': 'off',
|
||||
'no-console': ['error', { allow: ['error'] }],
|
||||
|
||||
// Specifies the maximum length of a line.
|
||||
'max-len': ['warn', 120, 2, {
|
||||
|
|
@ -49,7 +52,7 @@
|
|||
'ignoreStrings': true,
|
||||
'ignoreTemplateLiterals': true,
|
||||
}],
|
||||
// Enforces spacing between keys and values in object literal properties.
|
||||
// Enforces consistent spacing between keys and values in object literal properties.
|
||||
'key-spacing': ['error', {'align': {
|
||||
'beforeColon': false,
|
||||
'afterColon': true,
|
||||
|
|
|
|||
|
|
@ -63,3 +63,9 @@ export type MenuProp = {|...ProvidedMenuProp, id: string|};
|
|||
export const MENU: Map<string, {...MenuProp}> = new Map(menuSkeletons.map(({id, menu}) => ([id, {id, ...menu}])));
|
||||
|
||||
export const DURATION = 200;
|
||||
|
||||
export const styles = {
|
||||
light: {
|
||||
color: 'rgba(255, 255, 255, 0.54)',
|
||||
},
|
||||
}
|
||||
|
|
@ -18,35 +18,32 @@
|
|||
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import withStyles from 'material-ui/styles/withStyles';
|
||||
|
||||
import SideBar from './SideBar';
|
||||
import Main from './Main';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
// Styles for the Body component.
|
||||
const styles = () => ({
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
body: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export type Props = {
|
||||
classes: Object,
|
||||
opened: boolean,
|
||||
changeContent: () => {},
|
||||
changeContent: string => void,
|
||||
active: string,
|
||||
content: Content,
|
||||
shouldUpdate: Object,
|
||||
};
|
||||
|
||||
// Body renders the body of the dashboard.
|
||||
class Body extends Component<Props> {
|
||||
render() {
|
||||
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||
|
||||
return (
|
||||
<div className={classes.body}>
|
||||
<div style={styles.body}>
|
||||
<SideBar
|
||||
opened={this.props.opened}
|
||||
changeContent={this.props.changeContent}
|
||||
|
|
@ -61,4 +58,4 @@ class Body extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Body);
|
||||
export default Body;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// Copyright 2018 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
|
||||
|
|
@ -17,33 +17,41 @@
|
|||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import type {Node} from 'react';
|
||||
import type {ChildrenArray} from 'react';
|
||||
|
||||
import Grid from 'material-ui/Grid';
|
||||
import {ResponsiveContainer} from 'recharts';
|
||||
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
container: {
|
||||
flexWrap: 'nowrap',
|
||||
height: '100%',
|
||||
maxWidth: '100%',
|
||||
margin: 0,
|
||||
},
|
||||
item: {
|
||||
flex: 1,
|
||||
padding: 0,
|
||||
},
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
spacing: number,
|
||||
children: Node,
|
||||
children: ChildrenArray<React$Element<any>>,
|
||||
};
|
||||
// ChartGrid renders a grid container for responsive charts.
|
||||
// The children are Recharts components extended with the Material-UI's xs property.
|
||||
class ChartGrid extends Component<Props> {
|
||||
|
||||
// ChartRow renders a row of equally sized responsive charts.
|
||||
class ChartRow extends Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={this.props.spacing}>
|
||||
{
|
||||
React.Children.map(this.props.children, child => (
|
||||
<Grid item xs={child.props.xs}>
|
||||
<ResponsiveContainer width="100%" height={child.props.height}>
|
||||
{React.cloneElement(child, {data: child.props.values.map(value => ({value}))})}
|
||||
</ResponsiveContainer>
|
||||
</Grid>
|
||||
))
|
||||
}
|
||||
<Grid container direction='row' style={styles.container} justify='space-between'>
|
||||
{React.Children.map(this.props.children, child => (
|
||||
<Grid item xs style={styles.item}>
|
||||
{child}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChartGrid;
|
||||
export default ChartRow;
|
||||
95
dashboard/assets/components/CustomTooltip.jsx
Normal file
95
dashboard/assets/components/CustomTooltip.jsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// @flow
|
||||
|
||||
// Copyright 2018 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/>.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import Typography from 'material-ui/Typography';
|
||||
import {styles} from '../common';
|
||||
|
||||
// multiplier multiplies a number by another.
|
||||
export const multiplier = <T>(by: number = 1) => (x: number) => x * by;
|
||||
|
||||
// percentPlotter renders a tooltip, which displays the value of the payload followed by a percent sign.
|
||||
export const percentPlotter = <T>(text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => {
|
||||
const p = mapper(payload);
|
||||
if (typeof p !== 'number') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Typography type='caption' color='inherit'>
|
||||
<span style={styles.light}>{text}</span> {p.toFixed(2)} %
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
// unit contains the units for the bytePlotter.
|
||||
const unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||
|
||||
// simplifyBytes returns the simplified version of the given value followed by the unit.
|
||||
const simplifyBytes = (x: number) => {
|
||||
let i = 0;
|
||||
for (; x > 1024 && i < 5; i++) {
|
||||
x /= 1024;
|
||||
}
|
||||
return x.toFixed(2).toString().concat(' ', unit[i]);
|
||||
};
|
||||
|
||||
// bytePlotter renders a tooltip, which displays the payload as a byte value.
|
||||
export const bytePlotter = <T>(text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => {
|
||||
const p = mapper(payload);
|
||||
if (typeof p !== 'number') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Typography type='caption' color='inherit'>
|
||||
<span style={styles.light}>{text}</span> {simplifyBytes(p)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
// bytePlotter renders a tooltip, which displays the payload as a byte value followed by '/s'.
|
||||
export const bytePerSecPlotter = <T>(text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => {
|
||||
const p = mapper(payload);
|
||||
if (typeof p !== 'number') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Typography type='caption' color='inherit'>
|
||||
<span style={styles.light}>{text}</span> {simplifyBytes(p)}/s
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
export type Props = {
|
||||
active: boolean,
|
||||
payload: Object,
|
||||
tooltip: <T>(text: string, mapper?: T => T) => (payload: mixed) => null | React$Element<any>,
|
||||
};
|
||||
|
||||
// CustomTooltip takes a tooltip function, and uses it to plot the active value of the chart.
|
||||
class CustomTooltip extends Component<Props> {
|
||||
render() {
|
||||
const {active, payload, tooltip} = this.props;
|
||||
if (!active || typeof tooltip !== 'function') {
|
||||
return null;
|
||||
}
|
||||
return tooltip(payload[0].value);
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomTooltip;
|
||||
|
|
@ -22,8 +22,7 @@ import withStyles from 'material-ui/styles/withStyles';
|
|||
|
||||
import Header from './Header';
|
||||
import Body from './Body';
|
||||
import Footer from './Footer';
|
||||
import {MENU} from './Common';
|
||||
import {MENU} from '../common';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
// deepUpdate updates an object corresponding to the given update data, which has
|
||||
|
|
@ -35,17 +34,17 @@ import type {Content} from '../types/content';
|
|||
// the generalization of the message handling. The only necessary thing is to set a
|
||||
// handler function for every path of the state in order to maximize the flexibility
|
||||
// of the update.
|
||||
const deepUpdate = (prev: Object, update: Object, updater: Object) => {
|
||||
const deepUpdate = (updater: Object, update: Object, prev: Object): $Shape<Content> => {
|
||||
if (typeof update === 'undefined') {
|
||||
// TODO (kurkomisi): originally this was deep copy, investigate it.
|
||||
return prev;
|
||||
}
|
||||
if (typeof updater === 'function') {
|
||||
return updater(prev, update);
|
||||
return updater(update, prev);
|
||||
}
|
||||
const updated = {};
|
||||
Object.keys(prev).forEach((key) => {
|
||||
updated[key] = deepUpdate(prev[key], update[key], updater[key]);
|
||||
updated[key] = deepUpdate(updater[key], update[key], prev[key]);
|
||||
});
|
||||
|
||||
return updated;
|
||||
|
|
@ -56,21 +55,25 @@ const deepUpdate = (prev: Object, update: Object, updater: Object) => {
|
|||
// whether the involved data was changed or not by checking the message structure.
|
||||
//
|
||||
// We could return the message itself too, but it's safer not to give access to it.
|
||||
const shouldUpdate = (msg: Object, updater: Object) => {
|
||||
const shouldUpdate = (updater: Object, msg: Object) => {
|
||||
const su = {};
|
||||
Object.keys(msg).forEach((key) => {
|
||||
su[key] = typeof updater[key] !== 'function' ? shouldUpdate(msg[key], updater[key]) : true;
|
||||
su[key] = typeof updater[key] !== 'function' ? shouldUpdate(updater[key], msg[key]) : true;
|
||||
});
|
||||
|
||||
return su;
|
||||
};
|
||||
|
||||
// appender is a state update generalization function, which appends the update data
|
||||
// to the existing data. limit defines the maximum allowed size of the created array.
|
||||
const appender = <T>(limit: number) => (prev: Array<T>, update: Array<T>) => [...prev, ...update].slice(-limit);
|
||||
// replacer is a state updater function, which replaces the original data.
|
||||
const replacer = <T>(update: T) => update;
|
||||
|
||||
// replacer is a state update generalization function, which replaces the original data.
|
||||
const replacer = <T>(prev: T, update: T) => update;
|
||||
// appender is a state updater function, which appends the update data to the
|
||||
// existing data. limit defines the maximum allowed size of the created array,
|
||||
// mapper maps the update data.
|
||||
const appender = <T>(limit: number, mapper = replacer) => (update: Array<T>, prev: Array<T>) => [
|
||||
...prev,
|
||||
...update.map(sample => mapper(sample)),
|
||||
].slice(-limit);
|
||||
|
||||
// defaultContent is the initial value of the state content.
|
||||
const defaultContent: Content = {
|
||||
|
|
@ -79,8 +82,14 @@ const defaultContent: Content = {
|
|||
commit: null,
|
||||
},
|
||||
home: {
|
||||
memory: [],
|
||||
traffic: [],
|
||||
activeMemory: [],
|
||||
virtualMemory: [],
|
||||
networkIngress: [],
|
||||
networkEgress: [],
|
||||
processCPU: [],
|
||||
systemCPU: [],
|
||||
diskRead: [],
|
||||
diskWrite: [],
|
||||
},
|
||||
chain: {},
|
||||
txpool: {},
|
||||
|
|
@ -91,16 +100,23 @@ const defaultContent: Content = {
|
|||
},
|
||||
};
|
||||
|
||||
// updaters contains the state update generalization functions for each path of the state.
|
||||
// TODO (kurkomisi): Define a tricky type which embraces the content and the handlers.
|
||||
// updaters contains the state updater functions for each path of the state.
|
||||
//
|
||||
// TODO (kurkomisi): Define a tricky type which embraces the content and the updaters.
|
||||
const updaters = {
|
||||
general: {
|
||||
version: replacer,
|
||||
commit: replacer,
|
||||
},
|
||||
home: {
|
||||
memory: appender(200),
|
||||
traffic: appender(200),
|
||||
activeMemory: appender(200),
|
||||
virtualMemory: appender(200),
|
||||
networkIngress: appender(200),
|
||||
networkEgress: appender(200),
|
||||
processCPU: appender(200),
|
||||
systemCPU: appender(200),
|
||||
diskRead: appender(200),
|
||||
diskWrite: appender(200),
|
||||
},
|
||||
chain: null,
|
||||
txpool: null,
|
||||
|
|
@ -111,28 +127,34 @@ const updaters = {
|
|||
},
|
||||
};
|
||||
|
||||
// styles returns the styles for the Dashboard component.
|
||||
const styles = theme => ({
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
dashboard: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 1,
|
||||
overflow: 'hidden',
|
||||
}
|
||||
};
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles: Object = (theme: Object) => ({
|
||||
dashboard: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: theme.palette.background.default,
|
||||
zIndex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
classes: Object,
|
||||
classes: Object, // injected by withStyles()
|
||||
};
|
||||
|
||||
type State = {
|
||||
active: string, // active menu
|
||||
sideBar: boolean, // true if the sidebar is opened
|
||||
content: Content, // the visualized data
|
||||
shouldUpdate: Object // labels for the components, which need to rerender based on the incoming message
|
||||
shouldUpdate: Object, // labels for the components, which need to re-render based on the incoming message
|
||||
};
|
||||
|
||||
// Dashboard is the main component, which renders the whole page, makes connection with the server and
|
||||
|
|
@ -176,8 +198,8 @@ class Dashboard extends Component<Props, State> {
|
|||
// update updates the content corresponding to the incoming message.
|
||||
update = (msg: $Shape<Content>) => {
|
||||
this.setState(prevState => ({
|
||||
content: deepUpdate(prevState.content, msg, updaters),
|
||||
shouldUpdate: shouldUpdate(msg, updaters),
|
||||
content: deepUpdate(updaters, msg, prevState.content),
|
||||
shouldUpdate: shouldUpdate(updaters, msg),
|
||||
}));
|
||||
};
|
||||
|
||||
|
|
@ -186,25 +208,17 @@ class Dashboard extends Component<Props, State> {
|
|||
this.setState(prevState => (prevState.active !== newActive ? {active: newActive} : {}));
|
||||
};
|
||||
|
||||
// openSideBar opens the sidebar.
|
||||
openSideBar = () => {
|
||||
this.setState({sideBar: true});
|
||||
};
|
||||
|
||||
// closeSideBar closes the sidebar.
|
||||
closeSideBar = () => {
|
||||
this.setState({sideBar: false});
|
||||
// switchSideBar opens or closes the sidebar's state.
|
||||
switchSideBar = () => {
|
||||
this.setState(prevState => ({sideBar: !prevState.sideBar}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||
|
||||
return (
|
||||
<div className={classes.dashboard}>
|
||||
<div className={this.props.classes.dashboard} style={styles.dashboard}>
|
||||
<Header
|
||||
opened={this.state.sideBar}
|
||||
openSideBar={this.openSideBar}
|
||||
closeSideBar={this.closeSideBar}
|
||||
switchSideBar={this.switchSideBar}
|
||||
/>
|
||||
<Body
|
||||
opened={this.state.sideBar}
|
||||
|
|
@ -213,16 +227,9 @@ class Dashboard extends Component<Props, State> {
|
|||
content={this.state.content}
|
||||
shouldUpdate={this.state.shouldUpdate}
|
||||
/>
|
||||
<Footer
|
||||
opened={this.state.sideBar}
|
||||
openSideBar={this.openSideBar}
|
||||
closeSideBar={this.closeSideBar}
|
||||
general={this.state.content.general}
|
||||
shouldUpdate={this.state.shouldUpdate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Dashboard);
|
||||
export default withStyles(themeStyles)(Dashboard);
|
||||
|
|
|
|||
|
|
@ -19,62 +19,155 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
import withStyles from 'material-ui/styles/withStyles';
|
||||
import AppBar from 'material-ui/AppBar';
|
||||
import Toolbar from 'material-ui/Toolbar';
|
||||
import Typography from 'material-ui/Typography';
|
||||
import Grid from 'material-ui/Grid';
|
||||
import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts';
|
||||
|
||||
import type {General} from '../types/content';
|
||||
import ChartRow from './ChartRow';
|
||||
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
|
||||
import {styles as commonStyles} from '../common';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
// styles contains styles for the Header component.
|
||||
const styles = theme => ({
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
footer: {
|
||||
maxWidth: '100%',
|
||||
flexWrap: 'nowrap',
|
||||
margin: 0,
|
||||
},
|
||||
chartRowWrapper: {
|
||||
height: '100%',
|
||||
padding: 0,
|
||||
},
|
||||
doubleChartWrapper: {
|
||||
height: '100%',
|
||||
width: '99%',
|
||||
paddingTop: 5,
|
||||
},
|
||||
};
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles: Object = (theme: Object) => ({
|
||||
footer: {
|
||||
backgroundColor: theme.palette.background.appBar,
|
||||
color: theme.palette.getContrastText(theme.palette.background.appBar),
|
||||
zIndex: theme.zIndex.appBar,
|
||||
},
|
||||
toolbar: {
|
||||
paddingLeft: theme.spacing.unit,
|
||||
paddingRight: theme.spacing.unit,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
light: {
|
||||
color: 'rgba(255, 255, 255, 0.54)',
|
||||
height: theme.spacing.unit * 10,
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
general: General,
|
||||
classes: Object,
|
||||
classes: Object, // injected by withStyles()
|
||||
theme: Object,
|
||||
content: Content,
|
||||
shouldUpdate: Object,
|
||||
};
|
||||
// TODO (kurkomisi): If the structure is appropriate, make an abstraction of the common parts with the Header.
|
||||
// Footer renders the header of the dashboard.
|
||||
|
||||
// Footer renders the footer of the dashboard.
|
||||
class Footer extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return typeof nextProps.shouldUpdate.logs !== 'undefined';
|
||||
return typeof nextProps.shouldUpdate.home !== 'undefined';
|
||||
}
|
||||
|
||||
info = (about: string, data: string) => (
|
||||
<Typography type="caption" color="inherit">
|
||||
<span className={this.props.classes.light}>{about}</span> {data}
|
||||
// info renders a label with the given values.
|
||||
info = (about: string, value: ?string) => (value ? (
|
||||
<Typography type='caption' color='inherit'>
|
||||
<span style={commonStyles.light}>{about}</span> {value}
|
||||
</Typography>
|
||||
);
|
||||
) : null);
|
||||
|
||||
render() {
|
||||
const {classes, general} = this.props; // The classes property is injected by withStyles().
|
||||
const geth = general.version ? this.info('Geth', general.version) : null;
|
||||
const commit = general.commit ? this.info('Commit', general.commit.substring(0, 7)) : null;
|
||||
// doubleChart renders a pair of charts separated by the baseline.
|
||||
doubleChart = (syncId, topChart, bottomChart) => {
|
||||
const topKey = 'topKey';
|
||||
const bottomKey = 'bottomKey';
|
||||
const topDefault = topChart.default ? topChart.default : 0;
|
||||
const bottomDefault = bottomChart.default ? bottomChart.default : 0;
|
||||
const topTooltip = topChart.tooltip ? (
|
||||
<Tooltip cursor={false} content={<CustomTooltip tooltip={topChart.tooltip} />} />
|
||||
) : null;
|
||||
const bottomTooltip = bottomChart.tooltip ? (
|
||||
<Tooltip cursor={false} content={<CustomTooltip tooltip={bottomChart.tooltip} />} />
|
||||
) : null;
|
||||
const topColor = '#8884d8';
|
||||
const bottomColor = '#82ca9d';
|
||||
|
||||
// Put the samples of the two charts into the same array in order to avoid problems
|
||||
// at the synchronized area charts. If one of the two arrays doesn't have value at
|
||||
// a given position, give it a 0 default value.
|
||||
let data = [...topChart.data.map(({value}) => {
|
||||
const d = {};
|
||||
d[topKey] = value || topDefault;
|
||||
return d;
|
||||
})];
|
||||
for (let i = 0; i < data.length && i < bottomChart.data.length; i++) {
|
||||
// The value needs to be negative in order to plot it upside down.
|
||||
const d = bottomChart.data[i];
|
||||
data[i][bottomKey] = d && d.value ? -d.value : bottomDefault;
|
||||
}
|
||||
data = [...data, ...bottomChart.data.slice(data.length).map(({value}) => {
|
||||
const d = {};
|
||||
d[topKey] = topDefault;
|
||||
d[bottomKey] = -value || bottomDefault;
|
||||
return d;
|
||||
})];
|
||||
|
||||
return (
|
||||
<AppBar position="static" className={classes.footer}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<div>
|
||||
{geth}
|
||||
{commit}
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div style={styles.doubleChartWrapper}>
|
||||
<ResponsiveContainer width='100%' height='50%'>
|
||||
<AreaChart data={data} syncId={syncId} >
|
||||
{topTooltip}
|
||||
<Area type='monotone' dataKey={topKey} stroke={topColor} fill={topColor} />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
<div style={{marginTop: -10, width: '100%', height: '50%'}}>
|
||||
<ResponsiveContainer width='100%' height='100%'>
|
||||
<AreaChart data={data} syncId={syncId} >
|
||||
{bottomTooltip}
|
||||
<Area type='monotone' dataKey={bottomKey} stroke={bottomColor} fill={bottomColor} />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {content} = this.props;
|
||||
const {general, home} = content;
|
||||
|
||||
return (
|
||||
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
|
||||
<Grid item xs style={styles.chartRowWrapper}>
|
||||
<ChartRow>
|
||||
{this.doubleChart(
|
||||
'all',
|
||||
{data: home.processCPU, tooltip: percentPlotter('Process')},
|
||||
{data: home.systemCPU, tooltip: percentPlotter('System', multiplier(-1))},
|
||||
)}
|
||||
{this.doubleChart(
|
||||
'all',
|
||||
{data: home.activeMemory, tooltip: bytePlotter('Active')},
|
||||
{data: home.virtualMemory, tooltip: bytePlotter('Virtual', multiplier(-1))},
|
||||
)}
|
||||
{this.doubleChart(
|
||||
'all',
|
||||
{data: home.diskRead, tooltip: bytePerSecPlotter('Disk Read')},
|
||||
{data: home.diskWrite, tooltip: bytePerSecPlotter('Disk Write', multiplier(-1))},
|
||||
)}
|
||||
{this.doubleChart(
|
||||
'all',
|
||||
{data: home.networkIngress, tooltip: bytePerSecPlotter('Download')},
|
||||
{data: home.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
|
||||
)}
|
||||
</ChartRow>
|
||||
</Grid>
|
||||
<Grid item >
|
||||
{this.info('Geth', general.version)}
|
||||
{this.info('Commit', general.commit ? general.commit.substring(0, 7) : null)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Footer);
|
||||
export default withStyles(themeStyles)(Footer);
|
||||
|
|
|
|||
|
|
@ -26,18 +26,22 @@ import IconButton from 'material-ui/IconButton';
|
|||
import Typography from 'material-ui/Typography';
|
||||
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
|
||||
|
||||
import {DURATION} from './Common';
|
||||
import {DURATION} from '../common';
|
||||
|
||||
// arrowDefault is the default style of the arrow button.
|
||||
const arrowDefault = {
|
||||
transition: `transform ${DURATION}ms`,
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
arrow: {
|
||||
default: {
|
||||
transition: `transform ${DURATION}ms`,
|
||||
},
|
||||
transition: {
|
||||
entered: {transform: 'rotate(180deg)'},
|
||||
},
|
||||
},
|
||||
};
|
||||
// arrowTransition is the additional style of the arrow button corresponding to the transition's state.
|
||||
const arrowTransition = {
|
||||
entered: {transform: 'rotate(180deg)'},
|
||||
};
|
||||
// Styles for the Header component.
|
||||
const styles = theme => ({
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles = (theme: Object) => ({
|
||||
header: {
|
||||
backgroundColor: theme.palette.background.appBar,
|
||||
color: theme.palette.getContrastText(theme.palette.background.appBar),
|
||||
|
|
@ -47,53 +51,45 @@ const styles = theme => ({
|
|||
paddingLeft: theme.spacing.unit,
|
||||
paddingRight: theme.spacing.unit,
|
||||
},
|
||||
mainText: {
|
||||
title: {
|
||||
paddingLeft: theme.spacing.unit,
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
classes: Object,
|
||||
classes: Object, // injected by withStyles()
|
||||
opened: boolean,
|
||||
openSideBar: () => {},
|
||||
closeSideBar: () => {},
|
||||
switchSideBar: () => void,
|
||||
};
|
||||
|
||||
// Header renders the header of the dashboard.
|
||||
class Header extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.opened !== this.props.opened;
|
||||
}
|
||||
|
||||
// changeSideBar opens or closes the sidebar corresponding to the previous state.
|
||||
changeSideBar = () => {
|
||||
if (this.props.opened) {
|
||||
this.props.closeSideBar();
|
||||
} else {
|
||||
this.props.openSideBar();
|
||||
}
|
||||
};
|
||||
|
||||
// arrowButton is connected to the sidebar; changes its state.
|
||||
arrowButton = (transitionState: string) => (
|
||||
<IconButton onClick={this.changeSideBar}>
|
||||
// arrow renders a button, which changes the sidebar's state.
|
||||
arrow = (transitionState: string) => (
|
||||
<IconButton onClick={this.props.switchSideBar}>
|
||||
<ChevronLeftIcon
|
||||
style={{
|
||||
...arrowDefault,
|
||||
...arrowTransition[transitionState],
|
||||
...styles.arrow.default,
|
||||
...styles.arrow.transition[transitionState],
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
render() {
|
||||
const {classes, opened} = this.props; // The classes property is injected by withStyles().
|
||||
const {classes, opened} = this.props;
|
||||
|
||||
return (
|
||||
<AppBar position="static" className={classes.header}>
|
||||
<AppBar position='static' className={classes.header}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<Transition mountOnEnter in={opened} timeout={{enter: DURATION}}>
|
||||
{this.arrowButton}
|
||||
{this.arrow}
|
||||
</Transition>
|
||||
<Typography type="title" color="inherit" noWrap className={classes.mainText}>
|
||||
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||
Go Ethereum Dashboard
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
|
|
@ -102,4 +98,4 @@ class Header extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Header);
|
||||
export default withStyles(themeStyles)(Header);
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
// @flow
|
||||
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import withTheme from 'material-ui/styles/withTheme';
|
||||
import {LineChart, AreaChart, Area, YAxis, CartesianGrid, Line} from 'recharts';
|
||||
|
||||
import ChartGrid from './ChartGrid';
|
||||
import type {ChartEntry} from '../types/content';
|
||||
|
||||
export type Props = {
|
||||
theme: Object,
|
||||
memory: Array<ChartEntry>,
|
||||
traffic: Array<ChartEntry>,
|
||||
shouldUpdate: Object,
|
||||
};
|
||||
// Home renders the home content.
|
||||
class Home extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const {theme} = props; // The theme property is injected by withTheme().
|
||||
this.memoryColor = theme.palette.primary[300];
|
||||
this.trafficColor = theme.palette.secondary[300];
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return typeof nextProps.shouldUpdate.home !== 'undefined';
|
||||
}
|
||||
|
||||
memoryColor: Object;
|
||||
trafficColor: Object;
|
||||
|
||||
render() {
|
||||
let {memory, traffic} = this.props;
|
||||
memory = memory.map(({value}) => (value || 0));
|
||||
traffic = traffic.map(({value}) => (value || 0));
|
||||
|
||||
return (
|
||||
<ChartGrid spacing={24}>
|
||||
<AreaChart xs={6} height={300} values={memory}>
|
||||
<YAxis />
|
||||
<Area type="monotone" dataKey="value" stroke={this.memoryColor} fill={this.memoryColor} />
|
||||
</AreaChart>
|
||||
<LineChart xs={6} height={300} values={traffic}>
|
||||
<Line type="monotone" dataKey="value" stroke={this.trafficColor} dot={false} />
|
||||
</LineChart>
|
||||
<LineChart xs={6} height={300} values={memory}>
|
||||
<YAxis />
|
||||
<CartesianGrid stroke="#eee" strokeDasharray="5 5" />
|
||||
<Line type="monotone" dataKey="value" stroke={this.memoryColor} dot={false} />
|
||||
</LineChart>
|
||||
<AreaChart xs={6} height={300} values={traffic}>
|
||||
<CartesianGrid stroke="#eee" strokeDasharray="5 5" vertical={false} />
|
||||
<Area type="monotone" dataKey="value" stroke={this.trafficColor} fill={this.trafficColor} />
|
||||
</AreaChart>
|
||||
</ChartGrid>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme()(Home);
|
||||
|
|
@ -20,25 +20,38 @@ import React, {Component} from 'react';
|
|||
|
||||
import withStyles from 'material-ui/styles/withStyles';
|
||||
|
||||
import Home from './Home';
|
||||
import {MENU} from './Common';
|
||||
import {MENU} from '../common';
|
||||
import Footer from './Footer';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
// Styles for the Content component.
|
||||
const styles = theme => ({
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
},
|
||||
};
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles = theme => ({
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
padding: theme.spacing.unit * 3,
|
||||
overflow: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
classes: Object,
|
||||
active: string,
|
||||
content: Content,
|
||||
shouldUpdate: Object,
|
||||
};
|
||||
|
||||
// Main renders the chosen content.
|
||||
class Main extends Component<Props> {
|
||||
render() {
|
||||
|
|
@ -49,8 +62,6 @@ class Main extends Component<Props> {
|
|||
let children = null;
|
||||
switch (active) {
|
||||
case MENU.get('home').id:
|
||||
children = <Home memory={content.home.memory} traffic={content.home.traffic} shouldUpdate={shouldUpdate} />;
|
||||
break;
|
||||
case MENU.get('chain').id:
|
||||
case MENU.get('txpool').id:
|
||||
case MENU.get('network').id:
|
||||
|
|
@ -61,8 +72,16 @@ class Main extends Component<Props> {
|
|||
children = <div>{content.logs.log.map((log, index) => <div key={index}>{log}</div>)}</div>;
|
||||
}
|
||||
|
||||
return <div className={classes.content}>{children}</div>;
|
||||
return (
|
||||
<div style={styles.wrapper}>
|
||||
<div className={classes.content} style={styles.content}>{children}</div>
|
||||
<Footer
|
||||
content={content}
|
||||
shouldUpdate={shouldUpdate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Main);
|
||||
export default withStyles(themeStyles)(Main);
|
||||
|
|
|
|||
|
|
@ -24,18 +24,22 @@ import Icon from 'material-ui/Icon';
|
|||
import Transition from 'react-transition-group/Transition';
|
||||
import {Icon as FontAwesome} from 'react-fa';
|
||||
|
||||
import {MENU, DURATION} from './Common';
|
||||
import {MENU, DURATION} from '../common';
|
||||
|
||||
// menuDefault is the default style of the menu.
|
||||
const menuDefault = {
|
||||
transition: `margin-left ${DURATION}ms`,
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
menu: {
|
||||
default: {
|
||||
transition: `margin-left ${DURATION}ms`,
|
||||
},
|
||||
transition: {
|
||||
entered: {marginLeft: -200},
|
||||
},
|
||||
},
|
||||
};
|
||||
// menuTransition is the additional style of the menu corresponding to the transition's state.
|
||||
const menuTransition = {
|
||||
entered: {marginLeft: -200},
|
||||
};
|
||||
// Styles for the SideBar component.
|
||||
const styles = theme => ({
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles = theme => ({
|
||||
list: {
|
||||
background: theme.palette.background.appBar,
|
||||
},
|
||||
|
|
@ -46,38 +50,32 @@ const styles = theme => ({
|
|||
fontSize: theme.spacing.unit * 3,
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
classes: Object,
|
||||
classes: Object, // injected by withStyles()
|
||||
opened: boolean,
|
||||
changeContent: () => {},
|
||||
changeContent: string => void,
|
||||
};
|
||||
|
||||
// SideBar renders the sidebar of the dashboard.
|
||||
class SideBar extends Component<Props> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// clickOn contains onClick event functions for the menu items.
|
||||
// Instantiate only once, and reuse the existing functions to prevent the creation of
|
||||
// new function instances every time the render method is triggered.
|
||||
this.clickOn = {};
|
||||
MENU.forEach((menu) => {
|
||||
this.clickOn[menu.id] = (event) => {
|
||||
event.preventDefault();
|
||||
props.changeContent(menu.id);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.opened !== this.props.opened;
|
||||
}
|
||||
|
||||
// clickOn returns a click event handler function for the given menu item.
|
||||
clickOn = menu => (event) => {
|
||||
event.preventDefault();
|
||||
this.props.changeContent(menu);
|
||||
};
|
||||
|
||||
// menuItems returns the menu items corresponding to the sidebar state.
|
||||
menuItems = (transitionState) => {
|
||||
const {classes} = this.props;
|
||||
const children = [];
|
||||
MENU.forEach((menu) => {
|
||||
children.push(
|
||||
<ListItem button key={menu.id} onClick={this.clickOn[menu.id]} className={classes.listItem}>
|
||||
children.push((
|
||||
<ListItem button key={menu.id} onClick={this.clickOn(menu.id)} className={classes.listItem}>
|
||||
<ListItemIcon>
|
||||
<Icon className={classes.icon}>
|
||||
<FontAwesome name={menu.icon} />
|
||||
|
|
@ -86,29 +84,25 @@ class SideBar extends Component<Props> {
|
|||
<ListItemText
|
||||
primary={menu.title}
|
||||
style={{
|
||||
...menuDefault,
|
||||
...menuTransition[transitionState],
|
||||
...styles.menu.default,
|
||||
...styles.menu.transition[transitionState],
|
||||
padding: 0,
|
||||
}}
|
||||
/>
|
||||
</ListItem>,
|
||||
);
|
||||
</ListItem>
|
||||
));
|
||||
});
|
||||
return children;
|
||||
};
|
||||
|
||||
// menu renders the list of the menu items.
|
||||
menu = (transitionState) => {
|
||||
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||
|
||||
return (
|
||||
<div className={classes.list}>
|
||||
<List>
|
||||
{this.menuItems(transitionState)}
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
menu = (transitionState: Object) => (
|
||||
<div className={this.props.classes.list}>
|
||||
<List>
|
||||
{this.menuItems(transitionState)}
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
|
@ -119,4 +113,4 @@ class SideBar extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(SideBar);
|
||||
export default withStyles(themeStyles)(SideBar);
|
||||
|
|
|
|||
|
|
@ -24,18 +24,18 @@ import createMuiTheme from 'material-ui/styles/createMuiTheme';
|
|||
|
||||
import Dashboard from './components/Dashboard';
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
},
|
||||
const theme: Object = createMuiTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
},
|
||||
});
|
||||
const dashboard = document.getElementById('dashboard');
|
||||
if (dashboard) {
|
||||
// Renders the whole dashboard.
|
||||
render(
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<Dashboard />
|
||||
</MuiThemeProvider>,
|
||||
dashboard,
|
||||
);
|
||||
// Renders the whole dashboard.
|
||||
render(
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<Dashboard />
|
||||
</MuiThemeProvider>,
|
||||
dashboard,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
566
dashboard/assets/package-lock.json
generated
566
dashboard/assets/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^8.1.2",
|
||||
"babel-eslint": "^8.2.1",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
|
|
@ -12,8 +12,8 @@
|
|||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"classnames": "^2.2.5",
|
||||
"css-loader": "^0.28.8",
|
||||
"eslint": "^4.15.0",
|
||||
"css-loader": "^0.28.9",
|
||||
"eslint": "^4.16.0",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-loader": "^1.9.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
|
|
@ -24,16 +24,15 @@
|
|||
"flow-bin": "^0.63.1",
|
||||
"flow-bin-loader": "^1.0.2",
|
||||
"flow-typed": "^2.2.3",
|
||||
"material-ui": "^1.0.0-beta.24",
|
||||
"material-ui": "^1.0.0-beta.30",
|
||||
"material-ui-icons": "^1.0.0-beta.17",
|
||||
"path": "^0.12.7",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-fa": "^5.0.0",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"recharts": "^1.0.0-beta.7",
|
||||
"recharts": "^1.0.0-beta.9",
|
||||
"style-loader": "^0.19.1",
|
||||
"typeface-roboto": "^0.0.50",
|
||||
"url": "^0.11.0",
|
||||
"url-loader": "^0.6.2",
|
||||
"webpack": "^3.10.0"
|
||||
|
|
|
|||
|
|
@ -32,8 +32,14 @@ export type General = {
|
|||
};
|
||||
|
||||
export type Home = {
|
||||
memory: ChartEntries,
|
||||
traffic: ChartEntries,
|
||||
activeMemory: ChartEntries,
|
||||
virtualMemory: ChartEntries,
|
||||
networkIngress: ChartEntries,
|
||||
networkEgress: ChartEntries,
|
||||
processCPU: ChartEntries,
|
||||
systemCPU: ChartEntries,
|
||||
diskRead: ChartEntries,
|
||||
diskWrite: ChartEntries,
|
||||
};
|
||||
|
||||
export type ChartEntries = Array<ChartEntry>;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import "time"
|
|||
var DefaultConfig = Config{
|
||||
Host: "localhost",
|
||||
Port: 8080,
|
||||
Refresh: 3 * time.Second,
|
||||
Refresh: 5 * time.Second,
|
||||
}
|
||||
|
||||
// Config contains the configuration parameters of the dashboard.
|
||||
|
|
|
|||
35
dashboard/cpu.go
Normal file
35
dashboard/cpu.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2018 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 !windows
|
||||
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/EthereumCommonwealth/go-callisto/log"
|
||||
)
|
||||
|
||||
// getProcessCPUTime retrieves the process' CPU time since program startup.
|
||||
func getProcessCPUTime() float64 {
|
||||
var usage syscall.Rusage
|
||||
if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil {
|
||||
log.Warn("Failed to retrieve CPU time", "err", err)
|
||||
return 0
|
||||
}
|
||||
return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1000000
|
||||
}
|
||||
23
dashboard/cpu_windows.go
Normal file
23
dashboard/cpu_windows.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2018 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 dashboard
|
||||
|
||||
// getProcessCPUTime returns 0 on Windows as there is no system call to resolve
|
||||
// the actual process' CPU time.
|
||||
func getProcessCPUTime() float64 {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
|
@ -37,13 +38,20 @@ import (
|
|||
"github.com/EthereumCommonwealth/go-callisto/p2p"
|
||||
"github.com/EthereumCommonwealth/go-callisto/params"
|
||||
"github.com/EthereumCommonwealth/go-callisto/rpc"
|
||||
"github.com/elastic/gosigar"
|
||||
"github.com/rcrowley/go-metrics"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
memorySampleLimit = 200 // Maximum number of memory data samples
|
||||
trafficSampleLimit = 200 // Maximum number of traffic data samples
|
||||
activeMemorySampleLimit = 200 // Maximum number of active memory data samples
|
||||
virtualMemorySampleLimit = 200 // Maximum number of virtual memory data samples
|
||||
networkIngressSampleLimit = 200 // Maximum number of network ingress data samples
|
||||
networkEgressSampleLimit = 200 // Maximum number of network egress data samples
|
||||
processCPUSampleLimit = 200 // Maximum number of process cpu data samples
|
||||
systemCPUSampleLimit = 200 // Maximum number of system cpu data samples
|
||||
diskReadSampleLimit = 200 // Maximum number of disk read data samples
|
||||
diskWriteSampleLimit = 200 // Maximum number of disk write data samples
|
||||
)
|
||||
|
||||
var nextID uint32 // Next connection id
|
||||
|
|
@ -71,16 +79,35 @@ type client struct {
|
|||
|
||||
// New creates a new dashboard instance with the given configuration.
|
||||
func New(config *Config, commit string) (*Dashboard, error) {
|
||||
return &Dashboard{
|
||||
now := time.Now()
|
||||
db := &Dashboard{
|
||||
conns: make(map[uint32]*client),
|
||||
config: config,
|
||||
quit: make(chan chan error),
|
||||
charts: &HomeMessage{
|
||||
Memory: ChartEntries{},
|
||||
Traffic: ChartEntries{},
|
||||
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
|
||||
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
|
||||
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
|
||||
NetworkEgress: emptyChartEntries(now, networkEgressSampleLimit, config.Refresh),
|
||||
ProcessCPU: emptyChartEntries(now, processCPUSampleLimit, config.Refresh),
|
||||
SystemCPU: emptyChartEntries(now, systemCPUSampleLimit, config.Refresh),
|
||||
DiskRead: emptyChartEntries(now, diskReadSampleLimit, config.Refresh),
|
||||
DiskWrite: emptyChartEntries(now, diskWriteSampleLimit, config.Refresh),
|
||||
},
|
||||
commit: commit,
|
||||
}, nil
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// emptyChartEntries returns a ChartEntry array containing limit number of empty samples.
|
||||
func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntries {
|
||||
ce := make(ChartEntries, limit)
|
||||
for i := 0; i < limit; i++ {
|
||||
ce[i] = &ChartEntry{
|
||||
Time: t.Add(-time.Duration(i) * refresh),
|
||||
}
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
// Protocols is a meaningless implementation of node.Service.
|
||||
|
|
@ -215,8 +242,14 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
|||
Commit: db.commit,
|
||||
},
|
||||
Home: &HomeMessage{
|
||||
Memory: db.charts.Memory,
|
||||
Traffic: db.charts.Traffic,
|
||||
ActiveMemory: db.charts.ActiveMemory,
|
||||
VirtualMemory: db.charts.VirtualMemory,
|
||||
NetworkIngress: db.charts.NetworkIngress,
|
||||
NetworkEgress: db.charts.NetworkEgress,
|
||||
ProcessCPU: db.charts.ProcessCPU,
|
||||
SystemCPU: db.charts.SystemCPU,
|
||||
DiskRead: db.charts.DiskRead,
|
||||
DiskWrite: db.charts.DiskWrite,
|
||||
},
|
||||
}
|
||||
// Start tracking the connection and drop at connection loss.
|
||||
|
|
@ -241,6 +274,19 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
|||
// collectData collects the required data to plot on the dashboard.
|
||||
func (db *Dashboard) collectData() {
|
||||
defer db.wg.Done()
|
||||
systemCPUUsage := gosigar.Cpu{}
|
||||
systemCPUUsage.Get()
|
||||
var (
|
||||
prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count()
|
||||
prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
|
||||
prevProcessCPUTime = getProcessCPUTime()
|
||||
prevSystemCPUUsage = systemCPUUsage
|
||||
prevDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/input").(metrics.Meter).Count()
|
||||
prevDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/output").(metrics.Meter).Count()
|
||||
|
||||
frequency = float64(db.config.Refresh / time.Second)
|
||||
numCPU = float64(runtime.NumCPU())
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
|
@ -248,32 +294,84 @@ func (db *Dashboard) collectData() {
|
|||
errc <- nil
|
||||
return
|
||||
case <-time.After(db.config.Refresh):
|
||||
inboundTraffic := metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Rate1()
|
||||
memoryInUse := metrics.DefaultRegistry.Get("system/memory/inuse").(metrics.Meter).Rate1()
|
||||
systemCPUUsage.Get()
|
||||
var (
|
||||
curNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count()
|
||||
curNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
|
||||
curProcessCPUTime = getProcessCPUTime()
|
||||
curSystemCPUUsage = systemCPUUsage
|
||||
curDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/input").(metrics.Meter).Count()
|
||||
curDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/output").(metrics.Meter).Count()
|
||||
|
||||
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
|
||||
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
|
||||
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
|
||||
deltaSystemCPUUsage = systemCPUUsage.Delta(prevSystemCPUUsage)
|
||||
deltaDiskRead = curDiskRead - prevDiskRead
|
||||
deltaDiskWrite = curDiskWrite - prevDiskWrite
|
||||
)
|
||||
prevNetworkIngress = curNetworkIngress
|
||||
prevNetworkEgress = curNetworkEgress
|
||||
prevProcessCPUTime = curProcessCPUTime
|
||||
prevSystemCPUUsage = curSystemCPUUsage
|
||||
prevDiskRead = curDiskRead
|
||||
prevDiskWrite = curDiskWrite
|
||||
|
||||
now := time.Now()
|
||||
memory := &ChartEntry{
|
||||
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
activeMemory := &ChartEntry{
|
||||
Time: now,
|
||||
Value: memoryInUse,
|
||||
Value: float64(mem.Alloc) / frequency,
|
||||
}
|
||||
traffic := &ChartEntry{
|
||||
virtualMemory := &ChartEntry{
|
||||
Time: now,
|
||||
Value: inboundTraffic,
|
||||
Value: float64(mem.Sys) / frequency,
|
||||
}
|
||||
first := 0
|
||||
if len(db.charts.Memory) == memorySampleLimit {
|
||||
first = 1
|
||||
networkIngress := &ChartEntry{
|
||||
Time: now,
|
||||
Value: deltaNetworkIngress / frequency,
|
||||
}
|
||||
db.charts.Memory = append(db.charts.Memory[first:], memory)
|
||||
first = 0
|
||||
if len(db.charts.Traffic) == trafficSampleLimit {
|
||||
first = 1
|
||||
networkEgress := &ChartEntry{
|
||||
Time: now,
|
||||
Value: deltaNetworkEgress / frequency,
|
||||
}
|
||||
db.charts.Traffic = append(db.charts.Traffic[first:], traffic)
|
||||
processCPU := &ChartEntry{
|
||||
Time: now,
|
||||
Value: deltaProcessCPUTime / frequency / numCPU * 100,
|
||||
}
|
||||
systemCPU := &ChartEntry{
|
||||
Time: now,
|
||||
Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU,
|
||||
}
|
||||
diskRead := &ChartEntry{
|
||||
Time: now,
|
||||
Value: float64(deltaDiskRead) / frequency,
|
||||
}
|
||||
diskWrite := &ChartEntry{
|
||||
Time: now,
|
||||
Value: float64(deltaDiskWrite) / frequency,
|
||||
}
|
||||
db.charts.ActiveMemory = append(db.charts.ActiveMemory[1:], activeMemory)
|
||||
db.charts.VirtualMemory = append(db.charts.VirtualMemory[1:], virtualMemory)
|
||||
db.charts.NetworkIngress = append(db.charts.NetworkIngress[1:], networkIngress)
|
||||
db.charts.NetworkEgress = append(db.charts.NetworkEgress[1:], networkEgress)
|
||||
db.charts.ProcessCPU = append(db.charts.ProcessCPU[1:], processCPU)
|
||||
db.charts.SystemCPU = append(db.charts.SystemCPU[1:], systemCPU)
|
||||
db.charts.DiskRead = append(db.charts.DiskRead[1:], diskRead)
|
||||
db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite)
|
||||
|
||||
db.sendToAll(&Message{
|
||||
Home: &HomeMessage{
|
||||
Memory: ChartEntries{memory},
|
||||
Traffic: ChartEntries{traffic},
|
||||
ActiveMemory: ChartEntries{activeMemory},
|
||||
VirtualMemory: ChartEntries{virtualMemory},
|
||||
NetworkIngress: ChartEntries{networkIngress},
|
||||
NetworkEgress: ChartEntries{networkEgress},
|
||||
ProcessCPU: ChartEntries{processCPU},
|
||||
SystemCPU: ChartEntries{systemCPU},
|
||||
DiskRead: ChartEntries{diskRead},
|
||||
DiskWrite: ChartEntries{diskWrite},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,14 @@ type GeneralMessage struct {
|
|||
}
|
||||
|
||||
type HomeMessage struct {
|
||||
Memory ChartEntries `json:"memory,omitempty"`
|
||||
Traffic ChartEntries `json:"traffic,omitempty"`
|
||||
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
|
||||
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
|
||||
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
|
||||
NetworkEgress ChartEntries `json:"networkEgress,omitempty"`
|
||||
ProcessCPU ChartEntries `json:"processCPU,omitempty"`
|
||||
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
|
||||
DiskRead ChartEntries `json:"diskRead,omitempty"`
|
||||
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
|
||||
}
|
||||
|
||||
type ChartEntries []*ChartEntry
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
ethereum "github.com/EthereumCommonwealth/go-callisto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
|
|
@ -240,7 +241,7 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
|||
matchedLogs = make(chan []*types.Log)
|
||||
)
|
||||
|
||||
logsSub, err := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), matchedLogs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -267,6 +268,8 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
|||
}
|
||||
|
||||
// FilterCriteria represents a request to create a new filter.
|
||||
//
|
||||
// TODO(karalabe): Kill this in favor of ethereum.FilterQuery.
|
||||
type FilterCriteria struct {
|
||||
FromBlock *big.Int
|
||||
ToBlock *big.Int
|
||||
|
|
@ -289,7 +292,7 @@ type FilterCriteria struct {
|
|||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
|
||||
logs := make(chan []*types.Log)
|
||||
logsSub, err := api.events.SubscribeLogs(crit, logs)
|
||||
logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs)
|
||||
if err != nil {
|
||||
return rpc.ID(""), err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
ethereum "github.com/EthereumCommonwealth/go-callisto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||
|
|
@ -75,7 +76,7 @@ type subscription struct {
|
|||
id rpc.ID
|
||||
typ Type
|
||||
created time.Time
|
||||
logsCrit FilterCriteria
|
||||
logsCrit ethereum.FilterQuery
|
||||
logs chan []*types.Log
|
||||
hashes chan common.Hash
|
||||
headers chan *types.Header
|
||||
|
|
@ -162,7 +163,7 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription {
|
|||
// SubscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel. Default value for the from and to
|
||||
// block is "latest". If the fromBlock > toBlock an error is returned.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*types.Log) (*Subscription, error) {
|
||||
func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) (*Subscription, error) {
|
||||
var from, to rpc.BlockNumber
|
||||
if crit.FromBlock == nil {
|
||||
from = rpc.LatestBlockNumber
|
||||
|
|
@ -200,7 +201,7 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*types.Log
|
|||
|
||||
// subscribeMinedPendingLogs creates a subscription that returned mined and
|
||||
// pending logs that match the given criteria.
|
||||
func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
|
||||
func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: MinedAndPendingLogsSubscription,
|
||||
|
|
@ -217,7 +218,7 @@ func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan
|
|||
|
||||
// subscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel.
|
||||
func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
|
||||
func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: LogsSubscription,
|
||||
|
|
@ -234,7 +235,7 @@ func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*types.Log
|
|||
|
||||
// subscribePendingLogs creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
|
||||
func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingLogsSubscription,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
ethereum "github.com/EthereumCommonwealth/go-callisto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||
"github.com/EthereumCommonwealth/go-callisto/consensus/ethash"
|
||||
"github.com/EthereumCommonwealth/go-callisto/core"
|
||||
|
|
@ -488,27 +489,27 @@ func TestPendingLogsSubscription(t *testing.T) {
|
|||
}
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
crit ethereum.FilterQuery
|
||||
expected []*types.Log
|
||||
c chan []*types.Log
|
||||
sub *Subscription
|
||||
}{
|
||||
// match all
|
||||
{FilterCriteria{}, convertLogs(allLogs), nil, nil},
|
||||
{ethereum.FilterQuery{}, convertLogs(allLogs), nil, nil},
|
||||
// match none due to no matching addresses
|
||||
{FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, nil, nil},
|
||||
// match logs based on addresses, ignore topics
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
|
||||
// match none due to no matching topics (match with address)
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil},
|
||||
// match logs based on addresses and topics
|
||||
{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
|
||||
// match logs based on multiple addresses and "or" topics
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
|
||||
// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
|
||||
// multiple pending logs, should match only 2 topics from the logs in block 5
|
||||
{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
|
||||
{ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -333,28 +333,19 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
|
|||
return fetchKeystore(s.am).Lock(addr) == nil
|
||||
}
|
||||
|
||||
// SendTransaction will create a transaction from the given arguments and
|
||||
// tries to sign it with the key associated with args.To. If the given passwd isn't
|
||||
// able to decrypt the key it fails.
|
||||
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
|
||||
// signTransactions sets defaults and signs the given transation
|
||||
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
|
||||
// and release it after the transaction has been submitted to the tx pool
|
||||
func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
|
||||
// Look up the wallet containing the requested signer
|
||||
account := accounts.Account{Address: args.From}
|
||||
|
||||
wallet, err := s.am.Find(account)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if args.Nonce == nil {
|
||||
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||
// the same nonce to multiple accounts.
|
||||
s.nonceLock.LockAddr(args.From)
|
||||
defer s.nonceLock.UnlockAddr(args.From)
|
||||
}
|
||||
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
return common.Hash{}, err
|
||||
return nil, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
tx := args.toTransaction()
|
||||
|
|
@ -363,13 +354,53 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
|||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||
chainID = config.ChainId
|
||||
}
|
||||
signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
|
||||
return wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
|
||||
}
|
||||
|
||||
// SendTransaction will create a transaction from the given arguments and
|
||||
// tries to sign it with the key associated with args.To. If the given passwd isn't
|
||||
// able to decrypt the key it fails.
|
||||
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
|
||||
if args.Nonce == nil {
|
||||
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||
// the same nonce to multiple accounts.
|
||||
s.nonceLock.LockAddr(args.From)
|
||||
defer s.nonceLock.UnlockAddr(args.From)
|
||||
}
|
||||
signed, err := s.signTransaction(ctx, args, passwd)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return submitTransaction(ctx, s.b, signed)
|
||||
}
|
||||
|
||||
// SignTransaction will create a transaction from the given arguments and
|
||||
// tries to sign it with the key associated with args.To. If the given passwd isn't
|
||||
// able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast
|
||||
// to other nodes
|
||||
func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs, passwd string) (*SignTransactionResult, error) {
|
||||
// No need to obtain the noncelock mutex, since we won't be sending this
|
||||
// tx into the transaction pool, but right back to the user
|
||||
if args.Gas == nil {
|
||||
return nil, fmt.Errorf("gas not specified")
|
||||
}
|
||||
if args.GasPrice == nil {
|
||||
return nil, fmt.Errorf("gasPrice not specified")
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
return nil, fmt.Errorf("nonce not specified")
|
||||
}
|
||||
signed, err := s.signTransaction(ctx, args, passwd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(signed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SignTransactionResult{data, signed}, nil
|
||||
}
|
||||
|
||||
// signHash is a helper function that calculates a hash for the given message that can be
|
||||
// safely used to calculate a signature from.
|
||||
//
|
||||
|
|
@ -1223,11 +1254,14 @@ type SignTransactionResult struct {
|
|||
// The node needs to have the private key of the account corresponding with
|
||||
// the given from address and it needs to be unlocked.
|
||||
func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) {
|
||||
if args.Gas == nil {
|
||||
return nil, fmt.Errorf("gas not specified")
|
||||
}
|
||||
if args.GasPrice == nil {
|
||||
return nil, fmt.Errorf("gasPrice not specified")
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||
// the same nonce to multiple accounts.
|
||||
s.nonceLock.LockAddr(args.From)
|
||||
defer s.nonceLock.UnlockAddr(args.From)
|
||||
return nil, fmt.Errorf("nonce not specified")
|
||||
}
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -517,6 +517,12 @@ web3._extend({
|
|||
call: 'personal_deriveAccount',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'personal_signTransaction',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
|
||||
}),
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
|
|
|
|||
|
|
@ -221,9 +221,8 @@ func (s *LightEthereum) Start(srvr *p2p.Server) error {
|
|||
s.startBloomHandlers()
|
||||
log.Warn("Light client mode is an experimental feature")
|
||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
|
||||
// search the topic belonging to the oldest supported protocol because
|
||||
// servers always advertise all supported protocols
|
||||
protocolVersion := ClientProtocolVersions[len(ClientProtocolVersions)-1]
|
||||
// clients are searching for the first advertised protocol in the list
|
||||
protocolVersion := AdvertiseProtocolVersions[0]
|
||||
s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion))
|
||||
s.protocolManager.Start()
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1014,7 +1014,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
for i, stat := range stats {
|
||||
if stat.Status == core.TxStatusUnknown {
|
||||
if errs := pm.txpool.AddRemotes([]*types.Transaction{req.Txs[i]}); errs[0] != nil {
|
||||
stats[i].Error = errs[0]
|
||||
stats[i].Error = errs[0].Error()
|
||||
continue
|
||||
}
|
||||
stats[i] = pm.txStatus([]common.Hash{hashes[i]})[0]
|
||||
|
|
@ -1055,7 +1055,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
p.Log().Trace("Received tx status response")
|
||||
var resp struct {
|
||||
ReqID, BV uint64
|
||||
Status []core.TxStatus
|
||||
Status []txStatus
|
||||
}
|
||||
if err := msg.Decode(&resp); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ func TestTransactionStatusLes2(t *testing.T) {
|
|||
|
||||
// test error status by sending an underpriced transaction
|
||||
tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced})
|
||||
test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
|
||||
|
||||
tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
|
||||
test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ const (
|
|||
|
||||
// Supported versions of the les protocol (first is primary)
|
||||
var (
|
||||
ClientProtocolVersions = []uint{lpv2, lpv1}
|
||||
ServerProtocolVersions = []uint{lpv2, lpv1}
|
||||
ClientProtocolVersions = []uint{lpv2, lpv1}
|
||||
ServerProtocolVersions = []uint{lpv2, lpv1}
|
||||
AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list
|
||||
)
|
||||
|
||||
// Number of implemented message corresponding to different protocol versions.
|
||||
|
|
@ -223,6 +224,6 @@ type proofsData [][]rlp.RawValue
|
|||
|
||||
type txStatus struct {
|
||||
Status core.TxStatus
|
||||
Lookup *core.TxLookupEntry
|
||||
Error error
|
||||
Lookup *core.TxLookupEntry `rlp:"nil"`
|
||||
Error string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
lesTopics := make([]discv5.Topic, len(ServerProtocolVersions))
|
||||
for i, pv := range ServerProtocolVersions {
|
||||
lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
|
||||
for i, pv := range AdvertiseProtocolVersions {
|
||||
lesTopics[i] = lesTopic(eth.BlockChain().Genesis().Hash(), pv)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
|||
return err
|
||||
}
|
||||
i, err := self.hc.InsertHeaderChain(chain, whFunc, start)
|
||||
go self.postChainEvents(events)
|
||||
self.postChainEvents(events)
|
||||
return i, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,18 +53,18 @@ type trustedCheckpoint struct {
|
|||
var (
|
||||
mainnetCheckpoint = trustedCheckpoint{
|
||||
name: "ETH mainnet",
|
||||
sectionIdx: 129,
|
||||
sectionHead: common.HexToHash("64100587c8ec9a76870056d07cb0f58622552d16de6253a59cac4b580c899501"),
|
||||
chtRoot: common.HexToHash("bb4fb4076cbe6923c8a8ce8f158452bbe19564959313466989fda095a60884ca"),
|
||||
bloomTrieRoot: common.HexToHash("0db524b2c4a2a9520a42fd842b02d2e8fb58ff37c75cf57bd0eb82daeace6716"),
|
||||
sectionIdx: 150,
|
||||
sectionHead: common.HexToHash("1e2e67f289565cbe7bd4367f7960dbd73a3f7c53439e1047cd7ba331c8109e39"),
|
||||
chtRoot: common.HexToHash("f2a6c9ca143d647b44523cc249f1072c8912358ab873a77a5fdc792b8df99e80"),
|
||||
bloomTrieRoot: common.HexToHash("c018952fa1513c97857e79fbb9a37acaf8432d5b85e52a78eca7dff5fd5900ee"),
|
||||
}
|
||||
|
||||
ropstenCheckpoint = trustedCheckpoint{
|
||||
name: "Ropsten testnet",
|
||||
sectionIdx: 50,
|
||||
sectionHead: common.HexToHash("00bd65923a1aa67f85e6b4ae67835784dd54be165c37f056691723c55bf016bd"),
|
||||
chtRoot: common.HexToHash("6f56dc61936752cc1f8c84b4addabdbe6a1c19693de3f21cb818362df2117f03"),
|
||||
bloomTrieRoot: common.HexToHash("aca7d7c504d22737242effc3fdc604a762a0af9ced898036b5986c3a15220208"),
|
||||
sectionIdx: 75,
|
||||
sectionHead: common.HexToHash("12e68324f4578ea3e8e7fb3968167686729396c9279287fa1f1a8b51bb2d05b4"),
|
||||
chtRoot: common.HexToHash("3e51dc095c69fa654a4cac766e0afff7357515b4b3c3a379c675f810363e54be"),
|
||||
bloomTrieRoot: common.HexToHash("33e3a70b33c1d73aa698d496a80615e98ed31fa8f56969876180553b32333339"),
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class AndroidTest extends InstrumentationTestCase {
|
|||
|
||||
Transaction tx = new Transaction(
|
||||
1, new Address("0x0000000000000000000000000000000000000000"),
|
||||
new BigInt(0), new BigInt(0), new BigInt(1), null); // Random empty transaction
|
||||
new BigInt(0), 0, new BigInt(1), null); // Random empty transaction
|
||||
BigInt chain = new BigInt(1); // Chain identifier of the main net
|
||||
|
||||
// Sign a transaction with a single authorization
|
||||
|
|
@ -164,12 +164,17 @@ func TestAndroid(t *testing.T) {
|
|||
t.Skip("command gradle not found, skipping")
|
||||
}
|
||||
if sdk := os.Getenv("ANDROID_HOME"); sdk == "" {
|
||||
t.Skip("ANDROID_HOME environment var not set, skipping")
|
||||
// Android SDK not explicitly given, try to auto-resolve
|
||||
autopath := filepath.Join(os.Getenv("HOME"), "Android", "Sdk")
|
||||
if _, err := os.Stat(autopath); err != nil {
|
||||
t.Skip("ANDROID_HOME environment var not set, skipping")
|
||||
}
|
||||
os.Setenv("ANDROID_HOME", autopath)
|
||||
}
|
||||
if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil {
|
||||
t.Log("gomobile missing, installing it...")
|
||||
if _, err := exec.Command("go", "install", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
|
||||
t.Fatalf("install failed: %v", err)
|
||||
if out, err := exec.Command("go", "get", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
|
||||
t.Fatalf("install failed: %v\n%s", err, string(out))
|
||||
}
|
||||
t.Log("initializing gomobile...")
|
||||
start := time.Now()
|
||||
|
|
@ -239,7 +244,7 @@ const gradleConfig = `buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
}
|
||||
}
|
||||
allprojects {
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func BindContract(address *Address, abiJSON string, client *EthereumClient) (con
|
|||
return nil, err
|
||||
}
|
||||
return &BoundContract{
|
||||
contract: bind.NewBoundContract(address.address, parsed, client.client, client.client),
|
||||
contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client),
|
||||
address: address.address,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
|||
P2P: p2p.Config{
|
||||
NoDiscovery: true,
|
||||
DiscoveryV5: true,
|
||||
DiscoveryV5Addr: ":0",
|
||||
BootstrapNodesV5: config.BootstrapNodes.nodes,
|
||||
ListenAddr: ":0",
|
||||
NAT: nat.Any(),
|
||||
|
|
|
|||
|
|
@ -41,10 +41,9 @@ var DefaultConfig = Config{
|
|||
WSPort: DefaultWSPort,
|
||||
WSModules: []string{"net", "web3"},
|
||||
P2P: p2p.Config{
|
||||
ListenAddr: ":30303",
|
||||
DiscoveryV5Addr: ":30304",
|
||||
MaxPeers: 25,
|
||||
NAT: nat.Any(),
|
||||
ListenAddr: ":30303",
|
||||
MaxPeers: 25,
|
||||
NAT: nat.Any(),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,17 +210,15 @@ type reply struct {
|
|||
matched chan<- bool
|
||||
}
|
||||
|
||||
// ReadPacket is sent to the unhandled channel when it could not be processed
|
||||
type ReadPacket struct {
|
||||
Data []byte
|
||||
Addr *net.UDPAddr
|
||||
}
|
||||
|
||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) {
|
||||
addr, err := net.ResolveUDPAddr("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tab, _, err := newUDP(priv, conn, natm, nodeDBPath, netrestrict)
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, conn conn, realaddr *net.UDPAddr, unhandled chan ReadPacket, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) {
|
||||
tab, _, err := newUDP(priv, conn, realaddr, unhandled, nodeDBPath, netrestrict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -228,7 +226,7 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
|
|||
return tab, nil
|
||||
}
|
||||
|
||||
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) {
|
||||
func newUDP(priv *ecdsa.PrivateKey, c conn, realaddr *net.UDPAddr, unhandled chan ReadPacket, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) {
|
||||
udp := &udp{
|
||||
conn: c,
|
||||
priv: priv,
|
||||
|
|
@ -237,16 +235,6 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
|
|||
gotreply: make(chan reply),
|
||||
addpending: make(chan *pending),
|
||||
}
|
||||
realaddr := c.LocalAddr().(*net.UDPAddr)
|
||||
if natm != nil {
|
||||
if !realaddr.IP.IsLoopback() {
|
||||
go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||
}
|
||||
// TODO: react to external IP changes over time.
|
||||
if ext, err := natm.ExternalIP(); err == nil {
|
||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||
}
|
||||
}
|
||||
// TODO: separate TCP port
|
||||
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
|
||||
tab, err := newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath)
|
||||
|
|
@ -256,7 +244,7 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
|
|||
udp.Table = tab
|
||||
|
||||
go udp.loop()
|
||||
go udp.readLoop()
|
||||
go udp.readLoop(unhandled)
|
||||
return udp.Table, udp, nil
|
||||
}
|
||||
|
||||
|
|
@ -492,8 +480,11 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte,
|
|||
}
|
||||
|
||||
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
||||
func (t *udp) readLoop() {
|
||||
func (t *udp) readLoop(unhandled chan ReadPacket) {
|
||||
defer t.conn.Close()
|
||||
if unhandled != nil {
|
||||
defer close(unhandled)
|
||||
}
|
||||
// Discovery packets are defined to be no larger than 1280 bytes.
|
||||
// Packets larger than this size will be cut at the end and treated
|
||||
// as invalid because their hash won't match.
|
||||
|
|
@ -509,7 +500,12 @@ func (t *udp) readLoop() {
|
|||
log.Debug("UDP read error", "err", err)
|
||||
return
|
||||
}
|
||||
t.handlePacket(from, buf[:nbytes])
|
||||
if t.handlePacket(from, buf[:nbytes]) != nil && unhandled != nil {
|
||||
select {
|
||||
case unhandled <- ReadPacket{buf[:nbytes], from}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ func newUDPTest(t *testing.T) *udpTest {
|
|||
remotekey: newkey(),
|
||||
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
|
||||
}
|
||||
test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "", nil)
|
||||
realaddr := test.pipe.LocalAddr().(*net.UDPAddr)
|
||||
test.table, test.udp, _ = newUDP(test.localkey, test.pipe, realaddr, nil, "", nil)
|
||||
return test
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import (
|
|||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||
"github.com/EthereumCommonwealth/go-callisto/crypto/sha3"
|
||||
"github.com/EthereumCommonwealth/go-callisto/log"
|
||||
"github.com/EthereumCommonwealth/go-callisto/p2p/nat"
|
||||
"github.com/EthereumCommonwealth/go-callisto/p2p/netutil"
|
||||
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
||||
)
|
||||
|
|
@ -134,7 +133,7 @@ type timeoutEvent struct {
|
|||
node *Node
|
||||
}
|
||||
|
||||
func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, dbPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
ourID := PubkeyID(&ourPubkey)
|
||||
|
||||
var db *nodeDB
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
|
||||
func TestNetwork_Lookup(t *testing.T) {
|
||||
key, _ := crypto.GenerateKey()
|
||||
network, err := newNetwork(lookupTestnet, key.PublicKey, nil, "", nil)
|
||||
network, err := newNetwork(lookupTestnet, key.PublicKey, "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ func (s *simulation) launchNode(log bool) *Network {
|
|||
addr := &net.UDPAddr{IP: ip, Port: 30303}
|
||||
|
||||
transport := &simTransport{joinTime: time.Now(), sender: id, senderAddr: addr, sim: s, priv: key}
|
||||
net, err := newNetwork(transport, key.PublicKey, nil, "<no database>", nil)
|
||||
net, err := newNetwork(transport, key.PublicKey, "<no database>", nil)
|
||||
if err != nil {
|
||||
panic("cannot launch new node: " + err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ func (s *ticketStore) nextFilteredTicket() (*ticketRef, time.Duration) {
|
|||
|
||||
regTime := now + mclock.AbsTime(wait)
|
||||
topic := ticket.t.topics[ticket.idx]
|
||||
if regTime >= s.tickets[topic].nextReg {
|
||||
if s.tickets[topic] != nil && regTime >= s.tickets[topic].nextReg {
|
||||
return ticket, wait
|
||||
}
|
||||
s.removeTicketRef(*ticket)
|
||||
|
|
@ -642,7 +642,7 @@ func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNod
|
|||
if ip.IsUnspecified() || ip.IsLoopback() {
|
||||
ip = from.IP
|
||||
}
|
||||
n := NewNode(node.ID, ip, node.UDP-1, node.TCP-1) // subtract one from port while discv5 is running in test mode on UDPport+1
|
||||
n := NewNode(node.ID, ip, node.UDP, node.TCP)
|
||||
select {
|
||||
case chn <- n:
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const Version = 4
|
|||
// Errors
|
||||
var (
|
||||
errPacketTooSmall = errors.New("too small")
|
||||
errBadHash = errors.New("bad hash")
|
||||
errBadPrefix = errors.New("bad prefix")
|
||||
errExpired = errors.New("expired")
|
||||
errUnsolicitedReply = errors.New("unsolicited reply")
|
||||
errUnknownNode = errors.New("unknown node")
|
||||
|
|
@ -145,10 +145,11 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
const (
|
||||
macSize = 256 / 8
|
||||
sigSize = 520 / 8
|
||||
headSize = macSize + sigSize // space of packet frame data
|
||||
var (
|
||||
versionPrefix = []byte("temporary discovery v5")
|
||||
versionPrefixSize = len(versionPrefix)
|
||||
sigSize = 520 / 8
|
||||
headSize = versionPrefixSize + sigSize // space of packet frame data
|
||||
)
|
||||
|
||||
// Neighbors replies are sent across multiple packets to
|
||||
|
|
@ -237,30 +238,23 @@ type udp struct {
|
|||
}
|
||||
|
||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
transport, err := listenUDP(priv, laddr)
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, conn conn, realaddr *net.UDPAddr, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
transport, err := listenUDP(priv, conn, realaddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath, netrestrict)
|
||||
net, err := newNetwork(transport, priv.PublicKey, nodeDBPath, netrestrict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("UDP listener up", "net", net.tab.self)
|
||||
transport.net = net
|
||||
go transport.readLoop()
|
||||
return net, nil
|
||||
}
|
||||
|
||||
func listenUDP(priv *ecdsa.PrivateKey, laddr string) (*udp, error) {
|
||||
addr, err := net.ResolveUDPAddr("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(addr, uint16(addr.Port))}, nil
|
||||
func listenUDP(priv *ecdsa.PrivateKey, conn conn, realaddr *net.UDPAddr) (*udp, error) {
|
||||
return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(realaddr, uint16(realaddr.Port))}, nil
|
||||
}
|
||||
|
||||
func (t *udp) localAddr() *net.UDPAddr {
|
||||
|
|
@ -372,11 +366,9 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (p, hash
|
|||
log.Error(fmt.Sprint("could not sign packet:", err))
|
||||
return nil, nil, err
|
||||
}
|
||||
copy(packet[macSize:], sig)
|
||||
// add the hash to the front. Note: this doesn't protect the
|
||||
// packet in any way.
|
||||
hash = crypto.Keccak256(packet[macSize:])
|
||||
copy(packet, hash)
|
||||
copy(packet, versionPrefix)
|
||||
copy(packet[versionPrefixSize:], sig)
|
||||
hash = crypto.Keccak256(packet[versionPrefixSize:])
|
||||
return packet, hash, nil
|
||||
}
|
||||
|
||||
|
|
@ -420,17 +412,16 @@ func decodePacket(buffer []byte, pkt *ingressPacket) error {
|
|||
}
|
||||
buf := make([]byte, len(buffer))
|
||||
copy(buf, buffer)
|
||||
hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
|
||||
shouldhash := crypto.Keccak256(buf[macSize:])
|
||||
if !bytes.Equal(hash, shouldhash) {
|
||||
return errBadHash
|
||||
prefix, sig, sigdata := buf[:versionPrefixSize], buf[versionPrefixSize:headSize], buf[headSize:]
|
||||
if !bytes.Equal(prefix, versionPrefix) {
|
||||
return errBadPrefix
|
||||
}
|
||||
fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkt.rawData = buf
|
||||
pkt.hash = hash
|
||||
pkt.hash = crypto.Keccak256(buf[versionPrefixSize:])
|
||||
pkt.remoteID = fromID
|
||||
switch pkt.ev = nodeEvent(sigdata[0]); pkt.ev {
|
||||
case pingPacket:
|
||||
|
|
|
|||
|
|
@ -78,9 +78,6 @@ type Config struct {
|
|||
// protocol should be started or not.
|
||||
DiscoveryV5 bool `toml:",omitempty"`
|
||||
|
||||
// Listener address for the V5 discovery protocol UDP traffic.
|
||||
DiscoveryV5Addr string `toml:",omitempty"`
|
||||
|
||||
// Name sets the node name of this server.
|
||||
// Use common.MakeName to create a name that follows existing conventions.
|
||||
Name string `toml:"-"`
|
||||
|
|
@ -354,6 +351,32 @@ func (srv *Server) Stop() {
|
|||
srv.loopWG.Wait()
|
||||
}
|
||||
|
||||
// sharedUDPConn implements a shared connection. Write sends messages to the underlying connection while read returns
|
||||
// messages that were found unprocessable and sent to the unhandled channel by the primary listener.
|
||||
type sharedUDPConn struct {
|
||||
*net.UDPConn
|
||||
unhandled chan discover.ReadPacket
|
||||
}
|
||||
|
||||
// ReadFromUDP implements discv5.conn
|
||||
func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
packet, ok := <-s.unhandled
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("Connection was closed")
|
||||
}
|
||||
l := len(packet.Data)
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
copy(b[:l], packet.Data[:l])
|
||||
return l, packet.Addr, nil
|
||||
}
|
||||
|
||||
// Close implements discv5.conn
|
||||
func (s *sharedUDPConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts running the server.
|
||||
// Servers can not be re-used after stopping.
|
||||
func (srv *Server) Start() (err error) {
|
||||
|
|
@ -388,9 +411,43 @@ func (srv *Server) Start() (err error) {
|
|||
srv.peerOp = make(chan peerOpFunc)
|
||||
srv.peerOpDone = make(chan struct{})
|
||||
|
||||
var (
|
||||
conn *net.UDPConn
|
||||
sconn *sharedUDPConn
|
||||
realaddr *net.UDPAddr
|
||||
unhandled chan discover.ReadPacket
|
||||
)
|
||||
|
||||
if !srv.NoDiscovery || srv.DiscoveryV5 {
|
||||
addr, err := net.ResolveUDPAddr("udp", srv.ListenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err = net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
realaddr = conn.LocalAddr().(*net.UDPAddr)
|
||||
if srv.NAT != nil {
|
||||
if !realaddr.IP.IsLoopback() {
|
||||
go nat.Map(srv.NAT, srv.quit, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||
}
|
||||
// TODO: react to external IP changes over time.
|
||||
if ext, err := srv.NAT.ExternalIP(); err == nil {
|
||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !srv.NoDiscovery && srv.DiscoveryV5 {
|
||||
unhandled = make(chan discover.ReadPacket, 100)
|
||||
sconn = &sharedUDPConn{conn, unhandled}
|
||||
}
|
||||
|
||||
// node table
|
||||
if !srv.NoDiscovery {
|
||||
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict)
|
||||
ntab, err := discover.ListenUDP(srv.PrivateKey, conn, realaddr, unhandled, srv.NodeDatabase, srv.NetRestrict)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -401,7 +458,15 @@ func (srv *Server) Start() (err error) {
|
|||
}
|
||||
|
||||
if srv.DiscoveryV5 {
|
||||
ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "", srv.NetRestrict) //srv.NodeDatabase)
|
||||
var (
|
||||
ntab *discv5.Network
|
||||
err error
|
||||
)
|
||||
if sconn != nil {
|
||||
ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase)
|
||||
} else {
|
||||
ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,18 +45,10 @@ var RinkebyBootnodes = []string{
|
|||
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303", // AKASHA
|
||||
}
|
||||
|
||||
// RinkebyV5Bootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||
// Rinkeby test network for the experimental RLPx v5 topic-discovery network.
|
||||
var RinkebyV5Bootnodes = []string{
|
||||
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303?discport=30304", // IE
|
||||
"enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303?discport=30304", // INFURA
|
||||
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303?discport=30304", // AKASHA
|
||||
}
|
||||
|
||||
// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
|
||||
// experimental RLPx v5 topic-discovery network.
|
||||
var DiscoveryV5Bootnodes = []string{
|
||||
//"enode://ecaebb39dafa9c1d811c4a2544b21a1bf604325f74c568f36ac7a9a5bb91c1b74d3378b059169fb56586735ff8b03533ac1748bec1edd07ea278bb2c40565e4f@52.161.13.240:30303",
|
||||
//"enode://ecaebb39dafa9c1d811c4a2544b21a1bf604325f74c568f36ac7a9a5bb91c1b74d3378b059169fb56586735ff8b03533ac1748bec1edd07ea278bb2c40565e4f@52.161.13.240:30303",
|
||||
}
|
||||
|
||||
// CallistoTestnetBootnodes are the enode URLs of the P2P bootstrap nodes for the
|
||||
|
|
|
|||
12
rpc/http.go
12
rpc/http.go
|
|
@ -65,8 +65,9 @@ func (hc *httpConn) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DialHTTP creates a new RPC clients that connection to an RPC server over HTTP.
|
||||
func DialHTTP(endpoint string) (*Client, error) {
|
||||
// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
|
||||
// using the provided HTTP Client.
|
||||
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -76,10 +77,15 @@ func DialHTTP(endpoint string) (*Client, error) {
|
|||
|
||||
initctx := context.Background()
|
||||
return newClient(initctx, func(context.Context) (net.Conn, error) {
|
||||
return &httpConn{client: new(http.Client), req: req, closed: make(chan struct{})}, nil
|
||||
return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// DialHTTP creates a new RPC client that connects to an RPC server over HTTP.
|
||||
func DialHTTP(endpoint string) (*Client, error) {
|
||||
return DialHTTPWithClient(endpoint, new(http.Client))
|
||||
}
|
||||
|
||||
func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error {
|
||||
hc := c.writeConn.(*httpConn)
|
||||
respBody, err := hc.doRequest(ctx, msg)
|
||||
|
|
|
|||
20
vendor/github.com/StackExchange/wmi/LICENSE
generated
vendored
Normal file
20
vendor/github.com/StackExchange/wmi/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Stack Exchange
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
6
vendor/github.com/StackExchange/wmi/README.md
generated
vendored
Normal file
6
vendor/github.com/StackExchange/wmi/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
wmi
|
||||
===
|
||||
|
||||
Package wmi provides a WQL interface to Windows WMI.
|
||||
|
||||
Note: It interfaces with WMI on the local machine, therefore it only runs on Windows.
|
||||
260
vendor/github.com/StackExchange/wmi/swbemservices.go
generated
vendored
Normal file
260
vendor/github.com/StackExchange/wmi/swbemservices.go
generated
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// +build windows
|
||||
|
||||
package wmi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
)
|
||||
|
||||
// SWbemServices is used to access wmi. See https://msdn.microsoft.com/en-us/library/aa393719(v=vs.85).aspx
|
||||
type SWbemServices struct {
|
||||
//TODO: track namespace. Not sure if we can re connect to a different namespace using the same instance
|
||||
cWMIClient *Client //This could also be an embedded struct, but then we would need to branch on Client vs SWbemServices in the Query method
|
||||
sWbemLocatorIUnknown *ole.IUnknown
|
||||
sWbemLocatorIDispatch *ole.IDispatch
|
||||
queries chan *queryRequest
|
||||
closeError chan error
|
||||
lQueryorClose sync.Mutex
|
||||
}
|
||||
|
||||
type queryRequest struct {
|
||||
query string
|
||||
dst interface{}
|
||||
args []interface{}
|
||||
finished chan error
|
||||
}
|
||||
|
||||
// InitializeSWbemServices will return a new SWbemServices object that can be used to query WMI
|
||||
func InitializeSWbemServices(c *Client, connectServerArgs ...interface{}) (*SWbemServices, error) {
|
||||
//fmt.Println("InitializeSWbemServices: Starting")
|
||||
//TODO: implement connectServerArgs as optional argument for init with connectServer call
|
||||
s := new(SWbemServices)
|
||||
s.cWMIClient = c
|
||||
s.queries = make(chan *queryRequest)
|
||||
initError := make(chan error)
|
||||
go s.process(initError)
|
||||
|
||||
err, ok := <-initError
|
||||
if ok {
|
||||
return nil, err //Send error to caller
|
||||
}
|
||||
//fmt.Println("InitializeSWbemServices: Finished")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Close will clear and release all of the SWbemServices resources
|
||||
func (s *SWbemServices) Close() error {
|
||||
s.lQueryorClose.Lock()
|
||||
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||
s.lQueryorClose.Unlock()
|
||||
return fmt.Errorf("SWbemServices is not Initialized")
|
||||
}
|
||||
if s.queries == nil {
|
||||
s.lQueryorClose.Unlock()
|
||||
return fmt.Errorf("SWbemServices has been closed")
|
||||
}
|
||||
//fmt.Println("Close: sending close request")
|
||||
var result error
|
||||
ce := make(chan error)
|
||||
s.closeError = ce //Race condition if multiple callers to close. May need to lock here
|
||||
close(s.queries) //Tell background to shut things down
|
||||
s.lQueryorClose.Unlock()
|
||||
err, ok := <-ce
|
||||
if ok {
|
||||
result = err
|
||||
}
|
||||
//fmt.Println("Close: finished")
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SWbemServices) process(initError chan error) {
|
||||
//fmt.Println("process: starting background thread initialization")
|
||||
//All OLE/WMI calls must happen on the same initialized thead, so lock this goroutine
|
||||
runtime.LockOSThread()
|
||||
defer runtime.LockOSThread()
|
||||
|
||||
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
|
||||
if err != nil {
|
||||
oleCode := err.(*ole.OleError).Code()
|
||||
if oleCode != ole.S_OK && oleCode != S_FALSE {
|
||||
initError <- fmt.Errorf("ole.CoInitializeEx error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer ole.CoUninitialize()
|
||||
|
||||
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
|
||||
if err != nil {
|
||||
initError <- fmt.Errorf("CreateObject SWbemLocator error: %v", err)
|
||||
return
|
||||
} else if unknown == nil {
|
||||
initError <- ErrNilCreateObject
|
||||
return
|
||||
}
|
||||
defer unknown.Release()
|
||||
s.sWbemLocatorIUnknown = unknown
|
||||
|
||||
dispatch, err := s.sWbemLocatorIUnknown.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
initError <- fmt.Errorf("SWbemLocator QueryInterface error: %v", err)
|
||||
return
|
||||
}
|
||||
defer dispatch.Release()
|
||||
s.sWbemLocatorIDispatch = dispatch
|
||||
|
||||
// we can't do the ConnectServer call outside the loop unless we find a way to track and re-init the connectServerArgs
|
||||
//fmt.Println("process: initialized. closing initError")
|
||||
close(initError)
|
||||
//fmt.Println("process: waiting for queries")
|
||||
for q := range s.queries {
|
||||
//fmt.Printf("process: new query: len(query)=%d\n", len(q.query))
|
||||
errQuery := s.queryBackground(q)
|
||||
//fmt.Println("process: s.queryBackground finished")
|
||||
if errQuery != nil {
|
||||
q.finished <- errQuery
|
||||
}
|
||||
close(q.finished)
|
||||
}
|
||||
//fmt.Println("process: queries channel closed")
|
||||
s.queries = nil //set channel to nil so we know it is closed
|
||||
//TODO: I think the Release/Clear calls can panic if things are in a bad state.
|
||||
//TODO: May need to recover from panics and send error to method caller instead.
|
||||
close(s.closeError)
|
||||
}
|
||||
|
||||
// Query runs the WQL query using a SWbemServices instance and appends the values to dst.
|
||||
//
|
||||
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
|
||||
// the query must have the same name in dst. Supported types are all signed and
|
||||
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
|
||||
// Array types are not supported.
|
||||
//
|
||||
// By default, the local machine and default namespace are used. These can be
|
||||
// changed using connectServerArgs. See
|
||||
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
|
||||
func (s *SWbemServices) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
s.lQueryorClose.Lock()
|
||||
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||
s.lQueryorClose.Unlock()
|
||||
return fmt.Errorf("SWbemServices is not Initialized")
|
||||
}
|
||||
if s.queries == nil {
|
||||
s.lQueryorClose.Unlock()
|
||||
return fmt.Errorf("SWbemServices has been closed")
|
||||
}
|
||||
|
||||
//fmt.Println("Query: Sending query request")
|
||||
qr := queryRequest{
|
||||
query: query,
|
||||
dst: dst,
|
||||
args: connectServerArgs,
|
||||
finished: make(chan error),
|
||||
}
|
||||
s.queries <- &qr
|
||||
s.lQueryorClose.Unlock()
|
||||
err, ok := <-qr.finished
|
||||
if ok {
|
||||
//fmt.Println("Query: Finished with error")
|
||||
return err //Send error to caller
|
||||
}
|
||||
//fmt.Println("Query: Finished")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SWbemServices) queryBackground(q *queryRequest) error {
|
||||
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||
return fmt.Errorf("SWbemServices is not Initialized")
|
||||
}
|
||||
wmi := s.sWbemLocatorIDispatch //Should just rename in the code, but this will help as we break things apart
|
||||
//fmt.Println("queryBackground: Starting")
|
||||
|
||||
dv := reflect.ValueOf(q.dst)
|
||||
if dv.Kind() != reflect.Ptr || dv.IsNil() {
|
||||
return ErrInvalidEntityType
|
||||
}
|
||||
dv = dv.Elem()
|
||||
mat, elemType := checkMultiArg(dv)
|
||||
if mat == multiArgTypeInvalid {
|
||||
return ErrInvalidEntityType
|
||||
}
|
||||
|
||||
// service is a SWbemServices
|
||||
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := serviceRaw.ToIDispatch()
|
||||
defer serviceRaw.Clear()
|
||||
|
||||
// result is a SWBemObjectSet
|
||||
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", q.query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := resultRaw.ToIDispatch()
|
||||
defer resultRaw.Clear()
|
||||
|
||||
count, err := oleInt64(result, "Count")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enumProperty, err := result.GetProperty("_NewEnum")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer enumProperty.Clear()
|
||||
|
||||
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enum == nil {
|
||||
return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
|
||||
}
|
||||
defer enum.Release()
|
||||
|
||||
// Initialize a slice with Count capacity
|
||||
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
|
||||
|
||||
var errFieldMismatch error
|
||||
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
// item is a SWbemObject, but really a Win32_Process
|
||||
item := itemRaw.ToIDispatch()
|
||||
defer item.Release()
|
||||
|
||||
ev := reflect.New(elemType)
|
||||
if err = s.cWMIClient.loadEntity(ev.Interface(), item); err != nil {
|
||||
if _, ok := err.(*ErrFieldMismatch); ok {
|
||||
// We continue loading entities even in the face of field mismatch errors.
|
||||
// If we encounter any other error, that other error is returned. Otherwise,
|
||||
// an ErrFieldMismatch is returned.
|
||||
errFieldMismatch = err
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mat != multiArgTypeStructPtr {
|
||||
ev = ev.Elem()
|
||||
}
|
||||
dv.Set(reflect.Append(dv, ev))
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
//fmt.Println("queryBackground: Finished")
|
||||
return errFieldMismatch
|
||||
}
|
||||
486
vendor/github.com/StackExchange/wmi/wmi.go
generated
vendored
Normal file
486
vendor/github.com/StackExchange/wmi/wmi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
Package wmi provides a WQL interface for WMI on Windows.
|
||||
|
||||
Example code to print names of running processes:
|
||||
|
||||
type Win32_Process struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
var dst []Win32_Process
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(q, &dst)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for i, v := range dst {
|
||||
println(i, v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
package wmi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
)
|
||||
|
||||
var l = log.New(os.Stdout, "", log.LstdFlags)
|
||||
|
||||
var (
|
||||
ErrInvalidEntityType = errors.New("wmi: invalid entity type")
|
||||
// ErrNilCreateObject is the error returned if CreateObject returns nil even
|
||||
// if the error was nil.
|
||||
ErrNilCreateObject = errors.New("wmi: create object returned nil")
|
||||
lock sync.Mutex
|
||||
)
|
||||
|
||||
// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
|
||||
const S_FALSE = 0x00000001
|
||||
|
||||
// QueryNamespace invokes Query with the given namespace on the local machine.
|
||||
func QueryNamespace(query string, dst interface{}, namespace string) error {
|
||||
return Query(query, dst, nil, namespace)
|
||||
}
|
||||
|
||||
// Query runs the WQL query and appends the values to dst.
|
||||
//
|
||||
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
|
||||
// the query must have the same name in dst. Supported types are all signed and
|
||||
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
|
||||
// Array types are not supported.
|
||||
//
|
||||
// By default, the local machine and default namespace are used. These can be
|
||||
// changed using connectServerArgs. See
|
||||
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
|
||||
//
|
||||
// Query is a wrapper around DefaultClient.Query.
|
||||
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
if DefaultClient.SWbemServicesClient == nil {
|
||||
return DefaultClient.Query(query, dst, connectServerArgs...)
|
||||
}
|
||||
return DefaultClient.SWbemServicesClient.Query(query, dst, connectServerArgs...)
|
||||
}
|
||||
|
||||
// A Client is an WMI query client.
|
||||
//
|
||||
// Its zero value (DefaultClient) is a usable client.
|
||||
type Client struct {
|
||||
// NonePtrZero specifies if nil values for fields which aren't pointers
|
||||
// should be returned as the field types zero value.
|
||||
//
|
||||
// Setting this to true allows stucts without pointer fields to be used
|
||||
// without the risk failure should a nil value returned from WMI.
|
||||
NonePtrZero bool
|
||||
|
||||
// PtrNil specifies if nil values for pointer fields should be returned
|
||||
// as nil.
|
||||
//
|
||||
// Setting this to true will set pointer fields to nil where WMI
|
||||
// returned nil, otherwise the types zero value will be returned.
|
||||
PtrNil bool
|
||||
|
||||
// AllowMissingFields specifies that struct fields not present in the
|
||||
// query result should not result in an error.
|
||||
//
|
||||
// Setting this to true allows custom queries to be used with full
|
||||
// struct definitions instead of having to define multiple structs.
|
||||
AllowMissingFields bool
|
||||
|
||||
// SWbemServiceClient is an optional SWbemServices object that can be
|
||||
// initialized and then reused across multiple queries. If it is null
|
||||
// then the method will initialize a new temporary client each time.
|
||||
SWbemServicesClient *SWbemServices
|
||||
}
|
||||
|
||||
// DefaultClient is the default Client and is used by Query, QueryNamespace
|
||||
var DefaultClient = &Client{}
|
||||
|
||||
// Query runs the WQL query and appends the values to dst.
|
||||
//
|
||||
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
|
||||
// the query must have the same name in dst. Supported types are all signed and
|
||||
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
|
||||
// Array types are not supported.
|
||||
//
|
||||
// By default, the local machine and default namespace are used. These can be
|
||||
// changed using connectServerArgs. See
|
||||
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
|
||||
func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
dv := reflect.ValueOf(dst)
|
||||
if dv.Kind() != reflect.Ptr || dv.IsNil() {
|
||||
return ErrInvalidEntityType
|
||||
}
|
||||
dv = dv.Elem()
|
||||
mat, elemType := checkMultiArg(dv)
|
||||
if mat == multiArgTypeInvalid {
|
||||
return ErrInvalidEntityType
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
|
||||
if err != nil {
|
||||
oleCode := err.(*ole.OleError).Code()
|
||||
if oleCode != ole.S_OK && oleCode != S_FALSE {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer ole.CoUninitialize()
|
||||
|
||||
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
|
||||
if err != nil {
|
||||
return err
|
||||
} else if unknown == nil {
|
||||
return ErrNilCreateObject
|
||||
}
|
||||
defer unknown.Release()
|
||||
|
||||
wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wmi.Release()
|
||||
|
||||
// service is a SWbemServices
|
||||
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := serviceRaw.ToIDispatch()
|
||||
defer serviceRaw.Clear()
|
||||
|
||||
// result is a SWBemObjectSet
|
||||
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := resultRaw.ToIDispatch()
|
||||
defer resultRaw.Clear()
|
||||
|
||||
count, err := oleInt64(result, "Count")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enumProperty, err := result.GetProperty("_NewEnum")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer enumProperty.Clear()
|
||||
|
||||
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enum == nil {
|
||||
return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
|
||||
}
|
||||
defer enum.Release()
|
||||
|
||||
// Initialize a slice with Count capacity
|
||||
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
|
||||
|
||||
var errFieldMismatch error
|
||||
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
// item is a SWbemObject, but really a Win32_Process
|
||||
item := itemRaw.ToIDispatch()
|
||||
defer item.Release()
|
||||
|
||||
ev := reflect.New(elemType)
|
||||
if err = c.loadEntity(ev.Interface(), item); err != nil {
|
||||
if _, ok := err.(*ErrFieldMismatch); ok {
|
||||
// We continue loading entities even in the face of field mismatch errors.
|
||||
// If we encounter any other error, that other error is returned. Otherwise,
|
||||
// an ErrFieldMismatch is returned.
|
||||
errFieldMismatch = err
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mat != multiArgTypeStructPtr {
|
||||
ev = ev.Elem()
|
||||
}
|
||||
dv.Set(reflect.Append(dv, ev))
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return errFieldMismatch
|
||||
}
|
||||
|
||||
// ErrFieldMismatch is returned when a field is to be loaded into a different
|
||||
// type than the one it was stored from, or when a field is missing or
|
||||
// unexported in the destination struct.
|
||||
// StructType is the type of the struct pointed to by the destination argument.
|
||||
type ErrFieldMismatch struct {
|
||||
StructType reflect.Type
|
||||
FieldName string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *ErrFieldMismatch) Error() string {
|
||||
return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
|
||||
e.FieldName, e.StructType, e.Reason)
|
||||
}
|
||||
|
||||
var timeType = reflect.TypeOf(time.Time{})
|
||||
|
||||
// loadEntity loads a SWbemObject into a struct pointer.
|
||||
func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
|
||||
v := reflect.ValueOf(dst).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
of := f
|
||||
isPtr := f.Kind() == reflect.Ptr
|
||||
if isPtr {
|
||||
ptr := reflect.New(f.Type().Elem())
|
||||
f.Set(ptr)
|
||||
f = f.Elem()
|
||||
}
|
||||
n := v.Type().Field(i).Name
|
||||
if !f.CanSet() {
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "CanSet() is false",
|
||||
}
|
||||
}
|
||||
prop, err := oleutil.GetProperty(src, n)
|
||||
if err != nil {
|
||||
if !c.AllowMissingFields {
|
||||
errFieldMismatch = &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "no such struct field",
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
defer prop.Clear()
|
||||
|
||||
switch val := prop.Value().(type) {
|
||||
case int8, int16, int32, int64, int:
|
||||
v := reflect.ValueOf(val).Int()
|
||||
switch f.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
f.SetInt(v)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
f.SetUint(uint64(v))
|
||||
default:
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "not an integer class",
|
||||
}
|
||||
}
|
||||
case uint8, uint16, uint32, uint64:
|
||||
v := reflect.ValueOf(val).Uint()
|
||||
switch f.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
f.SetInt(int64(v))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
f.SetUint(v)
|
||||
default:
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "not an integer class",
|
||||
}
|
||||
}
|
||||
case string:
|
||||
switch f.Kind() {
|
||||
case reflect.String:
|
||||
f.SetString(val)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
iv, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetInt(iv)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
uv, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetUint(uv)
|
||||
case reflect.Struct:
|
||||
switch f.Type() {
|
||||
case timeType:
|
||||
if len(val) == 25 {
|
||||
mins, err := strconv.Atoi(val[22:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
|
||||
}
|
||||
t, err := time.Parse("20060102150405.000000-0700", val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Set(reflect.ValueOf(t))
|
||||
}
|
||||
}
|
||||
case bool:
|
||||
switch f.Kind() {
|
||||
case reflect.Bool:
|
||||
f.SetBool(val)
|
||||
default:
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "not a bool",
|
||||
}
|
||||
}
|
||||
case float32:
|
||||
switch f.Kind() {
|
||||
case reflect.Float32:
|
||||
f.SetFloat(float64(val))
|
||||
default:
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: "not a Float32",
|
||||
}
|
||||
}
|
||||
default:
|
||||
if f.Kind() == reflect.Slice {
|
||||
switch f.Type().Elem().Kind() {
|
||||
case reflect.String:
|
||||
safeArray := prop.ToArray()
|
||||
if safeArray != nil {
|
||||
arr := safeArray.ToValueArray()
|
||||
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
|
||||
for i, v := range arr {
|
||||
s := fArr.Index(i)
|
||||
s.SetString(v.(string))
|
||||
}
|
||||
f.Set(fArr)
|
||||
}
|
||||
case reflect.Uint8:
|
||||
safeArray := prop.ToArray()
|
||||
if safeArray != nil {
|
||||
arr := safeArray.ToValueArray()
|
||||
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
|
||||
for i, v := range arr {
|
||||
s := fArr.Index(i)
|
||||
s.SetUint(reflect.ValueOf(v).Uint())
|
||||
}
|
||||
f.Set(fArr)
|
||||
}
|
||||
default:
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: fmt.Sprintf("unsupported slice type (%T)", val),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
typeof := reflect.TypeOf(val)
|
||||
if typeof == nil && (isPtr || c.NonePtrZero) {
|
||||
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
|
||||
of.Set(reflect.Zero(of.Type()))
|
||||
}
|
||||
break
|
||||
}
|
||||
return &ErrFieldMismatch{
|
||||
StructType: of.Type(),
|
||||
FieldName: n,
|
||||
Reason: fmt.Sprintf("unsupported type (%T)", val),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errFieldMismatch
|
||||
}
|
||||
|
||||
type multiArgType int
|
||||
|
||||
const (
|
||||
multiArgTypeInvalid multiArgType = iota
|
||||
multiArgTypeStruct
|
||||
multiArgTypeStructPtr
|
||||
)
|
||||
|
||||
// checkMultiArg checks that v has type []S, []*S for some struct type S.
|
||||
//
|
||||
// It returns what category the slice's elements are, and the reflect.Type
|
||||
// that represents S.
|
||||
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
|
||||
if v.Kind() != reflect.Slice {
|
||||
return multiArgTypeInvalid, nil
|
||||
}
|
||||
elemType = v.Type().Elem()
|
||||
switch elemType.Kind() {
|
||||
case reflect.Struct:
|
||||
return multiArgTypeStruct, elemType
|
||||
case reflect.Ptr:
|
||||
elemType = elemType.Elem()
|
||||
if elemType.Kind() == reflect.Struct {
|
||||
return multiArgTypeStructPtr, elemType
|
||||
}
|
||||
}
|
||||
return multiArgTypeInvalid, nil
|
||||
}
|
||||
|
||||
func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
|
||||
v, err := oleutil.GetProperty(item, prop)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer v.Clear()
|
||||
|
||||
i := int64(v.Val)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// CreateQuery returns a WQL query string that queries all columns of src. where
|
||||
// is an optional string that is appended to the query, to be used with WHERE
|
||||
// clauses. In such a case, the "WHERE" string should appear at the beginning.
|
||||
func CreateQuery(src interface{}, where string) string {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("SELECT ")
|
||||
s := reflect.Indirect(reflect.ValueOf(src))
|
||||
t := s.Type()
|
||||
if s.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return ""
|
||||
}
|
||||
var fields []string
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fields = append(fields, t.Field(i).Name)
|
||||
}
|
||||
b.WriteString(strings.Join(fields, ", "))
|
||||
b.WriteString(" FROM ")
|
||||
b.WriteString(t.Name())
|
||||
b.WriteString(" " + where)
|
||||
return b.String()
|
||||
}
|
||||
102
vendor/github.com/elastic/gosigar/CHANGELOG.md
generated
vendored
Normal file
102
vendor/github.com/elastic/gosigar/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
### Deprecated
|
||||
|
||||
## [0.8.0]
|
||||
|
||||
### Added
|
||||
- Added partial `getrusage` support for Windows to retrieve system CPU time and user CPU time. #95
|
||||
- Added full `getrusage` support for Unix. #95
|
||||
|
||||
## [0.7.0]
|
||||
|
||||
### Added
|
||||
- Added method stubs for process handling for operating system that are not supported
|
||||
by gosigar. All methods return `ErrNotImplemented` on such systems. #88
|
||||
|
||||
### Fixed
|
||||
- Fix freebsd build by using the common version of Get(pid). #91
|
||||
|
||||
### Changed
|
||||
- Fixed issues in cgroup package by adding missing error checks and closing
|
||||
file handles. #92
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
### Added
|
||||
- Added method stubs to enable compilation for operating systems that are not
|
||||
supported by gosigar. All methods return `ErrNotImplemented` on these unsupported
|
||||
operating systems. #83
|
||||
- FreeBSD returns `ErrNotImplemented` for `ProcTime.Get`. #83
|
||||
|
||||
### Changed
|
||||
- OpenBSD returns `ErrNotImplemented` for `ProcTime.Get` instead of `nil`. #83
|
||||
- Fixed incorrect `Mem.Used` calculation under linux. #82
|
||||
- Fixed `ProcState` on Linux and FreeBSD when process names contain parentheses. #81
|
||||
|
||||
### Removed
|
||||
- Remove NetBSD build from sigar_unix.go as it is not supported by gosigar. #83
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
### Changed
|
||||
- Fixed Trim environment variables when comparing values in the test suite. #79
|
||||
- Make `kern_procargs` more robust under darwin when we cannot retrieve
|
||||
all the information about a process. #78
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
### Changed
|
||||
- Fixed Windows issue that caused a hang during `init()` if WMI wasn't ready. #74
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
### Added
|
||||
- Read `MemAvailable` value for kernel 3.14+ #71
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### Added
|
||||
- Added `ErrCgroupsMissing` to indicate that /proc/cgroups is missing which is
|
||||
an indicator that cgroups were disabled at compile time. #64
|
||||
|
||||
### Changed
|
||||
- Changed `cgroup.SupportedSubsystems()` to honor the "enabled" column in the
|
||||
/proc/cgroups file. #64
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
### Added
|
||||
- Added `CpuList` implementation for Windows that returns CPU timing information
|
||||
on a per CPU basis. #55
|
||||
- Added `Uptime` implementation for Windows. #55
|
||||
- Added `Swap` implementation for Windows based on page file metrics. #55
|
||||
- Added support to `github.com/gosigar/sys/windows` for querying and enabling
|
||||
privileges in a process token.
|
||||
- Added utility code for interfacing with linux NETLINK_INET_DIAG. #60
|
||||
- Added `ProcEnv` for getting a process's environment variables. #61
|
||||
|
||||
### Changed
|
||||
- Changed several `OpenProcess` calls on Windows to request the lowest possible
|
||||
access privileges. #50
|
||||
- Removed cgo usage from Windows code.
|
||||
- Added OS version checks to `ProcArgs.Get` on Windows because the
|
||||
`Win32_Process` WMI query is not available prior to Windows vista. On XP and
|
||||
Windows 2003, this method returns `ErrNotImplemented`. #55
|
||||
|
||||
### Fixed
|
||||
- Fixed value of `Mem.ActualFree` and `Mem.ActualUsed` on Windows. #49
|
||||
- Fixed `ProcTime.StartTime` on Windows to report value in milliseconds since
|
||||
Unix epoch. #51
|
||||
- Fixed `ProcStatus.PPID` value is wrong on Windows. #55
|
||||
- Fixed `ProcStatus.Username` error on Windows XP #56
|
||||
201
vendor/github.com/elastic/gosigar/LICENSE
generated
vendored
Normal file
201
vendor/github.com/elastic/gosigar/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
9
vendor/github.com/elastic/gosigar/NOTICE
generated
vendored
Normal file
9
vendor/github.com/elastic/gosigar/NOTICE
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Copyright (c) [2009-2011] VMware, Inc. All Rights Reserved.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
You may not use this product except in compliance with the License.
|
||||
|
||||
This product includes a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of these
|
||||
subcomponents is subject to the terms and conditions of the
|
||||
subcomponent's license, as noted in the LICENSE file.
|
||||
57
vendor/github.com/elastic/gosigar/README.md
generated
vendored
Normal file
57
vendor/github.com/elastic/gosigar/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Go sigar [](https://travis-ci.org/elastic/gosigar) [](https://ci.appveyor.com/project/elastic-beats/gosigar/branch/master)
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
Go sigar is a golang implementation of the
|
||||
[sigar API](https://github.com/hyperic/sigar). The Go version of
|
||||
sigar has a very similar interface, but is being written from scratch
|
||||
in pure go/cgo, rather than cgo bindings for libsigar.
|
||||
|
||||
## Test drive
|
||||
|
||||
$ go get github.com/elastic/gosigar
|
||||
$ cd $GOPATH/src/github.com/elastic/gosigar/examples/ps
|
||||
$ go build
|
||||
$ ./ps
|
||||
|
||||
## Supported platforms
|
||||
|
||||
The features vary by operating system.
|
||||
|
||||
| Feature | Linux | Darwin | Windows | OpenBSD | FreeBSD |
|
||||
|-----------------|:-----:|:------:|:-------:|:-------:|:-------:|
|
||||
| Cpu | X | X | X | X | X |
|
||||
| CpuList | X | X | | X | X |
|
||||
| FDUsage | X | | | | X |
|
||||
| FileSystemList | X | X | X | X | X |
|
||||
| FileSystemUsage | X | X | X | X | X |
|
||||
| LoadAverage | X | X | | X | X |
|
||||
| Mem | X | X | X | X | X |
|
||||
| ProcArgs | X | X | X | | X |
|
||||
| ProcEnv | X | X | | | X |
|
||||
| ProcExe | X | X | | | X |
|
||||
| ProcFDUsage | X | | | | X |
|
||||
| ProcList | X | X | X | | X |
|
||||
| ProcMem | X | X | X | | X |
|
||||
| ProcState | X | X | X | | X |
|
||||
| ProcTime | X | X | X | | X |
|
||||
| Swap | X | X | | X | X |
|
||||
| Uptime | X | X | | X | X |
|
||||
|
||||
## OS Specific Notes
|
||||
|
||||
### FreeBSD
|
||||
|
||||
Mount both `linprocfs` and `procfs` for compatability. Consider adding these
|
||||
mounts to your `/etc/fstab` file so they are mounted automatically at boot.
|
||||
|
||||
```
|
||||
sudo mount -t procfs proc /proc
|
||||
sudo mkdir -p /compat/linux/proc
|
||||
sudo mount -t linprocfs /dev/null /compat/linux/proc
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0
|
||||
25
vendor/github.com/elastic/gosigar/Vagrantfile
generated
vendored
Normal file
25
vendor/github.com/elastic/gosigar/Vagrantfile
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "hashicorp/precise64"
|
||||
config.vm.provision "shell", inline: "mkdir -p /home/vagrant/go"
|
||||
config.vm.synced_folder ".", "/home/vagrant/go/src/github.com/cloudfoundry/gosigar"
|
||||
config.vm.provision "shell", inline: "chown -R vagrant:vagrant /home/vagrant/go"
|
||||
install_go = <<-BASH
|
||||
set -e
|
||||
|
||||
if [ ! -d "/usr/local/go" ]; then
|
||||
cd /tmp && wget https://storage.googleapis.com/golang/go1.3.3.linux-amd64.tar.gz
|
||||
cd /usr/local
|
||||
tar xvzf /tmp/go1.3.3.linux-amd64.tar.gz
|
||||
echo 'export GOPATH=/home/vagrant/go; export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin' >> /home/vagrant/.bashrc
|
||||
fi
|
||||
export GOPATH=/home/vagrant/go
|
||||
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||
/usr/local/go/bin/go get -u github.com/onsi/ginkgo/ginkgo
|
||||
/usr/local/go/bin/go get -u github.com/onsi/gomega;
|
||||
BASH
|
||||
config.vm.provision "shell", inline: 'apt-get install -y git-core'
|
||||
config.vm.provision "shell", inline: install_go
|
||||
end
|
||||
21
vendor/github.com/elastic/gosigar/codecov.yml
generated
vendored
Normal file
21
vendor/github.com/elastic/gosigar/codecov.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Enable coverage report message for diff on commit
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch:
|
||||
default:
|
||||
# basic
|
||||
target: auto
|
||||
threshold: null
|
||||
base: auto
|
||||
# advanced
|
||||
branches: null
|
||||
if_no_uploads: error
|
||||
if_not_found: success
|
||||
if_ci_failed: error
|
||||
only_pulls: false
|
||||
flags: null
|
||||
paths: null
|
||||
|
||||
# Disable comments on Pull Requests
|
||||
comment: false
|
||||
83
vendor/github.com/elastic/gosigar/concrete_sigar.go
generated
vendored
Normal file
83
vendor/github.com/elastic/gosigar/concrete_sigar.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package gosigar
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ConcreteSigar struct{}
|
||||
|
||||
func (c *ConcreteSigar) CollectCpuStats(collectionInterval time.Duration) (<-chan Cpu, chan<- struct{}) {
|
||||
// samplesCh is buffered to 1 value to immediately return first CPU sample
|
||||
samplesCh := make(chan Cpu, 1)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
var cpuUsage Cpu
|
||||
|
||||
// Immediately provide non-delta value.
|
||||
// samplesCh is buffered to 1 value, so it will not block.
|
||||
cpuUsage.Get()
|
||||
samplesCh <- cpuUsage
|
||||
|
||||
ticker := time.NewTicker(collectionInterval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
previousCpuUsage := cpuUsage
|
||||
|
||||
cpuUsage.Get()
|
||||
|
||||
select {
|
||||
case samplesCh <- cpuUsage.Delta(previousCpuUsage):
|
||||
default:
|
||||
// Include default to avoid channel blocking
|
||||
}
|
||||
|
||||
case <-stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return samplesCh, stopCh
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetLoadAverage() (LoadAverage, error) {
|
||||
l := LoadAverage{}
|
||||
err := l.Get()
|
||||
return l, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetMem() (Mem, error) {
|
||||
m := Mem{}
|
||||
err := m.Get()
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetSwap() (Swap, error) {
|
||||
s := Swap{}
|
||||
err := s.Get()
|
||||
return s, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetFileSystemUsage(path string) (FileSystemUsage, error) {
|
||||
f := FileSystemUsage{}
|
||||
err := f.Get(path)
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetFDUsage() (FDUsage, error) {
|
||||
fd := FDUsage{}
|
||||
err := fd.Get()
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// GetRusage return the resource usage of the process
|
||||
// Possible params: 0 = RUSAGE_SELF, 1 = RUSAGE_CHILDREN, 2 = RUSAGE_THREAD
|
||||
func (c *ConcreteSigar) GetRusage(who int) (Rusage, error) {
|
||||
r := Rusage{}
|
||||
err := r.Get(who)
|
||||
return r, err
|
||||
}
|
||||
494
vendor/github.com/elastic/gosigar/sigar_darwin.go
generated
vendored
Normal file
494
vendor/github.com/elastic/gosigar/sigar_darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/processor_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
avg := []C.double{0, 0, 0}
|
||||
|
||||
C.getloadavg(&avg[0], C.int(len(avg)))
|
||||
|
||||
self.One = float64(avg[0])
|
||||
self.Five = float64(avg[1])
|
||||
self.Fifteen = float64(avg[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
tv := syscall.Timeval32{}
|
||||
|
||||
if err := sysctlbyname("kern.boottime", &tv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
var vmstat C.vm_statistics_data_t
|
||||
|
||||
if err := sysctlbyname("hw.memsize", &self.Total); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := vm_info(&vmstat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kern := uint64(vmstat.inactive_count) << 12
|
||||
self.Free = uint64(vmstat.free_count) << 12
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free + kern
|
||||
self.ActualUsed = self.Used - kern
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type xsw_usage struct {
|
||||
Total, Avail, Used uint64
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
sw_usage := xsw_usage{}
|
||||
|
||||
if err := sysctlbyname("vm.swapusage", &sw_usage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = sw_usage.Total
|
||||
self.Used = sw_usage.Used
|
||||
self.Free = sw_usage.Avail
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT
|
||||
var cpuload C.host_cpu_load_info_data_t
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_CPU_LOAD_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
self.User = uint64(cpuload.cpu_ticks[C.CPU_STATE_USER])
|
||||
self.Sys = uint64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM])
|
||||
self.Idle = uint64(cpuload.cpu_ticks[C.CPU_STATE_IDLE])
|
||||
self.Nice = uint64(cpuload.cpu_ticks[C.CPU_STATE_NICE])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
var count C.mach_msg_type_number_t
|
||||
var cpuload *C.processor_cpu_load_info_data_t
|
||||
var ncpu C.natural_t
|
||||
|
||||
status := C.host_processor_info(C.host_t(C.mach_host_self()),
|
||||
C.PROCESSOR_CPU_LOAD_INFO,
|
||||
&ncpu,
|
||||
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
// jump through some cgo casting hoops and ensure we properly free
|
||||
// the memory that cpuload points to
|
||||
target := C.vm_map_t(C.mach_task_self_)
|
||||
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
|
||||
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
|
||||
|
||||
// the body of struct processor_cpu_load_info
|
||||
// aka processor_cpu_load_info_data_t
|
||||
var cpu_ticks [C.CPU_STATE_MAX]uint32
|
||||
|
||||
// copy the cpuload array to a []byte buffer
|
||||
// where we can binary.Read the data
|
||||
size := int(ncpu) * binary.Size(cpu_ticks)
|
||||
buf := C.GoBytes(unsafe.Pointer(cpuload), C.int(size))
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
self.List = make([]Cpu, 0, ncpu)
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
cpu := Cpu{}
|
||||
|
||||
err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cpu.User = uint64(cpu_ticks[C.CPU_STATE_USER])
|
||||
cpu.Sys = uint64(cpu_ticks[C.CPU_STATE_SYSTEM])
|
||||
cpu.Idle = uint64(cpu_ticks[C.CPU_STATE_IDLE])
|
||||
cpu.Nice = uint64(cpu_ticks[C.CPU_STATE_NICE])
|
||||
|
||||
self.List = append(self.List, cpu)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
num, err := syscall.Getfsstat(nil, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]syscall.Statfs_t, num)
|
||||
|
||||
_, err = syscall.Getfsstat(buf, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fslist := make([]FileSystem, 0, num)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
fs := FileSystem{}
|
||||
|
||||
fs.DirName = bytePtrToString(&buf[i].Mntonname[0])
|
||||
fs.DevName = bytePtrToString(&buf[i].Mntfromname[0])
|
||||
fs.SysTypeName = bytePtrToString(&buf[i].Fstypename[0])
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
}
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *ProcList) Get() error {
|
||||
n := C.proc_listpids(C.PROC_ALL_PIDS, 0, nil, 0)
|
||||
if n <= 0 {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
n = C.proc_listpids(C.PROC_ALL_PIDS, 0, unsafe.Pointer(&buf[0]), n)
|
||||
if n <= 0 {
|
||||
return syscall.ENOMEM
|
||||
}
|
||||
|
||||
var pid int32
|
||||
num := int(n) / binary.Size(pid)
|
||||
list := make([]int, 0, num)
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
if err := binary.Read(bbuf, binary.LittleEndian, &pid); err != nil {
|
||||
return err
|
||||
}
|
||||
if pid == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
list = append(list, int(pid))
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := task_info(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Name = C.GoString(&info.pbsd.pbi_comm[0])
|
||||
|
||||
switch info.pbsd.pbi_status {
|
||||
case C.SIDL:
|
||||
self.State = RunStateIdle
|
||||
case C.SRUN:
|
||||
self.State = RunStateRun
|
||||
case C.SSLEEP:
|
||||
self.State = RunStateSleep
|
||||
case C.SSTOP:
|
||||
self.State = RunStateStop
|
||||
case C.SZOMB:
|
||||
self.State = RunStateZombie
|
||||
default:
|
||||
self.State = RunStateUnknown
|
||||
}
|
||||
|
||||
self.Ppid = int(info.pbsd.pbi_ppid)
|
||||
|
||||
self.Pgid = int(info.pbsd.pbi_pgid)
|
||||
|
||||
self.Tty = int(info.pbsd.e_tdev)
|
||||
|
||||
self.Priority = int(info.ptinfo.pti_priority)
|
||||
|
||||
self.Nice = int(info.pbsd.pbi_nice)
|
||||
|
||||
// Get process username. Fallback to UID if username is not available.
|
||||
uid := strconv.Itoa(int(info.pbsd.pbi_uid))
|
||||
user, err := user.LookupId(uid)
|
||||
if err == nil && user.Username != "" {
|
||||
self.Username = user.Username
|
||||
} else {
|
||||
self.Username = uid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := task_info(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Size = uint64(info.ptinfo.pti_virtual_size)
|
||||
self.Resident = uint64(info.ptinfo.pti_resident_size)
|
||||
self.PageFaults = uint64(info.ptinfo.pti_faults)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := task_info(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.User =
|
||||
uint64(info.ptinfo.pti_total_user) / uint64(time.Millisecond)
|
||||
|
||||
self.Sys =
|
||||
uint64(info.ptinfo.pti_total_system) / uint64(time.Millisecond)
|
||||
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
self.StartTime = (uint64(info.pbsd.pbi_start_tvsec) * 1000) +
|
||||
(uint64(info.pbsd.pbi_start_tvusec) / 1000)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
var args []string
|
||||
|
||||
argv := func(arg string) {
|
||||
args = append(args, arg)
|
||||
}
|
||||
|
||||
err := kern_procargs(pid, nil, argv, nil)
|
||||
|
||||
self.List = args
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
if self.Vars == nil {
|
||||
self.Vars = map[string]string{}
|
||||
}
|
||||
|
||||
env := func(k, v string) {
|
||||
self.Vars[k] = v
|
||||
}
|
||||
|
||||
return kern_procargs(pid, nil, nil, env)
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
exe := func(arg string) {
|
||||
self.Name = arg
|
||||
}
|
||||
|
||||
return kern_procargs(pid, exe, nil, nil)
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// wrapper around sysctl KERN_PROCARGS2
|
||||
// callbacks params are optional,
|
||||
// up to the caller as to which pieces of data they want
|
||||
func kern_procargs(pid int,
|
||||
exe func(string),
|
||||
argv func(string),
|
||||
env func(string, string)) error {
|
||||
|
||||
mib := []C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
|
||||
argmax := uintptr(C.ARG_MAX)
|
||||
buf := make([]byte, argmax)
|
||||
err := sysctl(mib, &buf[0], &argmax, nil, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
bbuf.Truncate(int(argmax))
|
||||
|
||||
var argc int32
|
||||
binary.Read(bbuf, binary.LittleEndian, &argc)
|
||||
|
||||
path, err := bbuf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading the argv[0]: %v", err)
|
||||
}
|
||||
if exe != nil {
|
||||
exe(string(chop(path)))
|
||||
}
|
||||
|
||||
// skip trailing \0's
|
||||
for {
|
||||
c, err := bbuf.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error skipping nils: %v", err)
|
||||
}
|
||||
if c != 0 {
|
||||
bbuf.UnreadByte()
|
||||
break // start of argv[0]
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < int(argc); i++ {
|
||||
arg, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading args: %v", err)
|
||||
}
|
||||
if argv != nil {
|
||||
argv(string(chop(arg)))
|
||||
}
|
||||
}
|
||||
|
||||
if env == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
delim := []byte{61} // "="
|
||||
|
||||
for {
|
||||
line, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF || line[0] == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading args: %v", err)
|
||||
}
|
||||
pair := bytes.SplitN(chop(line), delim, 2)
|
||||
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("Error reading process information for PID: %d", pid)
|
||||
}
|
||||
|
||||
env(string(pair[0]), string(pair[1]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX copied from zsyscall_darwin_amd64.go
|
||||
func sysctl(mib []C.int, old *byte, oldlen *uintptr,
|
||||
new *byte, newlen uintptr) (err error) {
|
||||
var p0 unsafe.Pointer
|
||||
p0 = unsafe.Pointer(&mib[0])
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p0),
|
||||
uintptr(len(mib)),
|
||||
uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)),
|
||||
uintptr(unsafe.Pointer(new)), uintptr(newlen))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func vm_info(vmstat *C.vm_statistics_data_t) error {
|
||||
var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT
|
||||
|
||||
status := C.host_statistics(
|
||||
C.host_t(C.mach_host_self()),
|
||||
C.HOST_VM_INFO,
|
||||
C.host_info_t(unsafe.Pointer(vmstat)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_statistics=%d", status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generic Sysctl buffer unmarshalling
|
||||
func sysctlbyname(name string, data interface{}) (err error) {
|
||||
val, err := syscall.Sysctl(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := []byte(val)
|
||||
|
||||
switch v := data.(type) {
|
||||
case *uint64:
|
||||
*v = *(*uint64)(unsafe.Pointer(&buf[0]))
|
||||
return
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer([]byte(val))
|
||||
return binary.Read(bbuf, binary.LittleEndian, data)
|
||||
}
|
||||
|
||||
func task_info(pid int, info *C.struct_proc_taskallinfo) error {
|
||||
size := C.int(unsafe.Sizeof(*info))
|
||||
ptr := unsafe.Pointer(info)
|
||||
|
||||
n := C.proc_pidinfo(C.int(pid), C.PROC_PIDTASKALLINFO, 0, ptr, size)
|
||||
if n != size {
|
||||
return fmt.Errorf("Could not read process info for pid %d", pid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
126
vendor/github.com/elastic/gosigar/sigar_format.go
generated
vendored
Normal file
126
vendor/github.com/elastic/gosigar/sigar_format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Go version of apr_strfsize
|
||||
func FormatSize(size uint64) string {
|
||||
ord := []string{"K", "M", "G", "T", "P", "E"}
|
||||
o := 0
|
||||
buf := new(bytes.Buffer)
|
||||
w := bufio.NewWriter(buf)
|
||||
|
||||
if size < 973 {
|
||||
fmt.Fprintf(w, "%3d ", size)
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for {
|
||||
remain := size & 1023
|
||||
size >>= 10
|
||||
|
||||
if size >= 973 {
|
||||
o++
|
||||
continue
|
||||
}
|
||||
|
||||
if size < 9 || (size == 9 && remain < 973) {
|
||||
remain = ((remain * 5) + 256) / 512
|
||||
if remain >= 10 {
|
||||
size++
|
||||
remain = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%d.%d%s", size, remain, ord[o])
|
||||
break
|
||||
}
|
||||
|
||||
if remain >= 512 {
|
||||
size++
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%3d%s", size, ord[o])
|
||||
break
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func FormatPercent(percent float64) string {
|
||||
return strconv.FormatFloat(percent, 'f', -1, 64) + "%"
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) UsePercent() float64 {
|
||||
b_used := (self.Total - self.Free) / 1024
|
||||
b_avail := self.Avail / 1024
|
||||
utotal := b_used + b_avail
|
||||
used := b_used
|
||||
|
||||
if utotal != 0 {
|
||||
u100 := used * 100
|
||||
pct := u100 / utotal
|
||||
if u100%utotal != 0 {
|
||||
pct += 1
|
||||
}
|
||||
return (float64(pct) / float64(100)) * 100.0
|
||||
}
|
||||
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (self *Uptime) Format() string {
|
||||
buf := new(bytes.Buffer)
|
||||
w := bufio.NewWriter(buf)
|
||||
uptime := uint64(self.Length)
|
||||
|
||||
days := uptime / (60 * 60 * 24)
|
||||
|
||||
if days != 0 {
|
||||
s := ""
|
||||
if days > 1 {
|
||||
s = "s"
|
||||
}
|
||||
fmt.Fprintf(w, "%d day%s, ", days, s)
|
||||
}
|
||||
|
||||
minutes := uptime / 60
|
||||
hours := minutes / 60
|
||||
hours %= 24
|
||||
minutes %= 60
|
||||
|
||||
fmt.Fprintf(w, "%2d:%02d", hours, minutes)
|
||||
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (self *ProcTime) FormatStartTime() string {
|
||||
if self.StartTime == 0 {
|
||||
return "00:00"
|
||||
}
|
||||
start := time.Unix(int64(self.StartTime)/1000, 0)
|
||||
format := "Jan02"
|
||||
if time.Since(start).Seconds() < (60 * 60 * 24) {
|
||||
format = "15:04"
|
||||
}
|
||||
return start.Format(format)
|
||||
}
|
||||
|
||||
func (self *ProcTime) FormatTotal() string {
|
||||
t := self.Total / 1000
|
||||
ss := t % 60
|
||||
t /= 60
|
||||
mm := t % 60
|
||||
t /= 60
|
||||
hh := t % 24
|
||||
return fmt.Sprintf("%02d:%02d:%02d", hh, mm, ss)
|
||||
}
|
||||
108
vendor/github.com/elastic/gosigar/sigar_freebsd.go
generated
vendored
Normal file
108
vendor/github.com/elastic/gosigar/sigar_freebsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// Copied and modified from sigar_linux.go.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func init() {
|
||||
system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
|
||||
|
||||
Procd = "/compat/linux/proc"
|
||||
|
||||
getLinuxBootTime()
|
||||
}
|
||||
|
||||
func getMountTableFileName() string {
|
||||
return Procd + "/mtab"
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
ts := C.struct_timespec{}
|
||||
|
||||
if _, err := C.clock_gettime(C.CLOCK_UPTIME, &ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = float64(ts.tv_sec) + 1e-9*float64(ts.tv_nsec)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
val := C.uint32_t(0)
|
||||
sc := C.size_t(4)
|
||||
|
||||
name := C.CString("kern.openfiles")
|
||||
_, err := C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(val)
|
||||
|
||||
name = C.CString("kern.maxfiles")
|
||||
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Max = uint64(val)
|
||||
|
||||
self.Unused = self.Max - self.Open
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
err := readFile("/proc/"+strconv.Itoa(pid)+"/rlimit", func(line string) bool {
|
||||
if strings.HasPrefix(line, "nofile") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 3 {
|
||||
self.SoftLimit, _ = strconv.ParseUint(fields[1], 10, 64)
|
||||
self.HardLimit, _ = strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// linprocfs only provides this information for this process (self).
|
||||
fds, err := ioutil.ReadDir(procFileName(pid, "fd"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(len(fds))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCpuStat(self *Cpu, line string) error {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
self.User, _ = strtoull(fields[1])
|
||||
self.Nice, _ = strtoull(fields[2])
|
||||
self.Sys, _ = strtoull(fields[3])
|
||||
self.Idle, _ = strtoull(fields[4])
|
||||
return nil
|
||||
}
|
||||
197
vendor/github.com/elastic/gosigar/sigar_interface.go
generated
vendored
Normal file
197
vendor/github.com/elastic/gosigar/sigar_interface.go
generated
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
package gosigar
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ErrNotImplemented struct {
|
||||
OS string
|
||||
}
|
||||
|
||||
func (e ErrNotImplemented) Error() string {
|
||||
return "not implemented on " + e.OS
|
||||
}
|
||||
|
||||
func IsNotImplemented(err error) bool {
|
||||
switch err.(type) {
|
||||
case ErrNotImplemented, *ErrNotImplemented:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type Sigar interface {
|
||||
CollectCpuStats(collectionInterval time.Duration) (<-chan Cpu, chan<- struct{})
|
||||
GetLoadAverage() (LoadAverage, error)
|
||||
GetMem() (Mem, error)
|
||||
GetSwap() (Swap, error)
|
||||
GetFileSystemUsage(string) (FileSystemUsage, error)
|
||||
GetFDUsage() (FDUsage, error)
|
||||
GetRusage(who int) (Rusage, error)
|
||||
}
|
||||
|
||||
type Cpu struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Idle uint64
|
||||
Wait uint64
|
||||
Irq uint64
|
||||
SoftIrq uint64
|
||||
Stolen uint64
|
||||
}
|
||||
|
||||
func (cpu *Cpu) Total() uint64 {
|
||||
return cpu.User + cpu.Nice + cpu.Sys + cpu.Idle +
|
||||
cpu.Wait + cpu.Irq + cpu.SoftIrq + cpu.Stolen
|
||||
}
|
||||
|
||||
func (cpu Cpu) Delta(other Cpu) Cpu {
|
||||
return Cpu{
|
||||
User: cpu.User - other.User,
|
||||
Nice: cpu.Nice - other.Nice,
|
||||
Sys: cpu.Sys - other.Sys,
|
||||
Idle: cpu.Idle - other.Idle,
|
||||
Wait: cpu.Wait - other.Wait,
|
||||
Irq: cpu.Irq - other.Irq,
|
||||
SoftIrq: cpu.SoftIrq - other.SoftIrq,
|
||||
Stolen: cpu.Stolen - other.Stolen,
|
||||
}
|
||||
}
|
||||
|
||||
type LoadAverage struct {
|
||||
One, Five, Fifteen float64
|
||||
}
|
||||
|
||||
type Uptime struct {
|
||||
Length float64
|
||||
}
|
||||
|
||||
type Mem struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
ActualFree uint64
|
||||
ActualUsed uint64
|
||||
}
|
||||
|
||||
type Swap struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
}
|
||||
|
||||
type CpuList struct {
|
||||
List []Cpu
|
||||
}
|
||||
|
||||
type FDUsage struct {
|
||||
Open uint64
|
||||
Unused uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type FileSystem struct {
|
||||
DirName string
|
||||
DevName string
|
||||
TypeName string
|
||||
SysTypeName string
|
||||
Options string
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
type FileSystemList struct {
|
||||
List []FileSystem
|
||||
}
|
||||
|
||||
type FileSystemUsage struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
Avail uint64
|
||||
Files uint64
|
||||
FreeFiles uint64
|
||||
}
|
||||
|
||||
type ProcList struct {
|
||||
List []int
|
||||
}
|
||||
|
||||
type RunState byte
|
||||
|
||||
const (
|
||||
RunStateSleep = 'S'
|
||||
RunStateRun = 'R'
|
||||
RunStateStop = 'T'
|
||||
RunStateZombie = 'Z'
|
||||
RunStateIdle = 'D'
|
||||
RunStateUnknown = '?'
|
||||
)
|
||||
|
||||
type ProcState struct {
|
||||
Name string
|
||||
Username string
|
||||
State RunState
|
||||
Ppid int
|
||||
Pgid int
|
||||
Tty int
|
||||
Priority int
|
||||
Nice int
|
||||
Processor int
|
||||
}
|
||||
|
||||
type ProcMem struct {
|
||||
Size uint64
|
||||
Resident uint64
|
||||
Share uint64
|
||||
MinorFaults uint64
|
||||
MajorFaults uint64
|
||||
PageFaults uint64
|
||||
}
|
||||
|
||||
type ProcTime struct {
|
||||
StartTime uint64
|
||||
User uint64
|
||||
Sys uint64
|
||||
Total uint64
|
||||
}
|
||||
|
||||
type ProcArgs struct {
|
||||
List []string
|
||||
}
|
||||
|
||||
type ProcEnv struct {
|
||||
Vars map[string]string
|
||||
}
|
||||
|
||||
type ProcExe struct {
|
||||
Name string
|
||||
Cwd string
|
||||
Root string
|
||||
}
|
||||
|
||||
type ProcFDUsage struct {
|
||||
Open uint64
|
||||
SoftLimit uint64
|
||||
HardLimit uint64
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime time.Duration
|
||||
Stime time.Duration
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
84
vendor/github.com/elastic/gosigar/sigar_linux.go
generated
vendored
Normal file
84
vendor/github.com/elastic/gosigar/sigar_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
system.ticks = 100 // C.sysconf(C._SC_CLK_TCK)
|
||||
|
||||
Procd = "/proc"
|
||||
|
||||
getLinuxBootTime()
|
||||
}
|
||||
|
||||
func getMountTableFileName() string {
|
||||
return "/etc/mtab"
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
sysinfo := syscall.Sysinfo_t{}
|
||||
|
||||
if err := syscall.Sysinfo(&sysinfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = float64(sysinfo.Uptime)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return readFile(Procd+"/sys/fs/file-nr", func(line string) bool {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 3 {
|
||||
self.Open, _ = strconv.ParseUint(fields[0], 10, 64)
|
||||
self.Unused, _ = strconv.ParseUint(fields[1], 10, 64)
|
||||
self.Max, _ = strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
err := readFile(procFileName(pid, "limits"), func(line string) bool {
|
||||
if strings.HasPrefix(line, "Max open files") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 6 {
|
||||
self.SoftLimit, _ = strconv.ParseUint(fields[3], 10, 64)
|
||||
self.HardLimit, _ = strconv.ParseUint(fields[4], 10, 64)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fds, err := ioutil.ReadDir(procFileName(pid, "fd"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(len(fds))
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCpuStat(self *Cpu, line string) error {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
self.User, _ = strtoull(fields[1])
|
||||
self.Nice, _ = strtoull(fields[2])
|
||||
self.Sys, _ = strtoull(fields[3])
|
||||
self.Idle, _ = strtoull(fields[4])
|
||||
self.Wait, _ = strtoull(fields[5])
|
||||
self.Irq, _ = strtoull(fields[6])
|
||||
self.SoftIrq, _ = strtoull(fields[7])
|
||||
self.Stolen, _ = strtoull(fields[8])
|
||||
|
||||
return nil
|
||||
}
|
||||
468
vendor/github.com/elastic/gosigar/sigar_linux_common.go
generated
vendored
Normal file
468
vendor/github.com/elastic/gosigar/sigar_linux_common.go
generated
vendored
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
// +build freebsd linux
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var system struct {
|
||||
ticks uint64
|
||||
btime uint64
|
||||
}
|
||||
|
||||
var Procd string
|
||||
|
||||
func getLinuxBootTime() {
|
||||
// grab system boot time
|
||||
readFile(Procd+"/stat", func(line string) bool {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
system.btime, _ = strtoull(line[6:])
|
||||
return false // stop reading
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
line, err := ioutil.ReadFile(Procd + "/loadavg")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(line))
|
||||
|
||||
self.One, _ = strconv.ParseFloat(fields[0], 64)
|
||||
self.Five, _ = strconv.ParseFloat(fields[1], 64)
|
||||
self.Fifteen, _ = strconv.ParseFloat(fields[2], 64)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
|
||||
table, err := parseMeminfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total, _ = table["MemTotal"]
|
||||
self.Free, _ = table["MemFree"]
|
||||
buffers, _ := table["Buffers"]
|
||||
cached, _ := table["Cached"]
|
||||
|
||||
if available, ok := table["MemAvailable"]; ok {
|
||||
// MemAvailable is in /proc/meminfo (kernel 3.14+)
|
||||
self.ActualFree = available
|
||||
} else {
|
||||
self.ActualFree = self.Free + buffers + cached
|
||||
}
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualUsed = self.Total - self.ActualFree
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
|
||||
table, err := parseMeminfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Total, _ = table["SwapTotal"]
|
||||
self.Free, _ = table["SwapFree"]
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
return readFile(Procd+"/stat", func(line string) bool {
|
||||
if len(line) > 4 && line[0:4] == "cpu " {
|
||||
parseCpuStat(self, line)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
capacity := len(self.List)
|
||||
if capacity == 0 {
|
||||
capacity = 4
|
||||
}
|
||||
list := make([]Cpu, 0, capacity)
|
||||
|
||||
err := readFile(Procd+"/stat", func(line string) bool {
|
||||
if len(line) > 3 && line[0:3] == "cpu" && line[3] != ' ' {
|
||||
cpu := Cpu{}
|
||||
parseCpuStat(&cpu, line)
|
||||
list = append(list, cpu)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
self.List = list
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
capacity := len(self.List)
|
||||
if capacity == 0 {
|
||||
capacity = 10
|
||||
}
|
||||
fslist := make([]FileSystem, 0, capacity)
|
||||
|
||||
err := readFile(getMountTableFileName(), func(line string) bool {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
fs := FileSystem{}
|
||||
fs.DevName = fields[0]
|
||||
fs.DirName = fields[1]
|
||||
fs.SysTypeName = fields[2]
|
||||
fs.Options = fields[3]
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *ProcList) Get() error {
|
||||
dir, err := os.Open(Procd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
const readAllDirnames = -1 // see os.File.Readdirnames doc
|
||||
|
||||
names, err := dir.Readdirnames(readAllDirnames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
capacity := len(names)
|
||||
list := make([]int, 0, capacity)
|
||||
|
||||
for _, name := range names {
|
||||
if name[0] < '0' || name[0] > '9' {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.Atoi(name)
|
||||
if err == nil {
|
||||
list = append(list, pid)
|
||||
}
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
data, err := readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract the comm value with is surrounded by parentheses.
|
||||
lIdx := bytes.Index(data, []byte("("))
|
||||
rIdx := bytes.LastIndex(data, []byte(")"))
|
||||
if lIdx < 0 || rIdx < 0 || lIdx >= rIdx || rIdx+2 >= len(data) {
|
||||
return fmt.Errorf("failed to extract comm for pid %d from '%v'", pid, string(data))
|
||||
}
|
||||
self.Name = string(data[lIdx+1 : rIdx])
|
||||
|
||||
// Extract the rest of the fields that we are interested in.
|
||||
fields := bytes.Fields(data[rIdx+2:])
|
||||
if len(fields) <= 36 {
|
||||
return fmt.Errorf("expected more stat fields for pid %d from '%v'", pid, string(data))
|
||||
}
|
||||
|
||||
interests := bytes.Join([][]byte{
|
||||
fields[0], // state
|
||||
fields[1], // ppid
|
||||
fields[2], // pgrp
|
||||
fields[4], // tty_nr
|
||||
fields[15], // priority
|
||||
fields[16], // nice
|
||||
fields[36], // processor (last processor executed on)
|
||||
}, []byte(" "))
|
||||
|
||||
var state string
|
||||
_, err = fmt.Fscan(bytes.NewBuffer(interests),
|
||||
&state,
|
||||
&self.Ppid,
|
||||
&self.Pgid,
|
||||
&self.Tty,
|
||||
&self.Priority,
|
||||
&self.Nice,
|
||||
&self.Processor,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse stat fields for pid %d from '%v': %v", pid, string(data), err)
|
||||
}
|
||||
self.State = RunState(state[0])
|
||||
|
||||
// Read /proc/[pid]/status to get the uid, then lookup uid to get username.
|
||||
status, err := getProcStatus(pid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
|
||||
}
|
||||
uids, err := getUIDs(status)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
|
||||
}
|
||||
user, err := user.LookupId(uids[0])
|
||||
if err == nil {
|
||||
self.Username = user.Username
|
||||
} else {
|
||||
self.Username = uids[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "statm")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(contents))
|
||||
|
||||
size, _ := strtoull(fields[0])
|
||||
self.Size = size << 12
|
||||
|
||||
rss, _ := strtoull(fields[1])
|
||||
self.Resident = rss << 12
|
||||
|
||||
share, _ := strtoull(fields[2])
|
||||
self.Share = share << 12
|
||||
|
||||
contents, err = readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields = strings.Fields(string(contents))
|
||||
|
||||
self.MinorFaults, _ = strtoull(fields[10])
|
||||
self.MajorFaults, _ = strtoull(fields[12])
|
||||
self.PageFaults = self.MinorFaults + self.MajorFaults
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(contents))
|
||||
|
||||
user, _ := strtoull(fields[13])
|
||||
sys, _ := strtoull(fields[14])
|
||||
// convert to millis
|
||||
self.User = user * (1000 / system.ticks)
|
||||
self.Sys = sys * (1000 / system.ticks)
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
// convert to millis
|
||||
self.StartTime, _ = strtoull(fields[21])
|
||||
self.StartTime /= system.ticks
|
||||
self.StartTime += system.btime
|
||||
self.StartTime *= 1000
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "cmdline")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(contents)
|
||||
|
||||
var args []string
|
||||
|
||||
for {
|
||||
arg, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
args = append(args, string(chop(arg)))
|
||||
}
|
||||
|
||||
self.List = args
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "environ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.Vars == nil {
|
||||
self.Vars = map[string]string{}
|
||||
}
|
||||
|
||||
pairs := bytes.Split(contents, []byte{0})
|
||||
for _, kv := range pairs {
|
||||
parts := bytes.SplitN(kv, []byte{'='}, 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := string(bytes.TrimSpace(parts[0]))
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
self.Vars[key] = string(bytes.TrimSpace(parts[1]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
fields := map[string]*string{
|
||||
"exe": &self.Name,
|
||||
"cwd": &self.Cwd,
|
||||
"root": &self.Root,
|
||||
}
|
||||
|
||||
for name, field := range fields {
|
||||
val, err := os.Readlink(procFileName(pid, name))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*field = val
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMeminfo() (map[string]uint64, error) {
|
||||
table := map[string]uint64{}
|
||||
|
||||
err := readFile(Procd+"/meminfo", func(line string) bool {
|
||||
fields := strings.Split(line, ":")
|
||||
|
||||
if len(fields) != 2 {
|
||||
return true // skip on errors
|
||||
}
|
||||
|
||||
num := strings.TrimLeft(fields[1], " ")
|
||||
val, err := strtoull(strings.Fields(num)[0])
|
||||
if err != nil {
|
||||
return true // skip on errors
|
||||
}
|
||||
table[fields[0]] = val * 1024 //in bytes
|
||||
|
||||
return true
|
||||
})
|
||||
return table, err
|
||||
}
|
||||
|
||||
func readFile(file string, handler func(string) bool) error {
|
||||
contents, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(bytes.NewBuffer(contents))
|
||||
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if !handler(string(line)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func strtoull(val string) (uint64, error) {
|
||||
return strconv.ParseUint(val, 10, 64)
|
||||
}
|
||||
|
||||
func procFileName(pid int, name string) string {
|
||||
return Procd + "/" + strconv.Itoa(pid) + "/" + name
|
||||
}
|
||||
|
||||
func readProcFile(pid int, name string) ([]byte, error) {
|
||||
path := procFileName(pid, name)
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
|
||||
if err != nil {
|
||||
if perr, ok := err.(*os.PathError); ok {
|
||||
if perr.Err == syscall.ENOENT {
|
||||
return nil, syscall.ESRCH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contents, err
|
||||
}
|
||||
|
||||
// getProcStatus reads /proc/[pid]/status which contains process status
|
||||
// information in human readable form.
|
||||
func getProcStatus(pid int) (map[string]string, error) {
|
||||
status := make(map[string]string, 42)
|
||||
path := filepath.Join(Procd, strconv.Itoa(pid), "status")
|
||||
err := readFile(path, func(line string) bool {
|
||||
fields := strings.SplitN(line, ":", 2)
|
||||
if len(fields) == 2 {
|
||||
status[fields[0]] = strings.TrimSpace(fields[1])
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
return status, err
|
||||
}
|
||||
|
||||
// getUIDs reads the "Uid" value from status and splits it into four values --
|
||||
// real, effective, saved set, and file system UIDs.
|
||||
func getUIDs(status map[string]string) ([]string, error) {
|
||||
uidLine, ok := status["Uid"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Uid not found in proc status")
|
||||
}
|
||||
|
||||
uidStrs := strings.Fields(uidLine)
|
||||
if len(uidStrs) != 4 {
|
||||
return nil, fmt.Errorf("Uid line ('%s') did not contain four values", uidLine)
|
||||
}
|
||||
|
||||
return uidStrs, nil
|
||||
}
|
||||
418
vendor/github.com/elastic/gosigar/sigar_openbsd.go
generated
vendored
Normal file
418
vendor/github.com/elastic/gosigar/sigar_openbsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// Copyright (c) 2016 Jasper Lievisse Adriaanse <j@jasper.la>.
|
||||
|
||||
// +build openbsd
|
||||
|
||||
package gosigar
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/swap.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
//import "github.com/davecgh/go-spew/spew"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Uvmexp struct {
|
||||
pagesize uint32
|
||||
pagemask uint32
|
||||
pageshift uint32
|
||||
npages uint32
|
||||
free uint32
|
||||
active uint32
|
||||
inactive uint32
|
||||
paging uint32
|
||||
wired uint32
|
||||
zeropages uint32
|
||||
reserve_pagedaemon uint32
|
||||
reserve_kernel uint32
|
||||
anonpages uint32
|
||||
vnodepages uint32
|
||||
vtextpages uint32
|
||||
freemin uint32
|
||||
freetarg uint32
|
||||
inactarg uint32
|
||||
wiredmax uint32
|
||||
anonmin uint32
|
||||
vtextmin uint32
|
||||
vnodemin uint32
|
||||
anonminpct uint32
|
||||
vtextmi uint32
|
||||
npct uint32
|
||||
vnodeminpct uint32
|
||||
nswapdev uint32
|
||||
swpages uint32
|
||||
swpginuse uint32
|
||||
swpgonly uint32
|
||||
nswget uint32
|
||||
nanon uint32
|
||||
nanonneeded uint32
|
||||
nfreeanon uint32
|
||||
faults uint32
|
||||
traps uint32
|
||||
intrs uint32
|
||||
swtch uint32
|
||||
softs uint32
|
||||
syscalls uint32
|
||||
pageins uint32
|
||||
obsolete_swapins uint32
|
||||
obsolete_swapouts uint32
|
||||
pgswapin uint32
|
||||
pgswapout uint32
|
||||
forks uint32
|
||||
forks_ppwait uint32
|
||||
forks_sharevm uint32
|
||||
pga_zerohit uint32
|
||||
pga_zeromiss uint32
|
||||
zeroaborts uint32
|
||||
fltnoram uint32
|
||||
fltnoanon uint32
|
||||
fltpgwait uint32
|
||||
fltpgrele uint32
|
||||
fltrelck uint32
|
||||
fltrelckok uint32
|
||||
fltanget uint32
|
||||
fltanretry uint32
|
||||
fltamcopy uint32
|
||||
fltnamap uint32
|
||||
fltnomap uint32
|
||||
fltlget uint32
|
||||
fltget uint32
|
||||
flt_anon uint32
|
||||
flt_acow uint32
|
||||
flt_obj uint32
|
||||
flt_prcopy uint32
|
||||
flt_przero uint32
|
||||
pdwoke uint32
|
||||
pdrevs uint32
|
||||
pdswout uint32
|
||||
pdfreed uint32
|
||||
pdscans uint32
|
||||
pdanscan uint32
|
||||
pdobscan uint32
|
||||
pdreact uint32
|
||||
pdbusy uint32
|
||||
pdpageouts uint32
|
||||
pdpending uint32
|
||||
pddeact uint32
|
||||
pdreanon uint32
|
||||
pdrevnode uint32
|
||||
pdrevtext uint32
|
||||
fpswtch uint32
|
||||
kmapent uint32
|
||||
}
|
||||
|
||||
type Bcachestats struct {
|
||||
numbufs uint64
|
||||
numbufpages uint64
|
||||
numdirtypages uint64
|
||||
numcleanpages uint64
|
||||
pendingwrites uint64
|
||||
pendingreads uint64
|
||||
numwrites uint64
|
||||
numreads uint64
|
||||
cachehits uint64
|
||||
busymapped uint64
|
||||
dmapages uint64
|
||||
highpages uint64
|
||||
delwribufs uint64
|
||||
kvaslots uint64
|
||||
kvaslots_avail uint64
|
||||
}
|
||||
|
||||
type Swapent struct {
|
||||
se_dev C.dev_t
|
||||
se_flags int32
|
||||
se_nblks int32
|
||||
se_inuse int32
|
||||
se_priority int32
|
||||
sw_path []byte
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
num, err := syscall.Getfsstat(nil, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]syscall.Statfs_t, num)
|
||||
|
||||
_, err = syscall.Getfsstat(buf, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fslist := make([]FileSystem, 0, num)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
fs := FileSystem{}
|
||||
|
||||
fs.DirName = bytePtrToString(&buf[i].F_mntonname[0])
|
||||
fs.DevName = bytePtrToString(&buf[i].F_mntfromname[0])
|
||||
fs.SysTypeName = bytePtrToString(&buf[i].F_fstypename[0])
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
}
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = uint64(stat.F_blocks) * uint64(stat.F_bsize)
|
||||
self.Free = uint64(stat.F_bfree) * uint64(stat.F_bsize)
|
||||
self.Avail = uint64(stat.F_bavail) * uint64(stat.F_bsize)
|
||||
self.Used = self.Total - self.Free
|
||||
self.Files = stat.F_files
|
||||
self.FreeFiles = stat.F_ffree
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
avg := []C.double{0, 0, 0}
|
||||
|
||||
C.getloadavg(&avg[0], C.int(len(avg)))
|
||||
|
||||
self.One = float64(avg[0])
|
||||
self.Five = float64(avg[1])
|
||||
self.Fifteen = float64(avg[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
tv := syscall.Timeval{}
|
||||
mib := [2]int32{C.CTL_KERN, C.KERN_BOOTTIME}
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now perform the actual sysctl(3) call, storing the result in tv
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&tv)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
n := uintptr(0)
|
||||
|
||||
var uvmexp Uvmexp
|
||||
mib := [2]int32{C.CTL_VM, C.VM_UVMEXP}
|
||||
n = uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&uvmexp)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var bcachestats Bcachestats
|
||||
mib3 := [3]int32{C.CTL_VFS, C.VFS_GENERIC, C.VFS_BCACHESTAT}
|
||||
n = uintptr(0)
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, uintptr(unsafe.Pointer(&bcachestats)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.Total = uint64(uvmexp.npages) << uvmexp.pageshift
|
||||
self.Used = uint64(uvmexp.npages-uvmexp.free) << uvmexp.pageshift
|
||||
self.Free = uint64(uvmexp.free) << uvmexp.pageshift
|
||||
|
||||
self.ActualFree = self.Free + (uint64(bcachestats.numbufpages) << uvmexp.pageshift)
|
||||
self.ActualUsed = self.Used - (uint64(bcachestats.numbufpages) << uvmexp.pageshift)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
nswap := C.swapctl(C.SWAP_NSWAP, unsafe.Pointer(uintptr(0)), 0)
|
||||
|
||||
// If there are no swap devices, nothing to do here.
|
||||
if nswap == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
swdev := make([]Swapent, nswap)
|
||||
|
||||
rnswap := C.swapctl(C.SWAP_STATS, unsafe.Pointer(&swdev[0]), nswap)
|
||||
if rnswap == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < int(nswap); i++ {
|
||||
if swdev[i].se_flags&C.SWF_ENABLE == 2 {
|
||||
self.Used = self.Used + uint64(swdev[i].se_inuse/(1024/C.DEV_BSIZE))
|
||||
self.Total = self.Total + uint64(swdev[i].se_nblks/(1024/C.DEV_BSIZE))
|
||||
}
|
||||
}
|
||||
|
||||
self.Free = self.Total - self.Used
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
load := [C.CPUSTATES]C.long{C.CP_USER, C.CP_NICE, C.CP_SYS, C.CP_INTR, C.CP_IDLE}
|
||||
|
||||
mib := [2]int32{C.CTL_KERN, C.KERN_CPTIME}
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&load)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.User = uint64(load[0])
|
||||
self.Nice = uint64(load[1])
|
||||
self.Sys = uint64(load[2])
|
||||
self.Irq = uint64(load[3])
|
||||
self.Idle = uint64(load[4])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
mib := [2]int32{C.CTL_HW, C.HW_NCPU}
|
||||
var ncpu int
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now perform the actual sysctl(3) call, storing the result in ncpu
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&ncpu)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
load := [C.CPUSTATES]C.long{C.CP_USER, C.CP_NICE, C.CP_SYS, C.CP_INTR, C.CP_IDLE}
|
||||
|
||||
self.List = make([]Cpu, ncpu)
|
||||
for curcpu := range self.List {
|
||||
sysctlCptime(ncpu, curcpu, &load)
|
||||
fillCpu(&self.List[curcpu], load)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcList) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func fillCpu(cpu *Cpu, load [C.CPUSTATES]C.long) {
|
||||
cpu.User = uint64(load[0])
|
||||
cpu.Nice = uint64(load[1])
|
||||
cpu.Sys = uint64(load[2])
|
||||
cpu.Irq = uint64(load[3])
|
||||
cpu.Idle = uint64(load[4])
|
||||
}
|
||||
|
||||
func sysctlCptime(ncpu int, curcpu int, load *[C.CPUSTATES]C.long) error {
|
||||
var mib []int32
|
||||
|
||||
// Use the correct mib based on the number of CPUs and fill out the
|
||||
// current CPU number in case of SMP. (0 indexed cf. self.List)
|
||||
if ncpu == 0 {
|
||||
mib = []int32{C.CTL_KERN, C.KERN_CPTIME}
|
||||
} else {
|
||||
mib = []int32{C.CTL_KERN, C.KERN_CPTIME2, int32(curcpu)}
|
||||
}
|
||||
|
||||
len := len(mib)
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), uintptr(unsafe.Pointer(load)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
71
vendor/github.com/elastic/gosigar/sigar_stub.go
generated
vendored
Normal file
71
vendor/github.com/elastic/gosigar/sigar_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// +build !darwin,!freebsd,!linux,!openbsd,!windows
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func (c *Cpu) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (l *LoadAverage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (m *Mem) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (s *Swap) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (f *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcTime) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcState) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcExe) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcMem) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcFDUsage) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcEnv) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcList) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcArgs) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Rusage) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
69
vendor/github.com/elastic/gosigar/sigar_unix.go
generated
vendored
Normal file
69
vendor/github.com/elastic/gosigar/sigar_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
// +build darwin freebsd linux
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = uint64(stat.Blocks) * uint64(stat.Bsize)
|
||||
self.Free = uint64(stat.Bfree) * uint64(stat.Bsize)
|
||||
self.Avail = uint64(stat.Bavail) * uint64(stat.Bsize)
|
||||
self.Used = self.Total - self.Free
|
||||
self.Files = stat.Files
|
||||
self.FreeFiles = uint64(stat.Ffree)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Rusage) Get(who int) error {
|
||||
ru, err := getResourceUsage(who)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uTime := convertRtimeToDur(ru.Utime)
|
||||
sTime := convertRtimeToDur(ru.Stime)
|
||||
|
||||
r.Utime = uTime
|
||||
r.Stime = sTime
|
||||
r.Maxrss = int64(ru.Maxrss)
|
||||
r.Ixrss = int64(ru.Ixrss)
|
||||
r.Idrss = int64(ru.Idrss)
|
||||
r.Isrss = int64(ru.Isrss)
|
||||
r.Minflt = int64(ru.Minflt)
|
||||
r.Majflt = int64(ru.Majflt)
|
||||
r.Nswap = int64(ru.Nswap)
|
||||
r.Inblock = int64(ru.Inblock)
|
||||
r.Oublock = int64(ru.Oublock)
|
||||
r.Msgsnd = int64(ru.Msgsnd)
|
||||
r.Msgrcv = int64(ru.Msgrcv)
|
||||
r.Nsignals = int64(ru.Nsignals)
|
||||
r.Nvcsw = int64(ru.Nvcsw)
|
||||
r.Nivcsw = int64(ru.Nivcsw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getResourceUsage(who int) (unix.Rusage, error) {
|
||||
r := unix.Rusage{}
|
||||
err := unix.Getrusage(who, &r)
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
func convertRtimeToDur(t unix.Timeval) time.Duration {
|
||||
return time.Duration(t.Nano())
|
||||
}
|
||||
22
vendor/github.com/elastic/gosigar/sigar_util.go
generated
vendored
Normal file
22
vendor/github.com/elastic/gosigar/sigar_util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func bytePtrToString(ptr *int8) string {
|
||||
bytes := (*[10000]byte)(unsafe.Pointer(ptr))
|
||||
|
||||
n := 0
|
||||
for bytes[n] != 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
return string(bytes[0:n])
|
||||
}
|
||||
|
||||
func chop(buf []byte) []byte {
|
||||
return buf[0 : len(buf)-1]
|
||||
}
|
||||
437
vendor/github.com/elastic/gosigar/sigar_windows.go
generated
vendored
Normal file
437
vendor/github.com/elastic/gosigar/sigar_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/elastic/gosigar/sys/windows"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Win32_Process represents a process on the Windows operating system. If
|
||||
// additional fields are added here (that match the Windows struct) they will
|
||||
// automatically be populated when calling getWin32Process.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394372(v=vs.85).aspx
|
||||
type Win32_Process struct {
|
||||
CommandLine string
|
||||
}
|
||||
|
||||
// Win32_OperatingSystem WMI class represents a Windows-based operating system
|
||||
// installed on a computer.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394239(v=vs.85).aspx
|
||||
type Win32_OperatingSystem struct {
|
||||
LastBootUpTime time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
// version is Windows version of the host OS.
|
||||
version = windows.GetWindowsVersion()
|
||||
|
||||
// processQueryLimitedInfoAccess is set to PROCESS_QUERY_INFORMATION for Windows
|
||||
// 2003 and XP where PROCESS_QUERY_LIMITED_INFORMATION is unknown. For all newer
|
||||
// OS versions it is set to PROCESS_QUERY_LIMITED_INFORMATION.
|
||||
processQueryLimitedInfoAccess = windows.PROCESS_QUERY_LIMITED_INFORMATION
|
||||
|
||||
// bootTime is the time when the OS was last booted. This value may be nil
|
||||
// on operating systems that do not support the WMI query used to obtain it.
|
||||
bootTime *time.Time
|
||||
bootTimeLock sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
// PROCESS_QUERY_LIMITED_INFORMATION cannot be used on 2003 or XP.
|
||||
processQueryLimitedInfoAccess = syscall.PROCESS_QUERY_INFORMATION
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
// Minimum supported OS is Windows Vista.
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
bootTimeLock.Lock()
|
||||
defer bootTimeLock.Unlock()
|
||||
if bootTime == nil {
|
||||
os, err := getWin32OperatingSystem()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get boot time using WMI")
|
||||
}
|
||||
bootTime = &os.LastBootUpTime
|
||||
}
|
||||
|
||||
self.Length = time.Since(*bootTime).Seconds()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
memoryStatusEx, err := windows.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
||||
}
|
||||
|
||||
self.Total = memoryStatusEx.TotalPhys
|
||||
self.Free = memoryStatusEx.AvailPhys
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free
|
||||
self.ActualUsed = self.Used
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
memoryStatusEx, err := windows.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
||||
}
|
||||
|
||||
self.Total = memoryStatusEx.TotalPageFile
|
||||
self.Free = memoryStatusEx.AvailPageFile
|
||||
self.Used = self.Total - self.Free
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
idle, kernel, user, err := windows.GetSystemTimes()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSystemTimes failed")
|
||||
}
|
||||
|
||||
// CPU times are reported in milliseconds by gosigar.
|
||||
self.Idle = uint64(idle / time.Millisecond)
|
||||
self.Sys = uint64(kernel / time.Millisecond)
|
||||
self.User = uint64(user / time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
cpus, err := windows.NtQuerySystemProcessorPerformanceInformation()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NtQuerySystemProcessorPerformanceInformation failed")
|
||||
}
|
||||
|
||||
self.List = make([]Cpu, 0, len(cpus))
|
||||
for _, cpu := range cpus {
|
||||
self.List = append(self.List, Cpu{
|
||||
Idle: uint64(cpu.IdleTime / time.Millisecond),
|
||||
Sys: uint64(cpu.KernelTime / time.Millisecond),
|
||||
User: uint64(cpu.UserTime / time.Millisecond),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
drives, err := windows.GetLogicalDriveStrings()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetLogicalDriveStrings failed")
|
||||
}
|
||||
|
||||
for _, drive := range drives {
|
||||
dt, err := windows.GetDriveType(drive)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetDriveType failed")
|
||||
}
|
||||
|
||||
self.List = append(self.List, FileSystem{
|
||||
DirName: drive,
|
||||
DevName: drive,
|
||||
TypeName: dt.String(),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a list of all process identifiers (PIDs) in the system.
|
||||
func (self *ProcList) Get() error {
|
||||
pids, err := windows.EnumProcesses()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "EnumProcesses failed")
|
||||
}
|
||||
|
||||
// Convert uint32 PIDs to int.
|
||||
self.List = make([]int, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
self.List = append(self.List, int(pid))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
var errs []error
|
||||
|
||||
var err error
|
||||
self.Name, err = getProcName(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getProcName failed"))
|
||||
}
|
||||
|
||||
self.State, err = getProcStatus(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getProcStatus failed"))
|
||||
}
|
||||
|
||||
self.Ppid, err = getParentPid(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getParentPid failed"))
|
||||
}
|
||||
|
||||
self.Username, err = getProcCredName(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getProcCredName failed"))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
errStrs := make([]string, 0, len(errs))
|
||||
for _, e := range errs {
|
||||
errStrs = append(errStrs, e.Error())
|
||||
}
|
||||
return errors.New(strings.Join(errStrs, "; "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getProcName returns the process name associated with the PID.
|
||||
func getProcName(pid int) (string, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
filename, err := windows.GetProcessImageFileName(handle)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetProcessImageFileName failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return filepath.Base(filename), nil
|
||||
}
|
||||
|
||||
// getProcStatus returns the status of a process.
|
||||
func getProcStatus(pid int) (RunState, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
var exitCode uint32
|
||||
err = syscall.GetExitCodeProcess(handle, &exitCode)
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "GetExitCodeProcess failed for pid=%v")
|
||||
}
|
||||
|
||||
if exitCode == 259 { //still active
|
||||
return RunStateRun, nil
|
||||
}
|
||||
return RunStateSleep, nil
|
||||
}
|
||||
|
||||
// getParentPid returns the parent process ID of a process.
|
||||
func getParentPid(pid int) (int, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
procInfo, err := windows.NtQueryProcessBasicInformation(handle)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "NtQueryProcessBasicInformation failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return int(procInfo.InheritedFromUniqueProcessID), nil
|
||||
}
|
||||
|
||||
func getProcCredName(pid int) (string, error) {
|
||||
handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
// Find process token via win32.
|
||||
var token syscall.Token
|
||||
err = syscall.OpenProcessToken(handle, syscall.TOKEN_QUERY, &token)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcessToken failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
// Find the token user.
|
||||
tokenUser, err := token.GetTokenUser()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetTokenInformation failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
// Close token to prevent handle leaks.
|
||||
err = token.Close()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed while closing process token handle for pid=%v", pid)
|
||||
}
|
||||
|
||||
// Look up domain account by SID.
|
||||
account, domain, _, err := tokenUser.User.Sid.LookupAccount("")
|
||||
if err != nil {
|
||||
sid, sidErr := tokenUser.User.Sid.String()
|
||||
if sidErr != nil {
|
||||
return "", errors.Wrapf(err, "failed while looking up account name for pid=%v", pid)
|
||||
}
|
||||
return "", errors.Wrapf(err, "failed while looking up account name for SID=%v of pid=%v", sid, pid)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`%s\%s`, domain, account), nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
counters, err := windows.GetProcessMemoryInfo(handle)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetProcessMemoryInfo failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
self.Resident = uint64(counters.WorkingSetSize)
|
||||
self.Size = uint64(counters.PrivateUsage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
cpu, err := getProcTimes(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Windows epoch times are expressed as time elapsed since midnight on
|
||||
// January 1, 1601 at Greenwich, England. This converts the Filetime to
|
||||
// unix epoch in milliseconds.
|
||||
self.StartTime = uint64(cpu.CreationTime.Nanoseconds() / 1e6)
|
||||
|
||||
// Convert to millis.
|
||||
self.User = uint64(windows.FiletimeToDuration(&cpu.UserTime).Nanoseconds() / 1e6)
|
||||
self.Sys = uint64(windows.FiletimeToDuration(&cpu.KernelTime).Nanoseconds() / 1e6)
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcTimes(pid int) (*syscall.Rusage, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
var cpu syscall.Rusage
|
||||
if err := syscall.GetProcessTimes(handle, &cpu.CreationTime, &cpu.ExitTime, &cpu.KernelTime, &cpu.UserTime); err != nil {
|
||||
return nil, errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return &cpu, nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
// The minimum supported client for Win32_Process is Windows Vista.
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
process, err := getWin32Process(int32(pid))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "ProcArgs failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
self.List = []string{process.CommandLine}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes, err := windows.GetDiskFreeSpaceEx(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetDiskFreeSpaceEx failed")
|
||||
}
|
||||
|
||||
self.Total = totalNumberOfBytes
|
||||
self.Free = totalNumberOfFreeBytes
|
||||
self.Used = self.Total - self.Free
|
||||
self.Avail = freeBytesAvailable
|
||||
return nil
|
||||
}
|
||||
|
||||
// getWin32Process gets information about the process with the given process ID.
|
||||
// It uses a WMI query to get the information from the local system.
|
||||
func getWin32Process(pid int32) (Win32_Process, error) {
|
||||
var dst []Win32_Process
|
||||
query := fmt.Sprintf("WHERE ProcessId = %d", pid)
|
||||
q := wmi.CreateQuery(&dst, query)
|
||||
err := wmi.Query(q, &dst)
|
||||
if err != nil {
|
||||
return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: %v", query, err)
|
||||
}
|
||||
if len(dst) < 1 {
|
||||
return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: Process not found", query)
|
||||
}
|
||||
return dst[0], nil
|
||||
}
|
||||
|
||||
func getWin32OperatingSystem() (Win32_OperatingSystem, error) {
|
||||
var dst []Win32_OperatingSystem
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(q, &dst)
|
||||
if err != nil {
|
||||
return Win32_OperatingSystem{}, errors.Wrap(err, "wmi query for Win32_OperatingSystem failed")
|
||||
}
|
||||
if len(dst) != 1 {
|
||||
return Win32_OperatingSystem{}, errors.New("wmi query for Win32_OperatingSystem failed")
|
||||
}
|
||||
return dst[0], nil
|
||||
}
|
||||
|
||||
func (self *Rusage) Get(who int) error {
|
||||
if who != 0 {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
pid := os.Getpid()
|
||||
cpu, err := getProcTimes(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Utime = windows.FiletimeToDuration(&cpu.UserTime)
|
||||
self.Stime = windows.FiletimeToDuration(&cpu.KernelTime)
|
||||
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue