aboutsummaryrefslogtreecommitdiffstats
path: root/eth/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'eth/api.go')
-rw-r--r--eth/api.go492
1 files changed, 372 insertions, 120 deletions
diff --git a/eth/api.go b/eth/api.go
index a1630e2d1..0fa855f15 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -42,8 +42,10 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/miner"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
- rpc "github.com/ethereum/go-ethereum/rpc/v2"
+ "github.com/ethereum/go-ethereum/rpc"
)
const (
@@ -51,6 +53,40 @@ const (
defaultGas = uint64(90000)
)
+// blockByNumber is a commonly used helper function which retrieves and returns
+// the block for the given block number, capable of handling two special blocks:
+// rpc.LatestBlockNumber adn rpc.PendingBlockNumber. It returns nil when no block
+// could be found.
+func blockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block {
+ // Pending block is only known by the miner
+ if blockNr == rpc.PendingBlockNumber {
+ return m.PendingBlock()
+ }
+ // Otherwise resolve and return the block
+ if blockNr == rpc.LatestBlockNumber {
+ return bc.CurrentBlock()
+ }
+ return bc.GetBlockByNumber(uint64(blockNr))
+}
+
+// stateAndBlockByNumber is a commonly used helper function which retrieves and
+// returns the state and containing block for the given block number, capable of
+// handling two special states: rpc.LatestBlockNumber adn rpc.PendingBlockNumber.
+// It returns nil when no block or state could be found.
+func stateAndBlockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber, chainDb ethdb.Database) (*state.StateDB, *types.Block, error) {
+ // Pending state is only known by the miner
+ if blockNr == rpc.PendingBlockNumber {
+ return m.PendingState(), m.PendingBlock(), nil
+ }
+ // Otherwise resolve the block number and return its state
+ block := blockByNumber(m, bc, blockNr)
+ if block == nil {
+ return nil, nil, nil
+ }
+ stateDb, err := state.New(block.Root(), chainDb)
+ return stateDb, block, err
+}
+
// PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct {
@@ -212,6 +248,39 @@ func NewPublicTxPoolAPI(e *Ethereum) *PublicTxPoolAPI {
return &PublicTxPoolAPI{e}
}
+// Content returns the transactions contained within the transaction pool.
+func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTransaction {
+ content := map[string]map[string]map[string][]*RPCTransaction{
+ "pending": make(map[string]map[string][]*RPCTransaction),
+ "queued": make(map[string]map[string][]*RPCTransaction),
+ }
+ pending, queue := s.e.TxPool().Content()
+
+ // Flatten the pending transactions
+ for account, batches := range pending {
+ dump := make(map[string][]*RPCTransaction)
+ for nonce, txs := range batches {
+ nonce := fmt.Sprintf("%d", nonce)
+ for _, tx := range txs {
+ dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
+ }
+ }
+ content["pending"][account.Hex()] = dump
+ }
+ // Flatten the queued transactions
+ for account, batches := range queue {
+ dump := make(map[string][]*RPCTransaction)
+ for nonce, txs := range batches {
+ nonce := fmt.Sprintf("%d", nonce)
+ for _, tx := range txs {
+ dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
+ }
+ }
+ content["queued"][account.Hex()] = dump
+ }
+ return content
+}
+
// Status returns the number of pending and queued transaction in the pool.
func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber {
pending, queue := s.e.TxPool().Stats()
@@ -221,6 +290,47 @@ func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber {
}
}
+// Inspect retrieves the content of the transaction pool and flattens it into an
+// easily inspectable list.
+func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string {
+ content := map[string]map[string]map[string][]string{
+ "pending": make(map[string]map[string][]string),
+ "queued": make(map[string]map[string][]string),
+ }
+ pending, queue := s.e.TxPool().Content()
+
+ // Define a formatter to flatten a transaction into a string
+ var format = func(tx *types.Transaction) string {
+ if to := tx.To(); to != nil {
+ return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
+ }
+ return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice())
+ }
+ // Flatten the pending transactions
+ for account, batches := range pending {
+ dump := make(map[string][]string)
+ for nonce, txs := range batches {
+ nonce := fmt.Sprintf("%d", nonce)
+ for _, tx := range txs {
+ dump[nonce] = append(dump[nonce], format(tx))
+ }
+ }
+ content["pending"][account.Hex()] = dump
+ }
+ // Flatten the queued transactions
+ for account, batches := range queue {
+ dump := make(map[string][]string)
+ for nonce, txs := range batches {
+ nonce := fmt.Sprintf("%d", nonce)
+ for _, tx := range txs {
+ dump[nonce] = append(dump[nonce], format(tx))
+ }
+ }
+ content["queued"][account.Hex()] = dump
+ }
+ return content
+}
+
// PublicAccountAPI provides an API to access accounts managed by this node.
// It offers only methods that can retrieve accounts.
type PublicAccountAPI struct {
@@ -293,11 +403,12 @@ type PublicBlockChainAPI struct {
chainDb ethdb.Database
eventMux *event.TypeMux
am *accounts.Manager
+ miner *miner.Miner
}
// NewPublicBlockChainAPI creates a new Etheruem blockchain API.
-func NewPublicBlockChainAPI(bc *core.BlockChain, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
- return &PublicBlockChainAPI{bc: bc, chainDb: chainDb, eventMux: eventMux, am: am}
+func NewPublicBlockChainAPI(bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
+ return &PublicBlockChainAPI{bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am}
}
// BlockNumber returns the block number of the chain head.
@@ -305,36 +416,29 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int {
return s.bc.CurrentHeader().Number
}
-// GetBalance returns the amount of wei for the given address in the state of the given block number.
-// When block number equals rpc.LatestBlockNumber the current block is used.
+// GetBalance returns the amount of wei for the given address in the state of the
+// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
+// block numbers are also allowed.
func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) {
- block := blockByNumber(s.bc, blockNr)
- if block == nil {
- return nil, nil
- }
-
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
return nil, err
}
return state.GetBalance(address), nil
}
-// blockByNumber is a commonly used helper function which retrieves and returns the block for the given block number. It
-// returns nil when no block could be found.
-func blockByNumber(bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block {
- if blockNr == rpc.LatestBlockNumber {
- return bc.CurrentBlock()
- }
-
- return bc.GetBlockByNumber(uint64(blockNr))
-}
-
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
- if block := blockByNumber(s.bc, blockNr); block != nil {
- return s.rpcOutputBlock(block, true, fullTx)
+ if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
+ response, err := s.rpcOutputBlock(block, true, fullTx)
+ if err == nil && blockNr == rpc.PendingBlockNumber {
+ // Pending blocks need to nil out a few fields
+ for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} {
+ response[field] = nil
+ }
+ }
+ return response, err
}
return nil, nil
}
@@ -351,11 +455,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(blockHash common.Hash, fullTx bool)
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) {
- if blockNr == rpc.PendingBlockNumber {
- return nil, nil
- }
-
- if block := blockByNumber(s.bc, blockNr); block != nil {
+ if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) {
glog.V(logger.Debug).Infof("uncle block on index %d not found for block #%d", index.Int(), blockNr)
@@ -384,11 +484,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(blockHash common.Hash,
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(blockNr rpc.BlockNumber) *rpc.HexNumber {
- if blockNr == rpc.PendingBlockNumber {
- return rpc.NewHexNumber(0)
- }
-
- if block := blockByNumber(s.bc, blockNr); block != nil {
+ if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Uncles()))
}
return nil
@@ -428,38 +524,26 @@ func (s *PublicBlockChainAPI) NewBlocks(args NewBlocksArgs) (rpc.Subscription, e
// GetCode returns the code stored at the given address in the state for the given block number.
func (s *PublicBlockChainAPI) GetCode(address common.Address, blockNr rpc.BlockNumber) (string, error) {
- return s.GetData(address, blockNr)
-}
-
-// GetData returns the data stored at the given address in the state for the given block number.
-func (s *PublicBlockChainAPI) GetData(address common.Address, blockNr rpc.BlockNumber) (string, error) {
- if block := blockByNumber(s.bc, blockNr); block != nil {
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "", err
- }
- res := state.GetCode(address)
- if len(res) == 0 { // backwards compatibility
- return "0x", nil
- }
- return common.ToHex(res), nil
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
+ return "", err
}
-
- return "0x", nil
+ res := state.GetCode(address)
+ if len(res) == 0 { // backwards compatibility
+ return "0x", nil
+ }
+ return common.ToHex(res), nil
}
-// GetStorageAt returns the storage from the state at the given address, key and block number.
+// GetStorageAt returns the storage from the state at the given address, key and
+// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
+// numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(address common.Address, key string, blockNr rpc.BlockNumber) (string, error) {
- if block := blockByNumber(s.bc, blockNr); block != nil {
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "", err
- }
-
- return state.GetState(address, common.HexToHash(key)).Hex(), nil
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
+ return "0x", err
}
-
- return "0x", nil
+ return state.GetState(address, common.HexToHash(key)).Hex(), nil
}
// callmsg is the message type used for call transations.
@@ -490,55 +574,51 @@ type CallArgs struct {
}
func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) {
- if block := blockByNumber(s.bc, blockNr); block != nil {
- stateDb, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "0x", nil, err
- }
-
- stateDb = stateDb.Copy()
- var from *state.StateObject
- if args.From == (common.Address{}) {
- accounts, err := s.am.Accounts()
- if err != nil || len(accounts) == 0 {
- from = stateDb.GetOrNewStateObject(common.Address{})
- } else {
- from = stateDb.GetOrNewStateObject(accounts[0].Address)
- }
+ // Fetch the state associated with the block number
+ stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if stateDb == nil || err != nil {
+ return "0x", nil, err
+ }
+ stateDb = stateDb.Copy()
+
+ // Retrieve the account state object to interact with
+ var from *state.StateObject
+ if args.From == (common.Address{}) {
+ accounts, err := s.am.Accounts()
+ if err != nil || len(accounts) == 0 {
+ from = stateDb.GetOrNewStateObject(common.Address{})
} else {
- from = stateDb.GetOrNewStateObject(args.From)
- }
-
- from.SetBalance(common.MaxBig)
-
- msg := callmsg{
- from: from,
- to: &args.To,
- gas: args.Gas.BigInt(),
- gasPrice: args.GasPrice.BigInt(),
- value: args.Value.BigInt(),
- data: common.FromHex(args.Data),
- }
-
- if msg.gas.Cmp(common.Big0) == 0 {
- msg.gas = big.NewInt(50000000)
- }
-
- if msg.gasPrice.Cmp(common.Big0) == 0 {
- msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
+ from = stateDb.GetOrNewStateObject(accounts[0].Address)
}
+ } else {
+ from = stateDb.GetOrNewStateObject(args.From)
+ }
+ from.SetBalance(common.MaxBig)
- header := s.bc.CurrentBlock().Header()
- vmenv := core.NewEnv(stateDb, s.bc, msg, header)
- gp := new(core.GasPool).AddGas(common.MaxBig)
- res, gas, err := core.ApplyMessage(vmenv, msg, gp)
- if len(res) == 0 { // backwards compatability
- return "0x", gas, err
- }
- return common.ToHex(res), gas, err
+ // Assemble the CALL invocation
+ msg := callmsg{
+ from: from,
+ to: &args.To,
+ gas: args.Gas.BigInt(),
+ gasPrice: args.GasPrice.BigInt(),
+ value: args.Value.BigInt(),
+ data: common.FromHex(args.Data),
}
+ if msg.gas.Cmp(common.Big0) == 0 {
+ msg.gas = big.NewInt(50000000)
+ }
+ if msg.gasPrice.Cmp(common.Big0) == 0 {
+ msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
+ }
+ // Execute the call and return
+ vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header())
+ gp := new(core.GasPool).AddGas(common.MaxBig)
- return "0x", common.Big0, nil
+ res, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ if len(res) == 0 { // backwards compatibility
+ return "0x", gas, err
+ }
+ return common.ToHex(res), gas, err
}
// Call executes the given transaction on the state for the given block number.
@@ -684,19 +764,21 @@ type PublicTransactionPoolAPI struct {
eventMux *event.TypeMux
chainDb ethdb.Database
bc *core.BlockChain
+ miner *miner.Miner
am *accounts.Manager
txPool *core.TxPool
txMu sync.Mutex
}
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
-func NewPublicTransactionPoolAPI(txPool *core.TxPool, chainDb ethdb.Database, eventMux *event.TypeMux, bc *core.BlockChain, am *accounts.Manager) *PublicTransactionPoolAPI {
+func NewPublicTransactionPoolAPI(txPool *core.TxPool, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, bc *core.BlockChain, am *accounts.Manager) *PublicTransactionPoolAPI {
return &PublicTransactionPoolAPI{
eventMux: eventMux,
chainDb: chainDb,
bc: bc,
am: am,
txPool: txPool,
+ miner: m,
}
}
@@ -720,14 +802,9 @@ func getTransaction(chainDb ethdb.Database, txPool *core.TxPool, txHash common.H
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(blockNr rpc.BlockNumber) *rpc.HexNumber {
- if blockNr == rpc.PendingBlockNumber {
- return rpc.NewHexNumber(0)
- }
-
- if block := blockByNumber(s.bc, blockNr); block != nil {
+ if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Transactions()))
}
-
return nil
}
@@ -741,7 +818,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(blockHash comm
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) {
- if block := blockByNumber(s.bc, blockNr); block != nil {
+ if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
return newRPCTransactionFromBlockIndex(block, index.Int())
}
return nil, nil
@@ -757,13 +834,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(blockHash c
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (s *PublicTransactionPoolAPI) GetTransactionCount(address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) {
- block := blockByNumber(s.bc, blockNr)
- if block == nil {
- return nil, nil
- }
-
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
return nil, err
}
return rpc.NewHexNumber(state.GetNonce(address)), nil
@@ -1256,6 +1328,16 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
return true, nil
}
+func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
+ for _, b := range bs {
+ if !chain.HasBlock(b.Hash()) {
+ return false
+ }
+ }
+
+ return true
+}
+
// ImportChain imports a blockchain from a local file.
func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
// Make sure the can access the file to import
@@ -1284,6 +1366,11 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
if len(blocks) == 0 {
break
}
+
+ if hasAllBlocks(api.eth.BlockChain(), blocks) {
+ blocks = blocks[:0]
+ continue
+ }
// Import the batch and reset the buffer
if _, err := api.eth.BlockChain().InsertChain(blocks); err != nil {
return false, fmt.Errorf("batch %d: failed to insert: %v", batch, err)
@@ -1403,3 +1490,168 @@ func (api *PrivateDebugAPI) ProcessBlock(number uint64) (bool, error) {
func (api *PrivateDebugAPI) SetHead(number uint64) {
api.eth.BlockChain().SetHead(number)
}
+
+// StructLogRes stores a structured log emitted by the evm while replaying a
+// transaction in debug mode
+type structLogRes struct {
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas *big.Int `json:"gas"`
+ GasCost *big.Int `json:"gasCost"`
+ Error error `json:"error"`
+ Stack []string `json:"stack"`
+ Memory map[string]string `json:"memory"`
+ Storage map[string]string `json:"storage"`
+}
+
+// TransactionExecutionRes groups all structured logs emitted by the evm
+// while replaying a transaction in debug mode as well as the amount of
+// gas used and the return value
+type TransactionExecutionResult struct {
+ Gas *big.Int `json:"gas"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []structLogRes `json:"structLogs"`
+}
+
+func (s *PrivateDebugAPI) doReplayTransaction(txHash common.Hash) ([]vm.StructLog, []byte, *big.Int, error) {
+ // Retrieve the tx from the chain
+ tx, _, blockIndex, _ := core.GetTransaction(s.eth.ChainDb(), txHash)
+
+ if tx == nil {
+ return nil, nil, nil, fmt.Errorf("Transaction not found")
+ }
+
+ block := s.eth.BlockChain().GetBlockByNumber(blockIndex - 1)
+ if block == nil {
+ return nil, nil, nil, fmt.Errorf("Unable to retrieve prior block")
+ }
+
+ // Create the state database
+ stateDb, err := state.New(block.Root(), s.eth.ChainDb())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ txFrom, err := tx.From()
+
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Unable to create transaction sender")
+ }
+ from := stateDb.GetOrNewStateObject(txFrom)
+ msg := callmsg{
+ from: from,
+ to: tx.To(),
+ gas: tx.Gas(),
+ gasPrice: tx.GasPrice(),
+ value: tx.Value(),
+ data: tx.Data(),
+ }
+
+ vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header())
+ gp := new(core.GasPool).AddGas(block.GasLimit())
+ vm.GenerateStructLogs = true
+ defer func() { vm.GenerateStructLogs = false }()
+
+ ret, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Error executing transaction %v", err)
+ }
+
+ return vmenv.StructLogs(), ret, gas, nil
+}
+
+// Executes a transaction and returns the structured logs of the evm
+// gathered during the execution
+func (s *PrivateDebugAPI) ReplayTransaction(txHash common.Hash, stackDepth int, memorySize int, storageSize int) (*TransactionExecutionResult, error) {
+
+ structLogs, ret, gas, err := s.doReplayTransaction(txHash)
+
+ if err != nil {
+ return nil, err
+ }
+
+ res := TransactionExecutionResult{
+ Gas: gas,
+ ReturnValue: fmt.Sprintf("%x", ret),
+ StructLogs: make([]structLogRes, len(structLogs)),
+ }
+
+ for index, trace := range structLogs {
+
+ stackLength := len(trace.Stack)
+
+ // Return full stack by default
+ if stackDepth != -1 && stackDepth < stackLength {
+ stackLength = stackDepth
+ }
+
+ res.StructLogs[index] = structLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Error: trace.Err,
+ Stack: make([]string, stackLength),
+ Memory: make(map[string]string),
+ Storage: make(map[string]string),
+ }
+
+ for i := 0; i < stackLength; i++ {
+ res.StructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(trace.Stack[i].Bytes(), 32))
+ }
+
+ addr := 0
+ memorySizeLocal := memorySize
+
+ // Return full memory by default
+ if memorySize == -1 {
+ memorySizeLocal = len(trace.Memory)
+ }
+
+ for i := 0; i+16 <= len(trace.Memory) && addr < memorySizeLocal; i += 16 {
+ res.StructLogs[index].Memory[fmt.Sprintf("%04d", addr*16)] = fmt.Sprintf("%x", trace.Memory[i:i+16])
+ addr++
+ }
+
+ storageLength := len(trace.Stack)
+ if storageSize != -1 && storageSize < storageLength {
+ storageLength = storageSize
+ }
+
+ i := 0
+ for storageIndex, storageValue := range trace.Storage {
+ if i >= storageLength {
+ break
+ }
+ res.StructLogs[index].Storage[fmt.Sprintf("%x", storageIndex)] = fmt.Sprintf("%x", storageValue)
+ i++
+ }
+ }
+ return &res, nil
+}
+
+// PublicNetAPI offers network related RPC methods
+type PublicNetAPI struct {
+ net *p2p.Server
+ networkVersion int
+}
+
+// NewPublicNetAPI creates a new net api instance.
+func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI {
+ return &PublicNetAPI{net, networkVersion}
+}
+
+// Listening returns an indication if the node is listening for network connections.
+func (s *PublicNetAPI) Listening() bool {
+ return true // always listening
+}
+
+// Peercount returns the number of connected peers
+func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
+ return rpc.NewHexNumber(s.net.PeerCount())
+}
+
+// ProtocolVersion returns the current ethereum protocol version.
+func (s *PublicNetAPI) Version() string {
+ return fmt.Sprintf("%d", s.networkVersion)
+}