1
0
Fork 0
mirror of https://github.com/eosswedenorg/thalos synced 2026-06-19 04:50:02 +02:00

Compare commits

..

111 commits

Author SHA1 Message Date
6ad6b44433 Version 1.1.9 2025-01-23 19:32:45 +01:00
365558019c
Merge pull request #38 from eosswedenorg/dependabot/go_modules/golang.org/x/net-0.33.0
build(deps): bump golang.org/x/net from 0.32.0 to 0.33.0
2025-01-23 19:27:52 +01:00
dependabot[bot]
438699fb10
build(deps): bump golang.org/x/net from 0.32.0 to 0.33.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-23 18:26:09 +00:00
07ccab4628 Version 1.1.8 2024-12-23 09:30:24 +01:00
7a3a83a097
Merge pull request #36 from eosswedenorg/dependabot/go_modules/github.com/quic-go/quic-go-0.48.2
build(deps): bump github.com/quic-go/quic-go from 0.42.0 to 0.48.2
2024-12-21 19:52:42 +01:00
af5593b5f3 github workflows: update to go version 1.22 2024-12-21 19:50:01 +01:00
bb268aa9fa update github.com/imroc/req to v3.49.0 2024-12-21 19:47:51 +01:00
dependabot[bot]
4bc6df8c83 build(deps): bump github.com/quic-go/quic-go from 0.42.0 to 0.48.2
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.42.0 to 0.48.2.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.42.0...v0.48.2)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-21 19:42:01 +01:00
ce80405254 github workflows: add permissions. 2024-12-21 19:14:26 +01:00
a2c39f788e
Merge pull request #37 from eosswedenorg/dependabot/go_modules/golang.org/x/crypto-0.31.0
build(deps): bump golang.org/x/crypto from 0.22.0 to 0.31.0
2024-12-21 19:01:01 +01:00
dependabot[bot]
ece6ccfac1
build(deps): bump golang.org/x/crypto from 0.22.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-21 17:58:53 +00:00
96b6a79813 Version v1.1.8-rc1 2024-12-04 15:21:50 +01:00
2006da6a19 internal/types/blacklist.go: add BlacklistWildcard constant 2024-12-04 15:15:22 +01:00
eb2032e233 internal/types/blacklist.go: implement wildcard for contracts 2024-12-04 15:14:10 +01:00
120c2acdc6 internal/server/ship_processor.go: refactor processDeltaRows() to reduce nested blocks 2024-11-26 06:52:14 +01:00
d5bc23a63e Version 1.1.7 2024-11-11 19:40:17 +01:00
89011b8fc8 Version 1.1.7-rc2 2024-11-03 12:11:09 +01:00
d959645836 cmd/thalos/server.go: set MaxMessagesInFlight = 1 when creating ship client 2024-11-03 12:03:48 +01:00
9bdcbacaa0 go.mod: update eosswedenrg-go/antelope-ship-client to v0.3.2 2024-11-03 12:02:27 +01:00
50adfa0026 config.example.yml: minor fixes. 2024-10-21 13:52:11 +02:00
213a31570d Version 1.1.7-rc1 2024-10-21 12:32:08 +02:00
4ae30f0cc6 debian/changelog: format fix 2024-10-21 12:30:48 +02:00
e2f7efe8b0 config.example.yml: add ship.table_deltas 2024-10-21 12:28:55 +02:00
0cb86ff771 cmd/thalos/server.go: configure fetching of table deltas. 2024-10-21 12:28:15 +02:00
33c983f6c5 internal/server/ship_processor.go: adding FetchDeltas() 2024-10-21 12:27:29 +02:00
bedb8a92e8 config: adding Ship.EnableTableDeltas and table_deltas cli flag 2024-10-21 12:24:29 +02:00
ffd2504834 Version 1.1.6 2024-10-16 16:37:56 +02:00
6231142e4a makefile: make sure we apppend to GOBULDFLAGS if user wants to add their own. 2024-10-16 16:23:03 +02:00
84547e5ad3 minor style fixes. 2024-10-16 16:23:03 +02:00
70aa6cd295 api/channel_test.go: rearange fields. 2024-10-16 16:23:03 +02:00
020f81ed65 README.md: Update minimum go version 2024-10-16 16:23:03 +02:00
4f46188ed4 README.md: Link to docker page 2024-10-16 16:23:03 +02:00
ef0ac7a1b8 .github/workflows/release.yml: need to update version regex for musl builds 2024-10-16 16:23:03 +02:00
44dfd4d738
Merge pull request #35 from Avm07/master
Fix typo in config.example.yml
2024-10-09 22:13:31 +02:00
Artem Maliuga
08fcdb0590
Fix typo in config.example.yml 2024-10-09 20:17:04 +03:00
a3c428ee5c Version 1.1.5 2024-08-29 15:33:45 +02:00
6d292a482e Merge pull request #34 from eosswedenorg/dependabot/go_modules/github.com/quic-go/quic-go-0.42.0
build(deps): bump github.com/quic-go/quic-go from 0.41.0 to 0.42.0
2024-08-29 15:30:58 +02:00
982b920576 .github/workflows/test.yml: dont test on 1.20 2024-08-29 15:30:57 +02:00
dependabot[bot]
92ac8521ed build(deps): bump github.com/quic-go/quic-go from 0.41.0 to 0.42.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.41.0 to 0.42.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.41.0...v0.42.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-29 15:30:57 +02:00
7e42c27194 bump github.com/shufflingpixels/antelope-go to v0.1.5 2024-08-29 15:30:56 +02:00
0e94ca2688 debian/changelog: typo fix 2024-08-26 14:55:43 +02:00
7197720350 small fix to cli descriptions and flag help texts 2024-08-26 13:14:00 +02:00
c769b6a700 typo fixes and added comments 2024-08-26 13:11:32 +02:00
48ca5e4f92 .github/workflows/release.yml: fix regex that parses the Makefile for version number 2024-08-11 17:44:39 +02:00
c0c062a1b5 Version 1.1.5-rc1 2024-08-11 17:27:35 +02:00
aaeb2739ab config.example.yml: add example for cache section 2024-08-11 17:23:47 +02:00
fa6508ba90 internal/server/ship_processor.go: try decoding table data for contract_row table deltas 2024-08-11 17:04:06 +02:00
6d7d004b2b internal/ship/contract_row.go: adding DecodeContractRow that actually decodes data from abi 2024-08-11 17:03:42 +02:00
a892b40cde internal/ship/contract_row.go: rename DecodeContractRow to ParseContractRow 2024-08-11 17:03:14 +02:00
14a498d7d6 internal/server/ship_processor.go: add WithError() to a logging call 2024-08-11 16:59:23 +02:00
67d33a280e move internal/server/helpers.go to internal/ship/action.go 2024-08-11 16:56:55 +02:00
2ed9518635 internal/server/helpers.go: refactor some functions to internal/ship 2024-08-11 16:52:58 +02:00
0e75fb869b adding internal/ship/contract_row.go 2024-08-11 16:14:15 +02:00
3d83fcc869 go mod: update antelope-go to v0.1.4 2024-08-11 15:41:02 +02:00
4bf589647c internal/server/helpers.go: add a comment in empty for loop (as i almost removed it because it was empty) 2024-08-11 13:03:55 +02:00
ecdde63f68 internal/config/cli.go: set description for abi-cache-api-timeout flag 2024-07-28 14:45:33 +02:00
18a8feea9e make: no need to use find, can specify the directory where the go files are 2024-07-25 19:36:51 +02:00
e038be5324 .github/workflows/devbuild.yaml: update names 2024-07-25 12:22:00 +02:00
ec874430c3 .github/workflows/devbuild.yaml: improve the sed expression. 2024-07-24 00:53:24 +02:00
9778fa8610 .github/workflows/devbuild.yaml: fix version string bash stuff :) 2024-07-24 00:45:16 +02:00
e8b90dab77 Makefile: only set PROGRAM_VERSION if its unset 2024-07-24 00:22:14 +02:00
b7e0cb7b56 adding .github/workflows/devbuild.yaml 2024-07-24 00:17:14 +02:00
8a938c3f9e cmd/thalos/server.go: use factory and config to create cache store 2024-07-23 18:34:30 +02:00
ebbaf6e2c1 internal/config: Adding cache configuration 2024-07-23 16:46:02 +02:00
d1d4bf58b1 cache: adding factory method for redis 2024-07-23 16:32:36 +02:00
2f31eb47d4 cache: adding factory 2024-07-23 16:31:18 +02:00
ec40e954f2 internal/abi/manager.go: Use config.AbiCache to configure the manager 2024-07-21 12:52:40 +02:00
b60436c48a config: adding AbiCache 2024-07-21 12:50:41 +02:00
dccd7c0520 internal/config/builder_test.go: fix syntax error 2024-07-19 13:48:59 +02:00
1b1e6a1e33 cmd/thalos/server.go: move chainInfoOnce to its own file 2024-07-19 13:43:37 +02:00
c523f1c797 make: build all go files in cmd/thalos 2024-07-19 13:40:03 +02:00
ad90966e24 internal/abi/manager_test.go: remove call to fmt.Println() 2024-07-18 21:42:35 +02:00
22896f8859 internal/config/builder_test.go: rewrite ConfigWithFlags to WithDefaultConfig as we already have a test for flags 2024-07-18 21:35:23 +02:00
8c5815a2ce Version 1.1.4 2024-07-17 09:32:35 +02:00
8155d49ef4 .github/workflows/test.yml: can't run tests on different arch than host.
So just do i386 and amd64
2024-07-16 20:56:03 +02:00
621f9ceeea internal/server/helpers_test.go: set large untyped int constants to int64. 2024-07-16 20:44:36 +02:00
5121098cfe gomod: update shufflingpixels/antelope-go to v0.1.3 2024-07-16 20:02:00 +02:00
d1a6d038a3 .github/workflows/test.yml: run tests on all supported os/arch pairs 2024-07-16 17:15:07 +02:00
18918faad0 Merge branch 'blackwhitelist' into dev 2024-07-16 17:09:03 +02:00
20168a9329 implement whitelist option in blacklist 2024-07-16 16:59:01 +02:00
4f27307c70 internal/types/blacklist.go: add isWhitelist field 2024-07-15 23:02:29 +02:00
0aee0a97aa internal/types/blacklist.go: adding Empty() 2024-07-15 22:58:14 +02:00
cbd3196cf9 internal/types/blacklist.go: change Lookup to IsAllowed and add IsDenied 2024-07-13 19:23:49 +02:00
e2443dcd27 Version 1.1.3 2024-07-03 18:17:27 +02:00
a9a129e418 internal/abi/manager_test.go: update action and table names to chain.Name types 2024-07-03 18:16:14 +02:00
ef6329d1b7 internal/server/ship_processor.go: updateAbiFromAction() should not hex decode abi as it is in binary format
only when encoding the action to json it is in hex, but from ship =
always binary
2024-07-03 18:01:31 +02:00
1a782604d3 gomod: update shufflingpixels/antelope-go to v0.1.2 2024-07-03 17:59:24 +02:00
e0f598eba4 internal/server/ship_processor.go: in updateAbiFromAction() fix field order in set_abi struct 2024-07-02 19:46:24 +02:00
0e124bb866 gomod: update shufflingpixels/antelope-go from v0.1.0 to v0.1.1 2024-07-02 18:59:41 +02:00
71f9d4d789 gomod: update pnx/antelope-go v0.0.4 to shufflingpixels/antelope-go v0.1.0 2024-07-02 16:53:41 +02:00
a15cf50377 gomod: update eosswedenorg-go/antelope-ship-client to v0.3.0 2024-07-02 16:51:39 +02:00
dfa370a462 internal/server/ship_processor.go: adding some debug logging. 2024-07-02 11:26:26 +02:00
70eda8e397 Version 1.1.2 2024-06-27 14:30:20 +02:00
ec664ea207 debian: adding sysconfig environment config 2024-06-27 08:53:32 +02:00
aeeb049403 debian/patches/0001-fix-config-logpath.patch: update 2024-06-27 08:26:29 +02:00
690dabe6bd config.example.yml: add blacklist 2024-06-23 15:21:56 +02:00
be35936dc2 Version 1.1.2-rc4 2024-06-23 14:58:18 +02:00
32f3f1dfb1 cmd/thalos/server.go: set blacklist in the processor 2024-06-23 14:54:44 +02:00
0bc70f82d1 internal/server/ship_processor.go: implement blacklist 2024-06-23 14:53:58 +02:00
6d14591f6b internal/config: adding blacklist field to Ship config 2024-06-23 14:52:05 +02:00
93a816cb2c Adding internal/types/blacklist.go 2024-06-23 14:46:38 +02:00
a79bf8f2f3 Adding variabels for docker. 2024-06-23 11:42:26 +02:00
a466392b84 use github.com/shufflingpixels/jsontime-go instead of github.com/eosswedenorg-go/jsontime 2024-06-23 11:33:35 +02:00
2b3dc35393 jsontime: skip aliases and use actual format and location
This is so weird. this works in tests but not when running the
application. I guess there is some weird global state race condition
going on because its used by antelope-go.

Should rewrite the jsontime api because I have forked it anyway.
2024-06-22 17:39:10 +02:00
01054fb0bf internal/abi/manager_test.go: update because of antelope-go v0.0.4 2024-06-22 17:02:19 +02:00
f19304bf55 use antelope-go v0.0.4 2024-06-22 16:47:00 +02:00
5b02dfa53f jsontime: use struct tags instead of setting default format.
Some other package (antelope-go in this case) also sets the default
format and therefore it clashes with our code.
2024-06-22 16:19:48 +02:00
a2f8c3dc7c v1.1.2-rc3 2024-06-19 21:51:14 +02:00
8b8867394f tools: cleanup and refactor to make code more readable. 2024-06-19 21:45:45 +02:00
ea5b2b8fc2 internal/server/helpers.go fix a bug in isVariant() where v.Elem() was called on non interface/pointer 2024-05-28 13:30:21 +02:00
634205a546 Makefile: should be "docker image push" instead of "docker publish" 2024-05-17 18:35:40 +02:00
53 changed files with 1534 additions and 653 deletions

82
.github/workflows/devbuild.yaml vendored Normal file
View 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

View file

@ -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: |

View file

@ -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:

View file

@ -1,30 +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.2-rc2
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 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 ghcr.io/eosswedenorg/thalos:$(PROGRAM_VERSION) docker
docker image build --build-arg VERSION=$(PROGRAM_VERSION) -t $(DOCKER_IMAGE_REPO):$(DOCKER_IMAGE_TAG) docker
docker-publish:
docker publish ghcr.io/eosswedenorg/thalos:$(PROGRAM_VERSION)
docker image push $(DOCKER_IMAGE_REPO):$(DOCKER_IMAGE_TAG)
install: build tools
install -D build/$(PROGRAM) $(DESTDIR)$(BINDIR)/$(PROGRAM)

