Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MainDB V4 and LogDB with various improvements #942

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions api/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,15 @@ func (d *Debug) parseTarget(target string) (block *block.Block, txID thor.Bytes3
if err != nil {
return nil, thor.Bytes32{}, 0, utils.BadRequest(errors.WithMessage(err, "target([0]"))
}
txMeta, err := d.repo.NewBestChain().GetTransactionMeta(txID)
bestChain := d.repo.NewBestChain()
txMeta, err := bestChain.GetTransactionMeta(txID)
if err != nil {
if d.repo.IsNotFound(err) {
return nil, thor.Bytes32{}, 0, utils.Forbidden(errors.New("transaction not found"))
}
return nil, thor.Bytes32{}, 0, err
}
block, err = d.repo.GetBlock(txMeta.BlockID)
block, err = bestChain.GetBlock(txMeta.BlockNum)
if err != nil {
return nil, thor.Bytes32{}, 0, err
}
Expand Down
7 changes: 3 additions & 4 deletions api/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/vechain/thor/v2/thor"
"github.com/vechain/thor/v2/thorclient"
"github.com/vechain/thor/v2/tracers/logger"
"github.com/vechain/thor/v2/trie"
"github.com/vechain/thor/v2/tx"

// Force-load the tracer native engines to trigger registration
Expand Down Expand Up @@ -94,8 +95,7 @@ func TestDebug(t *testing.T) {
}

func TestStorageRangeFunc(t *testing.T) {
db := muxdb.NewMem()
state := state.New(db, thor.Bytes32{}, 0, 0, 0)
state := state.New(muxdb.NewMem(), trie.Root{})

// Create an account and set storage values
addr := thor.BytesToAddress([]byte("account1"))
Expand Down Expand Up @@ -124,8 +124,7 @@ func TestStorageRangeFunc(t *testing.T) {
}

func TestStorageRangeMaxResult(t *testing.T) {
db := muxdb.NewMem()
state := state.New(db, thor.Bytes32{}, 0, 0, 0)
state := state.New(muxdb.NewMem(), trie.Root{})

addr := thor.BytesToAddress([]byte("account1"))
for i := 0; i < 1001; i++ {
Expand Down
40 changes: 38 additions & 2 deletions api/doc/thor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ info:
license:
name: LGPL 3.0
url: https://www.gnu.org/licenses/lgpl-3.0.en.html
version: 2.1.6
version: 2.2.0
servers:
- url: /
description: Current Node
Expand Down Expand Up @@ -1326,6 +1326,16 @@ components:
description: The index of the clause in the transaction, from which the log was generated.
example: 0
nullable: false
txIndex:
description: The index of the transaction in the block, from which the log was generated.
type: integer
nullable: true
example: 1
logIndex:
description: The index of the log in the receipt's outputs. This is an overall index among all clauses.
type: integer
nullable: true
example: 1

Block:
title: Block
Expand Down Expand Up @@ -1856,6 +1866,11 @@ components:
The limit of records to be included in the output. Use this parameter for pagination.

Default's to all results.
includeIndexes:
type: boolean
example: true
nullable: true
description: Include both transaction and log index in the response.
description: |
Include these parameters to receive filtered results in a paged format.

Expand All @@ -1866,7 +1881,8 @@ components:
{
"options": {
"offset": 0,
"limit": 10
"limit": 10,
"includeIndexes": true
}
}
```
Expand Down Expand Up @@ -1917,6 +1933,26 @@ components:
}
```
This refers to the range from block 10 to block 1000.

EventOptionalData:
nullable: true
type: object
title: EventOptionalData
properties:
txIndex:
type: boolean
example: true
nullable: true
description: |
Specifies whether to include in the response the event transaction index.
loglIndex:
type: boolean
example: true
nullable: true
description: |
Specifies whether to include in the response the event log index.
description: |
Specifies all the optional data that can be included in the response.

EventCriteria:
type: object
Expand Down
9 changes: 5 additions & 4 deletions api/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (e *Events) filter(ctx context.Context, ef *EventFilter) ([]*FilteredEvent,
}
fes := make([]*FilteredEvent, len(events))
for i, e := range events {
fes[i] = convertEvent(e)
fes[i] = convertEvent(e, ef.Options.IncludeIndexes)
}
return fes, nil
}
Expand All @@ -60,9 +60,10 @@ func (e *Events) handleFilter(w http.ResponseWriter, req *http.Request) error {
if filter.Options == nil {
// if filter.Options is nil, set to the default limit +1
// to detect whether there are more logs than the default limit
filter.Options = &logdb.Options{
Offset: 0,
Limit: e.limit + 1,
filter.Options = &Options{
Offset: 0,
Limit: e.limit + 1,
IncludeIndexes: false,
}
}

Expand Down
151 changes: 105 additions & 46 deletions api/events/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package events_test

import (
"encoding/json"
"math/big"
"net/http"
"net/http/httptest"
"strings"
Expand All @@ -16,8 +17,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vechain/thor/v2/api/events"
"github.com/vechain/thor/v2/block"
"github.com/vechain/thor/v2/builtin"
"github.com/vechain/thor/v2/genesis"
"github.com/vechain/thor/v2/logdb"
"github.com/vechain/thor/v2/test/datagen"
"github.com/vechain/thor/v2/test/testchain"
"github.com/vechain/thor/v2/thor"
"github.com/vechain/thor/v2/thorclient"
Expand All @@ -28,8 +31,6 @@ const defaultLogLimit uint64 = 1000

var (
ts *httptest.Server
addr = thor.BytesToAddress([]byte("address"))
topic = thor.BytesToBytes32([]byte("topic"))
tclient *thorclient.Client
)

Expand All @@ -52,20 +53,70 @@ func TestEvents(t *testing.T) {

blocksToInsert := 5
tclient = thorclient.New(ts.URL)
insertBlocks(t, thorChain.LogDB(), blocksToInsert)
insertBlocks(t, thorChain, blocksToInsert)
testEventWithBlocks(t, blocksToInsert)
}

func TestOptionalIndexes(t *testing.T) {
thorChain := initEventServer(t, defaultLogLimit)
defer ts.Close()
insertBlocks(t, thorChain, 5)
tclient = thorclient.New(ts.URL)

testCases := []struct {
name string
includeIndexes bool
expected *uint32
}{
{
name: "do not include indexes",
includeIndexes: false,
expected: nil,
},
{
name: "include indexes",
includeIndexes: true,
expected: new(uint32),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
filter := events.EventFilter{
CriteriaSet: make([]*events.EventCriteria, 0),
Range: nil,
Options: &events.Options{Limit: 6, IncludeIndexes: tc.includeIndexes},
Order: logdb.DESC,
}

res, statusCode, err := tclient.RawHTTPClient().RawHTTPPost("/logs/event", filter)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, statusCode)
var tLogs []*events.FilteredEvent
if err := json.Unmarshal(res, &tLogs); err != nil {
t.Fatal(err)
}
assert.Equal(t, http.StatusOK, statusCode)
assert.Equal(t, 5, len(tLogs))

for _, tLog := range tLogs {
assert.Equal(t, tc.expected, tLog.Meta.TxIndex)
assert.Equal(t, tc.expected, tLog.Meta.LogIndex)
}
})
}
}

func TestOption(t *testing.T) {
thorChain := initEventServer(t, 5)
defer ts.Close()
insertBlocks(t, thorChain.LogDB(), 5)
insertBlocks(t, thorChain, 5)

tclient = thorclient.New(ts.URL)
filter := events.EventFilter{
CriteriaSet: make([]*events.EventCriteria, 0),
Range: nil,
Options: &logdb.Options{Limit: 6},
Options: &events.Options{Limit: 6},
Order: logdb.DESC,
}

Expand Down Expand Up @@ -93,13 +144,47 @@ func TestOption(t *testing.T) {
assert.Equal(t, 5, len(tLogs))

// when the filtered events exceed the limit, should return the forbidden
insertBlocks(t, thorChain.LogDB(), 6)
insertBlocks(t, thorChain, 6)
res, statusCode, err = tclient.RawHTTPClient().RawHTTPPost("/logs/event", filter)
require.NoError(t, err)
assert.Equal(t, http.StatusForbidden, statusCode)
assert.Equal(t, "the number of filtered logs exceeds the maximum allowed value of 5, please use pagination", strings.Trim(string(res), "\n"))
}

func TestZeroFrom(t *testing.T) {
thorChain := initEventServer(t, 100)
defer ts.Close()
insertBlocks(t, thorChain, 5)

tclient = thorclient.New(ts.URL)
transferTopic := thor.MustParseBytes32("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
criteria := []*events.EventCriteria{
{
TopicSet: events.TopicSet{
Topic0: &transferTopic,
},
},
}

from := uint64(0)
filter := events.EventFilter{
CriteriaSet: criteria,
Range: &events.Range{From: &from},
Options: nil,
Order: logdb.DESC,
}

res, statusCode, err := tclient.RawHTTPClient().RawHTTPPost("/logs/event", filter)
require.NoError(t, err)
var tLogs []*events.FilteredEvent
if err := json.Unmarshal(res, &tLogs); err != nil {
t.Fatal(err)
}

assert.Equal(t, http.StatusOK, statusCode)
assert.NotEmpty(t, tLogs)
}

// Test functions
func testEventsBadRequest(t *testing.T) {
badBody := []byte{0x00, 0x01, 0x02}
Expand Down Expand Up @@ -149,16 +234,14 @@ func testEventWithBlocks(t *testing.T, expectedBlocks int) {
assert.NotEmpty(t, tLog)
}

transferEvent := thor.MustParseBytes32("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

// Test with matching filter
matchingFilter := events.EventFilter{
CriteriaSet: []*events.EventCriteria{{
Address: &addr,
Address: &builtin.Energy.Address,
TopicSet: events.TopicSet{
&topic,
&topic,
&topic,
&topic,
&topic,
Topic0: &transferEvent,
},
}},
}
Expand Down Expand Up @@ -189,41 +272,17 @@ func initEventServer(t *testing.T, limit uint64) *testchain.Chain {
}

// Utilities functions
func insertBlocks(t *testing.T, db *logdb.LogDB, n int) {
b := new(block.Builder).Build()
for i := 0; i < n; i++ {
b = new(block.Builder).
ParentID(b.Header().ID()).
Build()
receipts := tx.Receipts{newReceipt()}
func insertBlocks(t *testing.T, chain *testchain.Chain, n int) {
transferABI, ok := builtin.Energy.ABI.MethodByName("transfer")
require.True(t, ok)

w := db.NewWriter()
if err := w.Write(b, receipts); err != nil {
t.Fatal(err)
}
encoded, err := transferABI.EncodeInput(genesis.DevAccounts()[2].Address, new(big.Int).SetUint64(datagen.RandUint64()))
require.NoError(t, err)

if err := w.Commit(); err != nil {
t.Fatal(err)
}
}
}
transferClause := tx.NewClause(&builtin.Energy.Address).WithData(encoded)

func newReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: addr,
Topics: []thor.Bytes32{
topic,
topic,
topic,
topic,
topic,
},
Data: []byte("0x0"),
}},
},
},
for i := 0; i < n; i++ {
err := chain.MintClauses(genesis.DevAccounts()[0], []*tx.Clause{transferClause})
require.NoError(t, err)
}
}
Loading
Loading