cmd, core, eth/tracers: support fancier js tracing (#15516)
* cmd, core, eth/tracers: support fancier js tracing * eth, internal/web3ext: rework trace API, concurrency, chain tracing * eth/tracers: add three more JavaScript tracers * eth/tracers, vendor: swap ottovm to duktape for tracing * core, eth, internal: finalize call tracer and needed extras * eth, tests: prestate tracer, call test suite, rewinding * vendor: fix windows builds for tracer js engine * vendor: temporary duktape fix * eth/tracers: fix up 4byte and evmdis tracer * vendor: pull in latest duktape with my upstream fixes * eth: fix some review comments * eth: rename rewind to reexec to make it more obvious * core/vm: terminate tracing using defers
This commit is contained in:
1 parent
1a5425779b
commit
5258785c81
64 files changed
+109115
-685
No files matched your search
@@ -19,6 +19,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -35,6 +36,10 @@ func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
|
||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
log := vm.StructLog{
|
||||
@@ -56,6 +61,11 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
type endLog struct {
|
||||
|
||||
+24
-3
@@ -19,6 +19,7 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -165,13 +166,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
start := time.Now()
|
||||
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
|
||||
defer func() { // Lazy evaluation of the parameters
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}()
|
||||
}
|
||||
ret, err = run(evm, contract, input)
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
@@ -338,7 +349,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, contractAddr, gas, nil
|
||||
}
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
ret, err = run(evm, contract, nil)
|
||||
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
@@ -367,6 +385,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||
if maxCodeSizeExceeded && err == nil {
|
||||
err = errMaxCodeSizeExceeded
|
||||
}
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}
|
||||
return ret, contractAddr, contract.Gas, err
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -144,12 +144,17 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
|
||||
)
|
||||
contract.Input = input
|
||||
|
||||
defer func() {
|
||||
if err != nil && !logged && in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if in.cfg.Debug {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
} else {
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// The Interpreter main run loop (contextual). This loop runs until either an
|
||||
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
|
||||
// the execution of one of the operations or until the done flag is set by the
|
||||
|
||||
@@ -84,7 +84,9 @@ func (s *StructLog) OpName() string {
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
|
||||
}
|
||||
|
||||
@@ -111,6 +113,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
return logger
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SSTORE ops to track dirty values.
|
||||
@@ -161,6 +167,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
fmt.Printf("0x%x", output)
|
||||
if err != nil {
|
||||
|
||||
+1
-250
@@ -17,24 +17,19 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -43,8 +38,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
const defaultTraceTimeout = 5 * time.Second
|
||||
|
||||
// PublicEthereumAPI provides an API to access Ethereum full node-related
|
||||
// information.
|
||||
type PublicEthereumAPI struct {
|
||||
@@ -348,248 +341,6 @@ func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebug
|
||||
return &PrivateDebugAPI{config: config, eth: eth}
|
||||
}
|
||||
|
||||
// BlockTraceResult is the returned value when replaying a block to check for
|
||||
// consensus results and full VM trace logs for all included transactions.
|
||||
type BlockTraceResult struct {
|
||||
Validated bool `json:"validated"`
|
||||
StructLogs []ethapi.StructLogRes `json:"structLogs"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// TraceArgs holds extra parameters to trace functions
|
||||
type TraceArgs struct {
|
||||
*vm.LogConfig
|
||||
Tracer *string
|
||||
Timeout *string
|
||||
}
|
||||
|
||||
// TraceBlock processes the given block'api RLP but does not import the block in to
|
||||
// the chain.
|
||||
func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult {
|
||||
var block types.Block
|
||||
err := rlp.Decode(bytes.NewReader(blockRlp), &block)
|
||||
if err != nil {
|
||||
return BlockTraceResult{Error: fmt.Sprintf("could not decode block: %v", err)}
|
||||
}
|
||||
|
||||
validated, logs, err := api.traceBlock(&block, config)
|
||||
return BlockTraceResult{
|
||||
Validated: validated,
|
||||
StructLogs: ethapi.FormatLogs(logs),
|
||||
Error: formatError(err),
|
||||
}
|
||||
}
|
||||
|
||||
// TraceBlockFromFile loads the block'api RLP from the given file name and attempts to
|
||||
// process it but does not import the block in to the chain.
|
||||
func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult {
|
||||
blockRlp, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)}
|
||||
}
|
||||
return api.TraceBlock(blockRlp, config)
|
||||
}
|
||||
|
||||
// TraceBlockByNumber processes the block by canonical block number.
|
||||
func (api *PrivateDebugAPI) TraceBlockByNumber(blockNr rpc.BlockNumber, config *vm.LogConfig) BlockTraceResult {
|
||||
// Fetch the block that we aim to reprocess
|
||||
var block *types.Block
|
||||
switch blockNr {
|
||||
case rpc.PendingBlockNumber:
|
||||
// Pending block is only known by the miner
|
||||
block = api.eth.miner.PendingBlock()
|
||||
case rpc.LatestBlockNumber:
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
default:
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", blockNr)}
|
||||
}
|
||||
|
||||
validated, logs, err := api.traceBlock(block, config)
|
||||
return BlockTraceResult{
|
||||
Validated: validated,
|
||||
StructLogs: ethapi.FormatLogs(logs),
|
||||
Error: formatError(err),
|
||||
}
|
||||
}
|
||||
|
||||
// TraceBlockByHash processes the block by hash.
|
||||
func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.LogConfig) BlockTraceResult {
|
||||
// Fetch the block that we aim to reprocess
|
||||
block := api.eth.BlockChain().GetBlockByHash(hash)
|
||||
if block == nil {
|
||||
return BlockTraceResult{Error: fmt.Sprintf("block #%x not found", hash)}
|
||||
}
|
||||
|
||||
validated, logs, err := api.traceBlock(block, config)
|
||||
return BlockTraceResult{
|
||||
Validated: validated,
|
||||
StructLogs: ethapi.FormatLogs(logs),
|
||||
Error: formatError(err),
|
||||
}
|
||||
}
|
||||
|
||||
// traceBlock processes the given block but does not save the state.
|
||||
func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConfig) (bool, []vm.StructLog, error) {
|
||||
// Validate and reprocess the block
|
||||
var (
|
||||
blockchain = api.eth.BlockChain()
|
||||
validator = blockchain.Validator()
|
||||
processor = blockchain.Processor()
|
||||
)
|
||||
|
||||
structLogger := vm.NewStructLogger(logConfig)
|
||||
|
||||
config := vm.Config{
|
||||
Debug: true,
|
||||
Tracer: structLogger,
|
||||
}
|
||||
if err := api.eth.engine.VerifyHeader(blockchain, block.Header(), true); err != nil {
|
||||
return false, structLogger.StructLogs(), err
|
||||
}
|
||||
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *trie.MissingNodeError:
|
||||
return false, structLogger.StructLogs(), fmt.Errorf("required historical state unavailable")
|
||||
default:
|
||||
return false, structLogger.StructLogs(), err
|
||||
}
|
||||
}
|
||||
|
||||
receipts, _, usedGas, err := processor.Process(block, statedb, config)
|
||||
if err != nil {
|
||||
return false, structLogger.StructLogs(), err
|
||||
}
|
||||
if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas); err != nil {
|
||||
return false, structLogger.StructLogs(), err
|
||||
}
|
||||
return true, structLogger.StructLogs(), nil
|
||||
}
|
||||
|
||||
// formatError formats a Go error into either an empty string or the data content
|
||||
// of the error itself.
|
||||
func formatError(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (t *timeoutError) Error() string {
|
||||
return "Execution time exceeded"
|
||||
}
|
||||
|
||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||
// and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.Hash, config *TraceArgs) (interface{}, error) {
|
||||
var tracer vm.Tracer
|
||||
if config != nil && config.Tracer != nil {
|
||||
timeout := defaultTraceTimeout
|
||||
if config.Timeout != nil {
|
||||
var err error
|
||||
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Handle timeouts and RPC cancellations
|
||||
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
go func() {
|
||||
<-deadlineCtx.Done()
|
||||
tracer.(*ethapi.JavascriptTracer).Stop(&timeoutError{})
|
||||
}()
|
||||
defer cancel()
|
||||
} else if config == nil {
|
||||
tracer = vm.NewStructLogger(nil)
|
||||
} else {
|
||||
tracer = vm.NewStructLogger(config.LogConfig)
|
||||
}
|
||||
|
||||
// Retrieve the tx from the chain and the containing block
|
||||
tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash)
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("transaction %x not found", txHash)
|
||||
}
|
||||
msg, context, statedb, err := api.computeTxEnv(blockHash, int(txIndex))
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *trie.MissingNodeError:
|
||||
return nil, fmt.Errorf("required historical state unavailable")
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
ret, gas, failed, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
}
|
||||
switch tracer := tracer.(type) {
|
||||
case *vm.StructLogger:
|
||||
return ðapi.ExecutionResult{
|
||||
Gas: gas,
|
||||
Failed: failed,
|
||||
ReturnValue: fmt.Sprintf("%x", ret),
|
||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||
}, nil
|
||||
case *ethapi.JavascriptTracer:
|
||||
return tracer.GetResult()
|
||||
default:
|
||||
panic(fmt.Sprintf("bad tracer type %T", tracer))
|
||||
}
|
||||
}
|
||||
|
||||
// computeTxEnv returns the execution environment of a certain transaction.
|
||||
func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (core.Message, vm.Context, *state.StateDB, error) {
|
||||
// Create the parent state.
|
||||
block := api.eth.BlockChain().GetBlockByHash(blockHash)
|
||||
if block == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
|
||||
}
|
||||
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
||||
}
|
||||
statedb, err := api.eth.BlockChain().StateAt(parent.Root())
|
||||
if err != nil {
|
||||
return nil, vm.Context{}, nil, err
|
||||
}
|
||||
txs := block.Transactions()
|
||||
|
||||
// Recompute transactions up to the target index.
|
||||
signer := types.MakeSigner(api.config, block.Number())
|
||||
for idx, tx := range txs {
|
||||
// Assemble the transaction call message
|
||||
msg, _ := tx.AsMessage(signer)
|
||||
context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil)
|
||||
if idx == txIndex {
|
||||
return msg, context, statedb, nil
|
||||
}
|
||||
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(tx.Gas())
|
||||
_, _, _, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
if err != nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
statedb.DeleteSuicides()
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
|
||||
}
|
||||
|
||||
// Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
|
||||
func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
db := core.PreimageTable(api.eth.ChainDb())
|
||||
@@ -617,7 +368,7 @@ type storageEntry struct {
|
||||
|
||||
// StorageRangeAt returns the storage at the given block height and transaction index.
|
||||
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
|
||||
_, _, statedb, err := api.computeTxEnv(blockHash, txIndex)
|
||||
_, _, statedb, err := api.computeTxEnv(blockHash, txIndex, 0)
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
// 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/>.
|
||||
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultTraceTimeout is the amount of time a single transaction can execute
|
||||
// by default before being forcefully aborted.
|
||||
defaultTraceTimeout = 5 * time.Second
|
||||
|
||||
// defaultTraceReexec is the number of blocks the tracer is willing to go back
|
||||
// and reexecute to produce missing historical state necessary to run a specific
|
||||
// trace.
|
||||
defaultTraceReexec = uint64(128)
|
||||
)
|
||||
|
||||
// TraceConfig holds extra parameters to trace functions.
|
||||
type TraceConfig struct {
|
||||
*vm.LogConfig
|
||||
Tracer *string
|
||||
Timeout *string
|
||||
Reexec *uint64
|
||||
}
|
||||
|
||||
// txTraceResult is the result of a single transaction trace.
|
||||
type txTraceResult struct {
|
||||
Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
|
||||
Error string `json:"error,omitempty"` // Trace failure produced by the tracer
|
||||
}
|
||||
|
||||
// blockTraceTask represents a single block trace task when an entire chain is
|
||||
// being traced.
|
||||
type blockTraceTask struct {
|
||||
statedb *state.StateDB // Intermediate state prepped for tracing
|
||||
block *types.Block // Block to trace the transactions from
|
||||
results []*txTraceResult // Trace results procudes by the task
|
||||
}
|
||||
|
||||
// blockTraceResult represets the results of tracing a single block when an entire
|
||||
// chain is being traced.
|
||||
type blockTraceResult struct {
|
||||
Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace
|
||||
Hash common.Hash `json:"hash"` // Block hash corresponding to this trace
|
||||
Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
|
||||
}
|
||||
|
||||
// txTraceTask represents a single transaction trace task when an entire block
|
||||
// is being traced.
|
||||
type txTraceTask struct {
|
||||
statedb *state.StateDB // Intermediate state prepped for tracing
|
||||
index int // Transaction offset in the block
|
||||
}
|
||||
|
||||
// ephemeralDatabase is a memory wrapper around a proper database, which acts as
|
||||
// an ephemeral write layer. This construct is used by the chain tracer to write
|
||||
// state tries for intermediate blocks without serializing to disk, but at the
|
||||
// same time to allow disk fallback for reads that do no hit the memory layer.
|
||||
type ephemeralDatabase struct {
|
||||
diskdb ethdb.Database // Persistent disk database to fall back to with reads
|
||||
memdb *ethdb.MemDatabase // Ephemeral memory database for primary reads and writes
|
||||
}
|
||||
|
||||
func (db *ephemeralDatabase) Put(key []byte, value []byte) error { return db.memdb.Put(key, value) }
|
||||
func (db *ephemeralDatabase) Delete(key []byte) error { return errors.New("delete not supported") }
|
||||
func (db *ephemeralDatabase) Close() { db.memdb.Close() }
|
||||
func (db *ephemeralDatabase) NewBatch() ethdb.Batch {
|
||||
return db.memdb.NewBatch()
|
||||
}
|
||||
func (db *ephemeralDatabase) Has(key []byte) (bool, error) {
|
||||
if has, _ := db.memdb.Has(key); has {
|
||||
return has, nil
|
||||
}
|
||||
return db.diskdb.Has(key)
|
||||
}
|
||||
func (db *ephemeralDatabase) Get(key []byte) ([]byte, error) {
|
||||
if blob, _ := db.memdb.Get(key); blob != nil {
|
||||
return blob, nil
|
||||
}
|
||||
return db.diskdb.Get(key)
|
||||
}
|
||||
|
||||
// Prune does a state sync into a new memory write layer and replaces the old one.
|
||||
// This allows us to discard entries that are no longer referenced from the current
|
||||
// state.
|
||||
func (db *ephemeralDatabase) Prune(root common.Hash) {
|
||||
// Pull the still relevant state data into memory
|
||||
sync := state.NewStateSync(root, db.diskdb)
|
||||
for sync.Pending() > 0 {
|
||||
hash := sync.Missing(1)[0]
|
||||
|
||||
// Move the next trie node from the memory layer into a sync struct
|
||||
node, err := db.memdb.Get(hash[:])
|
||||
if err != nil {
|
||||
panic(err) // memdb must have the data
|
||||
}
|
||||
if _, _, err := sync.Process([]trie.SyncResult{{Hash: hash, Data: node}}); err != nil {
|
||||
panic(err) // it's not possible to fail processing a node
|
||||
}
|
||||
}
|
||||
// Discard the old memory layer and write a new one
|
||||
db.memdb, _ = ethdb.NewMemDatabaseWithCap(db.memdb.Len())
|
||||
if _, err := sync.Commit(db); err != nil {
|
||||
panic(err) // writing into a memdb cannot fail
|
||||
}
|
||||
}
|
||||
|
||||
// TraceChain returns the structured logs created during the execution of EVM
|
||||
// between two blocks (excluding start) and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
|
||||
// Fetch the block interval that we want to trace
|
||||
var from, to *types.Block
|
||||
|
||||
switch start {
|
||||
case rpc.PendingBlockNumber:
|
||||
from = api.eth.miner.PendingBlock()
|
||||
case rpc.LatestBlockNumber:
|
||||
from = api.eth.blockchain.CurrentBlock()
|
||||
default:
|
||||
from = api.eth.blockchain.GetBlockByNumber(uint64(start))
|
||||
}
|
||||
switch end {
|
||||
case rpc.PendingBlockNumber:
|
||||
to = api.eth.miner.PendingBlock()
|
||||
case rpc.LatestBlockNumber:
|
||||
to = api.eth.blockchain.CurrentBlock()
|
||||
default:
|
||||
to = api.eth.blockchain.GetBlockByNumber(uint64(end))
|
||||
}
|
||||
// Trace the chain if we've found all our blocks
|
||||
if from == nil {
|
||||
return nil, fmt.Errorf("starting block #%d not found", start)
|
||||
}
|
||||
if to == nil {
|
||||
return nil, fmt.Errorf("end block #%d not found", end)
|
||||
}
|
||||
return api.traceChain(ctx, from, to, config)
|
||||
}
|
||||
|
||||
// traceChain configures a new tracer according to the provided configuration, and
|
||||
// executes all the transactions contained within. The return value will be one item
|
||||
// per transaction, dependent on the requestd tracer.
|
||||
func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
|
||||
// Tracing a chain is a **long** operation, only do with subscriptions
|
||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||
if !supported {
|
||||
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
|
||||
}
|
||||
sub := notifier.CreateSubscription()
|
||||
|
||||
// Ensure we have a valid starting state before doing any work
|
||||
origin := start.NumberU64()
|
||||
|
||||
memdb, _ := ethdb.NewMemDatabase()
|
||||
db := &ephemeralDatabase{
|
||||
diskdb: api.eth.ChainDb(),
|
||||
memdb: memdb,
|
||||
}
|
||||
if number := start.NumberU64(); number > 0 {
|
||||
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
||||
if start == nil {
|
||||
return nil, fmt.Errorf("parent block #%d not found", number-1)
|
||||
}
|
||||
}
|
||||
statedb, err := state.New(start.Root(), state.NewDatabase(db))
|
||||
if err != nil {
|
||||
// If the starting state is missing, allow some number of blocks to be reexecuted
|
||||
reexec := defaultTraceReexec
|
||||
if config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
// Find the most recent block that has the state available
|
||||
for i := uint64(0); i < reexec; i++ {
|
||||
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
||||
if start == nil {
|
||||
break
|
||||
}
|
||||
if statedb, err = state.New(start.Root(), state.NewDatabase(db)); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
// If we still don't have the state available, bail out
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *trie.MissingNodeError:
|
||||
return nil, errors.New("required historical state unavailable")
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// Execute all the transaction contained within the chain concurrently for each block
|
||||
blocks := int(end.NumberU64() - origin)
|
||||
|
||||
threads := runtime.NumCPU()
|
||||
if threads > blocks {
|
||||
threads = blocks
|
||||
}
|
||||
var (
|
||||
pend = new(sync.WaitGroup)
|
||||
tasks = make(chan *blockTraceTask, threads)
|
||||
results = make(chan *blockTraceTask, threads)
|
||||
)
|
||||
for th := 0; th < threads; th++ {
|
||||
pend.Add(1)
|
||||
go func() {
|
||||
defer pend.Done()
|
||||
|
||||
// Fetch and execute the next block trace tasks
|
||||
for task := range tasks {
|
||||
signer := types.MakeSigner(api.config, task.block.Number())
|
||||
|
||||
// Trace all the transactions contained within
|
||||
for i, tx := range task.block.Transactions() {
|
||||
msg, _ := tx.AsMessage(signer)
|
||||
vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil)
|
||||
|
||||
res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
|
||||
if err != nil {
|
||||
task.results[i] = &txTraceResult{Error: err.Error()}
|
||||
log.Warn("Tracing failed", "err", err)
|
||||
break
|
||||
}
|
||||
task.statedb.DeleteSuicides()
|
||||
task.results[i] = &txTraceResult{Result: res}
|
||||
}
|
||||
// Stream the result back to the user or abort on teardown
|
||||
select {
|
||||
case results <- task:
|
||||
case <-notifier.Closed():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Start a goroutine to feed all the blocks into the tracers
|
||||
begin := time.Now()
|
||||
complete := start.NumberU64()
|
||||
|
||||
go func() {
|
||||
var (
|
||||
logged time.Time
|
||||
number uint64
|
||||
traced uint64
|
||||
failed error
|
||||
)
|
||||
// Ensure everything is properly cleaned up on any exit path
|
||||
defer func() {
|
||||
close(tasks)
|
||||
pend.Wait()
|
||||
|
||||
switch {
|
||||
case failed != nil:
|
||||
log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
|
||||
case number < end.NumberU64():
|
||||
log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin))
|
||||
default:
|
||||
log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
|
||||
}
|
||||
close(results)
|
||||
}()
|
||||
// Feed all the blocks both into the tracer, as well as fast process concurrently
|
||||
for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ {
|
||||
// Stop tracing if interruption was requested
|
||||
select {
|
||||
case <-notifier.Closed():
|
||||
return
|
||||
default:
|
||||
}
|
||||
// Print progress logs if long enough time elapsed
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
if number > origin {
|
||||
log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
|
||||
} else {
|
||||
log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin))
|
||||
}
|
||||
logged = time.Now()
|
||||
}
|
||||
// Retrieve the next block to trace
|
||||
block := api.eth.blockchain.GetBlockByNumber(number)
|
||||
if block == nil {
|
||||
failed = fmt.Errorf("block #%d not found", number)
|
||||
break
|
||||
}
|
||||
// Send the block over to the concurrent tracers (if not in the fast-forward phase)
|
||||
if number > origin {
|
||||
txs := block.Transactions()
|
||||
|
||||
select {
|
||||
case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, results: make([]*txTraceResult, len(txs))}:
|
||||
case <-notifier.Closed():
|
||||
return
|
||||
}
|
||||
traced += uint64(len(txs))
|
||||
} else {
|
||||
atomic.StoreUint64(&complete, number)
|
||||
}
|
||||
// Generate the next state snapshot fast without tracing
|
||||
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.CommitTo(db, true)
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
if err := statedb.Reset(root); err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// After every N blocks, prune the database to only retain relevant data
|
||||
if (number-start.NumberU64())%4096 == 0 {
|
||||
// Wait until currently pending trace jobs finish
|
||||
for atomic.LoadUint64(&complete) != number {
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case <-notifier.Closed():
|
||||
return
|
||||
}
|
||||
}
|
||||
// No more concurrent access at this point, prune the database
|
||||
var (
|
||||
nodes = db.memdb.Len()
|
||||
start = time.Now()
|
||||
)
|
||||
db.Prune(root)
|
||||
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(start))
|
||||
|
||||
statedb, _ = state.New(root, state.NewDatabase(db))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Keep reading the trace results and stream the to the user
|
||||
go func() {
|
||||
var (
|
||||
done = make(map[uint64]*blockTraceResult)
|
||||
next = origin + 1
|
||||
)
|
||||
for res := range results {
|
||||
// Queue up next received result
|
||||
result := &blockTraceResult{
|
||||
Block: hexutil.Uint64(res.block.NumberU64()),
|
||||
Hash: res.block.Hash(),
|
||||
Traces: res.results,
|
||||
}
|
||||
done[uint64(result.Block)] = result
|
||||
|
||||
// Stream completed traces to the user, aborting on the first error
|
||||
for result, ok := done[next]; ok; result, ok = done[next] {
|
||||
if len(result.Traces) > 0 || next == end.NumberU64() {
|
||||
notifier.Notify(sub.ID, result)
|
||||
}
|
||||
atomic.StoreUint64(&complete, next)
|
||||
delete(done, next)
|
||||
next++
|
||||
}
|
||||
}
|
||||
}()
|
||||
return sub, nil
|
||||
}
|
||||
|
||||
// TraceBlockByNumber returns the structured logs created during the execution of
|
||||
// EVM and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
// Fetch the block that we want to trace
|
||||
var block *types.Block
|
||||
|
||||
switch number {
|
||||
case rpc.PendingBlockNumber:
|
||||
block = api.eth.miner.PendingBlock()
|
||||
case rpc.LatestBlockNumber:
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
default:
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
|
||||
}
|
||||
// Trace the block if it was found
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block #%d not found", number)
|
||||
}
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
|
||||
// TraceBlockByHash returns the structured logs created during the execution of
|
||||
// EVM and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
block := api.eth.blockchain.GetBlockByHash(hash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block #%x not found", hash)
|
||||
}
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
|
||||
// TraceBlock returns the structured logs created during the execution of EVM
|
||||
// and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
block := new(types.Block)
|
||||
if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
|
||||
return nil, fmt.Errorf("could not decode block: %v", err)
|
||||
}
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
|
||||
// TraceBlockFromFile returns the structured logs created during the execution of
|
||||
// EVM and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
blob, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read file: %v", err)
|
||||
}
|
||||
return api.TraceBlock(ctx, blob, config)
|
||||
}
|
||||
|
||||
// traceBlock configures a new tracer according to the provided configuration, and
|
||||
// executes all the transactions contained within. The return value will be one item
|
||||
// per transaction, dependent on the requestd tracer.
|
||||
func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
// Create the parent state database
|
||||
if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
statedb, err := api.computeStateDB(parent, reexec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Execute all the transaction contained within the block concurrently
|
||||
var (
|
||||
signer = types.MakeSigner(api.config, block.Number())
|
||||
|
||||
txs = block.Transactions()
|
||||
results = make([]*txTraceResult, len(txs))
|
||||
|
||||
pend = new(sync.WaitGroup)
|
||||
jobs = make(chan *txTraceTask, len(txs))
|
||||
)
|
||||
threads := runtime.NumCPU()
|
||||
if threads > len(txs) {
|
||||
threads = len(txs)
|
||||
}
|
||||
for th := 0; th < threads; th++ {
|
||||
pend.Add(1)
|
||||
go func() {
|
||||
defer pend.Done()
|
||||
|
||||
// Fetch and execute the next transaction trace tasks
|
||||
for task := range jobs {
|
||||
msg, _ := txs[task.index].AsMessage(signer)
|
||||
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
||||
|
||||
res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
|
||||
if err != nil {
|
||||
results[task.index] = &txTraceResult{Error: err.Error()}
|
||||
continue
|
||||
}
|
||||
results[task.index] = &txTraceResult{Result: res}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Feed the transactions into the tracers and return
|
||||
var failed error
|
||||
for i, tx := range txs {
|
||||
// Send the trace task over for execution
|
||||
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
|
||||
|
||||
// Generate the next state snapshot fast without tracing
|
||||
msg, _ := tx.AsMessage(signer)
|
||||
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
||||
|
||||
vmenv := vm.NewEVM(vmctx, statedb, api.config, vm.Config{})
|
||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
close(jobs)
|
||||
pend.Wait()
|
||||
|
||||
// If execution failed in between, abort
|
||||
if failed != nil {
|
||||
return nil, failed
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// computeStateDB retrieves the state database associated with a certain block.
|
||||
// If no state is locally available for the given block, a number of blocks are
|
||||
// attempted to be reexecuted to generate the desired state.
|
||||
func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) {
|
||||
// If we have the state fully available, use that
|
||||
statedb, err := api.eth.blockchain.StateAt(block.Root())
|
||||
if err == nil {
|
||||
return statedb, nil
|
||||
}
|
||||
// Otherwise try to reexec blocks until we find a state or reach our limit
|
||||
origin := block.NumberU64()
|
||||
|
||||
memdb, _ := ethdb.NewMemDatabase()
|
||||
db := &ephemeralDatabase{
|
||||
diskdb: api.eth.ChainDb(),
|
||||
memdb: memdb,
|
||||
}
|
||||
for i := uint64(0); i < reexec; i++ {
|
||||
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if statedb, err = state.New(block.Root(), state.NewDatabase(db)); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *trie.MissingNodeError:
|
||||
return nil, errors.New("required historical state unavailable")
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// State was available at historical point, regenerate
|
||||
var (
|
||||
start = time.Now()
|
||||
logged time.Time
|
||||
)
|
||||
for block.NumberU64() < origin {
|
||||
// Print progress logs if long enough time elapsed
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "elapsed", time.Since(start))
|
||||
logged = time.Now()
|
||||
}
|
||||
// Retrieve the next block to regenerate and process it
|
||||
if block = api.eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil {
|
||||
return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1)
|
||||
}
|
||||
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.CommitTo(db, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := statedb.Reset(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// After every N blocks, prune the database to only retain relevant data
|
||||
if block.NumberU64()%4096 == 0 || block.NumberU64() == origin {
|
||||
var (
|
||||
nodes = db.memdb.Len()
|
||||
begin = time.Now()
|
||||
)
|
||||
db.Prune(root)
|
||||
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(begin))
|
||||
|
||||
statedb, _ = state.New(root, state.NewDatabase(db))
|
||||
}
|
||||
}
|
||||
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start))
|
||||
return statedb, nil
|
||||
}
|
||||
|
||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||
// and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||
// Retrieve the transaction and assemble its EVM context
|
||||
tx, blockHash, _, index := core.GetTransaction(api.eth.ChainDb(), hash)
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("transaction %x not found", hash)
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), reexec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Trace the transaction and return
|
||||
return api.traceTx(ctx, msg, vmctx, statedb, config)
|
||||
}
|
||||
|
||||
// traceTx configures a new tracer according to the provided configuration, and
|
||||
// executes the given message in the provided environment. The return value will
|
||||
// be tracer dependent.
|
||||
func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
|
||||
// Assemble the structured logger or the JavaScript tracer
|
||||
var (
|
||||
tracer vm.Tracer
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
case config != nil && config.Tracer != nil:
|
||||
// Define a meaningful timeout of a single transaction trace
|
||||
timeout := defaultTraceTimeout
|
||||
if config.Timeout != nil {
|
||||
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Constuct the JavaScript tracer to execute with
|
||||
if tracer, err = tracers.New(*config.Tracer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Handle timeouts and RPC cancellations
|
||||
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
go func() {
|
||||
<-deadlineCtx.Done()
|
||||
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
case config == nil:
|
||||
tracer = vm.NewStructLogger(nil)
|
||||
|
||||
default:
|
||||
tracer = vm.NewStructLogger(config.LogConfig)
|
||||
}
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(vmctx, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
}
|
||||
// Depending on the tracer type, format and return the output
|
||||
switch tracer := tracer.(type) {
|
||||
case *vm.StructLogger:
|
||||
return ðapi.ExecutionResult{
|
||||
Gas: gas,
|
||||
Failed: failed,
|
||||
ReturnValue: fmt.Sprintf("%x", ret),
|
||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||
}, nil
|
||||
|
||||
case *tracers.Tracer:
|
||||
return tracer.GetResult()
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("bad tracer type %T", tracer))
|
||||
}
|
||||
}
|
||||
|
||||
// computeTxEnv returns the execution environment of a certain transaction.
|
||||
func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) {
|
||||
// Create the parent state database
|
||||
block := api.eth.blockchain.GetBlockByHash(blockHash)
|
||||
if block == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
|
||||
}
|
||||
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||
}
|
||||
statedb, err := api.computeStateDB(parent, reexec)
|
||||
if err != nil {
|
||||
return nil, vm.Context{}, nil, err
|
||||
}
|
||||
// Recompute transactions up to the target index.
|
||||
signer := types.MakeSigner(api.config, block.Number())
|
||||
|
||||
for idx, tx := range block.Transactions() {
|
||||
// Assemble the transaction call message and return if the requested offset
|
||||
msg, _ := tx.AsMessage(signer)
|
||||
context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
||||
if idx == txIndex {
|
||||
return msg, context, statedb, nil
|
||||
}
|
||||
// Not yet the searched for transaction, execute on top of the current state
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
|
||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
statedb.DeleteSuicides()
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
|
||||
}
|
||||
+6
-4
@@ -747,10 +747,11 @@ func (self *ProtocolManager) txBroadcastLoop() {
|
||||
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
|
||||
// about the host peer.
|
||||
type EthNodeInfo struct {
|
||||
Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
||||
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
|
||||
Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
||||
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
||||
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
|
||||
}
|
||||
|
||||
// NodeInfo retrieves some protocol metadata about the running host node.
|
||||
@@ -760,6 +761,7 @@ func (self *ProtocolManager) NodeInfo() *EthNodeInfo {
|
||||
Network: self.networkId,
|
||||
Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
|
||||
Genesis: self.blockchain.Genesis().Hash(),
|
||||
Config: self.blockchain.Config(),
|
||||
Head: currentBlock.Hash(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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/>.
|
||||
|
||||
// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
|
||||
// It collects the methods identifiers along with the size of the supplied data, so
|
||||
// a reversed signature can be matched against the size of the data.
|
||||
//
|
||||
// Example:
|
||||
// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
|
||||
// {
|
||||
// 0x27dc297e-128: 1,
|
||||
// 0x38cc4831-0: 2,
|
||||
// 0x524f3889-96: 1,
|
||||
// 0xadf59f99-288: 1,
|
||||
// 0xc281d19e-0: 1
|
||||
// }
|
||||
{
|
||||
// ids aggregates the 4byte ids found.
|
||||
ids : {},
|
||||
|
||||
// callType returns 'false' for non-calls, or the peek-index for the first param
|
||||
// after 'value', i.e. meminstart.
|
||||
callType: function(opstr){
|
||||
switch(opstr){
|
||||
case "CALL": case "CALLCODE":
|
||||
// gas, addr, val, memin, meminsz, memout, memoutsz
|
||||
return 3; // stack ptr to memin
|
||||
|
||||
case "DELEGATECALL": case "STATICCALL":
|
||||
// gas, addr, memin, meminsz, memout, memoutsz
|
||||
return 2; // stack ptr to memin
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// store save the given indentifier and datasize.
|
||||
store: function(id, size){
|
||||
var key = "" + toHex(id) + "-" + size;
|
||||
this.ids[key] = this.ids[key] + 1 || 1;
|
||||
},
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Skip any opcodes that are not internal calls
|
||||
var ct = this.callType(log.op.toString());
|
||||
if (!ct) {
|
||||
return;
|
||||
}
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
if (isPrecompiled(toAddress(log.stack.peek(1)))) {
|
||||
return;
|
||||
}
|
||||
// Gather internal call details
|
||||
var inSz = log.stack.peek(ct + 1).valueOf();
|
||||
if (inSz >= 4) {
|
||||
var inOff = log.stack.peek(ct).valueOf();
|
||||
this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx) {
|
||||
// Save the outer calldata also
|
||||
if (ctx.input.length > 4) {
|
||||
this.store(slice(ctx.input, 0, 4), ctx.input.length-4)
|
||||
}
|
||||
return this.ids;
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// 4byte_tracer.js
|
||||
// call_tracer.js
|
||||
// evmdis_tracer.js
|
||||
// noop_tracer.js
|
||||
// opcount_tracer.js
|
||||
// prestate_tracer.js
|
||||
// DO NOT EDIT!
|
||||
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5b\x6f\xdb\x4a\x0e\x7e\xb6\x7f\x05\xd7\x2f\xb5\x51\x59\x8e\x2f\x89\x2f\xd9\x16\xf0\xe6\xa4\x6d\x80\x9c\x24\x88\xdd\x3d\x28\x16\xfb\x30\x9e\xa1\xac\xd9\xc8\x33\xc2\x0c\xe5\x4b\x73\xf2\xdf\x17\x1c\x49\x89\x93\xd3\x62\xbb\x4f\x96\x47\xc3\x8f\x1f\xc9\x8f\xa4\x7a\x3d\xb8\xb0\xf9\xc1\xe9\x75\x4a\x30\x38\xe9\x8f\x61\x99\x22\xac\x6d\x17\x29\x45\x87\xc5\x06\xe6\x05\xa5\xd6\xf9\x66\xaf\x07\xcb\x54\x7b\x48\x74\x86\xa0\x3d\xe4\xc2\x11\xd8\x04\xe8\xcd\xfd\x4c\xaf\x9c\x70\x87\xb8\xd9\xeb\x95\x36\x3f\x7c\xcd\x08\x89\x43\x04\x6f\x13\xda\x09\x87\x33\x38\xd8\x02\xa4\x30\xe0\x50\x69\x4f\x4e\xaf\x0a\x42\xd0\x04\xc2\xa8\x9e\x75\xb0\xb1\x4a\x27\x07\x86\xd4\x04\x85\x51\xe8\x82\x6b\x42\xb7\xf1\x35\x8f\xcf\x37\x5f\xe1\x1a\xbd\x47\x07\x9f\xd1\xa0\x13\x19\xdc\x15\xab\x4c\x4b\xb8\xd6\x12\x8d\x47\x10\x1e\x72\x3e\xf1\x29\x2a\x58\x05\x38\x36\xfc\xc4\x54\x16\x15\x15\xf8\x64\x0b\xa3\x04\x69\x6b\x22\x40\xcd\xcc\x61\x8b\xce\x6b\x6b\x60\x58\xbb\xaa\x00\x23\xb0\x8e\x41\xda\x82\x38\x00\x07\x36\x67\xbb\x0e\x08\x73\x80\x4c\xd0\x8b\xe9\x2f\x24\xe4\x25\x6e\x05\xda\x04\x37\xa9\xcd\x11\x28\x15\xc4\x51\xef\x74\x96\xc1\x0a\xa1\xf0\x98\x14\x59\xc4\x68\xab\x82\xe0\x8f\xab\xe5\x97\xdb\xaf\x4b\x98\xdf\x7c\x83\x3f\xe6\xf7\xf7\xf3\x9b\xe5\xb7\x73\xd8\x69\x4a\x6d\x41\x80\x5b\x2c\xa1\xf4\x26\xcf\x34\x2a\xd8\x09\xe7\x84\xa1\x03\xd8\x84\x11\x7e\xbf\xbc\xbf\xf8\x32\xbf\x59\xce\xff\x71\x75\x7d\xb5\xfc\x06\xd6\xc1\xa7\xab\xe5\xcd\xe5\x62\x01\x9f\x6e\xef\x61\x0e\x77\xf3\xfb\xe5\xd5\xc5\xd7\xeb\xf9\x3d\xdc\x7d\xbd\xbf\xbb\x5d\x5c\xc6\xb0\x40\x66\x85\x6c\xff\xbf\x73\x9e\x84\xea\x39\x04\x85\x24\x74\xe6\xeb\x4c\x7c\xb3\x05\xf8\xd4\x16\x99\x82\x54\x6c\x11\x1c\x4a\xd4\x5b\x54\x20\x40\xda\xfc\xf0\xcb\x45\x65\x2c\x91\x59\xb3\x0e\x31\xff\x54\x90\x70\x95\x80\xb1\x14\x81\x47\x84\xbf\xa7\x44\xf9\xac\xd7\xdb\xed\x76\xf1\xda\x14\xb1\x75\xeb\x5e\x56\xc2\xf9\xde\xc7\xb8\xc9\x98\xa3\xd5\x81\x70\xe9\x84\x44\x07\x1e\x85\x93\x29\xfa\x10\x4c\x78\xd1\xd5\x0a\x0d\xe9\x44\xa3\xf3\x11\x8b\x14\xa4\xcd\x32\x94\xe4\x99\xc1\x26\x5c\xcc\xad\xa7\x6e\xee\xac\x44\xef\xb5\x59\x73\xe0\x70\x45\xaf\x2e\xc2\x06\x29\xb5\xca\xc3\x11\xdc\xdb\x68\xbc\xfe\x8e\x75\x36\x7c\x91\x97\x65\x54\x82\x44\x04\xde\x86\xe8\xc1\x21\xcb\x0c\x15\x78\xbd\x36\x82\x0a\x87\xa1\x97\x56\x08\x1b\x41\x92\xc5\x2e\xd6\x42\x1b\x4f\x7f\x01\x64\x9c\xba\x22\x97\x7b\xb1\xc9\x33\x9c\xf1\x33\xc0\x47\x50\xb8\x2a\xd6\x31\x71\x0a\x96\x4e\x18\x2f\x24\x8b\xbb\x0d\xad\x93\xfd\xa0\x3f\xc2\xd3\xe9\x18\x87\xa7\x4a\x9c\x4c\x86\x67\xd3\x41\x72\x3a\x9c\x9c\xf5\x47\x7d\x3c\x9b\x26\xa3\x31\x4e\xc7\xc3\xd5\x40\x9e\x9e\xe1\x58\x4c\x4e\xc6\xc3\x55\x1f\xc5\xc9\x24\x51\xe3\xd3\x71\x1f\xa7\x0a\x5b\x11\x3c\x06\x60\x37\x83\xd6\x51\xa6\x5b\x4f\x9d\xd2\xfb\x63\xf9\x03\x70\xb2\x1f\x8c\x95\x1c\x4c\xc7\xd8\xed\x0f\x26\x33\xe8\x47\x2f\x6f\x86\x13\x29\x47\x93\x61\xbf\x7b\x32\x83\xc1\xd1\xf9\xe9\x60\x94\x0c\x27\x93\x69\x77\x7a\xf6\xda\x40\xa8\xe4\x74\x9a\x4c\xa7\xdd\xc1\xe4\x0d\x94\x1c\x4c\xfa\xaa\x3f\x45\x86\xea\x97\xc7\x4f\xcd\xc7\x66\x83\x07\x8e\xf2\x20\xd6\x6b\x87\x6b\x41\x58\x56\x2d\x30\x0e\x2f\x12\x1e\x16\x71\xb3\xc1\xcf\x33\x78\x7c\x8a\x9a\xc1\x46\x8a\x2c\x5b\x1e\x72\x56\x35\x15\xce\x78\x78\x97\x88\xcc\xe3\xbb\xa0\x0b\x63\x4d\x97\x2f\x78\x1e\x1f\x01\x2f\x47\x7c\xe8\x6a\xa3\x70\x1f\x2e\xf0\x51\xa2\x9d\x27\x1e\xb3\x62\x13\x10\x45\xc2\xd3\xe4\xdd\x56\x64\x05\xbe\x8b\x40\xc7\x18\xc3\x06\x37\x5c\x54\xe1\x28\x6e\x36\x6a\x97\x33\x48\x0a\x53\x56\xca\xe6\x9e\x5c\xe7\xb1\xd9\x68\xf8\x9d\x26\x99\x1e\x1d\x48\xe1\x11\x5a\x17\xf3\xeb\xeb\xd6\x0c\x5e\xfe\x5c\xdc\xfe\x76\xd9\x9a\x35\x1b\x0d\x76\xb9\x16\x2c\x6d\xa5\x5c\x04\x5b\x91\x45\xa5\xbb\xea\xc7\x7f\x0f\x0f\xb6\xa0\xfa\xd7\x7f\x67\xb3\x32\x5e\x18\x9e\x43\xaf\x07\x9e\x84\x7c\x80\x9c\x1c\x90\x2d\xcd\x9a\xcf\xae\x7f\xbb\xbc\xbe\xfc\x3c\x5f\x5e\xbe\xa2\xb0\x58\xce\x97\x57\x17\xe5\xd1\x5f\x49\xfc\x1f\xfe\x07\x3f\xf3\xdf\x68\x3c\x35\x9f\x6f\x85\x9a\x9c\x37\x1b\x75\xd5\x3c\xf1\x9c\xf2\x3c\x8d\xc2\x18\xd1\x3c\x3c\xb9\x2c\x55\x6b\x86\x3e\xe7\x8e\xe1\x0e\x8a\x9b\x8d\x70\xff\x28\xdf\x5a\x45\xa1\xb9\x42\x86\xb7\xc2\xc1\x03\x1e\xe0\x03\xb4\x5a\xf0\x1e\xc8\x7e\xc1\x7d\x5b\xab\x0e\xbc\x87\x56\x97\x4f\xf8\xe6\x79\xb3\xd1\xa0\x54\xfb\x58\x2b\xff\xaf\x07\x3c\xfc\x1b\x3e\xc0\xeb\xff\xef\xa1\x0f\x7f\xfe\x09\xfd\x57\x34\x31\xe7\x85\xa1\xcd\xd6\x3e\xa0\x0a\x92\xe1\x01\x70\x00\x9b\x4b\xab\xaa\x8d\xc1\x11\xfc\xf3\x77\xc0\x3d\xca\x82\xd0\x07\xba\x98\x1f\xb1\xcd\xec\x3a\x02\xb5\xea\x00\xb3\xed\xf5\x60\xf1\xa0\xf3\xb0\xb8\x4a\x14\x5f\xc2\xf0\x46\x34\x96\x40\x1b\x42\x67\x44\x16\xa4\xed\xab\xf8\x24\xd5\x7c\x6b\xf5\x31\x6a\x6c\xf3\x98\xec\x82\x9c\x36\xeb\x76\xa7\xc3\x31\xea\x04\xda\x7f\x93\x54\xfa\xaa\xd2\x7f\x5e\x15\xe3\xd8\x75\xee\xb0\x2b\xed\x26\x0f\x5f\x19\x66\x6b\x65\xd8\xc3\x3e\x02\x4a\x2d\xef\x6f\x87\xf0\x9f\xc2\x13\x24\xc2\xc8\x67\xa2\x15\xbe\xf6\x77\x0e\x2b\x63\xd5\x26\x3b\x57\xca\xa1\xf7\x81\x51\x50\x42\xcc\x6d\xd6\xee\x77\x3a\x9d\x9f\xf1\xf8\x2c\xc2\xba\x7f\x15\x6b\xbd\xb7\xaa\x90\xb5\x59\x7c\x87\x0f\xf0\x06\x54\x12\x17\xaa\x13\x87\xf6\xbc\x4d\xda\xcf\x41\x87\xebLine truncated
|
||||
|
||||
func _4byte_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__4byte_tracerJs,
|
||||
"4byte_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func _4byte_tracerJs() (*asset, error) {
|
||||
bytes, err := _4byte_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "4byte_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x59\xdf\x6f\x1b\x37\xf2\x7f\x96\xfe\x8a\x49\x1e\x6a\x09\x51\x24\x27\xe9\xb7\x5f\xc0\xae\x7a\xd0\x39\x4a\x6a\xc0\x8d\x03\x5b\x69\x10\x04\x79\xa0\x76\x67\x25\xd6\x5c\x72\x4b\x72\x2d\xef\xa5\xfe\xdf\x0f\x33\xe4\xae\x56\x3f\xec\xe8\x7a\xb8\x43\xef\x45\xd0\x2e\x67\x86\xc3\x99\xcf\xfc\xe2\x8e\x46\x70\x66\x8a\xca\xca\xc5\xd2\xc3\xcb\xe3\x17\xff\x0f\xb3\x25\xc2\xc2\x3c\x47\xbf\x44\x8b\x65\x0e\x93\xd2\x2f\x8d\x75\xdd\xd1\x08\x66\x4b\xe9\x20\x93\x0a\x41\x3a\x28\x84\xf5\x60\x32\xf0\x5b\xf4\x4a\xce\xad\xb0\xd5\xb0\x3b\x1a\x05\x9e\xbd\xcb\x24\x21\xb3\x88\xe0\x4c\xe6\x57\xc2\xe2\x09\x54\xa6\x84\x44\x68\xb0\x98\x4a\xe7\xad\x9c\x97\x1e\x41\x7a\x10\x3a\x1d\x19\x0b\xb9\x49\x65\x56\x91\x48\xe9\xa1\xd4\x29\x5a\xde\xda\xa3\xcd\x5d\xad\xc7\xdb\x77\x1f\xe0\x02\x9d\x43\x0b\x6f\x51\xa3\x15\x0a\xde\x97\x73\x25\x13\xb8\x90\x09\x6a\x87\x20\x1c\x14\xf4\xc6\x2d\x31\x85\x39\x8b\x23\xc6\x37\xa4\xca\x75\x54\x05\xde\x98\x52\xa7\xc2\x4b\xa3\x07\x80\x92\x34\x87\x5b\xb4\x4e\x1a\x0d\xaf\xea\xad\xa2\xc0\x01\x18\x4b\x42\x7a\xc2\xd3\x01\x2c\x98\x82\xf8\xfa\x20\x74\x05\x4a\xf8\x35\xeb\x01\x06\x59\x9f\x3b\x05\xa9\x79\x9b\xa5\x29\x10\xfc\x52\x78\x3a\xf5\x4a\x2a\x05\x73\x84\xd2\x61\x56\xaa\x01\x49\x9b\x97\x1e\x3e\x9e\xcf\x7e\xbe\xfc\x30\x83\xc9\xbb\x4f\xf0\x71\x72\x75\x35\x79\x37\xfb\x74\x0a\x2b\xe9\x97\xa6\xf4\x80\xb7\x18\x44\xc9\xbc\x50\x12\x53\x58\x09\x6b\x85\xf6\x15\x98\x8c\x24\xfc\x32\xbd\x3a\xfb\x79\xf2\x6e\x36\xf9\xfb\xf9\xc5\xf9\xec\x13\x18\x0b\x6f\xce\x67\xef\xa6\xd7\xd7\xf0\xe6\xf2\x0a\x26\xf0\x7e\x72\x35\x3b\x3f\xfb\x70\x31\xb9\x82\xf7\x1f\xae\xde\x5f\x5e\x4f\x87\x70\x8d\xa4\x15\x12\xff\xb7\x6d\x9e\xb1\xf7\x2c\x42\x8a\x5e\x48\xe5\x6a\x4b\x7c\x32\x25\xb8\xa5\x29\x55\x0a\x4b\x71\x8b\x60\x31\x41\x79\x8b\x29\x08\x48\x4c\x51\x1d\xec\x54\x92\x25\x94\xd1\x0b\x3e\xf3\x83\x80\x84\xf3\x0c\xb4\xf1\x03\x70\x88\xf0\xe3\xd2\xfb\xe2\x64\x34\x5a\xad\x56\xc3\x85\x2e\x87\xc6\x2e\x46\x2a\x88\x73\xa3\x9f\x86\x5d\x92\x99\x08\xa5\x66\x56\x24\x68\xc9\x39\x02\xb2\x92\xcc\xaf\xcc\x4a\x83\xb7\x42\x3b\x91\x90\xab\xe9\x7f\xc2\x60\x14\x1e\xf0\x8e\x9e\xbc\x23\xd0\x82\xc5\xc2\x58\xfa\xaf\x54\x8d\x33\xa9\x3d\x5a\x2d\x14\xcb\x76\x90\x8b\x14\x61\x5e\x81\x68\x0b\x1c\xb4\x0f\x43\x30\x0a\xee\x06\xa9\x33\x63\x73\x86\xe5\xb0\xfb\xb5\xdb\x89\x1a\x3a\x2f\x92\x1b\x52\x90\xe4\x27\xa5\xb5\xa8\x3d\x99\xb2\xb4\x4e\xde\x22\x93\x40\xa0\x89\xf6\x9c\xfe\xfa\x0b\xe0\x1d\x26\x65\x90\xd4\x69\x84\x9c\xc0\xe7\xaf\xf7\x5f\x06\x5d\x16\x9d\xa2\x4b\x50\xa7\x98\xf2\xf9\x6e\x1c\xac\x96\x6c\x51\x58\xe1\xd1\x2d\xc2\x6f\xa5\xf3\x2d\x9a\xcc\x9a\x1c\x84\x06\x53\x12\xe2\xdb\xd6\x91\xda\x1b\x16\x28\xe8\xbf\x46\xcb\x1a\x0d\xbb\x9d\x86\xf9\x04\x32\xa1\x1c\xc6\x7d\x9d\xc7\x82\x4e\x23\xf5\xad\xb9\x21\xc9\xc6\x12\x84\x6d\x05\xa6\x48\x4c\x1a\x83\x81\xce\xd1\x1c\x03\xdd\xb0\xdb\x21\xbe\x13\xc8\x4a\xcd\xdb\xf6\x94\x59\x0c\x20\x9d\xf7\xe1\x6b\xb7\x43\x62\xcf\x44\xe1\x4b\x8b\x6c\x4f\xb4\xd6\x58\x07\x32\xcf\x31\x95\xc2\xa3\xaa\xba\x9d\xce\xad\xb0\x61\x01\xc6\xa0\xcc\x62\xb8\x40\x3f\xa5\xc7\x5e\xff\xb4\xdb\xe9\xc8\x0c\x7a\x61\xf5\xc9\x78\xcc\xd9\x27\x93\x1a\xd3\x20\xbe\xe3\x97\xd2\x0d\x33\x51\x2a\xdf\xec\x4b\x4c\x1d\x8b\xbe\xb4\x9a\xfe\xde\x07\x2d\x3e\x22\x18\xad\x2a\x48\x28\xcb\x88\x39\x85\xa7\xab\x9c\xc7\x3c\x1e\xce\x0d\x20\x13\x8e\x4c\x28\x33\x58\x21\x14\x16\x9f\x27\x4b\x24\xdf\xe9\x04\xa3\x96\xae\x72\xec\xd4\x31\xd0\x6e\x43\x53\x0c\xbd\x79\x57\xe6\x73\xb4\xbd\x3e\x7c\x07\xc7\x77\xd9\x71\x1f\xc6\x63\xfe\x53\xeb\x1e\x79\xa2\xbe\x24\xc5\x14\xf1\xa0\xcc\x7f\xed\xad\xd4\x8b\x70\xd6\xa8\xeb\x79\x06\x02\x34\xae\x20\x31\x9a\x41\x4d\x5e\x99\xa3\xd4\x0b\x48\x2c\x0a\x8f\xe9\x00\x44\x9a\x82\x37\x01\x79\x0d\xce\x36\xb7\x84\xef\xbe\xe3\xbd\xc6\x70\x74\x76\x35\x9d\xcc\xa6\x47\x2d\x25\xa4\xbe\xcc\xb2\xa8\x07\xf3\x0e\x0b\xc4\x9b\xde\x8b\xfe\xf0\x56\xa8\x12\x2f\xb3\xa0\x51\xa4\x9d\xea\x14\xc6\x91\xe7\xd9\x36\xcf\xcb\x0d\x1e\x62\x1a\x8d\x60\xe2\x1c\xe6\x73\x85\xbb\xb1\x17\x83\x93\xe3\xd4\x79\x4a\x4e\x04\xb4\xc4\xe4\x85\x42\x02\x50\xbd\x6b\xb4\x34\x6b\xdc\xf1\x55\x81\x27\x00\x00\xa6\x18\xf0\x0b\x82\x3d\xbf\xf0\xe6\x67\xbc\x63\x77\xd4\xd6\x22\x00\x4d\xd2\xd4\xa2\x73\xbd\x7e\x3f\x90\x4b\x5d\x94\xfe\x64\x83\x3c\xc7\xdc\xd8\x6a\xe8\x28\xf7\xf4\xf8\x68\x83\x70\xd2\x9a\x67\x21\xdc\xb9\x26\x9e\x08\xca\xb7\xc2\xf5\xd6\x4b\x67\xc6\xf9\x93\x7a\x89\x1e\xea\x35\xb6\x05\xb1\x1d\x1d\xdf\x1d\xed\x5a\xeb\xb8\xbf\x76\xfa\x8b\x1f\xfa\xc4\x72\x7f\xda\x40\xb9\xc9\x08\xc3\xa2\x74\xcb\x1e\x23\x67\xbd\xba\x8e\xfa\x31\x78\x5b\xe2\x5e\xa4\x33\x7a\x76\x91\xe3\x50\x65\x94\x36\xbc\x2d\x13\x46\xd0\x42\x70\x52\xe1\xa0\x16\x94\x64\x5d\x39\x67\x9b\x7b\x63\x1e\x04\xd2\xf5\xf4\xe2\xcd\xeb\xe9\xf5\xec\xea\xc3\xd9\xac\x0d\x27\x85\x99\x27\xa5\x36\xcf\xa0\x50\x2f\xfc\x92\xf5\x27\x71\x9b\xab\x9f\x89\xe7\xf9\x8b\x2f\xe1\x0d\x8c\xf7\x44\x77\xe7\x71\x0e\xf8\xfc\x85\x65\xdf\xef\x9a\x6f\x93\x34\x18\xf3\x6b\x00\x91\x29\xee\xdb\x39\x62\x4f\xd8\xe5\xe8\x97\x26\xe5\x3c\x98\x88\x90\x4a\x6b\x2b\xa6\x46\xe3\xc1\xc1\xd7\xab\xa3\x6f\x72\x71\x71\x04\x7f\xfc\x01\xad\xe7\xb3\xcb\xd7\xd3\xf6\xbb\xd7\xd3\x8b\xe9\xdb\xLine truncated
|
||||
|
||||
func call_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_call_tracerJs,
|
||||
"call_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func call_tracerJs() (*asset, error) {
|
||||
bytes, err := call_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "call_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _evmdis_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x57\xdf\x6f\xda\xca\x12\x7e\x86\xbf\x62\x94\x27\x50\x29\x60\x63\x08\x38\x27\x47\xe2\xa6\xf4\x1c\xae\xd2\x24\x02\x72\x8f\x2a\x94\x87\x05\xc6\xb0\xaa\xf1\x5a\xbb\x6b\x72\xb8\x55\xfe\xf7\xab\xd9\x59\x03\xf9\x75\xdb\x4a\xa7\x0f\x3b\xb5\x77\xbe\x6f\xbe\x9d\x19\xcf\x92\x56\x0b\xae\x54\xbe\xd7\x72\xbd\xb1\x10\xb6\x83\x73\x98\x6d\x10\xd6\xea\x23\xda\x0d\x6a\x2c\xb6\x30\x2c\xec\x46\x69\x53\x6d\xb5\x60\xb6\x91\x06\x12\x99\x22\x48\x03\xb9\xd0\x16\x54\x02\xf6\x85\x7f\x2a\x17\x5a\xe8\x7d\xb3\xda\x6a\x31\xe6\xcd\x6d\x62\x48\x34\x22\x18\x95\xd8\x47\xa1\x31\x86\xbd\x2a\x60\x29\x32\xd0\xb8\x92\xc6\x6a\xb9\x28\x2c\x82\xb4\x20\xb2\x55\x4b\x69\xd8\xaa\x95\x4c\xf6\x44\x29\x2d\x14\xd9\x0a\xb5\x0b\x6d\x51\x6f\x4d\xa9\xe3\x8f\x9b\x7b\xb8\x46\x63\x50\xc3\x1f\x98\xa1\x16\x29\xdc\x15\x8b\x54\x2e\xe1\x5a\x2e\x31\x33\x08\xc2\x40\x4e\x6f\xcc\x06\x57\xb0\x70\x74\x04\xfc\x4c\x52\xa6\x5e\x0a\x7c\x56\x45\xb6\x12\x56\xaa\xac\x01\x28\x49\x39\xec\x50\x1b\xa9\x32\xe8\x94\xa1\x3c\x61\x03\x94\x26\x92\x9a\xb0\x74\x00\x0d\x2a\x27\x5c\x1d\x44\xb6\x87\x54\xd8\x23\xf4\x27\x12\x72\x3c\xf7\x0a\x64\xe6\xc2\x6c\x54\x8e\x60\x37\xc2\xd2\xa9\x1f\x65\x9a\xc2\x02\xa1\x30\x98\x14\x69\x83\xd8\x16\x85\x85\xbf\xc6\xb3\x3f\x6f\xef\x67\x30\xbc\xf9\x0a\x7f\x0d\x27\x93\xe1\xcd\xec\xeb\x05\x3c\x4a\xbb\x51\x85\x05\xdc\x21\x53\xc9\x6d\x9e\x4a\x5c\xc1\xa3\xd0\x5a\x64\x76\x0f\x2a\x21\x86\x2f\xa3\xc9\xd5\x9f\xc3\x9b\xd9\xf0\x5f\xe3\xeb\xf1\xec\x2b\x28\x0d\x9f\xc7\xb3\x9b\xd1\x74\x0a\x9f\x6f\x27\x30\x84\xbb\xe1\x64\x36\xbe\xba\xbf\x1e\x4e\xe0\xee\x7e\x72\x77\x3b\x1d\x35\x61\x8a\xa4\x0a\x09\xff\xe3\x9c\x27\xae\x7a\x1a\x61\x85\x56\xc8\xd4\x94\x99\xf8\xaa\x0a\x30\x1b\x55\xa4\x2b\xd8\x88\x1d\x82\xc6\x25\xca\x1d\xae\x40\xc0\x52\xe5\xfb\x9f\x2e\x2a\x71\x89\x54\x65\x6b\x77\xe6\x77\x1b\x12\xc6\x09\x64\xca\x36\xc0\x20\xc2\x6f\x1b\x6b\xf3\xb8\xd5\x7a\x7c\x7c\x6c\xae\xb3\xa2\xa9\xf4\xba\x95\x32\x9d\x69\xfd\xde\xac\x12\x27\xee\xb6\x2b\x69\x66\x5a\x2c\x51\x83\x46\x5b\xe8\xcc\x80\x29\x92\x84\xfc\x2c\xc8\x2c\x51\x7a\xeb\xda\x04\x12\xad\xb6\x20\xc0\x92\x2f\x58\x05\x39\x6a\xda\xf4\x14\x1f\x8d\xdd\xa7\x4e\xe6\x4a\x1a\x61\x0c\x6e\x17\xe9\xbe\x59\xfd\x5e\xad\x18\x2b\x96\xdf\x62\x98\x7f\x57\xb9\x89\x61\xfe\xf0\xf4\xd0\xa8\x56\x2b\x59\x5e\x98\x0d\x9a\x18\xbe\xb7\x63\x68\x37\x20\x88\x21\x68\x40\xe8\xd6\x8e\x5b\x23\xb7\x76\xdd\xda\x73\xeb\xb9\x5b\xfb\x6e\x1d\xb8\x35\x68\xb3\x61\x74\xc0\x6e\x01\xfb\x05\xec\x18\xb0\x67\xc8\x9e\xa1\x8f\xc3\x81\x42\x8e\x14\x72\xa8\x90\x63\x85\xcc\xd2\x61\x97\x88\x59\x22\x66\xe9\x32\x4b\x97\x59\xba\xec\xd2\x65\x96\xae\x17\xdc\x75\xe7\xe9\x32\x4b\xf7\x9c\x9f\x98\xa5\xcb\x2c\x3d\x3e\x72\x8f\x01\x3d\x7f\x44\x06\xf4\x58\x7c\x8f\x01\x3d\x06\xf4\x19\xd0\xe7\xb0\xfd\x90\x9f\x3a\x6c\x98\xa5\xcf\x61\xfb\x3d\x36\x1c\xb6\xcf\x2c\x7d\x66\x19\xb0\xf8\x41\xe0\xf6\x06\x1c\x6f\xc0\xf1\x06\x3e\xab\x65\x5a\x7d\x5e\xdb\x3e\xb1\xed\xd0\xdb\x8e\xb7\x91\xb7\x5d\x6f\x7d\xe6\xdb\x3e\xf5\x6d\x9f\xfb\xb6\xe7\x3b\xd4\xc9\xf3\x05\x9e\x2f\xf0\x7c\x81\xe7\x0b\x3c\x5f\x59\xc9\xb2\x94\x65\x2d\x7d\x31\x03\x5f\xcd\xc0\x97\x33\xf0\xf5\x0c\x7c\x41\x03\x5f\xd1\xc0\x97\x34\xf0\x35\x0d\x42\xcf\x17\xf6\x63\x08\xc9\x0e\x62\xe8\x34\x20\xe8\xb4\x63\x88\xc8\x06\x31\x74\xc9\x86\x31\xf4\xc8\x76\x62\x38\x27\x1b\xc5\xd0\x27\xdb\x8d\x61\x40\x96\xf8\xa8\x6b\x3b\x44\x48\x8c\x1d\x52\x48\x94\x1d\x92\x48\x9c\x11\x69\x24\xd2\x88\x44\x12\x6b\x44\x2a\x89\x36\x22\x99\xc4\x1b\x45\xac\x23\xea\xb2\x8e\xa8\xc7\x3a\xa2\x73\xd6\x41\xdd\xe7\x00\x03\xd6\x41\xfd\x47\x3a\xa8\x01\x49\x87\xeb\x40\xd2\xe1\x7a\x90\x74\xb8\x2e\x24\x4a\xea\x43\xa7\xc3\x75\x22\x91\x52\x2f\x3a\x1d\xae\x1b\x89\xd6\xf5\x23\xf1\xfa\x8e\x0c\x7a\x81\xb7\xa1\xb7\x1d\x6f\x23\x67\xc3\xc8\x7f\x45\x91\xff\x8c\x22\xff\x1d\x45\x1d\xbf\xef\xfd\xdc\x47\xf0\x44\xdf\x79\xab\x05\x1a\x4d\x91\x5a\x1a\xfe\x32\xdb\xa9\x6f\x34\x9e\x37\x98\x81\x48\x53\x37\xc7\x54\xbe\x54\x2b\x34\x3c\x1f\x17\x88\x19\x48\x8b\x5a\xd0\x05\xa1\x76\xa8\xe9\x6e\x2c\x27\x93\xa3\x23\x4c\x22\x33\x91\x96\xc4\x7e\x86\xd2\x60\x92\xd9\xba\x59\xad\xf0\xfb\x18\x92\x22\x5b\xd2\xe8\xaa\xd5\xe1\xbb\xa7\x00\xbb\x91\xa6\xe9\x46\xd2\xbc\xfd\xd0\x54\xb9\xb9\x80\x52\x67\x22\xde\x92\x49\xd4\x62\x69\x0b\x91\x02\xfe\x8d\xcb\xc2\xcd\x42\x95\x80\xc8\xbc\x72\x48\x78\xe0\x57\x1c\xfe\x24\x6a\xaa\xd6\x0d\x58\x2d\x28\x78\x19\xc2\x58\xcc\x4f\x23\xd0\xb5\x81\x3b\xd4\xfb\x92\xcb\x5d\x83\x14\xf2\x3f\x5f\x7c\x38\x24\x6a\xc2\xbd\xc9\x5c\xad\x54\x76\x42\x43\xa2\xc5\x16\xe1\xf2\xf4\x74\xc7\xff\x36\x53\xcc\xd6\x76\x03\x1f\x21\x78\xb8\xa8\x7a\x04\x6a\xad\x34\x5c\x42\xaa\xd6\xcd\x35\xda\x11\x3d\xd6\xea\x17\xd5\x4a\x45\x26\x50\x73\xbb\x4c\x5f\x71\xdc\xf3\x33\xf7\xea\xec\x01\x2e\x19\x4a\x9e\x4f\x80\xa9\x41\x20\x80\xa7\xf9\x84\xb9\xdd\xd4\xea\x70\x79\x2a\xc5\xc7\xf7\x74\x2a\xa7\x4b\x05\x2e\xf9\xa9\xa2\xf2\x18\xe8\x1f\x11\xa8\xbc\x69\xd5\x4d\xb1\x5d\xa0\xae\xd5\x1b\x6e\x7b\x45\x84\x10\xc3\x73\x7e\xde\x2b\xcb\x3c\x7f\x70\xcf\x4f\x24\xc9\xa9\x77\x8a\xa9\xb6\xe5\xc9\x7f\x87\xb6\x8f\xee\xce\x9e\x6b\xdc\xa9\x1c\x2e\xe1\xe0\x38\x7f\x05\xe1\x64\x11\x22\x51\xba\x46\x28\x09\x97\xd0Line truncated
|
||||
|
||||
func evmdis_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_evmdis_tracerJs,
|
||||
"evmdis_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func evmdis_tracerJs() (*asset, error) {
|
||||
bytes, err := evmdis_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "evmdis_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _noop_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x93\x4f\x6f\xdb\x46\x10\xc5\xcf\xe6\xa7\x78\xc7\x04\x50\xc5\xfe\x39\x14\x70\x8b\x02\xac\x61\x27\x2a\x1c\xd9\x90\xe8\x06\x3e\x0e\xc9\xa1\xb8\xe9\x6a\x87\x9d\x9d\x95\x22\x04\xf9\xee\xc5\x92\x52\x13\x14\x69\x9b\x9b\xb0\xd2\xfb\xbd\x37\xf3\x46\x65\x89\x1b\x19\x4f\xea\x76\x83\xe1\xfb\x6f\xbf\xfb\x11\xf5\xc0\xd8\xc9\x37\x6c\x03\x2b\xa7\x3d\xaa\x64\x83\x68\x2c\xca\x12\xf5\xe0\x22\x7a\xe7\x19\x2e\x62\x24\x35\x48\x0f\xfb\xc7\xef\xbd\x6b\x94\xf4\xb4\x2c\xca\x72\xd6\x7c\xf1\xeb\x4c\xe8\x95\x19\x51\x7a\x3b\x92\xf2\x35\x4e\x92\xd0\x52\x80\x72\xe7\xa2\xa9\x6b\x92\x31\x9c\x81\x42\x57\x8a\x62\x2f\x9d\xeb\x4f\x19\xe9\x0c\x29\x74\xac\x93\xb5\xb1\xee\xe3\x25\xc7\xab\xf5\x13\xee\x39\x46\x56\xbc\xe2\xc0\x4a\x1e\x8f\xa9\xf1\xae\xc5\xbd\x6b\x39\x44\x06\x45\x8c\xf9\x25\x0e\xdc\xa1\x99\x70\x59\x78\x97\xa3\x6c\xcf\x51\x70\x27\x29\x74\x64\x4e\xc2\x02\xec\x72\x72\x1c\x58\xa3\x93\x80\x1f\x2e\x56\x67\xe0\x02\xa2\x19\xf2\x82\x2c\x0f\xa0\x90\x31\xeb\x5e\x82\xc2\x09\x9e\xec\x93\xf4\x2b\x16\xf2\x69\xee\x0e\x2e\x4c\x36\x83\x8c\x0c\x1b\xc8\xf2\xd4\x47\xe7\x3d\x1a\x46\x8a\xdc\x27\xbf\xc8\xb4\x26\x19\xde\xae\xea\xd7\x0f\x4f\x35\xaa\xf5\x33\xde\x56\x9b\x4d\xb5\xae\x9f\x7f\xc2\xd1\xd9\x20\xc9\xc0\x07\x9e\x51\x6e\x3f\x7a\xc7\x1d\x8e\xa4\x4a\xc1\x4e\x90\x3e\x13\xde\xdc\x6e\x6e\x5e\x57\xeb\xba\xfa\x75\x75\xbf\xaa\x9f\x21\x8a\xbb\x55\xbd\xbe\xdd\x6e\x71\xf7\xb0\x41\x85\xc7\x6a\x53\xaf\x6e\x9e\xee\xab\x0d\x1e\x9f\x36\x8f\x0f\xdb\xdb\x25\xb6\x9c\x53\x71\xd6\xff\xff\xce\xfb\xa9\x3d\x65\x74\x6c\xe4\x7c\xbc\x6c\xe2\x59\x12\xe2\x20\xc9\x77\x18\xe8\xc0\x50\x6e\xd9\x1d\xb8\x03\xa1\x95\xf1\xf4\xd5\xa5\x66\x16\x79\x09\xbb\x69\xe6\x7f\x3d\x48\xac\x7a\x04\xb1\x05\x22\x33\x7e\x1e\xcc\xc6\xeb\xb2\x3c\x1e\x8f\xcb\x5d\x48\x4b\xd1\x5d\xe9\x67\x5c\x2c\x7f\x59\x16\x99\x19\x44\xc6\x5a\xa9\x65\xcd\xe5\xbc\x4b\xd1\x26\x76\x43\xca\x8d\x04\x46\x23\xce\xb3\x8e\xb9\x65\xb4\xd2\xe5\x01\xfe\x4c\x4e\xb9\x43\xaf\xb2\x07\xe1\x37\x3a\xd0\xb6\x55\x37\x5a\xc6\x49\xf3\x8e\x5b\x83\xc9\x5c\x21\x35\x7e\x3a\x47\x82\x29\x85\x48\x6d\xbe\x9b\xfc\xb9\x65\x5d\x16\x1f\x8a\xab\xb2\x44\x34\x1e\xb3\xb7\x0b\x07\xf9\x23\x73\x45\x73\x9f\x7a\x82\x8c\x93\xe3\x74\x19\x39\xd4\xef\x6f\xc0\xef\xb9\x4d\xc6\x71\x59\x5c\x65\xdd\x35\xfa\x14\x26\xe8\x0b\x2f\xbb\x05\xba\xe6\x25\x3e\xe0\xe3\xa2\x98\xc8\x3d\x25\x6f\x9f\xa3\x8f\xc3\xf9\x4c\xa8\xb5\x44\xfe\x4c\xcb\x91\xa4\x07\x85\x8b\x61\x3f\x17\x78\x35\xe9\xff\xdb\x42\x39\x7e\xc9\x83\xbc\x9f\x7c\x66\x60\x9c\xab\x6f\x98\x03\x9c\xb1\x52\xbe\x7d\x39\xb0\xe6\xbf\x3d\x94\x2d\x69\x88\x13\x2e\x6b\x7a\x17\xc8\x5f\xc0\xe7\xf3\xc8\x1b\x73\x61\xb7\x2c\xae\xe6\xf7\xcf\x42\xb5\xf6\xfe\xef\x50\xc5\xc7\xe2\xaf\x00\x00\x00\xff\xff\x13\x5b\x7d\x37\xec\x04\x00\x00")
|
||||
|
||||
func noop_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_noop_tracerJs,
|
||||
"noop_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func noop_tracerJs() (*asset, error) {
|
||||
bytes, err := noop_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "noop_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _opcount_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\xcf\x6e\xdb\x46\x10\x87\xcf\xe2\x53\xfc\x8e\x09\xa2\x92\x69\x7b\x28\xe0\x16\x05\x58\xc3\x4e\x04\xd8\xb2\x21\xd1\x09\x7c\x5c\x92\x43\x71\x9b\xd5\x2e\x31\x3b\x2b\x86\x08\xfc\xee\xc5\x2e\xc5\xc6\x08\x5c\xd4\xd7\xd5\xcc\xf7\xcd\x3f\xb1\x28\x70\xe9\x86\x89\xf5\xa1\x17\xfc\xf2\xfe\xe7\xdf\x50\xf5\x84\x83\xfb\x89\xa4\x27\xa6\x70\x44\x19\xa4\x77\xec\xb3\xa2\x40\xd5\x6b\x8f\x4e\x1b\x82\xf6\x18\x14\x0b\x5c\x07\xf9\x21\xde\xe8\x9a\x15\x4f\x79\x56\x14\x73\xce\x8b\x3f\x47\x42\xc7\x44\xf0\xae\x93\x51\x31\x5d\x60\x72\x01\x8d\xb2\x60\x6a\xb5\x17\xd6\x75\x10\x82\x16\x28\xdb\x16\x8e\x71\x74\xad\xee\xa6\x88\xd4\x82\x60\x5b\xe2\xa4\x16\xe2\xa3\x5f\xea\xf8\xb0\x7d\xc0\x0d\x79\x4f\x8c\x0f\x64\x89\x95\xc1\x7d\xa8\x8d\x6e\x70\xa3\x1b\xb2\x9e\xa0\x3c\x86\xf8\xe2\x7b\x6a\x51\x27\x5c\x4c\xbc\x8e\xa5\xec\xcf\xa5\xe0\xda\x05\xdb\x2a\xd1\xce\xae\x41\x3a\x56\x8e\x13\xb1\xd7\xce\xe2\xd7\x45\x75\x06\xae\xe1\x38\x42\xde\x28\x89\x0d\x30\xdc\x10\xf3\xde\x42\xd9\x09\x46\xc9\xf7\xd4\x57\x0c\xe4\x7b\xdf\x2d\xb4\x4d\x9a\xde\x0d\x04\xe9\x95\xc4\xae\x47\x6d\x0c\x6a\x42\xf0\xd4\x05\xb3\x8e\xb4\x3a\x08\x3e\x6f\xaa\x8f\x77\x0f\x15\xca\xed\x23\x3e\x97\xbb\x5d\xb9\xad\x1e\x7f\xc7\xa8\xa5\x77\x41\x40\x27\x9a\x51\xfa\x38\x18\x4d\x2d\x46\xc5\xac\xac\x4c\x70\x5d\x24\xdc\x5e\xed\x2e\x3f\x96\xdb\xaa\xfc\x6b\x73\xb3\xa9\x1e\xe1\x18\xd7\x9b\x6a\x7b\xb5\xdf\xe3\xfa\x6e\x87\x12\xf7\xe5\xae\xda\x5c\x3e\xdc\x94\x3b\xdc\x3f\xec\xee\xef\xf6\x57\x39\xf6\x14\xab\xa2\x98\xff\xff\x33\xef\xd2\xf6\x98\xd0\x92\x28\x6d\xfc\x32\x89\x47\x17\xe0\x7b\x17\x4c\x8b\x5e\x9d\x08\x4c\x0d\xe9\x13\xb5\x50\x68\xdc\x30\xbd\x7a\xa9\x91\xa5\x8c\xb3\x87\xd4\xf3\x7f\x1e\x24\x36\x1d\xac\x93\x35\x3c\x11\xfe\xe8\x45\x86\x8b\xa2\x18\xc7\x31\x3f\xd8\x90\x3b\x3e\x14\x66\xc6\xf9\xe2\xcf\x3c\x8b\x4c\x37\x34\x2e\x58\xa9\x58\x35\xc4\x71\x3f\x0a\x5e\x1d\x07\x43\x90\xf9\x29\xed\xe5\xef\xe0\x05\x29\xd0\x27\xb5\x0d\xc7\x9a\x38\x16\xaf\xad\x17\x0e\x4d\xbc\x87\xf4\xf7\xa1\xaf\xd4\xa4\xdd\xd6\x53\x8a\xbc\xfa\x74\x8b\x9a\xba\x38\x99\x74\xc9\xac\xac\x57\x29\x3c\x5d\xb5\xb6\x4a\xa8\xcd\xb3\x6f\xd9\xaa\x28\x66\x43\x12\x7f\xf9\xd1\x13\x39\xcf\x5d\xff\x8a\xf2\x6c\x95\xd2\x2e\xf0\x7e\x9d\x25\x8a\x17\x1a\x62\x27\xda\x9e\xdc\x17\x6a\xd3\x6a\xe8\x44\x3c\xa5\x66\xdb\xf3\xa9\x45\xfc\xa7\xdb\x05\xe3\xf3\x6c\x15\xf3\x2e\xd0\x05\x9b\x0c\x6f\x8c\x3b\xac\xd1\xd6\x6f\xf1\x0d\xd2\x6b\x9f\x27\xcb\xbb\x77\x78\x3a\x6b\x3a\x15\x8c\x3c\xf7\x8c\xfd\xf9\x08\x55\x23\x41\x99\x33\x3a\x76\xea\x3a\x28\xbb\xd8\xbb\xf9\x3c\x56\x29\xff\x65\xdf\xa2\x60\xf2\x2f\x39\x94\x31\xc9\x33\x03\xfd\x7c\x58\x35\x91\x85\x16\xe2\x38\x50\xb8\x13\x71\xfc\xa8\x80\x49\x02\x5b\x9f\x70\x31\xa7\xd3\x56\x99\x05\x7c\x3e\xbe\x38\x70\x6d\x0f\x79\xb6\x9a\xdf\x9f\x15\xd5\xc8\xd7\xa5\xa8\x99\xf4\x6c\x16\x78\xca\x9e\xb2\x7f\x02\x00\x00\xff\xff\xdd\xd8\xa1\x0a\x5c\x05\x00\x00")
|
||||
|
||||
func opcount_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_opcount_tracerJs,
|
||||
"opcount_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func opcount_tracerJs() (*asset, error) {
|
||||
bytes, err := opcount_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "opcount_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x57\xdd\x6f\x1b\xb9\x11\x7f\xde\xfd\x2b\xa6\x7e\x91\x84\x53\x56\xce\x15\xb8\x02\x72\x5d\x60\xa3\x28\x89\x00\x9d\x6d\x48\x4a\x5d\xf7\x70\x0f\x5c\x72\x76\xc5\x13\x45\x2e\x48\xae\x3e\x10\xf8\x7f\x2f\x86\xfb\x21\xcb\x67\x27\x6e\xeb\x27\x2f\x39\xfc\xcd\xf7\x6f\x46\xa3\x11\x4c\x4c\x79\xb4\xb2\x58\x7b\xf8\xf9\xf2\xfd\xdf\x60\xb5\x46\x28\xcc\x3b\xf4\x6b\xb4\x58\x6d\x21\xad\xfc\xda\x58\x17\x8f\x46\xb0\x5a\x4b\x07\xb9\x54\x08\xd2\x41\xc9\xac\x07\x93\x83\x7f\x26\xaf\x64\x66\x99\x3d\x26\xf1\x68\x54\xbf\x79\xf1\x9a\x10\x72\x8b\x08\xce\xe4\x7e\xcf\x2c\x8e\xe1\x68\x2a\xe0\x4c\x83\x45\x21\x9d\xb7\x32\xab\x3c\x82\xf4\xc0\xb4\x18\x19\x0b\x5b\x23\x64\x7e\x24\x48\xe9\xa1\xd2\x02\x6d\x50\xed\xd1\x6e\x5d\x6b\xc7\xe7\x9b\xaf\x30\x47\xe7\xd0\xc2\x67\xd4\x68\x99\x82\xbb\x2a\x53\x92\xc3\x5c\x72\xd4\x0e\x81\x39\x28\xe9\xc4\xad\x51\x40\x16\xe0\xe8\xe1\x27\x32\x65\xd9\x98\x02\x9f\x4c\xa5\x05\xf3\xd2\xe8\x21\xa0\x24\xcb\x61\x87\xd6\x49\xa3\xe1\xaf\xad\xaa\x06\x70\x08\xc6\x12\x48\x9f\x79\x72\xc0\x82\x29\xe9\xdd\x00\x98\x3e\x82\x62\xfe\xf4\xf4\x0d\x01\x39\xf9\x2d\x40\xea\xa0\x66\x6d\x4a\x04\xbf\x66\x9e\xbc\xde\x4b\xa5\x20\x43\xa8\x1c\xe6\x95\x1a\x12\x5a\x56\x79\xb8\x9f\xad\xbe\xdc\x7e\x5d\x41\x7a\xf3\x00\xf7\xe9\x62\x91\xde\xac\x1e\xae\x60\x2f\xfd\xda\x54\x1e\x70\x87\x35\x94\xdc\x96\x4a\xa2\x80\x3d\xb3\x96\x69\x7f\x04\x93\x13\xc2\xaf\xd3\xc5\xe4\x4b\x7a\xb3\x4a\x3f\xcc\xe6\xb3\xd5\x03\x18\x0b\x9f\x66\xab\x9b\xe9\x72\x09\x9f\x6e\x17\x90\xc2\x5d\xba\x58\xcd\x26\x5f\xe7\xe9\x02\xee\xbe\x2e\xee\x6e\x97\xd3\x04\x96\x48\x56\x21\xbd\xff\x71\xcc\xf3\x90\x3d\x8b\x20\xd0\x33\xa9\x5c\x1b\x89\x07\x53\x81\x5b\x9b\x4a\x09\x58\xb3\x1d\x82\x45\x8e\x72\x87\x02\x18\x70\x53\x1e\xdf\x9c\x54\xc2\x62\xca\xe8\x22\xf8\xfc\x6a\x41\xc2\x2c\x07\x6d\xfc\x10\x1c\x22\xfc\x7d\xed\x7d\x39\x1e\x8d\xf6\xfb\x7d\x52\xe8\x2a\x31\xb6\x18\xa9\x1a\xce\x8d\xfe\x91\xc4\x84\x59\x5a\x74\x9e\x79\x5c\x59\xc6\xd1\x82\xa9\x7c\x59\x79\x07\xae\xca\x73\xc9\x25\x6a\x0f\x52\xe7\xc6\x6e\x43\xa5\x80\x37\xc0\x2d\x32\x8f\xc0\x40\x19\xce\x14\xe0\x01\x79\x15\xee\xea\x48\x87\x72\xb5\x4c\x3b\xc6\xc3\x69\x6e\xcd\x96\x7c\xad\x9c\xa7\x7f\x9c\xc3\x6d\xa6\x50\x40\x81\x1a\x9d\x74\x90\x29\xc3\x37\x49\xfc\x2d\x8e\x9e\x18\x43\x75\x12\x3c\x6c\x84\x42\x6d\xec\xb1\x67\x11\xb2\x4a\x2a\x21\x75\x91\xc4\x51\x2b\x3d\x06\x5d\x29\x35\x8c\x03\x84\x32\x66\x53\x95\x29\xe7\xa6\x0a\xb6\xff\x81\xdc\xd7\x60\xae\x44\x2e\x73\x2a\x0e\xd6\xdd\x7a\x13\xae\x3a\xbd\x26\x23\xf9\x24\x8e\xce\x60\xc6\x90\x57\x3a\xb8\xd3\x67\x42\xd8\x21\x88\x6c\xf0\x2d\x8e\xa2\x1d\xb3\x84\x05\xd7\xe0\xcd\x17\x3c\x84\xcb\xc1\x55\x1c\x45\x32\x87\xbe\x5f\x4b\x97\xb4\xc0\xbf\x31\xce\x7f\x87\xeb\xeb\xeb\xd0\xd4\xb9\xd4\x28\x06\x40\x10\xd1\x4b\x62\xf5\x4d\x94\x31\xc5\x34\xc7\x31\xf4\x2e\x0f\x3d\xf8\x09\x44\x96\x14\xe8\x3f\xd4\xa7\xb5\xb2\xc4\x9b\xa5\xb7\x52\x17\xfd\xf7\xbf\x0c\x86\xe1\x95\x36\xe1\x0d\x34\xe2\x37\xa6\x13\xae\xef\xb9\x11\xe1\xba\xb1\xb9\x96\x9a\x18\xd1\x08\x35\x52\xce\x1b\xcb\x0a\x1c\xc3\xb7\x47\xfa\x7e\x24\xaf\x1e\xe3\xe8\xf1\x2c\xca\xcb\x5a\xe8\x95\x28\x37\x10\x80\xda\xdb\xae\xce\x0b\x49\x9d\xfa\x34\x01\x01\xef\x7b\x49\x58\xb6\xa6\x3c\x4b\xc2\x06\x8f\x3f\xce\x04\x5d\x48\x71\xe8\x2e\x36\x78\x1c\x5c\xc5\xaf\xa6\x28\x69\x8c\xfe\x4d\x8a\xc3\xcb\xf9\x22\xc0\x1d\x53\x1d\x60\x1d\xbf\x25\x21\x9c\xec\x1a\x04\xdd\x41\x07\xc9\xfe\xe5\x1a\x2e\x2e\x0f\x97\xff\xe7\xdf\x45\x63\xc1\x0b\x25\xf3\xcc\xec\x37\x98\xf6\x78\x9e\x4f\x8b\xae\x52\x9e\xda\x4e\xea\x9d\xd9\x10\x81\xae\x29\x4f\x4a\x85\xd4\x98\x92\xaa\xc6\xd5\x0c\x96\x21\x6a\x90\x1e\x2d\x23\x0a\x37\x3b\xb4\x34\xbd\xc0\xa2\xaf\xac\x76\x5d\x3a\x73\xa9\x99\x6a\x81\x9b\xec\x7b\xcb\x78\xdd\xbb\xf5\xf9\x93\x9c\x72\x7f\x08\xd9\x0c\x3e\x8e\x46\x90\x7a\x20\x3f\xa1\x34\x52\xfb\x21\xec\x11\x34\xa2\x20\x02\x12\x28\x2a\xee\x03\x5e\x6f\xc7\x54\x85\xbd\x9a\x64\x88\xaa\xc3\x53\x53\xd1\x44\x7a\x42\x42\xc3\x60\xe0\xd6\xec\xc2\xa8\xcd\x18\xdf\x40\xd3\xf8\xc6\xca\x42\xea\xb8\x89\xe9\x59\xd3\x93\x45\x09\x01\x07\xb3\x42\xcd\x50\xee\xe9\xe4\x43\xc8\x7f\x26\x8b\x99\xf6\xcf\x8a\xa8\x8e\x7c\xfb\x74\xf0\x7b\xd2\x34\x71\xe2\x88\x78\xfb\x3f\x0f\x86\xf0\xfe\x97\xae\x32\xbd\x21\x28\xf8\x31\x98\x37\xaf\x43\xc5\xcf\x2b\xe2\xe5\x67\x41\x0d\x31\xc9\x4f\x41\x6b\xe2\xaa\x8c\xd2\x51\xfb\x19\xe2\x78\xce\x26\x57\xdf\xc1\x3d\xf7\xad\xc5\x6d\x42\x93\x30\x21\x5e\x07\xad\x53\xf4\x11\xb9\xc5\x2d\x4d\x17\xca\x02\x67\x4a\xa1\xed\x39\x08\xdc\x35\x6c\xca\x29\xe4\x0b\xb7\xa5\x3f\xb6\x33\xc7\x33\x5b\xa0\x77\x3f\x36\x2c\xe0\xbc\x7b\xd7\x52\x71\x08\xc5\xb1\x44\xb8\xbe\x86\xde\x64\x31\x4d\x57\xd3\x5e\xd3\x4c\xa3\x11\xdc\x63\xd8\xc8\x32\x25\x33\xa1\x8e\x20\x50\xa1\xc7\xda\x2e\xa3\x43\x88\x3a\x6a\x1a\xd2\x6a\x45\x4b\x0f\x1e\xa4\xf3\x52\x17\x50\x33\xd6\x9e\xe6\x7b\x03\x17\x7a\x84\xb3\xca\x51\xb5\x3e\x1b\x86\xde\xd0\x66\x63\x91\xf8\x8d\xe6\x50\x68\x37\xa6\x64\xb7\x09\xe5\xd2\x3a\x0f\xa5\x62\x1c\x13\xc2\xeb\x8c\x79\x3d\xbf\x0d\x33\x93\xea\x45\x68\xc1\x00\x74\x1a\xb4\x4c\xd1\xa0\x26\xf5\x0e\xfa\x2d\xc6\x20\x8e\x22\xdb\x4a\x3f\xc1\xbe\x3a\x51\x82\xf3\x58\x3e\x25\x04\x5a\x70\x70\x87\x44\xLine truncated
|
||||
|
||||
func prestate_tracerJsBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_prestate_tracerJs,
|
||||
"prestate_tracer.js",
|
||||
)
|
||||
}
|
||||
|
||||
func prestate_tracerJs() (*asset, error) {
|
||||
bytes, err := prestate_tracerJsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "prestate_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"4byte_tracer.js": _4byte_tracerJs,
|
||||
"call_tracer.js": call_tracerJs,
|
||||
"evmdis_tracer.js": evmdis_tracerJs,
|
||||
"noop_tracer.js": noop_tracerJs,
|
||||
"opcount_tracer.js": opcount_tracerJs,
|
||||
"prestate_tracer.js": prestate_tracerJs,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"4byte_tracer.js": {_4byte_tracerJs, map[string]*bintree{}},
|
||||
"call_tracer.js": {call_tracerJs, map[string]*bintree{}},
|
||||
"evmdis_tracer.js": {evmdis_tracerJs, map[string]*bintree{}},
|
||||
"noop_tracer.js": {noop_tracerJs, map[string]*bintree{}},
|
||||
"opcount_tracer.js": {opcount_tracerJs, map[string]*bintree{}},
|
||||
"prestate_tracer.js": {prestate_tracerJs, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
// 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/>.
|
||||
|
||||
// callTracer is a full blown transaction tracer that extracts and reports all
|
||||
// the internal calls made by a transaction, along with any useful information.
|
||||
{
|
||||
// callstack is the current recursive call stack of the EVM execution.
|
||||
callstack: [{}],
|
||||
|
||||
// descended tracks whether we've just descended from an outer transaction into
|
||||
// an inner call.
|
||||
descended: false,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Capture any errors immediately
|
||||
var error = log.getError();
|
||||
if (error !== undefined) {
|
||||
this.fault(log, db);
|
||||
return;
|
||||
}
|
||||
// We only care about system opcodes, faster if we pre-check once
|
||||
var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
|
||||
if (syscall) {
|
||||
var op = log.op.toString();
|
||||
}
|
||||
// If a new contract is being created, add to the call stack
|
||||
if (syscall && op == 'CREATE') {
|
||||
var inOff = log.stack.peek(1).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(2).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
value: '0x' + log.stack.peek(0).toString(16)
|
||||
};
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If a contract is being self destructed, gather that as a subcall too
|
||||
if (syscall && op == 'SELFDESTRUCT') {
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push({type: op});
|
||||
return
|
||||
}
|
||||
// If a new method invocation is being done, add to the call stack
|
||||
if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
var to = toAddress(log.stack.peek(1).toString(16));
|
||||
if (isPrecompiled(to)) {
|
||||
return
|
||||
}
|
||||
var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
|
||||
|
||||
var inOff = log.stack.peek(2 + off).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(3 + off).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
to: toHex(to),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
outOff: log.stack.peek(4 + off).valueOf(),
|
||||
outLen: log.stack.peek(5 + off).valueOf()
|
||||
};
|
||||
if (op != 'DELEGATECALL' && op != 'STATICCALL') {
|
||||
call.value = '0x' + log.stack.peek(2).toString(16);
|
||||
}
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If we've just descended into an inner call, retrieve it's true allowance. We
|
||||
// need to extract if from within the call as there may be funky gas dynamics
|
||||
// with regard to requested and actually given gas (2300 stipend, 63/64 rule).
|
||||
if (this.descended) {
|
||||
if (log.getDepth() >= this.callstack.length) {
|
||||
this.callstack[this.callstack.length - 1].gas = log.getGas();
|
||||
} else {
|
||||
// TODO(karalabe): The call was made to a plain account. We currently don't
|
||||
// have access to the true gas amount inside the call and so any amount will
|
||||
// mostly be wrong since it depends on a lot of input args. Skip gas for now.
|
||||
}
|
||||
this.descended = false;
|
||||
}
|
||||
// If an existing call is returning, pop off the call stack
|
||||
if (syscall && op == 'REVERT') {
|
||||
this.callstack[this.callstack.length - 1].error = "execution reverted";
|
||||
return;
|
||||
}
|
||||
if (log.getDepth() == this.callstack.length - 1) {
|
||||
// Pop off the last call and get the execution results
|
||||
var call = this.callstack.pop();
|
||||
|
||||
if (call.type == 'CREATE') {
|
||||
// If the call was a CREATE, retrieve the contract address and output code
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.to = toHex(toAddress(ret.toString(16)));
|
||||
call.output = toHex(db.getCode(toAddress(ret.toString(16))));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
} else {
|
||||
// If the call was a contract call, retrieve the gas usage and output
|
||||
if (call.gas !== undefined) {
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
|
||||
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
}
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
}
|
||||
// Inject the call into the previous one
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) {
|
||||
// If the topmost call already reverted, don't handle the additional fault again
|
||||
if (this.callstack[this.callstack.length - 1].error !== undefined) {
|
||||
return;
|
||||
}
|
||||
// Pop off the just failed call
|
||||
var call = this.callstack.pop();
|
||||
call.error = log.getError();
|
||||
|
||||
// Consume all available gas and clean any leftovers
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
call.gasUsed = call.gas
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
|
||||
// Flatten the failed call into its parent
|
||||
var left = this.callstack.length;
|
||||
if (left > 0) {
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
return;
|
||||
}
|
||||
// Last call failed too, leave it in the stack
|
||||
this.callstack.push(call);
|
||||
},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) {
|
||||
var result = {
|
||||
type: ctx.type,
|
||||
from: toHex(ctx.from),
|
||||
to: toHex(ctx.to),
|
||||
value: '0x' + ctx.value.toString(16),
|
||||
gas: '0x' + bigInt(ctx.gas).toString(16),
|
||||
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
|
||||
input: toHex(ctx.input),
|
||||
output: toHex(ctx.output),
|
||||
time: ctx.time,
|
||||
};
|
||||
if (this.callstack[0].calls !== undefined) {
|
||||
result.calls = this.callstack[0].calls;
|
||||
}
|
||||
if (this.callstack[0].error !== undefined) {
|
||||
result.error = this.callstack[0].error;
|
||||
} else if (ctx.error !== undefined) {
|
||||
result.error = ctx.error;
|
||||
}
|
||||
if (result.error !== undefined) {
|
||||
delete result.output;
|
||||
}
|
||||
return this.finalize(result);
|
||||
},
|
||||
|
||||
// finalize recreates a call object using the final desired field oder for json
|
||||
// serialization. This is a nicety feature to pass meaningfully ordered results
|
||||
// to users who don't interpret it, just display it.
|
||||
finalize: function(call) {
|
||||
var sorted = {
|
||||
type: call.type,
|
||||
from: call.from,
|
||||
to: call.to,
|
||||
value: call.value,
|
||||
gas: call.gas,
|
||||
gasUsed: call.gasUsed,
|
||||
input: call.input,
|
||||
output: call.output,
|
||||
error: call.error,
|
||||
time: call.time,
|
||||
calls: call.calls,
|
||||
}
|
||||
for (var key in sorted) {
|
||||
if (sorted[key] === undefined) {
|
||||
delete sorted[key];
|
||||
}
|
||||
}
|
||||
if (sorted.calls !== undefined) {
|
||||
for (var i=0; i<sorted.calls.length; i++) {
|
||||
sorted.calls[i] = this.finalize(sorted.calls[i]);
|
||||
}
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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/>.
|
||||
|
||||
// evmdisTracer returns sufficent information from a trace to perform evmdis-style
|
||||
// disassembly.
|
||||
{
|
||||
stack: [{ops: []}],
|
||||
|
||||
npushes: {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 21: 1, 22: 1, 23: 1, 24: 1, 25: 1, 26: 1, 32: 1, 48: 1, 49: 1, 50: 1, 51: 1, 52: 1, 53: 1, 54: 1, 55: 0, 56: 1, 57: 0, 58: 1, 59: 1, 60: 0, 64: 1, 65: 1, 66: 1, 67: 1, 68: 1, 69: 1, 80: 0, 81: 1, 82: 0, 83: 0, 84: 1, 85: 0, 86: 0, 87: 0, 88: 1, 89: 1, 90: 1, 91: 0, 96: 1, 97: 1, 98: 1, 99: 1, 100: 1, 101: 1, 102: 1, 103: 1, 104: 1, 105: 1, 106: 1, 107: 1, 108: 1, 109: 1, 110: 1, 111: 1, 112: 1, 113: 1, 114: 1, 115: 1, 116: 1, 117: 1, 118: 1, 119: 1, 120: 1, 121: 1, 122: 1, 123: 1, 124: 1, 125: 1, 126: 1, 127: 1, 128: 2, 129: 3, 130: 4, 131: 5, 132: 6, 133: 7, 134: 8, 135: 9, 136: 10, 137: 11, 138: 12, 139: 13, 140: 14, 141: 15, 142: 16, 143: 17, 144: 2, 145: 3, 146: 4, 147: 5, 148: 6, 149: 7, 150: 8, 151: 9, 152: 10, 153: 11, 154: 12, 155: 13, 156: 14, 157: 15, 158: 16, 159: 17, 160: 0, 161: 0, 162: 0, 163: 0, 164: 0, 240: 1, 241: 1, 242: 1, 243: 0, 244: 0, 255: 0},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function() { return this.stack[0].ops; },
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
var frame = this.stack[this.stack.length - 1];
|
||||
|
||||
var error = log.getError();
|
||||
if (error) {
|
||||
frame["error"] = error;
|
||||
} else if (log.getDepth() == this.stack.length) {
|
||||
opinfo = {
|
||||
op: log.op.toNumber(),
|
||||
depth : log.getDepth(),
|
||||
result: [],
|
||||
};
|
||||
if (frame.ops.length > 0) {
|
||||
var prevop = frame.ops[frame.ops.length - 1];
|
||||
for(var i = 0; i < this.npushes[prevop.op]; i++)
|
||||
prevop.result.push(log.stack.peek(i).toString(16));
|
||||
}
|
||||
switch(log.op.toString()) {
|
||||
case "CALL": case "CALLCODE":
|
||||
var instart = log.stack.peek(3).valueOf();
|
||||
var insize = log.stack.peek(4).valueOf();
|
||||
opinfo["gas"] = log.stack.peek(0).valueOf();
|
||||
opinfo["to"] = log.stack.peek(1).toString(16);
|
||||
opinfo["value"] = log.stack.peek(2).toString();
|
||||
opinfo["input"] = log.memory.slice(instart, instart + insize);
|
||||
opinfo["error"] = null;
|
||||
opinfo["return"] = null;
|
||||
opinfo["ops"] = [];
|
||||
this.stack.push(opinfo);
|
||||
break;
|
||||
case "DELEGATECALL": case "STATICCALL":
|
||||
var instart = log.stack.peek(2).valueOf();
|
||||
var insize = log.stack.peek(3).valueOf();
|
||||
opinfo["op"] = log.op.toString();
|
||||
opinfo["gas"] = log.stack.peek(0).valueOf();
|
||||
opinfo["to"] = log.stack.peek(1).toString(16);
|
||||
opinfo["input"] = log.memory.slice(instart, instart + insize);
|
||||
opinfo["error"] = null;
|
||||
opinfo["return"] = null;
|
||||
opinfo["ops"] = [];
|
||||
this.stack.push(opinfo);
|
||||
break;
|
||||
case "RETURN":
|
||||
var out = log.stack.peek(0).valueOf();
|
||||
var outsize = log.stack.peek(1).valueOf();
|
||||
frame.return = log.memory.slice(out, out + outsize);
|
||||
break;
|
||||
case "STOP": case "SUICIDE":
|
||||
frame.return = log.memory.slice(0, 0);
|
||||
break;
|
||||
case "JUMPDEST":
|
||||
opinfo["pc"] = log.getPC();
|
||||
}
|
||||
if(log.op.isPush()) {
|
||||
opinfo["len"] = log.op.toNumber() - 0x5e;
|
||||
}
|
||||
frame.ops.push(opinfo);
|
||||
} else {
|
||||
this.stack = this.stack.slice(0, log.getDepth());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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/>.
|
||||
|
||||
// noopTracer is just the barebone boilerplate code required from a JavaScript
|
||||
// object to be usable as a transaction tracer.
|
||||
{
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) { },
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) { }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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/>.
|
||||
|
||||
// opcountTracer is a sample tracer that just counts the number of instructions
|
||||
// executed by the EVM before the transaction terminated.
|
||||
{
|
||||
// count tracks the number of EVM instructions executed.
|
||||
count: 0,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) { this.count++ },
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) { return this.count }
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// 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/>.
|
||||
|
||||
// prestateTracer outputs sufficient information to create a local execution of
|
||||
// the transaction from a custom assembled genesis block.
|
||||
{
|
||||
// prestate is the genesis that we're building.
|
||||
prestate: null,
|
||||
|
||||
// lookupAccount injects the specified account into the prestate object.
|
||||
lookupAccount: function(addr, db){
|
||||
var acc = toHex(addr);
|
||||
if (this.prestate[acc] === undefined) {
|
||||
this.prestate[acc] = {
|
||||
balance: '0x' + db.getBalance(addr).toString(16),
|
||||
nonce: db.getNonce(addr),
|
||||
code: toHex(db.getCode(addr)),
|
||||
storage: {}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// lookupStorage injects the specified storage entry of the given account into
|
||||
// the prestate object.
|
||||
lookupStorage: function(addr, key, db){
|
||||
var acc = toHex(addr);
|
||||
var idx = toHex(key);
|
||||
|
||||
if (this.prestate[acc].storage[idx] === undefined) {
|
||||
var val = toHex(db.getState(addr, key));
|
||||
if (val != "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
||||
this.prestate[acc].storage[idx] = toHex(db.getState(addr, key));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) {
|
||||
// At this point, we need to deduct the 'value' from the
|
||||
// outer transaction, and move it back to the origin
|
||||
this.lookupAccount(ctx.from, db);
|
||||
|
||||
var fromBal = bigInt(this.prestate[toHex(ctx.from)].balance.slice(2), 16);
|
||||
var toBal = bigInt(this.prestate[toHex(ctx.to)].balance.slice(2), 16);
|
||||
|
||||
this.prestate[toHex(ctx.to)].balance = '0x'+toBal.subtract(ctx.value).toString(16);
|
||||
this.prestate[toHex(ctx.from)].balance = '0x'+fromBal.add(ctx.value).toString(16);
|
||||
|
||||
// Decrement the caller's nonce, and remove empty create targets
|
||||
this.prestate[toHex(ctx.from)].nonce--;
|
||||
if (ctx.type == 'CREATE') {
|
||||
// We can blibdly delete the contract prestate, as any existing state would
|
||||
// have caused the transaction to be rejected as invalid in the first place.
|
||||
delete this.prestate[toHex(ctx.to)];
|
||||
}
|
||||
// Return the assembled allocations (prestate)
|
||||
return this.prestate;
|
||||
},
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Add the current account if we just started tracing
|
||||
if (this.prestate === null){
|
||||
this.prestate = {};
|
||||
// Balance will potentially be wrong here, since this will include the value
|
||||
// sent along with the message. We fix that in 'result()'.
|
||||
this.lookupAccount(log.contract.getAddress(), db);
|
||||
}
|
||||
// Whenever new state is accessed, add it to the prestate
|
||||
switch (log.op.toString()) {
|
||||
case "EXTCODECOPY": case "EXTCODESIZE": case "BALANCE":
|
||||
this.lookupAccount(toAddress(log.stack.peek(0).toString(16)), db);
|
||||
break;
|
||||
case "CREATE":
|
||||
var from = log.contract.getAddress();
|
||||
this.lookupAccount(toContract(from, db.getNonce(from)), db);
|
||||
break;
|
||||
case "CALL": case "CALLCODE": case "DELEGATECALL": case "STATICCALL":
|
||||
this.lookupAccount(toAddress(log.stack.peek(1).toString(16)), db);
|
||||
break;
|
||||
case 'SSTORE':case 'SLOAD':
|
||||
this.lookupStorage(log.contract.getAddress(), toWord(log.stack.peek(0).toString(16)), db);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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/>.
|
||||
|
||||
//go:generate go-bindata -nometadata -o assets.go -pkg tracers -ignore ((tracers)|(assets)).go ./...
|
||||
//go:generate gofmt -s -w assets.go
|
||||
|
||||
// Package tracers contains the actual JavaScript tracer assets.
|
||||
package tracers
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3755480783",
|
||||
"gasLimit": "5401723",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"number": "2294702",
|
||||
"timestamp": "1513676146"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x13e4acefe6a6700604929946e70e6443e4e73447": {
|
||||
"balance": "0xcf3e0938579f000",
|
||||
"code": "0x",
|
||||
"nonce": "9",
|
||||
"storage": {}
|
||||
},
|
||||
"0x7dc9c9730689ff0b0fd506c67db815f12d90a448": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "0",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3757315409",
|
||||
"extraData": "0x566961425443",
|
||||
"gasLimit": "5406414",
|
||||
"hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1",
|
||||
"nonce": "0x93363bbd2c95f410",
|
||||
"number": "2294701",
|
||||
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||
"timestamp": "1513676127",
|
||||
"totalDifficulty": "7160808139332585"
|
||||
},
|
||||
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||
"result": {
|
||||
"from": "0x13e4acefe6a6700604929946e70e6443e4e73447",
|
||||
"gas": "0x5e106",
|
||||
"gasUsed": "0x5e106",
|
||||
"input": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11",
|
||||
"output": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029",
|
||||
"to": "0x7dc9c9730689ff0b0fd506c67db815f12d90a448",
|
||||
"type": "CREATE",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "117066904",
|
||||
"gasLimit": "4712384",
|
||||
"miner": "0x1977c248e1014cc103929dd7f154199c916e39ec",
|
||||
"number": "25001",
|
||||
"timestamp": "1479891545"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100825760e060020a600035046302d05d3f811461008a5780630accce061461009c5780631ab9075a146100c757806331ed274614610102578063645a3b7214610133578063772fdae314610155578063a7f4377914610180578063ae5f80801461019e578063c9bded21146101ea578063f905c15a14610231575b61023a610002565b61023c600054600160a060020a031681565b61023a600435602435604435606435608435600254600160a060020a03166000141561024657610002565b61023a600435600254600160a060020a03166000148015906100f8575060025433600160a060020a03908116911614155b156102f457610002565b61023a60043560243560443560643560843560a43560c435600254600160a060020a03166000141561031657610002565b61023a600435602435600254600160a060020a0316600014156103d057610002565b61023a600435602435604435606435608435600254600160a060020a03166000141561046157610002565b61023a60025433600160a060020a0390811691161461051657610002565b61023a6004356024356044356060828152600160a060020a0382169060ff8516907fa6c2f0913db6f79ff0a4365762c61718973b3413d6e40382e704782a9a5099f690602090a3505050565b61023a600435602435600160a060020a038116606090815260ff8316907fee6348a7ec70f74e3d6cba55a53e9f9110d180d7698e9117fc466ae29a43e34790602090a25050565b61023c60035481565b005b6060908152602090f35b60025460e060020a6313bc6d4b02606090815233600160a060020a0390811660645291909116906313bc6d4b906084906020906024816000876161da5a03f115610002575050604051511515905061029d57610002565b60408051858152602081018390528151600160a060020a03858116939087169260ff8a16927f5a690ecd0cb15c1c1fd6b6f8a32df0d4f56cb41a54fea7e94020f013595de796929181900390910190a45050505050565b6002805473ffffffffffffffffffffffffffffffffffffffff19168217905550565b60025460e060020a6313bc6d4b02606090815233600160a060020a0390811660645291909116906313bc6d4b906084906020906024816000876161da5a03f115610002575050604051511515905061036d57610002565b6040805186815260208101869052808201859052606081018490529051600160a060020a03831691889160ff8b16917fd65d9ddafbad8824e2bbd6f56cc9f4ac27ba60737035c10a321ea2f681c94d47919081900360800190a450505050505050565b60025460e060020a6313bc6d4b02606090815233600160a060020a0390811660645291909116906313bc6d4b906084906020906024816000876161da5a03f115610002575050604051511515905061042757610002565b60408051828152905183917fa9c6cbc4bd352a6940479f6d802a1001550581858b310d7f68f7bea51218cda6919081900360200190a25050565b60025460e060020a6313bc6d4b02606090815233600160a060020a0390811660645291909116906313bc6d4b906084906020906024816000876161da5a03f11561000257505060405151151590506104b857610002565b80600160a060020a031684600160a060020a03168660ff167f69bdaf789251e1d3a0151259c0c715315496a7404bce9fd0b714674685c2cab78686604051808381526020018281526020019250505060405180910390a45050505050565b600254600160a060020a0316ff",
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396"
|
||||
}
|
||||
},
|
||||
"0x2cccf5e0538493c235d1c5ef6580f77d99e91396": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100775760e060020a600035046302d05d3f811461007f57806313bc6d4b146100915780633688a877146100b95780635188f9961461012f5780637eadc976146101545780638ad79680146101d3578063a43e04d814610238578063a7f437791461025e578063e16c7d981461027c575b61029f610002565b6102a1600054600160a060020a031681565b6102be600435600160a060020a03811660009081526002602052604090205460ff165b919050565b6102d26004356040805160208181018352600080835284815260038252835190849020805460026001821615610100026000190190911604601f8101849004840283018401909552848252929390929183018282801561037d5780601f106103525761010080835404028352916020019161037d565b61029f6004356024356000805433600160a060020a039081169116146104a957610002565b61034060043560008181526001602090815260408083205481517ff905c15a0000000000000000000000000000000000000000000000000000000081529151600160a060020a03909116928392839263f905c15a92600483810193919291829003018189876161da5a03f1156100025750506040515195945050505050565b60408051602060248035600481810135601f810185900485028601850190965285855261029f9581359591946044949293909201918190840183828082843750949650505050505050600054600160a060020a0390811633909116146104f657610002565b61029f6004355b600080548190600160a060020a0390811633909116146105a457610002565b61029f60005433600160a060020a0390811691161461072957610002565b6102a1600435600081815260016020526040902054600160a060020a03166100b4565b005b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103325780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161036057829003601f168201915b505050505090506100b4565b506000828152600160208181526040808420805473ffffffffffffffffffffffffffffffffffffffff191686179055600160a060020a038581168086526002909352818520805460ff191690941790935580517f1ab9075a0000000000000000000000000000000000000000000000000000000081523090931660048401525184939192631ab9075a926024828101939192829003018183876161da5a03f11561000257505060408051602081018690528082019290925243606083015260808083526003908301527f414444000000000000000000000000000000000000000000000000000000000060a0830152517f8ac68d4e97d65912f220b4c5f87978b8186320a5e378c1369850b5b5f90323d39181900360c00190a15b505050565b600083815260016020526040902054600160a060020a03838116911614156104d0576104a4565b600083815260016020526040812054600160a060020a031614610389576103898361023f565b600082815260036020908152604082208054845182855293839020919360026001831615610100026000190190921691909104601f90810184900483019391929186019083901061056a57805160ff19168380011785555b5061059a9291505b808211156105a05760008155600101610556565b8280016001018555821561054e579182015b8281111561054e57825182600050559160200191906001019061057c565b50505050565b5090565b600083815260016020526040812054600160a060020a031614156105c757610002565b50506000818152600160205260408082205481517fa7f437790000000000000000000000000000000000000000000000000000000081529151600160a060020a0391909116928392839263a7f4377992600483810193919291829003018183876161da5a03f11561000257505050600160005060008460001916815260200190815260200160002060006101000a815490600160a060020a0302191690556002600050600083600160a060020a0316815260200190815260200160002060006101000a81549060ff02191690557f8ac68d4e97d65912f220b4c5f87978b8186320a5e378c1369850b5b5f90323d383834360405180806020018560001916815260200184600160a060020a03168152602001838152602001828103825260038152602001807f44454c000000000000000000000000000000000000000000000000000000000081526020015060200194505050505060405180910390a1505050565b600054600160a060020a0316ff",
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0684ac65a9fa32414dda56996f4183597d695987fdb82b145d722743891a6fe8": "0x0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"0x1cd76f78169a420d99346e3501dd3e541622c38a226f9b63e01cfebc69879dc7": "0x000000000000000000000000b4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"0x8e54a4494fe5da016bfc01363f4f6cdc91013bb5434bd2a4a3359f13a23afa2f": "0x000000000000000000000000cf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"0x94edf7f600ba56655fd65fca1f1424334ce369326c1dc3e53151dcd1ad06bc13": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0xbbee47108b275f55f98482c6800f6372165e88b0330d3f5dae6419df4734366c": "0x0000000000000000000000002a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"0xd38c0c4e84de118cfdcc775130155d83b8bbaaf23dc7f3c83a626b10473213bd": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0xfb3aa5c655c2ec9d40609401f88d505d1da61afaa550e36ef5da0509ada257ba": "0x0000000000000000000000007986bad81f4cbd9317f5a46861437dae58d69113"
|
||||
}
|
||||
},
|
||||
"0x3e9286eafa2db8101246c2131c09b49080d00690": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100cf5760e060020a600035046302d05d3f81146100d7578063056d4470146100e957806316c66cc61461010c5780631ab9075a146101935780633ae1005c146101ce57806358541662146101fe5780635ed61af014610231578063644e3b791461025457806384dbac3b146102db578063949ae479146102fd5780639859387b14610321578063a7f4377914610340578063ab03fc261461035e578063e8161b7814610385578063e964d4e114610395578063f905c15a146103a5578063f92eb774146103ae575b6103be610002565b6103c0600054600160a060020a031681565b6103be6004356002546000908190600160a060020a031681141561040357610002565b6103dd60043560006108365b6040805160025460e360020a631c2d8fb30282527f636f6e747261637464620000000000000000000000000000000000000000000060048301529151600092600160a060020a03169163e16c7d98916024828101926020929190829003018187876161da5a03f1156100025750506040515191506104e29050565b6103be600435600254600160a060020a03166000148015906101c4575060025433600160a060020a03908116911614155b1561088d57610002565b6103be600435602435604435606435600254600090819081908190600160a060020a03168114156108af57610002565b6103c0600435602435604435606435608435600254600090819081908190600160a060020a03168114156110e857610002565b6103be6004356002546000908190600160a060020a03168114156115ec57610002565b6103c06004356000611b635b6040805160025460e360020a631c2d8fb30282527f6d61726b6574646200000000000000000000000000000000000000000000000060048301529151600092600160a060020a03169163e16c7d98916024828101926020929190829003018187876161da5a03f1156100025750506040515191506104e29050565b6103be600435602435600254600160a060020a031660001415611bb557610002565b6103be600435602435600254600090600160a060020a0316811415611d2e57610002565b6103be600435600254600160a060020a031660001415611fc657610002565b6103be60025433600160a060020a0390811691161461207e57610002565b6103be600435602435604435600254600090600160a060020a031681141561208c57610002565b6103dd60043560006124b8610260565b6103c0600435600061250a610118565b6103f160035481565b6103f16004356000612561610260565b005b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60408051918252519081900360200190f35b6040805160025460e060020a6313bc6d4b02825233600160a060020a03908116600484015292519216916313bc6d4b9160248181019260209290919082900301816000876161da5a03f115610002575050604051511515905061046557610002565b8291506104e55b6040805160025460e360020a631c2d8fb30282527f63706f6f6c00000000000000000000000000000000000000000000000000000060048301529151600092600160a060020a03169163e16c7d98916024828101926020929190829003018187876161da5a03f115610002575050604051519150505b90565b600160a060020a031663b2206e6d83600160a060020a0316632e94420f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040805180517fb2206e6d0000000000000000000000000000000000000000000000000000000082526004820152600160a060020a038816602482015290516044808301935060209282900301816000876161da5a03f11561000257505060405151915061059b90506106ba565b600160a060020a031663d5b205ce83600160a060020a03166336da44686040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e160020a636ad902e7028252600160a060020a0390811660048301526024820187905288166044820152905160648281019350600092829003018183876161da5a03f115610002575050506107355b6040805160025460e360020a631c2d8fb30282527f6c6f676d6772000000000000000000000000000000000000000000000000000060048301529151600092600160a060020a03169163e16c7d98916024828101926020929190829003018187876161da5a03f1156100025750506040515191506104e29050565b50826120ee5b6040805160025460e360020a631c2d8fb30282527f6163636f756e7463746c0000000000000000000000000000000000000000000060048301529151600092600160a060020a03169163e16c7d98916024828101926020929190829003018187876161da5a03f1156100025750506040515191506104e29050565b600160a060020a0316630accce06600684600160a060020a0316632e94420f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e360020a6306db488d02825291519192899290916336da446891600482810192602092919082900301816000876161da5a03f1156100025750505060405180519060200150866040518660e060020a028152600401808681526020018560001916815260200184600160a060020a0316815260200183600160a060020a03168152602001828152602001955050505050506000604051808303816000876161da5a03f11561000257505050505050565b600160a060020a03166316c66cc6836040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051519150505b919050565b6002805473ffffffffffffffffffffffffffffffffffffffff19168217905550565b6040805160025460e060020a6313bc6d4b02825233600160a060020a03908116600484015292519216916313bc6d4b9160248181019260209290919082900301816000876161da5a03f115610002575050604051511515905061091157610002565b87935061091c610260565b600160a060020a031663bdbdb08685600160a060020a0316632e94420f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040805180517fbdbdb0860000000000000000000000000000000000000000000000000000000082526004820152602481018a905290516044808301935060209282900301816000876161da5a03f1156100025750Line truncated
|
||||
"nonce": "16",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396"
|
||||
}
|
||||
},
|
||||
"0x70c9217d814985faef62b124420f8dfbddd96433": {
|
||||
"balance": "0x4ef436dcbda6cd4a",
|
||||
"code": "0x",
|
||||
"nonce": "1634",
|
||||
"storage": {}
|
||||
},
|
||||
"0x7986bad81f4cbd9317f5a46861437dae58d69113": {
|
||||
"balance": "0x0",
|
||||
"code": "0x6060604052361561008d5760e060020a600035046302d05d3f811461009557806316c66cc6146100a75780631ab9075a146100d7578063213fe2b7146101125780639859387b1461013f578063988db79c1461015e578063a7f4377914610180578063b9858a281461019e578063c8e40fbf146101c0578063f4f2821b146101e8578063f905c15a14610209575b610212610002565b610214600054600160a060020a031681565b600160a060020a0360043581811660009081526005602052604081205461023193168114610257575060016101e3565b610212600435600254600160a060020a0316600014801590610108575060025433600160a060020a03908116911614155b1561025f57610002565b610214600435600160a060020a03811660009081526004602052604081205460ff16151561027557610002565b610212600435600254600160a060020a03166000141561029b57610002565b610212600435602435600254600160a060020a03166000141561050457610002565b61021260025433600160a060020a0390811691161461056757610002565b610212600435602435600254600160a060020a03166000141561057557610002565b610231600435600160a060020a03811660009081526004602052604090205460ff165b919050565b610212600435600254600090600160a060020a031681141561072057610002565b61024560035481565b005b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60408051918252519081900360200190f35b5060006101e3565b60028054600160a060020a031916821790555b50565b50600160a060020a038181166000908152600460205260409020546101009004166101e3565b6002546040805160e060020a6313bc6d4b02815233600160a060020a039081166004830152915191909216916313bc6d4b91602482810192602092919082900301816000876161da5a03f11561000257505060405151151590506102fe57610002565b600160a060020a03811660009081526004602052604090205460ff161515610272576040516104028061092e833901809050604051809103906000f06004600050600083600160a060020a0316815260200190815260200160002060005060000160016101000a815481600160a060020a030219169083021790555060016004600050600083600160a060020a0316815260200190815260200160002060005060000160006101000a81548160ff0219169083021790555050565b600160a060020a03821660009081526004602052604090205460ff1615156104725760405161040280610d30833901809050604051809103906000f06004600050600084600160a060020a0316815260200190815260200160002060005060000160016101000a815481600160a060020a030219169083021790555060016004600050600084600160a060020a0316815260200190815260200160002060005060000160006101000a81548160ff021916908302179055505b600160a060020a03828116600090815260046020819052604080518184205460e060020a630a3b0a4f02825286861693820193909352905161010090920490931692630a3b0a4f926024828101939192829003018183876161da5a03f11561000257505050600160a060020a03811660009081526006602052604090208054600160a060020a031916831790555b5050565b6002546040805160e060020a6313bc6d4b02815233600160a060020a039081166004830152915191909216916313bc6d4b91602482810192602092919082900301816000876161da5a03f11561000257505060405151151590506103b957610002565b600254600160a060020a0316ff5b6002546040805160e060020a6313bc6d4b02815233600160a060020a039081166004830152915191909216916313bc6d4b91602482810192602092919082900301816000876161da5a03f11561000257505060405151151590506105d857610002565b600160a060020a03821660009081526004602052604090205460ff1615156106915760405161040280611132833901809050604051809103906000f06004600050600084600160a060020a0316815260200190815260200160002060005060000160016101000a815481600160a060020a030219169083021790555060016004600050600084600160a060020a0316815260200190815260200160002060005060000160006101000a81548160ff021916908302179055505b600160a060020a03828116600090815260046020819052604080518184205460e060020a630a3b0a4f02825286861693820193909352905161010090920490931692630a3b0a4f926024828101939192829003018183876161da5a03f11561000257505050600160a060020a031660009081526005602052604090208054600160a060020a0319169091179055565b6002546040805160e060020a6313bc6d4b02815233600160a060020a039081166004830152915191909216916313bc6d4b91602482810192602092919082900301816000876161da5a03f115610002575050604051511515905061078357610002565b50600160a060020a0381811660009081526005602090815260408083205490931680835260049091529190205460ff161561080f576040600081812054825160e260020a632e72bafd028152600160a060020a03868116600483015293516101009092049093169263b9caebf4926024828101939192829003018183876161da5a03f115610002575050505b600160a060020a03828116600090815260056020526040812054909116146108545760406000908120600160a060020a0384169091528054600160a060020a03191690555b50600160a060020a0381811660009081526006602090815260408083205490931680835260049091529190205460ff16156108e657600160a060020a038181166000908152604080518183205460e260020a632e72bafd028252868516600483015291516101009092049093169263b9caebf4926024828101939192829003018183876161da5a03f115610002575050505b600160a060020a03828116600090815260066020526040812054909116146105005760406000908120600160a060020a0384169091528054600160a060020a0319169055505056606060405260008054600160a060020a031916331790556103de806100246000396000f3606060405236156100615760e060020a600035046302d05d3f81146100695780630a3b0a4f1461007b5780630d327fa7146100f6578063524d81d314610109578063a7f4377914610114578063b9caebf414610132578063bbec3bae14610296575b6102ce610002Line truncated
|
||||
"nonce": "7",
|
||||
"storage": {
|
||||
"0xffc4df2d4f3d2cffad590bed6296406ab7926ca9e74784f74a95191fa069a174": "0x00000000000000000000000070c9217d814985faef62b124420f8dfbddd96433"
|
||||
}
|
||||
},
|
||||
"0xb4fe7aa695b326c9d219158d2ca50db77b39f99f": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100ae5760e060020a600035046302d05d3f81146100b65780631ab9075a146100c85780632b68bb2d146101035780634cc927d7146101c557806351a34eb81461028e57806356ccb6f0146103545780635928d37f1461041d578063599efa6b146104e9578063759297bb146105b2578063771d50e11461067e578063a7f4377914610740578063f905c15a1461075e578063f92eb77414610767578063febf661214610836575b610902610002565b610904600054600160a060020a031681565b610902600435600254600160a060020a03166000148015906100f9575060025433600160a060020a03908116911614155b1561092057610002565b60025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b02606452610902916000918291600160a060020a03169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051511515905061094257610002565b61090260043560243560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610a0d57610002565b61090260043560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610ae957610002565b61090260043560243560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610bbc57610002565b61090260043560243560443560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610c9657610002565b61090260043560243560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610de057610002565b61090260043560243560443560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610ebb57610002565b60025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b02606452610902916000918291600160a060020a03169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610f9e57610002565b61090260025433600160a060020a0390811691161461106957610002565b61090e60035481565b61090e60043560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750506040805180517ff92eb774000000000000000000000000000000000000000000000000000000008252600482018790529151919350600160a060020a038416925063f92eb774916024828101926020929190829003018188876161da5a03f11561000257505060405151949350505050565b61090260043560243560443560025460e360020a631c2d8fb302606090815260aa60020a6a18dbdb9d1c9858dd18dd1b026064526000918291600160a060020a039091169063e16c7d989060849060209060248187876161da5a03f1156100025750505060405180519060200150905080600160a060020a03166316c66cc6336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051511515905061107757610002565b005b6060908152602090f35b60408051918252519081900360200190f35b6002805473ffffffffffffffffffffffffffffffffffffffff19168217905550565b6040805160025460e360020a631c2d8fb302825260aa60020a6a18dbdb9d1c9858dd18dd1b0260048301529151600160a060020a03929092169163e16c7d9891602481810192602092909190829003018188876161da5a03f1156100025750506040805180517f5ed61af0000000000000000000000000Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396"
|
||||
}
|
||||
},
|
||||
"0xc212e03b9e060e36facad5fd8f4435412ca22e6b": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156101745760e060020a600035046302d05d3f811461017c57806304a7fdbc1461018e5780630e90f957146101fb5780630fb5a6b41461021257806314baa1b61461021b57806317fc45e21461023a5780632b096926146102435780632e94420f1461025b578063325a19f11461026457806336da44681461026d5780633f81a2c01461027f5780633fc306821461029757806345ecd3d7146102d45780634665096d146102dd5780634e71d92d146102e657806351a34eb8146103085780636111bb951461032d5780636f265b93146103445780637e9014e11461034d57806390ba009114610360578063927df5e014610393578063a7f437791461046c578063ad8f50081461046e578063bc6d909414610477578063bdec3ad114610557578063c19d93fb1461059a578063c9503fe2146105ad578063e0a73a93146105b6578063ea71b02d146105bf578063ea8a1af0146105d1578063ee4a96f9146105f3578063f1ff78a01461065c575b61046c610002565b610665600054600160a060020a031681565b6040805160c081810190925261046c9160049160c4918390600690839083908082843760408051808301909152929750909561018495509193509091908390839080828437509095505050505050600554600090600160a060020a0390811633909116146106a857610002565b61068260015460a060020a900460ff166000145b90565b61069660085481565b61046c600435600154600160a060020a03166000141561072157610002565b610696600d5481565b610696600435600f8160068110156100025750015481565b61069660045481565b61069660035481565b610665600554600160a060020a031681565b61069660043560158160068110156100025750015481565b6106966004355b600b54600f5460009160028202808203928083039290810191018386101561078357601054840186900394505b50505050919050565b61069660025481565b61069660095481565b61046c600554600090600160a060020a03908116339091161461085857610002565b61046c600435600554600090600160a060020a03908116339091161461092e57610002565b6106826001805460a060020a900460ff161461020f565b610696600b5481565b61068260075460a060020a900460ff1681565b6106966004355b600b54601554600091600282028082039280830392908101910183861015610a6c5760165494506102cb565b61046c6004356024356044356040805160015460e360020a631c2d8fb302825260b260020a691858d8dbdd5b9d18dd1b02600483015291516000928392600160a060020a03919091169163e16c7d9891602481810192602092909190829003018187876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663c4b0c96a336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610b4657610002565b005b610696600a5481565b61046c60006000600060006000600160009054906101000a9004600160a060020a0316600160a060020a031663e16c7d986040518160e060020a028152600401808060b260020a691858d8dbdd5b9d18dd1b0281526020015060200190506020604051808303816000876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663c4b0c96a336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610f1757610002565b61046c5b60015b60058160ff16101561071e57600f6001820160ff166006811015610002578101549060ff83166006811015610002570154101561129057610002565b61069660015460a060020a900460ff1681565b61069660065481565b610696600c5481565b610665600754600160a060020a031681565b61046c600554600090600160a060020a0390811633909116146112c857610002565b6040805160c081810190925261046c9160049160c4918390600690839083908082843760408051808301909152929750909561018495509193509091908390839080828437509095505050505050600154600090600160a060020a03168114156113fb57610002565b610696600e5481565b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60408051918252519081900360200190f35b5060005b60068160ff16101561070857828160ff166006811015610002576020020151600f60ff831660068110156100025701558160ff82166006811015610002576020020151601560ff831660068110156100025701556001016106ac565b61071061055b565b505050565b600e8054820190555b50565b6040805160015460e060020a6313bc6d4b02825233600160a060020a03908116600484015292519216916313bc6d4b9160248181019260209290919082900301816000876161da5a03f115610002575050604051511515905061071557610002565b83861015801561079257508286105b156107b457600f546010546011548689039082030291909104900394506102cb565b8286101580156107c55750600b5486105b156107e757600f546011546012548589039082030291909104900394506102cb565b600b5486108015906107f857508186105b1561081d57600b54600f546012546013549289039281039290920204900394506102cb565b81861015801561082c57508086105b1561084e57600f546013546014548489039082030291909104900394506102cb565b60145494506102cb565b60015460a060020a900460ff1660001461087157610002565b600254600a01431161088257610002565b6040805160015460e360020a631c2d8fb302825260a860020a6a636f6e74726163746170690260048301529151600160a060020a03929092169163e16c7d989160248181019260209290919082900301816000876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663771d50e16040518160e060020a0281526004018090506000604051808303816000876161da5a03f1156100025750505050565b60015460a060020a900460ff1660001461094757610002565b600254600a01431161095857610002565b6040805160015460e360020a631c2d8fb302825260a860020a6a636f6e74726163746170690260048301529151600160a060020a03929092169163e16c7d989160248181019260209290919082900301816000876161da5a03f115610002575050Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000006195",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000004": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000070c9217d814985faef62b124420f8dfbddd96433",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000008ac7230489e80000",
|
||||
"0x000000000000000000000000000000000000000000000000000000000000000b": "0x0000000000000000000000000000000000000000000000283c7b9181eca20000"
|
||||
}
|
||||
},
|
||||
"0xcf00ffd997ad14939736f026006498e3f099baaf": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100cf5760e060020a600035046302d05d3f81146100d7578063031e7f5d146100e95780631ab9075a1461010b5780632243118a1461014657806327aad68a1461016557806338a699a4146101da5780635188f996146101f8578063581d5d601461021e57806381738c5914610246578063977da54014610269578063a07421ce14610288578063a7f43779146102be578063bdbdb086146102dc578063e1c7111914610303578063f4f2821b14610325578063f905c15a1461034a578063f92eb77414610353575b610387610002565b610389600054600160a060020a031681565b610387600435602435600254600160a060020a0316600014156103a857610002565b610387600435600254600160a060020a031660001480159061013c575060025433600160a060020a03908116911614155b1561042957610002565b610387600435600254600160a060020a03166000141561044b57610002565b6102ac60043560008181526004602081815260408320547f524d81d3000000000000000000000000000000000000000000000000000000006060908152610100909104600160a060020a031692839263524d81d3926064928188876161da5a03f1156100025750506040515192506103819050565b61039c60043560008181526004602052604090205460ff165b919050565b6103876004356024356002546000908190600160a060020a031681141561079457610002565b61038760043560243560025460009081908190600160a060020a031681141561080457610002565b61038960043560008181526004602052604081205460ff1615156109e357610002565b610387600435600254600160a060020a0316600014156109fb57610002565b600435600090815260096020526040902054670de0b6b3a764000090810360243502045b60408051918252519081900360200190f35b61038760025433600160a060020a03908116911614610a9257610002565b600435600090815260086020526040902054670de0b6b3a7640000602435909102046102ac565b610387600435602435600254600160a060020a031660001415610aa057610002565b61038760043560025460009081908190600160a060020a0316811415610b3657610002565b6102ac60035481565b6102ac600435600081815260076020908152604080832054600690925290912054670de0b6b3a76400000204805b50919050565b005b600160a060020a03166060908152602090f35b15156060908152602090f35b60025460e060020a6313bc6d4b02606090815233600160a060020a03908116606452909116906313bc6d4b906084906020906024816000876161da5a03f11561000257505060405151151590506103fe57610002565b60008281526004602052604090205460ff16151561041b57610002565b600860205260406000205550565b6002805473ffffffffffffffffffffffffffffffffffffffff19168217905550565b60025460e060020a6313bc6d4b02606090815233600160a060020a03908116606452909116906313bc6d4b906084906020906024816000876161da5a03f11561000257505060405151151590506104a157610002565b604080516000838152600460205291909120805460ff1916600117905561040280610de2833901809050604051809103906000f0600460005060008360001916815260200190815260200160002060005060000160016101000a815481600160a060020a030219169083021790555066470de4df8200006008600050600083600019168152602001908152602001600020600050819055506703782dace9d9000060096000506000836000191681526020019081526020016000206000508190555050565b600460005060008560001916815260200190815260200160002060005060000160019054906101000a9004600160a060020a0316915081600160a060020a031663524d81d36040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151821415905061060057838152600660209081526040808320839055600790915281208190555b81600160a060020a0316630a3b0a4f846040518260e060020a0281526004018082600160a060020a031681526020019150506000604051808303816000876161da5a03f11561000257505050600160a060020a038316808252600560209081526040808420879055805160e160020a6364a81ff102815290518694670de0b6b3a7640000949363c9503fe29360048181019492939183900301908290876161da5a03f11561000257505060408051805160e060020a636f265b930282529151919291636f265b939160048181019260209290919082900301816000876161da5a03f11561000257505050604051805190602001500204600660005060008660001916815260200190815260200160002060008282825054019250508190555080600160a060020a031663c9503fe26040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050506040518051906020015060076000506000866000191681526020019081526020016000206000828282505401925050819055505b50505050565b60025460e060020a6313bc6d4b02606090815233600160a060020a03908116606452909116906313bc6d4b9060849060209060248187876161da5a03f11561000257505060405151151590506107e957610002565b8381526004602052604081205460ff16151561056657610002565b60025460e060020a6313bc6d4b02606090815233600160a060020a03908116606452909116906313bc6d4b9060849060209060248187876161da5a03f115610002575050604051511515905061085957610002565b849250670de0b6b3a764000083600160a060020a031663c9503fe26040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575060408051805160e160020a6364a81ff102825291519189028590049650600481810192602092909190829003018188876161da5a03f11561000257505060408051805160e060020a636f265b930282529151919291636f265b9391600481810192602092909190829003018189876161da5a03f115610002575050506040518051906020015002049050806006600050600085600160a060020a0316632e94420f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750604080518051855260208681528286208054989098039097557f2e94420f0000000000000000000000000000000000000000000000000000000081529051889660048381019391929182900301818787Line truncated
|
||||
"nonce": "3",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"0x3571d73f14f31a1463bd0a2f92f7fde1653d4e1ead7aedf4b0a5df02f16092ab": "0x0000000000000000000000000000000000000000000007d634e4c55188be0000",
|
||||
"0x4e64fe2d1b72d95a0a31945cc6e4f4e524ac5ad56d6bd44a85ec7bc9cc0462c0": "0x000000000000000000000000000000000000000000000002b5e3af16b1880000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "117124093",
|
||||
"extraData": "0xd5830105008650617269747986312e31322e31826d61",
|
||||
"gasLimit": "4707788",
|
||||
"hash": "0xad325e4c49145fb7a4058a68ac741cc8607a71114e23fc88083c7e881dd653e7",
|
||||
"miner": "0x00714b9ac97fd6bd9325a059a70c9b9fa94ce050",
|
||||
"mixHash": "0x0af918f65cb4af04b608fc1f14a849707696986a0e7049e97ef3981808bcc65f",
|
||||
"nonce": "0x38dee147326a8d40",
|
||||
"number": "25000",
|
||||
"stateRoot": "0xc5d6bbcd46236fcdcc80b332ffaaa5476b980b01608f9708408cfef01b58bd5b",
|
||||
"timestamp": "1479891517",
|
||||
"totalDifficulty": "1895410389427"
|
||||
},
|
||||
"input": "0xf88b8206628504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb80000000000000000000000000000000000000000000000280faf689c35ac00002aa0a7ee5b7877811bf671d121b40569462e722657044808dc1d6c4f1e4233ec145ba0417e7543d52b65738d9df419cbe40a708424f4d54b0fc145c0a64545a2bb1065",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"from": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"gas": "0x31217",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d98636f6e7472616374617069000000000000000000000000000000000000000000",
|
||||
"output": "0x000000000000000000000000b4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"gas": "0x2a68d",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d98636f6e747261637463746c000000000000000000000000000000000000000000",
|
||||
"output": "0x0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x23ac9",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d98636f6e7472616374646200000000000000000000000000000000000000000000",
|
||||
"output": "0x0000000000000000000000007986bad81f4cbd9317f5a46861437dae58d69113",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x23366",
|
||||
"gasUsed": "0x273",
|
||||
"input": "0x16c66cc6000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x7986bad81f4cbd9317f5a46861437dae58d69113",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"gas": "0x29f35",
|
||||
"gasUsed": "0xf8d",
|
||||
"input": "0x16c66cc6000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"gas": "0x28a9e",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d98636f6e747261637463746c000000000000000000000000000000000000000000",
|
||||
"output": "0x0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x21d79",
|
||||
"gasUsed": "0x24d",
|
||||
"input": "0x13bc6d4b000000000000000000000000b4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x2165b",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d986d61726b65746462000000000000000000000000000000000000000000000000",
|
||||
"output": "0x000000000000000000000000cf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x1a8e8",
|
||||
"gasUsed": "0x24d",
|
||||
"input": "0x13bc6d4b0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x1a2c6",
|
||||
"gasUsed": "0x3cb",
|
||||
"input": "0xc9503fe2",
|
||||
"output": "0x0000000000000000000000000000000000000000000000008ac7230489e80000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x19b72",
|
||||
"gasUsed": "0x3cb",
|
||||
"input": "0xc9503fe2",
|
||||
"output": "0x0000000000000000000000000000000000000000000000008ac7230489e80000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x19428",
|
||||
"gasUsed": "0x305",
|
||||
"input": "0x6f265b93",
|
||||
"output": "0x0000000000000000000000000000000000000000000000283c7b9181eca20000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x18d45",
|
||||
"gasUsed": "0x229",
|
||||
"input": "0x2e94420f",
|
||||
"output": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"gas": "0x1734e",
|
||||
"gasUsed": "0x229",
|
||||
"input": "0x2e94420f",
|
||||
"output": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x20ee1",
|
||||
"gasUsed": "0x5374",
|
||||
"input": "0x581d5d60000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000",
|
||||
"output": "0x",
|
||||
"to": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x1b6c1",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d986c6f676d67720000000000000000000000000000000000000000000000000000",
|
||||
"output": "0x0000000000000000000000002a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x1af69",
|
||||
"gasUsed": "0x229",
|
||||
"input": "0x2e94420f",
|
||||
"output": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"gas": "0x143a5",
|
||||
"gasUsed": "0x24d",
|
||||
"input": "0x13bc6d4b0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x1a91d",
|
||||
"gasUsed": "0x12fa",
|
||||
"input": "0x0accce0600000000000000000000000000000000000000000000000000000000000000025842545553440000000000000000000000000000000000000000000000000000000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"output": "0x",
|
||||
"to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x19177",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d986c6f676d67720000000000000000000000000000000000000000000000000000",
|
||||
"output": "0x0000000000000000000000002a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x18a22",
|
||||
"gasUsed": "0x229",
|
||||
"input": "0x2e94420f",
|
||||
"output": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x18341",
|
||||
"gasUsed": "0x334",
|
||||
"input": "0xe16c7d986d61726b65746462000000000000000000000000000000000000000000000000",
|
||||
"output": "0x000000000000000000000000cf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x17bec",
|
||||
"gasUsed": "0x229",
|
||||
"input": "0x2e94420f",
|
||||
"output": "0x5842545553440000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x1764e",
|
||||
"gasUsed": "0x45c",
|
||||
"input": "0xf92eb7745842545553440000000000000000000000000000000000000000000000000000",
|
||||
"output": "0x00000000000000000000000000000000000000000000002816d180e30c390000",
|
||||
"to": "0xcf00ffd997ad14939736f026006498e3f099baaf",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
},
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"gas": "0x108ba",
|
||||
"gasUsed": "0x24d",
|
||||
"input": "0x13bc6d4b0000000000000000000000003e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x2cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"gas": "0x16e62",
|
||||
"gasUsed": "0xebb",
|
||||
"input": "0x645a3b72584254555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002816d180e30c390000",
|
||||
"output": "0x",
|
||||
"to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"gas": "0x283b9",
|
||||
"gasUsed": "0xc51c",
|
||||
"input": "0x949ae479000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000",
|
||||
"output": "0x",
|
||||
"to": "0x3e9286eafa2db8101246c2131c09b49080d00690",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"gas": "0x30b4a",
|
||||
"gasUsed": "0xedb7",
|
||||
"input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000",
|
||||
"output": "0x",
|
||||
"to": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
||||
"gas": "0x37b38",
|
||||
"gasUsed": "0x12bb3",
|
||||
"input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000",
|
||||
"output": "0x",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "31927752",
|
||||
"gasLimit": "4707788",
|
||||
"miner": "0x5659922ce141eedbc2733678f9806c77b4eebee8",
|
||||
"number": "11495",
|
||||
"timestamp": "1479735917"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x13204f5d64c28326fd7bd05fd4ea855302d7f2ff": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100825760e060020a60003504630a0313a981146100875780630a3b0a4f146101095780630cd40fea1461021257806329092d0e1461021f5780634cd06a5f146103295780635dbe47e8146103395780637a9e5410146103d9578063825db5f7146103e6578063a820b44d146103f3578063efa52fb31461047a575b610002565b34610002576104fc600435600060006000507342b02b5deeb78f34cd5ac896473b63e6c99a71a26333556e849091846000604051602001526040518360e060020a028152600401808381526020018281526020019250505060206040518083038186803b156100025760325a03f415610002575050604051519150505b919050565b346100025761051060043560006000507342b02b5deeb78f34cd5ac896473b63e6c99a71a2637d65837a9091336000604051602001526040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038186803b156100025760325a03f4156100025750506040515115905061008257604080517f21ce24d4000000000000000000000000000000000000000000000000000000008152600060048201819052600160a060020a038416602483015291517342b02b5deeb78f34cd5ac896473b63e6c99a71a2926321ce24d49260448082019391829003018186803b156100025760325a03f415610002575050505b50565b3461000257610512600181565b346100025761051060043560006000507342b02b5deeb78f34cd5ac896473b63e6c99a71a2637d65837a9091336000604051602001526040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038186803b156100025760325a03f4156100025750506040515115905061008257604080517f89489a87000000000000000000000000000000000000000000000000000000008152600060048201819052600160a060020a038416602483015291517342b02b5deeb78f34cd5ac896473b63e6c99a71a2926389489a879260448082019391829003018186803b156100025760325a03f4156100025750505061020f565b3461000257610528600435610403565b34610002576104fc600435604080516000602091820181905282517f7d65837a00000000000000000000000000000000000000000000000000000000815260048101829052600160a060020a0385166024820152925190927342b02b5deeb78f34cd5ac896473b63e6c99a71a292637d65837a92604480840193829003018186803b156100025760325a03f4156100025750506040515191506101049050565b3461000257610512600c81565b3461000257610512600081565b3461000257610528600061055660005b600060006000507342b02b5deeb78f34cd5ac896473b63e6c99a71a263685a1f3c9091846000604051602001526040518360e060020a028152600401808381526020018281526020019250505060206040518083038186803b156100025760325a03f4156100025750506040515191506101049050565b346100025761053a600435600060006000507342b02b5deeb78f34cd5ac896473b63e6c99a71a263f775b6b59091846000604051602001526040518360e060020a028152600401808381526020018281526020019250505060206040518083038186803b156100025760325a03f4156100025750506040515191506101049050565b604080519115158252519081900360200190f35b005b6040805160ff9092168252519081900360200190f35b60408051918252519081900360200190f35b60408051600160a060020a039092168252519081900360200190f35b90509056",
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x4d140b25abf3c71052885c66f73ce07cff141c1afabffdaf5cba04d625b7ebcc": "0x0000000000000000000000000000000000000000000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"0x269296dddce321a6bcbaa2f0181127593d732cba": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156101275760e060020a60003504630cd40fea811461012c578063173825d9146101395780631849cb5a146101c7578063285791371461030f5780632a58b3301461033f5780632cb0d48a146103565780632f54bf6e1461036a578063332b9f061461039d5780633ca8b002146103c55780633df4ddf4146103d557806341c0e1b5146103f457806347799da81461040557806362a51eee1461042457806366907d13146104575780637065cb48146104825780637a9e541014610496578063825db5f7146104a3578063949d225d146104b0578063a51687df146104c7578063b4da4e37146104e6578063b4e6850b146104ff578063bd7474ca14610541578063e75623d814610541578063e9938e1114610555578063f5d241d314610643575b610002565b3461000257610682600181565b34610002576106986004356106ff335b60006001600a9054906101000a9004600160a060020a0316600160a060020a0316635dbe47e8836000604051602001526040518260e060020a0281526004018082600160a060020a03168152602001915050602060405180830381600087803b156100025760325a03f1156100025750506040515191506103989050565b3461000257604080516101008082018352600080835260208084018290528385018290526060808501839052608080860184905260a080870185905260c080880186905260e09788018690526001605060020a0360043581168752600586529589902089519788018a528054808816808a52605060020a91829004600160a060020a0316978a01889052600183015463ffffffff8082169d8c018e905264010000000082048116988c01899052604060020a90910416958a018690526002830154948a01859052600390920154808916938a01849052049096169690970186905293969495949293604080516001605060020a03998a16815297891660208901529590971686860152600160a060020a03909316606086015263ffffffff9182166080860152811660a08501521660c083015260e08201929092529051908190036101000190f35b346100025761069a60043560018054600091829160ff60f060020a909104161515141561063d5761072833610376565b34610002576106ae6004546001605060020a031681565b34610002576106986004356108b333610149565b346100025761069a6004355b600160a060020a03811660009081526002602052604090205460ff1615156001145b919050565b34610002576106986001805460ff60f060020a9091041615151415610913576108ed33610376565b346100025761069a600435610149565b34610002576106ae6003546001605060020a03605060020a9091041681565b346100025761069861091533610149565b34610002576106ae6003546001605060020a0360a060020a9091041681565b346100025761069a60043560243560018054600091829160ff60f060020a909104161515141561095e5761092633610376565b34610002576106986004356001805460ff60f060020a909104161515141561072557610a8b33610376565b3461000257610698600435610aa533610149565b3461000257610682600c81565b3461000257610682600081565b34610002576106ae6003546001605060020a031681565b34610002576106ca600154600160a060020a03605060020a9091041681565b346100025761069a60015460ff60f060020a9091041681565b346100025761069a60043560243560443560643560843560a43560c43560018054600091829160ff60f060020a9091041615151415610b5857610ad233610376565b3461000257610698600435610bd633610149565b34610002576106e6600435604080516101008181018352600080835260208084018290528385018290526060808501839052608080860184905260a080870185905260c080880186905260e09788018690526001605060020a03808b168752600586529589902089519788018a5280548088168952600160a060020a03605060020a918290041696890196909652600181015463ffffffff8082169b8a019b909b5264010000000081048b1695890195909552604060020a90940490981691860182905260028301549086015260039091015480841696850196909652940416918101919091525b50919050565b346100025761069a60043560243560443560643560843560a43560018054600091829160ff60f060020a9091041615151415610c8e57610bfb33610376565b6040805160ff9092168252519081900360200190f35b005b604080519115158252519081900360200190f35b604080516001605060020a039092168252519081900360200190f35b60408051600160a060020a039092168252519081900360200190f35b6040805163ffffffff9092168252519081900360200190f35b1561012757600160a060020a0381166000908152600260205260409020805460ff191690555b50565b1561063d57506001605060020a0380831660009081526005602052604090208054909116151561075b576000915061063d565b604080516101008101825282546001605060020a038082168352600160a060020a03605060020a92839004166020840152600185015463ffffffff80821695850195909552640100000000810485166060850152604060020a90049093166080830152600284015460a0830152600384015480841660c08401520490911660e0820152610817905b8051600354600090819060016001605060020a0390911611610c995760038054605060020a60f060020a0319169055610ddf565b600380546001605060020a031981166000196001605060020a03928316011782558416600090815260056020526040812080547fffff000000000000000000000000000000000000000000000000000000000000168155600181810180546bffffffffffffffffffffffff191690556002820192909255909101805473ffffffffffffffffffffffffffffffffffffffff19169055915061063d565b1561012757600180547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660f060020a8302179055610725565b1561091357600480546001605060020a031981166001605060020a039091166001011790555b565b156101275733600160a060020a0316ff5b1561095e57506001605060020a03808416600090815260056020526040902080549091161515610965576000915061095e565b600191505b5092915050565b60038101546001605060020a0384811691161415610986576001915061095e565b604080516101008101825282546001605060020a038082168352600160a060020a03605060020a928390041660208401526001Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x000113204f5d64c28326fd7bd05fd4ea855302d7f2ff00000000000000000000"
|
||||
}
|
||||
},
|
||||
"0x42b02b5deeb78f34cd5ac896473b63e6c99a71a2": {
|
||||
"balance": "0x0",
|
||||
"code": "0x6504032353da7150606060405236156100695760e060020a60003504631bf7509d811461006e57806321ce24d41461008157806333556e84146100ec578063685a1f3c146101035780637d65837a1461011757806389489a8714610140578063f775b6b5146101fc575b610007565b61023460043560006100fd82600061010d565b610246600435602435600160a060020a03811660009081526020839052604081205415156102cb57826001016000508054806001018281815481835581811511610278576000838152602090206102789181019083015b808211156102d057600081556001016100d8565b610248600435602435600182015481105b92915050565b6102346004356024355b60018101906100fd565b610248600435602435600160a060020a03811660009081526020839052604090205415156100fd565b61024660043560243580600160a060020a031632600160a060020a03161415156101f857600160a060020a038116600090815260208390526040902054156101f857600160a060020a038116600090815260208390526040902054600183018054909160001901908110156100075760009182526020808320909101805473ffffffffffffffffffffffffffffffffffffffff19169055600160a060020a038316825283905260408120556002820180546000190190555b5050565b61025c60043560243560008260010160005082815481101561000757600091825260209091200154600160a060020a03169392505050565b60408051918252519081900360200190f35b005b604080519115158252519081900360200190f35b60408051600160a060020a039092168252519081900360200190f35b50505060009283526020808420909201805473ffffffffffffffffffffffffffffffffffffffff191686179055600160a060020a0385168352908590526040909120819055600284018054600101905590505b505050565b509056",
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
},
|
||||
"0xa529806c67cc6486d4d62024471772f47f6fd672": {
|
||||
"balance": "0x67820e39ac8fe9800",
|
||||
"code": "0x",
|
||||
"nonce": "68",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "31912170",
|
||||
"extraData": "0xd783010502846765746887676f312e372e33856c696e7578",
|
||||
"gasLimit": "4712388",
|
||||
"hash": "0x0855914bdc581bccdc62591fd438498386ffb59ea4d5361ed5c3702e26e2c72f",
|
||||
"miner": "0x334391aa808257952a462d1475562ee2106a6c90",
|
||||
"mixHash": "0x64bb70b8ca883cadb8fbbda2c70a861612407864089ed87b98e5de20acceada6",
|
||||
"nonce": "0x684129f283aaef18",
|
||||
"number": "11494",
|
||||
"stateRoot": "0x7057f31fe3dab1d620771adad35224aae43eb70e94861208bc84c557ff5b9d10",
|
||||
"timestamp": "1479735912",
|
||||
"totalDifficulty": "90744064339"
|
||||
},
|
||||
"input": "0xf889448504a817c800832dc6c094269296dddce321a6bcbaa2f0181127593d732cba80a47065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e29a080ed81e4c5e9971a730efab4885566e2c868cd80bd4166d0ed8c287fdf181650a069d7c49215e3d4416ad239cd09dbb71b9f04c16b33b385d14f40b618a7a65115",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x13204f5d64c28326fd7bd05fd4ea855302d7f2ff",
|
||||
"gas": "0x2bf459",
|
||||
"gasUsed": "0x2aa",
|
||||
"input": "0x7d65837a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a529806c67cc6486d4d62024471772f47f6fd672",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x42b02b5deeb78f34cd5ac896473b63e6c99a71a2",
|
||||
"type": "DELEGATECALL"
|
||||
}
|
||||
],
|
||||
"from": "0x269296dddce321a6bcbaa2f0181127593d732cba",
|
||||
"gas": "0x2cae73",
|
||||
"gasUsed": "0xa9d",
|
||||
"input": "0x5dbe47e8000000000000000000000000a529806c67cc6486d4d62024471772f47f6fd672",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x13204f5d64c28326fd7bd05fd4ea855302d7f2ff",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"from": "0xa529806c67cc6486d4d62024471772f47f6fd672",
|
||||
"gas": "0x2d6e28",
|
||||
"gasUsed": "0x64bd",
|
||||
"input": "0x7065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e",
|
||||
"output": "0x",
|
||||
"to": "0x269296dddce321a6bcbaa2f0181127593d732cba",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3451177886",
|
||||
"gasLimit": "4709286",
|
||||
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||
"number": "2290744",
|
||||
"timestamp": "1513616439"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x1d3ddf7caf024f253487e18bc4a15b1a360c170a": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405263ffffffff60e060020a6000350416633b91f50681146100505780635bb47808146100715780635f51fca01461008c578063bc7647a9146100ad578063f1bd0d7a146100c8575b610000565b346100005761006f600160a060020a03600435811690602435166100e9565b005b346100005761006f600160a060020a0360043516610152565b005b346100005761006f600160a060020a036004358116906024351661019c565b005b346100005761006f600160a060020a03600435166101fa565b005b346100005761006f600160a060020a0360043581169060243516610db8565b005b600160a060020a038083166000908152602081905260408120549091908116903316811461011657610000565b839150600160a060020a038316151561012d573392505b6101378284610e2e565b6101418284610db8565b61014a826101fa565b5b5b50505050565b600154600160a060020a03908116903316811461016e57610000565b6002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5b5050565b600254600160a060020a0390811690331681146101b857610000565b600160a060020a038381166000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19169184169190911790555b5b505050565b6040805160e260020a631a481fc102815260016024820181905260026044830152606482015262093a8060848201819052600060a4830181905260c06004840152601e60c48401527f736574456e7469747953746174757328616464726573732c75696e743829000060e484015292519091600160a060020a038516916369207f049161010480820192879290919082900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526000602482018190526001604483015260606004830152602360648301527f626567696e506f6c6c28616464726573732c75696e7436342c626f6f6c2c626f60848301527f6f6c29000000000000000000000000000000000000000000000000000000000060a48301529151600160a060020a038716935063de64e15c9260c48084019391929182900301818387803b156100005760325a03f1156100005750506040805160e260020a631a481fc102815260016024820181905260026044830152606482015267ffffffffffffffff8416608482015260ff851660a482015260c06004820152601960c48201527f61646453746f636b28616464726573732c75696e74323536290000000000000060e48201529051600160a060020a03861692506369207f04916101048082019260009290919082900301818387803b156100005760325a03f1156100005750506040805160e260020a631a481fc102815260016024820181905260026044830152606482015267ffffffffffffffff8416608482015260ff851660a482015260c06004820152601960c48201527f697373756553746f636b2875696e74382c75696e74323536290000000000000060e48201529051600160a060020a03861692506369207f04916101048082019260009290919082900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526002602482015260006044820181905260606004830152602160648301527f6772616e7453746f636b2875696e74382c75696e743235362c61646472657373608483015260f860020a60290260a48301529151600160a060020a038716935063de64e15c9260c48084019391929182900301818387803b156100005760325a03f115610000575050604080517f010555b8000000000000000000000000000000000000000000000000000000008152600160a060020a03338116602483015260006044830181905260606004840152603c60648401527f6772616e7456657374656453746f636b2875696e74382c75696e743235362c6160848401527f6464726573732c75696e7436342c75696e7436342c75696e743634290000000060a48401529251908716935063010555b89260c48084019391929182900301818387803b156100005760325a03f1156100005750506040805160e260020a631a481fc102815260016024820181905260026044830152606482015267ffffffffffffffff8416608482015260ff851660a482015260c06004820152601260c48201527f626567696e53616c65286164647265737329000000000000000000000000000060e48201529051600160a060020a03861692506369207f04916101048082019260009290919082900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526002602482015260006044820181905260606004830152601a60648301527f7472616e7366657253616c6546756e64732875696e743235362900000000000060848301529151600160a060020a038716935063de64e15c9260a48084019391929182900301818387803b156100005760325a03f1156100005750506040805160e260020a631a481fc102815260016024820181905260026044830152606482015267ffffffffffffffff8416608482015260ff851660a482015260c06004820152602d60c48201527f7365744163636f756e74696e6753657474696e67732875696e743235362c756960e48201527f6e7436342c75696e7432353629000000000000000000000000000000000000006101048201529051600160a060020a03861692506369207f04916101248082019260009290919082900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526002602482015260006044820181905260606004830152603460648301527f637265617465526563757272696e6752657761726428616464726573732c756960848301527f6e743235362c75696e7436342c737472696e672900000000000000000000000060a48301529151600160a060020a038716935063de64e15c9260c48084019391929182900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526002602482015260006044820181905260606004830152601b60648301527f72656d6f7665526563757272696e675265776172642875696e7429000000000060848301529151600160a060020a038716935063de64e15c9260a48084019391929182900301818387803b156100005760325a03f1156100005750506040805160e260020a63379938570281526002602482015260006044820181905260606004830152602360648301527f697373756552657761726428616464726573732c75696e743235362c7374726960848301527f6e67Line truncated
|
||||
"nonce": "789",
|
||||
"storage": {
|
||||
"0xfe9ec0542a1c009be8b1f3acf43af97100ffff42eb736850fb038fa1151ad4d9": "0x000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e8"
|
||||
}
|
||||
},
|
||||
"0x5cb4a6b902fcb21588c86c3517e797b07cdaadb9": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "0",
|
||||
"storage": {}
|
||||
},
|
||||
"0xe4a13bc304682a903e9472f469c33801dd18d9e8": {
|
||||
"balance": "0x33c763c929f62c4f",
|
||||
"code": "0x",
|
||||
"nonce": "14",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3451177886",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "4713874",
|
||||
"hash": "0x5d52a672417cd1269bf4f7095e25dcbf837747bba908cd5ef809dc1bd06144b5",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0x01a12845ed546b94a038a7a03e8df8d7952024ed41ccb3db7a7ade4abc290ce1",
|
||||
"nonce": "0x28c446f1cb9748c1",
|
||||
"number": "2290743",
|
||||
"stateRoot": "0x4898aceede76739daef76448a367d10015a2c022c9e7909b99a10fbf6fb16708",
|
||||
"timestamp": "1513616414",
|
||||
"totalDifficulty": "7146523769022564"
|
||||
},
|
||||
"input": "0xf8aa0e8509502f9000830493e0941d3ddf7caf024f253487e18bc4a15b1a360c170a80b8443b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e829a0524564944fa419f5c189b5074044f89210c6d6b2d77ee8f7f12a927d59b636dfa0015b28986807a424b18b186ee6642d76739df36cad802d20e8c00e79a61d7281",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"error": "internal failure",
|
||||
"from": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a",
|
||||
"gas": "0x39ff0",
|
||||
"gasUsed": "0x39ff0",
|
||||
"input": "0x606060405234620000005760405160208062001fd283398101604052515b805b600a8054600160a060020a031916600160a060020a0383161790555b506001600d819055600e81905560408051808201909152600c8082527f566f74696e672053746f636b00000000000000000000000000000000000000006020928301908152600b805460008290528251601860ff1990911617825590947f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9600291831615610100026000190190921604601f0193909304830192906200010c565b828001600101855582156200010c579182015b828111156200010c578251825591602001919060010190620000ef565b5b50620001309291505b808211156200012c576000815560010162000116565b5090565b50506040805180820190915260038082527f43565300000000000000000000000000000000000000000000000000000000006020928301908152600c805460008290528251600660ff1990911617825590937fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c760026001841615610100026000190190931692909204601f010481019291620001f7565b82800160010185558215620001f7579182015b82811115620001f7578251825591602001919060010190620001da565b5b506200021b9291505b808211156200012c576000815560010162000116565b5090565b50505b505b611da280620002306000396000f3006060604052361561019a5763ffffffff60e060020a600035041662e1986d811461019f57806302a72a4c146101d657806306eb4e421461020157806306fdde0314610220578063095ea7b3146102ad578063158ccb99146102dd57806318160ddd146102f85780631cf65a781461031757806323b872dd146103365780632c71e60a1461036c57806333148fd6146103ca578063435ebc2c146103f55780635eeb6e451461041e578063600e85b71461043c5780636103d70b146104a157806362c1e46a146104b05780636c182e99146104ba578063706dc87c146104f057806370a082311461052557806377174f851461055057806395d89b411461056f578063a7771ee3146105fc578063a9059cbb14610629578063ab377daa14610659578063b25dbb5e14610685578063b89a73cb14610699578063ca5eb5e1146106c6578063cbcf2e5a146106e1578063d21f05ba1461070e578063d347c2051461072d578063d96831e114610765578063dd62ed3e14610777578063df3c211b146107a8578063e2982c21146107d6578063eb944e4c14610801575b610000565b34610000576101d4600160a060020a036004351660243567ffffffffffffffff6044358116906064358116906084351661081f565b005b34610000576101ef600160a060020a0360043516610a30565b60408051918252519081900360200190f35b34610000576101ef610a4f565b60408051918252519081900360200190f35b346100005761022d610a55565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516602435610ae3565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516610b4e565b005b34610000576101ef610b89565b60408051918252519081900360200190f35b34610000576101ef610b8f565b60408051918252519081900360200190f35b34610000576102c9600160a060020a0360043581169060243516604435610b95565b604080519115158252519081900360200190f35b3461000057610388600160a060020a0360043516602435610bb7565b60408051600160a060020a039096168652602086019490945267ffffffffffffffff928316858501529082166060850152166080830152519081900360a00190f35b34610000576101ef600160a060020a0360043516610c21565b60408051918252519081900360200190f35b3461000057610402610c40565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d4600160a060020a0360043516602435610c4f565b005b3461000057610458600160a060020a0360043516602435610cc9565b60408051600160a060020a03909716875260208701959095528585019390935267ffffffffffffffff9182166060860152811660808501521660a0830152519081900360c00190f35b34610000576101d4610d9e565b005b6101d4610e1e565b005b34610000576104d3600160a060020a0360043516610e21565b6040805167ffffffffffffffff9092168252519081900360200190f35b3461000057610402600160a060020a0360043516610ead565b60408051600160a060020a039092168252519081900360200190f35b34610000576101ef600160a060020a0360043516610ef9565b60408051918252519081900360200190f35b34610000576101ef610f18565b60408051918252519081900360200190f35b346100005761022d610f1e565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516610fac565b604080519115158252519081900360200190f35b34610000576102c9600160a060020a0360043516602435610fc2565b604080519115158252519081900360200190f35b3461000057610402600435610fe2565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d46004351515610ffd565b005b34610000576102c9600160a060020a036004351661104c565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516611062565b005b34610000576102c9600160a060020a0360043516611070565b604080519115158252519081900360200190f35b34610000576101ef6110f4565b60408051918252519081900360200190f35b34610000576101ef600160a060020a036004351667ffffffffffffffff602435166110fa565b60408051918252519081900360200190f35b34610000576101d4600435611121565b005b34610000576101ef600160a060020a03600Line truncated
|
||||
"type": "CREATE",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"error": "invalid jump destination (PUSH1) 0",
|
||||
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
||||
"gas": "0x435c8",
|
||||
"gasUsed": "0x435c8",
|
||||
"input": "0x3b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e8",
|
||||
"to": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3956606365",
|
||||
"gasLimit": "5413248",
|
||||
"miner": "0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d",
|
||||
"number": "2295104",
|
||||
"timestamp": "1513681256"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76": {
|
||||
"balance": "0x0",
|
||||
"code": "0x60606040526004361061015e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680625b4487146101a257806311df9995146101cb578063278ecde11461022057806330adce0e146102435780633197cbb61461026c5780634bb278f3146102955780636103d70b146102aa57806363a599a4146102bf5780636a2d1cb8146102d457806375f12b21146102fd57806378e979251461032a578063801db9cc1461035357806386d1a69f1461037c5780638da5cb5b146103915780638ef26a71146103e65780639890220b1461040f5780639b39caef14610424578063b85dfb801461044d578063be9a6555146104a1578063ccb07cef146104b6578063d06c91e4146104e3578063d669e1d414610538578063df40503c14610561578063e2982c2114610576578063f02e030d146105c3578063f2fde38b146105d8578063f3283fba14610611575b600060149054906101000a900460ff1615151561017a57600080fd5b60075442108061018b575060085442115b15151561019757600080fd5b6101a03361064a565b005b34156101ad57600080fd5b6101b5610925565b6040518082815260200191505060405180910390f35b34156101d657600080fd5b6101de61092b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561022b57600080fd5b6102416004808035906020019091905050610951565b005b341561024e57600080fd5b610256610c48565b6040518082815260200191505060405180910390f35b341561027757600080fd5b61027f610c4e565b6040518082815260200191505060405180910390f35b34156102a057600080fd5b6102a8610c54565b005b34156102b557600080fd5b6102bd610f3e565b005b34156102ca57600080fd5b6102d261105d565b005b34156102df57600080fd5b6102e76110d5565b6040518082815260200191505060405180910390f35b341561030857600080fd5b6103106110e1565b604051808215151515815260200191505060405180910390f35b341561033557600080fd5b61033d6110f4565b6040518082815260200191505060405180910390f35b341561035e57600080fd5b6103666110fa565b6040518082815260200191505060405180910390f35b341561038757600080fd5b61038f611104565b005b341561039c57600080fd5b6103a4611196565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156103f157600080fd5b6103f96111bb565b6040518082815260200191505060405180910390f35b341561041a57600080fd5b6104226111c1565b005b341561042f57600080fd5b610437611296565b6040518082815260200191505060405180910390f35b341561045857600080fd5b610484600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061129c565b604051808381526020018281526020019250505060405180910390f35b34156104ac57600080fd5b6104b46112c0565b005b34156104c157600080fd5b6104c9611341565b604051808215151515815260200191505060405180910390f35b34156104ee57600080fd5b6104f6611354565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561054357600080fd5b61054b61137a565b6040518082815260200191505060405180910390f35b341561056c57600080fd5b610574611385565b005b341561058157600080fd5b6105ad600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506116c3565b6040518082815260200191505060405180910390f35b34156105ce57600080fd5b6105d66116db565b005b34156105e357600080fd5b61060f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611829565b005b341561061c57600080fd5b610648600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118fe565b005b600080670de0b6b3a7640000341015151561066457600080fd5b61069b610696670de0b6b3a7640000610688610258346119d990919063ffffffff16565b611a0c90919063ffffffff16565b611a27565b9150660221b262dd80006106ba60065484611a7e90919063ffffffff16565b111515156106c757600080fd5b600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb84846000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15156107d557600080fd5b6102c65a03f115156107e657600080fd5b5050506040518051905050610808828260010154611a7e90919063ffffffff16565b8160010181905550610827348260000154611a7e90919063ffffffff16565b816000018190555061084434600554611a7e90919063ffffffff16565b60058190555061085f82600654611a7e90919063ffffffff16565b6006819055503373ffffffffffffffffffffffffffffffffffffffff167ff3c1c7c0eb1328ddc834c4c9e579c06d35f443bf1102b034653624a239c7a40c836040518082815260200191505060405180910390a27fd1dc370699ae69fb860ed754789a4327413ec1cd379b93f2cbedf449a26b0e8583600554604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a1505050565b60025481565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060085442108061096b5750651b48eb57e00060065410155b15151561097757600080fd5b600a60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000008d69d00910d0b2afb2a99ed6c16c8129fa8e1751",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000e819f024b41358d2c08e3a868a5c5dd0566078d4",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000007": "0x000000000000000000000000000000000000000000000000000000005a388981",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000005a3b38e6"
|
||||
}
|
||||
},
|
||||
"0xd4fcab9f0a6dc0493af47c864f6f17a8a5e2e826": {
|
||||
"balance": "0x2a2dd979a35cf000",
|
||||
"code": "0x",
|
||||
"nonce": "0",
|
||||
"storage": {}
|
||||
},
|
||||
"0xe819f024b41358d2c08e3a868a5c5dd0566078d4": {
|
||||
"balance": "0x0",
|
||||
"code": "0x6060604052600436106100ba576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100bf578063095ea7b31461014d57806318160ddd146101a757806323b872dd146101d0578063313ce5671461024957806342966c681461027257806370a08231146102ad5780638da5cb5b146102fa57806395d89b411461034f578063a9059cbb146103dd578063dd62ed3e14610437578063f2fde38b146104a3575b600080fd5b34156100ca57600080fd5b6100d26104dc565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101125780820151818401526020810190506100f7565b50505050905090810190601f16801561013f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015857600080fd5b61018d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610515565b604051808215151515815260200191505060405180910390f35b34156101b257600080fd5b6101ba61069c565b6040518082815260200191505060405180910390f35b34156101db57600080fd5b61022f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506106a2565b604051808215151515815260200191505060405180910390f35b341561025457600080fd5b61025c610952565b6040518082815260200191505060405180910390f35b341561027d57600080fd5b6102936004808035906020019091905050610957565b604051808215151515815260200191505060405180910390f35b34156102b857600080fd5b6102e4600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610abe565b6040518082815260200191505060405180910390f35b341561030557600080fd5b61030d610b07565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561035a57600080fd5b610362610b2d565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103a2578082015181840152602081019050610387565b50505050905090810190601f1680156103cf5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156103e857600080fd5b61041d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610b66565b604051808215151515815260200191505060405180910390f35b341561044257600080fd5b61048d600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d01565b6040518082815260200191505060405180910390f35b34156104ae57600080fd5b6104da600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d88565b005b6040805190810160405280600b81526020017f416c6c436f6465436f696e00000000000000000000000000000000000000000081525081565b6000808214806105a157506000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054145b15156105ac57600080fd5b81600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905061077683600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610e5f90919063ffffffff16565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061080b83600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610e7d90919063ffffffff16565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506108618382610e7d90919063ffffffff16565b600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b600681565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156109b557600080fd5b610a0782Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3956606365",
|
||||
"extraData": "0x566961425443",
|
||||
"gasLimit": "5418523",
|
||||
"hash": "0x6f37eb930a25da673ea1bb80fd9e32ddac19cdf7cd4bb2eac62cc13598624077",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"mixHash": "0x10971cde68c587c750c23b8589ae868ce82c2c646636b97e7d9856470c5297c7",
|
||||
"nonce": "0x810f923ff4b450a1",
|
||||
"number": "2295103",
|
||||
"stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
|
||||
"timestamp": "1513681246",
|
||||
"totalDifficulty": "7162347056825919"
|
||||
},
|
||||
"input": "0xf86d808504e3b292008307dfa69433056b5dcac09a9b4becad0e1dcf92c19bd0af76880e92596fd62900008029a0e5f27bb66431f7081bb7f1f242003056d7f3f35414c352cd3d1848b52716dac2a07d0be78980edb0bd2a0678fc53aa90ea9558ce346b0d947967216918ac74ccea",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"error": "invalid opcode 0xfe",
|
||||
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
|
||||
"gas": "0x75fe3",
|
||||
"gasUsed": "0x75fe3",
|
||||
"input": "0xa9059cbb000000000000000000000000d4fcab9f0a6dc0493af47c864f6f17a8a5e2e82600000000000000000000000000000000000000000000000000000000000002f4",
|
||||
"to": "0xe819f024b41358d2c08e3a868a5c5dd0566078d4",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"error": "execution reverted",
|
||||
"from": "0xd4fcab9f0a6dc0493af47c864f6f17a8a5e2e826",
|
||||
"gas": "0x78d9e",
|
||||
"gasUsed": "0x76fc0",
|
||||
"input": "0x",
|
||||
"to": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
|
||||
"type": "CALL",
|
||||
"value": "0xe92596fd6290000"
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3699098917",
|
||||
"gasLimit": "5258985",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"number": "2294631",
|
||||
"timestamp": "1513675366"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x43064693d3d38ad6a7cb579e0d6d9718c8aa6b62": {
|
||||
"balance": "0x0",
|
||||
"code": "0x6060604052600436106100ba576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100bf578063095ea7b31461014d57806318160ddd146101a757806323b872dd146101d0578063313ce5671461024957806342966c68146102785780635a3b7e42146102b357806370a082311461034157806379cc67901461038e57806395d89b41146103e8578063a9059cbb14610476578063dd62ed3e146104b8575b600080fd5b34156100ca57600080fd5b6100d2610524565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101125780820151818401526020810190506100f7565b50505050905090810190601f16801561013f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015857600080fd5b61018d600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061055d565b604051808215151515815260200191505060405180910390f35b34156101b257600080fd5b6101ba6105ea565b6040518082815260200191505060405180910390f35b34156101db57600080fd5b61022f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506105f0565b604051808215151515815260200191505060405180910390f35b341561025457600080fd5b61025c610910565b604051808260ff1660ff16815260200191505060405180910390f35b341561028357600080fd5b6102996004808035906020019091905050610915565b604051808215151515815260200191505060405180910390f35b34156102be57600080fd5b6102c6610a18565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103065780820151818401526020810190506102eb565b50505050905090810190601f1680156103335780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561034c57600080fd5b610378600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a51565b6040518082815260200191505060405180910390f35b341561039957600080fd5b6103ce600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a69565b604051808215151515815260200191505060405180910390f35b34156103f357600080fd5b6103fb610bf8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561043b578082015181840152602081019050610420565b50505050905090810190601f1680156104685780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561048157600080fd5b6104b6600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610c31565b005b34156104c357600080fd5b61050e600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610e34565b6040518082815260200191505060405180910390f35b6040805190810160405280600881526020017f446f70616d696e6500000000000000000000000000000000000000000000000081525081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506001905092915050565b60005481565b6000808373ffffffffffffffffffffffffffffffffffffffff161415151561061757600080fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561066557600080fd5b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401101515156106f157fe5b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115151561077c57600080fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b601281565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561096557600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600080828254Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x296b66049cc4f9c8bf3d4f14752add261d1a980b39bdd194a7897baf39ac7579": "0x0000000000000000000000000000000000000000033b2e3c9fc9653f9e72b1e0"
|
||||
}
|
||||
},
|
||||
"0x94194bc2aaf494501d7880b61274a169f6502a54": {
|
||||
"balance": "0xea8c39a876d19888d",
|
||||
"code": "0x",
|
||||
"nonce": "265",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3699098917",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "5263953",
|
||||
"hash": "0x03a0f62a8106793dafcfae7b75fd2654322062d585a19cea568314d7205790dc",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0x15482cc64b7c00a947f5bf015dfc010db1a6a668c74df61974d6a7848c174408",
|
||||
"nonce": "0xd1bdb150f6fd170e",
|
||||
"number": "2294630",
|
||||
"stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
|
||||
"timestamp": "1513675347",
|
||||
"totalDifficulty": "7160543502214733"
|
||||
},
|
||||
"input": "0xf8ab820109855d21dba00082ca1d9443064693d3d38ad6a7cb579e0d6d9718c8aa6b6280b844a9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f90001ba0ce3ad83f5530136467b7c2bb225f406bd170f4ad59c254e5103c34eeabb5bd69a0455154527224a42ab405cacf0fe92918a75641ce4152f8db292019a5527aa956",
|
||||
"result": {
|
||||
"error": "out of gas",
|
||||
"from": "0x94194bc2aaf494501d7880b61274a169f6502a54",
|
||||
"gas": "0x7045",
|
||||
"gasUsed": "0x7045",
|
||||
"input": "0xa9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f9000",
|
||||
"to": "0x43064693d3d38ad6a7cb579e0d6d9718c8aa6b62",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3665057456",
|
||||
"gasLimit": "5232723",
|
||||
"miner": "0xf4d8e706cfb25c0decbbdd4d2e2cc10c66376a3f",
|
||||
"number": "2294501",
|
||||
"timestamp": "1513673601"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9": {
|
||||
"balance": "0x2a3fc32bcc019283",
|
||||
"code": "0x",
|
||||
"nonce": "10",
|
||||
"storage": {}
|
||||
},
|
||||
"0xabbcd5b340c80b5f1c0545c04c987b87310296ae": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100755763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632d0335ab811461007a578063548db174146100ab5780637f649783146100fc578063b092145e1461014d578063c3f44c0a14610186578063c47cf5de14610203575b600080fd5b341561008557600080fd5b610099600160a060020a0360043516610270565b60405190815260200160405180910390f35b34156100b657600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061028f95505050505050565b005b341561010757600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061029e95505050505050565b005b341561015857600080fd5b610172600160a060020a03600435811690602435166102ad565b604051901515815260200160405180910390f35b341561019157600080fd5b6100fa6004803560ff1690602480359160443591606435600160a060020a0316919060a49060843590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965050509235600160a060020a031692506102cd915050565b005b341561020e57600080fd5b61025460046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061056a95505050505050565b604051600160a060020a03909116815260200160405180910390f35b600160a060020a0381166000908152602081905260409020545b919050565b61029a816000610594565b5b50565b61029a816001610594565b5b50565b600160209081526000928352604080842090915290825290205460ff1681565b60008080600160a060020a038416158061030d5750600160a060020a038085166000908152600160209081526040808320339094168352929052205460ff165b151561031857600080fd5b6103218561056a565b600160a060020a038116600090815260208190526040808220549295507f19000000000000000000000000000000000000000000000000000000000000009230918891908b908b90517fff000000000000000000000000000000000000000000000000000000000000008089168252871660018201526c01000000000000000000000000600160a060020a038088168202600284015286811682026016840152602a8301869052841602604a820152605e810182805190602001908083835b6020831061040057805182525b601f1990920191602091820191016103e0565b6001836020036101000a0380198251168184511617909252505050919091019850604097505050505050505051809103902091506001828a8a8a6040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f1151561049957600080fd5b5050602060405103519050600160a060020a03838116908216146104bc57600080fd5b600160a060020a0380841660009081526020819052604090819020805460010190559087169086905180828051906020019080838360005b8381101561050d5780820151818401525b6020016104f4565b50505050905090810190601f16801561053a5780820380516001836020036101000a031916815260200191505b5091505060006040518083038160008661646e5a03f1915050151561055e57600080fd5b5b505050505050505050565b600060248251101561057e5750600061028a565b600160a060020a0360248301511690505b919050565b60005b825181101561060157600160a060020a033316600090815260016020526040812083918584815181106105c657fe5b90602001906020020151600160a060020a031681526020810191909152604001600020805460ff19169115159190911790555b600101610597565b5b5050505600a165627a7a723058200027e8b695e9d2dea9f3629519022a69f3a1d23055ce86406e686ea54f31ee9c0029",
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3672229776",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "5227619",
|
||||
"hash": "0xa07b3d6c6bf63f5f981016db9f2d1d93033833f2c17e8bf7209e85f1faf08076",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0x806e151ce2817be922e93e8d5921fa0f0d0fd213d6b2b9a3fa17458e74a163d0",
|
||||
"nonce": "0xbc5d43adc2c30c7d",
|
||||
"number": "2294500",
|
||||
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||
"timestamp": "1513673552",
|
||||
"totalDifficulty": "7160066586979149"
|
||||
},
|
||||
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||
"result": {
|
||||
"error": "execution reverted",
|
||||
"from": "0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9",
|
||||
"gas": "0x2d55e8",
|
||||
"gasUsed": "0xc3",
|
||||
"input": "0x73b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a98800000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xabbcd5b340c80b5f1c0545c04c987b87310296ae",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3502894804",
|
||||
"gasLimit": "4722976",
|
||||
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||
"number": "2289806",
|
||||
"timestamp": "1513601314"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "22",
|
||||
"storage": {}
|
||||
},
|
||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||
"balance": "0x4d87094125a369d9bd5",
|
||||
"code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
|
||||
}
|
||||
},
|
||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||
"balance": "0x1780d77678137ac1b775",
|
||||
"code": "0x",
|
||||
"nonce": "29072",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3509749784",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "4727564",
|
||||
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"input": "0x",
|
||||
"to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"type": "CALL",
|
||||
"value": "0x6f05b59d3b20000"
|
||||
}
|
||||
],
|
||||
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"gas": "0x10738",
|
||||
"gasUsed": "0x3ef9",
|
||||
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "117009631",
|
||||
"gasLimit": "4712388",
|
||||
"miner": "0x294e5d6c39a36ce38af1dca70c1060f78dee8070",
|
||||
"number": "25009",
|
||||
"timestamp": "1479891666"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x70c9217d814985faef62b124420f8dfbddd96433": {
|
||||
"balance": "0x4ecd70668f5d854a",
|
||||
"code": "0x",
|
||||
"nonce": "1638",
|
||||
"storage": {}
|
||||
},
|
||||
"0xc212e03b9e060e36facad5fd8f4435412ca22e6b": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156101745760e060020a600035046302d05d3f811461017c57806304a7fdbc1461018e5780630e90f957146101fb5780630fb5a6b41461021257806314baa1b61461021b57806317fc45e21461023a5780632b096926146102435780632e94420f1461025b578063325a19f11461026457806336da44681461026d5780633f81a2c01461027f5780633fc306821461029757806345ecd3d7146102d45780634665096d146102dd5780634e71d92d146102e657806351a34eb8146103085780636111bb951461032d5780636f265b93146103445780637e9014e11461034d57806390ba009114610360578063927df5e014610393578063a7f437791461046c578063ad8f50081461046e578063bc6d909414610477578063bdec3ad114610557578063c19d93fb1461059a578063c9503fe2146105ad578063e0a73a93146105b6578063ea71b02d146105bf578063ea8a1af0146105d1578063ee4a96f9146105f3578063f1ff78a01461065c575b61046c610002565b610665600054600160a060020a031681565b6040805160c081810190925261046c9160049160c4918390600690839083908082843760408051808301909152929750909561018495509193509091908390839080828437509095505050505050600554600090600160a060020a0390811633909116146106a857610002565b61068260015460a060020a900460ff166000145b90565b61069660085481565b61046c600435600154600160a060020a03166000141561072157610002565b610696600d5481565b610696600435600f8160068110156100025750015481565b61069660045481565b61069660035481565b610665600554600160a060020a031681565b61069660043560158160068110156100025750015481565b6106966004355b600b54600f5460009160028202808203928083039290810191018386101561078357601054840186900394505b50505050919050565b61069660025481565b61069660095481565b61046c600554600090600160a060020a03908116339091161461085857610002565b61046c600435600554600090600160a060020a03908116339091161461092e57610002565b6106826001805460a060020a900460ff161461020f565b610696600b5481565b61068260075460a060020a900460ff1681565b6106966004355b600b54601554600091600282028082039280830392908101910183861015610a6c5760165494506102cb565b61046c6004356024356044356040805160015460e360020a631c2d8fb302825260b260020a691858d8dbdd5b9d18dd1b02600483015291516000928392600160a060020a03919091169163e16c7d9891602481810192602092909190829003018187876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663c4b0c96a336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610b4657610002565b005b610696600a5481565b61046c60006000600060006000600160009054906101000a9004600160a060020a0316600160a060020a031663e16c7d986040518160e060020a028152600401808060b260020a691858d8dbdd5b9d18dd1b0281526020015060200190506020604051808303816000876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663c4b0c96a336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515115159050610f1757610002565b61046c5b60015b60058160ff16101561071e57600f6001820160ff166006811015610002578101549060ff83166006811015610002570154101561129057610002565b61069660015460a060020a900460ff1681565b61069660065481565b610696600c5481565b610665600754600160a060020a031681565b61046c600554600090600160a060020a0390811633909116146112c857610002565b6040805160c081810190925261046c9160049160c4918390600690839083908082843760408051808301909152929750909561018495509193509091908390839080828437509095505050505050600154600090600160a060020a03168114156113fb57610002565b610696600e5481565b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60408051918252519081900360200190f35b5060005b60068160ff16101561070857828160ff166006811015610002576020020151600f60ff831660068110156100025701558160ff82166006811015610002576020020151601560ff831660068110156100025701556001016106ac565b61071061055b565b505050565b600e8054820190555b50565b6040805160015460e060020a6313bc6d4b02825233600160a060020a03908116600484015292519216916313bc6d4b9160248181019260209290919082900301816000876161da5a03f115610002575050604051511515905061071557610002565b83861015801561079257508286105b156107b457600f546010546011548689039082030291909104900394506102cb565b8286101580156107c55750600b5486105b156107e757600f546011546012548589039082030291909104900394506102cb565b600b5486108015906107f857508186105b1561081d57600b54600f546012546013549289039281039290920204900394506102cb565b81861015801561082c57508086105b1561084e57600f546013546014548489039082030291909104900394506102cb565b60145494506102cb565b60015460a060020a900460ff1660001461087157610002565b600254600a01431161088257610002565b6040805160015460e360020a631c2d8fb302825260a860020a6a636f6e74726163746170690260048301529151600160a060020a03929092169163e16c7d989160248181019260209290919082900301816000876161da5a03f1156100025750505060405180519060200150905080600160a060020a031663771d50e16040518160e060020a0281526004018090506000604051808303816000876161da5a03f1156100025750505050565b60015460a060020a900460ff1660001461094757610002565b600254600a01431161095857610002565b6040805160015460e360020a631c2d8fb302825260a860020a6a636f6e74726163746170690260048301529151600160a060020a03929092169163e16c7d989160248181019260209290919082900301816000876161da5a03f115610002575050Line truncated
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000002cccf5e0538493c235d1c5ef6580f77d99e91396",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000000000000061a9",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000070c9217d814985faef62b124420f8dfbddd96433"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "117066792",
|
||||
"extraData": "0xd783010502846765746887676f312e372e33856c696e7578",
|
||||
"gasLimit": "4712388",
|
||||
"hash": "0xe23e8d4562a1045b70cbc99fefb20c101a8f0fc8559a80d65fea8896e2f1d46e",
|
||||
"miner": "0x71842f946b98800fe6feb49f0ae4e253259031c9",
|
||||
"mixHash": "0x0aada9d6e93dd4db0d09c0488dc0a048fca2ccdc1f3fc7b83ba2a8d393a3a4ff",
|
||||
"nonce": "0x70849d5838dee2e9",
|
||||
"number": "25008",
|
||||
"stateRoot": "0x1e01d2161794768c5b917069e73d86e8dca80cd7f3168c0597de420ab93a3b7b",
|
||||
"timestamp": "1479891641",
|
||||
"totalDifficulty": "1896347038589"
|
||||
},
|
||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||
"result": {
|
||||
"error": "invalid jump destination (PUSH1) 2",
|
||||
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
||||
"gas": "0x37b38",
|
||||
"gasUsed": "0x37b38",
|
||||
"input": "0x51a34eb8000000000000000000000000000000000000000000000027fad02094277c0000",
|
||||
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,618 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
duktape "gopkg.in/olebedev/go-duktape.v3"
|
||||
)
|
||||
|
||||
// bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js.
|
||||
const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT<n&&n<MAX_INT}function smallToArray(n){if(n<1e7)return[n];if(n<1e14)return[n%1e7,Math.floor(n/1e7)];return[n%1e7,Math.floor(n/1e7)%1e7,Math.floor(n/1e14)]}function arrayToSmall(arr){trim(arr);var length=arr.length;if(length<4&&compareAbs(arr,MAX_INT_ARR)<0){switch(length){case 0:return 0;case 1:return arr[0];case 2:return arr[0]+arr[1]*BASE;default:return arr[0]+(arr[1]+arr[2]*BASE)*BASE}}return arr}function trim(v){var i=v.length;while(v[--i]===0);v.length=i+1}function createArray(length){var x=new Array(length);var i=-1;while(++i<length){x[i]=0}return x}function truncate(n){if(n>0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i<l_b;i++){sum=a[i]+b[i]+carry;carry=sum>=base?1:0;r[i]=sum-carry*base}while(i<l_a){sum=a[i]+carry;carry=sum===base?1:0;r[i++]=sum-carry*base}if(carry>0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i<l;i++){sum=a[i]-base+carry;carry=Math.floor(sum/base);r[i]=sum-carry*base;carry+=1}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i<b_l;i++){difference=a[i]-borrow-b[i];if(difference<0){difference+=base;borrow=1}else borrow=0;r[i]=difference}for(i=b_l;i<a_l;i++){difference=a[i]-borrow;if(difference<0)difference+=base;else{r[i++]=difference;break}r[i]=difference}for(;i<a_l;i++){r[i]=a[i]}trim(r);return r}function subtractAny(a,b,sign){var value;if(compareAbs(a,b)>=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i<l;i++){difference=a[i]+carry;carry=Math.floor(difference/base);difference%=base;r[i]=difference<0?difference+base:difference}r=arrayToSmall(r);if(typeof r==="number"){if(sign)r=-r;return new SmallInteger(r)}return new BigInteger(r,sign)}BigInteger.prototype.subtract=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.add(n.negate())}var a=this.value,b=n.value;if(n.isSmall)return subtractSmall(a,Math.abs(b),this.sign);return subtractAny(a,b,this.sign)};BigInteger.prototype.minus=BigInteger.prototype.subtract;SmallInteger.prototype.subtract=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.add(n.negate())}var b=n.value;if(n.isSmall){return new SmallInteger(a-b)}return subtractSmall(b,Math.abs(a),a>=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i<a_l;++i){a_i=a[i];for(var j=0;j<b_l;++j){b_j=b[j];product=a_i*b_j+r[i+j];carry=Math.floor(product/base);r[i+j]=product-carry*base;r[i+j+1]+=carry}}trim(r);return r}function multiplySmall(a,b){var l=a.length,r=new Array(l),base=BASE,carry=0,product,i;for(i=0;i<l;i++){product=a[i]*b+carry;carry=Math.floor(product/base);r[i]=product-carry*base}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}Line truncated
|
||||
|
||||
// makeSlice convert an unsafe memory pointer with the given type into a Go byte
|
||||
// slice.
|
||||
//
|
||||
// Note, the returned slice uses the same memory area as the input arguments.
|
||||
// If those are duktape stack items, popping them off **will** make the slice
|
||||
// contents change.
|
||||
func makeSlice(ptr unsafe.Pointer, size uint) []byte {
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(ptr), int(size), int(size)}
|
||||
|
||||
return *(*[]byte)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
|
||||
func popSlice(ctx *duktape.Context) []byte {
|
||||
blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
|
||||
ctx.Pop()
|
||||
return blob
|
||||
}
|
||||
|
||||
// pushBigInt create a JavaScript BigInteger in the VM.
|
||||
func pushBigInt(n *big.Int, ctx *duktape.Context) {
|
||||
ctx.GetGlobalString("bigInt")
|
||||
ctx.PushString(n.String())
|
||||
ctx.Call(1)
|
||||
}
|
||||
|
||||
// opWrapper provides a JavaScript wrapper around OpCode.
|
||||
type opWrapper struct {
|
||||
op vm.OpCode
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
|
||||
// onto the VM stack.
|
||||
func (ow *opWrapper) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
|
||||
vm.PutPropString(obj, "toNumber")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
|
||||
vm.PutPropString(obj, "toString")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
|
||||
vm.PutPropString(obj, "isPush")
|
||||
}
|
||||
|
||||
// memoryWrapper provides a JavaScript wrapper around vm.Memory.
|
||||
type memoryWrapper struct {
|
||||
memory *vm.Memory
|
||||
}
|
||||
|
||||
// slice returns the requested range of memory as a byte slice.
|
||||
func (mw *memoryWrapper) slice(begin, end int64) []byte {
|
||||
if mw.memory.Len() < int(end) {
|
||||
// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
|
||||
// runtime goes belly up https://github.com/golang/go/issues/15639.
|
||||
log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
|
||||
return nil
|
||||
}
|
||||
return mw.memory.Get(begin, end-begin)
|
||||
}
|
||||
|
||||
// getUint returns the 32 bytes at the specified address interpreted as a uint.
|
||||
func (mw *memoryWrapper) getUint(addr int64) *big.Int {
|
||||
if mw.memory.Len() < int(addr)+32 {
|
||||
// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
|
||||
// runtime goes belly up https://github.com/golang/go/issues/15639.
|
||||
log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
|
||||
return new(big.Int)
|
||||
}
|
||||
return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
|
||||
// onto the VM stack.
|
||||
func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
// Generate the `slice` method which takes two ints and returns a buffer
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
|
||||
ctx.Pop2()
|
||||
|
||||
ptr := ctx.PushFixedBuffer(len(blob))
|
||||
copy(makeSlice(ptr, uint(len(blob))), blob[:])
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "slice")
|
||||
|
||||
// Generate the `getUint` method which takes an int and returns a bigint
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
offset := int64(ctx.GetInt(-1))
|
||||
ctx.Pop()
|
||||
|
||||
pushBigInt(mw.getUint(offset), ctx)
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getUint")
|
||||
}
|
||||
|
||||
// stackWrapper provides a JavaScript wrapper around vm.Stack.
|
||||
type stackWrapper struct {
|
||||
stack *vm.Stack
|
||||
}
|
||||
|
||||
// peek returns the nth-from-the-top element of the stack.
|
||||
func (sw *stackWrapper) peek(idx int) *big.Int {
|
||||
if len(sw.stack.Data()) <= idx {
|
||||
// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
|
||||
// runtime goes belly up https://github.com/golang/go/issues/15639.
|
||||
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
|
||||
return new(big.Int)
|
||||
}
|
||||
return sw.stack.Data()[len(sw.stack.Data())-idx-1]
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
|
||||
// onto the VM stack.
|
||||
func (sw *stackWrapper) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
|
||||
vm.PutPropString(obj, "length")
|
||||
|
||||
// Generate the `peek` method which takes an int and returns a bigint
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
offset := ctx.GetInt(-1)
|
||||
ctx.Pop()
|
||||
|
||||
pushBigInt(sw.peek(offset), ctx)
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "peek")
|
||||
}
|
||||
|
||||
// dbWrapper provides a JavaScript wrapper around vm.Database.
|
||||
type dbWrapper struct {
|
||||
db vm.StateDB
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable database and pushes it
|
||||
// onto the VM stack.
|
||||
func (dw *dbWrapper) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
// Push the wrapper for statedb.GetBalance
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getBalance")
|
||||
|
||||
// Push the wrapper for statedb.GetNonce
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getNonce")
|
||||
|
||||
// Push the wrapper for statedb.GetCode
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
|
||||
|
||||
ptr := ctx.PushFixedBuffer(len(code))
|
||||
copy(makeSlice(ptr, uint(len(code))), code[:])
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getCode")
|
||||
|
||||
// Push the wrapper for statedb.GetState
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
hash := popSlice(ctx)
|
||||
addr := popSlice(ctx)
|
||||
|
||||
state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
|
||||
|
||||
ptr := ctx.PushFixedBuffer(len(state))
|
||||
copy(makeSlice(ptr, uint(len(state))), state[:])
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getState")
|
||||
|
||||
// Push the wrapper for statedb.Exists
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "exists")
|
||||
}
|
||||
|
||||
// contractWrapper provides a JavaScript wrapper around vm.Contract
|
||||
type contractWrapper struct {
|
||||
contract *vm.Contract
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
|
||||
// onto the VM stack.
|
||||
func (cw *contractWrapper) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
// Push the wrapper for contract.Caller
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
ptr := ctx.PushFixedBuffer(20)
|
||||
copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getCaller")
|
||||
|
||||
// Push the wrapper for contract.Address
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
ptr := ctx.PushFixedBuffer(20)
|
||||
copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getAddress")
|
||||
|
||||
// Push the wrapper for contract.Value
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
pushBigInt(cw.contract.Value(), ctx)
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getValue")
|
||||
|
||||
// Push the wrapper for contract.Input
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
blob := cw.contract.Input
|
||||
|
||||
ptr := ctx.PushFixedBuffer(len(blob))
|
||||
copy(makeSlice(ptr, uint(len(blob))), blob[:])
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getInput")
|
||||
}
|
||||
|
||||
// Tracer provides an implementation of Tracer that evaluates a Javascript
|
||||
// function for each VM execution step.
|
||||
type Tracer struct {
|
||||
inited bool // Flag whether the context was already inited from the EVM
|
||||
|
||||
vm *duktape.Context // Javascript VM instance
|
||||
|
||||
tracerObject int // Stack index of the tracer JavaScript object
|
||||
stateObject int // Stack index of the global state to pull arguments from
|
||||
|
||||
opWrapper *opWrapper // Wrapper around the VM opcode
|
||||
stackWrapper *stackWrapper // Wrapper around the VM stack
|
||||
memoryWrapper *memoryWrapper // Wrapper around the VM memory
|
||||
contractWrapper *contractWrapper // Wrapper around the contract object
|
||||
dbWrapper *dbWrapper // Wrapper around the VM environment
|
||||
|
||||
pcValue *uint // Swappable pc value wrapped by a log accessor
|
||||
gasValue *uint // Swappable gas value wrapped by a log accessor
|
||||
costValue *uint // Swappable cost value wrapped by a log accessor
|
||||
depthValue *uint // Swappable depth value wrapped by a log accessor
|
||||
errorValue *string // Swappable error value wrapped by a log accessor
|
||||
|
||||
ctx map[string]interface{} // Transaction context gathered throughout execution
|
||||
err error // Error, if one has occurred
|
||||
|
||||
interrupt uint32 // Atomic flag to signal execution interruption
|
||||
reason error // Textual reason for the interruption
|
||||
}
|
||||
|
||||
// New instantiates a new tracer instance. code specifies a Javascript snippet,
|
||||
// which must evaluate to an expression returning an object with 'step', 'fault'
|
||||
// and 'result' functions.
|
||||
func New(code string) (*Tracer, error) {
|
||||
// Resolve any tracers by name and assemble the tracer object
|
||||
if tracer, ok := tracer(code); ok {
|
||||
code = tracer
|
||||
}
|
||||
tracer := &Tracer{
|
||||
vm: duktape.New(),
|
||||
ctx: make(map[string]interface{}),
|
||||
opWrapper: new(opWrapper),
|
||||
stackWrapper: new(stackWrapper),
|
||||
memoryWrapper: new(memoryWrapper),
|
||||
contractWrapper: new(contractWrapper),
|
||||
dbWrapper: new(dbWrapper),
|
||||
pcValue: new(uint),
|
||||
gasValue: new(uint),
|
||||
costValue: new(uint),
|
||||
depthValue: new(uint),
|
||||
}
|
||||
// Set up builtins for this environment
|
||||
tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
|
||||
ctx.PushString(hexutil.Encode(popSlice(ctx)))
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
|
||||
var word common.Hash
|
||||
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
|
||||
word = common.BytesToHash(makeSlice(ptr, size))
|
||||
} else {
|
||||
word = common.HexToHash(ctx.GetString(-1))
|
||||
}
|
||||
ctx.Pop()
|
||||
copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
|
||||
var addr common.Address
|
||||
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
|
||||
addr = common.BytesToAddress(makeSlice(ptr, size))
|
||||
} else {
|
||||
addr = common.HexToAddress(ctx.GetString(-1))
|
||||
}
|
||||
ctx.Pop()
|
||||
copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
|
||||
var from common.Address
|
||||
if ptr, size := ctx.GetBuffer(-2); ptr != nil {
|
||||
from = common.BytesToAddress(makeSlice(ptr, size))
|
||||
} else {
|
||||
from = common.HexToAddress(ctx.GetString(-2))
|
||||
}
|
||||
nonce := uint64(ctx.GetInt(-1))
|
||||
ctx.Pop2()
|
||||
|
||||
contract := crypto.CreateAddress(from, nonce)
|
||||
copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
|
||||
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))]
|
||||
ctx.PushBoolean(ok)
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
|
||||
start, end := ctx.GetInt(-2), ctx.GetInt(-1)
|
||||
ctx.Pop2()
|
||||
|
||||
blob := popSlice(ctx)
|
||||
size := end - start
|
||||
|
||||
if start < 0 || start > end || end > len(blob) {
|
||||
// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
|
||||
// runtime goes belly up https://github.com/golang/go/issues/15639.
|
||||
log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
|
||||
ctx.PushFixedBuffer(0)
|
||||
return 1
|
||||
}
|
||||
copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
|
||||
return 1
|
||||
})
|
||||
// Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
|
||||
if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
|
||||
log.Warn("Failed to compile tracer", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
|
||||
|
||||
if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
|
||||
return nil, fmt.Errorf("Trace object must expose a function step()")
|
||||
}
|
||||
tracer.vm.Pop()
|
||||
|
||||
if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
|
||||
return nil, fmt.Errorf("Trace object must expose a function fault()")
|
||||
}
|
||||
tracer.vm.Pop()
|
||||
|
||||
if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
|
||||
return nil, fmt.Errorf("Trace object must expose a function result()")
|
||||
}
|
||||
tracer.vm.Pop()
|
||||
|
||||
// Tracer is valid, inject the big int library to access large numbers
|
||||
tracer.vm.EvalString(bigIntegerJS)
|
||||
tracer.vm.PutGlobalString("bigInt")
|
||||
|
||||
// Push the global environment state as object #1 into the JSVM stack
|
||||
tracer.stateObject = tracer.vm.PushObject()
|
||||
|
||||
logObject := tracer.vm.PushObject()
|
||||
|
||||
tracer.opWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(logObject, "op")
|
||||
|
||||
tracer.stackWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(logObject, "stack")
|
||||
|
||||
tracer.memoryWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(logObject, "memory")
|
||||
|
||||
tracer.contractWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(logObject, "contract")
|
||||
|
||||
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
|
||||
tracer.vm.PutPropString(logObject, "getPC")
|
||||
|
||||
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
|
||||
tracer.vm.PutPropString(logObject, "getGas")
|
||||
|
||||
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
|
||||
tracer.vm.PutPropString(logObject, "getCost")
|
||||
|
||||
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
|
||||
tracer.vm.PutPropString(logObject, "getDepth")
|
||||
|
||||
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
if tracer.errorValue != nil {
|
||||
ctx.PushString(*tracer.errorValue)
|
||||
} else {
|
||||
ctx.PushUndefined()
|
||||
}
|
||||
return 1
|
||||
})
|
||||
tracer.vm.PutPropString(logObject, "getError")
|
||||
|
||||
tracer.vm.PutPropString(tracer.stateObject, "log")
|
||||
|
||||
tracer.dbWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(tracer.stateObject, "db")
|
||||
|
||||
return tracer, nil
|
||||
}
|
||||
|
||||
// Stop terminates execution of the tracer at the first opportune moment.
|
||||
func (jst *Tracer) Stop(err error) {
|
||||
jst.reason = err
|
||||
atomic.StoreUint32(&jst.interrupt, 1)
|
||||
}
|
||||
|
||||
// call executes a method on a JS object, catching any errors, formatting and
|
||||
// returning them as error objects.
|
||||
func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error) {
|
||||
// Execute the JavaScript call and return any error
|
||||
jst.vm.PushString(method)
|
||||
for _, arg := range args {
|
||||
jst.vm.GetPropString(jst.stateObject, arg)
|
||||
}
|
||||
code := jst.vm.PcallProp(jst.tracerObject, len(args))
|
||||
defer jst.vm.Pop()
|
||||
|
||||
if code != 0 {
|
||||
err := jst.vm.SafeToString(-1)
|
||||
return nil, errors.New(err)
|
||||
}
|
||||
// No error occurred, extract return value and return
|
||||
return json.RawMessage(jst.vm.JsonEncode(-1)), nil
|
||||
}
|
||||
|
||||
func wrapError(context string, err error) error {
|
||||
var message string
|
||||
switch err := err.(type) {
|
||||
default:
|
||||
message = err.Error()
|
||||
}
|
||||
return fmt.Errorf("%v in server-side tracer function '%v'", message, context)
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
jst.ctx["type"] = "CALL"
|
||||
if create {
|
||||
jst.ctx["type"] = "CREATE"
|
||||
}
|
||||
jst.ctx["from"] = from
|
||||
jst.ctx["to"] = to
|
||||
jst.ctx["input"] = input
|
||||
jst.ctx["gas"] = gas
|
||||
jst.ctx["value"] = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
// Initialize the context if it wasn't done yet
|
||||
if !jst.inited {
|
||||
jst.ctx["block"] = env.BlockNumber.Uint64()
|
||||
jst.inited = true
|
||||
}
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if atomic.LoadUint32(&jst.interrupt) > 0 {
|
||||
jst.err = jst.reason
|
||||
return nil
|
||||
}
|
||||
jst.opWrapper.op = op
|
||||
jst.stackWrapper.stack = stack
|
||||
jst.memoryWrapper.memory = memory
|
||||
jst.contractWrapper.contract = contract
|
||||
jst.dbWrapper.db = env.StateDB
|
||||
|
||||
*jst.pcValue = uint(pc)
|
||||
*jst.gasValue = uint(gas)
|
||||
*jst.costValue = uint(cost)
|
||||
*jst.depthValue = uint(depth)
|
||||
|
||||
jst.errorValue = nil
|
||||
if err != nil {
|
||||
jst.errorValue = new(string)
|
||||
*jst.errorValue = err.Error()
|
||||
}
|
||||
_, err := jst.call("step", "log", "db")
|
||||
if err != nil {
|
||||
jst.err = wrapError("step", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
// while running an opcode.
|
||||
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
// Apart from the error, everything matches the previous invocation
|
||||
jst.errorValue = new(string)
|
||||
*jst.errorValue = err.Error()
|
||||
|
||||
_, err := jst.call("fault", "log", "db")
|
||||
if err != nil {
|
||||
jst.err = wrapError("fault", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
jst.ctx["output"] = output
|
||||
jst.ctx["gasUsed"] = gasUsed
|
||||
jst.ctx["time"] = t.String()
|
||||
|
||||
if err != nil {
|
||||
jst.ctx["error"] = err.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||
func (jst *Tracer) GetResult() (json.RawMessage, error) {
|
||||
// Transform the context into a JavaScript object and inject into the state
|
||||
obj := jst.vm.PushObject()
|
||||
|
||||
for key, val := range jst.ctx {
|
||||
switch val := val.(type) {
|
||||
case uint64:
|
||||
jst.vm.PushUint(uint(val))
|
||||
|
||||
case string:
|
||||
jst.vm.PushString(val)
|
||||
|
||||
case []byte:
|
||||
ptr := jst.vm.PushFixedBuffer(len(val))
|
||||
copy(makeSlice(ptr, uint(len(val))), val[:])
|
||||
|
||||
case common.Address:
|
||||
ptr := jst.vm.PushFixedBuffer(20)
|
||||
copy(makeSlice(ptr, 20), val[:])
|
||||
|
||||
case *big.Int:
|
||||
pushBigInt(val, jst.vm)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type: %T", val))
|
||||
}
|
||||
jst.vm.PutPropString(obj, key)
|
||||
}
|
||||
jst.vm.PutPropString(jst.stateObject, "ctx")
|
||||
|
||||
// Finalize the trace and return the results
|
||||
result, err := jst.call("result", "ctx", "db")
|
||||
if err != nil {
|
||||
jst.err = wrapError("result", err)
|
||||
}
|
||||
// Clean up the JavaScript environment
|
||||
jst.vm.DestroyHeap()
|
||||
jst.vm.Destroy()
|
||||
|
||||
return result, jst.err
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -42,8 +43,8 @@ func (account) ReturnGas(*big.Int) {}
|
||||
func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
func runTrace(tracer *Tracer) (json.RawMessage, error) {
|
||||
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
@@ -52,12 +53,11 @@ func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tracer.GetResult()
|
||||
}
|
||||
|
||||
func TestTracing(t *testing.T) {
|
||||
tracer, err := NewJavascriptTracer("{count: 0, step: function() { this.count += 1; }, result: function() { return this.count; }}")
|
||||
tracer, err := New("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -66,18 +66,13 @@ func TestTracing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
value, ok := ret.(float64)
|
||||
if !ok {
|
||||
t.Errorf("Expected return value to be float64, was %T", ret)
|
||||
}
|
||||
if value != 3 {
|
||||
t.Errorf("Expected return value to be 3, got %v", value)
|
||||
if !bytes.Equal(ret, []byte("3")) {
|
||||
t.Errorf("Expected return value to be 3, got %s", string(ret))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStack(t *testing.T) {
|
||||
tracer, err := NewJavascriptTracer("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, result: function() { return this.depths; }}")
|
||||
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -86,15 +81,13 @@ func TestStack(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := []int{0, 1, 2}
|
||||
if !reflect.DeepEqual(ret, expected) {
|
||||
t.Errorf("Expected return value to be %#v, got %#v", expected, ret)
|
||||
if !bytes.Equal(ret, []byte("[0,1,2]")) {
|
||||
t.Errorf("Expected return value to be [0,1,2], got %s", string(ret))
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpcodes(t *testing.T) {
|
||||
tracer, err := NewJavascriptTracer("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, result: function() { return this.opcodes; }}")
|
||||
tracer, err := New("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -103,16 +96,16 @@ func TestOpcodes(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := []string{"PUSH1", "PUSH1", "STOP"}
|
||||
if !reflect.DeepEqual(ret, expected) {
|
||||
t.Errorf("Expected return value to be %#v, got %#v", expected, ret)
|
||||
if !bytes.Equal(ret, []byte("[\"PUSH1\",\"PUSH1\",\"STOP\"]")) {
|
||||
t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHalt(t *testing.T) {
|
||||
t.Skip("duktape doesn't support abortion")
|
||||
|
||||
timeout := errors.New("stahp")
|
||||
tracer, err := NewJavascriptTracer("{step: function() { while(1); }, result: function() { return null; }}")
|
||||
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -128,12 +121,12 @@ func TestHalt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHaltBetweenSteps(t *testing.T) {
|
||||
tracer, err := NewJavascriptTracer("{step: function() {}, result: function() { return null; }}")
|
||||
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
|
||||
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
|
||||
@@ -141,7 +134,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||
tracer.Stop(timeout)
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
|
||||
|
||||
if _, err := tracer.GetResult(); err.Error() != "stahp in server-side tracer function 'step'" {
|
||||
if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
|
||||
t.Errorf("Expected timeout error, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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/>.
|
||||
|
||||
// Package tracers is a collection of JavaScript transaction tracers.
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/internal/tracers"
|
||||
)
|
||||
|
||||
// all contains all the built in JavaScript tracers by name.
|
||||
var all = make(map[string]string)
|
||||
|
||||
// camel converts a snake cased input string into a camel cased output.
|
||||
func camel(str string) string {
|
||||
pieces := strings.Split(str, "_")
|
||||
for i := 1; i < len(pieces); i++ {
|
||||
pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
|
||||
}
|
||||
return strings.Join(pieces, "")
|
||||
}
|
||||
|
||||
// init retrieves the JavaScript transaction tracers included in go-ethereum.
|
||||
func init() {
|
||||
for _, file := range tracers.AssetNames() {
|
||||
name := camel(strings.TrimSuffix(file, ".js"))
|
||||
all[name] = string(tracers.MustAsset(file))
|
||||
}
|
||||
}
|
||||
|
||||
// tracer retrieves a specific JavaScript tracer by name.
|
||||
func tracer(name string) (string, bool) {
|
||||
if tracer, ok := all[name]; ok {
|
||||
return tracer, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// 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/>.
|
||||
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
)
|
||||
|
||||
// To generate a new callTracer test, copy paste the makeTest method below into
|
||||
// a Geth console and call it with a transaction hash you which to export.
|
||||
|
||||
/*
|
||||
// makeTest generates a callTracer test by running a prestate reassembled and a
|
||||
// call trace run, assembling all the gathered information into a test case.
|
||||
var makeTest = function(tx, rewind) {
|
||||
// Generate the genesis block from the block, transaction and prestate data
|
||||
var block = eth.getBlock(eth.getTransaction(tx).blockHash);
|
||||
var genesis = eth.getBlock(block.parentHash);
|
||||
|
||||
delete genesis.gasUsed;
|
||||
delete genesis.logsBloom;
|
||||
delete genesis.parentHash;
|
||||
delete genesis.receiptsRoot;
|
||||
delete genesis.sha3Uncles;
|
||||
delete genesis.size;
|
||||
delete genesis.transactions;
|
||||
delete genesis.transactionsRoot;
|
||||
delete genesis.uncles;
|
||||
|
||||
genesis.gasLimit = genesis.gasLimit.toString();
|
||||
genesis.number = genesis.number.toString();
|
||||
genesis.timestamp = genesis.timestamp.toString();
|
||||
|
||||
genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
|
||||
for (var key in genesis.alloc) {
|
||||
genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
|
||||
}
|
||||
genesis.config = admin.nodeInfo.protocols.eth.config;
|
||||
|
||||
// Generate the call trace and produce the test input
|
||||
var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
|
||||
delete result.time;
|
||||
|
||||
console.log(JSON.stringify({
|
||||
genesis: genesis,
|
||||
context: {
|
||||
number: block.number.toString(),
|
||||
difficulty: block.difficulty,
|
||||
timestamp: block.timestamp.toString(),
|
||||
gasLimit: block.gasLimit.toString(),
|
||||
miner: block.miner,
|
||||
},
|
||||
input: eth.getRawTransaction(tx),
|
||||
result: result,
|
||||
}, null, 2));
|
||||
}
|
||||
*/
|
||||
|
||||
// callTrace is the result of a callTracer run.
|
||||
type callTrace struct {
|
||||
Type string `json:"type"`
|
||||
From common.Address `json:"from"`
|
||||
To common.Address `json:"to"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Output hexutil.Bytes `json:"output"`
|
||||
Gas *hexutil.Uint64 `json:"gas,omitempty"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
|
||||
Value *hexutil.Big `json:"value,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Calls []callTrace `json:"calls,omitempty"`
|
||||
}
|
||||
|
||||
type callContext struct {
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||
Time math.HexOrDecimal64 `json:"timestamp"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit"`
|
||||
Miner common.Address `json:"miner"`
|
||||
}
|
||||
|
||||
// callTracerTest defines a single test to check the call tracer against.
|
||||
type callTracerTest struct {
|
||||
Genesis *core.Genesis `json:"genesis"`
|
||||
Context *callContext `json:"context"`
|
||||
Input string `json:"input"`
|
||||
Result *callTrace `json:"result"`
|
||||
}
|
||||
|
||||
// Iterates over all the input-output datasets in the tracer test harness and
|
||||
// runs the JavaScript tracers against them.
|
||||
func TestCallTracer(t *testing.T) {
|
||||
files, err := ioutil.ReadDir("testdata")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve tracer test suite: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file.Name(), "call_tracer_") {
|
||||
continue
|
||||
}
|
||||
file := file // capture range variable
|
||||
t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Call tracer test found, read if from disk
|
||||
blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read testcase: %v", err)
|
||||
}
|
||||
test := new(callTracerTest)
|
||||
if err := json.Unmarshal(blob, test); err != nil {
|
||||
t.Fatalf("failed to parse testcase: %v", err)
|
||||
}
|
||||
// Configure a blockchain with the given prestate
|
||||
tx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
|
||||
t.Fatalf("failed to parse testcase input: %v", err)
|
||||
}
|
||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
||||
origin, _ := signer.Sender(tx)
|
||||
|
||||
context := vm.Context{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
Origin: origin,
|
||||
Coinbase: test.Context.Miner,
|
||||
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
|
||||
Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
|
||||
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||
GasLimit: new(big.Int).SetUint64(uint64(test.Context.GasLimit)),
|
||||
GasPrice: tx.GasPrice(),
|
||||
}
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := tests.MakePreState(db, test.Genesis.Alloc)
|
||||
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer, err := New("callTracer")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
msg, err := tx.AsMessage(signer)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if _, _, _, _, err = st.TransitionDb(); err != nil {
|
||||
t.Fatalf("failed to execute transaction: %v", err)
|
||||
}
|
||||
// Retrieve the trace result and compare against the etalon
|
||||
res, err := tracer.GetResult()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve trace result: %v", err)
|
||||
}
|
||||
ret := new(callTrace)
|
||||
if err := json.Unmarshal(res, ret); err != nil {
|
||||
t.Fatalf("failed to unmarshal trace result: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(ret, test.Result) {
|
||||
t.Fatalf("trace mismatch: have %+v, want %+v", ret, test.Result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,12 @@ func NewMemDatabase() (*MemDatabase, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewMemDatabaseWithCap(size int) (*MemDatabase, error) {
|
||||
return &MemDatabase{
|
||||
db: make(map[string][]byte, size),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *MemDatabase) Put(key []byte, value []byte) error {
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
@@ -74,14 +80,6 @@ func (db *MemDatabase) Keys() [][]byte {
|
||||
return keys
|
||||
}
|
||||
|
||||
/*
|
||||
func (db *MemDatabase) GetKeys() []*common.Key {
|
||||
data, _ := db.Get([]byte("KeyRing"))
|
||||
|
||||
return []*common.Key{common.NewKeyFromBytes(data)}
|
||||
}
|
||||
*/
|
||||
|
||||
func (db *MemDatabase) Delete(key []byte) error {
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
@@ -96,6 +94,8 @@ func (db *MemDatabase) NewBatch() Batch {
|
||||
return &memBatch{db: db}
|
||||
}
|
||||
|
||||
func (db *MemDatabase) Len() int { return len(db.db) }
|
||||
|
||||
type kv struct{ k, v []byte }
|
||||
|
||||
type memBatch struct {
|
||||
|
||||
@@ -1,364 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
// fakeBig is used to provide an interface to Javascript for 'big.NewInt'
|
||||
type fakeBig struct{}
|
||||
|
||||
// NewInt creates a new big.Int with the specified int64 value.
|
||||
func (fb *fakeBig) NewInt(x int64) *big.Int {
|
||||
return big.NewInt(x)
|
||||
}
|
||||
|
||||
// OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it
|
||||
// as an object, instead of a number.
|
||||
type opCodeWrapper struct {
|
||||
op vm.OpCode
|
||||
}
|
||||
|
||||
// toNumber returns the ID of this opcode as an integer
|
||||
func (ocw *opCodeWrapper) toNumber() int {
|
||||
return int(ocw.op)
|
||||
}
|
||||
|
||||
// toString returns the string representation of the opcode
|
||||
func (ocw *opCodeWrapper) toString() string {
|
||||
return ocw.op.String()
|
||||
}
|
||||
|
||||
// isPush returns true if the op is a Push
|
||||
func (ocw *opCodeWrapper) isPush() bool {
|
||||
return ocw.op.IsPush()
|
||||
}
|
||||
|
||||
// MarshalJSON serializes the opcode as JSON
|
||||
func (ocw *opCodeWrapper) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(ocw.op.String())
|
||||
}
|
||||
|
||||
// toValue returns an otto.Value for the opCodeWrapper
|
||||
func (ocw *opCodeWrapper) toValue(vm *otto.Otto) otto.Value {
|
||||
value, _ := vm.ToValue(ocw)
|
||||
obj := value.Object()
|
||||
obj.Set("toNumber", ocw.toNumber)
|
||||
obj.Set("toString", ocw.toString)
|
||||
obj.Set("isPush", ocw.isPush)
|
||||
return value
|
||||
}
|
||||
|
||||
// memoryWrapper provides a JS wrapper around vm.Memory
|
||||
type memoryWrapper struct {
|
||||
memory *vm.Memory
|
||||
}
|
||||
|
||||
// slice returns the requested range of memory as a byte slice
|
||||
func (mw *memoryWrapper) slice(begin, end int64) []byte {
|
||||
return mw.memory.Get(begin, end-begin)
|
||||
}
|
||||
|
||||
// getUint returns the 32 bytes at the specified address interpreted
|
||||
// as an unsigned integer
|
||||
func (mw *memoryWrapper) getUint(addr int64) *big.Int {
|
||||
ret := big.NewInt(0)
|
||||
ret.SetBytes(mw.memory.GetPtr(addr, 32))
|
||||
return ret
|
||||
}
|
||||
|
||||
// toValue returns an otto.Value for the memoryWrapper
|
||||
func (mw *memoryWrapper) toValue(vm *otto.Otto) otto.Value {
|
||||
value, _ := vm.ToValue(mw)
|
||||
obj := value.Object()
|
||||
obj.Set("slice", mw.slice)
|
||||
obj.Set("getUint", mw.getUint)
|
||||
return value
|
||||
}
|
||||
|
||||
// stackWrapper provides a JS wrapper around vm.Stack
|
||||
type stackWrapper struct {
|
||||
stack *vm.Stack
|
||||
}
|
||||
|
||||
// peek returns the nth-from-the-top element of the stack.
|
||||
func (sw *stackWrapper) peek(idx int) *big.Int {
|
||||
return sw.stack.Data()[len(sw.stack.Data())-idx-1]
|
||||
}
|
||||
|
||||
// length returns the length of the stack
|
||||
func (sw *stackWrapper) length() int {
|
||||
return len(sw.stack.Data())
|
||||
}
|
||||
|
||||
// toValue returns an otto.Value for the stackWrapper
|
||||
func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value {
|
||||
value, _ := vm.ToValue(sw)
|
||||
obj := value.Object()
|
||||
obj.Set("peek", sw.peek)
|
||||
obj.Set("length", sw.length)
|
||||
return value
|
||||
}
|
||||
|
||||
// dbWrapper provides a JS wrapper around vm.Database
|
||||
type dbWrapper struct {
|
||||
db vm.StateDB
|
||||
}
|
||||
|
||||
// getBalance retrieves an account's balance
|
||||
func (dw *dbWrapper) getBalance(addr []byte) *big.Int {
|
||||
return dw.db.GetBalance(common.BytesToAddress(addr))
|
||||
}
|
||||
|
||||
// getNonce retrieves an account's nonce
|
||||
func (dw *dbWrapper) getNonce(addr []byte) uint64 {
|
||||
return dw.db.GetNonce(common.BytesToAddress(addr))
|
||||
}
|
||||
|
||||
// getCode retrieves an account's code
|
||||
func (dw *dbWrapper) getCode(addr []byte) []byte {
|
||||
return dw.db.GetCode(common.BytesToAddress(addr))
|
||||
}
|
||||
|
||||
// getState retrieves an account's state data for the given hash
|
||||
func (dw *dbWrapper) getState(addr []byte, hash common.Hash) common.Hash {
|
||||
return dw.db.GetState(common.BytesToAddress(addr), hash)
|
||||
}
|
||||
|
||||
// exists returns true iff the account exists
|
||||
func (dw *dbWrapper) exists(addr []byte) bool {
|
||||
return dw.db.Exist(common.BytesToAddress(addr))
|
||||
}
|
||||
|
||||
// toValue returns an otto.Value for the dbWrapper
|
||||
func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value {
|
||||
value, _ := vm.ToValue(dw)
|
||||
obj := value.Object()
|
||||
obj.Set("getBalance", dw.getBalance)
|
||||
obj.Set("getNonce", dw.getNonce)
|
||||
obj.Set("getCode", dw.getCode)
|
||||
obj.Set("getState", dw.getState)
|
||||
obj.Set("exists", dw.exists)
|
||||
return value
|
||||
}
|
||||
|
||||
// contractWrapper provides a JS wrapper around vm.Contract
|
||||
type contractWrapper struct {
|
||||
contract *vm.Contract
|
||||
}
|
||||
|
||||
func (c *contractWrapper) caller() common.Address {
|
||||
return c.contract.Caller()
|
||||
}
|
||||
|
||||
func (c *contractWrapper) address() common.Address {
|
||||
return c.contract.Address()
|
||||
}
|
||||
|
||||
func (c *contractWrapper) value() *big.Int {
|
||||
return c.contract.Value()
|
||||
}
|
||||
|
||||
func (c *contractWrapper) calldata() []byte {
|
||||
return c.contract.Input
|
||||
}
|
||||
|
||||
func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value {
|
||||
value, _ := vm.ToValue(c)
|
||||
obj := value.Object()
|
||||
obj.Set("caller", c.caller)
|
||||
obj.Set("address", c.address)
|
||||
obj.Set("value", c.value)
|
||||
obj.Set("calldata", c.calldata)
|
||||
return value
|
||||
}
|
||||
|
||||
// JavascriptTracer provides an implementation of Tracer that evaluates a
|
||||
// Javascript function for each VM execution step.
|
||||
type JavascriptTracer struct {
|
||||
vm *otto.Otto // Javascript VM instance
|
||||
traceobj *otto.Object // User-supplied object to call
|
||||
op *opCodeWrapper // Wrapper around the VM opcode
|
||||
log map[string]interface{} // (Reusable) map for the `log` arg to `step`
|
||||
logvalue otto.Value // JS view of `log`
|
||||
memory *memoryWrapper // Wrapper around the VM memory
|
||||
stack *stackWrapper // Wrapper around the VM stack
|
||||
db *dbWrapper // Wrapper around the VM environment
|
||||
dbvalue otto.Value // JS view of `db`
|
||||
contract *contractWrapper // Wrapper around the contract object
|
||||
err error // Error, if one has occurred
|
||||
result interface{} // Final result to return to the user
|
||||
}
|
||||
|
||||
// NewJavascriptTracer instantiates a new JavascriptTracer instance.
|
||||
// code specifies a Javascript snippet, which must evaluate to an expression
|
||||
// returning an object with 'step' and 'result' functions.
|
||||
func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
||||
vm := otto.New()
|
||||
vm.Interrupt = make(chan func(), 1)
|
||||
|
||||
// Set up builtins for this environment
|
||||
vm.Set("big", &fakeBig{})
|
||||
vm.Set("toHex", hexutil.Encode)
|
||||
|
||||
jstracer, err := vm.Object("(" + code + ")")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check the required functions exist
|
||||
step, err := jstracer.Get("step")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !step.IsFunction() {
|
||||
return nil, fmt.Errorf("Trace object must expose a function step()")
|
||||
}
|
||||
|
||||
result, err := jstracer.Get("result")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !result.IsFunction() {
|
||||
return nil, fmt.Errorf("Trace object must expose a function result()")
|
||||
}
|
||||
// Create the persistent log object
|
||||
var (
|
||||
op = new(opCodeWrapper)
|
||||
mem = new(memoryWrapper)
|
||||
stack = new(stackWrapper)
|
||||
db = new(dbWrapper)
|
||||
contract = new(contractWrapper)
|
||||
)
|
||||
log := map[string]interface{}{
|
||||
"op": op.toValue(vm),
|
||||
"memory": mem.toValue(vm),
|
||||
"stack": stack.toValue(vm),
|
||||
"contract": contract.toValue(vm),
|
||||
}
|
||||
logvalue, _ := vm.ToValue(log)
|
||||
|
||||
return &JavascriptTracer{
|
||||
vm: vm,
|
||||
traceobj: jstracer,
|
||||
op: op,
|
||||
log: log,
|
||||
logvalue: logvalue,
|
||||
memory: mem,
|
||||
stack: stack,
|
||||
db: db,
|
||||
dbvalue: db.toValue(vm),
|
||||
contract: contract,
|
||||
err: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Stop terminates execution of any JavaScript
|
||||
func (jst *JavascriptTracer) Stop(err error) {
|
||||
jst.vm.Interrupt <- func() {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// callSafely executes a method on a JS object, catching any panics and
|
||||
// returning them as error objects.
|
||||
func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) {
|
||||
defer func() {
|
||||
if caught := recover(); caught != nil {
|
||||
switch caught := caught.(type) {
|
||||
case error:
|
||||
err = caught
|
||||
case string:
|
||||
err = errors.New(caught)
|
||||
case fmt.Stringer:
|
||||
err = errors.New(caught.String())
|
||||
default:
|
||||
panic(caught)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
value, err := jst.traceobj.Call(method, argumentList...)
|
||||
ret, _ = value.Export()
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func wrapError(context string, err error) error {
|
||||
var message string
|
||||
switch err := err.(type) {
|
||||
case *otto.Error:
|
||||
message = err.String()
|
||||
default:
|
||||
message = err.Error()
|
||||
}
|
||||
return fmt.Errorf("%v in server-side tracer function '%v'", message, context)
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution
|
||||
func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
jst.op.op = op
|
||||
jst.memory.memory = memory
|
||||
jst.stack.stack = stack
|
||||
jst.db.db = env.StateDB
|
||||
jst.contract.contract = contract
|
||||
|
||||
jst.log["pc"] = pc
|
||||
jst.log["gas"] = gas
|
||||
jst.log["cost"] = cost
|
||||
jst.log["depth"] = depth
|
||||
jst.log["account"] = contract.Address()
|
||||
|
||||
delete(jst.log, "error")
|
||||
if err != nil {
|
||||
jst.log["error"] = err
|
||||
}
|
||||
_, err := jst.callSafely("step", jst.logvalue, jst.dbvalue)
|
||||
if err != nil {
|
||||
jst.err = wrapError("step", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes
|
||||
func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
//TODO! @Arachnid please figure out of there's anything we can use this method for
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||
func (jst *JavascriptTracer) GetResult() (result interface{}, err error) {
|
||||
if jst.err != nil {
|
||||
return nil, jst.err
|
||||
}
|
||||
|
||||
result, err = jst.callSafely("result")
|
||||
if err != nil {
|
||||
err = wrapError("result", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
+24
-20
@@ -196,26 +196,6 @@ web3._extend({
|
||||
call: 'debug_setHead',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlock',
|
||||
call: 'debug_traceBlock',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockFromFile',
|
||||
call: 'debug_traceBlockFromFile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByNumber',
|
||||
call: 'debug_traceBlockByNumber',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByHash',
|
||||
call: 'debug_traceBlockByHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'seedHash',
|
||||
call: 'debug_seedHash',
|
||||
@@ -332,6 +312,30 @@ web3._extend({
|
||||
call: 'debug_writeMemProfile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlock',
|
||||
call: 'debug_traceBlock',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockFromFile',
|
||||
call: 'debug_traceBlockFromFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByNumber',
|
||||
call: 'debug_traceBlockByNumber',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByHash',
|
||||
call: 'debug_traceBlockByHash',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceTransaction',
|
||||
call: 'debug_traceTransaction',
|
||||
|
||||
@@ -127,7 +127,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
|
||||
}
|
||||
block, _ := t.genesis(config).ToBlock()
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, t.json.Pre)
|
||||
statedb := MakePreState(db, t.json.Pre)
|
||||
|
||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
||||
msg, err := t.json.Tx.toMessage(post)
|
||||
@@ -158,7 +158,7 @@ func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
|
||||
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
||||
}
|
||||
|
||||
func makePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||
sdb := state.NewDatabase(db)
|
||||
statedb, _ := state.New(common.Hash{}, sdb)
|
||||
for addr, a := range accounts {
|
||||
|
||||
@@ -80,7 +80,7 @@ type vmExecMarshaling struct {
|
||||
|
||||
func (t *VMTest) Run(vmconfig vm.Config) error {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, t.json.Pre)
|
||||
statedb := MakePreState(db, t.json.Pre)
|
||||
ret, gasRemaining, err := t.exec(statedb, vmconfig)
|
||||
|
||||
if t.json.GasRemaining == nil {
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
name = "gopkg.in/check.v1"
|
||||
packages = ["."]
|
||||
revision = "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec"
|
||||
|
||||
[[projects]]
|
||||
branch = "v3"
|
||||
name = "gopkg.in/olebedev/go-duktape.v3"
|
||||
packages = ["."]
|
||||
revision = "391c1c40178e77a6003d889b96e0e41129aeb894"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "043f802c0b40e2622bf784443d3e3959f0d01e9a795e3bfe30a72060dec10c63"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
[[constraint]]
|
||||
branch = "v1"
|
||||
name = "gopkg.in/check.v1"
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Oleg Lebedev
|
||||
|
||||
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.
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
# Duktape bindings for Go(Golang)
|
||||
|
||||
[](https://app.wercker.com/project/bykey/3a5bb2e639a4b4efaf4c8bf7cab7442d)
|
||||
[](https://travis-ci.org/olebedev/go-duktape)
|
||||
[](https://ci.appveyor.com/project/olebedev/go-duktape/branch/v3)
|
||||
[](https://gitter.im/olebedev/go-duktape?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
[Duktape](http://duktape.org/index.html) is a thin, embeddable javascript engine.
|
||||
Most of the [api](http://duktape.org/api.html) is implemented.
|
||||
The exceptions are listed [here](https://github.com/olebedev/go-duktape/blob/master/api.go#L1566).
|
||||
|
||||
### Usage
|
||||
|
||||
The package is fully go-getable, no need to install any external C libraries.
|
||||
So, just type `go get gopkg.in/olebedev/go-duktape.v3` to install.
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "gopkg.in/olebedev/go-duktape.v3"
|
||||
|
||||
func main() {
|
||||
ctx := duktape.New()
|
||||
ctx.PevalString(`2 + 3`)
|
||||
result := ctx.GetNumber(-1)
|
||||
ctx.Pop()
|
||||
fmt.Println("result is:", result)
|
||||
// To prevent memory leaks, don't forget to clean up after
|
||||
// yourself when you're done using a context.
|
||||
ctx.DestroyHeap()
|
||||
}
|
||||
```
|
||||
|
||||
### Go specific notes
|
||||
|
||||
Bindings between Go and Javascript contexts are not fully functional.
|
||||
However, binding a Go function to the Javascript context is available:
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "gopkg.in/olebedev/go-duktape.v3"
|
||||
|
||||
func main() {
|
||||
ctx := duktape.New()
|
||||
ctx.PushGlobalGoFunction("log", func(c *duktape.Context) int {
|
||||
fmt.Println(c.SafeToString(-1))
|
||||
return 0
|
||||
})
|
||||
ctx.PevalString(`log('Go lang Go!')`)
|
||||
}
|
||||
```
|
||||
then run it.
|
||||
```bash
|
||||
$ go run *.go
|
||||
Go lang Go!
|
||||
$
|
||||
```
|
||||
|
||||
### Timers
|
||||
|
||||
There is a method to inject timers to the global scope:
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "gopkg.in/olebedev/go-duktape.v3"
|
||||
|
||||
func main() {
|
||||
ctx := duktape.New()
|
||||
|
||||
// Let's inject `setTimeout`, `setInterval`, `clearTimeout`,
|
||||
// `clearInterval` into global scope.
|
||||
ctx.PushTimers()
|
||||
|
||||
ch := make(chan string)
|
||||
ctx.PushGlobalGoFunction("second", func(_ *Context) int {
|
||||
ch <- "second step"
|
||||
return 0
|
||||
})
|
||||
ctx.PevalString(`
|
||||
setTimeout(second, 0);
|
||||
print('first step');
|
||||
`)
|
||||
fmt.Println(<-ch)
|
||||
}
|
||||
```
|
||||
then run it
|
||||
```bash
|
||||
$ go run *.go
|
||||
first step
|
||||
second step
|
||||
$
|
||||
```
|
||||
|
||||
Also you can `FlushTimers()`.
|
||||
|
||||
### Command line tool
|
||||
|
||||
Install `go get gopkg.in/olebedev/go-duktape.v3/...`.
|
||||
Execute file.js: `$GOPATH/bin/go-duk file.js`.
|
||||
|
||||
### Benchmarks
|
||||
| prog | time |
|
||||
| ------------|-------|
|
||||
|[otto](https://github.com/robertkrimen/otto)|200.13s|
|
||||
|[anko](https://github.com/mattn/anko)|231.19s|
|
||||
|[agora](https://github.com/PuerkitoBio/agora/)|149.33s|
|
||||
|[GopherLua](https://github.com/yuin/gopher-lua/)|8.39s|
|
||||
|**go-duktape**|**9.80s**|
|
||||
|
||||
More details are [here](https://github.com/olebedev/go-duktape/wiki/Benchmarks).
|
||||
|
||||
### Status
|
||||
|
||||
The package is not fully tested, so be careful.
|
||||
|
||||
|
||||
### Contribution
|
||||
|
||||
Pull requests are welcome! Also, if you want to discuss something send a pull request with proposal and changes.
|
||||
__Convention:__ fork the repository and make changes on your fork in a feature branch.
|
||||
+1616
File diff suppressed because it is too large.
Load diff
+34
@@ -0,0 +1,34 @@
|
||||
os: Visual Studio 2015
|
||||
|
||||
clone_folder: C:\gopath\src\gopkg.in/olebedev/go-duktape.v3
|
||||
clone_depth: 5
|
||||
version: "{branch}.{build}"
|
||||
environment:
|
||||
global:
|
||||
GOPATH: C:\gopath
|
||||
CC: gcc.exe
|
||||
matrix:
|
||||
- DUKTAPE_ARCH: amd64
|
||||
MSYS2_ARCH: x86_64
|
||||
MSYS2_BITS: 64
|
||||
MSYSTEM: MINGW64
|
||||
PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH%
|
||||
- DUKTAPE_ARCH: 386
|
||||
MSYS2_ARCH: i686
|
||||
MSYS2_BITS: 32
|
||||
MSYSTEM: MINGW32
|
||||
PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%
|
||||
|
||||
install:
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.9.2.windows-%DUKTAPE_ARCH%.zip
|
||||
- 7z x go1.9.2.windows-%DUKTAPE_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
build_script:
|
||||
- go get -t
|
||||
- go install ./...
|
||||
|
||||
test_script:
|
||||
- go test ./...
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package duktape
|
||||
|
||||
const (
|
||||
CompileEval uint = 1 << iota
|
||||
CompileFunction
|
||||
CompileStrict
|
||||
CompileSafe
|
||||
CompileNoResult
|
||||
CompileNoSource
|
||||
CompileStrlen
|
||||
)
|
||||
|
||||
const (
|
||||
TypeNone Type = iota
|
||||
TypeUndefined
|
||||
TypeNull
|
||||
TypeBoolean
|
||||
TypeNumber
|
||||
TypeString
|
||||
TypeObject
|
||||
TypeBuffer
|
||||
TypePointer
|
||||
TypeLightFunc
|
||||
)
|
||||
|
||||
const (
|
||||
TypeMaskNone uint = 1 << iota
|
||||
TypeMaskUndefined
|
||||
TypeMaskNull
|
||||
TypeMaskBoolean
|
||||
TypeMaskNumber
|
||||
TypeMaskString
|
||||
TypeMaskObject
|
||||
TypeMaskBuffer
|
||||
TypeMaskPointer
|
||||
TypeMaskLightFunc
|
||||
)
|
||||
|
||||
const (
|
||||
EnumIncludeNonenumerable uint = 1 << iota
|
||||
EnumIncludeInternal
|
||||
EnumOwnPropertiesOnly
|
||||
EnumArrayIndicesOnly
|
||||
EnumSortArrayIndices
|
||||
NoProxyBehavior
|
||||
)
|
||||
|
||||
const (
|
||||
ErrNone int = 0
|
||||
|
||||
// Internal to Duktape
|
||||
ErrUnimplemented int = 50 + iota
|
||||
ErrUnsupported
|
||||
ErrInternal
|
||||
ErrAlloc
|
||||
ErrAssertion
|
||||
ErrAPI
|
||||
ErrUncaughtError
|
||||
)
|
||||
|
||||
const (
|
||||
// Common prototypes
|
||||
ErrError int = 1 + iota
|
||||
ErrEval
|
||||
ErrRange
|
||||
ErrReference
|
||||
ErrSyntax
|
||||
ErrType
|
||||
ErrURI
|
||||
)
|
||||
|
||||
const (
|
||||
// Returned error values
|
||||
ErrRetUnimplemented int = -(ErrUnimplemented + iota)
|
||||
ErrRetUnsupported
|
||||
ErrRetInternal
|
||||
ErrRetAlloc
|
||||
ErrRetAssertion
|
||||
ErrRetAPI
|
||||
ErrRetUncaughtError
|
||||
)
|
||||
|
||||
const (
|
||||
ErrRetError int = -(ErrError + iota)
|
||||
ErrRetEval
|
||||
ErrRetRange
|
||||
ErrRetReference
|
||||
ErrRetSyntax
|
||||
ErrRetType
|
||||
ErrRetURI
|
||||
)
|
||||
|
||||
const (
|
||||
ExecSuccess = iota
|
||||
ExecError
|
||||
)
|
||||
|
||||
const (
|
||||
LogTrace int = iota
|
||||
LogDebug
|
||||
LogInfo
|
||||
LogWarn
|
||||
LogError
|
||||
LogFatal
|
||||
)
|
||||
|
||||
const (
|
||||
BufobjDuktapeAuffer = 0
|
||||
BufobjNodejsAuffer = 1
|
||||
BufobjArraybuffer = 2
|
||||
BufobjDataview = 3
|
||||
BufobjInt8array = 4
|
||||
BufobjUint8array = 5
|
||||
BufobjUint8clampedarray = 6
|
||||
BufobjInt16array = 7
|
||||
BufobjUint16array = 8
|
||||
BufobjInt32array = 9
|
||||
BufobjUint32array = 10
|
||||
BufobjFloat32array = 11
|
||||
BufobjFloat64array = 12
|
||||
)
|
||||
+612
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* Pool allocator for low memory targets.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include "duktape.h"
|
||||
#include "duk_alloc_pool.h"
|
||||
|
||||
/* Define to enable some debug printfs. */
|
||||
/* #define DUK_ALLOC_POOL_DEBUG */
|
||||
|
||||
/* Define to enable approximate waste tracking. */
|
||||
/* #define DUK_ALLOC_POOL_TRACK_WASTE */
|
||||
|
||||
/* Define to track global highwater for used and waste bytes. VERY SLOW, only
|
||||
* useful for manual testing.
|
||||
*/
|
||||
/* #define DUK_ALLOC_POOL_TRACK_HIGHWATER */
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
#if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */
|
||||
extern const void * const duk_rom_compressed_pointers[];
|
||||
#endif
|
||||
const void *duk_alloc_pool_romptr_low = NULL;
|
||||
const void *duk_alloc_pool_romptr_high = NULL;
|
||||
static void duk__alloc_pool_romptr_init(void);
|
||||
#endif
|
||||
|
||||
#if defined(DUK_USE_HEAPPTR16)
|
||||
void *duk_alloc_pool_ptrcomp_base = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
static void duk__alloc_pool_dprintf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pool initialization
|
||||
*/
|
||||
|
||||
void *duk_alloc_pool_init(char *buffer,
|
||||
size_t size,
|
||||
const duk_pool_config *configs,
|
||||
duk_pool_state *states,
|
||||
int num_pools,
|
||||
duk_pool_global *global) {
|
||||
double t_min, t_max, t_curr, x;
|
||||
int step, i, j, n;
|
||||
size_t total;
|
||||
char *p;
|
||||
|
||||
/* XXX: check that 'size' is not too large when using pointer
|
||||
* compression.
|
||||
*/
|
||||
|
||||
/* To optimize pool counts first come up with a 't' which still allows
|
||||
* total pool size to fit within user provided region. After that
|
||||
* sprinkle any remaining bytes to the counts. Binary search with a
|
||||
* fixed step count; last round uses 't_min' as 't_curr' to ensure it
|
||||
* succeeds.
|
||||
*/
|
||||
|
||||
t_min = 0.0; /* Unless config is insane, this should always be "good". */
|
||||
t_max = 1e6;
|
||||
|
||||
for (step = 0; ; step++) {
|
||||
if (step >= 100) {
|
||||
/* Force "known good", rerun config, and break out.
|
||||
* Deals with rounding corner cases where t_curr is
|
||||
* persistently "bad" even though t_min is a valid
|
||||
* solution.
|
||||
*/
|
||||
t_curr = t_min;
|
||||
} else {
|
||||
t_curr = (t_min + t_max) / 2.0;
|
||||
}
|
||||
|
||||
for (i = 0, total = 0; i < num_pools; i++) {
|
||||
states[i].size = configs[i].size;
|
||||
|
||||
/* Target bytes = A*t + B ==> target count = (A*t + B) / block_size.
|
||||
* Rely on A and B being small enough so that 'x' won't wrap.
|
||||
*/
|
||||
x = ((double) configs[i].a * t_curr + (double) configs[i].b) / (double) configs[i].size;
|
||||
|
||||
states[i].count = (unsigned int) x;
|
||||
total += (size_t) states[i].size * (size_t) states[i].count;
|
||||
if (total > size) {
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
/* t_curr is good. */
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (good)\n",
|
||||
step, t_min, t_curr, t_max, (long) total, (long) size);
|
||||
#endif
|
||||
if (step >= 100) {
|
||||
/* Keep state[] initialization state. The state was
|
||||
* created using the highest 't_min'.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
t_min = t_curr;
|
||||
continue;
|
||||
|
||||
bad:
|
||||
/* t_curr is bad. */
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (bad)\n",
|
||||
step, t_min, t_curr, t_max, (long) total, (long) size);
|
||||
#endif
|
||||
|
||||
if (step >= 1000) {
|
||||
/* Cannot find any good solution; shouldn't happen
|
||||
* unless config is bad or 'size' is so small that
|
||||
* even a baseline allocation won't fit.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
t_max = t_curr;
|
||||
/* continue */
|
||||
}
|
||||
|
||||
/* The base configuration is now good; sprinkle any leftovers to
|
||||
* pools in descending order. Note that for good t_curr, 'total'
|
||||
* indicates allocated bytes so far and 'size - total' indicates
|
||||
* leftovers.
|
||||
*/
|
||||
for (i = num_pools - 1; i >= 0; i--) {
|
||||
while (size - total >= states[i].size) {
|
||||
/* Ignore potential wrapping of states[i].count as the count
|
||||
* is 32 bits and shouldn't wrap in practice.
|
||||
*/
|
||||
states[i].count++;
|
||||
total += states[i].size;
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: sprinkle %ld bytes (%ld left after) to pool index %ld, new count %ld\n",
|
||||
(long) states[i].size, (long) (size - total), (long) i, (long) states[i].count);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Pool counts are final. Allocate the user supplied region based
|
||||
* on the final counts, initialize free lists for each block size,
|
||||
* and otherwise finalize 'state' for use.
|
||||
*/
|
||||
p = buffer;
|
||||
global->num_pools = num_pools;
|
||||
global->states = states;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: global highwater mark tracking enabled, THIS IS VERY SLOW!\n");
|
||||
#endif
|
||||
global->hwm_used_bytes = 0U;
|
||||
global->hwm_waste_bytes = 0U;
|
||||
#endif
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: approximate waste tracking enabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DUK_USE_HEAPPTR16)
|
||||
/* Register global base value for pointer compression, assumes
|
||||
* a single active pool -4 allows a single subtract to be used and
|
||||
* still ensures no non-NULL pointer encodes to zero.
|
||||
*/
|
||||
duk_alloc_pool_ptrcomp_base = (void *) (p - 4);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < num_pools; i++) {
|
||||
n = (int) states[i].count;
|
||||
if (n > 0) {
|
||||
states[i].first = (duk_pool_free *) p;
|
||||
for (j = 0; j < n; j++) {
|
||||
char *p_next = p + states[i].size;
|
||||
((duk_pool_free *) p)->next =
|
||||
(j == n - 1) ? (duk_pool_free *) NULL : (duk_pool_free *) p_next;
|
||||
p = p_next;
|
||||
}
|
||||
} else {
|
||||
states[i].first = (duk_pool_free *) NULL;
|
||||
}
|
||||
states[i].alloc_end = p;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
states[i].hwm_used_count = 0;
|
||||
#endif
|
||||
/* All members of 'state' now initialized. */
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool_init: block size %5ld, count %5ld, %8ld total bytes, "
|
||||
"end %p\n",
|
||||
(long) states[i].size, (long) states[i].count,
|
||||
(long) states[i].size * (long) states[i].count,
|
||||
(void *) states[i].alloc_end);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
/* ROM pointer compression precomputation. Assumes a single active
|
||||
* pool.
|
||||
*/
|
||||
duk__alloc_pool_romptr_init();
|
||||
#endif
|
||||
|
||||
/* Use 'global' as udata. */
|
||||
return (void *) global;
|
||||
}
|
||||
|
||||
/*
|
||||
* Misc helpers
|
||||
*/
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
|
||||
static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) {
|
||||
/* Rely on the base pointer and size being divisible by 4 and thus
|
||||
* aligned. Use 32-bit markers: a 4-byte resolution is good enough,
|
||||
* and comparing 32 bits at a time makes false waste estimates less
|
||||
* likely than when comparing as bytes.
|
||||
*/
|
||||
duk_uint32_t *p, *p_start, *p_end;
|
||||
size_t used_round;
|
||||
|
||||
used_round = (used + 3U) & ~0x03U; /* round up to 4 */
|
||||
p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size);
|
||||
p_start = (duk_uint32_t *) ((duk_uint8_t *) ptr + used_round);
|
||||
p = (duk_uint32_t *) p_start;
|
||||
while (p != p_end) {
|
||||
*p++ = DUK_ALLOC_POOL_WASTE_MARKER;
|
||||
}
|
||||
}
|
||||
#else /* DUK_ALLOC_POOL_TRACK_WASTE */
|
||||
static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) {
|
||||
(void) ptr; (void) used; (void) size;
|
||||
}
|
||||
#endif /* DUK_ALLOC_POOL_TRACK_WASTE */
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
|
||||
static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) {
|
||||
duk_uint32_t *p, *p_end, *p_start;
|
||||
|
||||
/* Assumes size is >= 4. */
|
||||
p_start = (duk_uint32_t *) ptr;
|
||||
p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size);
|
||||
p = p_end;
|
||||
|
||||
/* This scan may cause harmless valgrind complaints: there may be
|
||||
* uninitialized bytes within the legitimate allocation or between
|
||||
* the start of the waste marker and the end of the allocation.
|
||||
*/
|
||||
do {
|
||||
p--;
|
||||
if (*p == DUK_ALLOC_POOL_WASTE_MARKER) {
|
||||
;
|
||||
} else {
|
||||
return (size_t) (p_end - p - 1) * 4U;
|
||||
}
|
||||
} while (p != p_start);
|
||||
|
||||
return size;
|
||||
}
|
||||
#else /* DUK_ALLOC_POOL_TRACK_WASTE */
|
||||
static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) {
|
||||
(void) ptr; (void) size;
|
||||
return 0;
|
||||
}
|
||||
#endif /* DUK_ALLOC_POOL_TRACK_WASTE */
|
||||
|
||||
static int duk__alloc_pool_ptr_in_freelist(duk_pool_state *s, void *ptr) {
|
||||
duk_pool_free *curr;
|
||||
|
||||
for (curr = s->first; curr != NULL; curr = curr->next) {
|
||||
if ((void *) curr == ptr) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res) {
|
||||
void *curr;
|
||||
size_t free_count;
|
||||
size_t used_count;
|
||||
size_t waste_bytes;
|
||||
|
||||
curr = s->alloc_end - (s->size * s->count);
|
||||
free_count = 0U;
|
||||
waste_bytes = 0U;
|
||||
while (curr != s->alloc_end) {
|
||||
if (duk__alloc_pool_ptr_in_freelist(s, curr)) {
|
||||
free_count++;
|
||||
} else {
|
||||
waste_bytes += duk__alloc_pool_get_waste_estimate(curr, s->size);
|
||||
}
|
||||
curr = curr + s->size;
|
||||
}
|
||||
used_count = (size_t) (s->count - free_count);
|
||||
|
||||
res->used_count = used_count;
|
||||
res->used_bytes = (size_t) (used_count * s->size);
|
||||
res->free_count = free_count;
|
||||
res->free_bytes = (size_t) (free_count * s->size);
|
||||
res->waste_bytes = waste_bytes;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
res->hwm_used_count = s->hwm_used_count;
|
||||
#else
|
||||
res->hwm_used_count = 0U;
|
||||
#endif
|
||||
}
|
||||
|
||||
void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res) {
|
||||
int i;
|
||||
size_t total_used = 0U;
|
||||
size_t total_free = 0U;
|
||||
size_t total_waste = 0U;
|
||||
|
||||
for (i = 0; i < g->num_pools; i++) {
|
||||
duk_pool_state *s = &g->states[i];
|
||||
duk_pool_stats stats;
|
||||
|
||||
duk_alloc_pool_get_pool_stats(s, &stats);
|
||||
|
||||
total_used += stats.used_bytes;
|
||||
total_free += stats.free_bytes;
|
||||
total_waste += stats.waste_bytes;
|
||||
}
|
||||
|
||||
res->used_bytes = total_used;
|
||||
res->free_bytes = total_free;
|
||||
res->waste_bytes = total_waste;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
res->hwm_used_bytes = g->hwm_used_bytes;
|
||||
res->hwm_waste_bytes = g->hwm_waste_bytes;
|
||||
#else
|
||||
res->hwm_used_bytes = 0U;
|
||||
res->hwm_waste_bytes = 0U;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
static void duk__alloc_pool_update_highwater(duk_pool_global *g) {
|
||||
int i;
|
||||
size_t total_used = 0U;
|
||||
size_t total_free = 0U;
|
||||
size_t total_waste = 0U;
|
||||
|
||||
/* Per pool highwater used count, useful to checking if a pool is
|
||||
* too small.
|
||||
*/
|
||||
for (i = 0; i < g->num_pools; i++) {
|
||||
duk_pool_state *s = &g->states[i];
|
||||
duk_pool_stats stats;
|
||||
|
||||
duk_alloc_pool_get_pool_stats(s, &stats);
|
||||
if (stats.used_count > s->hwm_used_count) {
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: pool %ld (%ld bytes) highwater updated: count %ld -> %ld\n",
|
||||
(long) i, (long) s->size,
|
||||
(long) s->hwm_used_count, (long) stats.used_count);
|
||||
#endif
|
||||
s->hwm_used_count = stats.used_count;
|
||||
}
|
||||
|
||||
total_used += stats.used_bytes;
|
||||
total_free += stats.free_bytes;
|
||||
total_waste += stats.waste_bytes;
|
||||
}
|
||||
|
||||
/* Global highwater mark for used and waste bytes. Both fields are
|
||||
* updated from the same snapshot based on highest used count.
|
||||
* This is VERY, VERY slow and only useful for development.
|
||||
* (Note that updating HWM states for pools individually and then
|
||||
* summing them won't create a consistent global snapshot. There
|
||||
* are still easy ways to make this much, much faster.)
|
||||
*/
|
||||
if (total_used > g->hwm_used_bytes) {
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: global highwater updated: used=%ld, bytes=%ld -> "
|
||||
"used=%ld, bytes=%ld\n",
|
||||
(long) g->hwm_used_bytes, (long) g->hwm_waste_bytes,
|
||||
(long) total_used, (long) total_waste);
|
||||
#endif
|
||||
g->hwm_used_bytes = total_used;
|
||||
g->hwm_waste_bytes = total_waste;
|
||||
}
|
||||
}
|
||||
#else /* DUK_ALLOC_POOL_TRACK_HIGHWATER */
|
||||
static void duk__alloc_pool_update_highwater(duk_pool_global *g) {
|
||||
(void) g;
|
||||
}
|
||||
#endif /* DUK_ALLOC_POOL_TRACK_HIGHWATER */
|
||||
|
||||
/*
|
||||
* Allocation providers
|
||||
*/
|
||||
|
||||
void *duk_alloc_pool(void *udata, duk_size_t size) {
|
||||
duk_pool_global *g = (duk_pool_global *) udata;
|
||||
int i, n;
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_alloc_pool: %p %ld\n", udata, (long) size);
|
||||
#endif
|
||||
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0, n = g->num_pools; i < n; i++) {
|
||||
duk_pool_state *st = g->states + i;
|
||||
|
||||
if (size <= st->size) {
|
||||
duk_pool_free *res = st->first;
|
||||
if (res != NULL) {
|
||||
st->first = res->next;
|
||||
duk__alloc_pool_set_waste_marker((void *) res, size, st->size);
|
||||
duk__alloc_pool_update_highwater(g);
|
||||
return (void *) res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocation doesn't fit or no free entries, try to borrow
|
||||
* from the next block size. There's no support for preventing
|
||||
* a borrow at present.
|
||||
*/
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size) {
|
||||
duk_pool_global *g = (duk_pool_global *) udata;
|
||||
int i, j, n;
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_realloc_pool: %p %p %ld\n", udata, ptr, (long) size);
|
||||
#endif
|
||||
|
||||
if (ptr == NULL) {
|
||||
return duk_alloc_pool(udata, size);
|
||||
}
|
||||
if (size == 0) {
|
||||
duk_free_pool(udata, ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Non-NULL pointers are necessarily from the pool so we should
|
||||
* always be able to find the allocation.
|
||||
*/
|
||||
|
||||
for (i = 0, n = g->num_pools; i < n; i++) {
|
||||
duk_pool_state *st = g->states + i;
|
||||
char *new_ptr;
|
||||
|
||||
/* Because 'ptr' is assumed to be in the pool and pools are
|
||||
* allocated in sequence, it suffices to check for end pointer
|
||||
* only.
|
||||
*/
|
||||
if ((char *) ptr >= st->alloc_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (size <= st->size) {
|
||||
/* Allocation still fits existing allocation. Check if
|
||||
* we can shrink the allocation to a smaller block size
|
||||
* (smallest possible).
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
duk_pool_state *st2 = g->states + j;
|
||||
|
||||
if (size <= st2->size) {
|
||||
new_ptr = (char *) st2->first;
|
||||
if (new_ptr != NULL) {
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_realloc_pool: shrink, block size %ld -> %ld\n",
|
||||
(long) st->size, (long) st2->size);
|
||||
#endif
|
||||
st2->first = ((duk_pool_free *) new_ptr)->next;
|
||||
memcpy((void *) new_ptr, (const void *) ptr, (size_t) size);
|
||||
((duk_pool_free *) ptr)->next = st->first;
|
||||
st->first = (duk_pool_free *) ptr;
|
||||
duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size);
|
||||
duk__alloc_pool_update_highwater(g);
|
||||
return (void *) new_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to shrink; return existing pointer. */
|
||||
duk__alloc_pool_set_waste_marker((void *) ptr, size, st->size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Find first free larger block. */
|
||||
for (j = i + 1; j < n; j++) {
|
||||
duk_pool_state *st2 = g->states + j;
|
||||
|
||||
if (size <= st2->size) {
|
||||
new_ptr = (char *) st2->first;
|
||||
if (new_ptr != NULL) {
|
||||
st2->first = ((duk_pool_free *) new_ptr)->next;
|
||||
memcpy((void *) new_ptr, (const void *) ptr, (size_t) st->size);
|
||||
((duk_pool_free *) ptr)->next = st->first;
|
||||
st->first = (duk_pool_free *) ptr;
|
||||
duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size);
|
||||
duk__alloc_pool_update_highwater(g);
|
||||
return (void *) new_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to resize. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We should never be here because 'ptr' should be a valid pool
|
||||
* entry and thus always found above.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void duk_free_pool(void *udata, void *ptr) {
|
||||
duk_pool_global *g = (duk_pool_global *) udata;
|
||||
int i, n;
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_DEBUG)
|
||||
duk__alloc_pool_dprintf("duk_free_pool: %p %p\n", udata, ptr);
|
||||
#endif
|
||||
|
||||
if (ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, n = g->num_pools; i < n; i++) {
|
||||
duk_pool_state *st = g->states + i;
|
||||
|
||||
/* Enough to check end address only. */
|
||||
if ((char *) ptr >= st->alloc_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
((duk_pool_free *) ptr)->next = st->first;
|
||||
st->first = (duk_pool_free *) ptr;
|
||||
#if 0 /* never necessary when freeing */
|
||||
duk__alloc_pool_update_highwater(g);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* We should never be here because 'ptr' should be a valid pool
|
||||
* entry and thus always found above.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Pointer compression
|
||||
*/
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
static void duk__alloc_pool_romptr_init(void) {
|
||||
/* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer"
|
||||
* later on.
|
||||
*/
|
||||
const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers;
|
||||
duk_alloc_pool_romptr_low = duk_alloc_pool_romptr_high = (const void *) *ptrs;
|
||||
while (*ptrs) {
|
||||
if (*ptrs > duk_alloc_pool_romptr_high) {
|
||||
duk_alloc_pool_romptr_high = (const void *) *ptrs;
|
||||
}
|
||||
if (*ptrs < duk_alloc_pool_romptr_low) {
|
||||
duk_alloc_pool_romptr_low = (const void *) *ptrs;
|
||||
}
|
||||
ptrs++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Encode/decode functions are defined in the header to allow inlining. */
|
||||
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr) {
|
||||
/* The if-condition should be the fastest possible check
|
||||
* for "is 'ptr' in ROM?". If pointer is in ROM, we'd like
|
||||
* to compress it quickly. Here we just scan a ~1K array
|
||||
* which is very bad for performance.
|
||||
*/
|
||||
const void * const * ptrs = duk_rom_compressed_pointers;
|
||||
while (*ptrs) {
|
||||
if (*ptrs == ptr) {
|
||||
return DUK_ALLOC_POOL_ROMPTR_FIRST + (duk_uint16_t) (ptrs - duk_rom_compressed_pointers);
|
||||
}
|
||||
ptrs++;
|
||||
}
|
||||
|
||||
/* We should really never be here: Duktape should only be
|
||||
* compressing pointers which are in the ROM compressed
|
||||
* pointers list, which are known at 'make dist' time.
|
||||
* We go on, causing a pointer compression error.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
#if !defined(DUK_ALLOC_POOL_H_INCLUDED)
|
||||
#define DUK_ALLOC_POOL_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* 32-bit (big endian) marker used at the end of pool entries so that wasted
|
||||
* space can be detected. Waste tracking must be enabled explicitly.
|
||||
*/
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
|
||||
#define DUK_ALLOC_POOL_WASTE_MARKER 0xedcb2345UL
|
||||
#endif
|
||||
|
||||
/* Pointer compression with ROM strings/objects:
|
||||
*
|
||||
* For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM
|
||||
* pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer
|
||||
* compression range minimum to avoid duplication in user code.
|
||||
*/
|
||||
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
|
||||
#define DUK_ALLOC_POOL_ROMPTR_COMPRESSION
|
||||
#define DUK_ALLOC_POOL_ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST
|
||||
|
||||
/* This extern declaration is provided by duktape.h, array provided by duktape.c.
|
||||
* Because duk_config.h may include this file (to get the inline functions) we
|
||||
* need to forward declare this also here.
|
||||
*/
|
||||
extern const void * const duk_rom_compressed_pointers[];
|
||||
#endif
|
||||
|
||||
/* Pool configuration for a certain block size. */
|
||||
typedef struct {
|
||||
unsigned int size; /* must be divisible by 4 and >= sizeof(void *) */
|
||||
unsigned int a; /* bytes (not count) to allocate: a*t + b, t is an arbitrary scale parameter */
|
||||
unsigned int b;
|
||||
} duk_pool_config;
|
||||
|
||||
/* Freelist entry, must fit into the smallest block size. */
|
||||
struct duk_pool_free;
|
||||
typedef struct duk_pool_free duk_pool_free;
|
||||
struct duk_pool_free {
|
||||
duk_pool_free *next;
|
||||
};
|
||||
|
||||
/* Pool state for a certain block size. */
|
||||
typedef struct {
|
||||
duk_pool_free *first;
|
||||
char *alloc_end;
|
||||
unsigned int size;
|
||||
unsigned int count;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
unsigned int hwm_used_count;
|
||||
#endif
|
||||
} duk_pool_state;
|
||||
|
||||
/* Statistics for a certain pool. */
|
||||
typedef struct {
|
||||
size_t used_count;
|
||||
size_t used_bytes;
|
||||
size_t free_count;
|
||||
size_t free_bytes;
|
||||
size_t waste_bytes;
|
||||
size_t hwm_used_count;
|
||||
} duk_pool_stats;
|
||||
|
||||
/* Top level state for all pools. Pointer to this struct is used as the allocator
|
||||
* userdata pointer.
|
||||
*/
|
||||
typedef struct {
|
||||
int num_pools;
|
||||
duk_pool_state *states;
|
||||
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
|
||||
size_t hwm_used_bytes;
|
||||
size_t hwm_waste_bytes;
|
||||
#endif
|
||||
} duk_pool_global;
|
||||
|
||||
/* Statistics for the entire set of pools. */
|
||||
typedef struct {
|
||||
size_t used_bytes;
|
||||
size_t free_bytes;
|
||||
size_t waste_bytes;
|
||||
size_t hwm_used_bytes;
|
||||
size_t hwm_waste_bytes;
|
||||
} duk_pool_global_stats;
|
||||
|
||||
/* Initialize a pool allocator, arguments:
|
||||
* - buffer and size: continuous region to use for pool, must align to 4
|
||||
* - config: configuration for pools in ascending block size
|
||||
* - state: state for pools, matches config order
|
||||
* - num_pools: number of entries in 'config' and 'state'
|
||||
* - global: global state structure
|
||||
*
|
||||
* The 'config', 'state', and 'global' pointers must be valid beyond the init
|
||||
* call, as long as the pool is used.
|
||||
*
|
||||
* Returns a void pointer to be used as userdata for the allocator functions.
|
||||
* Concretely the return value will be "(void *) global", i.e. the global
|
||||
* state struct. If pool init fails, the return value will be NULL.
|
||||
*/
|
||||
void *duk_alloc_pool_init(char *buffer,
|
||||
size_t size,
|
||||
const duk_pool_config *configs,
|
||||
duk_pool_state *states,
|
||||
int num_pools,
|
||||
duk_pool_global *global);
|
||||
|
||||
/* Duktape allocation providers. Typing matches Duktape requirements. */
|
||||
void *duk_alloc_pool(void *udata, duk_size_t size);
|
||||
void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size);
|
||||
void duk_free_pool(void *udata, void *ptr);
|
||||
|
||||
/* Stats. */
|
||||
void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res);
|
||||
void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res);
|
||||
|
||||
/* Duktape pointer compression global state (assumes single pool). */
|
||||
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
|
||||
extern const void *duk_alloc_pool_romptr_low;
|
||||
extern const void *duk_alloc_pool_romptr_high;
|
||||
duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr);
|
||||
#endif
|
||||
#if defined(DUK_USE_HEAPPTR16)
|
||||
extern void *duk_alloc_pool_ptrcomp_base;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
duk_uint16_t duk_alloc_pool_enc16(void *ptr);
|
||||
void *duk_alloc_pool_dec16(duk_uint16_t val);
|
||||
#endif
|
||||
|
||||
/* Inlined pointer compression functions. Gcc and clang -Os won't in
|
||||
* practice inline these without an "always inline" attribute because it's
|
||||
* more size efficient (by a few kB) to use explicit calls instead. Having
|
||||
* these defined inline here allows performance optimized builds to inline
|
||||
* pointer compression operations.
|
||||
*
|
||||
* Pointer compression assumes there's a single globally registered memory
|
||||
* pool which makes pointer compression more efficient. This would be easy
|
||||
* to fix by adding a userdata pointer to the compression functions and
|
||||
* plumbing the heap userdata from the compression/decompression macros.
|
||||
*/
|
||||
|
||||
/* DUK_ALWAYS_INLINE is not a public API symbol so it may go away in even a
|
||||
* minor update. But it's pragmatic for this extra because it handles many
|
||||
* compilers via duk_config.h detection. Check that the macro exists so that
|
||||
* if it's gone, we can still compile.
|
||||
*/
|
||||
#if defined(DUK_ALWAYS_INLINE)
|
||||
#define DUK__ALLOC_POOL_ALWAYS_INLINE DUK_ALWAYS_INLINE
|
||||
#else
|
||||
#define DUK__ALLOC_POOL_ALWAYS_INLINE /* nop */
|
||||
#endif
|
||||
|
||||
#if defined(DUK_USE_HEAPPTR16)
|
||||
static DUK__ALLOC_POOL_ALWAYS_INLINE duk_uint16_t duk_alloc_pool_enc16(void *ptr) {
|
||||
if (ptr == NULL) {
|
||||
/* With 'return 0' gcc and clang -Os generate inefficient code.
|
||||
* For example, gcc -Os generates:
|
||||
*
|
||||
* 0804911d <duk_alloc_pool_enc16>:
|
||||
* 804911d: 55 push %ebp
|
||||
* 804911e: 85 c0 test %eax,%eax
|
||||
* 8049120: 89 e5 mov %esp,%ebp
|
||||
* 8049122: 74 0b je 804912f <duk_alloc_pool_enc16+0x12>
|
||||
* 8049124: 2b 05 e4 90 07 08 sub 0x80790e4,%eax
|
||||
* 804912a: c1 e8 02 shr $0x2,%eax
|
||||
* 804912d: eb 02 jmp 8049131 <duk_alloc_pool_enc16+0x14>
|
||||
* 804912f: 31 c0 xor %eax,%eax
|
||||
* 8049131: 5d pop %ebp
|
||||
* 8049132: c3 ret
|
||||
*
|
||||
* The NULL path checks %eax for zero; if it is zero, a zero
|
||||
* is unnecessarily loaded into %eax again. The non-zero path
|
||||
* has an unnecessary jump as a side effect of this.
|
||||
*
|
||||
* Using 'return (duk_uint16_t) (intptr_t) ptr;' generates similarly
|
||||
* inefficient code; not sure how to make the result better.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
if (ptr >= duk_alloc_pool_romptr_low && ptr <= duk_alloc_pool_romptr_high) {
|
||||
/* This is complex enough now to need a separate function. */
|
||||
return duk_alloc_pool_enc16_rom(ptr);
|
||||
}
|
||||
#endif
|
||||
return (duk_uint16_t) (((size_t) ((char *) ptr - (char *) duk_alloc_pool_ptrcomp_base)) >> 2);
|
||||
}
|
||||
|
||||
static DUK__ALLOC_POOL_ALWAYS_INLINE void *duk_alloc_pool_dec16(duk_uint16_t val) {
|
||||
if (val == 0) {
|
||||
/* As with enc16 the gcc and clang -Os output is inefficient,
|
||||
* e.g. gcc -Os:
|
||||
*
|
||||
* 08049133 <duk_alloc_pool_dec16>:
|
||||
* 8049133: 55 push %ebp
|
||||
* 8049134: 66 85 c0 test %ax,%ax
|
||||
* 8049137: 89 e5 mov %esp,%ebp
|
||||
* 8049139: 74 0e je 8049149 <duk_alloc_pool_dec16+0x16>
|
||||
* 804913b: 8b 15 e4 90 07 08 mov 0x80790e4,%edx
|
||||
* 8049141: 0f b7 c0 movzwl %ax,%eax
|
||||
* 8049144: 8d 04 82 lea (%edx,%eax,4),%eax
|
||||
* 8049147: eb 02 jmp 804914b <duk_alloc_pool_dec16+0x18>
|
||||
* 8049149: 31 c0 xor %eax,%eax
|
||||
* 804914b: 5d pop %ebp
|
||||
* 804914c: c3 ret
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
|
||||
if (val >= DUK_ALLOC_POOL_ROMPTR_FIRST) {
|
||||
/* This is a blind lookup, could check index validity.
|
||||
* Duktape should never decompress a pointer which would
|
||||
* be out-of-bounds here.
|
||||
*/
|
||||
return (void *) (intptr_t) (duk_rom_compressed_pointers[val - DUK_ALLOC_POOL_ROMPTR_FIRST]);
|
||||
}
|
||||
#endif
|
||||
return (void *) ((char *) duk_alloc_pool_ptrcomp_base + (((size_t) val) << 2));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DUK_ALLOC_POOL_H_INCLUDED */
|
||||
+3672
File diff suppressed because it is too large.
Load diff
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Minimal 'console' binding.
|
||||
*
|
||||
* https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
|
||||
* https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
|
||||
* https://developer.mozilla.org/en/docs/Web/API/console
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "duktape.h"
|
||||
#include "duk_console.h"
|
||||
|
||||
/* XXX: Add some form of log level filtering. */
|
||||
|
||||
/* XXX: For now logs everything to stdout, V8/Node.js logs debug/info level
|
||||
* to stdout, warn and above to stderr. Should this extra do the same?
|
||||
*/
|
||||
|
||||
/* XXX: Should all output be written via e.g. console.write(formattedMsg)?
|
||||
* This would make it easier for user code to redirect all console output
|
||||
* to a custom backend.
|
||||
*/
|
||||
|
||||
/* XXX: Init console object using duk_def_prop() when that call is available. */
|
||||
|
||||
static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
|
||||
duk_idx_t i, n;
|
||||
duk_uint_t flags;
|
||||
|
||||
flags = (duk_uint_t) duk_get_current_magic(ctx);
|
||||
|
||||
n = duk_get_top(ctx);
|
||||
|
||||
duk_get_global_string(ctx, "console");
|
||||
duk_get_prop_string(ctx, -1, "format");
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
|
||||
/* Slow path formatting. */
|
||||
duk_dup(ctx, -1); /* console.format */
|
||||
duk_dup(ctx, i);
|
||||
duk_call(ctx, 1);
|
||||
duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
|
||||
}
|
||||
}
|
||||
|
||||
duk_pop_2(ctx);
|
||||
|
||||
duk_push_string(ctx, " ");
|
||||
duk_insert(ctx, 0);
|
||||
duk_join(ctx, n);
|
||||
|
||||
if (error_name) {
|
||||
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, error_name);
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
|
||||
duk_get_prop_string(ctx, -1, "stack");
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\n", duk_to_string(ctx, -1));
|
||||
if (flags & DUK_CONSOLE_FLUSH) {
|
||||
fflush(stdout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_assert(duk_context *ctx) {
|
||||
if (duk_to_boolean(ctx, 0)) {
|
||||
return 0;
|
||||
}
|
||||
duk_remove(ctx, 0);
|
||||
|
||||
return duk__console_log_helper(ctx, "AssertionError");
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_log(duk_context *ctx) {
|
||||
return duk__console_log_helper(ctx, NULL);
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_trace(duk_context *ctx) {
|
||||
return duk__console_log_helper(ctx, "Trace");
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_info(duk_context *ctx) {
|
||||
return duk__console_log_helper(ctx, NULL);
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_warn(duk_context *ctx) {
|
||||
return duk__console_log_helper(ctx, NULL);
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_error(duk_context *ctx) {
|
||||
return duk__console_log_helper(ctx, "Error");
|
||||
}
|
||||
|
||||
static duk_ret_t duk__console_dir(duk_context *ctx) {
|
||||
/* For now, just share the formatting of .log() */
|
||||
return duk__console_log_helper(ctx, 0);
|
||||
}
|
||||
|
||||
static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
|
||||
duk_push_c_function(ctx, func, DUK_VARARGS);
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, name);
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
|
||||
duk_set_magic(ctx, -1, (duk_int_t) flags);
|
||||
duk_put_prop_string(ctx, -2, name);
|
||||
}
|
||||
|
||||
void duk_console_init(duk_context *ctx, duk_uint_t flags) {
|
||||
duk_push_object(ctx);
|
||||
|
||||
/* Custom function to format objects; user can replace.
|
||||
* For now, try JX-formatting and if that fails, fall back
|
||||
* to ToString(v).
|
||||
*/
|
||||
duk_eval_string(ctx,
|
||||
"(function (E) {"
|
||||
"return function format(v){"
|
||||
"try{"
|
||||
"return E('jx',v);"
|
||||
"}catch(e){"
|
||||
"return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
|
||||
"}"
|
||||
"};"
|
||||
"})(Duktape.enc)");
|
||||
duk_put_prop_string(ctx, -2, "format");
|
||||
|
||||
duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
|
||||
duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
|
||||
duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
|
||||
duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
|
||||
|
||||
duk_put_global_string(ctx, "console");
|
||||
|
||||
/* Proxy wrapping: ensures any undefined console method calls are
|
||||
* ignored silently. This is required specifically by the
|
||||
* DeveloperToolsWG proposal (and is implemented also by Firefox:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=629607).
|
||||
*/
|
||||
|
||||
if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
|
||||
/* Tolerate errors: Proxy may be disabled. */
|
||||
duk_peval_string_noresult(ctx,
|
||||
"(function(){"
|
||||
"var D=function(){};"
|
||||
"console=new Proxy(console,{"
|
||||
"get:function(t,k){"
|
||||
"var v=t[k];"
|
||||
"return typeof v==='function'?v:D;"
|
||||
"}"
|
||||
"});"
|
||||
"})();"
|
||||
);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#if !defined(DUK_CONSOLE_H_INCLUDED)
|
||||
#define DUK_CONSOLE_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */
|
||||
#define DUK_CONSOLE_PROXY_WRAPPER (1 << 0)
|
||||
|
||||
/* Flush output after every call. */
|
||||
#define DUK_CONSOLE_FLUSH (1 << 1)
|
||||
|
||||
extern void duk_console_init(duk_context *ctx, duk_uint_t flags);
|
||||
|
||||
#endif /* DUK_CONSOLE_H_INCLUDED */
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Logging support
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "duktape.h"
|
||||
#include "duk_logging.h"
|
||||
|
||||
/* XXX: uses stderr always for now, configurable? */
|
||||
|
||||
#define DUK_LOGGING_FLUSH /* Duktape 1.x: flush stderr */
|
||||
|
||||
/* 3-letter log level strings. */
|
||||
static const char duk__log_level_strings[] = {
|
||||
'T', 'R', 'C', 'D', 'B', 'G', 'I', 'N', 'F',
|
||||
'W', 'R', 'N', 'E', 'R', 'R', 'F', 'T', 'L'
|
||||
};
|
||||
|
||||
/* Log method names. */
|
||||
static const char *duk__log_method_names[] = {
|
||||
"trace", "debug", "info", "warn", "error", "fatal"
|
||||
};
|
||||
|
||||
/* Constructor. */
|
||||
static duk_ret_t duk__logger_constructor(duk_context *ctx) {
|
||||
duk_idx_t nargs;
|
||||
|
||||
/* Calling as a non-constructor is not meaningful. */
|
||||
if (!duk_is_constructor_call(ctx)) {
|
||||
return DUK_RET_TYPE_ERROR;
|
||||
}
|
||||
|
||||
nargs = duk_get_top(ctx);
|
||||
duk_set_top(ctx, 1);
|
||||
|
||||
duk_push_this(ctx);
|
||||
|
||||
/* [ name this ] */
|
||||
|
||||
if (nargs == 0) {
|
||||
/* Automatic defaulting of logger name from caller. This
|
||||
* would work poorly with tail calls, but constructor calls
|
||||
* are currently never tail calls, so tail calls are not an
|
||||
* issue now.
|
||||
*/
|
||||
|
||||
duk_inspect_callstack_entry(ctx, -2);
|
||||
if (duk_is_object(ctx, -1)) {
|
||||
if (duk_get_prop_string(ctx, -1, "function")) {
|
||||
if (duk_get_prop_string(ctx, -1, "fileName")) {
|
||||
if (duk_is_string(ctx, -1)) {
|
||||
duk_replace(ctx, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Leave values on stack on purpose, ignored below. */
|
||||
|
||||
/* Stripping the filename might be a good idea
|
||||
* ("/foo/bar/quux.js" -> logger name "quux"),
|
||||
* but now used verbatim.
|
||||
*/
|
||||
}
|
||||
/* The stack is unbalanced here on purpose; we only rely on the
|
||||
* initial two values: [ name this ].
|
||||
*/
|
||||
|
||||
if (duk_is_string(ctx, 0)) {
|
||||
duk_dup(ctx, 0);
|
||||
duk_put_prop_string(ctx, 1, "n");
|
||||
} else {
|
||||
/* don't set 'n' at all, inherited value is used as name */
|
||||
}
|
||||
|
||||
duk_compact(ctx, 1);
|
||||
|
||||
return 0; /* keep default instance */
|
||||
}
|
||||
|
||||
/* Default function to format objects. Tries to use toLogString() but falls
|
||||
* back to toString(). Any errors are propagated out without catching.
|
||||
*/
|
||||
static duk_ret_t duk__logger_prototype_fmt(duk_context *ctx) {
|
||||
if (duk_get_prop_string(ctx, 0, "toLogString")) {
|
||||
/* [ arg toLogString ] */
|
||||
|
||||
duk_dup(ctx, 0);
|
||||
duk_call_method(ctx, 0);
|
||||
|
||||
/* [ arg result ] */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* [ arg undefined ] */
|
||||
duk_pop(ctx);
|
||||
duk_to_string(ctx, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Default function to write a formatted log line. Writes to stderr,
|
||||
* appending a newline to the log line.
|
||||
*
|
||||
* The argument is a buffer; avoid coercing the buffer to a string to
|
||||
* avoid string table traffic.
|
||||
*/
|
||||
static duk_ret_t duk__logger_prototype_raw(duk_context *ctx) {
|
||||
const char *data;
|
||||
duk_size_t data_len;
|
||||
|
||||
data = (const char *) duk_require_buffer(ctx, 0, &data_len);
|
||||
fwrite((const void *) data, 1, data_len, stderr);
|
||||
fputc((int) '\n', stderr);
|
||||
#if defined(DUK_LOGGING_FLUSH)
|
||||
fflush(stderr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Log frontend shared helper, magic value indicates log level. Provides
|
||||
* frontend functions: trace(), debug(), info(), warn(), error(), fatal().
|
||||
* This needs to have small footprint, reasonable performance, minimal
|
||||
* memory churn, etc.
|
||||
*/
|
||||
static duk_ret_t duk__logger_prototype_log_shared(duk_context *ctx) {
|
||||
duk_double_t now;
|
||||
duk_time_components comp;
|
||||
duk_small_int_t entry_lev;
|
||||
duk_small_int_t logger_lev;
|
||||
duk_int_t nargs;
|
||||
duk_int_t i;
|
||||
duk_size_t tot_len;
|
||||
const duk_uint8_t *arg_str;
|
||||
duk_size_t arg_len;
|
||||
duk_uint8_t *buf, *p;
|
||||
const duk_uint8_t *q;
|
||||
duk_uint8_t date_buf[32]; /* maximum format length is 24+1 (NUL), round up. */
|
||||
duk_size_t date_len;
|
||||
duk_small_int_t rc;
|
||||
|
||||
/* XXX: sanitize to printable (and maybe ASCII) */
|
||||
/* XXX: better multiline */
|
||||
|
||||
/*
|
||||
* Logger arguments are:
|
||||
*
|
||||
* magic: log level (0-5)
|
||||
* this: logger
|
||||
* stack: plain log args
|
||||
*
|
||||
* We want to minimize memory churn so a two-pass approach
|
||||
* is used: first pass formats arguments and computes final
|
||||
* string length, second pass copies strings into a buffer
|
||||
* allocated directly with the correct size. If the backend
|
||||
* function plays nice, it won't coerce the buffer to a string
|
||||
* (and thus intern it).
|
||||
*/
|
||||
|
||||
entry_lev = duk_get_current_magic(ctx);
|
||||
if (entry_lev < DUK_LOG_TRACE || entry_lev > DUK_LOG_FATAL) {
|
||||
/* Should never happen, check just in case. */
|
||||
return 0;
|
||||
}
|
||||
nargs = duk_get_top(ctx);
|
||||
|
||||
/* [ arg1 ... argN this ] */
|
||||
|
||||
/*
|
||||
* Log level check
|
||||
*/
|
||||
|
||||
duk_push_this(ctx);
|
||||
|
||||
duk_get_prop_string(ctx, -1, "l");
|
||||
logger_lev = (duk_small_int_t) duk_get_int(ctx, -1);
|
||||
if (entry_lev < logger_lev) {
|
||||
return 0;
|
||||
}
|
||||
/* log level could be popped but that's not necessary */
|
||||
|
||||
now = duk_get_now(ctx);
|
||||
duk_time_to_components(ctx, now, &comp);
|
||||
sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
|
||||
(int) comp.year, (int) comp.month + 1, (int) comp.day,
|
||||
(int) comp.hours, (int) comp.minutes, (int) comp.seconds,
|
||||
(int) comp.milliseconds);
|
||||
|
||||
date_len = strlen((const char *) date_buf);
|
||||
|
||||
duk_get_prop_string(ctx, -2, "n");
|
||||
duk_to_string(ctx, -1);
|
||||
|
||||
/* [ arg1 ... argN this loggerLevel loggerName ] */
|
||||
|
||||
/*
|
||||
* Pass 1
|
||||
*/
|
||||
|
||||
/* Line format: <time> <entryLev> <loggerName>: <msg> */
|
||||
|
||||
tot_len = 0;
|
||||
tot_len += 3 + /* separators: space, space, colon */
|
||||
3 + /* level string */
|
||||
date_len + /* time */
|
||||
duk_get_length(ctx, -1); /* loggerName */
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
/* When formatting an argument to a string, errors may happen from multiple
|
||||
* causes. In general we want to catch obvious errors like a toLogString()
|
||||
* throwing an error, but we don't currently try to catch every possible
|
||||
* error. In particular, internal errors (like out of memory or stack) are
|
||||
* not caught. Also, we expect Error toString() to not throw an error.
|
||||
*/
|
||||
if (duk_is_object(ctx, i)) {
|
||||
/* duk_pcall_prop() may itself throw an error, but we're content
|
||||
* in catching the obvious errors (like toLogString() throwing an
|
||||
* error).
|
||||
*/
|
||||
duk_push_string(ctx, "fmt");
|
||||
duk_dup(ctx, i);
|
||||
/* [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ] */
|
||||
/* call: this.fmt(arg) */
|
||||
rc = duk_pcall_prop(ctx, -5 /*obj_index*/, 1 /*nargs*/);
|
||||
if (rc) {
|
||||
/* Keep the error as the result (coercing it might fail below,
|
||||
* but we don't catch that now).
|
||||
*/
|
||||
;
|
||||
}
|
||||
duk_replace(ctx, i);
|
||||
}
|
||||
(void) duk_to_lstring(ctx, i, &arg_len);
|
||||
tot_len++; /* sep (even before first one) */
|
||||
tot_len += arg_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass 2
|
||||
*/
|
||||
|
||||
/* XXX: Here it'd be nice if we didn't need to allocate a new fixed
|
||||
* buffer for every write. This would be possible if raw() took a
|
||||
* buffer and a length. We could then use a preallocated buffer for
|
||||
* most log writes and request raw() to write a partial buffer.
|
||||
*/
|
||||
|
||||
buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, tot_len);
|
||||
p = buf;
|
||||
|
||||
memcpy((void *) p, (const void *) date_buf, (size_t) date_len);
|
||||
p += date_len;
|
||||
*p++ = (duk_uint8_t) ' ';
|
||||
|
||||
q = (const duk_uint8_t *) duk__log_level_strings + (entry_lev * 3);
|
||||
memcpy((void *) p, (const void *) q, (size_t) 3);
|
||||
p += 3;
|
||||
|
||||
*p++ = (duk_uint8_t) ' ';
|
||||
|
||||
arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, -2, &arg_len);
|
||||
memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
|
||||
p += arg_len;
|
||||
|
||||
*p++ = (duk_uint8_t) ':';
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
*p++ = (duk_uint8_t) ' ';
|
||||
|
||||
arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &arg_len);
|
||||
memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
|
||||
p += arg_len;
|
||||
}
|
||||
|
||||
/* [ arg1 ... argN this loggerLevel loggerName buffer ] */
|
||||
|
||||
/* Call this.raw(msg); look up through the instance allows user to override
|
||||
* the raw() function in the instance or in the prototype for maximum
|
||||
* flexibility.
|
||||
*/
|
||||
duk_push_string(ctx, "raw");
|
||||
duk_dup(ctx, -2);
|
||||
/* [ arg1 ... argN this loggerLevel loggerName buffer 'raw' buffer ] */
|
||||
duk_call_prop(ctx, -6, 1); /* this.raw(buffer) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap) {
|
||||
if (level < 0) {
|
||||
level = 0;
|
||||
} else if (level > (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1) {
|
||||
level = (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1;
|
||||
}
|
||||
|
||||
duk_push_global_stash(ctx);
|
||||
duk_get_prop_string(ctx, -1, "\xff" "logger:constructor"); /* fixed at init time */
|
||||
duk_get_prop_string(ctx, -1, "clog");
|
||||
duk_get_prop_string(ctx, -1, duk__log_method_names[level]);
|
||||
duk_dup(ctx, -2);
|
||||
duk_push_vsprintf(ctx, fmt, ap);
|
||||
|
||||
/* [ ... stash Logger clog logfunc clog(=this) msg ] */
|
||||
|
||||
duk_call_method(ctx, 1 /*nargs*/);
|
||||
|
||||
/* [ ... stash Logger clog res ] */
|
||||
|
||||
duk_pop_n(ctx, 4);
|
||||
}
|
||||
|
||||
void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
duk_log_va(ctx, level, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void duk_logging_init(duk_context *ctx, duk_uint_t flags) {
|
||||
/* XXX: Add .name property for logger functions (useful for stack traces if they throw). */
|
||||
|
||||
(void) flags;
|
||||
|
||||
duk_eval_string(ctx,
|
||||
"(function(cons,prot){"
|
||||
"Object.defineProperty(Duktape,'Logger',{value:cons,writable:true,configurable:true});"
|
||||
"Object.defineProperty(cons,'prototype',{value:prot});"
|
||||
"Object.defineProperty(cons,'clog',{value:new Duktape.Logger('C'),writable:true,configurable:true});"
|
||||
"});");
|
||||
|
||||
duk_push_c_function(ctx, duk__logger_constructor, DUK_VARARGS /*nargs*/); /* Duktape.Logger */
|
||||
duk_push_object(ctx); /* Duktape.Logger.prototype */
|
||||
|
||||
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
|
||||
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, "Logger");
|
||||
duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
|
||||
|
||||
duk_dup_top(ctx);
|
||||
duk_put_prop_string(ctx, -2, "constructor");
|
||||
duk_push_int(ctx, 2);
|
||||
duk_put_prop_string(ctx, -2, "l");
|
||||
duk_push_string(ctx, "anon");
|
||||
duk_put_prop_string(ctx, -2, "n");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_fmt, 1 /*nargs*/);
|
||||
duk_put_prop_string(ctx, -2, "fmt");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_raw, 1 /*nargs*/);
|
||||
duk_put_prop_string(ctx, -2, "raw");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 0); /* magic=0: trace */
|
||||
duk_put_prop_string(ctx, -2, "trace");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 1); /* magic=1: debug */
|
||||
duk_put_prop_string(ctx, -2, "debug");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 2); /* magic=2: info */
|
||||
duk_put_prop_string(ctx, -2, "info");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 3); /* magic=3: warn */
|
||||
duk_put_prop_string(ctx, -2, "warn");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 4); /* magic=4: error */
|
||||
duk_put_prop_string(ctx, -2, "error");
|
||||
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
|
||||
duk_set_magic(ctx, -1, 5); /* magic=5: fatal */
|
||||
duk_put_prop_string(ctx, -2, "fatal");
|
||||
|
||||
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
|
||||
|
||||
/* XXX: when using ROM built-ins, "Duktape" is read-only by default so
|
||||
* setting Duktape.Logger will now fail.
|
||||
*/
|
||||
|
||||
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
|
||||
|
||||
duk_call(ctx, 2);
|
||||
duk_pop(ctx);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#if !defined(DUK_LOGGING_H_INCLUDED)
|
||||
#define DUK_LOGGING_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* Log levels */
|
||||
#define DUK_LOG_TRACE 0
|
||||
#define DUK_LOG_DEBUG 1
|
||||
#define DUK_LOG_INFO 2
|
||||
#define DUK_LOG_WARN 3
|
||||
#define DUK_LOG_ERROR 4
|
||||
#define DUK_LOG_FATAL 5
|
||||
|
||||
/* No flags at the moment. */
|
||||
|
||||
extern void duk_logging_init(duk_context *ctx, duk_uint_t flags);
|
||||
extern void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap);
|
||||
extern void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...);
|
||||
|
||||
#endif /* DUK_LOGGING_H_INCLUDED */
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Minimal vsnprintf(), snprintf(), sprintf(), and sscanf() for Duktape.
|
||||
* The supported conversion formats narrowly match what Duktape needs.
|
||||
*/
|
||||
|
||||
#include <stdarg.h> /* va_list etc */
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdint.h> /* SIZE_MAX */
|
||||
|
||||
/* Write character with bound checking. Offset 'off' is updated regardless
|
||||
* of whether an actual write is made. This is necessary to satisfy snprintf()
|
||||
* return value semantics.
|
||||
*/
|
||||
#define DUK__WRITE_CHAR(c) do { \
|
||||
if (off < size) { \
|
||||
str[off] = (char) c; \
|
||||
} \
|
||||
off++; \
|
||||
} while (0)
|
||||
|
||||
/* Digits up to radix 16. */
|
||||
static const char duk__format_digits[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
|
||||
/* Format an unsigned long with various options. An unsigned long is large
|
||||
* enough for formatting all supported types.
|
||||
*/
|
||||
static size_t duk__format_long(char *str,
|
||||
size_t size,
|
||||
size_t off,
|
||||
int fixed_length,
|
||||
char pad,
|
||||
int radix,
|
||||
int neg_sign,
|
||||
unsigned long v) {
|
||||
char buf[24]; /* 2^64 = 18446744073709552000, length 20 */
|
||||
char *required;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
/* Format in reverse order first. Ensure at least one digit is output
|
||||
* to handle '0' correctly. Note that space padding and zero padding
|
||||
* handle negative sign differently:
|
||||
*
|
||||
* %9d and -321 => ' -321'
|
||||
* %09d and -321 => '-00000321'
|
||||
*/
|
||||
|
||||
for (i = 0; i < (int) sizeof(buf); i++) {
|
||||
buf[i] = pad; /* compiles into memset() equivalent, avoid memset() dependency */
|
||||
}
|
||||
|
||||
p = buf;
|
||||
do {
|
||||
*p++ = duk__format_digits[v % radix];
|
||||
v /= radix;
|
||||
} while (v != 0);
|
||||
|
||||
required = buf + fixed_length;
|
||||
if (p < required && pad == (char) '0') {
|
||||
/* Zero padding and we didn't reach maximum length: place
|
||||
* negative sign at the last position. We can't get here
|
||||
* with fixed_length == 0 so that required[-1] is safe.
|
||||
*
|
||||
* Technically we should only do this for 'neg_sign == 1',
|
||||
* but it's OK to advance the pointer even when that's not
|
||||
* the case.
|
||||
*/
|
||||
p = required - 1;
|
||||
}
|
||||
if (neg_sign) {
|
||||
*p++ = (char) '-';
|
||||
}
|
||||
if (p < required) {
|
||||
p = required;
|
||||
}
|
||||
|
||||
/* Now [buf,p[ contains the result in reverse; copy into place. */
|
||||
|
||||
while (p > buf) {
|
||||
p--;
|
||||
DUK__WRITE_CHAR(*p);
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
/* Parse a pointer. Must parse whatever is produced by '%p' in sprintf(). */
|
||||
static int duk__parse_pointer(const char *str, void **out) {
|
||||
const unsigned char *p;
|
||||
unsigned char ch;
|
||||
int count;
|
||||
int limit;
|
||||
long val; /* assume void * fits into long */
|
||||
|
||||
/* We only need to parse what our minimal printf() produces, so that
|
||||
* we can check for a '0x' prefix, and assume all hex digits are
|
||||
* lowercase.
|
||||
*/
|
||||
|
||||
p = (const unsigned char *) str;
|
||||
if (p[0] != (unsigned char) '0' || p[1] != (unsigned char) 'x') {
|
||||
return 0;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
for (val = 0, count = 0, limit = sizeof(void *) * 2; count < limit; count++) {
|
||||
ch = *p++;
|
||||
|
||||
val <<= 4;
|
||||
if (ch >= (unsigned char) '0' && ch <= (unsigned char) '9') {
|
||||
val += ch - (unsigned char) '0';
|
||||
} else if (ch >= (unsigned char) 'a' && ch <= (unsigned char) 'f') {
|
||||
val += ch - (unsigned char) 'a' + 0x0a;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The input may end at a NUL or garbage may follow. As long as we
|
||||
* parse the '%p' correctly, garbage is allowed to follow, and the
|
||||
* JX pointer parsing also relies on that.
|
||||
*/
|
||||
|
||||
*out = (void *) val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Minimal vsnprintf() entry point. */
|
||||
int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
||||
size_t off = 0;
|
||||
const char *p;
|
||||
#if 0
|
||||
const char *p_tmp;
|
||||
const char *p_fmt_start;
|
||||
#endif
|
||||
char c;
|
||||
char pad;
|
||||
int fixed_length;
|
||||
int is_long;
|
||||
|
||||
/* Assume str != NULL unless size == 0.
|
||||
* Assume format != NULL.
|
||||
*/
|
||||
|
||||
p = format;
|
||||
for (;;) {
|
||||
c = *p++;
|
||||
if (c == (char) 0) {
|
||||
break;
|
||||
}
|
||||
if (c != (char) '%') {
|
||||
DUK__WRITE_CHAR(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Start format sequence. Scan flags and format specifier. */
|
||||
|
||||
#if 0
|
||||
p_fmt_start = p - 1;
|
||||
#endif
|
||||
is_long = 0;
|
||||
pad = ' ';
|
||||
fixed_length = 0;
|
||||
for (;;) {
|
||||
c = *p++;
|
||||
if (c == (char) 'l') {
|
||||
is_long = 1;
|
||||
} else if (c == (char) '0') {
|
||||
/* Only support pad character '0'. */
|
||||
pad = '0';
|
||||
} else if (c >= (char) '1' && c <= (char) '9') {
|
||||
/* Only support fixed lengths 1-9. */
|
||||
fixed_length = (int) (c - (char) '0');
|
||||
} else if (c == (char) 'd') {
|
||||
long v;
|
||||
int neg_sign = 0;
|
||||
if (is_long) {
|
||||
v = va_arg(ap, long);
|
||||
} else {
|
||||
v = (long) va_arg(ap, int);
|
||||
}
|
||||
if (v < 0) {
|
||||
neg_sign = 1;
|
||||
v = -v;
|
||||
}
|
||||
off = duk__format_long(str, size, off, fixed_length, pad, 10, neg_sign, (unsigned long) v);
|
||||
break;
|
||||
} else if (c == (char) 'u') {
|
||||
unsigned long v;
|
||||
if (is_long) {
|
||||
v = va_arg(ap, unsigned long);
|
||||
} else {
|
||||
v = (unsigned long) va_arg(ap, unsigned int);
|
||||
}
|
||||
off = duk__format_long(str, size, off, fixed_length, pad, 10, 0, v);
|
||||
break;
|
||||
} else if (c == (char) 'x') {
|
||||
unsigned long v;
|
||||
if (is_long) {
|
||||
v = va_arg(ap, unsigned long);
|
||||
} else {
|
||||
v = (unsigned long) va_arg(ap, unsigned int);
|
||||
}
|
||||
off = duk__format_long(str, size, off, fixed_length, pad, 16, 0, v);
|
||||
break;
|
||||
} else if (c == (char) 'c') {
|
||||
char v;
|
||||
v = (char) va_arg(ap, int); /* intentionally not 'char' */
|
||||
DUK__WRITE_CHAR(v);
|
||||
break;
|
||||
} else if (c == (char) 's') {
|
||||
const char *v;
|
||||
char c_tmp;
|
||||
v = va_arg(ap, const char *);
|
||||
if (v) {
|
||||
for (;;) {
|
||||
c_tmp = *v++;
|
||||
if (c_tmp) {
|
||||
DUK__WRITE_CHAR(c_tmp);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if (c == (char) 'p') {
|
||||
/* Assume a void * can be represented by 'long'. This is not
|
||||
* always the case. NULL pointer is printed out as 0x0000...
|
||||
*/
|
||||
void *v;
|
||||
v = va_arg(ap, void *);
|
||||
DUK__WRITE_CHAR('0');
|
||||
DUK__WRITE_CHAR('x');
|
||||
off = duk__format_long(str, size, off, sizeof(void *) * 2, '0', 16, 0, (unsigned long) v);
|
||||
break;
|
||||
} else {
|
||||
/* Unrecognized, bail out early. We could also emit the format
|
||||
* specifier verbatim, but it'd be a waste of footprint because
|
||||
* this case should never happen in practice.
|
||||
*/
|
||||
#if 0
|
||||
DUK__WRITE_CHAR('!');
|
||||
#endif
|
||||
#if 0
|
||||
for (p_tmp = p_fmt_start; p_tmp != p; p_tmp++) {
|
||||
DUK__WRITE_CHAR(*p_tmp);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (off < size) {
|
||||
str[off] = (char) 0; /* No increment for 'off', not counted in return value. */
|
||||
} else if (size > 0) {
|
||||
/* Forced termination. */
|
||||
str[size - 1] = 0;
|
||||
}
|
||||
|
||||
return (int) off;
|
||||
}
|
||||
|
||||
/* Minimal snprintf() entry point. */
|
||||
int duk_minimal_snprintf(char *str, size_t size, const char *format, ...) {
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = duk_minimal_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Minimal sprintf() entry point. */
|
||||
int duk_minimal_sprintf(char *str, const char *format, ...) {
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = duk_minimal_vsnprintf(str, SIZE_MAX, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Minimal sscanf() entry point. */
|
||||
int duk_minimal_sscanf(const char *str, const char *format, ...) {
|
||||
va_list ap;
|
||||
int ret;
|
||||
void **out;
|
||||
|
||||
/* Only the exact "%p" format is supported. */
|
||||
if (format[0] != (char) '%' ||
|
||||
format[1] != (char) 'p' ||
|
||||
format[2] != (char) 0) {
|
||||
}
|
||||
|
||||
va_start(ap, format);
|
||||
out = va_arg(ap, void **);
|
||||
ret = duk__parse_pointer(str, out);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef DUK__WRITE_CHAR
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#if !defined(DUK_MINIMAL_PRINTF_H_INCLUDED)
|
||||
#define DUK_MINIMAL_PRINTF_H_INCLUDED
|
||||
|
||||
#include <stdarg.h> /* va_list etc */
|
||||
#include <stddef.h> /* size_t */
|
||||
|
||||
extern int duk_minimal_sprintf(char *str, const char *format, ...);
|
||||
extern int duk_minimal_snprintf(char *str, size_t size, const char *format, ...);
|
||||
extern int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||
extern int duk_minimal_sscanf(const char *str, const char *format, ...);
|
||||
|
||||
#endif /* DUK_MINIMAL_PRINTF_H_INCLUDED */
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Duktape 1.x compatible module loading framework
|
||||
*/
|
||||
|
||||
#include "duktape.h"
|
||||
#include "duk_module_duktape.h"
|
||||
|
||||
/* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does
|
||||
* NOT NUL terminate on truncation, but that's OK here.
|
||||
* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
|
||||
*/
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#if 0 /* Enable manually */
|
||||
#define DUK__ASSERT(x) do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \
|
||||
fflush(stderr); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DUK__ASSERT_TOP(ctx,val) do { \
|
||||
DUK__ASSERT(duk_get_top((ctx)) == (val)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DUK__ASSERT(x) do { (void) (x); } while (0)
|
||||
#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0)
|
||||
#endif
|
||||
|
||||
static void duk__resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) {
|
||||
duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT];
|
||||
duk_uint8_t *p;
|
||||
duk_uint8_t *q;
|
||||
duk_uint8_t *q_last; /* last component */
|
||||
duk_int_t int_rc;
|
||||
|
||||
DUK__ASSERT(req_id != NULL);
|
||||
/* mod_id may be NULL */
|
||||
|
||||
/*
|
||||
* A few notes on the algorithm:
|
||||
*
|
||||
* - Terms are not allowed to begin with a period unless the term
|
||||
* is either '.' or '..'. This simplifies implementation (and
|
||||
* is within CommonJS modules specification).
|
||||
*
|
||||
* - There are few output bound checks here. This is on purpose:
|
||||
* the resolution input is length checked and the output is never
|
||||
* longer than the input. The resolved output is written directly
|
||||
* over the input because it's never longer than the input at any
|
||||
* point in the algorithm.
|
||||
*
|
||||
* - Non-ASCII characters are processed as individual bytes and
|
||||
* need no special treatment. However, U+0000 terminates the
|
||||
* algorithm; this is not an issue because U+0000 is not a
|
||||
* desirable term character anyway.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set up the resolution input which is the requested ID directly
|
||||
* (if absolute or no current module path) or with current module
|
||||
* ID prepended (if relative and current module path exists).
|
||||
*
|
||||
* Suppose current module is 'foo/bar' and relative path is './quux'.
|
||||
* The 'bar' component must be replaced so the initial input here is
|
||||
* 'foo/bar/.././quux'.
|
||||
*/
|
||||
|
||||
if (mod_id != NULL && req_id[0] == '.') {
|
||||
int_rc = snprintf((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id);
|
||||
} else {
|
||||
int_rc = snprintf((char *) buf, sizeof(buf), "%s", req_id);
|
||||
}
|
||||
if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) {
|
||||
/* Potentially truncated, NUL not guaranteed in any case.
|
||||
* The (int_rc < 0) case should not occur in practice.
|
||||
*/
|
||||
goto resolve_error;
|
||||
}
|
||||
DUK__ASSERT(strlen((const char *) buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */
|
||||
|
||||
/*
|
||||
* Resolution loop. At the top of the loop we're expecting a valid
|
||||
* term: '.', '..', or a non-empty identifier not starting with a period.
|
||||
*/
|
||||
|
||||
p = buf;
|
||||
q = buf;
|
||||
for (;;) {
|
||||
duk_uint_fast8_t c;
|
||||
|
||||
/* Here 'p' always points to the start of a term.
|
||||
*
|
||||
* We can also unconditionally reset q_last here: if this is
|
||||
* the last (non-empty) term q_last will have the right value
|
||||
* on loop exit.
|
||||
*/
|
||||
|
||||
DUK__ASSERT(p >= q); /* output is never longer than input during resolution */
|
||||
|
||||
q_last = q;
|
||||
|
||||
c = *p++;
|
||||
if (c == 0) {
|
||||
goto resolve_error;
|
||||
} else if (c == '.') {
|
||||
c = *p++;
|
||||
if (c == '/') {
|
||||
/* Term was '.' and is eaten entirely (including dup slashes). */
|
||||
goto eat_dup_slashes;
|
||||
}
|
||||
if (c == '.' && *p == '/') {
|
||||
/* Term was '..', backtrack resolved name by one component.
|
||||
* q[-1] = previous slash (or beyond start of buffer)
|
||||
* q[-2] = last char of previous component (or beyond start of buffer)
|
||||
*/
|
||||
p++; /* eat (first) input slash */
|
||||
DUK__ASSERT(q >= buf);
|
||||
if (q == buf) {
|
||||
goto resolve_error;
|
||||
}
|
||||
DUK__ASSERT(*(q - 1) == '/');
|
||||
q--; /* Backtrack to last output slash (dups already eliminated). */
|
||||
for (;;) {
|
||||
/* Backtrack to previous slash or start of buffer. */
|
||||
DUK__ASSERT(q >= buf);
|
||||
if (q == buf) {
|
||||
break;
|
||||
}
|
||||
if (*(q - 1) == '/') {
|
||||
break;
|
||||
}
|
||||
q--;
|
||||
}
|
||||
goto eat_dup_slashes;
|
||||
}
|
||||
goto resolve_error;
|
||||
} else if (c == '/') {
|
||||
/* e.g. require('/foo'), empty terms not allowed */
|
||||
goto resolve_error;
|
||||
} else {
|
||||
for (;;) {
|
||||
/* Copy term name until end or '/'. */
|
||||
*q++ = c;
|
||||
c = *p++;
|
||||
if (c == 0) {
|
||||
/* This was the last term, and q_last was
|
||||
* updated to match this term at loop top.
|
||||
*/
|
||||
goto loop_done;
|
||||
} else if (c == '/') {
|
||||
*q++ = '/';
|
||||
break;
|
||||
} else {
|
||||
/* write on next loop */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eat_dup_slashes:
|
||||
for (;;) {
|
||||
/* eat dup slashes */
|
||||
c = *p;
|
||||
if (c != '/') {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
loop_done:
|
||||
/* Output #1: resolved absolute name. */
|
||||
DUK__ASSERT(q >= buf);
|
||||
duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf));
|
||||
|
||||
/* Output #2: last component name. */
|
||||
DUK__ASSERT(q >= q_last);
|
||||
DUK__ASSERT(q_last >= buf);
|
||||
duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last));
|
||||
return;
|
||||
|
||||
resolve_error:
|
||||
(void) duk_type_error(ctx, "cannot resolve module id: %s", (const char *) req_id);
|
||||
}
|
||||
|
||||
/* Stack indices for better readability. */
|
||||
#define DUK__IDX_REQUESTED_ID 0 /* module id requested */
|
||||
#define DUK__IDX_REQUIRE 1 /* current require() function */
|
||||
#define DUK__IDX_REQUIRE_ID 2 /* the base ID of the current require() function, resolution base */
|
||||
#define DUK__IDX_RESOLVED_ID 3 /* resolved, normalized absolute module ID */
|
||||
#define DUK__IDX_LASTCOMP 4 /* last component name in resolved path */
|
||||
#define DUK__IDX_DUKTAPE 5 /* Duktape object */
|
||||
#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */
|
||||
#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */
|
||||
#define DUK__IDX_FRESH_REQUIRE 8 /* new require() function for module, updated resolution base */
|
||||
#define DUK__IDX_EXPORTS 9 /* default exports table */
|
||||
#define DUK__IDX_MODULE 10 /* module object containing module.exports, etc */
|
||||
|
||||
static duk_ret_t duk__require(duk_context *ctx) {
|
||||
const char *str_req_id; /* requested identifier */
|
||||
const char *str_mod_id; /* require.id of current module */
|
||||
duk_int_t pcall_rc;
|
||||
|
||||
/* NOTE: we try to minimize code size by avoiding unnecessary pops,
|
||||
* so the stack looks a bit cluttered in this function. DUK__ASSERT_TOP()
|
||||
* assertions are used to ensure stack configuration is correct at each
|
||||
* step.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Resolve module identifier into canonical absolute form.
|
||||
*/
|
||||
|
||||
str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID);
|
||||
duk_push_current_function(ctx);
|
||||
duk_get_prop_string(ctx, -1, "id");
|
||||
str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */
|
||||
duk__resolve_module_id(ctx, str_req_id, str_mod_id);
|
||||
str_req_id = NULL;
|
||||
str_mod_id = NULL;
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp ] */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1);
|
||||
|
||||
/*
|
||||
* Cached module check.
|
||||
*
|
||||
* If module has been loaded or its loading has already begun without
|
||||
* finishing, return the same cached value (module.exports). The
|
||||
* value is registered when module load starts so that circular
|
||||
* references can be supported to some extent.
|
||||
*/
|
||||
|
||||
duk_push_global_stash(ctx);
|
||||
duk_get_prop_string(ctx, -1, "\xff" "module:Duktape");
|
||||
duk_remove(ctx, -2); /* Lookup stashed, original 'Duktape' object. */
|
||||
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded"); /* Duktape.modLoaded */
|
||||
duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT);
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1);
|
||||
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) {
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
|
||||
duk_get_prop_string(ctx, -1, "exports"); /* return module.exports */
|
||||
return 1;
|
||||
}
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1);
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */
|
||||
|
||||
/*
|
||||
* Module not loaded (and loading not started previously).
|
||||
*
|
||||
* Create a new require() function with 'id' set to resolved ID
|
||||
* of module being loaded. Also create 'exports' and 'module'
|
||||
* tables but don't register exports to the loaded table yet.
|
||||
* We don't want to do that unless the user module search callbacks
|
||||
* succeeds in finding the module.
|
||||
*/
|
||||
|
||||
/* Fresh require: require.id is left configurable (but not writable)
|
||||
* so that is not easy to accidentally tweak it, but it can still be
|
||||
* done with Object.defineProperty().
|
||||
*
|
||||
* XXX: require.id could also be just made non-configurable, as there
|
||||
* is no practical reason to touch it (at least from Ecmascript code).
|
||||
*/
|
||||
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, "require");
|
||||
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE); /* not writable, not enumerable, not configurable */
|
||||
duk_push_string(ctx, "id");
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE); /* a fresh require() with require.id = resolved target module id */
|
||||
|
||||
/* Module table:
|
||||
* - module.exports: initial exports table (may be replaced by user)
|
||||
* - module.id is non-writable and non-configurable, as the CommonJS
|
||||
* spec suggests this if possible
|
||||
* - module.filename: not set, defaults to resolved ID if not explicitly
|
||||
* set by modSearch() (note capitalization, not .fileName, matches Node.js)
|
||||
* - module.name: not set, defaults to last component of resolved ID if
|
||||
* not explicitly set by modSearch()
|
||||
*/
|
||||
duk_push_object(ctx); /* exports */
|
||||
duk_push_object(ctx); /* module */
|
||||
duk_push_string(ctx, "exports");
|
||||
duk_dup(ctx, DUK__IDX_EXPORTS);
|
||||
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); /* module.exports = exports */
|
||||
duk_push_string(ctx, "id");
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */
|
||||
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE); /* module.id = resolved_id; not writable, not enumerable, not configurable */
|
||||
duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 1);
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */
|
||||
|
||||
/* Register the module table early to modLoaded[] so that we can
|
||||
* support circular references even in modSearch(). If an error
|
||||
* is thrown, we'll delete the reference.
|
||||
*/
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
duk_dup(ctx, DUK__IDX_MODULE);
|
||||
duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */
|
||||
|
||||
/*
|
||||
* Call user provided module search function and build the wrapped
|
||||
* module source code (if necessary). The module search function
|
||||
* can be used to implement pure Ecmacsript, pure C, and mixed
|
||||
* Ecmascript/C modules.
|
||||
*
|
||||
* The module search function can operate on the exports table directly
|
||||
* (e.g. DLL code can register values to it). It can also return a
|
||||
* string which is interpreted as module source code (if a non-string
|
||||
* is returned the module is assumed to be a pure C one). If a module
|
||||
* cannot be found, an error must be thrown by the user callback.
|
||||
*
|
||||
* Because Duktape.modLoaded[] already contains the module being
|
||||
* loaded, circular references for C modules should also work
|
||||
* (although expected to be quite rare).
|
||||
*/
|
||||
|
||||
duk_push_string(ctx, "(function(require,exports,module){");
|
||||
|
||||
/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
|
||||
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch"); /* Duktape.modSearch */
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);
|
||||
duk_dup(ctx, DUK__IDX_EXPORTS);
|
||||
duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */
|
||||
pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 3);
|
||||
|
||||
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||
/* Delete entry in Duktape.modLoaded[] and rethrow. */
|
||||
goto delete_rethrow;
|
||||
}
|
||||
|
||||
/* If user callback did not return source code, module loading
|
||||
* is finished (user callback initialized exports table directly).
|
||||
*/
|
||||
if (!duk_is_string(ctx, -1)) {
|
||||
/* User callback did not return source code, so module loading
|
||||
* is finished: just update modLoaded with final module.exports
|
||||
* and we're done.
|
||||
*/
|
||||
goto return_exports;
|
||||
}
|
||||
|
||||
/* Finish the wrapped module source. Force module.filename as the
|
||||
* function .fileName so it gets set for functions defined within a
|
||||
* module. This also ensures loggers created within the module get
|
||||
* the module ID (or overridden filename) as their default logger name.
|
||||
* (Note capitalization: .filename matches Node.js while .fileName is
|
||||
* used elsewhere in Duktape.)
|
||||
*/
|
||||
duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
|
||||
duk_concat(ctx, 3);
|
||||
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) {
|
||||
/* module.filename for .fileName, default to resolved ID if
|
||||
* not present.
|
||||
*/
|
||||
duk_pop(ctx);
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
}
|
||||
pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
|
||||
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||
goto delete_rethrow;
|
||||
}
|
||||
pcall_rc = duk_pcall(ctx, 0); /* -> eval'd function wrapper (not called yet) */
|
||||
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||
goto delete_rethrow;
|
||||
}
|
||||
|
||||
/* Module has now evaluated to a wrapped module function. Force its
|
||||
* .name to match module.name (defaults to last component of resolved
|
||||
* ID) so that it is shown in stack traces too. Note that we must not
|
||||
* introduce an actual name binding into the function scope (which is
|
||||
* usually the case with a named function) because it would affect the
|
||||
* scope seen by the module and shadow accesses to globals of the same name.
|
||||
* This is now done by compiling the function as anonymous and then forcing
|
||||
* its .name without setting a "has name binding" flag.
|
||||
*/
|
||||
|
||||
duk_push_string(ctx, "name");
|
||||
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) {
|
||||
/* module.name for .name, default to last component if
|
||||
* not present.
|
||||
*/
|
||||
duk_pop(ctx);
|
||||
duk_dup(ctx, DUK__IDX_LASTCOMP);
|
||||
}
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
|
||||
|
||||
/*
|
||||
* Call the wrapped module function.
|
||||
*
|
||||
* Use a protected call so that we can update Duktape.modLoaded[resolved_id]
|
||||
* even if the module throws an error.
|
||||
*/
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
|
||||
|
||||
duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */
|
||||
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */
|
||||
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports"); /* relookup exports from module.exports in case it was changed by modSearch */
|
||||
duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 6);
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
|
||||
|
||||
pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
|
||||
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||
/* Module loading failed. Node.js will forget the module
|
||||
* registration so that another require() will try to load
|
||||
* the module again. Mimic that behavior.
|
||||
*/
|
||||
goto delete_rethrow;
|
||||
}
|
||||
|
||||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
|
||||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
|
||||
|
||||
/* fall through */
|
||||
|
||||
return_exports:
|
||||
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports");
|
||||
duk_compact(ctx, -1); /* compact the exports table */
|
||||
return 1; /* return module.exports */
|
||||
|
||||
delete_rethrow:
|
||||
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||
duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */
|
||||
(void) duk_throw(ctx); /* rethrow original error */
|
||||
return 0; /* not reachable */
|
||||
}
|
||||
|
||||
void duk_module_duktape_init(duk_context *ctx) {
|
||||
/* Stash 'Duktape' in case it's modified. */
|
||||
duk_push_global_stash(ctx);
|
||||
duk_get_global_string(ctx, "Duktape");
|
||||
duk_put_prop_string(ctx, -2, "\xff" "module:Duktape");
|
||||
duk_pop(ctx);
|
||||
|
||||
/* Register `require` as a global function. */
|
||||
duk_eval_string(ctx,
|
||||
"(function(req){"
|
||||
"var D=Object.defineProperty;"
|
||||
"D(req,'name',{value:'require'});"
|
||||
"D(this,'require',{value:req,writable:true,configurable:true});"
|
||||
"D(Duktape,'modLoaded',{value:Object.create(null),writable:true,configurable:true});"
|
||||
"})");
|
||||
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
|
||||
duk_call(ctx, 1);
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
#undef DUK__ASSERT
|
||||
#undef DUK__ASSERT_TOP
|
||||
#undef DUK__IDX_REQUESTED_ID
|
||||
#undef DUK__IDX_REQUIRE
|
||||
#undef DUK__IDX_REQUIRE_ID
|
||||
#undef DUK__IDX_RESOLVED_ID
|
||||
#undef DUK__IDX_LASTCOMP
|
||||
#undef DUK__IDX_DUKTAPE
|
||||
#undef DUK__IDX_MODLOADED
|
||||
#undef DUK__IDX_UNDEFINED
|
||||
#undef DUK__IDX_FRESH_REQUIRE
|
||||
#undef DUK__IDX_EXPORTS
|
||||
#undef DUK__IDX_MODULE
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED)
|
||||
#define DUK_MODULE_DUKTAPE_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* Maximum length of CommonJS module identifier to resolve. Length includes
|
||||
* both current module ID, requested (possibly relative) module ID, and a
|
||||
* slash in between.
|
||||
*/
|
||||
#define DUK_COMMONJS_MODULE_ID_LIMIT 256
|
||||
|
||||
extern void duk_module_duktape_init(duk_context *ctx);
|
||||
|
||||
#endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Node.js-like module loading framework for Duktape
|
||||
*
|
||||
* https://nodejs.org/api/modules.html
|
||||
*/
|
||||
|
||||
#include "duktape.h"
|
||||
#include "duk_module_node.h"
|
||||
|
||||
#if DUK_VERSION >= 19999
|
||||
static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata);
|
||||
#else
|
||||
static duk_int_t duk__eval_module_source(duk_context *ctx);
|
||||
#endif
|
||||
static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main);
|
||||
|
||||
static duk_bool_t duk__get_cached_module(duk_context *ctx, const char *id) {
|
||||
duk_push_global_stash(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
|
||||
if (duk_get_prop_string(ctx, -1, id)) {
|
||||
duk_remove(ctx, -2);
|
||||
duk_remove(ctx, -2);
|
||||
return 1;
|
||||
} else {
|
||||
duk_pop_3(ctx);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Place a `module` object on the top of the value stack into the require cache
|
||||
* based on its `.id` property. As a convenience to the caller, leave the
|
||||
* object on top of the value stack afterwards.
|
||||
*/
|
||||
static void duk__put_cached_module(duk_context *ctx) {
|
||||
/* [ ... module ] */
|
||||
|
||||
duk_push_global_stash(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
|
||||
duk_dup(ctx, -3);
|
||||
|
||||
/* [ ... module stash req_cache module ] */
|
||||
|
||||
(void) duk_get_prop_string(ctx, -1, "id");
|
||||
duk_dup(ctx, -2);
|
||||
duk_put_prop(ctx, -4);
|
||||
|
||||
duk_pop_3(ctx); /* [ ... module ] */
|
||||
}
|
||||
|
||||
static void duk__del_cached_module(duk_context *ctx, const char *id) {
|
||||
duk_push_global_stash(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
|
||||
duk_del_prop_string(ctx, -1, id);
|
||||
duk_pop_2(ctx);
|
||||
}
|
||||
|
||||
static duk_ret_t duk__handle_require(duk_context *ctx) {
|
||||
/*
|
||||
* Value stack handling here is a bit sloppy but should be correct.
|
||||
* Call handling will clean up any extra garbage for us.
|
||||
*/
|
||||
|
||||
const char *id;
|
||||
const char *parent_id;
|
||||
duk_idx_t module_idx;
|
||||
duk_idx_t stash_idx;
|
||||
duk_int_t ret;
|
||||
|
||||
duk_push_global_stash(ctx);
|
||||
stash_idx = duk_normalize_index(ctx, -1);
|
||||
|
||||
duk_push_current_function(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "moduleId");
|
||||
parent_id = duk_require_string(ctx, -1);
|
||||
(void) parent_id; /* not used directly; suppress warning */
|
||||
|
||||
/* [ id stash require parent_id ] */
|
||||
|
||||
id = duk_require_string(ctx, 0);
|
||||
|
||||
(void) duk_get_prop_string(ctx, stash_idx, "\xff" "modResolve");
|
||||
duk_dup(ctx, 0); /* module ID */
|
||||
duk_dup(ctx, -3); /* parent ID */
|
||||
duk_call(ctx, 2);
|
||||
|
||||
/* [ ... stash ... resolved_id ] */
|
||||
|
||||
id = duk_require_string(ctx, -1);
|
||||
|
||||
if (duk__get_cached_module(ctx, id)) {
|
||||
goto have_module; /* use the cached module */
|
||||
}
|
||||
|
||||
duk__push_module_object(ctx, id, 0 /*main*/);
|
||||
duk__put_cached_module(ctx); /* module remains on stack */
|
||||
|
||||
/*
|
||||
* From here on out, we have to be careful not to throw. If it can't be
|
||||
* avoided, the error must be caught and the module removed from the
|
||||
* require cache before rethrowing. This allows the application to
|
||||
* reattempt loading the module.
|
||||
*/
|
||||
|
||||
module_idx = duk_normalize_index(ctx, -1);
|
||||
|
||||
/* [ ... stash ... resolved_id module ] */
|
||||
|
||||
(void) duk_get_prop_string(ctx, stash_idx, "\xff" "modLoad");
|
||||
duk_dup(ctx, -3); /* resolved ID */
|
||||
(void) duk_get_prop_string(ctx, module_idx, "exports");
|
||||
duk_dup(ctx, module_idx);
|
||||
ret = duk_pcall(ctx, 3);
|
||||
if (ret != DUK_EXEC_SUCCESS) {
|
||||
duk__del_cached_module(ctx, id);
|
||||
(void) duk_throw(ctx); /* rethrow */
|
||||
}
|
||||
|
||||
if (duk_is_string(ctx, -1)) {
|
||||
duk_int_t ret;
|
||||
|
||||
/* [ ... module source ] */
|
||||
|
||||
#if DUK_VERSION >= 19999
|
||||
ret = duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
|
||||
#else
|
||||
ret = duk_safe_call(ctx, duk__eval_module_source, 2, 1);
|
||||
#endif
|
||||
if (ret != DUK_EXEC_SUCCESS) {
|
||||
duk__del_cached_module(ctx, id);
|
||||
(void) duk_throw(ctx); /* rethrow */
|
||||
}
|
||||
} else if (duk_is_undefined(ctx, -1)) {
|
||||
duk_pop(ctx);
|
||||
} else {
|
||||
duk__del_cached_module(ctx, id);
|
||||
(void) duk_type_error(ctx, "invalid module load callback return value");
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
have_module:
|
||||
/* [ ... module ] */
|
||||
|
||||
(void) duk_get_prop_string(ctx, -1, "exports");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void duk__push_require_function(duk_context *ctx, const char *id) {
|
||||
duk_push_c_function(ctx, duk__handle_require, 1);
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, "require");
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
|
||||
duk_push_string(ctx, id);
|
||||
duk_put_prop_string(ctx, -2, "\xff" "moduleId");
|
||||
|
||||
/* require.cache */
|
||||
duk_push_global_stash(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
|
||||
duk_put_prop_string(ctx, -3, "cache");
|
||||
duk_pop(ctx);
|
||||
|
||||
/* require.main */
|
||||
duk_push_global_stash(ctx);
|
||||
(void) duk_get_prop_string(ctx, -1, "\xff" "mainModule");
|
||||
duk_put_prop_string(ctx, -3, "main");
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main) {
|
||||
duk_push_object(ctx);
|
||||
|
||||
/* Set this as the main module, if requested */
|
||||
if (main) {
|
||||
duk_push_global_stash(ctx);
|
||||
duk_dup(ctx, -2);
|
||||
duk_put_prop_string(ctx, -2, "\xff" "mainModule");
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
/* Node.js uses the canonicalized filename of a module for both module.id
|
||||
* and module.filename. We have no concept of a file system here, so just
|
||||
* use the module ID for both values.
|
||||
*/
|
||||
duk_push_string(ctx, id);
|
||||
duk_dup(ctx, -1);
|
||||
duk_put_prop_string(ctx, -3, "filename");
|
||||
duk_put_prop_string(ctx, -2, "id");
|
||||
|
||||
/* module.exports = {} */
|
||||
duk_push_object(ctx);
|
||||
duk_put_prop_string(ctx, -2, "exports");
|
||||
|
||||
/* module.loaded = false */
|
||||
duk_push_false(ctx);
|
||||
duk_put_prop_string(ctx, -2, "loaded");
|
||||
|
||||
/* module.require */
|
||||
duk__push_require_function(ctx, id);
|
||||
duk_put_prop_string(ctx, -2, "require");
|
||||
}
|
||||
|
||||
#if DUK_VERSION >= 19999
|
||||
static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata) {
|
||||
#else
|
||||
static duk_int_t duk__eval_module_source(duk_context *ctx) {
|
||||
#endif
|
||||
const char *src;
|
||||
|
||||
/*
|
||||
* Stack: [ ... module source ]
|
||||
*/
|
||||
|
||||
#if DUK_VERSION >= 19999
|
||||
(void) udata;
|
||||
#endif
|
||||
|
||||
/* Wrap the module code in a function expression. This is the simplest
|
||||
* way to implement CommonJS closure semantics and matches the behavior of
|
||||
* e.g. Node.js.
|
||||
*/
|
||||
duk_push_string(ctx, "(function(exports,require,module,__filename,__dirname){");
|
||||
src = duk_require_string(ctx, -2);
|
||||
duk_push_string(ctx, (src[0] == '#' && src[1] == '!') ? "//" : ""); /* Shebang support. */
|
||||
duk_dup(ctx, -3); /* source */
|
||||
duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
|
||||
duk_concat(ctx, 4);
|
||||
|
||||
/* [ ... module source func_src ] */
|
||||
|
||||
(void) duk_get_prop_string(ctx, -3, "filename");
|
||||
duk_compile(ctx, DUK_COMPILE_EVAL);
|
||||
duk_call(ctx, 0);
|
||||
|
||||
/* [ ... module source func ] */
|
||||
|
||||
/* Set name for the wrapper function. */
|
||||
duk_push_string(ctx, "name");
|
||||
duk_push_string(ctx, "main");
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
|
||||
|
||||
/* call the function wrapper */
|
||||
(void) duk_get_prop_string(ctx, -3, "exports"); /* exports */
|
||||
(void) duk_get_prop_string(ctx, -4, "require"); /* require */
|
||||
duk_dup(ctx, -5); /* module */
|
||||
(void) duk_get_prop_string(ctx, -6, "filename"); /* __filename */
|
||||
duk_push_undefined(ctx); /* __dirname */
|
||||
duk_call(ctx, 5);
|
||||
|
||||
/* [ ... module source result(ignore) ] */
|
||||
|
||||
/* module.loaded = true */
|
||||
duk_push_true(ctx);
|
||||
duk_put_prop_string(ctx, -4, "loaded");
|
||||
|
||||
/* [ ... module source retval ] */
|
||||
|
||||
duk_pop_2(ctx);
|
||||
|
||||
/* [ ... module ] */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Load a module as the 'main' module. */
|
||||
duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path) {
|
||||
/*
|
||||
* Stack: [ ... source ]
|
||||
*/
|
||||
|
||||
duk__push_module_object(ctx, path, 1 /*main*/);
|
||||
/* [ ... source module ] */
|
||||
|
||||
duk_dup(ctx, 0);
|
||||
/* [ ... source module source ] */
|
||||
|
||||
#if DUK_VERSION >= 19999
|
||||
return duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
|
||||
#else
|
||||
return duk_safe_call(ctx, duk__eval_module_source, 2, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void duk_module_node_init(duk_context *ctx) {
|
||||
/*
|
||||
* Stack: [ ... options ] => [ ... ]
|
||||
*/
|
||||
|
||||
duk_idx_t options_idx;
|
||||
|
||||
duk_require_object_coercible(ctx, -1); /* error before setting up requireCache */
|
||||
options_idx = duk_require_normalize_index(ctx, -1);
|
||||
|
||||
/* Initialize the require cache to a fresh object. */
|
||||
duk_push_global_stash(ctx);
|
||||
#if DUK_VERSION >= 19999
|
||||
duk_push_bare_object(ctx);
|
||||
#else
|
||||
duk_push_object(ctx);
|
||||
duk_push_undefined(ctx);
|
||||
duk_set_prototype(ctx, -2);
|
||||
#endif
|
||||
duk_put_prop_string(ctx, -2, "\xff" "requireCache");
|
||||
duk_pop(ctx);
|
||||
|
||||
/* Stash callbacks for later use. User code can overwrite them later
|
||||
* on directly by accessing the global stash.
|
||||
*/
|
||||
duk_push_global_stash(ctx);
|
||||
duk_get_prop_string(ctx, options_idx, "resolve");
|
||||
duk_require_function(ctx, -1);
|
||||
duk_put_prop_string(ctx, -2, "\xff" "modResolve");
|
||||
duk_get_prop_string(ctx, options_idx, "load");
|
||||
duk_require_function(ctx, -1);
|
||||
duk_put_prop_string(ctx, -2, "\xff" "modLoad");
|
||||
duk_pop(ctx);
|
||||
|
||||
/* Stash main module. */
|
||||
duk_push_global_stash(ctx);
|
||||
duk_push_undefined(ctx);
|
||||
duk_put_prop_string(ctx, -2, "\xff" "mainModule");
|
||||
duk_pop(ctx);
|
||||
|
||||
/* register `require` as a global function. */
|
||||
duk_push_global_object(ctx);
|
||||
duk_push_string(ctx, "require");
|
||||
duk__push_require_function(ctx, "");
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |
|
||||
DUK_DEFPROP_SET_WRITABLE |
|
||||
DUK_DEFPROP_SET_CONFIGURABLE);
|
||||
duk_pop(ctx);
|
||||
|
||||
duk_pop(ctx); /* pop argument */
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#if !defined(DUK_MODULE_NODE_H_INCLUDED)
|
||||
#define DUK_MODULE_NODE_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
extern duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path);
|
||||
extern void duk_module_node_init(duk_context *ctx);
|
||||
|
||||
#endif /* DUK_MODULE_NODE_H_INCLUDED */
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Duktape 1.x compatible print() and alert() bindings.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "duktape.h"
|
||||
#include "duk_print_alert.h"
|
||||
|
||||
#define DUK_PRINT_ALERT_FLUSH /* Flush after stdout/stderr write (Duktape 1.x: yes) */
|
||||
#undef DUK_PRINT_ALERT_SMALL /* Prefer smaller footprint (but slower and more memory churn) */
|
||||
|
||||
#if defined(DUK_PRINT_ALERT_SMALL)
|
||||
static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) {
|
||||
duk_idx_t nargs;
|
||||
|
||||
nargs = duk_get_top(ctx);
|
||||
|
||||
/* If argument count is 1 and first argument is a buffer, write the buffer
|
||||
* as raw data into the file without a newline; this allows exact control
|
||||
* over stdout/stderr without an additional entrypoint (useful for now).
|
||||
* Otherwise current print/alert semantics are to ToString() coerce
|
||||
* arguments, join them with a single space, and append a newline.
|
||||
*/
|
||||
|
||||
if (nargs == 1 && duk_is_buffer(ctx, 0)) {
|
||||
buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf);
|
||||
fwrite((const void *) buf, 1, (size_t) sz_buf, fh);
|
||||
} else {
|
||||
duk_push_string(ctx, " ");
|
||||
duk_insert(ctx, 0);
|
||||
duk_concat(ctx, nargs);
|
||||
fprintf(fh, "%s\n", duk_require_string(ctx, -1));
|
||||
}
|
||||
|
||||
#if defined(DUK_PRINT_ALERT_FLUSH)
|
||||
fflush(fh);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* Faster, less churn, higher footprint option. */
|
||||
static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) {
|
||||
duk_idx_t nargs;
|
||||
const duk_uint8_t *buf;
|
||||
duk_size_t sz_buf;
|
||||
const char nl = (const char) '\n';
|
||||
duk_uint8_t buf_stack[256];
|
||||
|
||||
nargs = duk_get_top(ctx);
|
||||
|
||||
/* If argument count is 1 and first argument is a buffer, write the buffer
|
||||
* as raw data into the file without a newline; this allows exact control
|
||||
* over stdout/stderr without an additional entrypoint (useful for now).
|
||||
* Otherwise current print/alert semantics are to ToString() coerce
|
||||
* arguments, join them with a single space, and append a newline.
|
||||
*/
|
||||
|
||||
if (nargs == 1 && duk_is_buffer(ctx, 0)) {
|
||||
buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf);
|
||||
} else if (nargs > 0) {
|
||||
duk_idx_t i;
|
||||
duk_size_t sz_str;
|
||||
const duk_uint8_t *p_str;
|
||||
duk_uint8_t *p;
|
||||
|
||||
sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */
|
||||
for (i = 0; i < nargs; i++) {
|
||||
(void) duk_to_lstring(ctx, i, &sz_str);
|
||||
sz_buf += sz_str;
|
||||
}
|
||||
|
||||
if (sz_buf <= sizeof(buf_stack)) {
|
||||
p = (duk_uint8_t *) buf_stack;
|
||||
} else {
|
||||
p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf);
|
||||
}
|
||||
|
||||
buf = (const duk_uint8_t *) p;
|
||||
for (i = 0; i < nargs; i++) {
|
||||
p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str);
|
||||
memcpy((void *) p, (const void *) p_str, sz_str);
|
||||
p += sz_str;
|
||||
*p++ = (duk_uint8_t) (i == nargs - 1 ? '\n' : ' ');
|
||||
}
|
||||
} else {
|
||||
buf = (const duk_uint8_t *) &nl;
|
||||
sz_buf = 1;
|
||||
}
|
||||
|
||||
/* 'buf' contains the string to write, 'sz_buf' contains the length
|
||||
* (which may be zero).
|
||||
*/
|
||||
|
||||
if (sz_buf > 0) {
|
||||
fwrite((const void *) buf, 1, (size_t) sz_buf, fh);
|
||||
#if defined(DUK_PRINT_ALERT_FLUSH)
|
||||
fflush(fh);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static duk_ret_t duk__print(duk_context *ctx) {
|
||||
return duk__print_alert_helper(ctx, stdout);
|
||||
}
|
||||
|
||||
static duk_ret_t duk__alert(duk_context *ctx) {
|
||||
return duk__print_alert_helper(ctx, stderr);
|
||||
}
|
||||
|
||||
void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) {
|
||||
(void) flags; /* unused at the moment */
|
||||
|
||||
/* XXX: use duk_def_prop_list(). */
|
||||
duk_push_global_object(ctx);
|
||||
duk_push_string(ctx, "print");
|
||||
duk_push_c_function(ctx, duk__print, DUK_VARARGS);
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
|
||||
duk_push_string(ctx, "alert");
|
||||
duk_push_c_function(ctx, duk__alert, DUK_VARARGS);
|
||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
|
||||
duk_pop(ctx);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#if !defined(DUK_PRINT_ALERT_H_INCLUDED)
|
||||
#define DUK_PRINT_ALERT_H_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* No flags at the moment. */
|
||||
|
||||
extern void duk_print_alert_init(duk_context *ctx, duk_uint_t flags);
|
||||
|
||||
#endif /* DUK_PRINT_ALERT_H_INCLUDED */
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#include <stdio.h>
|
||||
#include "duktape.h"
|
||||
#include "duk_v1_compat.h"
|
||||
|
||||
/*
|
||||
* duk_dump_context_{stdout,stderr}()
|
||||
*/
|
||||
|
||||
void duk_dump_context_stdout(duk_context *ctx) {
|
||||
duk_push_context_dump(ctx);
|
||||
fprintf(stdout, "%s\n", duk_safe_to_string(ctx, -1));
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
void duk_dump_context_stderr(duk_context *ctx) {
|
||||
duk_push_context_dump(ctx);
|
||||
fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* duk_push_string_file() and duk_push_string_file_raw()
|
||||
*/
|
||||
|
||||
const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
|
||||
FILE *f = NULL;
|
||||
char *buf;
|
||||
long sz; /* ANSI C typing */
|
||||
|
||||
if (!path) {
|
||||
goto fail;
|
||||
}
|
||||
f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
goto fail;
|
||||
}
|
||||
if (fseek(f, 0, SEEK_END) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
sz = ftell(f);
|
||||
if (sz < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (fseek(f, 0, SEEK_SET) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz);
|
||||
if ((size_t) fread(buf, 1, (size_t) sz, f) != (size_t) sz) {
|
||||
duk_pop(ctx);
|
||||
goto fail;
|
||||
}
|
||||
(void) fclose(f); /* ignore fclose() error */
|
||||
return duk_buffer_to_string(ctx, -1);
|
||||
|
||||
fail:
|
||||
if (f) {
|
||||
(void) fclose(f); /* ignore fclose() error */
|
||||
}
|
||||
|
||||
if (flags & DUK_STRING_PUSH_SAFE) {
|
||||
duk_push_undefined(ctx);
|
||||
} else {
|
||||
(void) duk_type_error(ctx, "read file error");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* duk_eval_file(), duk_compile_file(), and their variants
|
||||
*/
|
||||
|
||||
void duk_eval_file(duk_context *ctx, const char *path) {
|
||||
duk_push_string_file_raw(ctx, path, 0);
|
||||
duk_push_string(ctx, path);
|
||||
duk_compile(ctx, DUK_COMPILE_EVAL);
|
||||
duk_push_global_object(ctx); /* 'this' binding */
|
||||
duk_call_method(ctx, 0);
|
||||
}
|
||||
|
||||
void duk_eval_file_noresult(duk_context *ctx, const char *path) {
|
||||
duk_eval_file(ctx, path);
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
duk_int_t duk_peval_file(duk_context *ctx, const char *path) {
|
||||
duk_int_t rc;
|
||||
|
||||
duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
|
||||
duk_push_string(ctx, path);
|
||||
rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
duk_push_global_object(ctx); /* 'this' binding */
|
||||
rc = duk_pcall_method(ctx, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path) {
|
||||
duk_int_t rc;
|
||||
|
||||
rc = duk_peval_file(ctx, path);
|
||||
duk_pop(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
|
||||
duk_push_string_file_raw(ctx, path, 0);
|
||||
duk_push_string(ctx, path);
|
||||
duk_compile(ctx, flags);
|
||||
}
|
||||
|
||||
duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
|
||||
duk_int_t rc;
|
||||
|
||||
duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
|
||||
duk_push_string(ctx, path);
|
||||
rc = duk_pcompile(ctx, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* duk_to_defaultvalue()
|
||||
*/
|
||||
|
||||
void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint) {
|
||||
duk_require_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT |
|
||||
DUK_TYPE_MASK_BUFFER |
|
||||
DUK_TYPE_MASK_LIGHTFUNC);
|
||||
duk_to_primitive(ctx, idx, hint);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#if !defined(DUK_V1_COMPAT_INCLUDED)
|
||||
#define DUK_V1_COMPAT_INCLUDED
|
||||
|
||||
#include "duktape.h"
|
||||
|
||||
/* Straight flag rename */
|
||||
#if !defined(DUK_ENUM_INCLUDE_INTERNAL)
|
||||
#define DUK_ENUM_INCLUDE_INTERNAL DUK_ENUM_INCLUDE_HIDDEN
|
||||
#endif
|
||||
|
||||
/* Flags for duk_push_string_file_raw() */
|
||||
#define DUK_STRING_PUSH_SAFE (1 << 0) /* no error if file does not exist */
|
||||
|
||||
extern void duk_dump_context_stdout(duk_context *ctx);
|
||||
extern void duk_dump_context_stderr(duk_context *ctx);
|
||||
extern const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags);
|
||||
extern void duk_eval_file(duk_context *ctx, const char *path);
|
||||
extern void duk_eval_file_noresult(duk_context *ctx, const char *path);
|
||||
extern duk_int_t duk_peval_file(duk_context *ctx, const char *path);
|
||||
extern duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path);
|
||||
extern void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path);
|
||||
extern duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path);
|
||||
extern void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint);
|
||||
|
||||
#define duk_push_string_file(ctx,path) \
|
||||
duk_push_string_file_raw((ctx), (path), 0)
|
||||
|
||||
#endif /* DUK_V1_COMPAT_INCLUDED */
|
||||
+95118
File diff suppressed because it is too large.
Load diff
+356
@@ -0,0 +1,356 @@
|
||||
package duktape
|
||||
|
||||
/*
|
||||
#cgo !windows CFLAGS: -std=c99 -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
|
||||
#cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
|
||||
#cgo linux LDFLAGS: -lm
|
||||
#cgo freebsd LDFLAGS: -lm
|
||||
|
||||
#include "duktape.h"
|
||||
#include "duk_logging.h"
|
||||
#include "duk_print_alert.h"
|
||||
#include "duk_module_duktape.h"
|
||||
#include "duk_console.h"
|
||||
extern duk_ret_t goFunctionCall(duk_context *ctx);
|
||||
extern void goFinalizeCall(duk_context *ctx);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var reFuncName = regexp.MustCompile("^[a-z_][a-z0-9_]*([A-Z_][a-z0-9_]*)*$")
|
||||
|
||||
const (
|
||||
goFunctionPtrProp = "\xff" + "goFunctionPtrProp"
|
||||
goContextPtrProp = "\xff" + "goContextPtrProp"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
*context
|
||||
}
|
||||
|
||||
// transmute replaces the value from Context with the value of pointer
|
||||
func (c *Context) transmute(p unsafe.Pointer) {
|
||||
*c = *(*Context)(p)
|
||||
}
|
||||
|
||||
// this is a pojo containing only the values of the Context
|
||||
type context struct {
|
||||
sync.Mutex
|
||||
duk_context *C.duk_context
|
||||
fnIndex *functionIndex
|
||||
timerIndex *timerIndex
|
||||
}
|
||||
|
||||
// New returns plain initialized duktape context object
|
||||
// See: http://duktape.org/api.html#duk_create_heap_default
|
||||
func New() *Context {
|
||||
d := &Context{
|
||||
&context{
|
||||
duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
|
||||
fnIndex: newFunctionIndex(),
|
||||
timerIndex: &timerIndex{},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := d.duk_context
|
||||
C.duk_logging_init(ctx, 0)
|
||||
C.duk_print_alert_init(ctx, 0)
|
||||
C.duk_module_duktape_init(ctx)
|
||||
C.duk_console_init(ctx, 0)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Flags is a set of flags for controlling the behaviour of duktape.
|
||||
type Flags struct {
|
||||
Logging uint
|
||||
PrintAlert uint
|
||||
Console uint
|
||||
}
|
||||
|
||||
// FlagConsoleProxyWrapper is a Console flag.
|
||||
// Use a proxy wrapper to make undefined methods (console.foo()) no-ops.
|
||||
const FlagConsoleProxyWrapper = 1 << 0
|
||||
|
||||
// FlagConsoleFlush is a Console flag.
|
||||
// Flush output after every call.
|
||||
const FlagConsoleFlush = 1 << 1
|
||||
|
||||
// NewWithFlags returns plain initialized duktape context object
|
||||
// You can control the behaviour of duktape by setting flags.
|
||||
// See: http://duktape.org/api.html#duk_create_heap_default
|
||||
func NewWithFlags(flags *Flags) *Context {
|
||||
d := &Context{
|
||||
&context{
|
||||
duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
|
||||
fnIndex: newFunctionIndex(),
|
||||
timerIndex: &timerIndex{},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := d.duk_context
|
||||
C.duk_logging_init(ctx, C.duk_uint_t(flags.Logging))
|
||||
C.duk_print_alert_init(ctx, C.duk_uint_t(flags.PrintAlert))
|
||||
C.duk_module_duktape_init(ctx)
|
||||
C.duk_console_init(ctx, C.duk_uint_t(flags.Console))
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func contextFromPointer(ctx *C.duk_context) *Context {
|
||||
return &Context{&context{duk_context: ctx}}
|
||||
}
|
||||
|
||||
// PushGlobalGoFunction push the given function into duktape global object
|
||||
// Returns non-negative index (relative to stack bottom) of the pushed function
|
||||
// also returns error if the function name is invalid
|
||||
func (d *Context) PushGlobalGoFunction(name string, fn func(*Context) int) (int, error) {
|
||||
if !reFuncName.MatchString(name) {
|
||||
return -1, errors.New("Malformed function name '" + name + "'")
|
||||
}
|
||||
|
||||
d.PushGlobalObject()
|
||||
idx := d.PushGoFunction(fn)
|
||||
d.PutPropString(-2, name)
|
||||
d.Pop()
|
||||
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
// PushGoFunction push the given function into duktape stack, returns non-negative
|
||||
// index (relative to stack bottom) of the pushed function
|
||||
func (d *Context) PushGoFunction(fn func(*Context) int) int {
|
||||
funPtr := d.fnIndex.add(fn)
|
||||
ctxPtr := contexts.add(d)
|
||||
|
||||
idx := d.PushCFunction((*[0]byte)(C.goFunctionCall), C.DUK_VARARGS)
|
||||
d.PushCFunction((*[0]byte)(C.goFinalizeCall), 1)
|
||||
d.PushPointer(funPtr)
|
||||
d.PutPropString(-2, goFunctionPtrProp)
|
||||
d.PushPointer(ctxPtr)
|
||||
d.PutPropString(-2, goContextPtrProp)
|
||||
d.SetFinalizer(-2)
|
||||
|
||||
d.PushPointer(funPtr)
|
||||
d.PutPropString(-2, goFunctionPtrProp)
|
||||
d.PushPointer(ctxPtr)
|
||||
d.PutPropString(-2, goContextPtrProp)
|
||||
|
||||
return idx
|
||||
}
|
||||
|
||||
//export goFunctionCall
|
||||
func goFunctionCall(cCtx *C.duk_context) C.duk_ret_t {
|
||||
d := contextFromPointer(cCtx)
|
||||
|
||||
funPtr, ctx := d.getFunctionPtrs()
|
||||
d.transmute(unsafe.Pointer(ctx))
|
||||
|
||||
result := d.fnIndex.get(funPtr)(d)
|
||||
|
||||
return C.duk_ret_t(result)
|
||||
}
|
||||
|
||||
//export goFinalizeCall
|
||||
func goFinalizeCall(cCtx *C.duk_context) {
|
||||
d := contextFromPointer(cCtx)
|
||||
|
||||
funPtr, ctx := d.getFunctionPtrs()
|
||||
d.transmute(unsafe.Pointer(ctx))
|
||||
|
||||
d.fnIndex.delete(funPtr)
|
||||
}
|
||||
|
||||
func (d *Context) getFunctionPtrs() (unsafe.Pointer, *Context) {
|
||||
d.PushCurrentFunction()
|
||||
d.GetPropString(-1, goFunctionPtrProp)
|
||||
funPtr := d.GetPointer(-1)
|
||||
|
||||
d.Pop()
|
||||
|
||||
d.GetPropString(-1, goContextPtrProp)
|
||||
ctx := contexts.get(d.GetPointer(-1))
|
||||
d.Pop2()
|
||||
return funPtr, ctx
|
||||
}
|
||||
|
||||
// Destroy destroy all the references to the functions and freed the pointers
|
||||
func (d *Context) Destroy() {
|
||||
d.fnIndex.destroy()
|
||||
contexts.delete(d)
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Type string
|
||||
Message string
|
||||
FileName string
|
||||
LineNumber int
|
||||
Stack string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Type, e.Message)
|
||||
}
|
||||
|
||||
type Type int
|
||||
|
||||
func (t Type) IsNone() bool { return t == TypeNone }
|
||||
func (t Type) IsUndefined() bool { return t == TypeUndefined }
|
||||
func (t Type) IsNull() bool { return t == TypeNull }
|
||||
func (t Type) IsBool() bool { return t == TypeBoolean }
|
||||
func (t Type) IsNumber() bool { return t == TypeNumber }
|
||||
func (t Type) IsString() bool { return t == TypeString }
|
||||
func (t Type) IsObject() bool { return t == TypeObject }
|
||||
func (t Type) IsBuffer() bool { return t == TypeBuffer }
|
||||
func (t Type) IsPointer() bool { return t == TypePointer }
|
||||
func (t Type) IsLightFunc() bool { return t == TypeLightFunc }
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeNone:
|
||||
return "None"
|
||||
case TypeUndefined:
|
||||
return "Undefined"
|
||||
case TypeNull:
|
||||
return "Null"
|
||||
case TypeBoolean:
|
||||
return "Boolean"
|
||||
case TypeNumber:
|
||||
return "Number"
|
||||
case TypeString:
|
||||
return "String"
|
||||
case TypeObject:
|
||||
return "Object"
|
||||
case TypeBuffer:
|
||||
return "Buffer"
|
||||
case TypePointer:
|
||||
return "Pointer"
|
||||
case TypeLightFunc:
|
||||
return "LightFunc"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type functionIndex struct {
|
||||
functions map[unsafe.Pointer]func(*Context) int
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type timerIndex struct {
|
||||
c float64
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (t *timerIndex) get() float64 {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.c++
|
||||
return t.c
|
||||
}
|
||||
|
||||
func newFunctionIndex() *functionIndex {
|
||||
return &functionIndex{
|
||||
functions: make(map[unsafe.Pointer]func(*Context) int, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *functionIndex) add(fn func(*Context) int) unsafe.Pointer {
|
||||
ptr := C.malloc(1)
|
||||
|
||||
i.Lock()
|
||||
i.functions[ptr] = fn
|
||||
i.Unlock()
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (i *functionIndex) get(ptr unsafe.Pointer) func(*Context) int {
|
||||
i.RLock()
|
||||
fn := i.functions[ptr]
|
||||
i.RUnlock()
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (i *functionIndex) delete(ptr unsafe.Pointer) {
|
||||
i.Lock()
|
||||
delete(i.functions, ptr)
|
||||
i.Unlock()
|
||||
|
||||
C.free(ptr)
|
||||
}
|
||||
|
||||
func (i *functionIndex) destroy() {
|
||||
i.Lock()
|
||||
|
||||
for ptr, _ := range i.functions {
|
||||
delete(i.functions, ptr)
|
||||
C.free(ptr)
|
||||
}
|
||||
i.Unlock()
|
||||
}
|
||||
|
||||
type ctxIndex struct {
|
||||
sync.RWMutex
|
||||
ctxs map[unsafe.Pointer]*Context
|
||||
}
|
||||
|
||||
func (ci *ctxIndex) add(ctx *Context) unsafe.Pointer {
|
||||
|
||||
ci.RLock()
|
||||
for ptr, ctxPtr := range ci.ctxs {
|
||||
if ctxPtr == ctx {
|
||||
ci.RUnlock()
|
||||
return ptr
|
||||
}
|
||||
}
|
||||
ci.RUnlock()
|
||||
|
||||
ci.Lock()
|
||||
for ptr, ctxPtr := range ci.ctxs {
|
||||
if ctxPtr == ctx {
|
||||
ci.Unlock()
|
||||
return ptr
|
||||
}
|
||||
}
|
||||
ptr := C.malloc(1)
|
||||
ci.ctxs[ptr] = ctx
|
||||
ci.Unlock()
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (ci *ctxIndex) get(ptr unsafe.Pointer) *Context {
|
||||
ci.RLock()
|
||||
ctx := ci.ctxs[ptr]
|
||||
ci.RUnlock()
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (ci *ctxIndex) delete(ctx *Context) {
|
||||
ci.Lock()
|
||||
for ptr, ctxPtr := range ci.ctxs {
|
||||
if ctxPtr == ctx {
|
||||
delete(ci.ctxs, ptr)
|
||||
C.free(ptr)
|
||||
ci.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("context (%p) doesn't exist", ctx))
|
||||
}
|
||||
|
||||
var contexts *ctxIndex
|
||||
|
||||
func init() {
|
||||
contexts = &ctxIndex{
|
||||
ctxs: make(map[unsafe.Pointer]*Context),
|
||||
}
|
||||
}
|
||||
+1349
File diff suppressed because it is too large.
Load diff
+136
@@ -0,0 +1,136 @@
|
||||
package duktape
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefineTimers defines `setTimeout`, `clearTimeout`, `setInterval`,
|
||||
// `clearInterval` into global context.
|
||||
func (d *Context) PushTimers() error {
|
||||
d.PushGlobalStash()
|
||||
// check if timers already exists
|
||||
if !d.HasPropString(-1, "timers") {
|
||||
d.PushObject()
|
||||
d.PutPropString(-2, "timers") // stash -> [ timers:{} ]
|
||||
d.Pop()
|
||||
|
||||
d.PushGlobalGoFunction("setTimeout", setTimeout)
|
||||
d.PushGlobalGoFunction("setInterval", setInterval)
|
||||
d.PushGlobalGoFunction("clearTimeout", clearTimeout)
|
||||
d.PushGlobalGoFunction("clearInterval", clearTimeout)
|
||||
return nil
|
||||
} else {
|
||||
d.Pop()
|
||||
return errors.New("Timers are already defined")
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Context) FlushTimers() {
|
||||
d.PushGlobalStash()
|
||||
d.PushObject()
|
||||
d.PutPropString(-2, "timers") // stash -> [ timers:{} ]
|
||||
d.Pop()
|
||||
}
|
||||
|
||||
func setTimeout(c *Context) int {
|
||||
id := c.pushTimer(0)
|
||||
timeout := c.ToNumber(1)
|
||||
if timeout < 1 {
|
||||
timeout = 1
|
||||
}
|
||||
go func(id float64) {
|
||||
<-time.After(time.Duration(timeout) * time.Millisecond)
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.duk_context == nil {
|
||||
fmt.Println("[duktape] Warning!\nsetTimeout invokes callback after the context was destroyed.")
|
||||
return
|
||||
}
|
||||
|
||||
// check if timer still exists
|
||||
c.putTimer(id)
|
||||
if c.GetType(-1).IsObject() {
|
||||
c.Pcall(0 /* nargs */)
|
||||
}
|
||||
c.dropTimer(id)
|
||||
}(id)
|
||||
c.PushNumber(id)
|
||||
return 1
|
||||
}
|
||||
|
||||
func clearTimeout(c *Context) int {
|
||||
if c.GetType(0).IsNumber() {
|
||||
c.dropTimer(c.GetNumber(0))
|
||||
c.Pop()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func setInterval(c *Context) int {
|
||||
id := c.pushTimer(0)
|
||||
timeout := c.ToNumber(1)
|
||||
if timeout < 1 {
|
||||
timeout = 1
|
||||
}
|
||||
go func(id float64) {
|
||||
ticker := time.NewTicker(time.Duration(timeout) * time.Millisecond)
|
||||
for _ = range ticker.C {
|
||||
c.Lock()
|
||||
// check if duktape context exists
|
||||
if c.duk_context == nil {
|
||||
c.dropTimer(id)
|
||||
c.Pop()
|
||||
ticker.Stop()
|
||||
fmt.Println("[duktape] Warning!\nsetInterval invokes callback after the context was destroyed.")
|
||||
c.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
// check if timer still exists
|
||||
c.putTimer(id)
|
||||
if c.GetType(-1).IsObject() {
|
||||
c.Pcall(0 /* nargs */)
|
||||
c.Pop()
|
||||
} else {
|
||||
c.dropTimer(id)
|
||||
c.Pop()
|
||||
ticker.Stop()
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
}(id)
|
||||
c.PushNumber(id)
|
||||
return 1
|
||||
}
|
||||
|
||||
func (d *Context) pushTimer(index int) float64 {
|
||||
id := d.timerIndex.get()
|
||||
|
||||
d.PushGlobalStash()
|
||||
d.GetPropString(-1, "timers")
|
||||
d.PushNumber(id)
|
||||
d.Dup(index)
|
||||
d.PutProp(-3)
|
||||
d.Pop2()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (d *Context) dropTimer(id float64) {
|
||||
d.PushGlobalStash()
|
||||
d.GetPropString(-1, "timers")
|
||||
d.PushNumber(id)
|
||||
d.DelProp(-2)
|
||||
d.Pop2()
|
||||
}
|
||||
|
||||
func (d *Context) putTimer(id float64) {
|
||||
d.PushGlobalStash() // stash -> [ ..., timers: { <id>: { func: true } } ]
|
||||
d.GetPropString(-1, "timers") // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true } ]
|
||||
d.PushNumber(id)
|
||||
d.GetProp(-2) // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true }, { func: true } ]
|
||||
d.Replace(-3)
|
||||
d.Pop()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package duktape
|
||||
|
||||
// Must returns existing *Context or throw panic.
|
||||
// It is highly recommended to use Must all the time.
|
||||
func (d *Context) Must() *Context {
|
||||
if d.duk_context == nil {
|
||||
panic("[duktape] Context does not exists!\nYou cannot call any contexts methods after `DestroyHeap()` was called.")
|
||||
}
|
||||
return d
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
box: golang
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
cd $WERCKER_SOURCE_DIR
|
||||
go version
|
||||
go get gopkg.in/check.v1
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test . -v
|
||||
Vendored
+6
@@ -693,6 +693,12 @@
|
||||
"revision": "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6",
|
||||
"revisionTime": "2016-06-21T03:49:01Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "YvuEbc0a032zr+BTp/YbBQojiuY=",
|
||||
"path": "gopkg.in/olebedev/go-duktape.v3",
|
||||
"revision": "9af39127cb024b355a6a0221769f6ddfe3f542e7",
|
||||
"revisionTime": "2017-12-20T12:19:14Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4BwmmgQUhWtizsR2soXND0nqZ1I=",
|
||||
"path": "gopkg.in/sourcemap.v1",
|
||||
|
||||
Reference in new issue
Block a user