View file

@ -9,9 +9,13 @@ Consult the [documentation](https://thalos.waxsweden.org/docs) for more informat
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:

View file

@ -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) {

View file

@ -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

View file

@ -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
)

View file

@ -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=

View file

@ -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())
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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() {

View 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
}
}

View file

@ -26,11 +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"
antelopeapi "github.com/pnx/antelope-go/api"
"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,9 +154,9 @@ func LogLevels() []string {
return list
}
func initAbiManager(api *antelopeapi.Client, 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() *antelopeapi.Info, cache *cache.Cache, current_block_no_cache bool) StateLoader {
@ -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 *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
}
}
func ConnectRedis(conf *config.RedisConfig) (*redis.Client, error) {
logEntry := log.WithFields(log.Fields{
"addr": conf.Addr,
@ -371,12 +345,14 @@ 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)
@ -387,6 +363,7 @@ func serverCmd(cmd *cobra.Command, args []string) {
s.StartBlock = conf.Ship.StartBlockNum
s.EndBlock = conf.Ship.EndBlockNum
s.IrreversibleOnly = conf.Ship.IrreversibleOnly
s.MaxMessagesInFlight = 1
})
// Get codec
@ -411,10 +388,13 @@ func serverCmd(cmd *cobra.Command, args []string) {
Prefix: conf.Redis.Prefix,
ChainID: chain_id,
}),
initAbiManager(antelopeClient, 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)

