mirror of
https://github.com/eosswedenorg/thalos
synced 2026-06-20 09:56:47 +02:00
Compare commits
128 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ad6b44433 | |||
| 365558019c | |||
|
|
438699fb10 | ||
| 07ccab4628 | |||
| 7a3a83a097 | |||
| af5593b5f3 | |||
| bb268aa9fa | |||
|
|
4bc6df8c83 | ||
| ce80405254 | |||
| a2c39f788e | |||
|
|
ece6ccfac1 | ||
| 96b6a79813 | |||
| 2006da6a19 | |||
| eb2032e233 | |||
| 120c2acdc6 | |||
| d5bc23a63e | |||
| 89011b8fc8 | |||
| d959645836 | |||
| 9bdcbacaa0 | |||
| 50adfa0026 | |||
| 213a31570d | |||
| 4ae30f0cc6 | |||
| e2f7efe8b0 | |||
| 0cb86ff771 | |||
| 33c983f6c5 | |||
| bedb8a92e8 | |||
| ffd2504834 | |||
| 6231142e4a | |||
| 84547e5ad3 | |||
| 70aa6cd295 | |||
| 020f81ed65 | |||
| 4f46188ed4 | |||
| ef0ac7a1b8 | |||
| 44dfd4d738 | |||
|
|
08fcdb0590 | ||
| a3c428ee5c | |||
| 6d292a482e | |||
| 982b920576 | |||
|
|
92ac8521ed | ||
| 7e42c27194 | |||
| 0e94ca2688 | |||
| 7197720350 | |||
| c769b6a700 | |||
| 48ca5e4f92 | |||
| c0c062a1b5 | |||
| aaeb2739ab | |||
| fa6508ba90 | |||
| 6d7d004b2b | |||
| a892b40cde | |||
| 14a498d7d6 | |||
| 67d33a280e | |||
| 2ed9518635 | |||
| 0e75fb869b | |||
| 3d83fcc869 | |||
| 4bf589647c | |||
| ecdde63f68 | |||
| 18a8feea9e | |||
| e038be5324 | |||
| ec874430c3 | |||
| 9778fa8610 | |||
| e8b90dab77 | |||
| b7e0cb7b56 | |||
| 8a938c3f9e | |||
| ebbaf6e2c1 | |||
| d1d4bf58b1 | |||
| 2f31eb47d4 | |||
| ec40e954f2 | |||
| b60436c48a | |||
| dccd7c0520 | |||
| 1b1e6a1e33 | |||
| c523f1c797 | |||
| ad90966e24 | |||
| 22896f8859 | |||
| 8c5815a2ce | |||
| 8155d49ef4 | |||
| 621f9ceeea | |||
| 5121098cfe | |||
| d1a6d038a3 | |||
| 18918faad0 | |||
| 20168a9329 | |||
| 4f27307c70 | |||
| 0aee0a97aa | |||
| cbd3196cf9 | |||
| e2443dcd27 | |||
| a9a129e418 | |||
| ef6329d1b7 | |||
| 1a782604d3 | |||
| e0f598eba4 | |||
| 0e124bb866 | |||
| 71f9d4d789 | |||
| a15cf50377 | |||
| dfa370a462 | |||
| 70eda8e397 | |||
| ec664ea207 | |||
| aeeb049403 | |||
| 690dabe6bd | |||
| be35936dc2 | |||
| 32f3f1dfb1 | |||
| 0bc70f82d1 | |||
| 6d14591f6b | |||
| 93a816cb2c | |||
| a79bf8f2f3 | |||
| a466392b84 | |||
| 2b3dc35393 | |||
| 01054fb0bf | |||
| f19304bf55 | |||
| 5b02dfa53f | |||
| a2f8c3dc7c | |||
| 8b8867394f | |||
| ea5b2b8fc2 | |||
| 634205a546 | |||
| 3392533a91 | |||
| f2fe9244a1 | |||
| c414fad347 | |||
| 8bd3736684 | |||
| cc754ee976 | |||
| c02cff0e05 | |||
| 59bcc9f19f | |||
| 95365d0c26 | |||
| c876875a6e | |||
| 871f2ae04d | |||
| 815fa13e94 | |||
| ea52a3848e | |||
| c7f83425bd | |||
| 1c1f537850 | |||
| bcf1b99bd8 | |||
| 79e5777d56 | |||
| 63da39f03b |
54 changed files with 1918 additions and 828 deletions
82
.github/workflows/devbuild.yaml
vendored
Normal file
82
.github/workflows/devbuild.yaml
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
|
|
@ -1,4 +1,6 @@
|
|||
name: Package
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
release:
|
||||
|
|
@ -19,7 +21,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
go-version: 1.22
|
||||
|
||||
- name: compile
|
||||
id: compile
|
||||
|
|
@ -27,7 +29,7 @@ jobs:
|
|||
mkdir -p build/bundle/{bin,logs}
|
||||
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 .
|
||||
echo "version=$(sed -n 's/.*PROGRAM_VERSION\s*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$(sed -n 's/.*PROGRAM_VERSION.*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload thalos-server
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
|
@ -68,7 +70,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
name: Build musl (${{ matrix.arch }})
|
||||
container:
|
||||
image: golang:1.21-alpine3.19
|
||||
image: golang:1.22-alpine3.19
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
|
@ -81,7 +83,7 @@ jobs:
|
|||
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*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$(sed -n 's/.*PROGRAM_VERSION.*=\s*//p' Makefile)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload thalos-server
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
|
@ -127,7 +129,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
go-version: 1.22
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
|
|
|
|||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
|
|
@ -1,5 +1,8 @@
|
|||
name: Test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
|
@ -10,9 +13,10 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go-version: ["1.20", "1.21"]
|
||||
go-version: ["1.22"]
|
||||
arch: [ 386, amd64 ]
|
||||
runs-on: ubuntu-latest
|
||||
name: Test (go v${{ matrix.go-version }})
|
||||
name: Test (${{matrix.arch}} go v${{ matrix.go-version }})
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
|
@ -22,16 +26,16 @@ jobs:
|
|||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
run: GOARCH=${{matrix.arch}} go test -v ./...
|
||||
|
||||
- name: Test API
|
||||
run: cd api; go test -v ./...
|
||||
run: cd api; GOARCH=${{matrix.arch}} go test -v ./...
|
||||
|
||||
test-alpine:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tag: ["1.20-alpine3.19", "1.21-alpine3.19"]
|
||||
tag: [ "1.22-alpine3.19"]
|
||||
runs-on: ubuntu-latest
|
||||
name: Test alpine (${{ matrix.tag }})
|
||||
container:
|
||||
|
|
|
|||
18
Makefile
18
Makefile
|
|
@ -1,24 +1,32 @@
|
|||
|
||||
GO=go
|
||||
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_VERSION=1.1.1
|
||||
PROGRAM_VERSION ?= 1.1.9
|
||||
PREFIX=/usr/local
|
||||
BINDIR=$(PREFIX)/bin
|
||||
CFGDIR=$(PREFIX)/etc/thalos
|
||||
DOCKER_IMAGE_REPO ?= ghcr.io/eosswedenorg/thalos
|
||||
DOCKER_IMAGE_TAG ?= $(PROGRAM_VERSION)
|
||||
|
||||
.PHONY: build build/$(PROGRAM) build/thalos-tools test
|
||||
.PHONY: build build/$(PROGRAM) build/thalos-tools test docker-image docker-publish
|
||||
|
||||
build: build/$(PROGRAM)
|
||||
|
||||
build/$(PROGRAM) :
|
||||
$(GO) build $(GOBUILDFLAGS) -o $@ cmd/thalos/main.go cmd/thalos/server.go
|
||||
$(GO) build $(GOBUILDFLAGS) -o $@ ./cmd/thalos/
|
||||
|
||||
tools : build/thalos-tools
|
||||
|
||||
build/thalos-tools :
|
||||
$(GO) build $(GOBUILDFLAGS) -o $@ $(shell find cmd/tools -type f -name *.go)
|
||||
$(GO) build $(GOBUILDFLAGS) -o $@ ./cmd/tools/
|
||||
|
||||
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 -D build/$(PROGRAM) $(DESTDIR)$(BINDIR)/$(PROGRAM)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
# Thalos
|
||||
|
||||
[](https://github.com/eosswedenorg/thalos/actions/workflows/test.yml)
|
||||
|
||||
[](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.
|
||||
|
||||
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
|
||||
|
||||
You will need golang version `1.20` or later to compile the source.
|
||||
You will need golang version `1.21` or later to compile the source.
|
||||
|
||||
Compile using make:
|
||||
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@ func TestChannel_Is(t *testing.T) {
|
|||
func TestChannel_Format(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
c Channel
|
||||
delim string
|
||||
want string
|
||||
c Channel
|
||||
}{
|
||||
{"Empty", Channel{}, ":", ""},
|
||||
{"Alot#1", Channel{"one", "two", "three"}, "-", "one-two-three"},
|
||||
{"Alot#2", Channel{"first", "second"}, ":", "first:second"},
|
||||
{"Empty", ":", "", Channel{}},
|
||||
{"Alot#1", "-", "one-two-three", Channel{"one", "two", "three"}},
|
||||
{"Alot#2", ":", "first:second", Channel{"first", "second"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -75,11 +75,11 @@ func TestChannel_Format(t *testing.T) {
|
|||
func TestChannel_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
c Channel
|
||||
want string
|
||||
c Channel
|
||||
}{
|
||||
{"Empty", Channel{}, ""},
|
||||
{"Alot", Channel{"one", "two", "three"}, "one/two/three"},
|
||||
{"Empty", "", Channel{}},
|
||||
{"Alot", "one/two/three", Channel{"one", "two", "three"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -93,13 +93,13 @@ func TestChannel_String(t *testing.T) {
|
|||
func TestChannel_Type(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
c Channel
|
||||
want string
|
||||
c Channel
|
||||
}{
|
||||
{"Empty", Channel{}, "unknown"},
|
||||
{"Heartbeat", HeartbeatChannel, "heartbeat"},
|
||||
{"Transaction", TransactionChannel, "transactions"},
|
||||
{"Actions", ActionChannel{}.Channel(), "actions"},
|
||||
{"Empty", "unknown", Channel{}},
|
||||
{"Heartbeat", "heartbeat", HeartbeatChannel},
|
||||
{"Transaction", "transactions", TransactionChannel},
|
||||
{"Actions", "actions", ActionChannel{}.Channel()},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
type handler func([]byte)
|
||||
|
||||
// Client reads and decodes messages from a reader and posts thems to a go channel
|
||||
// Client reads and decodes messages from a reader and posts them to a go channel
|
||||
type Client struct {
|
||||
reader Reader
|
||||
decoder message.Decoder
|
||||
|
|
@ -35,6 +35,7 @@ func (c *Client) Channel() <-chan any {
|
|||
return c.channel
|
||||
}
|
||||
|
||||
// Helper method to post a message to a channel with timeout.
|
||||
func (c *Client) post(msg any) {
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
|
|
@ -46,7 +47,7 @@ func (c *Client) worker(channel Channel, h handler) {
|
|||
for {
|
||||
payload, err := c.reader.Read(channel)
|
||||
if err != nil {
|
||||
// Dont report EOF as an error because it is used
|
||||
// Don't report EOF as an error because it is used
|
||||
// by readers to signal an graceful end of input.
|
||||
if err != io.EOF {
|
||||
c.post(err)
|
||||
|
|
@ -59,7 +60,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.
|
||||
// Returns true if successfull. false otherwise
|
||||
// Returns true if successful. False otherwise
|
||||
func (c *Client) decode(payload []byte, msg any) bool {
|
||||
if err := c.decoder(payload, msg); err != nil {
|
||||
c.post(err)
|
||||
|
|
@ -152,7 +153,7 @@ func (c *Client) Run() {
|
|||
|
||||
func (c *Client) Close() error {
|
||||
err := c.reader.Close()
|
||||
// Wait for all goroutines before closing channel.
|
||||
// Wait for all goroutines to finish before closing channel.
|
||||
c.wg.Wait()
|
||||
close(c.channel)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ go 1.20
|
|||
|
||||
require (
|
||||
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/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/ugorji/go/codec v1.2.12
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ 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/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/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/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
|
||||
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
|
||||
|
|
@ -35,6 +33,8 @@ 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/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/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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package json
|
|||
import (
|
||||
"time"
|
||||
|
||||
jsontime "github.com/eosswedenorg-go/jsontime/v2"
|
||||
"github.com/eosswedenorg/thalos/api/message"
|
||||
"github.com/shufflingpixels/jsontime-go"
|
||||
)
|
||||
|
||||
func createCodec() message.Codec {
|
||||
json_codec := jsontime.ConfigWithCustomTimeFormat
|
||||
json_codec := jsontime.New("2006-01-02T15:04:05.000", time.UTC)
|
||||
|
||||
return message.Codec{
|
||||
Encoder: json_codec.Marshal,
|
||||
|
|
@ -17,9 +17,6 @@ func createCodec() message.Codec {
|
|||
}
|
||||
|
||||
func init() {
|
||||
// Set timeformat used by SHIP api
|
||||
jsontime.SetDefaultTimeFormat("2006-01-02T15:04:05.000", time.UTC)
|
||||
|
||||
// Register the json codec.
|
||||
message.RegisterCodec("json", createCodec())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func createCodec() message.Codec {
|
|||
handle.MapType = reflect.TypeOf(map[string]any(nil))
|
||||
handle.Canonical = true
|
||||
|
||||
// Wierd name but this is needed for the newest version of msgpack
|
||||
// Weird name but this is needed for the newest version of msgpack
|
||||
// that has support for time and string datatypes etc.
|
||||
handle.WriteExt = true
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package api
|
|||
// This is a low-level interface typically implemented by backend drivers
|
||||
type Reader interface {
|
||||
// Read a message from a channel.
|
||||
// Read may block until a message is ready or an error occured.
|
||||
// Read may block until a message is ready or an error occurred.
|
||||
//
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const (
|
|||
//
|
||||
// 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.
|
||||
// chain_id should be ok to not let multiple reader with different chains to write to the same channels.
|
||||
// chain_id should be fine to not let multiple reader with different chains to write to the same channels.
|
||||
|
||||
type Namespace struct {
|
||||
Prefix string
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ func NewSubscriber(ctx context.Context, client *redis.Client, ns Namespace, opti
|
|||
return sub
|
||||
}
|
||||
|
||||
// worker reads messages from redis pubsub and forwards them to
|
||||
// worker reads messages from Redis pubsub and forwards them to
|
||||
// correct channels.
|
||||
func (s *Subscriber) worker() {
|
||||
for msg := range s.sub.Channel() {
|
||||
|
|
|
|||
35
cmd/thalos/antelope_api_helpers.go
Normal file
35
cmd/thalos/antelope_api_helpers.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
eos "github.com/eoscanada/eos-go"
|
||||
shipclient "github.com/eosswedenorg-go/antelope-ship-client"
|
||||
shipws "github.com/eosswedenorg-go/antelope-ship-client/websocket"
|
||||
"github.com/eosswedenorg-go/pid"
|
||||
|
|
@ -27,10 +26,10 @@ import (
|
|||
driver "github.com/eosswedenorg/thalos/internal/driver/redis"
|
||||
. "github.com/eosswedenorg/thalos/internal/log"
|
||||
. "github.com/eosswedenorg/thalos/internal/server"
|
||||
redis_cache "github.com/go-redis/cache/v9"
|
||||
"github.com/nikoksr/notify"
|
||||
"github.com/nikoksr/notify/service/telegram"
|
||||
"github.com/redis/go-redis/v9"
|
||||
antelopeapi "github.com/shufflingpixels/antelope-go/api"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
|
@ -155,12 +154,12 @@ func LogLevels() []string {
|
|||
return list
|
||||
}
|
||||
|
||||
func initAbiManager(api *eos.API, store cache.Store, chain_id string) *abi.AbiManager {
|
||||
func initAbiManager(cfg *config.AbiCache, api *antelopeapi.Client, store cache.Store, chain_id string) *abi.AbiManager {
|
||||
cache := cache.NewCache("thalos::cache::abi::"+chain_id, store)
|
||||
return abi.NewAbiManager(cache, api)
|
||||
return abi.NewAbiManager(cfg, cache, api)
|
||||
}
|
||||
|
||||
func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo func() *eos.InfoResp, cache *cache.Cache, current_block_no_cache bool) StateLoader {
|
||||
func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo func() *antelopeapi.Info, cache *cache.Cache, current_block_no_cache bool) StateLoader {
|
||||
return func(state *State) {
|
||||
var source string
|
||||
|
||||
|
|
@ -186,7 +185,7 @@ func stateLoader(conf *config.Config, start_block_flag *pflag.Flag, chainInfo fu
|
|||
// Otherwise, set from api.
|
||||
if conf.Ship.IrreversibleOnly {
|
||||
source = "api (LIB)"
|
||||
state.CurrentBlock = uint32(chainInfo().LastIrreversibleBlockNum)
|
||||
state.CurrentBlock = uint32(chainInfo().LastIrreversableBlockNum)
|
||||
} else {
|
||||
source = "api (HEAD)"
|
||||
state.CurrentBlock = uint32(chainInfo().HeadBlockNum)
|
||||
|
|
@ -238,36 +237,11 @@ func GetConfig(flags *pflag.FlagSet) (*config.Config, error) {
|
|||
}
|
||||
}
|
||||
|
||||
cfg.Ship.Blacklist.SetWhitelist(cfg.Ship.BlacklistIsWhitelist)
|
||||
|
||||
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) {
|
||||
logEntry := log.WithFields(log.Fields{
|
||||
"addr": conf.Addr,
|
||||
|
|
@ -371,22 +345,25 @@ func serverCmd(cmd *cobra.Command, args []string) {
|
|||
return
|
||||
}
|
||||
|
||||
cache.RegisterFactory("redis", cache.NewRedisFactory(rdb))
|
||||
|
||||
// Setup cache storage
|
||||
cacheStore := cache.NewRedisStore(&redis_cache.Options{
|
||||
Redis: rdb,
|
||||
// Cache 10k keys for 10 minutes.
|
||||
LocalCache: redis_cache.NewTinyLFU(10000, 10*time.Minute),
|
||||
})
|
||||
cacheStore, err := cache.Make(conf.Cache.Storage, conf.Cache.Options)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to setup cache")
|
||||
return
|
||||
}
|
||||
|
||||
// Setup general cache
|
||||
cache := cache.NewCache("thalos::cache::instance::"+conf.Name, cacheStore)
|
||||
|
||||
eosClient := eos.New(conf.Api)
|
||||
antelopeClient := antelopeapi.New(conf.Api)
|
||||
|
||||
shClient := shipclient.NewStream(func(s *shipclient.Stream) {
|
||||
s.StartBlock = conf.Ship.StartBlockNum
|
||||
s.EndBlock = conf.Ship.EndBlockNum
|
||||
s.IrreversibleOnly = conf.Ship.IrreversibleOnly
|
||||
s.MaxMessagesInFlight = 1
|
||||
})
|
||||
|
||||
// Get codec
|
||||
|
|
@ -396,11 +373,11 @@ func serverCmd(cmd *cobra.Command, args []string) {
|
|||
return
|
||||
}
|
||||
|
||||
chainInfo := chainInfoOnce(eosClient)
|
||||
chainInfo := chainInfoOnce(antelopeClient)
|
||||
|
||||
chain_id := conf.Ship.Chain
|
||||
if len(chain_id) < 1 {
|
||||
chain_id = chainInfo().ChainID.String()
|
||||
chain_id = chainInfo().ChainID
|
||||
}
|
||||
|
||||
processor := SpawnProccessor(
|
||||
|
|
@ -411,10 +388,13 @@ func serverCmd(cmd *cobra.Command, args []string) {
|
|||
Prefix: conf.Redis.Prefix,
|
||||
ChainID: chain_id,
|
||||
}),
|
||||
initAbiManager(eosClient, cacheStore, chain_id),
|
||||
initAbiManager(&conf.AbiCache, antelopeClient, cacheStore, chain_id),
|
||||
codec,
|
||||
)
|
||||
|
||||
processor.SetBlacklist(conf.Ship.Blacklist)
|
||||
processor.FetchDeltas(conf.Ship.EnableTableDeltas)
|
||||
|
||||
// Run the application
|
||||
run(conf, shClient, processor)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,101 +18,108 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var benchCmd = &cobra.Command{
|
||||
Use: "bench",
|
||||
Short: "Run a benchmark against a thalos node",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
counter := 0
|
||||
interval, _ := cmd.Flags().GetDuration("interval")
|
||||
func CreateBenchCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "bench",
|
||||
Short: "Run a benchmark against a thalos node",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
counter := 0
|
||||
interval, _ := cmd.Flags().GetDuration("interval")
|
||||
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
user, _ := cmd.Flags().GetString("redis-user")
|
||||
pw, _ := cmd.Flags().GetString("redis-pw")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
user, _ := cmd.Flags().GetString("redis-user")
|
||||
pw, _ := cmd.Flags().GetString("redis-pw")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Connecting to redis")
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Connecting to redis")
|
||||
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
Username: user,
|
||||
Password: pw,
|
||||
DB: db,
|
||||
})
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
Username: user,
|
||||
Password: pw,
|
||||
DB: db,
|
||||
})
|
||||
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
log.WithError(err).Fatal("Failed to connect to redis")
|
||||
return
|
||||
}
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
log.WithError(err).Fatal("Failed to connect to redis")
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Connected to redis")
|
||||
log.Println("Connected to redis")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"interval": interval,
|
||||
}).Info("Starting benchmark")
|
||||
log.WithFields(log.Fields{
|
||||
"interval": interval,
|
||||
}).Info("Starting benchmark")
|
||||
|
||||
sub := api_redis.NewSubscriber(context.Background(), rdb, api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
})
|
||||
sub := api_redis.NewSubscriber(context.Background(), rdb, api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
})
|
||||
|
||||
codec, err := message.GetCodec("json")
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
codec, err := message.GetCodec("json")
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
|
||||
client := api.NewClient(sub, codec.Decoder)
|
||||
client := api.NewClient(sub, codec.Decoder)
|
||||
|
||||
// Subscribe to all actions
|
||||
if err = client.Subscribe(api.ActionChannel{}.Channel()); err != nil {
|
||||
log.WithError(err).Fatal("Failed to subscribe to channels")
|
||||
return
|
||||
}
|
||||
// Subscribe to all actions
|
||||
if err = client.Subscribe(api.ActionChannel{}.Channel()); err != nil {
|
||||
log.WithError(err).Fatal("Failed to subscribe to channels")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for t := range client.Channel() {
|
||||
switch err := t.(type) {
|
||||
case message.ActionTrace:
|
||||
counter++
|
||||
case error:
|
||||
log.WithError(err).Error("Error when reading stream")
|
||||
go func() {
|
||||
for t := range client.Channel() {
|
||||
switch err := t.(type) {
|
||||
case message.ActionTrace:
|
||||
counter++
|
||||
case error:
|
||||
log.WithError(err).Error("Error when reading stream")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
t := time.Now()
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
|
||||
// Read stuff.
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Println("Got interrupt")
|
||||
client.Close()
|
||||
return
|
||||
case now := <-time.After(interval):
|
||||
elapsed := now.Sub(t)
|
||||
t = now
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"num_messages": counter,
|
||||
"elapsed": elapsed,
|
||||
"msg_per_sec": float64(counter) / elapsed.Seconds(),
|
||||
"msg_per_ms": float64(counter) / float64(elapsed.Milliseconds()),
|
||||
"msg_per_min": float64(counter) / elapsed.Minutes(),
|
||||
}).Info("Benchmark results")
|
||||
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
}()
|
||||
},
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
cmd.Flags().AddFlagSet(RedisFlags())
|
||||
cmd.Flags().DurationP("interval", "i", time.Minute, "How often the benchmark results should be displayed.")
|
||||
|
||||
// Read stuff.
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Println("Got interrupt")
|
||||
client.Close()
|
||||
return
|
||||
case now := <-time.After(interval):
|
||||
elapsed := now.Sub(t)
|
||||
t = now
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"num_messages": counter,
|
||||
"elapsed": elapsed,
|
||||
"msg_per_sec": float64(counter) / elapsed.Seconds(),
|
||||
"msg_per_ms": float64(counter) / float64(elapsed.Milliseconds()),
|
||||
"msg_per_min": float64(counter) / elapsed.Minutes(),
|
||||
}).Info("Benchmark results")
|
||||
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
14
cmd/tools/flags.go
Normal file
14
cmd/tools/flags.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,63 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
_ "github.com/eosswedenorg/thalos/internal/log"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var VersionString string = "dev"
|
||||
|
||||
var 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{
|
||||
func main() {
|
||||
rootCmd := &cobra.Command{
|
||||
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{
|
||||
UnknownFlags: true,
|
||||
},
|
||||
Version: VersionString,
|
||||
}
|
||||
|
||||
benchCmd.Flags().AddFlagSet(&redisFlags)
|
||||
benchCmd.Flags().DurationP("interval", "i", time.Minute, "How often the benchmark results should be displayed.")
|
||||
|
||||
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)")
|
||||
RedisACLCmd.Flags().Uint("pass-len", 32, "The length of generated passwords")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
validateCmd,
|
||||
benchCmd,
|
||||
RedisACLCmd,
|
||||
MockPublisherCmd,
|
||||
CreateValidateCmd(),
|
||||
CreateBenchCmd(),
|
||||
CreateRedisACLCmd(),
|
||||
CreateMockPublisherCmd(),
|
||||
)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,100 +16,106 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var MockPublisherCmd = &cobra.Command{
|
||||
Use: "mock_publisher",
|
||||
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) {
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
user, _ := cmd.Flags().GetString("redis-user")
|
||||
pw, _ := cmd.Flags().GetString("redis-pw")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
func CreateMockPublisherCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "mock_publisher",
|
||||
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) {
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
user, _ := cmd.Flags().GetString("redis-user")
|
||||
pw, _ := cmd.Flags().GetString("redis-pw")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
Username: user,
|
||||
Password: pw,
|
||||
DB: db,
|
||||
})
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
Username: user,
|
||||
Password: pw,
|
||||
DB: db,
|
||||
})
|
||||
|
||||
codecArg, _ := cmd.Flags().GetString("codec")
|
||||
codecArg, _ := cmd.Flags().GetString("codec")
|
||||
|
||||
codec, err := message.GetCodec(codecArg)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
codec, err := message.GetCodec(codecArg)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Starting mock publisher")
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Starting mock publisher")
|
||||
|
||||
ns := api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
}
|
||||
publisher := redis_driver.NewPublisher(context.Background(), rdb, ns)
|
||||
ns := api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
}
|
||||
publisher := redis_driver.NewPublisher(context.Background(), rdb, ns)
|
||||
|
||||
msg := message.ActionTrace{
|
||||
TxID: "401e8a7e5deb18a2a69fc6559f49509a155f4355c85efb69c1c1fab5b60ee532",
|
||||
BlockNum: 18237917,
|
||||
Timestamp: time.Date(2014, 3, 22, 11, 36, 43, 0, time.UTC),
|
||||
Receipt: &message.ActionReceipt{
|
||||
Receiver: "acc1",
|
||||
ActDigest: "4c5c08be612e937564fc526ebb5fadf34ae8c2a571fe9d7cdb3ffcdfc53b0e8d",
|
||||
GlobalSequence: 12314,
|
||||
RecvSequence: 237187239,
|
||||
AuthSequence: []message.AccountAuthSequence{
|
||||
msg := message.ActionTrace{
|
||||
TxID: "401e8a7e5deb18a2a69fc6559f49509a155f4355c85efb69c1c1fab5b60ee532",
|
||||
BlockNum: 18237917,
|
||||
Timestamp: time.Date(2014, 3, 22, 11, 36, 43, 0, time.UTC),
|
||||
Receipt: &message.ActionReceipt{
|
||||
Receiver: "acc1",
|
||||
ActDigest: "4c5c08be612e937564fc526ebb5fadf34ae8c2a571fe9d7cdb3ffcdfc53b0e8d",
|
||||
GlobalSequence: 12314,
|
||||
RecvSequence: 237187239,
|
||||
AuthSequence: []message.AccountAuthSequence{
|
||||
{
|
||||
Account: "acc1",
|
||||
Sequence: 2732863,
|
||||
},
|
||||
{
|
||||
Account: "acc2",
|
||||
Sequence: 263762,
|
||||
},
|
||||
},
|
||||
CodeSequence: 2327832,
|
||||
ABISequence: 12376189,
|
||||
},
|
||||
Name: "fake",
|
||||
Contract: "fake",
|
||||
Receiver: "acc1",
|
||||
Data: map[string]interface{}{
|
||||
"one": 238771832,
|
||||
"two": "str",
|
||||
},
|
||||
Authorization: []message.PermissionLevel{
|
||||
{
|
||||
Account: "acc1",
|
||||
Sequence: 2732863,
|
||||
Actor: "acc1",
|
||||
Permission: "active",
|
||||
},
|
||||
{
|
||||
Account: "acc2",
|
||||
Sequence: 263762,
|
||||
Actor: "acc2",
|
||||
Permission: "owner",
|
||||
},
|
||||
},
|
||||
CodeSequence: 2327832,
|
||||
ABISequence: 12376189,
|
||||
},
|
||||
Name: "fake",
|
||||
Contract: "fake",
|
||||
Receiver: "acc1",
|
||||
Data: map[string]interface{}{
|
||||
"one": 238771832,
|
||||
"two": "str",
|
||||
},
|
||||
Authorization: []message.PermissionLevel{
|
||||
{
|
||||
Actor: "acc1",
|
||||
Permission: "active",
|
||||
},
|
||||
{
|
||||
Actor: "acc2",
|
||||
Permission: "owner",
|
||||
},
|
||||
},
|
||||
Except: "err",
|
||||
Error: 2,
|
||||
Return: []byte{0xbe, 0xef},
|
||||
}
|
||||
Except: "err",
|
||||
Error: 2,
|
||||
Return: []byte{0xbe, 0xef},
|
||||
}
|
||||
|
||||
payload, err := codec.Encoder(msg)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to encode message")
|
||||
return
|
||||
}
|
||||
channel := api.ActionChannel{}.Channel()
|
||||
payload, err := codec.Encoder(msg)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to encode message")
|
||||
return
|
||||
}
|
||||
channel := api.ActionChannel{}.Channel()
|
||||
|
||||
for {
|
||||
_ = publisher.Write(channel, payload)
|
||||
publisher.Flush()
|
||||
}
|
||||
},
|
||||
for {
|
||||
_ = publisher.Write(channel, payload)
|
||||
publisher.Flush()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(RedisFlags())
|
||||
cmd.Flags().String("codec", "json", "codec to use")
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ func NewUser(name, password string, pass_len uint) User {
|
|||
}
|
||||
|
||||
func (u *User) GetPassword() string {
|
||||
|
||||
if u.Hash {
|
||||
return "#" + hash(u.Password)
|
||||
}
|
||||
|
|
@ -99,60 +98,74 @@ user {{.client}} on {{.clientpw}} resetchannels &{{.prefix}}::* -@all +subscribe
|
|||
})
|
||||
}
|
||||
|
||||
var RedisACLCmd = &cobra.Command{
|
||||
Use: "redis-acl",
|
||||
Short: "create a users.acl file",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var err error
|
||||
out := os.Stdout
|
||||
func CreateRedisACLCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "redis-acl",
|
||||
Short: "create a users.acl file",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var err error
|
||||
out := os.Stdout
|
||||
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
flagDefUserPw, _ := cmd.Flags().GetString("default-pw")
|
||||
flagServer, _ := cmd.Flags().GetString("server")
|
||||
flagServerPw, _ := cmd.Flags().GetString("server-pw")
|
||||
flagClient, _ := cmd.Flags().GetString("client")
|
||||
flagClientPw, _ := cmd.Flags().GetString("client-pw")
|
||||
flagPrefix, _ := cmd.Flags().GetString("prefix")
|
||||
flagPassLen, _ := cmd.Flags().GetUint("pass-len")
|
||||
flagDefUserPw, _ := cmd.Flags().GetString("default-pw")
|
||||
flagServer, _ := cmd.Flags().GetString("server")
|
||||
flagServerPw, _ := cmd.Flags().GetString("server-pw")
|
||||
flagClient, _ := cmd.Flags().GetString("client")
|
||||
flagClientPw, _ := cmd.Flags().GetString("client-pw")
|
||||
flagPrefix, _ := cmd.Flags().GetString("prefix")
|
||||
flagPassLen, _ := cmd.Flags().GetUint("pass-len")
|
||||
|
||||
defaultUser := NewUser("default", flagDefUserPw, flagPassLen)
|
||||
serverUser := NewUser(flagServer, flagServerPw, flagPassLen)
|
||||
clientUser := NewUser(flagClient, flagClientPw, flagPassLen)
|
||||
defaultUser := NewUser("default", flagDefUserPw, flagPassLen)
|
||||
serverUser := NewUser(flagServer, flagServerPw, flagPassLen)
|
||||
clientUser := NewUser(flagClient, flagClientPw, flagPassLen)
|
||||
|
||||
atleastOneGeneratedPw := defaultUser.Generated || serverUser.Generated || clientUser.Generated
|
||||
atleastOneGeneratedPw := defaultUser.Generated || serverUser.Generated || clientUser.Generated
|
||||
|
||||
cleartext, _ := cmd.Flags().GetBool("cleartext")
|
||||
if !cleartext {
|
||||
if atleastOneGeneratedPw {
|
||||
println("Passwords")
|
||||
cleartext, _ := cmd.Flags().GetBool("cleartext")
|
||||
if !cleartext {
|
||||
if atleastOneGeneratedPw {
|
||||
println("Passwords")
|
||||
}
|
||||
|
||||
defaultUser.PrintIfGeneratedPW()
|
||||
serverUser.PrintIfGeneratedPW()
|
||||
clientUser.PrintIfGeneratedPW()
|
||||
|
||||
defaultUser.Hash = true
|
||||
serverUser.Hash = true
|
||||
clientUser.Hash = true
|
||||
}
|
||||
|
||||
defaultUser.PrintIfGeneratedPW()
|
||||
serverUser.PrintIfGeneratedPW()
|
||||
clientUser.PrintIfGeneratedPW()
|
||||
filename, _ := cmd.Flags().GetString("file")
|
||||
if len(filename) > 0 {
|
||||
out, err = os.Create(filename)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to create output file")
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
} else if !cleartext && atleastOneGeneratedPw {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
defaultUser.Hash = true
|
||||
serverUser.Hash = true
|
||||
clientUser.Hash = true
|
||||
}
|
||||
|
||||
filename, _ := cmd.Flags().GetString("file")
|
||||
if len(filename) > 0 {
|
||||
out, err = os.Create(filename)
|
||||
err = writeTemplate(out, defaultUser, serverUser, clientUser, flagPrefix)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to create output file")
|
||||
log.WithError(err).Fatal("Failed to writte config")
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
} else if !cleartext && atleastOneGeneratedPw {
|
||||
fmt.Println()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
err = writeTemplate(out, defaultUser, serverUser, clientUser, flagPrefix)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to writte config")
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,101 +18,107 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var validateCmd = &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validate a thalos server by following action traces and makes sure that blocks arrive in order.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
status_duration := time.Second * 10
|
||||
func CreateValidateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validate a thalos server by following action traces and makes sure that blocks arrive in order.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
status_duration := time.Second * 10
|
||||
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
url, _ := cmd.Flags().GetString("redis-url")
|
||||
prefix, _ := cmd.Flags().GetString("prefix")
|
||||
chain_id, _ := cmd.Flags().GetString("chain_id")
|
||||
db, _ := cmd.Flags().GetInt("redis-db")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Connecting to redis")
|
||||
log.WithFields(log.Fields{
|
||||
"url": url,
|
||||
"prefix": prefix,
|
||||
"chain_id": chain_id,
|
||||
"database": db,
|
||||
}).Info("Connecting to redis")
|
||||
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
DB: db,
|
||||
})
|
||||
// Create redis client
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
DB: db,
|
||||
})
|
||||
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
log.WithError(err).Fatal("Failed to connect to redis")
|
||||
return
|
||||
}
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
log.WithError(err).Fatal("Failed to connect to redis")
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Connected to redis")
|
||||
log.Println("Connected to redis")
|
||||
|
||||
log.Info("Starting validation, following the stream")
|
||||
log.Info("Starting validation, following the stream")
|
||||
|
||||
sub := api_redis.NewSubscriber(context.Background(), rdb, api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
})
|
||||
sub := api_redis.NewSubscriber(context.Background(), rdb, api_redis.Namespace{
|
||||
Prefix: prefix,
|
||||
ChainID: chain_id,
|
||||
})
|
||||
|
||||
codec, err := message.GetCodec("json")
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
codec, err := message.GetCodec("json")
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to get codec")
|
||||
return
|
||||
}
|
||||
|
||||
client := api.NewClient(sub, codec.Decoder)
|
||||
client := api.NewClient(sub, codec.Decoder)
|
||||
|
||||
// Subscribe to all actions
|
||||
if err = client.Subscribe(api.ActionChannel{}.Channel()); err != nil {
|
||||
log.WithError(err).Fatal("Failed to subscribe to channels")
|
||||
return
|
||||
}
|
||||
// Subscribe to all actions
|
||||
if err = client.Subscribe(api.ActionChannel{}.Channel()); err != nil {
|
||||
log.WithError(err).Fatal("Failed to subscribe to channels")
|
||||
return
|
||||
}
|
||||
|
||||
block_num := uint32(0)
|
||||
timeout := time.Second * 5
|
||||
timer := time.NewTicker(timeout)
|
||||
block_num := uint32(0)
|
||||
timeout := time.Second * 5
|
||||
timer := time.NewTicker(timeout)
|
||||
|
||||
go func() {
|
||||
for t := range client.Channel() {
|
||||
switch msg := t.(type) {
|
||||
case error:
|
||||
log.WithError(msg).Error("Error when reading stream")
|
||||
case message.ActionTrace:
|
||||
if block_num > 0 {
|
||||
diff := int32(msg.BlockNum - block_num)
|
||||
if diff < 0 || diff > 1 {
|
||||
log.WithFields(log.Fields{
|
||||
"current_block": block_num,
|
||||
"block": msg.BlockNum,
|
||||
"diff": diff,
|
||||
}).Warn("Invalid")
|
||||
go func() {
|
||||
for t := range client.Channel() {
|
||||
switch msg := t.(type) {
|
||||
case error:
|
||||
log.WithError(msg).Error("Error when reading stream")
|
||||
case message.ActionTrace:
|
||||
if block_num > 0 {
|
||||
diff := int32(msg.BlockNum - block_num)
|
||||
if diff < 0 || diff > 1 {
|
||||
log.WithFields(log.Fields{
|
||||
"current_block": block_num,
|
||||
"block": msg.BlockNum,
|
||||
"diff": diff,
|
||||
}).Warn("Invalid")
|
||||
}
|
||||
}
|
||||
block_num = msg.BlockNum
|
||||
timer.Reset(timeout)
|
||||
}
|
||||
block_num = msg.BlockNum
|
||||
timer.Reset(timeout)
|
||||
}
|
||||
}()
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Println("Got interrupt")
|
||||
client.Close()
|
||||
return
|
||||
case <-timer.C:
|
||||
log.WithField("duration", timeout).
|
||||
Warn("Did not get any messages during the defined duration")
|
||||
case <-time.After(status_duration):
|
||||
log.WithFields(log.Fields{
|
||||
"current_block": block_num,
|
||||
}).Info("Status")
|
||||
}
|
||||
}
|
||||
}()
|
||||
},
|
||||
}
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
cmd.Flags().AddFlagSet(RedisFlags())
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Println("Got interrupt")
|
||||
client.Close()
|
||||
return
|
||||
case <-timer.C:
|
||||
log.WithField("duration", timeout).
|
||||
Warn("Did not get any messages during the defined duration")
|
||||
case <-time.After(status_duration):
|
||||
log.WithFields(log.Fields{
|
||||
"current_block": block_num,
|
||||
}).Info("Status")
|
||||
}
|
||||
}
|
||||
},
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,15 +38,41 @@ ship:
|
|||
|
||||
# Request ship to start sending blocks from this block.
|
||||
# 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.
|
||||
#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:
|
||||
# id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP"
|
||||
# channel: -123456789
|
||||
# telegram:
|
||||
# id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP"
|
||||
# channel: -123456789
|
||||
|
||||
# Redis settings
|
||||
redis:
|
||||
|
|
@ -57,7 +83,7 @@ redis:
|
|||
user: ""
|
||||
|
||||
# Password to use when authenticating
|
||||
pasword: ""
|
||||
password: ""
|
||||
|
||||
# database index
|
||||
db: 0
|
||||
|
|
|
|||
148
debian/changelog
vendored
148
debian/changelog
vendored
|
|
@ -1,3 +1,150 @@
|
|||
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.
|
||||
|
|
@ -133,3 +280,4 @@ thalos-server (0.1.0) bionic focal jammy; urgency=medium
|
|||
Initial release.
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Sun, 14 May 2023 18:17:35 +0200
|
||||
|
||||
|
|
|
|||
3
debian/patches/0001-fix-config-logpath.patch
vendored
3
debian/patches/0001-fix-config-logpath.patch
vendored
|
|
@ -1,10 +1,9 @@
|
|||
diff --git a/config.example.yml b/config.example.yml
|
||||
index 9f4272a..8daac0a 100644
|
||||
--- a/config.example.yml
|
||||
+++ b/config.example.yml
|
||||
@@ -11,7 +11,7 @@ log:
|
||||
# Filename to use.
|
||||
filename: thalos.log
|
||||
filename: thalos
|
||||
# Directory to store the logfiles in.
|
||||
- directory: logs
|
||||
+ directory: /var/log/thalos
|
||||
|
|
|
|||
6
debian/sysconfig/thalos-server
vendored
Normal file
6
debian/sysconfig/thalos-server
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# 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"
|
||||
3
debian/thalos-server.service
vendored
3
debian/thalos-server.service
vendored
|
|
@ -6,7 +6,8 @@ After=network.target
|
|||
User=thalos
|
||||
Group=thalos
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/thalos-server -c /etc/thalos/config.yml
|
||||
EnvironmentFile=-/etc/sysconfig/thalos-server
|
||||
ExecStart=/usr/bin/thalos-server $THALOS_SERVER_ARGS
|
||||
ExecReload=kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
|
|
|
|||
1
debian/thalos.install
vendored
1
debian/thalos.install
vendored
|
|
@ -1 +1,2 @@
|
|||
debian/thalos-server.service /lib/systemd/system
|
||||
debian/sysconfig/thalos-server /etc/sysconfig
|
||||
|
|
|
|||
8
debian/thalos.postinst
vendored
8
debian/thalos.postinst
vendored
|
|
@ -2,12 +2,12 @@
|
|||
set -e
|
||||
|
||||
if [ "$1" = 'configure' ]; then
|
||||
adduser --force-badname --system --home /nonexistent \
|
||||
adduser --force-badname --system --home /nonexistent \
|
||||
--group --no-create-home --quiet thalos || true
|
||||
|
||||
# Create log directory
|
||||
mkdir -p /var/log/thalos
|
||||
chown thalos:adm /var/log/thalos
|
||||
# Create log directory
|
||||
mkdir -p /var/log/thalos
|
||||
chown thalos:adm /var/log/thalos
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
||||
|
|
|
|||
8
debian/thalos.postrm
vendored
8
debian/thalos.postrm
vendored
|
|
@ -3,11 +3,9 @@ set -e
|
|||
|
||||
#DEBHELPER#
|
||||
|
||||
|
||||
if [ "${1}" = "purge" ]
|
||||
then
|
||||
deluser --quiet thalos > /dev/null || true
|
||||
rm -rf /var/log/thalos
|
||||
if [ "${1}" = "purge" ]; then
|
||||
deluser --quiet thalos >/dev/null || true
|
||||
rm -rf /var/log/thalos
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
FROM alpine:latest
|
||||
LABEL maintainer="Henrik Hautakoski <henrik.Hautakoski@gmail.com>"
|
||||
ARG VERSION=1.1.1
|
||||
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" ]
|
||||
|
|
|
|||
53
go.mod
53
go.mod
|
|
@ -1,19 +1,20 @@
|
|||
module github.com/eosswedenorg/thalos
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/docker/go-units v0.5.0
|
||||
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/antelope-ship-client v0.3.2
|
||||
github.com/eosswedenorg-go/pid v1.0.1
|
||||
github.com/eosswedenorg/thalos/api v1.0.0
|
||||
github.com/go-redis/cache/v9 v9.0.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/nikoksr/notify v0.41.0
|
||||
github.com/redis/go-redis/v9 v9.5.1
|
||||
github.com/shufflingpixels/antelope-go v0.1.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
|
|
@ -23,51 +24,57 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/blendle/zapdriver v1.3.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // 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/eosswedenorg-go/jsontime v0.0.0-20230509125027-08422d6236c7 // 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/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/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/imroc/req/v3 v3.49.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/liamylian/jsontime/v2 v2.0.0 // 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/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/gomega v1.32.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // 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/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/spf13/afero v1.11.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/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // 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/vmihailenco/go-tinylfu v0.2.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
|
|
|||
153
go.sum
153
go.sum
|
|
@ -1,12 +1,15 @@
|
|||
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/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
|
||||
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
|
||||
github.com/alicebob/miniredis/v2 v2.30.2/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
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/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.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
|
|
@ -14,6 +17,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||
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/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/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=
|
||||
|
|
@ -24,25 +29,26 @@ 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/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/eoscanada/eos-go v0.10.3-0.20231109144819-59afdfa3a37d h1:vK5PijzcJaUPOhgWvY9lL99H9t3lrRNHx2IDHqS0ILc=
|
||||
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/antelope-ship-client v0.3.2 h1:mDXZkjQ0bTPJClkhoPEP5ltucxql6bR+QixhnQI/Og4=
|
||||
github.com/eosswedenorg-go/antelope-ship-client v0.3.2/go.mod h1:DnUmaRGxz/V73CtVEJx/fReqhgGzhVyWpOrEKVYQSgE=
|
||||
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/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.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
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/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/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/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/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/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -61,42 +67,56 @@ 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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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-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/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/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
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/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/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/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/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.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
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.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
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/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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0=
|
||||
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
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/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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
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/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ=
|
||||
|
|
@ -116,6 +136,8 @@ 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.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.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.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
|
|
@ -126,26 +148,36 @@ 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.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
|
||||
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
|
||||
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
|
||||
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
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.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
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.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/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.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/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
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/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/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
|
|
@ -160,12 +192,9 @@ 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/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
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.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.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
|
@ -180,15 +209,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
|||
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/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.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/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/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
|
||||
|
|
@ -198,51 +218,41 @@ 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/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/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.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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
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/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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
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-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-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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
|
||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
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.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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.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-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-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-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-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-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-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
|
|
@ -250,16 +260,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.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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
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-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-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -271,9 +281,7 @@ 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-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-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-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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -286,16 +294,14 @@ 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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.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-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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
@ -303,17 +309,19 @@ 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.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
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-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-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.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.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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -327,6 +335,8 @@ 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -340,11 +350,10 @@ 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/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.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.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-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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
|
|
|
|||
|
|
@ -5,45 +5,49 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
eos "github.com/eoscanada/eos-go"
|
||||
"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.
|
||||
type AbiManager struct {
|
||||
cfg *config.AbiCache
|
||||
cache *cache.Cache
|
||||
api *eos.API
|
||||
api *api.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Create a new ABI Manager
|
||||
func NewAbiManager(cache *cache.Cache, api *eos.API) *AbiManager {
|
||||
func NewAbiManager(cfg *config.AbiCache, cache *cache.Cache, api *api.Client) *AbiManager {
|
||||
return &AbiManager{
|
||||
cache: cache,
|
||||
api: api,
|
||||
cfg: cfg,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
// Set or update an ABI in the cache.
|
||||
func (mgr *AbiManager) SetAbi(account eos.AccountName, abi *eos.ABI) error {
|
||||
func (mgr *AbiManager) SetAbi(account chain.Name, abi *chain.Abi) error {
|
||||
ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500)
|
||||
defer cancel()
|
||||
return mgr.cache.Set(ctx, string(account), *abi, time.Hour)
|
||||
return mgr.cache.Set(ctx, account.String(), *abi, time.Hour)
|
||||
}
|
||||
|
||||
// Get an ABI from the cache, on cache miss it is fetched from the
|
||||
// API, gets cached and then returned to the user
|
||||
func (mgr *AbiManager) GetAbi(account eos.AccountName) (*eos.ABI, error) {
|
||||
var abi eos.ABI
|
||||
func (mgr *AbiManager) GetAbi(account chain.Name) (*chain.Abi, error) {
|
||||
var abi chain.Abi
|
||||
if err := mgr.cacheGet(account, &abi); err != nil {
|
||||
ctx, cancel := context.WithTimeout(mgr.ctx, time.Second)
|
||||
ctx, cancel := context.WithTimeout(mgr.ctx, mgr.cfg.ApiTimeout)
|
||||
defer cancel()
|
||||
resp, err := mgr.api.GetABI(ctx, account)
|
||||
resp, err := mgr.api.GetAbi(ctx, account.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("api: %s", err)
|
||||
}
|
||||
abi = resp.ABI
|
||||
abi = resp.Abi
|
||||
|
||||
err = mgr.SetAbi(account, &abi)
|
||||
if err != nil {
|
||||
|
|
@ -53,8 +57,8 @@ func (mgr *AbiManager) GetAbi(account eos.AccountName) (*eos.ABI, error) {
|
|||
return &abi, nil
|
||||
}
|
||||
|
||||
func (mgr *AbiManager) cacheGet(account eos.AccountName, value any) error {
|
||||
func (mgr *AbiManager) cacheGet(account chain.Name, value any) error {
|
||||
ctx, cancel := context.WithTimeout(mgr.ctx, time.Millisecond*500)
|
||||
defer cancel()
|
||||
return mgr.cache.Get(ctx, string(account), value)
|
||||
return mgr.cache.Get(ctx, account.String(), value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
package abi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eos "github.com/eoscanada/eos-go"
|
||||
"github.com/shufflingpixels/antelope-go/api"
|
||||
"github.com/shufflingpixels/antelope-go/chain"
|
||||
|
||||
"github.com/eosswedenorg/thalos/internal/cache"
|
||||
"github.com/eosswedenorg/thalos/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
@ -73,7 +76,7 @@ var abiString = `
|
|||
}
|
||||
`
|
||||
|
||||
func assert_abi(t *testing.T, abi *eos.ABI) {
|
||||
func assert_abi(t *testing.T, abi *chain.Abi) {
|
||||
assert.Equal(t, abi.Version, "eosio::abi/1.0")
|
||||
|
||||
// Types
|
||||
|
|
@ -110,12 +113,12 @@ func assert_abi(t *testing.T, abi *eos.ABI) {
|
|||
assert.Equal(t, abi.Structs[3].Fields[0].Type, "string")
|
||||
|
||||
// Actions
|
||||
assert.Equal(t, abi.Actions[0].Name, eos.ActN("action_name_1"))
|
||||
assert.Equal(t, abi.Actions[0].Name, chain.N("action_name_1"))
|
||||
assert.Equal(t, abi.Actions[0].Type, "struct_name_1")
|
||||
assert.Equal(t, abi.Actions[0].RicardianContract, "")
|
||||
|
||||
// Tables
|
||||
assert.Equal(t, abi.Tables[0].Name, eos.TableName("table_name_1"))
|
||||
assert.Equal(t, abi.Tables[0].Name, chain.N("table_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].KeyNames[0], "key_name_1")
|
||||
|
|
@ -124,37 +127,41 @@ func assert_abi(t *testing.T, abi *eos.ABI) {
|
|||
assert.Equal(t, abi.Tables[0].KeyTypes[1], "int")
|
||||
}
|
||||
|
||||
func mockAPI(handler http.HandlerFunc) (*eos.API, *httptest.Server) {
|
||||
func mockAPI(handler http.HandlerFunc) (*api.Client, *httptest.Server) {
|
||||
server := httptest.NewServer(handler)
|
||||
|
||||
return &eos.API{
|
||||
HttpClient: server.Client(),
|
||||
BaseURL: strings.TrimRight(server.URL, "/"),
|
||||
Compress: eos.CompressionZlib,
|
||||
Header: make(http.Header),
|
||||
}, server
|
||||
return api.New(server.URL), server
|
||||
}
|
||||
|
||||
func TestManager_GetAbiFromCache(t *testing.T) {
|
||||
cfg := &config.AbiCache{
|
||||
ApiTimeout: time.Second,
|
||||
}
|
||||
|
||||
cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore())
|
||||
|
||||
api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}))
|
||||
|
||||
mgr := NewAbiManager(cache, api)
|
||||
mgr := NewAbiManager(cfg, cache, api)
|
||||
|
||||
abi, err := eos.NewABI(strings.NewReader(abiString))
|
||||
abi := chain.Abi{}
|
||||
err := json.Unmarshal([]byte(abiString), &abi)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = mgr.SetAbi("testaccount", abi)
|
||||
err = mgr.SetAbi(chain.N("testaccount"), &abi)
|
||||
assert.NoError(t, err)
|
||||
|
||||
c_abi, err := mgr.GetAbi("testaccount")
|
||||
c_abi, err := mgr.GetAbi(chain.N("testaccount"))
|
||||
assert.NoError(t, err)
|
||||
assert_abi(t, c_abi)
|
||||
}
|
||||
|
||||
func TestManager_GetAbiFromAPI(t *testing.T) {
|
||||
cfg := &config.AbiCache{
|
||||
ApiTimeout: time.Second,
|
||||
}
|
||||
|
||||
cache := cache.NewCache("thalos::cache::abi::test", cache.NewMemoryStore())
|
||||
|
||||
api, _ := mockAPI(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -164,9 +171,9 @@ func TestManager_GetAbiFromAPI(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
mgr := NewAbiManager(cache, api)
|
||||
mgr := NewAbiManager(cfg, cache, api)
|
||||
|
||||
c_abi, err := mgr.GetAbi("testaccount")
|
||||
c_abi, err := mgr.GetAbi(chain.N("testaccount"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert_abi(t, c_abi)
|
||||
|
|
|
|||
26
internal/cache/factory.go
vendored
Normal file
26
internal/cache/factory.go
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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)
|
||||
}
|
||||
20
internal/cache/factory_test.go
vendored
Normal file
20
internal/cache/factory_test.go
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
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)
|
||||
}
|
||||
28
internal/cache/redis_store.go
vendored
28
internal/cache/redis_store.go
vendored
|
|
@ -5,18 +5,46 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-redis/cache/v9"
|
||||
"github.com/karlseguin/typed"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RedisStore struct {
|
||||
c *cache.Cache
|
||||
}
|
||||
|
||||
type options struct {
|
||||
Stats bool
|
||||
Size int
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
func NewRedisStore(options *cache.Options) *RedisStore {
|
||||
return &RedisStore{
|
||||
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 {
|
||||
return s.c.Get(ctx, key, value)
|
||||
}
|
||||
|
|
|
|||
33
internal/cache/redis_store_test.go
vendored
33
internal/cache/redis_store_test.go
vendored
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-redis/redismock/v9"
|
||||
"github.com/karlseguin/typed"
|
||||
|
||||
redis_cache "github.com/go-redis/cache/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -16,6 +17,38 @@ type testItem struct {
|
|||
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) {
|
||||
client, mock := redismock.NewClientMock()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@ package config
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/eosswedenorg/thalos/internal/types"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
|
|
@ -36,6 +40,11 @@ func NewBuilder() *Builder {
|
|||
"telegram.id": "telegram-id",
|
||||
"telegram.channel": "telegram-channel",
|
||||
|
||||
"cache.storage": "cache",
|
||||
|
||||
// AbiCache
|
||||
"abi_cache.api_timeout": "abi-cache-api-timeout",
|
||||
|
||||
// Log
|
||||
"log.maxfilesize": "log-max-filesize",
|
||||
"log.maxtime": "log-max-time",
|
||||
|
|
@ -48,6 +57,9 @@ func NewBuilder() *Builder {
|
|||
"ship.irreversible_only": "irreversible-only",
|
||||
"ship.max_messages_in_flight": "max-msg-in-flight",
|
||||
"ship.chain": "chain",
|
||||
"ship.blacklist": "blacklist",
|
||||
"ship.blacklist_is_whitelist": "blacklist-is-whitelist",
|
||||
"ship.table_deltas": "table-deltas",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -110,6 +122,12 @@ func (b *Builder) Build() (*Config, error) {
|
|||
mapstructure.TextUnmarshallerHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
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))
|
||||
|
|
@ -119,3 +137,61 @@ func (b *Builder) Build() (*Config, error) {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
shipclient "github.com/eosswedenorg-go/antelope-ship-client"
|
||||
"github.com/eosswedenorg/thalos/internal/log"
|
||||
"github.com/eosswedenorg/thalos/internal/types"
|
||||
"github.com/karlseguin/typed"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -21,12 +24,28 @@ func TestBuilder(t *testing.T) {
|
|||
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{
|
||||
Url: "127.0.0.1:8089",
|
||||
StartBlockNum: 23671836,
|
||||
EndBlockNum: 23872222,
|
||||
IrreversibleOnly: true,
|
||||
MaxMessagesInFlight: 1337,
|
||||
Blacklist: *types.NewBlacklist(map[string][]string{
|
||||
"eosio": {"noop"},
|
||||
"contract": {"skip1", "skip2"},
|
||||
}),
|
||||
BlacklistIsWhitelist: true,
|
||||
},
|
||||
Telegram: TelegramConfig{
|
||||
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
|
||||
|
|
@ -46,6 +65,14 @@ func TestBuilder(t *testing.T) {
|
|||
name: "ship-reader-1"
|
||||
api: "http://127.0.0.1:8080"
|
||||
message_codec: "mojibake"
|
||||
cache:
|
||||
storage: memcached
|
||||
options:
|
||||
ttl: 300m
|
||||
size: 400
|
||||
super_fast_mode: true
|
||||
abi_cache:
|
||||
api_timeout: 300m
|
||||
log:
|
||||
filename: some_file.log
|
||||
directory: /path/to/whatever
|
||||
|
|
@ -58,6 +85,12 @@ ship:
|
|||
max_messages_in_flight: 1337
|
||||
start_block_num: 23671836
|
||||
end_block_num: 23872222
|
||||
blacklist:
|
||||
eosio: noop
|
||||
contract:
|
||||
- skip1
|
||||
- skip2
|
||||
blacklist_is_whitelist: true
|
||||
telegram:
|
||||
id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
|
||||
channel: -123456789
|
||||
|
|
@ -75,74 +108,37 @@ redis:
|
|||
require.Equal(t, &expected, cfg)
|
||||
}
|
||||
|
||||
func TestBuilder_ConfigWithFlags(t *testing.T) {
|
||||
func TestBuilder_WithDefaultConfig(t *testing.T) {
|
||||
expected := Config{
|
||||
Name: "ship-reader-1",
|
||||
Api: "https://api.example.com",
|
||||
MessageCodec: "msgpack",
|
||||
MessageCodec: "json",
|
||||
Log: log.Config{
|
||||
Filename: "mylog.log",
|
||||
Directory: "/var/log",
|
||||
MaxFileSize: 200,
|
||||
MaxTime: 30 * time.Minute,
|
||||
MaxFileSize: 10 * 1000 * 1000,
|
||||
MaxTime: time.Hour * 24,
|
||||
FileTimestampFormat: "2006-01-02_150405",
|
||||
},
|
||||
Ship: ShipConfig{
|
||||
Url: "127.0.0.1:8089",
|
||||
StartBlockNum: 23671836,
|
||||
EndBlockNum: 23872222,
|
||||
IrreversibleOnly: true,
|
||||
MaxMessagesInFlight: 1337,
|
||||
Cache: Cache{
|
||||
Storage: "redis",
|
||||
},
|
||||
Telegram: TelegramConfig{
|
||||
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
|
||||
Channel: -123456789,
|
||||
AbiCache: AbiCache{
|
||||
ApiTimeout: time.Second,
|
||||
},
|
||||
Ship: ShipConfig{
|
||||
Url: "ws://127.0.0.1:8080",
|
||||
StartBlockNum: shipclient.NULL_BLOCK_NUMBER,
|
||||
EndBlockNum: shipclient.NULL_BLOCK_NUMBER,
|
||||
MaxMessagesInFlight: 10,
|
||||
EnableTableDeltas: true,
|
||||
},
|
||||
Redis: RedisConfig{
|
||||
Addr: "localhost:6379",
|
||||
User: "userfromcli",
|
||||
Password: "passwd",
|
||||
DB: 4,
|
||||
Prefix: "some::ship",
|
||||
Addr: "127.0.0.1:6379",
|
||||
Prefix: "ship",
|
||||
},
|
||||
}
|
||||
|
||||
builder := NewBuilder()
|
||||
builder.SetSource(bytes.NewBuffer([]byte(`
|
||||
name: "ship-reader-1"
|
||||
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()
|
||||
cfg, err := NewBuilder().
|
||||
SetSource(bytes.NewReader([]byte(``))).
|
||||
SetFlags(GetFlags()).
|
||||
Build()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &expected, cfg)
|
||||
|
|
@ -164,6 +160,8 @@ func TestBuilder_Flags(t *testing.T) {
|
|||
require.NoError(t, flags.Set("redis-password", "secret123"))
|
||||
require.NoError(t, flags.Set("redis-db", "3"))
|
||||
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-channel", "-293492332"))
|
||||
require.NoError(t, flags.Set("log-max-filesize", "25mb"))
|
||||
|
|
@ -175,6 +173,9 @@ func TestBuilder_Flags(t *testing.T) {
|
|||
require.NoError(t, flags.Set("irreversible-only", "true"))
|
||||
require.NoError(t, flags.Set("max-msg-in-flight", "98"))
|
||||
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().
|
||||
SetSource(bytes.NewReader([]byte(``))).
|
||||
|
|
@ -189,6 +190,12 @@ func TestBuilder_Flags(t *testing.T) {
|
|||
MaxTime: time.Minute * 10,
|
||||
FileTimestampFormat: "0102-15:04:05",
|
||||
},
|
||||
Cache: Cache{
|
||||
Storage: "memcached",
|
||||
},
|
||||
AbiCache: AbiCache{
|
||||
ApiTimeout: time.Hour * 16,
|
||||
},
|
||||
Ship: ShipConfig{
|
||||
Url: "ws://myship.com:7823",
|
||||
StartBlockNum: 7327833,
|
||||
|
|
@ -196,6 +203,11 @@ func TestBuilder_Flags(t *testing.T) {
|
|||
MaxMessagesInFlight: 98,
|
||||
IrreversibleOnly: true,
|
||||
Chain: "wax",
|
||||
Blacklist: *types.NewBlacklist(map[string][]string{
|
||||
"contract": {"action1", "action2"},
|
||||
"contract2": {"action1"},
|
||||
}),
|
||||
BlacklistIsWhitelist: true,
|
||||
},
|
||||
Telegram: TelegramConfig{
|
||||
Id: "72983126312982618",
|
||||
|
|
@ -213,3 +225,29 @@ func TestBuilder_Flags(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Get a flag set with all flags mapping to a config value.
|
||||
func GetFlags() *pflag.FlagSet {
|
||||
flags := pflag.FlagSet{}
|
||||
|
||||
|
|
@ -25,6 +26,12 @@ func GetFlags() *pflag.FlagSet {
|
|||
flags.String("telegram-id", "", "Id of telegram bot")
|
||||
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
|
||||
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")
|
||||
|
|
@ -36,12 +43,17 @@ func GetFlags() *pflag.FlagSet {
|
|||
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.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.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.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.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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg/thalos/internal/log"
|
||||
"github.com/eosswedenorg/thalos/internal/types"
|
||||
"github.com/karlseguin/typed"
|
||||
)
|
||||
|
||||
type RedisConfig struct {
|
||||
|
|
@ -12,18 +16,30 @@ type RedisConfig struct {
|
|||
Prefix string `yaml:"prefix"`
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
Storage string `yaml:"storage" mapstructure:"storage"`
|
||||
Options typed.Typed `yaml:"options" mapstructure:"options"`
|
||||
}
|
||||
|
||||
type TelegramConfig struct {
|
||||
Id string `yaml:"id" mapstructure:"id"`
|
||||
Channel int64 `yaml:"channel" mapstructure:"channel"`
|
||||
}
|
||||
|
||||
type AbiCache struct {
|
||||
ApiTimeout time.Duration `yaml:"api_timeout" mapstructure:"api_timeout"`
|
||||
}
|
||||
|
||||
type ShipConfig struct {
|
||||
Url string `yaml:"url" mapstructure:"url"`
|
||||
IrreversibleOnly bool `yaml:"irreversible_only" mapstructure:"irreversible_only"`
|
||||
MaxMessagesInFlight uint32 `yaml:"max_messages_in_flight" mapstructure:"max_messages_in_flight"`
|
||||
StartBlockNum uint32 `yaml:"start_block_num" mapstructure:"start_block_num"`
|
||||
EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"`
|
||||
Chain string `yaml:"chain" mapstructure:"chain"`
|
||||
Url string `yaml:"url" mapstructure:"url"`
|
||||
IrreversibleOnly bool `yaml:"irreversible_only" mapstructure:"irreversible_only"`
|
||||
MaxMessagesInFlight uint32 `yaml:"max_messages_in_flight" mapstructure:"max_messages_in_flight"`
|
||||
StartBlockNum uint32 `yaml:"start_block_num" mapstructure:"start_block_num"`
|
||||
EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"`
|
||||
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 {
|
||||
|
|
@ -33,8 +49,12 @@ type Config struct {
|
|||
|
||||
Log log.Config `yaml:"log" mapstructure:"log"`
|
||||
|
||||
Cache Cache `yaml:"cache" mapstructure:"cache"`
|
||||
|
||||
Redis RedisConfig `yaml:"redis" mapstructure:"redis"`
|
||||
MessageCodec string `yaml:"message_codec" mapstructure:"message_codec"`
|
||||
|
||||
AbiCache AbiCache `yaml:"abi_cache" mapstructure:"abi_cache"`
|
||||
|
||||
Telegram TelegramConfig `yaml:"telegram" mapstructure:"telegram"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package server
|
||||
|
||||
import "github.com/eoscanada/eos-go/ship"
|
||||
|
||||
// convert a ActionTrace to ActionTraceV1
|
||||
func toActionTraceV1(trace *ship.ActionTrace) *ship.ActionTraceV1 {
|
||||
if trace_v0, ok := trace.Impl.(*ship.ActionTraceV0); ok {
|
||||
// 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.Impl.(*ship.ActionTraceV1)
|
||||
}
|
||||
74
internal/server/message_queue.go
Normal file
74
internal/server/message_queue.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,34 +1,22 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/eosswedenorg/thalos/api"
|
||||
"github.com/eosswedenorg/thalos/api/message"
|
||||
"github.com/eosswedenorg/thalos/internal/abi"
|
||||
"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"
|
||||
|
||||
"github.com/eoscanada/eos-go"
|
||||
"github.com/eoscanada/eos-go/ship"
|
||||
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
|
||||
// thalos specific ones, encode them and finally post them to an api.Writer
|
||||
type ShipProcessor struct {
|
||||
|
|
@ -38,11 +26,7 @@ type ShipProcessor struct {
|
|||
// Abi manager used for cacheing
|
||||
abi *abi.AbiManager
|
||||
|
||||
// Writer to send messages to.
|
||||
writer driver.Writer
|
||||
|
||||
// Encoder used to encode messages
|
||||
encode message.Encoder
|
||||
queue MessageQueue
|
||||
|
||||
// Function for saving state.
|
||||
saver StateSaver
|
||||
|
|
@ -51,10 +35,13 @@ type ShipProcessor struct {
|
|||
state State
|
||||
|
||||
// System contract ("eosio" per default)
|
||||
syscontract eos.AccountName
|
||||
syscontract chain.Name
|
||||
|
||||
// ABI Returned from SHIP
|
||||
shipABI *eos.ABI
|
||||
shipABI *chain.Abi
|
||||
|
||||
// Action blacklist
|
||||
blacklist types.Blacklist
|
||||
}
|
||||
|
||||
// SpawnProcessor creates a new ShipProccessor that consumes the shipclient.Stream passed to it.
|
||||
|
|
@ -62,10 +49,9 @@ func SpawnProccessor(shipStream *shipclient.Stream, loader StateLoader, saver St
|
|||
processor := &ShipProcessor{
|
||||
saver: saver,
|
||||
abi: abi,
|
||||
writer: writer,
|
||||
shipStream: shipStream,
|
||||
encode: logDecoratedEncoder(codec.Encoder),
|
||||
syscontract: eos.AccountName("eosio"),
|
||||
syscontract: chain.N("eosio"),
|
||||
queue: NewMessageQueue(writer, codec.Encoder),
|
||||
}
|
||||
|
||||
loader(&processor.state)
|
||||
|
|
@ -75,66 +61,46 @@ func SpawnProccessor(shipStream *shipclient.Stream, loader StateLoader, saver St
|
|||
shipStream.InitHandler = processor.initHandler
|
||||
|
||||
// Needed because if nil, traces/table deltas will not be included in the response from ship.
|
||||
shipStream.TraceHandler = func([]*ship.TransactionTraceV0) {}
|
||||
shipStream.TableDeltaHandler = func([]*ship.TableDeltaV0) {}
|
||||
shipStream.TraceHandler = func(*ship.TransactionTraceArray) {}
|
||||
shipStream.TableDeltaHandler = func(*ship.TableDeltaArray) {}
|
||||
|
||||
return processor
|
||||
}
|
||||
|
||||
func (processor *ShipProcessor) initHandler(abi *eos.ABI) {
|
||||
func (processor *ShipProcessor) FetchDeltas(value bool) {
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
func (processor *ShipProcessor) updateAbiFromAction(act *ship.Action) error {
|
||||
ABI, err := processor.abi.GetAbi(processor.syscontract)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (processor *ShipProcessor) updateAbiFromAction(act *chain.Action) error {
|
||||
set_abi := struct {
|
||||
Abi string
|
||||
Account eos.AccountName
|
||||
Account chain.Name
|
||||
Abi chain.Bytes
|
||||
}{}
|
||||
if err = decode(ABI, act, &set_abi); err != nil {
|
||||
|
||||
if err := act.DecodeInto(&set_abi); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary_abi, err := hex.DecodeString(set_abi.Abi)
|
||||
if err != nil {
|
||||
abi := chain.Abi{}
|
||||
decoder := chain.NewDecoder(bytes.NewReader(set_abi.Abi))
|
||||
if err := decoder.Decode(&abi); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
return processor.abi.SetAbi(set_abi.Account, &abi)
|
||||
}
|
||||
|
||||
// Get the current block.
|
||||
|
|
@ -142,32 +108,15 @@ func (processor *ShipProcessor) GetCurrentBlock() uint32 {
|
|||
return processor.state.CurrentBlock
|
||||
}
|
||||
|
||||
func (processor *ShipProcessor) broadcastAction(act *message.ActionTrace) {
|
||||
payload, err := processor.encode(*act)
|
||||
if err != nil {
|
||||
log.WithField("act", act).Warn("failed to encode action")
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, block *ship.SignedBlockBytes, trace *ship.TransactionTraceV0) {
|
||||
func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, blockNumber uint32, block *ship.SignedBlock, trace *ship.TransactionTraceV0) {
|
||||
logger := log.WithField("type", "trace").WithField("tx_id", trace.ID.String()).Dup()
|
||||
|
||||
timestamp := block.BlockHeader.Timestamp.Time().UTC()
|
||||
|
||||
transaction := message.TransactionTrace{
|
||||
ID: trace.ID.String(),
|
||||
BlockNum: block.BlockNumber(),
|
||||
Timestamp: block.Timestamp.UTC(),
|
||||
BlockNum: blockNumber,
|
||||
Timestamp: timestamp,
|
||||
Status: trace.Status.String(),
|
||||
CPUUsageUS: trace.CPUUsageUS,
|
||||
NetUsage: trace.NetUsage,
|
||||
|
|
@ -181,31 +130,48 @@ func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, block *s
|
|||
// Actions
|
||||
for _, actionTraceVar := range trace.ActionTraces {
|
||||
|
||||
actionTrace := toActionTraceV1(actionTraceVar)
|
||||
actionTrace := ship_helper.ToActionTraceV1(actionTraceVar)
|
||||
actMsg := processor.proccessActionTrace(logger, actionTrace)
|
||||
if actMsg != nil {
|
||||
actMsg.TxID = trace.ID.String()
|
||||
actMsg.BlockNum = block.BlockNumber()
|
||||
actMsg.Timestamp = block.Timestamp.UTC()
|
||||
actMsg.BlockNum = blockNumber
|
||||
actMsg.Timestamp = timestamp
|
||||
|
||||
processor.broadcastAction(actMsg)
|
||||
processor.queue.PostAction(*actMsg)
|
||||
|
||||
transaction.ActionTraces = append(transaction.ActionTraces, *actMsg)
|
||||
}
|
||||
}
|
||||
|
||||
processor.encodeQueue(api.TransactionChannel, transaction)
|
||||
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.
|
||||
if trace.Act.Account == processor.syscontract && trace.Act.Name == eos.ActionName("setabi") {
|
||||
err := processor.updateAbiFromAction(trace.Act)
|
||||
if trace.Act.Account == processor.syscontract && trace.Act.Name == chain.N("setabi") {
|
||||
|
||||
logger.WithFields(log.Fields{
|
||||
"contract": trace.Act.Account,
|
||||
"action": trace.Act.Name,
|
||||
}).Debug("Update contract ABI")
|
||||
|
||||
err := processor.updateAbiFromAction(&trace.Act)
|
||||
if err != nil {
|
||||
logger.WithError(err).Warn("Failed to update abi")
|
||||
}
|
||||
}
|
||||
|
||||
// Check blacklist if we should skip this action
|
||||
if !processor.blacklist.IsAllowed(trace.Act.Account.String(), trace.Act.Name.String()) {
|
||||
logger.WithFields(log.Fields{
|
||||
"contract": trace.Act.Account,
|
||||
"action": trace.Act.Name,
|
||||
}).Debug("Found in blacklist, skipping")
|
||||
return nil
|
||||
}
|
||||
|
||||
act := &message.ActionTrace{
|
||||
Name: trace.Act.Name.String(),
|
||||
Contract: trace.Act.Account.String(),
|
||||
|
|
@ -214,7 +180,7 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
|
|||
}
|
||||
|
||||
if trace.Receipt != nil {
|
||||
receipt := trace.Receipt.Impl.(*ship.ActionReceiptV0)
|
||||
receipt := trace.Receipt.V0
|
||||
act.Receipt = &message.ActionReceipt{
|
||||
Receiver: receipt.Receiver.String(),
|
||||
ActDigest: receipt.ActDigest.String(),
|
||||
|
|
@ -239,9 +205,14 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
|
|||
})
|
||||
}
|
||||
|
||||
logger.WithFields(log.Fields{
|
||||
"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 = decode(ABI, trace.Act, &act.Data); err != nil {
|
||||
if act.Data, err = trace.Act.Decode(ABI); err != nil {
|
||||
logger.WithFields(log.Fields{
|
||||
"contract": trace.Act.Account,
|
||||
"action": trace.Act.Name,
|
||||
|
|
@ -255,94 +226,129 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
|
|||
return 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 {
|
||||
logger.WithError(err).Warn("Failed to processs table delta row")
|
||||
}
|
||||
out = append(out, msg)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (processor *ShipProcessor) proccessDeltaRow(row ship.Row, table_name string) (message.TableDeltaRow, error) {
|
||||
msg := message.TableDeltaRow{
|
||||
Present: row.Present,
|
||||
RawData: row.Data,
|
||||
}
|
||||
|
||||
if processor.shipABI == nil {
|
||||
return msg, errors.New("No SHIP ABI present")
|
||||
}
|
||||
|
||||
v, err := processor.shipABI.Decode(bytes.NewReader(row.Data), table_name)
|
||||
if err != nil {
|
||||
return msg, errors.New("Failed to decode table delta")
|
||||
}
|
||||
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(block *ship.GetBlocksResultV0) {
|
||||
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 && block.ThisBlock.BlockNum < processor.state.CurrentBlock {
|
||||
log.WithField("old_block", processor.state.CurrentBlock).
|
||||
WithField("new_block", block.ThisBlock.BlockNum).
|
||||
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")
|
||||
|
||||
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,
|
||||
if err := processor.queue.PostRollback(msg); err != nil {
|
||||
log.WithError(err).Error("Failed to write rollback message")
|
||||
}
|
||||
|
||||
processor.encodeQueue(api.HeartbeatChannel, hb)
|
||||
}
|
||||
|
||||
mainLogger := log.WithField("block", block.ThisBlock.BlockNum).Dup()
|
||||
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 block.Traces != nil && len(block.Traces.Elem) > 0 {
|
||||
for _, trace := range block.Traces.AsTransactionTracesV0() {
|
||||
processor.processTransactionTrace(mainLogger, block.Block, trace)
|
||||
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 {
|
||||
for _, trace := range unpacked {
|
||||
processor.processTransactionTrace(mainLogger, blockNumber, &block, trace.V0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process deltas
|
||||
for _, delta := range block.Deltas.AsTableDeltasV0() {
|
||||
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 {
|
||||
logger := mainLogger.WithField("type", "table_delta").Dup()
|
||||
for _, delta := range deltas {
|
||||
|
||||
logger := mainLogger.WithField("type", "table_delta").WithField("table", delta.Name).Dup()
|
||||
|
||||
rows := []message.TableDeltaRow{}
|
||||
for _, row := range delta.Rows {
|
||||
|
||||
msg := message.TableDeltaRow{
|
||||
Present: row.Present,
|
||||
RawData: row.Data,
|
||||
}
|
||||
|
||||
if processor.shipABI != nil {
|
||||
v, err := processor.shipABI.DecodeTableRowTyped(delta.Name, row.Data)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(v, &msg.Data)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to decode json")
|
||||
}
|
||||
} else {
|
||||
logger.Error("Failed to decode table delta")
|
||||
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 {
|
||||
logger.WithError(err).Error("Failed to post table delta message")
|
||||
}
|
||||
} else {
|
||||
logger.Warn("No SHIP ABI present")
|
||||
}
|
||||
|
||||
rows = append(rows, msg)
|
||||
}
|
||||
|
||||
message := message.TableDelta{
|
||||
BlockNum: block.Block.BlockNumber(),
|
||||
Timestamp: block.Block.Timestamp.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.writer.Flush()
|
||||
err := processor.queue.Flush()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to send messages")
|
||||
}
|
||||
|
|
@ -355,5 +361,5 @@ func (processor *ShipProcessor) processBlock(block *ship.GetBlocksResultV0) {
|
|||
|
||||
// Close closes the writer associated with the processor.
|
||||
func (processor *ShipProcessor) Close() error {
|
||||
return processor.writer.Close()
|
||||
return processor.queue.Close()
|
||||
}
|
||||
|
|
|
|||
27
internal/ship/action.go
Normal file
27
internal/ship/action.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
}
|
||||
37
internal/ship/contract_row.go
Normal file
37
internal/ship/contract_row.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
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)
|
||||
}
|
||||
32
internal/ship/contract_row_test.go
Normal file
32
internal/ship/contract_row_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
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)
|
||||
}
|
||||
36
internal/ship/table_delta.go
Normal file
36
internal/ship/table_delta.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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")
|
||||
}
|
||||
70
internal/ship/table_delta_test.go
Normal file
70
internal/ship/table_delta_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
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)
|
||||
}
|
||||
93
internal/ship/variant.go
Normal file
93
internal/ship/variant.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
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())
|
||||
}
|
||||
59
internal/types/blacklist.go
Normal file
59
internal/types/blacklist.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
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)
|
||||
}
|
||||
95
internal/types/blacklist_test.go
Normal file
95
internal/types/blacklist_test.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
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"))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue