Skip to content

Commit

Permalink
feat: Store block timestamps. (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajnavarro authored Sep 30, 2024
1 parent d0c91f5 commit 83b3c2f
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 29 deletions.
17 changes: 9 additions & 8 deletions backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"fmt"
"time"

_ "github.com/gnolang/gno/gno.land/pkg/sdk/vm"

"github.com/gnolang/tx-archive/backup/client"
"github.com/gnolang/tx-archive/backup/writer"
"github.com/gnolang/tx-archive/log"
"github.com/gnolang/tx-archive/log/noop"
"github.com/gnolang/tx-archive/types"

_ "github.com/gnolang/gno/gno.land/pkg/sdk/vm"
)

// Service is the chain backup service
Expand Down Expand Up @@ -56,22 +56,23 @@ func (s *Service) ExecuteBackup(ctx context.Context, cfg Config) error {
// Keep track of total txs backed up
totalTxs := uint64(0)

fetchAndWrite := func(block uint64) error {
txs, txErr := s.client.GetBlockTransactions(block)
fetchAndWrite := func(height uint64) error {
block, txErr := s.client.GetBlock(height)
if txErr != nil {
return fmt.Errorf("unable to fetch block transactions, %w", txErr)
}

// Skip empty blocks
if len(txs) == 0 {
if len(block.Txs) == 0 {
return nil
}

// Save the block transaction data, if any
for _, tx := range txs {
for _, tx := range block.Txs {
data := &types.TxData{
Tx: tx,
BlockNum: block,
Tx: tx,
BlockNum: block.Height,
Timestamp: block.Timestamp,
}

// Write the tx data to the file
Expand Down
28 changes: 22 additions & 6 deletions backup/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gnolang/tx-archive/backup/client"
"github.com/gnolang/tx-archive/backup/writer/standard"
"github.com/gnolang/tx-archive/log/noop"
"github.com/gnolang/tx-archive/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestBackup_DetermineRightBound(t *testing.T) {
Expand Down Expand Up @@ -96,17 +98,23 @@ func TestBackup_ExecuteBackup_FixedRange(t *testing.T) {

cfg = DefaultConfig()

blockTime = time.Date(1987, 0o6, 24, 6, 32, 11, 0, time.FixedZone("Europe/Madrid", 0))

mockClient = &mockClient{
getLatestBlockNumberFn: func() (uint64, error) {
return toBlock, nil
},
getBlockTransactionsFn: func(blockNum uint64) ([]std.Tx, error) {
getBlockFn: func(blockNum uint64) (*client.Block, error) {
// Sanity check
if blockNum < fromBlock && blockNum > toBlock {
t.Fatal("invalid block number requested")
}

return []std.Tx{exampleTx}, nil // 1 tx per block
return &client.Block{
Height: blockNum,
Timestamp: blockTime.Add(time.Duration(blockNum) * time.Minute).UnixMilli(),
Txs: []std.Tx{exampleTx},
}, nil // 1 tx per block
},
}
)
Expand Down Expand Up @@ -152,6 +160,7 @@ func TestBackup_ExecuteBackup_FixedRange(t *testing.T) {

assert.Equal(t, expectedBlock, txData.BlockNum)
assert.Equal(t, exampleTx, txData.Tx)
assert.Equal(t, blockTime.Add(time.Duration(expectedBlock)*time.Minute).Local(), time.UnixMilli(txData.Timestamp))

expectedBlock++
}
Expand Down Expand Up @@ -183,11 +192,13 @@ func TestBackup_ExecuteBackup_Watch(t *testing.T) {

cfg = DefaultConfig()

blockTime = time.Date(1987, 0o6, 24, 6, 32, 11, 0, time.FixedZone("Europe/Madrid", 0))

mockClient = &mockClient{
getLatestBlockNumberFn: func() (uint64, error) {
return toBlock, nil
},
getBlockTransactionsFn: func(blockNum uint64) ([]std.Tx, error) {
getBlockFn: func(blockNum uint64) (*client.Block, error) {
// Sanity check
if blockNum < fromBlock && blockNum > toBlock {
t.Fatal("invalid block number requested")
Expand All @@ -198,7 +209,11 @@ func TestBackup_ExecuteBackup_Watch(t *testing.T) {
cancelFn()
}

return []std.Tx{exampleTx}, nil // 1 tx per block
return &client.Block{
Height: blockNum,
Timestamp: blockTime.Add(time.Duration(blockNum) * time.Minute).UnixMilli(),
Txs: []std.Tx{exampleTx},
}, nil // 1 tx per block
},
}
)
Expand Down Expand Up @@ -246,6 +261,7 @@ func TestBackup_ExecuteBackup_Watch(t *testing.T) {

assert.Equal(t, expectedBlock, txData.BlockNum)
assert.Equal(t, exampleTx, txData.Tx)
assert.Equal(t, blockTime.Add(time.Duration(expectedBlock)*time.Minute).Local(), time.UnixMilli(txData.Timestamp))

expectedBlock++
}
Expand Down
17 changes: 13 additions & 4 deletions backup/client/client.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package client

import "github.com/gnolang/gno/tm2/pkg/std"
import (
"github.com/gnolang/gno/tm2/pkg/std"
)

// Client defines the client interface for fetching chain data
type Client interface {
// GetLatestBlockNumber returns the latest block height from the chain
GetLatestBlockNumber() (uint64, error)

// GetBlockTransactions returns the transactions contained
// within the specified block, if any
GetBlockTransactions(uint64) ([]std.Tx, error)
// GetBlock returns the transactions contained
// within the specified block, if any, apart from the block height and
// its timestamp in milliseconds.
GetBlock(uint64) (*Block, error)
}

type Block struct {
Txs []std.Tx
Height uint64
Timestamp int64
}
13 changes: 10 additions & 3 deletions backup/client/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ package http
import (
"fmt"

_ "github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/tm2/pkg/amino"
rpcClient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
"github.com/gnolang/gno/tm2/pkg/std"

_ "github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/tx-archive/backup/client"
)

var _ client.Client = &Client{}

// Client is the TM2 HTTP client
type Client struct {
client rpcClient.Client
Expand Down Expand Up @@ -40,7 +43,7 @@ func (c *Client) GetLatestBlockNumber() (uint64, error) {
return uint64(status.SyncInfo.LatestBlockHeight), nil
}

func (c *Client) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) {
func (c *Client) GetBlock(blockNum uint64) (*client.Block, error) {
// Fetch the block
blockNumInt64 := int64(blockNum)

Expand Down Expand Up @@ -68,5 +71,9 @@ func (c *Client) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) {
txs = append(txs, tx)
}

return txs, nil
return &client.Block{
Timestamp: block.Block.Time.UnixMilli(),
Height: blockNum,
Txs: txs,
}, nil
}
14 changes: 8 additions & 6 deletions backup/mock_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package backup

import "github.com/gnolang/gno/tm2/pkg/std"
import (
"github.com/gnolang/tx-archive/backup/client"
)

type (
getLatestBlockNumberDelegate func() (uint64, error)
getBlockTransactionsDelegate func(uint64) ([]std.Tx, error)
getBlockDelegate func(uint64) (*client.Block, error)
)

type mockClient struct {
getLatestBlockNumberFn getLatestBlockNumberDelegate
getBlockTransactionsFn getBlockTransactionsDelegate
getBlockFn getBlockDelegate
}

func (m *mockClient) GetLatestBlockNumber() (uint64, error) {
Expand All @@ -20,9 +22,9 @@ func (m *mockClient) GetLatestBlockNumber() (uint64, error) {
return 0, nil
}

func (m *mockClient) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) {
if m.getBlockTransactionsFn != nil {
return m.getBlockTransactionsFn(blockNum)
func (m *mockClient) GetBlock(blockNum uint64) (*client.Block, error) {
if m.getBlockFn != nil {
return m.getBlockFn(blockNum)
}

return nil, nil
Expand Down
41 changes: 40 additions & 1 deletion restore/restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import (
"testing"
"time"

_ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // this is needed to load amino types
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/tx-archive/log/noop"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gnolang/tx-archive/log/noop"
"github.com/gnolang/tx-archive/types"
)

func TestRestore_ExecuteRestore(t *testing.T) {
Expand Down Expand Up @@ -145,3 +150,37 @@ func TestRestore_ExecuteRestore_Watch(t *testing.T) {
assert.Equal(t, exampleTx, tx)
}
}

func TestRestore_BackwardCompatible(t *testing.T) {
t.Parallel()

oldTx := `{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9",
"send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":
["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},{"@type":"/vm.m_call","caller":
"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":
"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},
{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9","send":"","pkg_path":
"gno.land/r/demo/wugnot","func":"Approve","args":["g14fclvfqynndp0l6kpyxkpgn4sljw9rr96hz46l",
"18446744073709551615"]},{"@type":"/vm.m_call","caller":
"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9","send":"","pkg_path":"gno.land/r/gnoswap/v2/position",
"func":"CollectFee","args":["26"]},{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9",
"send":"","pkg_path":"gno.land/r/gnoswap/v2/staker","func":"CollectReward","args":["26","true"]},
{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9","send":"","pkg_path":
"gno.land/r/demo/wugnot","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8",
"18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9",
"send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8",
"18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1ngywvql2ql7t8uzl63w60eqcejkwg4rm4lxdw9","send":"",
"pkg_path":"gno.land/r/gnoswap/v2/position","func":"CollectFee","args":["146"]}],"fee":{"gas_wanted":"100000000",
"gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1",
"value":"Atgv/+TCwlR+jzjx94p4Ik0IuGET4J/q2q9ciaL4UOQh"},
"signature":"iVfxsF37nRtgqyq9tMRMhyFLxp5RVdpI1r0mSHLmdg5aly0w82/in0ECey2PSpRk2UQ/fCtMpyOzaqIXiVKC4Q=="}],
"memo":""},"blockNum":"1194460"}`

var out types.TxData
err := amino.UnmarshalJSON([]byte(oldTx), &out)
require.NoError(t, err)

require.Zero(t, out.Timestamp)
require.Equal(t, uint64(0x1239dc), out.BlockNum)
require.Len(t, out.Tx.Msgs, 8)
}
7 changes: 6 additions & 1 deletion types/types.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package types

import "github.com/gnolang/gno/tm2/pkg/std"
import (
"github.com/gnolang/gno/tm2/pkg/std"
)

// TxData contains the single block transaction,
// along with the block information
type TxData struct {
Tx std.Tx `json:"tx"`
BlockNum uint64 `json:"blockNum"`

// Timestamp contains the block creation time in unix milliseconds
Timestamp int64 `json:"bt"` //nolint:tagliatelle // this name reduces disk space usage
}

0 comments on commit 83b3c2f

Please sign in to comment.