View file

@ -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
View 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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

135
debian/changelog vendored
View file

@ -1,3 +1,137 @@
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
@ -146,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

View file

@ -1,12 +1,11 @@
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
# Format to rename log files when rotating
time_format: 2006-01-02_150405
time_format: 2006-01-02_150405

6
debian/sysconfig/thalos-server vendored Normal file
View 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"

View file

@ -6,9 +6,10 @@ 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
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target

View file

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

View file

@ -2,14 +2,14 @@
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#
exit 0
exit 0

10
debian/thalos.postrm vendored
View file

@ -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
exit 0

View file

@ -1,6 +1,6 @@
FROM alpine:latest
LABEL maintainer="Henrik Hautakoski <henrik.Hautakoski@gmail.com>"
ARG VERSION=1.1.2-rc2
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" ]

38
go.mod
View file

@ -1,19 +1,20 @@
module github.com/eosswedenorg/thalos
go 1.20
go 1.22.0
require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/docker/go-units v0.5.0
github.com/eosswedenorg-go/antelope-ship-client v0.2.9
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/pnx/antelope-go v0.0.4-0.20240511122657-905898ed2110
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,31 +24,38 @@ require (
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // 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.7.6 // 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.7 // 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/gomega v1.31.1 // 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
@ -57,12 +65,16 @@ require (
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
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.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
)

94
go.sum
View file

@ -1,15 +1,24 @@
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.30.2 h1:lc1UAUT9ZA7h4srlfBmBt2aorm5Yftk9nBjxz7EyY9I=
github.com/alicebob/miniredis/v2 v2.30.2/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/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 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/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=
@ -20,23 +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/eosswedenorg-go/antelope-ship-client v0.2.9 h1:Isa90Tktdumj/P9XgQ8kBRqS+2MAdcPl7ev/n0KFvUQ=
github.com/eosswedenorg-go/antelope-ship-client v0.2.9/go.mod h1:a0Kp4BJID8DMD0pmHnK5/gojtpRhh2pqqgt4Hcy5Wrw=
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=
@ -58,6 +70,8 @@ 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=
@ -69,21 +83,25 @@ 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.7.6 h1:SUVWgFt/dJsSzpzpnc8pHdL79zoE6O8FSCfNvbTZXVU=
github.com/imroc/req/v3 v3.7.6/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU=
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/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.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/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=
@ -118,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=
@ -128,8 +148,8 @@ 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.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
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=
@ -137,20 +157,27 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
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/pnx/antelope-go v0.0.3 h1:LUTQtCdErPqZqF+15ZinFXvQOHaPYuOHyO24TCaqrN8=
github.com/pnx/antelope-go v0.0.3/go.mod h1:NCCgAR1/Yzh6DigwtFFMcMhG6kGzfNx5sUorUMpBuNc=
github.com/pnx/antelope-go v0.0.4-0.20240511122657-905898ed2110 h1:ipRjWXy6HNkd3QdAdr2RJEHUxUETc9UbQB3dyU70Aq0=
github.com/pnx/antelope-go v0.0.4-0.20240511122657-905898ed2110/go.mod h1:NCCgAR1/Yzh6DigwtFFMcMhG6kGzfNx5sUorUMpBuNc=
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=
@ -191,10 +218,15 @@ 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.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=
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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -202,13 +234,17 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
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.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
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.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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -217,7 +253,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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-20220111093109-d55c255bac03/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=
@ -225,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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.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=
@ -259,8 +294,8 @@ 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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.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=
@ -274,8 +309,10 @@ 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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -283,6 +320,8 @@ 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=
@ -296,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=
@ -315,3 +356,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
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=

View file

@ -6,22 +6,25 @@ import (
"time"
"github.com/eosswedenorg/thalos/internal/cache"
"github.com/pnx/antelope-go/api"
"github.com/pnx/antelope-go/chain"
"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 *api.Client
ctx context.Context
}
// Create a new ABI Manager
func NewAbiManager(cache *cache.Cache, api *api.Client) *AbiManager {
func NewAbiManager(cfg *config.AbiCache, cache *cache.Cache, api *api.Client) *AbiManager {
return &AbiManager{
cache: cache,
api: api,
cfg: cfg,
ctx: context.Background(),
}
}
@ -38,7 +41,7 @@ func (mgr *AbiManager) SetAbi(account chain.Name, abi *chain.Abi) error {
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.String())
if err != nil {

View file

@ -6,11 +6,13 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/pnx/antelope-go/api"
"github.com/pnx/antelope-go/chain"
"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"
)
@ -132,12 +134,16 @@ func mockAPI(handler http.HandlerFunc) (*api.Client, *httptest.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 := chain.Abi{}
err := json.Unmarshal([]byte(abiString), &abi)
@ -152,6 +158,10 @@ func TestManager_GetAbiFromCache(t *testing.T) {
}
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) {
@ -161,12 +171,10 @@ func TestManager_GetAbiFromAPI(t *testing.T) {
assert.NoError(t, err)
}))
mgr := NewAbiManager(cache, api)
mgr := NewAbiManager(cfg, cache, api)
c_abi, err := mgr.GetAbi(chain.N("testaccount"))
assert.NoError(t, err)
fmt.Println(c_abi)
assert_abi(t, c_abi)
}

