Skip to content

Commit

Permalink
Add api-allowed-tracers flag (vechain#823)
Browse files Browse the repository at this point in the history
* Add allowed-tracers flag

* fix test

* pr comments

* tidy up err message

* Adding none as the default tracer

* invalid tracer name

* update swagger

* update e2e tests
  • Loading branch information
otherview authored Aug 22, 2024
1 parent c00c21e commit f1aa5ae
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 18 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ jobs:
uses: actions/checkout@v4
with:
repository: vechain/thor-e2e-tests
# https://github.com/vechain/thor-e2e-tests/tree/00bd3f1b949b05da94e82686e0089a11a136c34c
ref: 87aeb1747995e8b235a796303b0fc08ab16262c6
# https://github.com/vechain/thor-e2e-tests/tree/d86b30b6409b27e841eace0f7c5b6e75c0a4e25e
ref: d86b30b6409b27e841eace0f7c5b6e75c0a4e25e

- name: Download artifact
uses: actions/download-artifact@v4
Expand Down
3 changes: 2 additions & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func New(
enableReqLogger bool,
enableMetrics bool,
logsLimit uint64,
allowedTracers map[string]interface{},
) (http.HandlerFunc, func()) {
origins := strings.Split(strings.TrimSpace(allowedOrigins), ",")
for i, o := range origins {
Expand Down Expand Up @@ -82,7 +83,7 @@ func New(
Mount(router, "/blocks")
transactions.New(repo, txPool).
Mount(router, "/transactions")
debug.New(repo, stater, forkConfig, callGasLimit, allowCustomTracer, bft).
debug.New(repo, stater, forkConfig, callGasLimit, allowCustomTracer, bft, allowedTracers).
Mount(router, "/debug")
node.New(nw).
Mount(router, "/node")
Expand Down
18 changes: 14 additions & 4 deletions api/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package debug
import (
"context"
"encoding/json"
"fmt"
"math"
"math/big"
"net/http"
Expand All @@ -29,7 +30,6 @@ import (
"github.com/vechain/thor/v2/state"
"github.com/vechain/thor/v2/thor"
"github.com/vechain/thor/v2/tracers"
"github.com/vechain/thor/v2/tracers/logger"
"github.com/vechain/thor/v2/trie"
"github.com/vechain/thor/v2/tx"
"github.com/vechain/thor/v2/vm"
Expand All @@ -47,16 +47,18 @@ type Debug struct {
callGasLimit uint64
allowCustomTracer bool
bft bft.Finalizer
allowedTracers map[string]interface{}
}

func New(repo *chain.Repository, stater *state.Stater, forkConfig thor.ForkConfig, callGaslimit uint64, allowCustomTracer bool, bft bft.Finalizer) *Debug {
func New(repo *chain.Repository, stater *state.Stater, forkConfig thor.ForkConfig, callGaslimit uint64, allowCustomTracer bool, bft bft.Finalizer, allowedTracers map[string]interface{}) *Debug {
return &Debug{
repo,
stater,
forkConfig,
callGaslimit,
allowCustomTracer,
bft,
allowedTracers,
}
}

Expand Down Expand Up @@ -211,9 +213,17 @@ func (d *Debug) handleTraceCall(w http.ResponseWriter, req *http.Request) error
}

func (d *Debug) createTracer(name string, config json.RawMessage) (tracers.Tracer, error) {
if name == "" {
return logger.NewStructLogger(config)
if strings.TrimSpace(name) == "" {
return nil, fmt.Errorf("tracer name must be defined")
}
_, noTracers := d.allowedTracers["none"]
_, allTracers := d.allowedTracers["all"]

// fail if the requested tracer is not allowed OR if the "all" tracers code isn't active
if _, ok := d.allowedTracers[name]; noTracers || (!ok && !allTracers) {
return nil, fmt.Errorf("creating tracer is not allowed: %s", name)
}

return tracers.DefaultDirectory.New(name, config, d.allowCustomTracer)
}

Expand Down
38 changes: 33 additions & 5 deletions api/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func TestDebug(t *testing.T) {

// /tracers endpoint
for name, tt := range map[string]func(*testing.T){
"testTraceClauseWithEmptyTracerName": testTraceClauseWithEmptyTracerName,
"testTraceClauseWithInvalidTracerName": testTraceClauseWithInvalidTracerName,
"testTraceClauseWithEmptyTracerTarget": testTraceClauseWithEmptyTracerTarget,
"testTraceClauseWithBadBlockId": testTraceClauseWithBadBlockId,
"testTraceClauseWithNonExistingBlockId": testTraceClauseWithNonExistingBlockId,
Expand Down Expand Up @@ -156,13 +158,27 @@ func TestStorageRangeMaxResult(t *testing.T) {
assert.Equal(t, 10, len(storageRangeRes.Storage))
}

func testTraceClauseWithEmptyTracerName(t *testing.T) {
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", &TraceClauseOption{}, 403)
assert.Equal(t, "tracer name must be defined", strings.TrimSpace(res))

res = httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", &TraceClauseOption{Name: " "}, 403)
assert.Equal(t, "tracer name must be defined", strings.TrimSpace(res))
}

func testTraceClauseWithInvalidTracerName(t *testing.T) {
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", &TraceClauseOption{Name: "non-existent"}, 403)
assert.Contains(t, res, "unable to create custom tracer")
}

func testTraceClauseWithEmptyTracerTarget(t *testing.T) {
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", &TraceClauseOption{}, 400)
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", &TraceClauseOption{Name: "logger"}, 400)
assert.Equal(t, "target: unsupported", strings.TrimSpace(res))
}

func testTraceClauseWithBadBlockId(t *testing.T) {
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: "badBlockId/x/x",
}
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", traceClauseOption, 400)
Expand All @@ -177,6 +193,7 @@ func testTraceClauseWithNonExistingBlockId(t *testing.T) {

func testTraceClauseWithBadTxId(t *testing.T) {
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/badTxId/x", blk.Header().ID()),
}
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", traceClauseOption, 400)
Expand All @@ -186,6 +203,7 @@ func testTraceClauseWithBadTxId(t *testing.T) {
func testTraceClauseWithNonExistingTx(t *testing.T) {
nonExistingTxId := "0x4500ade0d72115abfc77571aef752df45ba5e87ca81fbd67fbfc46d455b17f91"
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/%s/x", blk.Header().ID(), nonExistingTxId),
}
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", traceClauseOption, 403)
Expand All @@ -195,13 +213,15 @@ func testTraceClauseWithNonExistingTx(t *testing.T) {
func testTraceClauseWithBadClauseIndex(t *testing.T) {
// Clause index is not a number
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/%s/x", blk.Header().ID(), transaction.ID()),
}
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", traceClauseOption, 400)
assert.Equal(t, `target[2]: strconv.ParseUint: parsing "x": invalid syntax`, strings.TrimSpace(res))

// Clause index is out of range
traceClauseOption = &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/%s/%d", blk.Header().ID(), transaction.ID(), uint64(math.MaxUint64)),
}
res = httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers", traceClauseOption, 400)
Expand Down Expand Up @@ -237,6 +257,7 @@ func testTraceClauseWithCustomTracer(t *testing.T) {

func testTraceClause(t *testing.T) {
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/%s/1", blk.Header().ID(), transaction.ID()),
}
expectedExecutionResult := &logger.ExecutionResult{
Expand All @@ -256,6 +277,7 @@ func testTraceClause(t *testing.T) {

func testTraceClauseWithTxIndexOutOfBound(t *testing.T) {
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/10/1", blk.Header().ID()),
}

Expand All @@ -266,6 +288,7 @@ func testTraceClauseWithTxIndexOutOfBound(t *testing.T) {

func testTraceClauseWithClauseIndexOutOfBound(t *testing.T) {
traceClauseOption := &TraceClauseOption{
Name: "logger",
Target: fmt.Sprintf("%s/%s/10", blk.Header().ID(), transaction.ID()),
}

Expand All @@ -280,7 +303,7 @@ func testHandleTraceCallWithMalformedBodyRequest(t *testing.T) {
}

func testHandleTraceCallWithEmptyTraceCallOption(t *testing.T) {
traceCallOption := &TraceCallOption{}
traceCallOption := &TraceCallOption{Name: "logger"}
expectedExecutionResult := &logger.ExecutionResult{
Gas: 0,
Failed: false,
Expand All @@ -298,14 +321,15 @@ func testHandleTraceCallWithEmptyTraceCallOption(t *testing.T) {
}

func testTraceCallNextBlock(t *testing.T) {
traceCallOption := &TraceCallOption{}
traceCallOption := &TraceCallOption{Name: "logger"}
httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers/call?revision=next", traceCallOption, 200)
}

func testHandleTraceCall(t *testing.T) {
addr := randAddress()
provedWork := math.HexOrDecimal256(*big.NewInt(1000))
traceCallOption := &TraceCallOption{
Name: "logger",
To: &addr,
Value: &math.HexOrDecimal256{},
Data: "0x00",
Expand Down Expand Up @@ -349,7 +373,7 @@ func testHandleTraceCallWithValidRevisions(t *testing.T) {
StructLogs: make([]logger.StructLogRes, 0),
}

res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers/call?revision="+revision, &TraceCallOption{}, 200)
res := httpPostAndCheckResponseStatus(t, ts.URL+"/debug/tracers/call?revision="+revision, &TraceCallOption{Name: "logger"}, 200)

var parsedExecutionRes *logger.ExecutionResult
if err := json.Unmarshal([]byte(res), &parsedExecutionRes); err != nil {
Expand Down Expand Up @@ -391,6 +415,7 @@ func testHandleTraceCallWithMalfomredRevision(t *testing.T) {
func testHandleTraceCallWithInsufficientGas(t *testing.T) {
addr := randAddress()
traceCallOption := &TraceCallOption{
Name: "logger",
To: &addr,
Value: &math.HexOrDecimal256{},
Data: "0x00",
Expand All @@ -410,6 +435,7 @@ func testHandleTraceCallWithInsufficientGas(t *testing.T) {
func testHandleTraceCallWithBadBlockRef(t *testing.T) {
addr := randAddress()
traceCallOption := &TraceCallOption{
Name: "logger",
To: &addr,
Value: &math.HexOrDecimal256{},
Data: "0x00",
Expand All @@ -429,6 +455,7 @@ func testHandleTraceCallWithBadBlockRef(t *testing.T) {
func testHandleTraceCallWithInvalidLengthBlockRef(t *testing.T) {
addr := randAddress()
traceCallOption := &TraceCallOption{
Name: "logger",
To: &addr,
Value: &math.HexOrDecimal256{},
Data: "0x00",
Expand Down Expand Up @@ -569,7 +596,8 @@ func initDebugServer(t *testing.T) {

forkConfig := thor.GetForkConfig(b.Header().ID())
router := mux.NewRouter()
debug = New(repo, stater, forkConfig, 21000, true, solo.NewBFTEngine(repo))
allTracersEnabled := map[string]interface{}{"all": new(interface{})}
debug = New(repo, stater, forkConfig, 21000, true, solo.NewBFTEngine(repo), allTracersEnabled)
debug.Mount(router, "/debug")
ts = httptest.NewServer(router)
}
Expand Down
2 changes: 1 addition & 1 deletion api/doc/thor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2117,7 +2117,7 @@ components:
name:
type: string
enum:
- ""
- logger
- 4byte
- call
- noop
Expand Down
6 changes: 6 additions & 0 deletions cmd/thor/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ var (
Usage: "set tx limit per account in pool",
}

allowedTracersFlag = cli.StringFlag{
Name: "api-allowed-tracers",
Value: "none",
Usage: "define allowed API tracers",
}

// solo mode only flags
onDemandFlag = cli.BoolFlag{
Name: "on-demand",
Expand Down
6 changes: 6 additions & 0 deletions cmd/thor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io"
"os"
"path/filepath"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/keystore"
Expand All @@ -35,6 +36,7 @@ import (

// Force-load the tracer engines to trigger registration
_ "github.com/vechain/thor/v2/tracers/js"
_ "github.com/vechain/thor/v2/tracers/logger"
_ "github.com/vechain/thor/v2/tracers/native"
)

Expand Down Expand Up @@ -96,6 +98,7 @@ func main() {
adminAddrFlag,
enableAdminFlag,
txPoolLimitPerAccountFlag,
allowedTracersFlag,
},
Action: defaultAction,
Commands: []cli.Command{
Expand Down Expand Up @@ -130,6 +133,7 @@ func main() {
metricsAddrFlag,
adminAddrFlag,
enableAdminFlag,
allowedTracersFlag,
},
Action: soloAction,
},
Expand Down Expand Up @@ -261,6 +265,7 @@ func defaultAction(ctx *cli.Context) error {
ctx.Bool(enableAPILogsFlag.Name),
ctx.Bool(enableMetricsFlag.Name),
ctx.Uint64(apiLogsLimitFlag.Name),
parseTracerList(strings.TrimSpace(ctx.String(allowedTracersFlag.Name))),
)
defer func() { log.Info("closing API..."); apiCloser() }()

Expand Down Expand Up @@ -410,6 +415,7 @@ func soloAction(ctx *cli.Context) error {
ctx.Bool(enableAPILogsFlag.Name),
ctx.Bool(enableMetricsFlag.Name),
ctx.Uint64(apiLogsLimitFlag.Name),
parseTracerList(strings.TrimSpace(ctx.String(allowedTracersFlag.Name))),
)
defer func() { log.Info("closing API..."); apiCloser() }()

Expand Down
13 changes: 13 additions & 0 deletions cmd/thor/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,16 @@ func readIntFromUInt64Flag(val uint64) (int, error) {

return i, nil
}

func parseTracerList(list string) map[string]interface{} {
inputs := strings.Split(list, ",")
tracerMap := map[string]interface{}{}
for _, i := range inputs {
if i == "" {
continue
}
tracerMap[i] = new(interface{})
}

return tracerMap
}
1 change: 1 addition & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ bin/thor -h
| `--api-call-gas-limit` | Limit contract call gas (default: 50000000) |
| `--api-backtrace-limit` | Limit the distance between 'position' and best block for subscriptions APIs (default: 1000) |
| `--api-allow-custom-tracer` | Allow custom JS tracer to be used for the tracer API |
| `--api-allowed-tracers` | Comma-separated list of allowed tracers (default: "none") |
| `--enable-api-logs` | Enables API requests logging |
| `--api-logs-limit` | Limit the number of logs returned by /logs API (default: 1000) |
| `--verbosity` | Log verbosity (0-9) (default: 3) |
Expand Down
6 changes: 5 additions & 1 deletion tracers/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import (
"github.com/vechain/thor/v2/vm"
)

func init() {
tracers.DefaultDirectory.Register("logger", NewStructLogger, false)
}

// Storage represents a contract's storage.
type Storage map[common.Hash]common.Hash

Expand Down Expand Up @@ -119,7 +123,7 @@ type StructLogger struct {
}

// NewStructLogger returns a new logger
func NewStructLogger(cfg json.RawMessage) (*StructLogger, error) {
func NewStructLogger(cfg json.RawMessage) (tracers.Tracer, error) {
var config Config
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
Expand Down
8 changes: 5 additions & 3 deletions tracers/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {}

func TestStoreCapture(t *testing.T) {
var (
logger, _ = NewStructLogger(nil)
env = vm.NewEVM(vm.Context{}, &dummyStatedb{}, &vm.ChainConfig{ChainConfig: *params.TestChainConfig}, vm.Config{Tracer: logger})
unCastedLogger, _ = NewStructLogger(nil)
env = vm.NewEVM(vm.Context{}, &dummyStatedb{}, &vm.ChainConfig{ChainConfig: *params.TestChainConfig}, vm.Config{Tracer: unCastedLogger.(*StructLogger)})

contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
)
logger := unCastedLogger.(*StructLogger)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
Expand Down Expand Up @@ -123,7 +124,8 @@ func TestFormatLogs(t *testing.T) {
}

func TestCaptureStart(t *testing.T) {
logger, _ := NewStructLogger(nil)
unCastedLogger, _ := NewStructLogger(nil)
logger := unCastedLogger.(*StructLogger)
env := &vm.EVM{}

logger.CaptureStart(env, common.Address{}, common.Address{}, false, nil, 0, nil)
Expand Down
2 changes: 1 addition & 1 deletion tracers/tracers.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (d *directory) New(name string, cfg json.RawMessage, allowCustom bool) (Tra
// Assume JS code
tracer, err := d.jsEval(name, cfg)
if err != nil {
return nil, errors.Wrap(err, "create custom tracer")
return nil, errors.Wrap(err, "unable to create custom tracer")
}
return tracer, nil
} else {
Expand Down

0 comments on commit f1aa5ae

Please sign in to comment.