1
0
Fork 0
mirror of https://github.com/eosswedenorg/thalos synced 2026-07-03 11:53:41 +02:00

Compare commits

..

No commits in common. "master" and "v1.1.0-rc1" have entirely different histories.

61 changed files with 1099 additions and 2420 deletions

View file

@ -1,82 +0,0 @@
name: Development build
permissions:
contents: write
on:
push:
branches: [ dev ]
jobs:
cross-compile:
strategy:
fail-fast: false
matrix:
os: [ linux, freebsd ]
arch: [ 386, amd64, arm, arm64 ]
name: ${{matrix.os}} (${{matrix.arch}})
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22
- name: compile
id: compile
run: |
VER=$(cat Makefile | sed -n 's/^PROGRAM_VERSION\s*\??=\s*//p')
GOOS=${{matrix.os}} GOARCH=${{matrix.arch}} PROGRAM_VERSION="${VER}-$(echo $GITHUB_SHA | cut -b -8)" make build tools
- name: Upload thalos-server
uses: actions/upload-artifact@v4
with:
name: thalos-server-${{github.sha}}-${{matrix.os}}-${{matrix.arch}}
path: build/thalos-server
retention-days: 7
- name: Upload thalos-tools
uses: actions/upload-artifact@v4
with:
name: thalos-tools-${{github.sha}}-${{matrix.os}}-${{matrix.arch}}
path: build/thalos-tools
retention-days: 7
# Build thalos binaries that are linked with musl libc.
musl:
strategy:
fail-fast: false
matrix:
arch: [ 386, amd64, arm, arm64 ]
runs-on: ubuntu-latest
name: musl (${{ matrix.arch }})
container:
image: golang:1.22-alpine3.19
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: apk add make
- name: compile
id: compile
run: |
VER=$(cat Makefile | sed -n 's/^PROGRAM_VERSION\s*\??=\s*//p')
GOOS=${{matrix.os}} GOARCH=${{matrix.arch}} PROGRAM_VERSION="${VER}-$(echo $GITHUB_SHA | cut -b -8)" make build tools
- name: Upload thalos-server
uses: actions/upload-artifact@v4
with:
name: thalos-server-${{github.sha}}-linux-${{matrix.arch}}-musl
path: build/thalos-server
retention-days: 7
- name: Upload thalos-tools
uses: actions/upload-artifact@v4
with:
name: thalos-tools-${{github.sha}}-linux-${{matrix.arch}}-musl
path: build/thalos-tools
retention-days: 7

View file

@ -1,6 +1,4 @@
name: Package name: Package
permissions:
contents: write
on: on:
release: release:
@ -21,7 +19,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.22 go-version: 1.21
- name: compile - name: compile
id: compile id: compile
@ -29,7 +27,7 @@ jobs:
mkdir -p build/bundle/{bin,logs} mkdir -p build/bundle/{bin,logs}
GOOS=${{matrix.os}} GOARCH=${{matrix.arch}} make -e DESTDIR=build/bundle PREFIX= CFGDIR= install install-scripts GOOS=${{matrix.os}} GOARCH=${{matrix.arch}} make -e DESTDIR=build/bundle PREFIX= CFGDIR= install install-scripts
tar -C build/bundle -z -cf build/bundle.tar.gz . tar -C build/bundle -z -cf build/bundle.tar.gz .
echo "version=$(sed -n 's/.*PROGRAM_VERSION.*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT" echo "version=$(sed -n 's/.*PROGRAM_VERSION\s*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
- name: Upload thalos-server - name: Upload thalos-server
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
@ -61,61 +59,6 @@ jobs:
asset_path: build/bundle.tar.gz asset_path: build/bundle.tar.gz
asset_content_type: application/tar+gzip asset_content_type: application/tar+gzip
# Build thalos binaries that are linked with musl libc.
musl:
strategy:
fail-fast: false
matrix:
arch: [ 386, amd64, arm, arm64 ]
runs-on: ubuntu-latest
name: Build musl (${{ matrix.arch }})
container:
image: golang:1.22-alpine3.19
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: apk add make
- name: compile
id: compile
run: |
mkdir -p build/bundle/{bin,logs}
GOARCH=${{matrix.arch}} make -e DESTDIR=build/bundle PREFIX= CFGDIR= install install-scripts
tar -C build/bundle -z -cf build/bundle.tar.gz .
echo "version=$(sed -n 's/.*PROGRAM_VERSION.*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
- name: Upload thalos-server
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_name: thalos-server-${{steps.compile.outputs.version}}-linux-${{matrix.arch}}-musl
asset_path: build/thalos-server
asset_content_type: application/octal-stream
- name: Upload thalos-tools
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_name: thalos-tools-${{steps.compile.outputs.version}}-linux-${{matrix.arch}}-musl
asset_path: build/thalos-tools
asset_content_type: application/octal-stream
- name: Upload bundle
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_name: thalos-${{steps.compile.outputs.version}}-linux-${{matrix.arch}}-musl.tar.gz
asset_path: build/bundle.tar.gz
asset_content_type: application/tar+gzip
package-ubuntu: package-ubuntu:
strategy: strategy:
fail-fast: false fail-fast: false
@ -129,7 +72,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.22 go-version: 1.21
- name: Install build dependencies - name: Install build dependencies
run: | run: |

View file

@ -1,8 +1,5 @@
name: Test name: Test
permissions:
contents: read
on: on:
- push - push
- pull_request - pull_request
@ -13,10 +10,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go-version: ["1.22"] go-version: ["1.20", "1.21"]
arch: [ 386, amd64 ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Test (${{matrix.arch}} go v${{ matrix.go-version }}) name: Test (go v${{ matrix.go-version }})
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -25,27 +21,8 @@ jobs:
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Test
run: GOARCH=${{matrix.arch}} go test -v ./...
- name: Test API
run: cd api; GOARCH=${{matrix.arch}} go test -v ./...
test-alpine:
strategy:
fail-fast: false
matrix:
tag: [ "1.22-alpine3.19"]
runs-on: ubuntu-latest
name: Test alpine (${{ matrix.tag }})
container:
image: golang:${{ matrix.tag }}
steps:
- uses: actions/checkout@v4
- name: Test - name: Test
run: go test -v ./... run: go test -v ./...
- name: Test API - name: Test API
run: cd api; go test -v ./... run: cd api; go test -v ./...

1
.gitignore vendored
View file

@ -2,4 +2,3 @@
/config.yml /config.yml
/.pc /.pc
/.vscode/ /.vscode/
/vendor

View file

@ -1,32 +1,24 @@
GO=go GO=go
GOLDFLAGS=-v -s -w -X main.VersionString=$(PROGRAM_VERSION) GOLDFLAGS=-v -s -w -X main.VersionString=$(PROGRAM_VERSION)
GOBUILDFLAGS+=-v -p $(shell nproc) -ldflags="$(GOLDFLAGS)" GOBUILDFLAGS=-v -p $(shell nproc) -ldflags="$(GOLDFLAGS)"
PROGRAM=thalos-server PROGRAM=thalos-server
PROGRAM_VERSION ?= 1.1.9 PROGRAM_VERSION=1.0.0-rc1
PREFIX=/usr/local PREFIX=/usr/local
BINDIR=$(PREFIX)/bin BINDIR=$(PREFIX)/bin
CFGDIR=$(PREFIX)/etc/thalos CFGDIR=$(PREFIX)/etc/thalos
DOCKER_IMAGE_REPO ?= ghcr.io/eosswedenorg/thalos
DOCKER_IMAGE_TAG ?= $(PROGRAM_VERSION)
.PHONY: build build/$(PROGRAM) build/thalos-tools test docker-image docker-publish .PHONY: build build/$(PROGRAM) build/thalos-tools test
build: build/$(PROGRAM) build: build/$(PROGRAM)
build/$(PROGRAM) : build/$(PROGRAM) :
$(GO) build $(GOBUILDFLAGS) -o $@ ./cmd/thalos/ $(GO) build $(GOBUILDFLAGS) -o $@ cmd/thalos/main.go cmd/thalos/server.go
tools : build/thalos-tools tools : build/thalos-tools
build/thalos-tools : build/thalos-tools :
$(GO) build $(GOBUILDFLAGS) -o $@ ./cmd/tools/ $(GO) build $(GOBUILDFLAGS) -o $@ $(shell find cmd/tools -type f -name *.go)
docker-image:
docker image build --build-arg VERSION=$(PROGRAM_VERSION) -t $(DOCKER_IMAGE_REPO):$(DOCKER_IMAGE_TAG) docker
docker-publish:
docker image push $(DOCKER_IMAGE_REPO):$(DOCKER_IMAGE_TAG)
install: build tools install: build tools
install -D build/$(PROGRAM) $(DESTDIR)$(BINDIR)/$(PROGRAM) install -D build/$(PROGRAM) $(DESTDIR)$(BINDIR)/$(PROGRAM)
@ -41,6 +33,6 @@ build-deb:
test: test:
$(GO) test -v ./... $(GO) test -v ./...
cd api; $(GO) test -v ./...
clean : clean :
$(RM) -fr build $(RM) -fr build

View file

@ -1,21 +1,12 @@
# Thalos # Thalos
[![Test](https://github.com/eosswedenorg/thalos/actions/workflows/test.yml/badge.svg)](https://github.com/eosswedenorg/thalos/actions/workflows/test.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/eosswedenorg/thalos)](https://goreportcard.com/report/github.com/eosswedenorg/thalos)
Thalos is a application that makes it easy for users to stream blockchain data from an Antelope SHIP node. Thalos is a application that makes it easy for users to stream blockchain data from an Antelope SHIP node.
Consult the [documentation](https://thalos.waxsweden.org/docs) for more information. Consult the [documentation](https://thalos.waxsweden.org/docs) for more information.
Join the discussion on [telegram](https://t.me/antelopethalos)
## Docker images
Docker images can be found [here](https://github.com/eosswedenorg/thalos/pkgs/container/thalos)
## Compiling ## Compiling
You will need golang version `1.21` or later to compile the source. You will need golang version `1.20` or later to compile the source.
Compile using make: Compile using make:

View file

@ -2,7 +2,7 @@
## Usage ## Usage
The api is designed with go channels. The api is designed with callback functions that are called when messages arrive
## Example ## Example
@ -31,7 +31,7 @@ func main() {
ChainID: "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", // Wax mainnet. ChainID: "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", // Wax mainnet.
}) })
// Create thalos client // Create client
codec, err := message.GetCodec("json") codec, err := message.GetCodec("json")
if err != nil { if err != nil {
fmt.Println("Failed to get json codec") fmt.Println("Failed to get json codec")
@ -81,6 +81,9 @@ func main() {
} }
} }
} }
``` ```
## Message channels and types ## Message channels and types

View file