26
internal/cache/factory.go vendored Normal file
View 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
View 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)
}

View file

@ -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)
}

View file

@ -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()

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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"`
}

View file

@ -13,7 +13,7 @@ type MessageQueue struct {
// Writer to write messages to
writer driver.Writer
// encoder to encode messages with
// Encoder to encode messages with
encode message.Encoder
}

View file

@ -2,17 +2,19 @@ package server
import (
"bytes"
"encoding/hex"
"errors"
"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"
shipclient "github.com/eosswedenorg-go/antelope-ship-client"
"github.com/pnx/antelope-go/chain"
"github.com/pnx/antelope-go/ship"
"github.com/shufflingpixels/antelope-go/chain"
"github.com/shufflingpixels/antelope-go/ship"
)
// A ShipProcessor will consume messages from a ship stream, convert the messages into
@ -37,6 +39,9 @@ type ShipProcessor struct {
// ABI Returned from SHIP
shipABI *chain.Abi
// Action blacklist
blacklist types.Blacklist
}
// SpawnProcessor creates a new ShipProccessor that consumes the shipclient.Stream passed to it.
@ -62,6 +67,19 @@ func SpawnProccessor(shipStream *shipclient.Stream, loader StateLoader, saver St
return processor
}
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
}
@ -69,25 +87,20 @@ func (processor *ShipProcessor) initHandler(abi *chain.Abi) {
// updateAbiFromAction updates the contract abi based on the ship.Action passed.
func (processor *ShipProcessor) updateAbiFromAction(act *chain.Action) error {
set_abi := struct {
Abi string
Account chain.Name
Abi chain.Bytes
}{}
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 := chain.Abi{}
err = chain.NewDecoder(bytes.NewReader(binary_abi)).Decode(&contract_abi)
if 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.
@ -117,7 +130,7 @@ func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, blockNum
// 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()
@ -138,12 +151,27 @@ func (processor *ShipProcessor) processTransactionTrace(log *log.Entry, blockNum
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 == 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(),
@ -177,6 +205,11 @@ 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 act.Data, err = trace.Act.Decode(ABI); err != nil {
@ -196,33 +229,47 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
func (processor *ShipProcessor) proccessDeltaRows(logger *log.Entry, table_name string, rows []ship.Row) []message.TableDeltaRow {
out := []message.TableDeltaRow{}
for _, row := range rows {
msg := message.TableDeltaRow{
Present: row.Present,
RawData: row.Data,
}
if processor.shipABI != nil {
v, err := processor.shipABI.Decode(bytes.NewReader(row.Data), table_name)
if err == nil {
v, err := parseTableDeltaData(v)
if err == nil {
msg.Data = v
} else {
logger.WithError(err).Error("Failed to parse table delta data")
}
} else {
logger.Error("Failed to decode table delta")
}
} else {
logger.Warn("No SHIP ABI present")
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(blockResult *ship.GetBlocksResultV0) {
block := ship.SignedBlock{}

27
internal/ship/action.go Normal file
View 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
}

View 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)
}

View 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)
}

View 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")
}

View file

@ -1,8 +1,9 @@
package server
package ship_test
import (
"testing"
"github.com/eosswedenorg/thalos/internal/ship"
"github.com/stretchr/testify/assert"
)
@ -23,7 +24,7 @@ func TestParseTableDeltaData(t *testing.T) {
map[string]interface{}{
"consumed": 8107,
"last_ordinal": 308855607,
"value_ex": 3854030492,
"value_ex": int64(3854030492),
},
},
"slice": []interface{}{
@ -51,7 +52,7 @@ func TestParseTableDeltaData(t *testing.T) {
"average_block_net_usage": map[string]interface{}{
"consumed": 8107,
"last_ordinal": 308855607,
"value_ex": 3854030492,
"value_ex": int64(3854030492),
},
"slice": []interface{}{1, 2, "tree"},
"single_value": uint32(12933729),
@ -62,7 +63,7 @@ func TestParseTableDeltaData(t *testing.T) {
"virtual_net_limit": 1048576000,
}
actual, err := parseTableDeltaData(input)
actual, err := ship.ParseTableDeltaData(input)
assert.NoError(t, err)
assert.Equal(t, expected, actual)

View file

@ -1,35 +1,8 @@
package server
package ship
import (
"fmt"
"reflect"
import "reflect"
"github.com/pnx/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
}
func isVariantName(name string) bool {
func IsVariantName(name string) bool {
validVariants := []string{
"get_status_request_v0",
"block_position",
@ -107,44 +80,14 @@ func isVariantName(name string) bool {
// 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 {
func IsVariant(v reflect.Value) bool {
if v.Kind() != reflect.Slice || v.Len() != 2 {
return false
}
if !isVariantName(v.Index(0).Elem().String()) {
return false
for v = v.Index(0); v.Kind() == reflect.Interface || v.Kind() == reflect.Pointer; v = v.Elem() {
// Intentionally empty
}
return true
}
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")
}
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
return v.Kind() == reflect.String && IsVariantName(v.String())
}

View 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)
}

View 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"))
}