@ -55,13 +55,13 @@ func TestChannel_Is(t *testing.T) {
func TestChannel_Format(t *testing.T) { func TestChannel_Format(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
c Channel
delim string delim string
want string want string
c Channel
}{ }{
{"Empty", ":", "", Channel{}}, {"Empty", Channel{}, ":", ""},
{"Alot#1", "-", "one-two-three", Channel{"one", "two", "three"}}, {"Alot#1", Channel{"one", "two", "three"}, "-", "one-two-three"},
{"Alot#2", ":", "first:second", Channel{"first", "second"}}, {"Alot#2", Channel{"first", "second"}, ":", "first:second"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -75,11 +75,11 @@ func TestChannel_Format(t *testing.T) {
func TestChannel_String(t *testing.T) { func TestChannel_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
want string
c Channel c Channel
want string
}{ }{
{"Empty", "", Channel{}}, {"Empty", Channel{}, ""},
{"Alot", "one/two/three", Channel{"one", "two", "three"}}, {"Alot", Channel{"one", "two", "three"}, "one/two/three"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -93,13 +93,13 @@ func TestChannel_String(t *testing.T) {
func TestChannel_Type(t *testing.T) { func TestChannel_Type(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
want string
c Channel c Channel
want string
}{ }{
{"Empty", "unknown", Channel{}}, {"Empty", Channel{}, "unknown"},
{"Heartbeat", "heartbeat", HeartbeatChannel}, {"Heartbeat", HeartbeatChannel, "heartbeat"},
{"Transaction", "transactions", TransactionChannel}, {"Transaction", TransactionChannel, "transactions"},
{"Actions", "actions", ActionChannel{}.Channel()}, {"Actions", ActionChannel{}.Channel(), "actions"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View file

@ -11,7 +11,7 @@ import (
type handler func([]byte) type handler func([]byte)
// Client reads and decodes messages from a reader and posts them to a go channel // Client reads and decodes messages from a reader and posts thems to a go channel
type Client struct { type Client struct {
reader Reader reader Reader
decoder message.Decoder decoder message.Decoder
@ -35,7 +35,6 @@ func (c *Client) Channel() <-chan any {
return c.channel return c.channel
} }
// Helper method to post a message to a channel with timeout.
func (c *Client) post(msg any) { func (c *Client) post(msg any) {
select { select {
case <-time.After(time.Second): case <-time.After(time.Second):
@ -47,7 +46,7 @@ func (c *Client) worker(channel Channel, h handler) {
for { for {
payload, err := c.reader.Read(channel) payload, err := c.reader.Read(channel)
if err != nil { if err != nil {
// Don't report EOF as an error because it is used // Dont report EOF as an error because it is used
// by readers to signal an graceful end of input. // by readers to signal an graceful end of input.
if err != io.EOF { if err != io.EOF {
c.post(err) c.post(err)
@ -60,7 +59,7 @@ func (c *Client) worker(channel Channel, h handler) {
} }
// Helper method to decode a message and post and error on the channel if it fails. // Helper method to decode a message and post and error on the channel if it fails.
// Returns true if successful. False otherwise // Returns true if successfull. false otherwise
func (c *Client) decode(payload []byte, msg any) bool { func (c *Client) decode(payload []byte, msg any) bool {
if err := c.decoder(payload, msg); err != nil { if err := c.decoder(payload, msg); err != nil {
c.post(err) c.post(err)
@ -110,19 +109,19 @@ func (c *Client) hbHandler(payload []byte) {
} }
func (c *Client) sub(channel Channel) error { func (c *Client) sub(channel Channel) error {
var h handler var handler handler
switch channel.Type() { switch channel.Type() {
case RollbackChannel.Type(): case RollbackChannel.Type():
h = c.rollbackHandler handler = c.rollbackHandler
case TransactionChannel.Type(): case TransactionChannel.Type():
h = c.transactionHandler handler = c.transactionHandler
case HeartbeatChannel.Type(): case HeartbeatChannel.Type():
h = c.hbHandler handler = c.hbHandler
case ActionChannel{}.Channel().Type(): case ActionChannel{}.Channel().Type():
h = c.actHandler handler = c.actHandler
case TableDeltaChannel{}.Channel().Type(): case TableDeltaChannel{}.Channel().Type():
h = c.tableDeltaHandler handler = c.tableDeltaHandler
default: default:
return fmt.Errorf("invalid channel type. %s", channel.Type()) return fmt.Errorf("invalid channel type. %s", channel.Type())
} }
@ -131,7 +130,7 @@ func (c *Client) sub(channel Channel) error {
c.wg.Add(1) c.wg.Add(1)
go func() { go func() {
defer c.wg.Done() defer c.wg.Done()
c.worker(channel, h) c.worker(channel, handler)
}() }()
return nil return nil
@ -153,7 +152,7 @@ func (c *Client) Run() {
func (c *Client) Close() error { func (c *Client) Close() error {
err := c.reader.Close() err := c.reader.Close()
// Wait for all goroutines to finish before closing channel. // Wait for all goroutines before closing channel.
c.wg.Wait() c.wg.Wait()
close(c.channel) close(c.channel)
return err return err

View file

@ -4,9 +4,9 @@ go 1.20
require ( require (
github.com/alicebob/miniredis/v2 v2.30.2 github.com/alicebob/miniredis/v2 v2.30.2
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7
github.com/go-redis/redismock/v9 v9.2.0 github.com/go-redis/redismock/v9 v9.2.0
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.4.0
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/ugorji/go/codec v1.2.12 github.com/ugorji/go/codec v1.2.12
) )

View file

@ -14,6 +14,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7 h1:rLPu++RHaxg4WmUOXeWYioZuafWs0PVcYuvzOWbOJjk=
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7/go.mod h1:eNUkVOymzgl0lViUhmm08PkutzqLnOQ6Dr+RUnf+Mq0=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw= github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0= github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
@ -33,8 +35,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d h1:nju7jR1Kf210tArPT6l//HlfLLFnvje1BWl5TSRsohQ=
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d/go.mod h1:W0TaKyg3kDqWmFUxlax3qAls/lRdo12EseM6f4f0dzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=

View file

@ -3,12 +3,12 @@ package json
import ( import (
"time" "time"
jsontime "github.com/eosswedenorg-go/jsontime/v2"
"github.com/eosswedenorg/thalos/api/message" "github.com/eosswedenorg/thalos/api/message"
"github.com/shufflingpixels/jsontime-go"
) )
func createCodec() message.Codec { func createCodec() message.Codec {
json_codec := jsontime.New("2006-01-02T15:04:05.000", time.UTC) json_codec := jsontime.ConfigWithCustomTimeFormat
return message.Codec{ return message.Codec{
Encoder: json_codec.Marshal, Encoder: json_codec.Marshal,
@ -17,6 +17,9 @@ func createCodec() message.Codec {
} }
func init() { func init() {
// Set timeformat used by SHIP api
jsontime.SetDefaultTimeFormat("2006-01-02T15:04:05.000", time.UTC)
// Register the json codec. // Register the json codec.
message.RegisterCodec("json", createCodec()) message.RegisterCodec("json", createCodec())
} }

View file

@ -16,7 +16,6 @@ func TestJson_EncodeActionTrace(t *testing.T) {
Name: "transfer", Name: "transfer",
Contract: "eosio", Contract: "eosio",
Receiver: "account2", Receiver: "account2",
FirstReceiver: true,
Receipt: &message.ActionReceipt{ Receipt: &message.ActionReceipt{
Receiver: "account2", Receiver: "account2",
ActDigest: "0a2c71dba327cf13a107d3a4f91c9c98f510a8fbbb483b233e222033f13a3e36", ActDigest: "0a2c71dba327cf13a107d3a4f91c9c98f510a8fbbb483b233e222033f13a3e36",
@ -44,7 +43,7 @@ func TestJson_EncodeActionTrace(t *testing.T) {
Return: []byte{0xde, 0xad, 0xbe, 0xef}, Return: []byte{0xde, 0xad, 0xbe, 0xef},
} }
expected := `{"tx_id":"ed3b8e853647971cf8296f004c3a1aeac255f082b2cb3c12cc3222e2d7c174ab","blocknum":267372365,"blocktimestamp":"2003-03-21T17:23:09.500","receipt":{"receiver":"account2","act_digest":"0a2c71dba327cf13a107d3a4f91c9c98f510a8fbbb483b233e222033f13a3e36","global_sequence":2329381932,"recv_sequence":22,"auth_sequence":[{"account":"account1","sequence":1382772}],"code_sequence":1122,"abi_sequence":12352},"name":"transfer","contract":"eosio","receiver":"account2","first_receiver":true,"data":{"from":"account1","quantity":"1000.0000 WAX","to":"account2"},"authorization":[{"actor":"account1","permission":"active"}],"except":"errstr","error":2,"return":"3q2+7w=="}` expected := `{"tx_id":"ed3b8e853647971cf8296f004c3a1aeac255f082b2cb3c12cc3222e2d7c174ab","blocknum":267372365,"blocktimestamp":"2003-03-21T17:23:09.500","receipt":{"receiver":"account2","act_digest":"0a2c71dba327cf13a107d3a4f91c9c98f510a8fbbb483b233e222033f13a3e36","global_sequence":2329381932,"recv_sequence":22,"auth_sequence":[{"account":"account1","sequence":1382772}],"code_sequence":1122,"abi_sequence":12352},"name":"transfer","contract":"eosio","receiver":"account2","data":{"from":"account1","quantity":"1000.0000 WAX","to":"account2"},"authorization":[{"actor":"account1","permission":"active"}],"except":"errstr","error":2,"return":"3q2+7w=="}`
data, err := createCodec().Encoder(msg) data, err := createCodec().Encoder(msg)
assert.NoError(t, err) assert.NoError(t, err)
@ -59,7 +58,6 @@ func TestJson_DecodeActionTrace(t *testing.T) {
Name: "transfer", Name: "transfer",
Contract: "eosio", Contract: "eosio",
Receiver: "account2", Receiver: "account2",
FirstReceiver: true,
Receipt: &message.ActionReceipt{ Receipt: &message.ActionReceipt{
Receiver: "account2", Receiver: "account2",
ActDigest: "f2f682847fd5bf00fb315b075dc00b4ff0ce18776758077b86a233dea49dc047", ActDigest: "f2f682847fd5bf00fb315b075dc00b4ff0ce18776758077b86a233dea49dc047",
@ -87,7 +85,7 @@ func TestJson_DecodeActionTrace(t *testing.T) {
Return: []byte{0xde, 0xad, 0xbe, 0xef}, Return: []byte{0xde, 0xad, 0xbe, 0xef},
} }
input := `{"tx_id":"952989f7464237b6cf9926e533ecd331df6794ed07564bd052bc368cbd65b4bc","blocknum":8723971,"blocktimestamp":"2024-06-21T08:08:26.500","receipt":{"receiver":"account2","act_digest":"f2f682847fd5bf00fb315b075dc00b4ff0ce18776758077b86a233dea49dc047","global_sequence":287328,"recv_sequence":42,"auth_sequence":[{"account":"account1","sequence":271877283}],"code_sequence":237823,"abi_sequence":68323},"name":"transfer","contract":"eosio","receiver":"account2","first_receiver":true,"data":{"from":"account1","quantity":"1000.0000 WAX","to":"account2"},"authorization":[{"actor":"account1","permission":"active"}],"except":"errstr","error":2,"return":"3q2+7w=="}` input := `{"tx_id":"952989f7464237b6cf9926e533ecd331df6794ed07564bd052bc368cbd65b4bc","blocknum":8723971,"blocktimestamp":"2024-06-21T08:08:26.500","receipt":{"receiver":"account2","act_digest":"f2f682847fd5bf00fb315b075dc00b4ff0ce18776758077b86a233dea49dc047","global_sequence":287328,"recv_sequence":42,"auth_sequence":[{"account":"account1","sequence":271877283}],"code_sequence":237823,"abi_sequence":68323},"name":"transfer","contract":"eosio","receiver":"account2","data":{"from":"account1","quantity":"1000.0000 WAX","to":"account2"},"authorization":[{"actor":"account1","permission":"active"}],"except":"errstr","error":2,"return":"3q2+7w=="}`
msg := message.ActionTrace{} msg := message.ActionTrace{}
err := createCodec().Decoder([]byte(input), &msg) err := createCodec().Decoder([]byte(input), &msg)
@ -136,7 +134,6 @@ func TestJson_EncodeTransactionTrace(t *testing.T) {
Timestamp: ts, Timestamp: ts,
Receiver: "coolgame", Receiver: "coolgame",
Contract: "coolgame", Contract: "coolgame",
FirstReceiver: true,
Name: "addpoints", Name: "addpoints",
Authorization: []message.PermissionLevel{ Authorization: []message.PermissionLevel{
{ {
@ -156,7 +153,7 @@ func TestJson_EncodeTransactionTrace(t *testing.T) {
Error: 2, Error: 2,
} }
expected := `{"id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","status":"executed","cpu_usage_us":442,"net_usage_words":16,"elapsed":22,"net_usage":128,"scheduled":true,"action_traces":[{"tx_id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","name":"mine","contract":"coolgame","receiver":"actor01","first_receiver":false,"data":{"equipment_id":1234,"location_id":5445453},"authorization":[{"actor":"actor01","permission":"active"}],"except":"","error":0,"return":"CPE="},{"tx_id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","name":"addpoints","contract":"coolgame","receiver":"coolgame","first_receiver":true,"data":{"points":"1023.0423 SCAM"},"authorization":[{"actor":"coolgame","permission":"usrpoints"}],"except":"some error string","error":2,"return":"/wI="}],"except":"errstr","error":2}` expected := `{"id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","status":"executed","cpu_usage_us":442,"net_usage_words":16,"elapsed":22,"net_usage":128,"scheduled":true,"action_traces":[{"tx_id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","name":"mine","contract":"coolgame","receiver":"actor01","data":{"equipment_id":1234,"location_id":5445453},"authorization":[{"actor":"actor01","permission":"active"}],"except":"","error":0,"return":"CPE="},{"tx_id":"ed04516bdd1194aa5f0ab4c8c5445eec542c17f45a85bb3e9e4bc33e1a2486f8","blocknum":283781923,"blocktimestamp":"2029-02-08T15:10:05.500","name":"addpoints","contract":"coolgame","receiver":"coolgame","data":{"points":"1023.0423 SCAM"},"authorization":[{"actor":"coolgame","permission":"usrpoints"}],"except":"some error string","error":2,"return":"/wI="}],"except":"errstr","error":2}`
data, err := createCodec().Encoder(msg) data, err := createCodec().Encoder(msg)
assert.NoError(t, err) assert.NoError(t, err)
@ -168,7 +165,7 @@ func TestJson_DecodeTransactionTrace(t *testing.T) {
ts := time.Unix(1730755743, int64(time.Millisecond)*500).UTC() ts := time.Unix(1730755743, int64(time.Millisecond)*500).UTC()
block_num := uint32(2378197231) block_num := uint32(2378197231)
input := `{"id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","status":"executed","cpu_usage_us":442,"net_usage_words":16,"elapsed":22,"net_usage":128,"scheduled":true,"action_traces":[{"tx_id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","receipt":{"receiver":"actor01","act_digest":"bf68a227a617aa9138215d31a2847005175f01151c6feeaeb6513fe2d090ff3a","global_sequence":47322,"recv_sequence":23289,"auth_sequence":[{"account":"actor01","sequence":56637232}],"code_sequence":98272,"abi_sequence":6823762},"name":"mine","contract":"coolgame","receiver":"actor01","first_receiver":false,"data":{"equipment_id":1234,"location_id":5445453},"authorization":[{"actor":"actor01","permission":"active"}],"except":"","error":2,"return":"AQI="},{"tx_id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","name":"addpoints","contract":"coolgame","receiver":"coolgame","first_receiver":true,"data":{"points":"1023.0423 SCAM"},"authorization":[{"actor":"coolgame","permission":"usrpoints"}],"except":"","error":2,"return":"CPE="}],"except":"errstr","error":2}` input := `{"id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","status":"executed","cpu_usage_us":442,"net_usage_words":16,"elapsed":22,"net_usage":128,"scheduled":true,"action_traces":[{"tx_id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","receipt":{"receiver":"actor01","act_digest":"bf68a227a617aa9138215d31a2847005175f01151c6feeaeb6513fe2d090ff3a","global_sequence":47322,"recv_sequence":23289,"auth_sequence":[{"account":"actor01","sequence":56637232}],"code_sequence":98272,"abi_sequence":6823762},"name":"mine","contract":"","receiver":"actor01","data":{"equipment_id":1234,"location_id":5445453},"authorization":[{"actor":"actor01","permission":"active"}],"except":"","error":2,"return":"AQI="},{"tx_id":"f58bf8a0137fcea644dbc2b0cc5b6a017a848cd33b2e924703e7e3c6d1ca0c2e","blocknum":2378197231,"blocktimestamp":"2024-11-04T21:29:03.500","name":"addpoints","contract":"","receiver":"coolgame","data":{"points":"1023.0423 SCAM"},"authorization":[{"actor":"coolgame","permission":"usrpoints"}],"except":"","error":2,"return":"CPE="}],"except":"errstr","error":2}`
expected := message.TransactionTrace{ expected := message.TransactionTrace{
Timestamp: ts, Timestamp: ts,
@ -186,7 +183,6 @@ func TestJson_DecodeTransactionTrace(t *testing.T) {
BlockNum: block_num, BlockNum: block_num,
Timestamp: ts, Timestamp: ts,
Receiver: "actor01", Receiver: "actor01",
Contract: "coolgame",
Name: "mine", Name: "mine",
Receipt: &message.ActionReceipt{ Receipt: &message.ActionReceipt{
Receiver: "actor01", Receiver: "actor01",
@ -220,8 +216,6 @@ func TestJson_DecodeTransactionTrace(t *testing.T) {
BlockNum: block_num, BlockNum: block_num,
Timestamp: ts, Timestamp: ts,
Receiver: "coolgame", Receiver: "coolgame",
Contract: "coolgame",
FirstReceiver: true,
Name: "addpoints", Name: "addpoints",
Authorization: []message.PermissionLevel{ Authorization: []message.PermissionLevel{
{ {

View file

@ -14,7 +14,7 @@ func createCodec() message.Codec {
handle.MapType = reflect.TypeOf(map[string]any(nil)) handle.MapType = reflect.TypeOf(map[string]any(nil))
handle.Canonical = true handle.Canonical = true
// Weird name but this is needed for the newest version of msgpack // Wierd name but this is needed for the newest version of msgpack
// that has support for time and string datatypes etc. // that has support for time and string datatypes etc.
handle.WriteExt = true handle.WriteExt = true

View file

@ -55,7 +55,7 @@ func TestMsgpack_EncodeActionTrace(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
expected := []byte{ expected := []byte{
0x8d, 0xad, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x8c, 0xad, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x91, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x91,
0x82, 0xa5, 0x61, 0x63, 0x74, 0x6f, 0x72, 0xa6, 0x82, 0xa5, 0x61, 0x63, 0x74, 0x6f, 0x72, 0xa6,
0x6d, 0x79, 0x67, 0x61, 0x6d, 0x65, 0xaa, 0x70, 0x6d, 0x79, 0x67, 0x61, 0x6d, 0x65, 0xaa, 0x70,
@ -85,8 +85,6 @@ func TestMsgpack_EncodeActionTrace(t *testing.T) {
0x6e, 0x74, 0x32, 0xa5, 0x65, 0x72, 0x72, 0x6f, 0x6e, 0x74, 0x32, 0xa5, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x02, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70, 0x72, 0x02, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70,
0x74, 0xa6, 0x65, 0x72, 0x72, 0x73, 0x74, 0x72, 0x74, 0xa6, 0x65, 0x72, 0x72, 0x73, 0x74, 0x72,
0xae, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x72,
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0xc2,
0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa8, 0x73, 0x65, 0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa8, 0x73, 0x65,
0x6c, 0x6c, 0x69, 0x74, 0x65, 0x6d, 0xa7, 0x72, 0x6c, 0x6c, 0x69, 0x74, 0x65, 0x6d, 0xa7, 0x72,
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x87, 0xac, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x87, 0xac,
@ -195,29 +193,11 @@ func TestMsgpack_EncodeHeartbeat(t *testing.T) {
data, err := createCodec().Encoder(msg) data, err := createCodec().Encoder(msg)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, data, []byte{ assert.Equal(t, data, []byte{0x83, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x4, 0xd2, 0xad, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x4, 0xd3, 0xba, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x4, 0xd4})
0x83, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e,
0x75, 0x6d, 0xcd, 0x04, 0xd2, 0xad, 0x68, 0x65,
0x61, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x6e, 0x75, 0x6d, 0xcd, 0x04, 0xd3, 0xba, 0x6c,
0x61, 0x73, 0x74, 0x5f, 0x69, 0x72, 0x72, 0x65,
0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65,
0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75,
0x6d, 0xcd, 0x04, 0xd4,
})
} }
func TestMsgpack_DecodeHeartbeat(t *testing.T) { func TestMsgpack_DecodeHeartbeat(t *testing.T) {
data := []byte{ data := []byte{0x83, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x03, 0xe8, 0xad, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x0b, 0xb8, 0xba, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xcd, 0x04, 0x06}
0x83, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e,
0x75, 0x6d, 0xcd, 0x03, 0xe8, 0xad, 0x68, 0x65,
0x61, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x6e, 0x75, 0x6d, 0xcd, 0x0b, 0xb8, 0xba, 0x6c,
0x61, 0x73, 0x74, 0x5f, 0x69, 0x72, 0x72, 0x65,
0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65,
0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75,
0x6d, 0xcd, 0x04, 0x06,
}
expected := message.HeartBeat{ expected := message.HeartBeat{
BlockNum: 1000, BlockNum: 1000,
@ -289,123 +269,104 @@ func TestMsgpack_EncodeTransactionTrace(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
expected := []byte{ expected := []byte{
0x8c, 0xad, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x8c, 0xad, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x92,
0x73, 0x92, 0x8d, 0xad, 0x61, 0x75, 0x74, 0x8c, 0xad, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x91,
0x69, 0x6f, 0x6e, 0x91, 0x82, 0xa5, 0x61, 0x82, 0xa5, 0x61, 0x63, 0x74, 0x6f, 0x72, 0xaa,
0x63, 0x74, 0x6f, 0x72, 0xaa, 0x61, 0x63, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x31, 0x32, 0x31,
0x74, 0x6f, 0x72, 0x31, 0x32, 0x31, 0x32, 0x32, 0x33, 0xaa, 0x70, 0x65, 0x72, 0x6d, 0x69,
0x33, 0xaa, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0xa6, 0x61, 0x63,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0xa6, 0x61, 0x74, 0x69, 0x76, 0x65, 0xa8, 0x62, 0x6c, 0x6f,
0x63, 0x74, 0x69, 0x76, 0x65, 0xa8, 0x62, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0x00, 0xae, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x69, 0x6d, 0x65,
0x00, 0xae, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x6d, 0x70, 0xc0, 0xa8, 0x63,
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0xa7,
0x6d, 0x70, 0xc0, 0xa8, 0x63, 0x6f, 0x6e, 0x73, 0x6b, 0x6a, 0x64, 0x68, 0x32, 0x33, 0xa4,
0x74, 0x72, 0x61, 0x63, 0x74, 0xa7, 0x73, 0x64, 0x61, 0x74, 0x61, 0x81, 0xa3, 0x6b, 0x65,
0x6b, 0x6a, 0x64, 0x68, 0x32, 0x33, 0xa4, 0x79, 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65, 0xa5,
0x64, 0x61, 0x74, 0x61, 0x81, 0xa3, 0x6b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, 0xa6, 0x65,
0x65, 0x79, 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x78, 0x63, 0x65, 0x70, 0x74, 0xa0, 0xa4, 0x6e,
0x65, 0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x61, 0x6d, 0x65, 0xa8, 0x73, 0x65, 0x6c, 0x6c,
0x00, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70, 0x69, 0x74, 0x65, 0x6d, 0xa7, 0x72, 0x65, 0x63,
0x74, 0xa0, 0xae, 0x66, 0x69, 0x72, 0x73, 0x65, 0x69, 0x70, 0x74, 0x87, 0xac, 0x61, 0x62,
0x74, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x69, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x76, 0x65, 0x72, 0xc2, 0xa4, 0x6e, 0x61, 0x63, 0x65, 0xce, 0x00, 0x48, 0xf8, 0x98, 0xaa,
0x6d, 0x65, 0xa8, 0x73, 0x65, 0x6c, 0x6c, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65,
0x69, 0x74, 0x65, 0x6d, 0xa7, 0x72, 0x65, 0x73, 0x74, 0xd9, 0x40, 0x36, 0x37, 0x36, 0x63,
0x63, 0x65, 0x69, 0x70, 0x74, 0x87, 0xac, 0x35, 0x33, 0x33, 0x36, 0x64, 0x65, 0x39, 0x64,
0x61, 0x62, 0x69, 0x5f, 0x73, 0x65, 0x71, 0x35, 0x32, 0x38, 0x64, 0x34, 0x35, 0x36, 0x62,
0x75, 0x65, 0x6e, 0x63, 0x65, 0xce, 0x00, 0x30, 0x31, 0x65, 0x39, 0x37, 0x35, 0x62, 0x31,
0x48, 0xf8, 0x98, 0xaa, 0x61, 0x63, 0x74, 0x30, 0x30, 0x36, 0x65, 0x32, 0x62, 0x64, 0x63,
0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x38, 0x36, 0x63, 0x38, 0x64, 0x35, 0x36, 0x36,
0xd9, 0x40, 0x36, 0x37, 0x36, 0x63, 0x35, 0x33, 0x33, 0x30, 0x33, 0x32, 0x31, 0x65, 0x39,
0x33, 0x33, 0x36, 0x64, 0x65, 0x39, 0x64, 0x62, 0x33, 0x30, 0x39, 0x62, 0x36, 0x33, 0x34,
0x35, 0x32, 0x38, 0x64, 0x34, 0x35, 0x36, 0x66, 0x35, 0x32, 0x33, 0xad, 0x61, 0x75, 0x74,
0x62, 0x30, 0x31, 0x65, 0x39, 0x37, 0x35, 0x68, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x62, 0x31, 0x30, 0x30, 0x36, 0x65, 0x32, 0x63, 0x65, 0x91, 0x82, 0xa7, 0x61, 0x63, 0x63,
0x62, 0x64, 0x63, 0x38, 0x36, 0x63, 0x38, 0x6f, 0x75, 0x6e, 0x74, 0xaa, 0x61, 0x63, 0x74,
0x64, 0x35, 0x36, 0x36, 0x33, 0x33, 0x30, 0x6f, 0x72, 0x31, 0x32, 0x31, 0x32, 0x33, 0xa8,
0x33, 0x32, 0x31, 0x65, 0x39, 0x62, 0x33, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
0x30, 0x39, 0x62, 0x36, 0x33, 0x34, 0x66, 0xce, 0x00, 0x08, 0x5c, 0x39, 0xad, 0x63, 0x6f,
0x35, 0x32, 0x33, 0xad, 0x61, 0x75, 0x74, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65,
0x68, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0xce, 0x01, 0x61, 0xe8, 0x79,
0x6e, 0x63, 0x65, 0x91, 0x82, 0xa7, 0x61, 0xaf, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0xaa, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
0x61, 0x63, 0x74, 0x6f, 0x72, 0x31, 0x32, 0xce, 0x02, 0x12, 0xd7, 0x5d, 0xa8, 0x72, 0x65,
0x31, 0x32, 0x33, 0xa8, 0x73, 0x65, 0x71, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0xa5, 0x65,
0x75, 0x65, 0x6e, 0x63, 0x65, 0xce, 0x00, 0x6f, 0x73, 0x69, 0x6f, 0xad, 0x72, 0x65, 0x63,
0x08, 0x5c, 0x39, 0xad, 0x63, 0x6f, 0x64, 0x76, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x65, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x63, 0x65, 0xce, 0x22, 0x81, 0x80, 0x7a, 0xa8,
0x6e, 0x63, 0x65, 0xce, 0x01, 0x61, 0xe8, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72,
0x79, 0xaf, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0xa5, 0x65, 0x6f, 0x73, 0x69, 0x6f, 0xa6, 0x72,
0x6c, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x65, 0x74, 0x75, 0x72, 0x6e, 0xc0, 0xa5, 0x74,
0x6e, 0x63, 0x65, 0xce, 0x02, 0x12, 0xd7, 0x78, 0x5f, 0x69, 0x64, 0xa0, 0x8b, 0xad, 0x61,
0x5d, 0xa8, 0x72, 0x65, 0x63, 0x65, 0x69, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x76, 0x65, 0x72, 0xa5, 0x65, 0x6f, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x91, 0x82, 0xa5, 0x61,
0x69, 0x6f, 0xad, 0x72, 0x65, 0x63, 0x76, 0x63, 0x74, 0x6f, 0x72, 0xa9, 0x61, 0x63, 0x74,
0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x6f, 0x72, 0x31, 0x34, 0x38, 0x32, 0xaa, 0x70,
0x63, 0x65, 0xce, 0x22, 0x81, 0x80, 0x7a, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
0xa8, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x6e, 0xa6, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65,
0x65, 0x72, 0xa5, 0x65, 0x6f, 0x73, 0x69, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6e, 0x75,
0x6f, 0xa6, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6d, 0x00, 0xae, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x6e, 0xc0, 0xa5, 0x74, 0x78, 0x5f, 0x69, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x64, 0xa0, 0x8c, 0xad, 0x61, 0x75, 0x74, 0x70, 0xc0, 0xa8, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x61, 0x63, 0x74, 0xa7, 0x73, 0x6b, 0x6a, 0x64,
0x69, 0x6f, 0x6e, 0x91, 0x82, 0xa5, 0x61, 0x68, 0x32, 0x34, 0xa4, 0x64, 0x61, 0x74, 0x61,
0x63, 0x74, 0x6f, 0x72, 0xa9, 0x61, 0x63, 0xc0, 0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00,
0x74, 0x6f, 0x72, 0x31, 0x34, 0x38, 0x32, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0xa0,
0xaa, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa8, 0x73, 0x65,
0x73, 0x69, 0x6f, 0x6e, 0xa6, 0x61, 0x63, 0x6c, 0x6c, 0x69, 0x74, 0x65, 0x6d, 0xa8, 0x72,
0x74, 0x69, 0x76, 0x65, 0xa8, 0x62, 0x6c, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0xa5,
0x6f, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0x00, 0x65, 0x6f, 0x73, 0x69, 0x6f, 0xa6, 0x72, 0x65,
0xae, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x74, 0x75, 0x72, 0x6e, 0xc0, 0xa5, 0x74, 0x78,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0xa0, 0xa8, 0x62, 0x6c, 0x6f,
0x70, 0xc0, 0xa8, 0x63, 0x6f, 0x6e, 0x74, 0x63, 0x6b, 0x6e, 0x75, 0x6d, 0xce, 0xa3, 0x3d,
0x72, 0x61, 0x63, 0x74, 0xa7, 0x73, 0x6b, 0x9b, 0xc5, 0xae, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x6a, 0x64, 0x68, 0x32, 0x34, 0xa4, 0x64, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x61, 0x74, 0x61, 0xc0, 0xa5, 0x65, 0x72, 0x70, 0xd7, 0xff, 0x77, 0x35, 0x94, 0x00, 0x65,
0x72, 0x6f, 0x72, 0x00, 0xa6, 0x65, 0x78, 0x4e, 0x19, 0xff, 0xac, 0x63, 0x70, 0x75, 0x5f,
0x63, 0x65, 0x70, 0x74, 0xa0, 0xae, 0x66, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x73,
0x69, 0x72, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x17, 0xa7, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65,
0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0xc2, 0x64, 0x04, 0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72,
0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa8, 0x73, 0x02, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74,
0x65, 0x6c, 0x6c, 0x69, 0x74, 0x65, 0x6d, 0xa9, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x73,
0xa8, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x74, 0x72, 0xa2, 0x69, 0x64, 0xd9, 0x40, 0x65,
0x65, 0x72, 0xa5, 0x65, 0x6f, 0x73, 0x69, 0x64, 0x63, 0x30, 0x36, 0x64, 0x63, 0x65, 0x36,
0x6f, 0xa6, 0x72, 0x65, 0x74, 0x75, 0x72, 0x33, 0x32, 0x30, 0x34, 0x35, 0x39, 0x66, 0x64,
0x6e, 0xc0, 0xa5, 0x74, 0x78, 0x5f, 0x69, 0x36, 0x34, 0x34, 0x37, 0x35, 0x36, 0x39, 0x37,
0x64, 0xa0, 0xa8, 0x62, 0x6c, 0x6f, 0x63, 0x32, 0x30, 0x34, 0x38, 0x64, 0x61, 0x34, 0x35,
0x6b, 0x6e, 0x75, 0x6d, 0xce, 0xa3, 0x3d, 0x33, 0x62, 0x32, 0x38, 0x31, 0x36, 0x62, 0x30,
0x9b, 0xc5, 0xae, 0x62, 0x6c, 0x6f, 0x63, 0x61, 0x34, 0x33, 0x34, 0x63, 0x33, 0x37, 0x64,
0x6b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x64, 0x66, 0x66, 0x64, 0x65, 0x33, 0x36, 0x37,
0x61, 0x6d, 0x70, 0xd7, 0xff, 0x77, 0x35, 0x37, 0x38, 0x64, 0x63, 0x61, 0x62, 0x33, 0xa9,
0x94, 0x00, 0x65, 0x4e, 0x19, 0xff, 0xac, 0x6e, 0x65, 0x74, 0x5f, 0x75, 0x73, 0x61, 0x67,
0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x65, 0xcc, 0x80, 0xaf, 0x6e, 0x65, 0x74, 0x5f,
0x67, 0x65, 0x5f, 0x75, 0x73, 0x17, 0xa7, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x77, 0x6f,
0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x72, 0x64, 0x73, 0x10, 0xa9, 0x73, 0x63, 0x68,
0x04, 0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0xc3, 0xa6,
0x02, 0xa6, 0x65, 0x78, 0x63, 0x65, 0x70, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0xa9, 0x73,
0x74, 0xa9, 0x65, 0x78, 0x63, 0x65, 0x70, 0x6f, 0x66, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c,
0x74, 0x73, 0x74, 0x72, 0xa2, 0x69, 0x64,
0xd9, 0x40, 0x65, 0x64, 0x63, 0x30, 0x36,
0x64, 0x63, 0x65, 0x36, 0x33, 0x32, 0x30,
0x34, 0x35, 0x39, 0x66, 0x64, 0x36, 0x34,
0x34, 0x37, 0x35, 0x36, 0x39, 0x37, 0x32,
0x30, 0x34, 0x38, 0x64, 0x61, 0x34, 0x35,
0x33, 0x62, 0x32, 0x38, 0x31, 0x36, 0x62,
0x30, 0x61, 0x34, 0x33, 0x34, 0x63, 0x33,
0x37, 0x64, 0x64, 0x66, 0x66, 0x64, 0x65,
0x33, 0x36, 0x37, 0x37, 0x38, 0x64, 0x63,
0x61, 0x62, 0x33, 0xa9, 0x6e, 0x65, 0x74,
0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0xcc,
0x80, 0xaf, 0x6e, 0x65, 0x74, 0x5f, 0x75,
0x73, 0x61, 0x67, 0x65, 0x5f, 0x77, 0x6f,
0x72, 0x64, 0x73, 0x10, 0xa9, 0x73, 0x63,
0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64,
0xc3, 0xa6, 0x73, 0x74, 0x61, 0x74, 0x75,
0x73, 0xa9, 0x73, 0x6f, 0x66, 0x74, 0x5f,
0x66, 0x61, 0x69, 0x6c,
} }
assert.Equal(t, expected, data) assert.Equal(t, expected, data)
} }

View file

@ -65,8 +65,6 @@ type ActionTrace struct {
Contract string `json:"contract" msgpack:"contract"` Contract string `json:"contract" msgpack:"contract"`
Receiver string `json:"receiver" msgpack:"receiver"` Receiver string `json:"receiver" msgpack:"receiver"`
FirstReceiver bool `json:"first_receiver" msgpack:"first_receiver"`
Data interface{} `json:"data" msgpack:"data"` Data interface{} `json:"data" msgpack:"data"`
Authorization []PermissionLevel `json:"authorization" msgpack:"authorization"` Authorization []PermissionLevel `json:"authorization" msgpack:"authorization"`

View file

@ -6,7 +6,7 @@ package api
// This is a low-level interface typically implemented by backend drivers // This is a low-level interface typically implemented by backend drivers
type Reader interface { type Reader interface {
// Read a message from a channel. // Read a message from a channel.
// Read may block until a message is ready or an error occurred. // Read may block until a message is ready or an error occured.
// //
// io.EOF is returned from a reader when there is no more data to be read. // io.EOF is returned from a reader when there is no more data to be read.
// If Read returns io.EOF all subsequent calls must also return io.EOF // If Read returns io.EOF all subsequent calls must also return io.EOF

View file

@ -19,7 +19,7 @@ const (
// //
// Contains a prefix and chain_id to guard keys against collision. // Contains a prefix and chain_id to guard keys against collision.
// Prefix should be sufficient to not collide with other application using the same redis database. // Prefix should be sufficient to not collide with other application using the same redis database.
// chain_id should be fine to not let multiple reader with different chains to write to the same channels. // chain_id should be ok to not let multiple reader with different chains to write to the same channels.
type Namespace struct { type Namespace struct {
Prefix string Prefix string

View file

@ -48,7 +48,7 @@ func NewSubscriber(ctx context.Context, client *redis.Client, ns Namespace, opti
return sub return sub
} }
// worker reads messages from Redis pubsub and forwards them to // worker reads messages from redis pubsub and forwards them to
// correct channels. // correct channels.
func (s *Subscriber) worker() { func (s *Subscriber) worker() {
for msg := range s.sub.Channel() { for msg := range s.sub.Channel() {

View file

@ -1,35 +0,0 @@
package main
import (
"context"
"time"
antelopeapi "github.com/shufflingpixels/antelope-go/api"
log "github.com/sirupsen/logrus"
)
// "Clever" way to make sure we only call the api once.
// Store a info pointer outside the returned closure.
// that pointer will live as long as the closure lives.
// and inside the closure we will reference the pointer and only
// call the api if it is nil.
func chainInfoOnce(api *antelopeapi.Client) func() *antelopeapi.Info {
var info *antelopeapi.Info
return func() *antelopeapi.Info {
if info == nil {
log.WithField("api", api.Url).Info("Get chain info from api")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
result, err := api.GetInfo(ctx)
if err != nil {
log.WithError(err).Fatal("Failed to call eos api")
return nil
}
info = &result
}
return info
}
}

View file

@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
eos "github.com/eoscanada/eos-go"
shipclient "github.com/eosswedenorg-go/antelope-ship-client" shipclient "github.com/eosswedenorg-go/antelope-ship-client"
shipws "github.com/eosswedenorg-go/antelope-ship-client/websocket" shipws "github.com/eosswedenorg-go/antelope-ship-client/websocket"
"github.com/eosswedenorg-go/pid" "github.com/eosswedenorg-go/pid"
@ -22,14 +23,15 @@ import (
api_redis "github.com/eosswedenorg/thalos/api/redis" api_redis "github.com/eosswedenorg/thalos/api/redis"
"github.com/eosswedenorg/thalos/internal/abi" "github.com/eosswedenorg/thalos/internal/abi"
"github.com/eosswedenorg/thalos/internal/cache" "github.com/eosswedenorg/thalos/internal/cache"
. "github.com/eosswedenorg/thalos/internal/cache"
"github.com/eosswedenorg/thalos/internal/config" "github.com/eosswedenorg/thalos/internal/config"
driver "github.com/eosswedenorg/thalos/internal/driver/redis" driver "github.com/eosswedenorg/thalos/internal/driver/redis"
. "github.com/eosswedenorg/thalos/internal/log" . "github.com/eosswedenorg/thalos/internal/log"
. "github.com/eosswedenorg/thalos/internal/server" . "github.com/eosswedenorg/thalos/internal/server"
redis_cache "github.com/go-redis/cache/v9"
"github.com/nikoksr/notify" "github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/telegram" "github.com/nikoksr/notify/service/telegram"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
antelopeapi "github.com/shufflingpixels/antelope-go/api"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -154,12 +156,12 @@ func LogLevels() []string {
return list return list
} }
func initAbiManager(cfg *config.AbiCache, api *antelopeapi.Client, store cache.Store, chain_id string) *abi.AbiManager { func initAbiManager(api *eos.API, store cache.Store, chain_id string) *abi.AbiManager {
cache := cache.NewCache("thalos::cache::abi::"+chain_id, store) cache := NewCache("thalos::cache::abi::"+chain_id, store)
return abi.NewAbiManager(cfg, cache, api) return abi.NewAbiManager(cache, api)
} }
func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo func() *antelopeapi.Info, cache *cache.Cache, current_block_no_cache bool) StateLoader { func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo func() *eos.InfoResp, cache *cache.Cache, current_block_no_cache bool) StateLoader {
return func(state *State) { return func(state *State) {
var source string var source string
@ -185,7 +187,7 @@ func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo fu
// Otherwise, set from api. // Otherwise, set from api.
if conf.Ship.IrreversibleOnly { if conf.Ship.IrreversibleOnly {
source = "api (LIB)" source = "api (LIB)"
state.CurrentBlock = uint32(chainInfo().LastIrreversableBlockNum) state.CurrentBlock = uint32(chainInfo().LastIrreversibleBlockNum)
} else { } else {
source = "api (HEAD)" source = "api (HEAD)"
state.CurrentBlock = uint32(chainInfo().HeadBlockNum) state.CurrentBlock = uint32(chainInfo().HeadBlockNum)
@ -237,11 +239,36 @@ func GetConfig(flags *pflag.FlagSet) (*config.Config, error) {
} }
} }
cfg.Ship.Blacklist.SetWhitelist(cfg.Ship.BlacklistIsWhitelist)
return cfg, nil return cfg, nil
} }
// "Clever" way to make sure we only call the api once.
// Store a info pointer outside the returned closure.
// that pointer will live as long as the closure lives.
// and inside the closure we will reference the pointer and only
// call the api if it is nil.
func chainInfoOnce(api *eos.API) func() *eos.InfoResp {
var info *eos.InfoResp
return func() *eos.InfoResp {
if info == nil {
log.WithField("api", api.BaseURL).Info("Get chain info from api")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
result, err := api.GetInfo(ctx)
if err != nil {
log.WithError(err).Fatal("Failed to call eos api")
return nil
}
info = result
}
return info
}
}
func ConnectRedis(conf *config.RedisConfig) (*redis.Client, error) { func ConnectRedis(conf *config.RedisConfig) (*redis.Client, error) {
logEntry := log.WithFields(log.Fields{ logEntry := log.WithFields(log.Fields{
"addr": conf.Addr, "addr": conf.Addr,
@ -345,25 +372,22 @@ func serverCmd(cmd *cobra.Command, args []string) {
return return
} }
cache.RegisterFactory("redis", cache.NewRedisFactory(rdb))
// Setup cache storage // Setup cache storage
cacheStore, err := cache.Make(conf.Cache.Storage, conf.Cache.Options) cacheStore := NewRedisStore(&redis_cache.Options{
if err != nil { Redis: rdb,
log.WithError(err).Fatal("Failed to setup cache") // Cache 10k keys for 10 minutes.
return LocalCache: redis_cache.NewTinyLFU(10000, 10*time.Minute),
} })
// Setup general cache // Setup general cache
cache := cache.NewCache("thalos::cache::instance::"+conf.Name, cacheStore) cache := NewCache("thalos::cache::instance::"+conf.Name, cacheStore)
antelopeClient := antelopeapi.New(conf.Api) eosClient := eos.New(conf.Api)
shClient := shipclient.NewStream(func(s *shipclient.Stream) { shClient := shipclient.NewStream(func(s *shipclient.Stream) {
s.StartBlock = conf.Ship.StartBlockNum s.StartBlock = conf.Ship.StartBlockNum
s.EndBlock = conf.Ship.EndBlockNum s.EndBlock = conf.Ship.EndBlockNum
s.IrreversibleOnly = conf.Ship.IrreversibleOnly s.IrreversibleOnly = conf.Ship.IrreversibleOnly
s.MaxMessagesInFlight = 1
}) })
// Get codec // Get codec
@ -373,11 +397,11 @@ func serverCmd(cmd *cobra.Command, args []string) {
return return
} }
chainInfo := chainInfoOnce(antelopeClient) chainInfo := chainInfoOnce(eosClient)
chain_id := conf.Ship.Chain chain_id := conf.Ship.Chain
if len(chain_id) < 1 { if len(chain_id) < 1 {
chain_id = chainInfo().ChainID chain_id = chainInfo().ChainID.String()
} }
processor := SpawnProccessor( processor := SpawnProccessor(
@ -388,13 +412,10 @@ func serverCmd(cmd *cobra.Command, args []string) {
Prefix: conf.Redis.Prefix, Prefix: conf.Redis.Prefix,
ChainID: chain_id, ChainID: chain_id,
}), }),
initAbiManager(&conf.AbiCache, antelopeClient, cacheStore, chain_id), initAbiManager(eosClient, cacheStore, chain_id),
codec, codec,
) )
processor.SetBlacklist(conf.Ship.Blacklist)
processor.FetchDeltas(conf.Ship.EnableTableDeltas)
// Run the application // Run the application
run(conf, shClient, processor) run(conf, shClient, processor)

View file

@ -18,12 +18,11 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func CreateBenchCmd() *cobra.Command { var benchCmd = &cobra.Command{
cmd := &cobra.Command{
Use: "bench", Use: "bench",
Short: "Run a benchmark against a thalos node", Short: "Run a benchmark against a thalos node",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
counter := 0 var counter int = 0
interval, _ := cmd.Flags().GetDuration("interval") interval, _ := cmd.Flags().GetDuration("interval")
url, _ := cmd.Flags().GetString("redis-url") url, _ := cmd.Flags().GetString("redis-url")
@ -116,10 +115,4 @@ func CreateBenchCmd() *cobra.Command {
} }
} }
}, },
}
cmd.Flags().AddFlagSet(RedisFlags())
cmd.Flags().DurationP("interval", "i", time.Minute, "How often the benchmark results should be displayed.")
return cmd
} }

View file

@ -1,14 +0,0 @@
package main
import "github.com/spf13/pflag"
func RedisFlags() *pflag.FlagSet {
set := pflag.FlagSet{}
set.String("redis-url", "127.0.0.1:6379", "host:port to the redis server")
set.String("redis-user", "", "User to use when authenticating to the server")
set.String("redis-pw", "", "Password to use when authenticating to the server")
set.Int("redis-db", 0, "What redis database we should connect to.")
set.String("prefix", "ship", "redis prefix")
set.String("chain_id", "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", "chain id")
return &set
}

View file

@ -1,30 +1,62 @@
package main package main
import ( import (
"time"
_ "github.com/eosswedenorg/thalos/internal/log" _ "github.com/eosswedenorg/thalos/internal/log"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
var VersionString string = "dev" var VersionString string = "dev"
func main() { var rootCmd *cobra.Command
rootCmd := &cobra.Command{
func init() {
redisFlags := pflag.FlagSet{}
redisFlags.String("redis-url", "127.0.0.1:6379", "host:port to the redis server")
redisFlags.String("redis-user", "", "User to use when authenticating to the server")
redisFlags.String("redis-pw", "", "Password to use when authenticating to the server")
redisFlags.Int("redis-db", 0, "What redis database we should connect to.")
redisFlags.String("prefix", "ship", "redis prefix")
redisFlags.String("chain_id", "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", "chain id")
rootCmd = &cobra.Command{
Use: "thalos-tools", Use: "thalos-tools",
Short: "Collection of tools for dealing with the Thalos application", Short: "Collection of tools for dealing with the thalos application",
FParseErrWhitelist: cobra.FParseErrWhitelist{ FParseErrWhitelist: cobra.FParseErrWhitelist{
UnknownFlags: true, UnknownFlags: true,
}, },
Version: VersionString, Version: VersionString,
} }
rootCmd.AddCommand( benchCmd.Flags().AddFlagSet(&redisFlags)
CreateValidateCmd(), benchCmd.Flags().DurationP("interval", "i", time.Minute, "How often the benchmark results should be displayed.")
CreateBenchCmd(),
CreateRedisACLCmd(),
CreateMockPublisherCmd(),
)
validateCmd.Flags().AddFlagSet(&redisFlags)
MockPublisherCmd.Flags().AddFlagSet(&redisFlags)
MockPublisherCmd.Flags().String("codec", "json", "codec to use")
RedisACLCmd.Flags().String("default-pw", "", "Password to use for the default account, if not provided a random one will be generated")
RedisACLCmd.Flags().String("client", "thalos-client", "Thalos client account name")
RedisACLCmd.Flags().String("client-pw", "", "Password to use for the thalos client account, if not provided a random one will be generated")
RedisACLCmd.Flags().String("server", "thalos", "Thalos account name")
RedisACLCmd.Flags().String("server-pw", "", "Password to use for the thalos server account, if not provided a random one will be generated")
RedisACLCmd.Flags().String("prefix", "ship", "Redis key prefix")
RedisACLCmd.Flags().Bool("cleartext", false, "If passwords should be hashed or left in cleartext.")
RedisACLCmd.Flags().String("file", "", "Where the config should be written to (default: standard out)")
rootCmd.AddCommand(
validateCmd,
benchCmd,
RedisACLCmd,
MockPublisherCmd,
)
}
func main() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -16,8 +16,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func CreateMockPublisherCmd() *cobra.Command { var MockPublisherCmd = &cobra.Command{
cmd := &cobra.Command{
Use: "mock_publisher", Use: "mock_publisher",
Short: "Run a publisher that mocks messages to a redis server. tries to send as many messages as possible", Short: "Run a publisher that mocks messages to a redis server. tries to send as many messages as possible",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -113,9 +112,4 @@ func CreateMockPublisherCmd() *cobra.Command {
publisher.Flush() publisher.Flush()
} }
}, },
}
cmd.Flags().AddFlagSet(RedisFlags())
cmd.Flags().String("codec", "json", "codec to use")
return cmd
} }

View file

@ -26,28 +26,21 @@ type User struct {
// True if password was generated, false if not. // True if password was generated, false if not.
Generated bool Generated bool
// True if password should be hashed, false otherwise.
Hash bool
} }
func NewUser(name, password string, pass_len uint) User { func NewUser(name, password string) User {
if len(password) < 1 { if len(password) < 1 {
return User{ return User{
Name: name, Name: name,
Password: randomString(pass_len), Password: randomString(32),
Generated: true, Generated: true,
} }
} }
return User{Name: name, Password: password} return User{Name: name, Password: password}
} }
func (u *User) GetPassword() string { func (u *User) Hash() {
if u.Hash { u.Password = "#" + hash(u.Password)
return "#" + hash(u.Password)
}
return ">" + u.Password
} }
func (u User) Print() { func (u User) Print() {
@ -60,10 +53,10 @@ func (u User) PrintIfGeneratedPW() {
} }
} }
func randomString(length uint) string { func randomString(length int) string {
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789" charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789"
out := "" out := ""
for i := 0; i < int(length); i++ { for i := 0; i < length; i++ {
idx := rnd.Intn(len(charset)) idx := rnd.Intn(len(charset))
out += string(charset[idx]) out += string(charset[idx])
} }
@ -78,7 +71,7 @@ func hash(str string) string {
func writeTemplate(w io.Writer, defUser, serverUser, clientUser User, prefix string) error { func writeTemplate(w io.Writer, defUser, serverUser, clientUser User, prefix string) error {
tmplStr := `# Created by thalos-tools on {{.timestamp}} tmplStr := `# Created by thalos-tools on {{.timestamp}}
user default on {{.defaultpw}} ~* &* +@all user default on {{.defaultpw}} ~* &* +@all
user {{.server}} on {{.serverpw}} resetchannels ~{{.prefix}}::* &{{.prefix}}::* -@all +ping +get +publish +set user {{.server}} on {{.serverpw}} resetchannels ~{{.prefix}}::* &{{.prefix}}::* -@all +get +publish +set
user {{.client}} on {{.clientpw}} resetchannels &{{.prefix}}::* -@all +subscribe user {{.client}} on {{.clientpw}} resetchannels &{{.prefix}}::* -@all +subscribe
` `
@ -88,23 +81,22 @@ user {{.client}} on {{.clientpw}} resetchannels &{{.prefix}}::* -@all +subscribe
} }
return tmpl.Execute(w, map[string]string{ return tmpl.Execute(w, map[string]string{
"defaultpw": defUser.GetPassword(), "defaultpw": defUser.Password,
"client": clientUser.Name, "client": clientUser.Name,
"clientpw": clientUser.GetPassword(), "clientpw": clientUser.Password,
"server": serverUser.Name, "server": serverUser.Name,
"serverpw": serverUser.GetPassword(), "serverpw": serverUser.Password,
"prefix": prefix, "prefix": prefix,
"timestamp": time.Now().Format(time.UnixDate), "timestamp": time.Now().Format(time.UnixDate),
}) })
} }
func CreateRedisACLCmd() *cobra.Command { var RedisACLCmd = &cobra.Command{
cmd := &cobra.Command{
Use: "redis-acl", Use: "redis-acl",
Short: "create a users.acl file", Short: "create a users.acl file",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error var err error
out := os.Stdout var out *os.File = os.Stdout
rnd = rand.New(rand.NewSource(time.Now().UnixNano())) rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
@ -114,11 +106,10 @@ func CreateRedisACLCmd() *cobra.Command {
flagClient, _ := cmd.Flags().GetString("client") flagClient, _ := cmd.Flags().GetString("client")
flagClientPw, _ := cmd.Flags().GetString("client-pw") flagClientPw, _ := cmd.Flags().GetString("client-pw")
flagPrefix, _ := cmd.Flags().GetString("prefix") flagPrefix, _ := cmd.Flags().GetString("prefix")
flagPassLen, _ := cmd.Flags().GetUint("pass-len")
defaultUser := NewUser("default", flagDefUserPw, flagPassLen) defaultUser := NewUser("default", flagDefUserPw)
serverUser := NewUser(flagServer, flagServerPw, flagPassLen) serverUser := NewUser(flagServer, flagServerPw)
clientUser := NewUser(flagClient, flagClientPw, flagPassLen) clientUser := NewUser(flagClient, flagClientPw)
atleastOneGeneratedPw := defaultUser.Generated || serverUser.Generated || clientUser.Generated atleastOneGeneratedPw := defaultUser.Generated || serverUser.Generated || clientUser.Generated
@ -132,9 +123,9 @@ func CreateRedisACLCmd() *cobra.Command {
serverUser.PrintIfGeneratedPW() serverUser.PrintIfGeneratedPW()
clientUser.PrintIfGeneratedPW() clientUser.PrintIfGeneratedPW()
defaultUser.Hash = true defaultUser.Hash()
serverUser.Hash = true serverUser.Hash()
clientUser.Hash = true clientUser.Hash()
} }
filename, _ := cmd.Flags().GetString("file") filename, _ := cmd.Flags().GetString("file")
@ -155,17 +146,4 @@ func CreateRedisACLCmd() *cobra.Command {
return return
} }
}, },
}
cmd.Flags().String("default-pw", "", "Password to use for the default account, if not provided a random one will be generated")
cmd.Flags().String("client", "thalos-client", "Thalos client account name")
cmd.Flags().String("client-pw", "", "Password to use for the thalos client account, if not provided a random one will be generated")
cmd.Flags().String("server", "thalos", "Thalos account name")
cmd.Flags().String("server-pw", "", "Password to use for the thalos server account, if not provided a random one will be generated")
cmd.Flags().String("prefix", "ship", "Redis key prefix")
cmd.Flags().Bool("cleartext", false, "If passwords should be hashed or left in cleartext.")
cmd.Flags().String("file", "", "Where the config should be written to (default: standard out)")
cmd.Flags().Uint("pass-len", 32, "The length of generated passwords")
return cmd
} }

View file

@ -18,8 +18,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func CreateValidateCmd() *cobra.Command { var validateCmd = &cobra.Command{
cmd := &cobra.Command{
Use: "validate", Use: "validate",
Short: "Validate a thalos server by following action traces and makes sure that blocks arrive in order.", Short: "Validate a thalos server by following action traces and makes sure that blocks arrive in order.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -82,7 +81,7 @@ func CreateValidateCmd() *cobra.Command {
log.WithError(msg).Error("Error when reading stream") log.WithError(msg).Error("Error when reading stream")
case message.ActionTrace: case message.ActionTrace:
if block_num > 0 { if block_num > 0 {
diff := int32(msg.BlockNum - block_num) var diff int32 = int32(msg.BlockNum - block_num)
if diff < 0 || diff > 1 { if diff < 0 || diff > 1 {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"current_block": block_num, "current_block": block_num,
@ -116,9 +115,4 @@ func CreateValidateCmd() *cobra.Command {
} }
} }
}, },
}
cmd.Flags().AddFlagSet(RedisFlags())
return cmd
} }

View file

@ -38,39 +38,13 @@ ship:
# Request ship to start sending blocks from this block. # Request ship to start sending blocks from this block.
# If not set, the head block reported by the nodeos api is used. # If not set, the head block reported by the nodeos api is used.
# start_block_num: 1000 #start_block_num: 1000
# Request ship to stop sending blocks when reaching this block. # Request ship to stop sending blocks when reaching this block.
# end_block_num: 2000 #end_block_num: 2000
# If true, Thalos will process table deltas
# table_deltas: false
# Blacklist contract/actions
blacklist:
# this is a "useless" action that results in alot of warning messages.
# becase thalos does not know it's ABI. Its recommended to have this action blacklisted
# unless you have a reason to use it.
eosio.null:
- nonce
# blacklist all action from a contract
# evilcontract: ["*"]
# blacklist_is_whitelist: true
# Configure the cache.
# Default is to use redis. But if you need to
# you can set additional values or even change the driver
# to something else that Thalos supports.
# See the documentation for details
# cache:
# storage: redis
# options: []
# Telegram notifications # Telegram notifications
# telegram: #telegram:
# id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP" # id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP"
# channel: -123456789 # channel: -123456789
@ -83,7 +57,7 @@ redis:
user: "" user: ""
# Password to use when authenticating # Password to use when authenticating
password: "" pasword: ""
# database index # database index
db: 0 db: 0

173
debian/changelog vendored
View file

@ -1,175 +1,3 @@
thalos (1.1.9) bionic focal jammy; urgency=medium
* [Security CVE-2024-45338] Update golang.org/x/net to 0.33.0
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Thu, 23 Jan 2025 19:30:31 +0100
thalos (1.1.8) bionic focal jammy; urgency=medium
* Support for wildcard contracts in Blacklist
* [Security CVE-2024-45337] Update golang.org/x/crypto to 0.31.0
* [Security CVE-2024-53259] Update github.com/quic-go/quic-go to 0.48.2
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Mon, 23 Dec 2024 09:25:44 +0100
thalos (1.1.8~rc1) bionic focal jammy; urgency=medium
* Support for wildcard contracts in Blacklist
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Wed, 04 Dec 2024 15:19:53 +0100
thalos (1.1.7) bionic focal jammy; urgency=medium
* ship: set MaxMessagesInFlight to 1. This forces the client/server to ack
every message and might be a workaround fix for issue #25
according to this comment:
https://github.com/AntelopeIO/leap/issues/1358#issuecomment-2276294557
* golang: update eosswedenrg-go/antelope-ship-client to v0.3.2
* Add support to disable processing of table deltas.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Mon, 11 Nov 2024 19:38:15 +0100
thalos (1.1.7~rc2) bionic focal jammy; urgency=medium
* ship: set MaxMessagesInFlight to 1. This forces the client/server to ack
every message and might be a workaround fix for issue #25
according to this comment:
https://github.com/AntelopeIO/leap/issues/1358#issuecomment-2276294557
* golang: update eosswedenrg-go/antelope-ship-client to v0.3.2
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Sun, 03 Nov 2024 12:04:29 +0100
thalos (1.1.7~rc1) bionic focal jammy; urgency=medium
* Add support to disable processing of table deltas.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Mon, 21 Oct 2024 12:31:21 +0200
thalos (1.1.6) bionic focal jammy; urgency=medium
[ Henrik Hautakoski ]
* makefile: make sure we apppend to GOBULDFLAGS if user wants to add their own.
* minor style fixes.
* api/channel_test.go: rearange fields.
* README.md: Update minimum go version
* README.md: Link to docker page
* .github/workflows/release.yml: need to update version regex for musl builds
[ Avm07 ]
* Fix typo in config.example.yml
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Wed, 16 Oct 2024 16:23:47 +0200
thalos (1.1.5) bionic focal jammy; urgency=medium
* New config section: `cache`
* New CLI flag: `cache` specify what cache driver to use
* New CLI flag: `abi-cache-api-timeout` configure the timeout for the HTTP
request made when Thalos wants to fetch a ABI from the api.
* API Table Deltas: abi decode the data in `value` field for contract_row deltas.
* golang: update github.com/shufflingpixels/antelope-go to v0.1.5
* golang: update github.com/quic-go/quic-go from 0.41.0 to 0.42.0
* golang: version 1.20 can no longer be used to build the project.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Thu, 29 Aug 2024 15:33:17 +0200
thalos (1.1.5~rc1) bionic focal jammy; urgency=medium
* New config section: `cache`
* New CLI flag: `cache` specify what cache driver to use
* New CLI flag: `abi-cache-api-timeout` configure the timeout for the HTTP
request made when Thalos wants to fetch a ABI from the api.
* API Table Deltas: abi decode the data in `value` field for contract_row deltas.
* golang: update github.com/shufflingpixels/antelope-go to v0.1.4
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Sun, 11 Aug 2024 17:04:55 +0200
thalos (1.1.4) bionic focal jammy; urgency=medium
* Implement whitelist option for ship contract/action blacklist
* Fix bug with integer overflow on 32 bit CPUs.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Tue, 16 Jul 2024 21:03:34 +0200
thalos (1.1.3) bionic focal jammy; urgency=medium
* Updated antelope-go library to v0.1.2 that fixes a bug in abi binary
decoder, it expects some fields to be strings while they are "names"
(strings encoded into a int64)
* Fix a bug with "set_abi" struct had the wrong order of fields in ShipProcessor.updateAbiFromAction()
* Fix a bug in ShipProcessor.updateAbiFromAction() that assumed the abi
was in hex format when in fact it is binary.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Wed, 03 Jul 2024 18:05:33 +0200
thalos (1.1.2) bionic focal jammy; urgency=medium
* API: Fix a bug regarding json timestamp being encoded/decoded with wrong
format
* Implement action blacklist, it is not possible to configure a blacklist
that will be used to filter out processing of unwanted contracts/actions.
* Fix a bug in isVariant() where v.Elem() was called on non interface/pointer
* Minor cleanups in tools
* Fix a bug where TableDeltaRow.Data was not set
* Fix a bug where blockResult.Deltas was not properly nil checked. Resulting in panic if accessed
* Moved from github.com/eoscanda/eos-go to github.com/pnx/antelope-go library
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Thu, 27 Jun 2024 14:27:38 +0200
thalos (1.1.2~rc4) bionic focal jammy; urgency=medium
* API: Fix a bug regarding json timestamp being encoded/decoded with wrong
format
* Implement action blacklist, it is now possible to configure a blacklist
that will be used to filter out processing of unwanted contracts/actions.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Sun, 23 Jun 2024 14:55:03 +0200
thalos (1.1.2~rc3) bionic focal jammy; urgency=medium
* Fix a bug in isVariant() where v.Elem() was called on non interface/pointer
* Minor cleanups in tools
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Wed, 19 Jun 2024 21:50:15 +0200
thalos (1.1.2~rc2) bionic focal jammy; urgency=medium
* fix a bug where TableDeltaRow.Data was not set
* fix a bug where blockResult.Deltas was not properly nil checked. Resulting in panic if accessed
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Fri, 17 May 2024 18:15:29 +0200
thalos (1.1.2~rc1) bionic focal jammy; urgency=medium
* Moved from github.com/eoscanda/eos-go to github.com/pnx/antelope-go library
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Mon, 29 Apr 2024 21:14:34 +0200
thalos (1.1.1) bionic focal jammy; urgency=medium
* Build binaries linked with musl libc for alpine linux.
* Added docker image.
* redis-acl tool: added `--pass-len` flag.
* redis-acl tool: fix correct syntax for cleartext passwords.
* redis-acl tool: allow ping command for server user.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Tue, 09 Apr 2024 22:40:20 +0200
thalos (1.1.0) bionic focal jammy; urgency=medium
* Adding `ActionTrace.FirstReceiver` flag, that is `true`
only if receiver is the same as contract name.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Fri, 01 Mar 2024 16:41:25 +0100
thalos (1.1.0~rc2) bionic focal jammy; urgency=medium
* Adding `log.file_timestamp_format` config field
* Added cli flag `--log-file-timestamp`
* Directory where log files are stored is created with correct permissions.
-- Henrik Hautakoski <henrik.hautakoski@gmail.com> Wed, 28 Feb 2024 23:16:44 +0100
thalos (1.1.0~rc1) bionic focal jammy; urgency=medium thalos (1.1.0~rc1) bionic focal jammy; urgency=medium
* Adding flags for almost all config values. * Adding flags for almost all config values.
@ -280,4 +108,3 @@ thalos-server (0.1.0) bionic focal jammy; urgency=medium
Initial release. Initial release.
-- Henrik Hautakoski <henrik@eossweden.org> Sun, 14 May 2023 18:17:35 +0200 -- Henrik Hautakoski <henrik@eossweden.org> Sun, 14 May 2023 18:17:35 +0200

View file

@ -1,9 +1,10 @@
diff --git a/config.example.yml b/config.example.yml diff --git a/config.example.yml b/config.example.yml
index 9f4272a..8daac0a 100644
--- a/config.example.yml --- a/config.example.yml
+++ b/config.example.yml +++ b/config.example.yml
@@ -11,7 +11,7 @@ log: @@ -11,7 +11,7 @@ log:
# Filename to use. # Filename to use.
filename: thalos filename: thalos.log
# Directory to store the logfiles in. # Directory to store the logfiles in.
- directory: logs - directory: logs
+ directory: /var/log/thalos + directory: /var/log/thalos

View file

@ -1,6 +0,0 @@
# Environment variables for thalos-server
#
# This is file sourced by the systemd unit file
# Append additional cli flags if needed. see: thalos-server -h
THALOS_SERVER_ARGS="-c /etc/thalos/config.yml"

View file

@ -6,8 +6,7 @@ After=network.target
User=thalos User=thalos
Group=thalos Group=thalos
Type=simple Type=simple
EnvironmentFile=-/etc/sysconfig/thalos-server ExecStart=/usr/bin/thalos-server -c /etc/thalos/config.yml
ExecStart=/usr/bin/thalos-server $THALOS_SERVER_ARGS
ExecReload=kill -HUP $MAINPID ExecReload=kill -HUP $MAINPID
Restart=on-failure Restart=on-failure

View file

@ -1,2 +1 @@
debian/thalos-server.service /lib/systemd/system debian/thalos-server.service /lib/systemd/system
debian/sysconfig/thalos-server /etc/sysconfig

View file

@ -3,8 +3,10 @@ set -e
#DEBHELPER# #DEBHELPER#
if [ "${1}" = "purge" ]; then
deluser --quiet thalos >/dev/null || true if [ "${1}" = "purge" ]
then
deluser --quiet thalos > /dev/null || true
rm -rf /var/log/thalos rm -rf /var/log/thalos
fi fi

View file

@ -1,6 +0,0 @@
FROM alpine:latest
LABEL maintainer="Henrik Hautakoski <henrik.Hautakoski@gmail.com>"
ARG VERSION=1.1.9
WORKDIR /thalos
ADD --chmod=755 https://github.com/eosswedenorg/thalos/releases/download/v$VERSION/thalos-server-${VERSION}-linux-amd64-musl thalos-server
ENTRYPOINT [ "./thalos-server" ]

58
go.mod
View file

@ -1,80 +1,74 @@
module github.com/eosswedenorg/thalos module github.com/eosswedenorg/thalos
go 1.22.0 go 1.20
require ( require (
github.com/cenkalti/backoff/v4 v4.2.1 github.com/cenkalti/backoff/v4 v4.2.1
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/eosswedenorg-go/antelope-ship-client v0.3.2 github.com/eoscanada/eos-go v0.10.3-0.20231109144819-59afdfa3a37d
github.com/eosswedenorg-go/antelope-ship-client v0.2.8
github.com/eosswedenorg-go/pid v1.0.1 github.com/eosswedenorg-go/pid v1.0.1
github.com/eosswedenorg/thalos/api v1.0.0 github.com/eosswedenorg/thalos/api v1.0.0
github.com/go-redis/cache/v9 v9.0.0 github.com/go-redis/cache/v9 v9.0.0
github.com/go-redis/redismock/v9 v9.2.0 github.com/go-redis/redismock/v9 v9.2.0
github.com/karlseguin/typed v1.1.8
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/nikoksr/notify v0.41.0 github.com/nikoksr/notify v0.41.0
github.com/redis/go-redis/v9 v9.5.1 github.com/redis/go-redis/v9 v9.4.0
github.com/shufflingpixels/antelope-go v0.1.5
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2 github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/andybalholm/brotli v1.1.1 // indirect github.com/blendle/zapdriver v1.3.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imroc/req/v3 v3.49.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.6 // indirect
github.com/liamylian/jsontime/v2 v2.0.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.22.0 // indirect github.com/onsi/gomega v1.31.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/vmihailenco/go-tinylfu v0.2.2 // indirect github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/goleak v1.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect golang.org/x/crypto v0.19.0 // indirect
golang.org/x/mod v0.22.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.33.0 // indirect golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.10.0 // indirect golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.28.0 // indirect golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/term v0.17.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
) )

159
go.sum
View file

@ -1,24 +1,18 @@
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.30.2 h1:lc1UAUT9ZA7h4srlfBmBt2aorm5Yftk9nBjxz7EyY9I= github.com/alicebob/miniredis/v2 v2.30.2 h1:lc1UAUT9ZA7h4srlfBmBt2aorm5Yftk9nBjxz7EyY9I=
github.com/alicebob/miniredis/v2 v2.30.2/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -29,26 +23,25 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/eosswedenorg-go/antelope-ship-client v0.3.2 h1:mDXZkjQ0bTPJClkhoPEP5ltucxql6bR+QixhnQI/Og4= github.com/eoscanada/eos-go v0.10.3-0.20231109144819-59afdfa3a37d h1:vK5PijzcJaUPOhgWvY9lL99H9t3lrRNHx2IDHqS0ILc=
github.com/eosswedenorg-go/antelope-ship-client v0.3.2/go.mod h1:DnUmaRGxz/V73CtVEJx/fReqhgGzhVyWpOrEKVYQSgE= github.com/eoscanada/eos-go v0.10.3-0.20231109144819-59afdfa3a37d/go.mod h1:L3avCf8OkDrjlUeNy9DdoV67TCmDNj2dSlc5Xp3DNNk=
github.com/eosswedenorg-go/antelope-ship-client v0.2.8 h1:xqtRrijqVcOgLeh5foXjNoqOXkU/w1l6T4unwLgCrP0=
github.com/eosswedenorg-go/antelope-ship-client v0.2.8/go.mod h1:YcOEgcsZs9a7MjFjRZiX6Qpgc+c0a09PPE4mg8I6IU4=
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7 h1:rLPu++RHaxg4WmUOXeWYioZuafWs0PVcYuvzOWbOJjk=
github.com/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7/go.mod h1:eNUkVOymzgl0lViUhmm08PkutzqLnOQ6Dr+RUnf+Mq0=
github.com/eosswedenorg-go/pid v1.0.1 h1:W4AEnnNwb041SpNR1uTZ/KbJ0OTA5eqiqIR1Q5Ah6A0= github.com/eosswedenorg-go/pid v1.0.1 h1:W4AEnnNwb041SpNR1uTZ/KbJ0OTA5eqiqIR1Q5Ah6A0=
github.com/eosswedenorg-go/pid v1.0.1/go.mod h1:wiOB/JXGt4YA3+T0j0xmCGSc3Jxzb7Ti/Ftli1fgWu4= github.com/eosswedenorg-go/pid v1.0.1/go.mod h1:wiOB/JXGt4YA3+T0j0xmCGSc3Jxzb7Ti/Ftli1fgWu4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0= github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI= github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw= github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0= github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -67,56 +60,42 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imroc/req/v3 v3.49.0 h1:5Rac2qvz7Dq0E3PeBo/c2szV3hagPQIGLoHtfBmYhu4=
github.com/imroc/req/v3 v3.49.0/go.mod h1:XZf4t94DNJzcA0UOBlA68hmSrWsAyvN407ADdH4mzCA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/karlseguin/typed v1.1.8 h1:ND0eDpwiUFIrm/n1ehxUyh/XNGs9zkYrLxtGqENSalY=
github.com/karlseguin/typed v1.1.8/go.mod h1:pZlmYaWQ7MVpwfIOP88fASh3LopVxKeE+uNXW3hQ2D8=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ= github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ=
@ -136,8 +115,6 @@ github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8Ay
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
@ -148,36 +125,26 @@ github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA= github.com/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA=
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shufflingpixels/antelope-go v0.1.5 h1:N0jCC5bya5shBb96Ff2cCpN+Yw99YldVjWvr0qoiW3E=
github.com/shufflingpixels/antelope-go v0.1.5/go.mod h1:VFULwUB/YfNZeZFzClTNrLyIKWChRhnPvxdzapeOcm0=
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d h1:nju7jR1Kf210tArPT6l//HlfLLFnvje1BWl5TSRsohQ=
github.com/shufflingpixels/jsontime-go v0.0.0-20240622163621-cf4b2804c92d/go.mod h1:W0TaKyg3kDqWmFUxlax3qAls/lRdo12EseM6f4f0dzE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@ -192,6 +159,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -202,13 +171,21 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI= github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
@ -218,41 +195,52 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -260,16 +248,16 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -281,7 +269,9 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -294,14 +284,16 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -309,19 +301,17 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -335,8 +325,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -350,10 +338,11 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=

View file

@ -5,49 +5,45 @@ import (
"fmt" "fmt"
"time" "time"
eos "github.com/eoscanada/eos-go"
"github.com/eosswedenorg/thalos/internal/cache" "github.com/eosswedenorg/thalos/internal/cache"
"github.com/eosswedenorg/thalos/internal/config"
"github.com/shufflingpixels/antelope-go/api"
"github.com/shufflingpixels/antelope-go/chain"
) )
// AbiManager handles an ABI cache that fetches the ABI from an API on cache miss. // AbiManager handles an ABI cache that fetches the ABI from an API on cache miss.
type AbiManager struct { type AbiManager struct {
cfg *config.AbiCache
cache *cache.Cache cache *cache.Cache
api *api.Client api *eos.API
ctx context.Context ctx context.Context
} }
// Create a new ABI Manager // Create a new ABI Manager
func NewAbiManager(cfg *config.AbiCache, cache *cache.Cache, api *api.Client) *AbiManager { func NewAbiManager(cache *cache.Cache, api *eos.API) *AbiManager {
return &AbiManager{ return &AbiManager{
cache: cache, cache: cache,
api: api, api: api,
cfg: cfg,
ctx: context.Background(), ctx: context.Background(),
} }
} }
// Set or update an ABI in the cache. // Set or update an ABI in the cache.
func (mgr *AbiManager) SetAbi(account chain.Name, abi *chain.Abi) error { func (mgr *AbiManager) SetAbi(account eos.AccountName, abi *eos.ABI) error {
ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500) ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500)
defer cancel() defer cancel()
return mgr.cache.Set(ctx, account.String(), *abi, time.Hour) return mgr.cache.Set(ctx, string(account), *abi, time.Hour)
} }
// Get an ABI from the cache, on cache miss it is fetched from the // Get an ABI from the cache, on cache miss it is fetched from the
// API, gets cached and then returned to the user // API, gets cached and then returned to the user
func (mgr *AbiManager) GetAbi(account chain.Name) (*chain.Abi, error) { func (mgr *AbiManager) GetAbi(account eos.AccountName) (*eos.ABI, error) {
var abi chain.Abi var abi eos.ABI
if err := mgr.cacheGet(account, &abi); err != nil { if err := mgr.cacheGet(account, &abi); err != nil {
ctx, cancel := context.WithTimeout(mgr.ctx, mgr.cfg.ApiTimeout) ctx, cancel := context.WithTimeout(mgr.ctx, time.Second)
defer cancel() defer cancel()
resp, err := mgr.api.GetAbi(ctx, account.String()) resp, err := mgr.api.GetABI(ctx, account)
if err != nil { if err != nil {
return nil, fmt.Errorf("api: %s", err) return nil, fmt.Errorf("api: %s", err)
} }
abi = resp.Abi abi = resp.ABI
err = mgr.SetAbi(account, &abi) err = mgr.SetAbi(account, &abi)
if err != nil { if err != nil {
@ -57,8 +53,8 @@ func (mgr *AbiManager) GetAbi(account chain.Name) (*chain.Abi, error) {
return &abi, nil return &abi, nil
} }
func (mgr *AbiManager) cacheGet(account chain.Name, value any) error { func (mgr *AbiManager) cacheGet(account eos.AccountName, value any) error {
ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500) ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500)
defer cancel() defer cancel()
return mgr.cache.Get(ctx, account.String(), value) return mgr.cache.Get(ctx, string(account), value)
} }

View file

@ -1,18 +1,15 @@
package abi package abi
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings"
"testing" "testing"
"time"
"github.com/shufflingpixels/antelope-go/api" eos "github.com/eoscanada/eos-go"
"github.com/shufflingpixels/antelope-go/chain"
"github.com/eosswedenorg/thalos/internal/cache" "github.com/eosswedenorg/thalos/internal/cache"
"github.com/eosswedenorg/thalos/internal/config"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -76,7 +73,7 @@ var abiString = `
} }
` `
func assert_abi(t *testing.T, abi *chain.Abi) { func assert_abi(t *testing.T, abi *eos.ABI) {
assert.Equal(t, abi.Version, "eosio::abi/1.0") assert.Equal(t, abi.Version, "eosio::abi/1.0")
// Types // Types
@ -113,12 +110,12 @@ func assert_abi(t *testing.T, abi *chain.Abi) {
assert.Equal(t, abi.Structs[3].Fields[0].Type, "string") assert.Equal(t, abi.Structs[3].Fields[0].Type, "string")
// Actions // Actions
assert.Equal(t, abi.Actions[0].Name, chain.N("action_name_1")) assert.Equal(t, abi.Actions[0].Name, eos.ActN("action_name_1"))
assert.Equal(t, abi.Actions[0].Type, "struct_name_1") assert.Equal(t, abi.Actions[0].Type, "struct_name_1")
assert.Equal(t, abi.Actions[0].RicardianContract, "") assert.Equal(t, abi.Actions[0].RicardianContract, "")
// Tables // Tables
assert.Equal(t, abi.Tables[0].Name, chain.N("table_name_1")) assert.Equal(t, abi.Tables[0].Name, eos.TableName("table_name_1"))
assert.Equal(t, abi.Tables[0].Type, "struct_name_1") assert.Equal(t, abi.Tables[0].Type, "struct_name_1")
assert.Equal(t, abi.Tables[0].IndexType, "i64") assert.Equal(t, abi.Tables[0].IndexType, "i64")
assert.Equal(t, abi.Tables[0].KeyNames[0], "key_name_1") assert.Equal(t, abi.Tables[0].KeyNames[0], "key_name_1")
@ -127,41 +124,37 @@ func assert_abi(t *testing.T, abi *chain.Abi) {
assert.Equal(t, abi.Tables[0].KeyTypes[1], "int") assert.Equal(t, abi.Tables[0].KeyTypes[1], "int")
} }
func mockAPI(handler http.HandlerFunc) (*api.Client, *httptest.Server) { func mockAPI(handler http.HandlerFunc) (*eos.API, *httptest.Server) {
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
return api.New(server.URL), server return &eos.API{
HttpClient: server.Client(),
BaseURL: strings.TrimRight(server.URL, "/"),
Compress: eos.CompressionZlib,
Header: make(http.Header),
}, server
} }
func TestManager_GetAbiFromCache(t *testing.T) { func TestManager_GetAbiFromCache(t *testing.T) {
cfg := &config.AbiCache{
ApiTimeout: time.Second,
}
cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore()) cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore())
api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})) }))
mgr := NewAbiManager(cfg, cache, api) mgr := NewAbiManager(cache, api)
abi := chain.Abi{} abi, err := eos.NewABI(strings.NewReader(abiString))
err := json.Unmarshal([]byte(abiString), &abi)
assert.NoError(t, err) assert.NoError(t, err)
err = mgr.SetAbi(chain.N("testaccount"), &abi) err = mgr.SetAbi("testaccount", abi)
assert.NoError(t, err) assert.NoError(t, err)
c_abi, err := mgr.GetAbi(chain.N("testaccount")) c_abi, err := mgr.GetAbi("testaccount")
assert.NoError(t, err) assert.NoError(t, err)
assert_abi(t, c_abi) assert_abi(t, c_abi)
} }
func TestManager_GetAbiFromAPI(t *testing.T) { func TestManager_GetAbiFromAPI(t *testing.T) {
cfg := &config.AbiCache{
ApiTimeout: time.Second,
}
cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore()) cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore())
api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -171,9 +164,9 @@ func TestManager_GetAbiFromAPI(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
})) }))
mgr := NewAbiManager(cfg, cache, api) mgr := NewAbiManager(cache, api)
c_abi, err := mgr.GetAbi(chain.N("testaccount")) c_abi, err := mgr.GetAbi("testaccount")
assert.NoError(t, err) assert.NoError(t, err)
assert_abi(t, c_abi) assert_abi(t, c_abi)

View file

@ -1,26 +0,0 @@
package cache
import (
"fmt"
"github.com/karlseguin/typed"
)
type Factory func(opts typed.Typed) (Store, error)
var factories = map[string]Factory{
"memory": func(opts typed.Typed) (Store, error) {
return NewMemoryStore(), nil
},
}
func RegisterFactory(driver string, factory Factory) {
factories[driver] = factory
}
func Make(driver string, opts typed.Typed) (Store, error) {
if factory, ok := factories[driver]; ok {
return factory(opts)
}
return nil, fmt.Errorf("Invalid cache storage: %s", driver)
}

View file

@ -1,20 +0,0 @@
package cache_test
import (
"testing"
"github.com/eosswedenorg/thalos/internal/cache"
"github.com/stretchr/testify/require"
)
func TestFactory_Make(t *testing.T) {
store, err := cache.Make("memory", map[string]any{})
require.NoError(t, err)
require.Equal(t, cache.NewMemoryStore(), store)
}
func TestFactory_MakeInvalidDriver(t *testing.T) {
store, err := cache.Make("87923yus", map[string]any{})
require.Error(t, err)
require.Nil(t, store)
}

View file

@ -5,46 +5,18 @@ import (
"time" "time"
"github.com/go-redis/cache/v9" "github.com/go-redis/cache/v9"
"github.com/karlseguin/typed"
"github.com/redis/go-redis/v9"
) )
type RedisStore struct { type RedisStore struct {
c *cache.Cache c *cache.Cache
} }
type options struct {
Stats bool
Size int
TTL time.Duration
}
func NewRedisStore(options *cache.Options) *RedisStore { func NewRedisStore(options *cache.Options) *RedisStore {
return &RedisStore{ return &RedisStore{
c: cache.New(options), c: cache.New(options),
} }
} }
func getOptions(opts typed.Typed) options {
return options{
Stats: opts.Bool("stats"),
Size: opts.IntOr("size", 1000),
TTL: time.Duration(opts.IntOr("ttl", 10)) * time.Minute,
}
}
func NewRedisFactory(client *redis.Client) Factory {
return func(opts typed.Typed) (Store, error) {
o := getOptions(opts)
return NewRedisStore(&cache.Options{
Redis: client,
StatsEnabled: o.Stats,
LocalCache: cache.NewTinyLFU(o.Size, o.TTL),
}), nil
}
}
func (s *RedisStore) Get(ctx context.Context, key string, value interface{}) error { func (s *RedisStore) Get(ctx context.Context, key string, value interface{}) error {
return s.c.Get(ctx, key, value) return s.c.Get(ctx, key, value)
} }

View file

@ -6,7 +6,6 @@ import (
"time" "time"
"github.com/go-redis/redismock/v9" "github.com/go-redis/redismock/v9"
"github.com/karlseguin/typed"
redis_cache "github.com/go-redis/cache/v9" redis_cache "github.com/go-redis/cache/v9"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -17,38 +16,6 @@ type testItem struct {
Name string Name string
} }
func TestRedisStore_getOptionsDefaults(t *testing.T) {
opts := typed.Typed{}
expected := options{
Stats: false,
Size: 1000,
TTL: 10 * time.Minute,
}
actual := getOptions(opts)
assert.Equal(t, expected, actual)
}
func TestRedisStore_getOptions(t *testing.T) {
opts := typed.Typed{
"stats": true,
"size": 123,
"ttl": 60,
}
expected := options{
Stats: true,
Size: 123,
TTL: 60 * time.Minute,
}
actual := getOptions(opts)
assert.Equal(t, expected, actual)
}
func TestRedisStore_Set(t *testing.T) { func TestRedisStore_Set(t *testing.T) {
client, mock := redismock.NewClientMock() client, mock := redismock.NewClientMock()

View file

@ -2,13 +2,9 @@ package config
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"reflect"
"strings"
"github.com/eosswedenorg/thalos/internal/types"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -40,15 +36,9 @@ func NewBuilder() *Builder {
"telegram.id": "telegram-id", "telegram.id": "telegram-id",
"telegram.channel": "telegram-channel", "telegram.channel": "telegram-channel",
"cache.storage": "cache",
// AbiCache
"abi_cache.api_timeout": "abi-cache-api-timeout",
// Log // Log
"log.maxfilesize": "log-max-filesize", "log.maxfilesize": "log-max-filesize",
"log.maxtime": "log-max-time", "log.maxtime": "log-max-time",
"log.file_timestamp_format": "log-file-timestamp",
// Ship // Ship
"ship.url": "ship-url", "ship.url": "ship-url",
@ -57,9 +47,6 @@ func NewBuilder() *Builder {
"ship.irreversible_only": "irreversible-only", "ship.irreversible_only": "irreversible-only",
"ship.max_messages_in_flight": "max-msg-in-flight", "ship.max_messages_in_flight": "max-msg-in-flight",
"ship.chain": "chain", "ship.chain": "chain",
"ship.blacklist": "blacklist",
"ship.blacklist_is_whitelist": "blacklist-is-whitelist",
"ship.table_deltas": "table-deltas",
}, },
} }
} }
@ -122,12 +109,6 @@ func (b *Builder) Build() (*Config, error) {
mapstructure.TextUnmarshallerHookFunc(), mapstructure.TextUnmarshallerHookFunc(),
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
func(f reflect.Type, t reflect.Type, in interface{}) (interface{}, error) {
if t == reflect.TypeOf(types.Blacklist{}) {
return decodeIntoBlacklist(in)
}
return in, nil
},
) )
err := v.Unmarshal(&conf, viper.DecodeHook(decoders)) err := v.Unmarshal(&conf, viper.DecodeHook(decoders))
@ -137,61 +118,3 @@ func (b *Builder) Build() (*Config, error) {
return &conf, nil return &conf, nil
} }
// Decode a generic structure into types.Blacklist
func decodeIntoBlacklist(in any) (*types.Blacklist, error) {
switch v := in.(type) {
// Standard map structure.
case map[string]any:
return blacklistParseMap(v)
// slice of "contract:action" pairs. Usually from CLI
case []string:
return blacklistParseSlice(v)
// Sometimes we have a slice of interfaces.
// Need to convert it to a slice of strings.
case []any:
sv := make([]string, len(v))
for i, j := range v {
sv[i] = j.(string)
}
return blacklistParseSlice(sv)
}
return nil, fmt.Errorf("Must be a string slice")
}
// Blacklist map parser
func blacklistParseMap(in map[string]any) (*types.Blacklist, error) {
list := &types.Blacklist{}
for k, v := range in {
switch v := v.(type) {
case []any:
for _, v := range v {
list.Add(k, v.(string))
}
case any:
list.Add(k, v.(string))
}
}
return list, nil
}
// Blacklist slice parser
func blacklistParseSlice(in []string) (*types.Blacklist, error) {
list := &types.Blacklist{}
for _, i := range in {
var action string
parts := strings.SplitN(i, ":", 2)
if len(parts) < 2 {
action = "*"
} else {
action = parts[1]
}
list.Add(parts[0], action)
}
return list, nil
}

View file

@ -5,10 +5,7 @@ import (
"testing" "testing"
"time" "time"
shipclient "github.com/eosswedenorg-go/antelope-ship-client"
"github.com/eosswedenorg/thalos/internal/log" "github.com/eosswedenorg/thalos/internal/log"
"github.com/eosswedenorg/thalos/internal/types"
"github.com/karlseguin/typed"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -22,18 +19,6 @@ func TestBuilder(t *testing.T) {
Directory: "/path/to/whatever", Directory: "/path/to/whatever",
MaxFileSize: 200, MaxFileSize: 200,
MaxTime: 30 * time.Minute, MaxTime: 30 * time.Minute,
FileTimestampFormat: "20060102@150405",
},
Cache: Cache{
Storage: "memcached",
Options: typed.Typed{
"ttl": "300m",
"size": 400,
"super_fast_mode": true,
},
},
AbiCache: AbiCache{
ApiTimeout: time.Minute * 300,
}, },
Ship: ShipConfig{ Ship: ShipConfig{
Url: "127.0.0.1:8089", Url: "127.0.0.1:8089",
@ -41,11 +26,6 @@ func TestBuilder(t *testing.T) {
EndBlockNum: 23872222, EndBlockNum: 23872222,
IrreversibleOnly: true, IrreversibleOnly: true,
MaxMessagesInFlight: 1337, MaxMessagesInFlight: 1337,
Blacklist: *types.NewBlacklist(map[string][]string{
"eosio": {"noop"},
"contract": {"skip1", "skip2"},
}),
BlacklistIsWhitelist: true,
}, },
Telegram: TelegramConfig{ Telegram: TelegramConfig{
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw", Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
@ -65,32 +45,17 @@ func TestBuilder(t *testing.T) {
name: "ship-reader-1" name: "ship-reader-1"
api: "http://127.0.0.1:8080" api: "http://127.0.0.1:8080"
message_codec: "mojibake" message_codec: "mojibake"
cache:
storage: memcached
options:
ttl: 300m
size: 400
super_fast_mode: true
abi_cache:
api_timeout: 300m
log: log:
filename: some_file.log filename: some_file.log
directory: /path/to/whatever directory: /path/to/whatever
maxtime: 30m maxtime: 30m
maxfilesize: 200b maxfilesize: 200b
file_timestamp_format: 20060102@150405
ship: ship:
url: "127.0.0.1:8089" url: "127.0.0.1:8089"
irreversible_only: true irreversible_only: true
max_messages_in_flight: 1337 max_messages_in_flight: 1337
start_block_num: 23671836 start_block_num: 23671836
end_block_num: 23872222 end_block_num: 23872222
blacklist:
eosio: noop
contract:
- skip1
- skip2
blacklist_is_whitelist: true
telegram: telegram:
id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw" id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
channel: -123456789 channel: -123456789
@ -108,37 +73,73 @@ redis:
require.Equal(t, &expected, cfg) require.Equal(t, &expected, cfg)
} }
func TestBuilder_WithDefaultConfig(t *testing.T) { func TestBuilder_ConfigWithFlags(t *testing.T) {
expected := Config{ expected := Config{
MessageCodec: "json", Name: "ship-reader-1",
Api: "https://api.example.com",
MessageCodec: "msgpack",
Log: log.Config{ Log: log.Config{
MaxFileSize: 10 * 1000 * 1000, Filename: "mylog.log",
MaxTime: time.Hour * 24, Directory: "/var/log",
FileTimestampFormat: "2006-01-02_150405", MaxFileSize: 200,
}, MaxTime: 30 * time.Minute,
Cache: Cache{
Storage: "redis",
},
AbiCache: AbiCache{
ApiTimeout: time.Second,
}, },
Ship: ShipConfig{ Ship: ShipConfig{
Url: "ws://127.0.0.1:8080", Url: "127.0.0.1:8089",
StartBlockNum: shipclient.NULL_BLOCK_NUMBER, StartBlockNum: 23671836,
EndBlockNum: shipclient.NULL_BLOCK_NUMBER, EndBlockNum: 23872222,
MaxMessagesInFlight: 10, IrreversibleOnly: true,
EnableTableDeltas: true, MaxMessagesInFlight: 1337,
},
Telegram: TelegramConfig{
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
Channel: -123456789,
}, },
Redis: RedisConfig{ Redis: RedisConfig{
Addr: "127.0.0.1:6379", Addr: "localhost:6379",
Prefix: "ship", User: "userfromcli",
Password: "passwd",
DB: 4,
Prefix: "some::ship",
}, },
} }
cfg, err := NewBuilder(). builder := NewBuilder()
SetSource(bytes.NewReader([]byte(``))). builder.SetSource(bytes.NewBuffer([]byte(`
SetFlags(GetFlags()). name: "ship-reader-1"
Build() api: "http://127.0.0.1:8080"
message_codec: "mojibake"
log:
filename: mylog.log
directory: /var/log
maxtime: 30m
maxfilesize: 200b
ship:
url: "127.0.0.1:8089"
irreversible_only: true
max_messages_in_flight: 1337
start_block_num: 23671836
end_block_num: 23872222
telegram:
id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
channel: -123456789
redis:
addr: "localhost:6379"
user: "myuser"
password: "passwd"
db: 4
prefix: "some::ship"
`)))
flags := GetFlags()
require.NoError(t, flags.Set("url", "https://api.example.com"))
require.NoError(t, flags.Set("codec", "msgpack"))
require.NoError(t, flags.Set("redis-user", "userfromcli"))
builder.SetFlags(flags)
cfg, err := builder.Build()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &expected, cfg) require.Equal(t, &expected, cfg)
@ -160,22 +161,16 @@ func TestBuilder_Flags(t *testing.T) {
require.NoError(t, flags.Set("redis-password", "secret123")) require.NoError(t, flags.Set("redis-password", "secret123"))
require.NoError(t, flags.Set("redis-db", "3")) require.NoError(t, flags.Set("redis-db", "3"))
require.NoError(t, flags.Set("redis-prefix", "custom-prefix")) require.NoError(t, flags.Set("redis-prefix", "custom-prefix"))
require.NoError(t, flags.Set("cache", "memcached"))
require.NoError(t, flags.Set("abi-cache-api-timeout", "16h"))
require.NoError(t, flags.Set("telegram-id", "72983126312982618")) require.NoError(t, flags.Set("telegram-id", "72983126312982618"))
require.NoError(t, flags.Set("telegram-channel", "-293492332")) require.NoError(t, flags.Set("telegram-channel", "-293492332"))
require.NoError(t, flags.Set("log-max-filesize", "25mb")) require.NoError(t, flags.Set("log-max-filesize", "25mb"))
require.NoError(t, flags.Set("log-max-time", "10m")) require.NoError(t, flags.Set("log-max-time", "10m"))
require.NoError(t, flags.Set("log-file-timestamp", "0102-15:04:05"))
require.NoError(t, flags.Set("ship-url", "ws://myship.com:7823")) require.NoError(t, flags.Set("ship-url", "ws://myship.com:7823"))
require.NoError(t, flags.Set("start-block", "7327833")) require.NoError(t, flags.Set("start-block", "7327833"))
require.NoError(t, flags.Set("end-block", "329408392")) require.NoError(t, flags.Set("end-block", "329408392"))
require.NoError(t, flags.Set("irreversible-only", "true")) require.NoError(t, flags.Set("irreversible-only", "true"))
require.NoError(t, flags.Set("max-msg-in-flight", "98")) require.NoError(t, flags.Set("max-msg-in-flight", "98"))
require.NoError(t, flags.Set("chain", "wax")) require.NoError(t, flags.Set("chain", "wax"))
require.NoError(t, flags.Set("blacklist", "contract:action1,contract:action2,contract2:action1"))
require.NoError(t, flags.Set("blacklist-is-whitelist", "true"))
require.NoError(t, flags.Set("table-deltas", "false"))
cfg, err := NewBuilder(). cfg, err := NewBuilder().
SetSource(bytes.NewReader([]byte(``))). SetSource(bytes.NewReader([]byte(``))).
@ -188,13 +183,6 @@ func TestBuilder_Flags(t *testing.T) {
Log: log.Config{ Log: log.Config{
MaxFileSize: 25 * 1000 * 1000, // 25 mb MaxFileSize: 25 * 1000 * 1000, // 25 mb
MaxTime: time.Minute * 10, MaxTime: time.Minute * 10,
FileTimestampFormat: "0102-15:04:05",
},
Cache: Cache{
Storage: "memcached",
},
AbiCache: AbiCache{
ApiTimeout: time.Hour * 16,
}, },
Ship: ShipConfig{ Ship: ShipConfig{
Url: "ws://myship.com:7823", Url: "ws://myship.com:7823",
@ -203,11 +191,6 @@ func TestBuilder_Flags(t *testing.T) {
MaxMessagesInFlight: 98, MaxMessagesInFlight: 98,
IrreversibleOnly: true, IrreversibleOnly: true,
Chain: "wax", Chain: "wax",
Blacklist: *types.NewBlacklist(map[string][]string{
"contract": {"action1", "action2"},
"contract2": {"action1"},
}),
BlacklistIsWhitelist: true,
}, },
Telegram: TelegramConfig{ Telegram: TelegramConfig{
Id: "72983126312982618", Id: "72983126312982618",
@ -225,29 +208,3 @@ func TestBuilder_Flags(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &expected, cfg) require.Equal(t, &expected, cfg)
} }
func TestBuilder_BlacklistSlice(t *testing.T) {
expected := Config{
Ship: ShipConfig{
Blacklist: *types.NewBlacklist(map[string][]string{
"contract": {"action"},
"contract2": {"action2"},
"contract3": {"*"},
}),
},
}
builder := NewBuilder()
builder.SetSource(bytes.NewBuffer([]byte(`
ship:
blacklist:
- "contract:action"
- "contract2:action2"
- contract3
`)))
cfg, err := builder.Build()
require.NoError(t, err)
require.Equal(t, &expected, cfg)
}

View file

@ -7,7 +7,6 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// Get a flag set with all flags mapping to a config value.
func GetFlags() *pflag.FlagSet { func GetFlags() *pflag.FlagSet {
flags := pflag.FlagSet{} flags := pflag.FlagSet{}
@ -26,34 +25,22 @@ func GetFlags() *pflag.FlagSet {
flags.String("telegram-id", "", "Id of telegram bot") flags.String("telegram-id", "", "Id of telegram bot")
flags.Int64("telegram-channel", 0, "Telegram channel to send notifications to") flags.Int64("telegram-channel", 0, "Telegram channel to send notifications to")
// Cache
flags.String("cache", "redis", "What cache driver to use")
// AbiCache
flags.Duration("abi-cache-api-timeout", time.Second, "Duration before the api call times out when the ABI cache requests an abi.")
// Log // Log
flags.StringP("log", "l", "", "Path to log file (default: print to stdout/stderr)") flags.StringP("log", "l", "", "Path to log file (default: print to stdout/stderr)")
flags.String("log-max-filesize", "10mb", "Max filesize for logfile to rotate") flags.String("log-max-filesize", "10mb", "Max filesize for logfile to rotate")
flags.Duration("log-max-time", time.Hour*24, "Max time for logfile to rotate") flags.Duration("log-max-time", time.Hour*24, "Max time for logfile to rotate")
flags.String("log-file-timestamp", "2006-01-02_150405", "Timestamp format to use when rotating log files")
// Ship // Ship
flags.String("ship-url", "ws://127.0.0.1:8080", "Url to ship node") flags.String("ship-url", "ws://127.0.0.1:8080", "Url to ship node")
flags.Uint32("start-block", shipclient.NULL_BLOCK_NUMBER, "Start to stream from this block") flags.Uint32("start-block", shipclient.NULL_BLOCK_NUMBER, "Start to stream from this block")
flags.Uint32("end-block", shipclient.NULL_BLOCK_NUMBER, "Stop streaming when this block is reached") flags.Uint32("end-block", shipclient.NULL_BLOCK_NUMBER, "Stop streaming when this block is reached")
flags.Lookup("start-block").DefValue = "Config value, cache, head from api" flags.Lookup("start-block").DefValue = "config value, cache, head from api"
flags.Lookup("end-block").DefValue = "none" flags.Lookup("end-block").DefValue = "none"
flags.Bool("table-deltas", true, "True if thalos should receive and process table deltas from ship.")
flags.Bool("irreversible-only", false, "Only stream irreversible blocks from ship") flags.Bool("irreversible-only", false, "Only stream irreversible blocks from ship")
flags.Int("max-msg-in-flight", 10, "Maximum messages that can be sent from SHIP without acknowledgement") flags.Int("max-msg-in-flight", 10, "Maximum messages that can be sent from SHIP without acknowledgement")
flags.String("chain", "", "ChainID used in channel namespace, can be any string (default from api)") flags.String("chain", "", "ChainID used in channel namespace, can be any string (default from api)")
flags.StringSlice("blacklist", []string{}, "Define a list of 'contract:action' pairs that will be blacklisted (Thalos will not process those actions)")
flags.Bool("blacklist-is-whitelist", false, "Thalos will treat the blacklist as a whitelist")
return &flags return &flags
} }

View file

@ -1,11 +1,7 @@
package config package config
import ( import (
"time"
"github.com/eosswedenorg/thalos/internal/log" "github.com/eosswedenorg/thalos/internal/log"
"github.com/eosswedenorg/thalos/internal/types"
"github.com/karlseguin/typed"
) )
type RedisConfig struct { type RedisConfig struct {
@ -16,20 +12,11 @@ type RedisConfig struct {
Prefix string `yaml:"prefix"` Prefix string `yaml:"prefix"`
} }
type Cache struct {
Storage string `yaml:"storage" mapstructure:"storage"`
Options typed.Typed `yaml:"options" mapstructure:"options"`
}
type TelegramConfig struct { type TelegramConfig struct {
Id string `yaml:"id" mapstructure:"id"` Id string `yaml:"id" mapstructure:"id"`
Channel int64 `yaml:"channel" mapstructure:"channel"` Channel int64 `yaml:"channel" mapstructure:"channel"`
} }
type AbiCache struct {
ApiTimeout time.Duration `yaml:"api_timeout" mapstructure:"api_timeout"`
}
type ShipConfig struct { type ShipConfig struct {
Url string `yaml:"url" mapstructure:"url"` Url string `yaml:"url" mapstructure:"url"`
IrreversibleOnly bool `yaml:"irreversible_only" mapstructure:"irreversible_only"` IrreversibleOnly bool `yaml:"irreversible_only" mapstructure:"irreversible_only"`
@ -37,9 +24,6 @@ type ShipConfig struct {
StartBlockNum uint32 `yaml:"start_block_num" mapstructure:"start_block_num"` StartBlockNum uint32 `yaml:"start_block_num" mapstructure:"start_block_num"`
EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"` EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"`
Chain string `yaml:"chain" mapstructure:"chain"` Chain string `yaml:"chain" mapstructure:"chain"`
Blacklist types.Blacklist `yaml:"blacklist" mapstructure:"blacklist"`
BlacklistIsWhitelist bool `yaml:"blacklist_is_whitelist" mapstructure:"blacklist_is_whitelist"`
EnableTableDeltas bool `yaml:"table_deltas" mapstructure:"table_deltas"`
} }
type Config struct { type Config struct {
@ -49,12 +33,8 @@ type Config struct {
Log log.Config `yaml:"log" mapstructure:"log"` Log log.Config `yaml:"log" mapstructure:"log"`
Cache Cache `yaml:"cache" mapstructure:"cache"`
Redis RedisConfig `yaml:"redis" mapstructure:"redis"` Redis RedisConfig `yaml:"redis" mapstructure:"redis"`
MessageCodec string `yaml:"message_codec" mapstructure:"message_codec"` MessageCodec string `yaml:"message_codec" mapstructure:"message_codec"`
AbiCache AbiCache `yaml:"abi_cache" mapstructure:"abi_cache"`
Telegram TelegramConfig `yaml:"telegram" mapstructure:"telegram"` Telegram TelegramConfig `yaml:"telegram" mapstructure:"telegram"`
} }

View file

@ -24,8 +24,8 @@ func open(filename string) (*os.File, error) {
} }
// Open a new rotating file. // Open a new rotating file.
func NewRotatingFile(filename string, opts ...RotatingFileOption) (*RotatingFile, error) { func NewRotatingFile(filename string, maxSize int64, maxAge time.Duration) (*RotatingFile, error) {
if err := os.MkdirAll(path.Dir(filename), 0o755); err != nil && !os.IsExist(err) { if err := os.MkdirAll(path.Dir(filename), 0o766); err != nil && !os.IsExist(err) {
return nil, err return nil, err
} }
@ -39,18 +39,14 @@ func NewRotatingFile(filename string, opts ...RotatingFileOption) (*RotatingFile
return nil, err return nil, err
} }
file := &RotatingFile{ return &RotatingFile{
fd: fd, fd: fd,
size: stat.Size(), size: stat.Size(),
maxSize: maxSize,
ts: time.Now(), ts: time.Now(),
maxAge: maxAge,
format: "2006-01-02_150405", format: "2006-01-02_150405",
} }, nil
for _, opt := range opts {
opt(file)
}
return file, nil
} }
// Open a new rotating file using a config struct. // Open a new rotating file using a config struct.
@ -59,13 +55,7 @@ func NewRotatingFileFromConfig(config Config, suffix string) (*RotatingFile, err
suffix = "_" + suffix suffix = "_" + suffix
} }
filename := config.GetFilePath() + suffix + ".log" return NewRotatingFile(config.GetFilePath()+suffix+".log", int64(config.MaxFileSize), config.MaxTime)
return NewRotatingFile(filename,
WithMaxAge(config.MaxTime),
WithMaxSize(int64(config.MaxFileSize)),
WithTimestampFormat(config.FileTimestampFormat),
)
} }
func (w *RotatingFile) newFilename(name string) string { func (w *RotatingFile) newFilename(name string) string {

View file

@ -1,25 +0,0 @@
package log
import "time"
type RotatingFileOption func(*RotatingFile)
func WithTimestampFormat(value string) RotatingFileOption {
return func(f *RotatingFile) {
if len(value) > 0 {
f.format = value
}
}
}
func WithMaxSize(value int64) RotatingFileOption {
return func(f *RotatingFile) {
f.maxSize = value
}
}
func WithMaxAge(value time.Duration) RotatingFileOption {
return func(f *RotatingFile) {
f.maxAge = value
}
}

View file

@ -15,9 +15,6 @@ type Config struct {
// Directory where the log files are stored. // Directory where the log files are stored.
Directory string `yaml:"directory" mapstructure:"directory"` Directory string `yaml:"directory" mapstructure:"directory"`
// Timestamp format when rotation files.
FileTimestampFormat string `yaml:"file_timestamp_format" mapstructure:"file_timestamp_format"`
// Maximum filesize, the log is rotated when this size is exceeded. // Maximum filesize, the log is rotated when this size is exceeded.
MaxFileSize types.Size `yaml:"maxfilesize" mapstructure:"maxfilesize"` MaxFileSize types.Size `yaml:"maxfilesize" mapstructure:"maxfilesize"`

View file

@ -1,74 +0,0 @@
package server
import (
"errors"
"github.com/eosswedenorg/thalos/api"
"github.com/eosswedenorg/thalos/api/message"
"github.com/eosswedenorg/thalos/internal/driver"
)
// MessageQueue takes care of message routing and encoding
type MessageQueue struct {
// Writer to write messages to
writer driver.Writer
// Encoder to encode messages with
encode message.Encoder
}
func NewMessageQueue(writer driver.Writer, encoder message.Encoder) MessageQueue {
return MessageQueue{
writer: writer,
encode: encoder,
}
}
func (mq MessageQueue) PostHeartbeat(hb message.HeartBeat) error {
return mq.post(hb, api.HeartbeatChannel)
}
func (mq MessageQueue) PostRollback(rb message.RollbackMessage) error {
return mq.post(rb, api.RollbackChannel)
}
func (mq MessageQueue) PostTransactionTrace(trace message.TransactionTrace) error {
return mq.post(trace, api.TransactionChannel)
}
// Post a ActionTrace message to the queue
func (mq MessageQueue) PostAction(act message.ActionTrace) error {
return mq.post(act,
api.ActionChannel{}.Channel(),
api.ActionChannel{Name: act.Name}.Channel(),
api.ActionChannel{Contract: act.Contract}.Channel(),
api.ActionChannel{Name: act.Name, Contract: act.Contract}.Channel(),
)
}
func (mq MessageQueue) PostTableDelta(delta message.TableDelta) error {
return mq.post(delta,
api.TableDeltaChannel{}.Channel(),
api.TableDeltaChannel{Name: delta.Name}.Channel(),
)
}
func (mq MessageQueue) Flush() error {
return mq.writer.Flush()
}
func (mq MessageQueue) Close() error {
return mq.writer.Close()
}
func (mq MessageQueue) post(v interface{}, channels ...api.Channel) error {
payload, err := mq.encode(v)
if err == nil {
for _, channel := range channels {
if w_err := mq.writer.Write(channel, payload); err != nil {
err = errors.Join(w_err)
}
}
}
return err
}

View file

@ -1,24 +1,36 @@
package server package server
import ( import (
"bytes" "encoding/hex"
"errors" "encoding/json"
"github.com/eosswedenorg/thalos/api"
"github.com/eosswedenorg/thalos/api/message" "github.com/eosswedenorg/thalos/api/message"
"github.com/eosswedenorg/thalos/internal/abi" "github.com/eosswedenorg/thalos/internal/abi"
"github.com/eosswedenorg/thalos/internal/driver" "github.com/eosswedenorg/thalos/internal/driver"
ship_helper "github.com/eosswedenorg/thalos/internal/ship"
"github.com/eosswedenorg/thalos/internal/types"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/eoscanada/eos-go"
"github.com/eoscanada/eos-go/ship"
shipclient "github.com/eosswedenorg-go/antelope-ship-client" shipclient "github.com/eosswedenorg-go/antelope-ship-client"
"github.com/shufflingpixels/antelope-go/chain"
"github.com/shufflingpixels/antelope-go/ship"
) )
// logDecoratedEncoder decorates a message.Encoder and logs any error.
func logDecoratedEncoder(encoder message.Encoder) message.Encoder {
return func(v interface{}) ([]byte, error) {
payload, err := encoder(v)
if err != nil {
log.WithError(err).
WithField("v", v).
Warn("Failed to encode message")
}
return payload, err
}
}
// A ShipProcessor will consume messages from a ship stream, convert the messages into // A ShipProcessor will consume messages from a ship stream, convert the messages into
// thalos specific ones, encode them and finally post them to an api.Writer // thalos specfic ones, encode them and finally post them to an api.Writer
type ShipProcessor struct { type ShipProcessor struct {
// The ship stream to process. // The ship stream to process.
shipStream *shipclient.Stream shipStream *shipclient.Stream
@ -26,7 +38,11 @@ type ShipProcessor struct {
// Abi manager used for cacheing // Abi manager used for cacheing
abi *abi.AbiManager abi *abi.AbiManager
queue MessageQueue // Writer to send messages to.
writer driver.Writer
// Encoder used to encode messages
encode message.Encoder
// Function for saving state. // Function for saving state.
saver StateSaver saver StateSaver
@ -35,13 +51,10 @@ type ShipProcessor struct {
state State state State
// System contract ("eosio" per default) // System contract ("eosio" per default)
syscontract chain.Name syscontract eos.AccountName
// ABI Returned from SHIP // ABI Returned from SHIP
shipABI *chain.Abi shipABI *eos.ABI
// Action blacklist
blacklist types.Blacklist
} }
// SpawnProcessor creates a new ShipProccessor that consumes the shipclient.Stream passed to it. // SpawnProcessor creates a new ShipProccessor that consumes the shipclient.Stream passed to it.
@ -49,9 +62,10 @@ func SpawnProccessor(shipStream *shipclient.Stream, loader StateLoader, saver St
processor := &ShipProcessor{ processor := &ShipProcessor{
saver: saver, saver: saver,
abi: abi, abi: abi,
writer: writer,
shipStream: shipStream, shipStream: shipStream,
syscontract: chain.N("eosio"), encode: logDecoratedEncoder(codec.Encoder),
queue: NewMessageQueue(writer, codec.Encoder), syscontract: eos.AccountName("eosio"),
} }
loader(&processor.state) loader(&processor.state)
@ -61,46 +75,66 @@ func SpawnProccessor(shipStream *shipclient.Stream, loader StateLoader, saver St
shipStream.InitHandler = processor.initHandler shipStream.InitHandler = processor.initHandler
// Needed because if nil, traces/table deltas will not be included in the response from ship. // Needed because if nil, traces/table deltas will not be included in the response from ship.
shipStream.TraceHandler = func(*ship.TransactionTraceArray) {} shipStream.TraceHandler = func([]*ship.TransactionTraceV0) {}
shipStream.TableDeltaHandler = func(*ship.TableDeltaArray) {} shipStream.TableDeltaHandler = func([]*ship.TableDeltaV0) {}
return processor return processor
} }
func (processor *ShipProcessor) FetchDeltas(value bool) { func (processor *ShipProcessor) initHandler(abi *eos.ABI) {
if value {
// empty callback will signal that traces should be included in the response from ship.
processor.shipStream.TableDeltaHandler = func(*ship.TableDeltaArray) {}
} else {
processor.shipStream.TableDeltaHandler = nil
}
}
func (processor *ShipProcessor) SetBlacklist(list types.Blacklist) {
processor.blacklist = list
}
func (processor *ShipProcessor) initHandler(abi *chain.Abi) {
processor.shipABI = abi processor.shipABI = abi
} }
func (processor *ShipProcessor) queueMessage(channel api.Channel, payload []byte) bool {
err := processor.writer.Write(channel, payload)
if err != nil {
log.WithError(err).Errorf("Failed to post to channel '%s'", channel)
return false
}
return true
}
func (processor *ShipProcessor) encodeQueue(channel api.Channel, v interface{}) bool {
if payload, err := processor.encode(v); err == nil {
return processor.queueMessage(channel, payload)
}
return false
}
func decode(abi *eos.ABI, act *ship.Action, v any) error {
jsondata, err := abi.DecodeAction(act.Data, act.Name)
if err != nil {
return err
}
return json.Unmarshal(jsondata, v)
}
// updateAbiFromAction updates the contract abi based on the ship.Action passed. // updateAbiFromAction updates the contract abi based on the ship.Action passed.
func (processor *ShipProcessor) updateAbiFromAction(act *chain.Action) error { func (processor *ShipProcessor) updateAbiFromAction(act *ship.Action) error {
ABI, err := processor.abi.GetAbi(processor.syscontract)
if err != nil {
return err
}
set_abi := struct { set_abi := struct {
Account chain.Name Abi string
Abi chain.Bytes Account eos.AccountName
}{} }{}
if err := decode(ABI, act, &set_abi); err != nil {
if err := act.DecodeInto(&set_abi); err != nil {
return err return err
} }
abi := chain.Abi{} binary_abi, err := hex.DecodeString(set_abi.Abi)
decoder := chain.NewDecoder(bytes.NewReader(set_abi.Abi)) if err != nil {
if err := decoder.Decode(&abi); err != nil {
return err return err
} }
return processor.abi.SetAbi(set_abi.Account, &abi)
contract_abi := eos.ABI{}
if err = eos.UnmarshalBinary(binary_abi, &contract_abi); err != nil {
return err
}
return processor.abi.SetAbi(set_abi.Account, &contract_abi)
} }
// Get the current block. // Get the current block.
@ -108,15 +142,49 @@ func (processor *ShipProcessor) GetCurrentBlock() uint32 {
return processor.state.CurrentBlock return processor.state.CurrentBlock
} }
func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, blockNumber uint32, block *ship.SignedBlock, trace *ship.TransactionTraceV0) { // Callback function called by shipclient.Stream when a new block arrives.
logger := log.WithField("type", "trace").WithField("tx_id", trace.ID.String()).Dup() func (processor *ShipProcessor) processBlock(block *ship.GetBlocksResultV0) {
// Check to see if we have a microfork and post a message to
// the rollback channel in that case.
if processor.state.CurrentBlock > 0 && block.ThisBlock.BlockNum < processor.state.CurrentBlock {
log.WithField("old_block", processor.state.CurrentBlock).
WithField("new_block", block.ThisBlock.BlockNum).
Warn("Fork detected, old_block is greater than new_block")
timestamp := block.BlockHeader.Timestamp.Time().UTC() processor.encodeQueue(api.RollbackChannel, message.RollbackMessage{
OldBlockNum: processor.state.CurrentBlock,
NewBlockNum: block.ThisBlock.BlockNum,
})
}
processor.state.CurrentBlock = block.ThisBlock.BlockNum
if block.ThisBlock.BlockNum%100 == 0 {
log.Infof("Current: %d, Head: %d", processor.state.CurrentBlock, block.Head.BlockNum)
}
if block.ThisBlock.BlockNum%10 == 0 {
hb := message.HeartBeat{
BlockNum: block.ThisBlock.BlockNum,
LastIrreversibleBlockNum: block.LastIrreversible.BlockNum,
HeadBlockNum: block.Head.BlockNum,
}
processor.encodeQueue(api.HeartbeatChannel, hb)
}
mainLogger := log.WithField("block", block.ThisBlock.BlockNum).Dup()
// Process traces
if block.Traces != nil && len(block.Traces.Elem) > 0 {
for _, trace := range block.Traces.AsTransactionTracesV0() {
logger := mainLogger.WithField("type", "trace").WithField("tx_id", trace.ID.String()).Dup()
transaction := message.TransactionTrace{ transaction := message.TransactionTrace{
ID: trace.ID.String(), ID: trace.ID.String(),
BlockNum: blockNumber, BlockNum: block.Block.BlockNumber(),
Timestamp: timestamp, Timestamp: block.Block.Timestamp.Time.UTC(),
Status: trace.Status.String(), Status: trace.Status.String(),
CPUUsageUS: trace.CPUUsageUS, CPUUsageUS: trace.CPUUsageUS,
NetUsage: trace.NetUsage, NetUsage: trace.NetUsage,
@ -129,58 +197,47 @@ func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, blockNum
// Actions // Actions
for _, actionTraceVar := range trace.ActionTraces { for _, actionTraceVar := range trace.ActionTraces {
var act_trace *ship.ActionTraceV1
actionTrace := ship_helper.ToActionTraceV1(actionTraceVar) if trace_v0, ok := actionTraceVar.Impl.(*ship.ActionTraceV0); ok {
actMsg := processor.proccessActionTrace(logger, actionTrace) // convert to v1
if actMsg != nil { act_trace = &ship.ActionTraceV1{
actMsg.TxID = trace.ID.String() ActionOrdinal: trace_v0.ActionOrdinal,
actMsg.BlockNum = blockNumber CreatorActionOrdinal: trace_v0.CreatorActionOrdinal,
actMsg.Timestamp = timestamp Receipt: trace_v0.Receipt,
Receiver: trace_v0.Receiver,
processor.queue.PostAction(*actMsg) Act: trace_v0.Act,
ContextFree: trace_v0.ContextFree,
transaction.ActionTraces = append(transaction.ActionTraces, *actMsg) Elapsed: trace_v0.Elapsed,
Console: trace_v0.Console,
AccountRamDeltas: trace_v0.AccountRamDeltas,
Except: trace_v0.Except,
ErrorCode: trace_v0.ErrorCode,
ReturnValue: []byte{},
} }
} else {
act_trace = actionTraceVar.Impl.(*ship.ActionTraceV1)
} }
if err := processor.queue.PostTransactionTrace(transaction); err != nil {
logger.WithError(err).Error("Failed to post transaction trace")
}
}
func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *ship.ActionTraceV1) *message.ActionTrace {
// Check if actions updates an abi. // Check if actions updates an abi.
if trace.Act.Account == processor.syscontract && trace.Act.Name == chain.N("setabi") { if act_trace.Act.Account == processor.syscontract && act_trace.Act.Name == eos.ActionName("setabi") {
err := processor.updateAbiFromAction(act_trace.Act)
logger.WithFields(log.Fields{
"contract": trace.Act.Account,
"action": trace.Act.Name,
}).Debug("Update contract ABI")
err := processor.updateAbiFromAction(&trace.Act)
if err != nil { if err != nil {
logger.WithError(err).Warn("Failed to update abi") logger.WithError(err).Warn("Failed to update abi")
} }
} }
// Check blacklist if we should skip this action act := message.ActionTrace{
if !processor.blacklist.IsAllowed(trace.Act.Account.String(), trace.Act.Name.String()) { TxID: trace.ID.String(),
logger.WithFields(log.Fields{ BlockNum: block.Block.BlockNumber(),
"contract": trace.Act.Account, Timestamp: block.Block.Timestamp.Time.UTC(),
"action": trace.Act.Name, Name: act_trace.Act.Name.String(),
}).Debug("Found in blacklist, skipping") Contract: act_trace.Act.Account.String(),
return nil Receiver: act_trace.Receiver.String(),
} }
act := &message.ActionTrace{ if act_trace.Receipt != nil {
Name: trace.Act.Name.String(), receipt := act_trace.Receipt.Impl.(*ship.ActionReceiptV0)
Contract: trace.Act.Account.String(),
Receiver: trace.Receiver.String(),
FirstReceiver: trace.Act.Account.String() == trace.Receiver.String(),
}
if trace.Receipt != nil {
receipt := trace.Receipt.V0
act.Receipt = &message.ActionReceipt{ act.Receipt = &message.ActionReceipt{
Receiver: receipt.Receiver.String(), Receiver: receipt.Receiver.String(),
ActDigest: receipt.ActDigest.String(), ActDigest: receipt.ActDigest.String(),
@ -198,157 +255,97 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
} }
} }
for _, auth := range trace.Act.Authorization { for _, auth := range act_trace.Act.Authorization {
act.Authorization = append(act.Authorization, message.PermissionLevel{ act.Authorization = append(act.Authorization, message.PermissionLevel{
Actor: auth.Actor.String(), Actor: auth.Actor.String(),
Permission: auth.Permission.String(), Permission: auth.Permission.String(),
}) })
} }
logger.WithFields(log.Fields{ ABI, err := processor.abi.GetAbi(act_trace.Act.Account)
"contract": trace.Act.Account,
"action": trace.Act.Name,
}).Debug("Reading contract ABI")
ABI, err := processor.abi.GetAbi(trace.Act.Account)
if err == nil { if err == nil {
if act.Data, err = trace.Act.Decode(ABI); err != nil { if err = decode(ABI, act_trace.Act, &act.Data); err != nil {
logger.WithFields(log.Fields{ logger.WithFields(log.Fields{
"contract": trace.Act.Account, "contract": act_trace.Act.Account,
"action": trace.Act.Name, "action": act_trace.Act.Name,
}).WithError(err).Warn("Failed to decode action") }).WithError(err).Warn("Failed to decode action")
} }
} else { } else {
logger.WithField("contract", trace.Act.Account). logger.WithField("contract", act_trace.Act.Account).
WithError(err).Error("Failed to get abi for contract") WithError(err).Error("Failed to get abi for contract")
} }
return act payload, err := processor.encode(act)
}
func (processor *ShipProcessor) proccessDeltaRows(logger *log.Entry, table_name string, rows []ship.Row) []message.TableDeltaRow {
out := []message.TableDeltaRow{}
for _, row := range rows {
msg, err := processor.proccessDeltaRow(row, table_name)
if err != nil { if err != nil {
logger.WithError(err).Warn("Failed to processs table delta row") continue
} }
out = append(out, msg)
}
return out
}
func (processor *ShipProcessor) proccessDeltaRow(row ship.Row, table_name string) (message.TableDeltaRow, error) { transaction.ActionTraces = append(transaction.ActionTraces, act)
channels := []api.Channel{
api.ActionChannel{}.Channel(),
api.ActionChannel{Name: act.Name}.Channel(),
api.ActionChannel{Contract: act.Contract}.Channel(),
api.ActionChannel{Name: act.Name, Contract: act.Contract}.Channel(),
}
for _, channel := range channels {
processor.queueMessage(channel, payload)
}
}
processor.encodeQueue(api.TransactionChannel, transaction)
}
}
// Process deltas
for _, delta := range block.Deltas.AsTableDeltasV0() {
logger := mainLogger.WithField("type", "table_delta").WithField("table", delta.Name).Dup()
rows := []message.TableDeltaRow{}
for _, row := range delta.Rows {
msg := message.TableDeltaRow{ msg := message.TableDeltaRow{
Present: row.Present, Present: row.Present,
RawData: row.Data, RawData: row.Data,
} }
if processor.shipABI == nil { if processor.shipABI != nil {
return msg, errors.New("No SHIP ABI present") v, err := processor.shipABI.DecodeTableRowTyped(delta.Name, row.Data)
} if err == nil {
err = json.Unmarshal(v, &msg.Data)
v, err := processor.shipABI.Decode(bytes.NewReader(row.Data), table_name)
if err != nil { if err != nil {
return msg, errors.New("Failed to decode table delta") logger.WithError(err).Error("Failed to decode json")
} }
data, err := ship_helper.ParseTableDeltaData(v)
if err != nil {
return msg, errors.New("Failed to parse table delta data")
}
msg.Data = data
// Decode contract row data
if table_name == "contract_row" {
dec, err := ship_helper.DecodeContractRow(processor.abi, data)
if err != nil {
return msg, errors.New("Failed to decode contract row")
}
msg.Data["value"] = dec
}
return msg, nil
}
// Callback function called by shipclient.Stream when a new block arrives.
func (processor *ShipProcessor) processBlock(blockResult *ship.GetBlocksResultV0) {
block := ship.SignedBlock{}
blockResult.Block.Unpack(&block)
timestamp := block.BlockHeader.Timestamp.Time().UTC()
blockNumber := blockResult.ThisBlock.BlockNum
// Check to see if we have a microfork and post a message to
// the rollback channel in that case.
if processor.state.CurrentBlock > 0 && blockNumber < processor.state.CurrentBlock {
msg := message.RollbackMessage{
OldBlockNum: processor.state.CurrentBlock,
NewBlockNum: blockResult.ThisBlock.BlockNum,
}
log.WithField("old_block", msg.OldBlockNum).
WithField("new_block", msg.NewBlockNum).
Warn("Fork detected, old_block is greater than new_block")
if err := processor.queue.PostRollback(msg); err != nil {
log.WithError(err).Error("Failed to write rollback message")
}
}
processor.state.CurrentBlock = blockNumber
if blockResult.ThisBlock.BlockNum%100 == 0 {
log.Infof("Current: %d, Head: %d", processor.state.CurrentBlock, blockResult.Head.BlockNum)
}
if blockResult.ThisBlock.BlockNum%10 == 0 {
hb := message.HeartBeat{
BlockNum: blockNumber,
LastIrreversibleBlockNum: blockResult.LastIrreversible.BlockNum,
HeadBlockNum: blockResult.Head.BlockNum,
}
if err := processor.queue.PostHeartbeat(hb); err != nil {
log.WithError(err).Error("Failed to write heartbeat message")
}
}
mainLogger := log.WithField("block", blockNumber).Dup()
// Process traces
if blockResult.Traces != nil {
unpacked := []ship.TransactionTrace{}
if err := blockResult.Traces.Unpack(&unpacked); err != nil {
mainLogger.WithError(err).Error("Failed to unpack transaction traces")
} else { } else {
for _, trace := range unpacked { logger.Error("Failed to decode table delta")
processor.processTransactionTrace(mainLogger, blockNumber, &block, trace.V0)
} }
}
}
// Process deltas
if blockResult.Deltas != nil {
deltas := []ship.TableDelta{}
if err := blockResult.Deltas.Unpack(&deltas); err != nil {
mainLogger.WithError(err).Error("Failed to unpack table deltas")
} else { } else {
logger := mainLogger.WithField("type", "table_delta").Dup() logger.Warn("No SHIP ABI present")
for _, delta := range deltas {
msg := message.TableDelta{
BlockNum: blockNumber,
Timestamp: timestamp,
Name: delta.V0.Name,
Rows: processor.proccessDeltaRows(logger, delta.V0.Name, delta.V0.Rows),
} }
if err := processor.queue.PostTableDelta(msg); err != nil { rows = append(rows, msg)
logger.WithError(err).Error("Failed to post table delta message")
} }
message := message.TableDelta{
BlockNum: block.Block.BlockNumber(),
Timestamp: block.Block.Timestamp.Time.UTC(),
Name: delta.Name,
Rows: rows,
} }
channels := []api.Channel{
api.TableDeltaChannel{}.Channel(),
api.TableDeltaChannel{Name: delta.Name}.Channel(),
}
for _, channel := range channels {
processor.encodeQueue(channel, message)
} }
} }
err := processor.queue.Flush() err := processor.writer.Flush()
if err != nil { if err != nil {
log.WithError(err).Error("Failed to send messages") log.WithError(err).Error("Failed to send messages")
} }
@ -361,5 +358,5 @@ func (processor *ShipProcessor) processBlock(blockResult *ship.GetBlocksResultV0
// Close closes the writer associated with the processor. // Close closes the writer associated with the processor.
func (processor *ShipProcessor) Close() error { func (processor *ShipProcessor) Close() error {
return processor.queue.Close() return processor.writer.Close()
} }

View file

@ -1,27 +0,0 @@
package ship
import (
"github.com/shufflingpixels/antelope-go/ship"
)
// convert a ActionTrace to ActionTraceV1
func ToActionTraceV1(trace *ship.ActionTrace) *ship.ActionTraceV1 {
if trace.V0 != nil {
// convert to v1
return &ship.ActionTraceV1{
ActionOrdinal: trace.V0.ActionOrdinal,
CreatorActionOrdinal: trace.V0.CreatorActionOrdinal,
Receipt: trace.V0.Receipt,
Receiver: trace.V0.Receiver,
Act: trace.V0.Act,
ContextFree: trace.V0.ContextFree,
Elapsed: trace.V0.Elapsed,
Console: trace.V0.Console,
AccountRamDeltas: trace.V0.AccountRamDeltas,
Except: trace.V0.Except,
ErrorCode: trace.V0.ErrorCode,
ReturnValue: []byte{},
}
}
return trace.V1
}

View file

@ -1,37 +0,0 @@
package ship
import (
"bytes"
"github.com/eosswedenorg/thalos/internal/abi"
"github.com/mitchellh/mapstructure"
"github.com/shufflingpixels/antelope-go/chain"
)
type ContractRow struct {
Code chain.Name `mapstructure:"code"`
Scope chain.Name `mapstructure:"scope"`
Table chain.Name `mapstructure:"table"`
PrimaryKey string `mapstructure:"primary_key"`
Payer chain.Name `mapstructure:"payer"`
Value chain.Bytes `mapstructure:"value"`
}
func ParseContractRow(v map[string]interface{}) (*ContractRow, error) {
out := &ContractRow{}
err := mapstructure.WeakDecode(v, out)
return out, err
}
func DecodeContractRow(manager *abi.AbiManager, data map[string]any) (any, error) {
row, err := ParseContractRow(data)
if err != nil {
return nil, err
}
abi, err := manager.GetAbi(row.Code)
if err != nil {
return nil, err
}
return abi.DecodeTable(bytes.NewReader(row.Value), row.Table)
}

View file

@ -1,32 +0,0 @@
package ship_test
import (
"testing"
"github.com/eosswedenorg/thalos/internal/ship"
"github.com/shufflingpixels/antelope-go/chain"
"github.com/stretchr/testify/assert"
)
func TestContractRow_Parse(t *testing.T) {
expected := &ship.ContractRow{
Code: chain.N("eosio"),
Scope: chain.N("scope"),
Table: chain.N("accounts"),
PrimaryKey: "1278127812",
Payer: chain.N("account1"),
Value: []byte{0x01, 0x01, 0x02, 0x03},
}
actual, err := ship.ParseContractRow(map[string]any{
"code": uint64(6138663577826885632),
"scope": uint64(13990807175891517440),
"table": uint64(3607749779137757184),
"primary_key": uint32(1278127812),
"payer": uint64(3607749778751881216),
"value": []byte{0x01, 0x01, 0x02, 0x03},
})
assert.NoError(t, err)
assert.Equal(t, expected, actual)
}

View file

@ -1,36 +0,0 @@
package ship
import (
"fmt"
"reflect"
)
func parseTableDeltaDataInner(v reflect.Value) reflect.Value {
if IsVariant(v) {
v = v.Index(1)
}
switch v.Kind() {
case reflect.Interface:
return parseTableDeltaDataInner(v.Elem())
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(parseTableDeltaDataInner(v.Index(i)))
}
case reflect.Map:
it := v.MapRange()
for it.Next() {
v.SetMapIndex(it.Key(), parseTableDeltaDataInner(it.Value()))
}
}
return v
}
func ParseTableDeltaData(v any) (map[string]interface{}, error) {
iface := parseTableDeltaDataInner(reflect.ValueOf(v)).Interface()
if out, ok := iface.(map[string]interface{}); ok {
return out, nil
}
return nil, fmt.Errorf("data is not an map")
}

View file

@ -1,70 +0,0 @@
package ship_test
import (
"testing"
"github.com/eosswedenorg/thalos/internal/ship"
"github.com/stretchr/testify/assert"
)
func TestParseTableDeltaData(t *testing.T) {
input := []interface{}{
"resource_limits_state_v0",
map[string]interface{}{
"average_block_cpu_usage": []interface{}{
"usage_accumulator_v0",
map[string]interface{}{
"consumed": 33679,
"last_ordinal": 308855607,
"value_ex": "18525321667",
},
},
"average_block_net_usage": []interface{}{
"usage_accumulator_v0",
map[string]interface{}{
"consumed": 8107,
"last_ordinal": 308855607,
"value_ex": int64(3854030492),
},
},
"slice": []interface{}{
"generated_transaction_v0",
[]interface{}{1, 2, "tree"},
},
"single_value": []interface{}{
"generated_transaction_v0",
uint32(12933729),
},
"total_cpu_weight": "44811223778385154",
"total_net_weight": "134285012330070718",
"total_ram_bytes": "172065109473",
"virtual_cpu_limit": 206081,
"virtual_net_limit": 1048576000,
},
}
expected := map[string]interface{}{
"average_block_cpu_usage": map[string]interface{}{
"consumed": 33679,
"last_ordinal": 308855607,
"value_ex": "18525321667",
},
"average_block_net_usage": map[string]interface{}{
"consumed": 8107,
"last_ordinal": 308855607,
"value_ex": int64(3854030492),
},
"slice": []interface{}{1, 2, "tree"},
"single_value": uint32(12933729),
"total_cpu_weight": "44811223778385154",
"total_net_weight": "134285012330070718",
"total_ram_bytes": "172065109473",
"virtual_cpu_limit": 206081,
"virtual_net_limit": 1048576000,
}
actual, err := ship.ParseTableDeltaData(input)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
}

View file

@ -1,93 +0,0 @@
package ship
import "reflect"
func IsVariantName(name string) bool {
validVariants := []string{
"get_status_request_v0",
"block_position",
"get_status_result_v0",
"get_blocks_request_v0",
"get_blocks_ack_request_v0",
"get_blocks_result_v0",
"row",
"table_delta_v0",
"action",
"account_auth_sequence",
"action_receipt_v0",
"account_delta",
"action_trace_v0",
"partial_transaction_v0",
"transaction_trace_v0",
"packed_transaction",
"transaction_receipt_header",
"transaction_receipt",
"extension",
"block_header",
"signed_block_header",
"signed_block",
"transaction_header",
"transaction",
"code_id",
"account_v0",
"account_metadata_v0",
"code_v0",
"contract_table_v0",
"contract_row_v0",
"contract_index64_v0",
"contract_index128_v0",
"contract_index256_v0",
"contract_index_double_v0",
"contract_index_long_double_v0",
"producer_key",
"producer_schedule",
"block_signing_authority_v0",
"producer_authority",
"producer_authority_schedule",
"chain_config_v0",
"global_property_v0",
"global_property_v1",
"generated_transaction_v0",
"activated_protocol_feature_v0",
"protocol_state_v0",
"key_weight",
"permission_level",
"permission_level_weight",
"wait_weight",
"authority",
"permission_v0",
"permission_link_v0",
"resource_limits_v0",
"usage_accumulator_v0",
"resource_usage_v0",
"resource_limits_state_v0",
"resource_limits_ratio_v0",
"elastic_limit_parameters_v0",
"resource_limits_config_v0",
}
for _, v := range validVariants {
if v == name {
return true
}
}
return false
}
// Check if a structure is a variant type.
// This is not 100% accurate. As variant types comes
// as a simple slice with the types name in the first index
// and the value as the second.
// So there could be some edge cases where this structure is actual data
// and not a variant type although should be super rare.
func IsVariant(v reflect.Value) bool {
if v.Kind() != reflect.Slice || v.Len() != 2 {
return false
}
for v = v.Index(0); v.Kind() == reflect.Interface || v.Kind() == reflect.Pointer; v = v.Elem() {
// Intentionally empty
}
return v.Kind() == reflect.String && IsVariantName(v.String())
}

View file

@ -1,59 +0,0 @@
package types
const BlacklistWildcard = "*"
type Blacklist struct {
table map[string][]string
isWhitelist bool
}
func NewBlacklist(entries map[string][]string) *Blacklist {
return &Blacklist{
table: entries,
}
}
func (bl *Blacklist) SetWhitelist(value bool) *Blacklist {
bl.isWhitelist = value
return bl
}
func (bl Blacklist) Empty() bool {
return len(bl.table) < 1
}
func (bl *Blacklist) Add(contract string, action string) {
if bl.table == nil {
bl.table = map[string][]string{}
}
if len(bl.table[contract]) < 1 {
bl.table[contract] = []string{}
}
bl.table[contract] = append(bl.table[contract], action)
}
func (bl Blacklist) list(contracts ...string) [][]string {
ret := [][]string{}
for _, contract := range contracts {
if v, ok := bl.table[contract]; ok {
ret = append(ret, v)
}
}
return ret
}
func (bl Blacklist) IsAllowed(contract string, action string) bool {
for _, v := range bl.list(contract, BlacklistWildcard) {
for _, act := range v {
if act == action || act == BlacklistWildcard {
return bl.isWhitelist == true
}
}
}
return bl.isWhitelist == false
}
func (bl Blacklist) IsDenied(contract string, action string) bool {
return bl.IsAllowed(contract, action)
}

View file

@ -1,95 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestBlacklist_Empty(t *testing.T) {
bl := Blacklist{
table: map[string][]string{},
}
require.True(t, bl.Empty())
bl.Add("contract", "action1")
require.False(t, bl.Empty())
}
func TestBlacklist_Add(t *testing.T) {
bl := Blacklist{
table: map[string][]string{},
}
bl.Add("contract", "action1")
bl.Add("contract", "action2")
bl.Add("contract2", "action1")
expected := Blacklist{
table: map[string][]string{
"contract": {"action1", "action2"},
"contract2": {"action1"},
},
}
require.Equal(t, expected, bl)
}
func TestBlacklist_IsAllowed(t *testing.T) {
bl := Blacklist{
table: map[string][]string{
"mycontract": {"myaction", "noop"},
},
}
require.False(t, bl.IsAllowed("mycontract", "myaction"))
require.False(t, bl.IsAllowed("mycontract", "noop"))
require.True(t, bl.IsAllowed("mycontract", "xxx"))
require.True(t, bl.IsAllowed("xxx", "yyy"))
}
func TestBlacklist_IsAllowedWildcard(t *testing.T) {
bl := Blacklist{
table: map[string][]string{
"mycontract": {"*"},
"*": {"action1", "action2"},
"evilcontract": {"evilaction"},
},
}
require.False(t, bl.IsAllowed("mycontract", "myaction"))
require.False(t, bl.IsAllowed("mycontract", "noop"))
require.False(t, bl.IsAllowed("mycontract", "xxx"))
// Wildcard contract
require.False(t, bl.IsAllowed("somecontract", "action1"))
require.False(t, bl.IsAllowed("someothercontract", "action1"))
require.False(t, bl.IsAllowed("randomcontract", "action2"))
require.False(t, bl.IsAllowed("evilcontract", "action2"))
require.False(t, bl.IsAllowed("evilcontract", "evilaction"))
require.True(t, bl.IsAllowed("xxx", "yyy"))
require.True(t, bl.IsAllowed("evilcontract", "alloweaction"))
}
func TestBlacklist_Whitelist(t *testing.T) {
bl := Blacklist{
table: map[string][]string{
"mycontract": {"myaction", "noop"},
"*": {"goodaction1", "goodaction2"},
},
}
bl.SetWhitelist(true)
require.True(t, bl.IsAllowed("mycontract", "myaction"))
require.True(t, bl.IsAllowed("mycontract", "noop"))
// Wildcard contract
require.True(t, bl.IsAllowed("mycontract", "goodaction1"))
require.True(t, bl.IsAllowed("someothercontract", "goodaction2"))
require.False(t, bl.IsAllowed("mycontract", "xxx"))
require.False(t, bl.IsAllowed("xxx", "yyy"))
}