Skip to content

Commit 71cfb61

Browse files
committed
Initial commit
0 parents  commit 71cfb61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+16835
-0
lines changed

.github/workflows/test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 10
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v2
16+
17+
- name: Setup Go
18+
uses: actions/setup-go@v4
19+
with:
20+
go-version: 1.23.x
21+
22+
- name: Test
23+
run: make test

.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
23+
# Build output directories
24+
build/
25+
26+
# Visual Studio Code
27+
.vscode/

Dockerfile.geyser

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM alpine
2+
3+
ARG GRPC_HEALTH_PROBE_VERSION=v0.3.1
4+
5+
RUN apk add --no-cache curl
6+
RUN curl -L https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/$GRPC_HEALTH_PROBE_VERSION/grpc_health_probe-linux-amd64 -o /bin/grpc_health_probe
7+
RUN chmod +x /bin/grpc_health_probe
8+
9+
COPY build/linux-amd64/geyser /geyser
10+
11+
HEALTHCHECK --interval=15s --timeout=5s --retries=3 CMD /bin/grpc_health_probe -addr=localhost:8086 -connect-timeout 250ms -rpc-timeout 100ms || exit 1
12+
13+
CMD ["/geyser"]

Dockerfile.rpc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM alpine
2+
3+
ARG GRPC_HEALTH_PROBE_VERSION=v0.3.1
4+
5+
RUN apk add --no-cache curl
6+
RUN curl -L https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/$GRPC_HEALTH_PROBE_VERSION/grpc_health_probe-linux-amd64 -o /bin/grpc_health_probe
7+
RUN chmod +x /bin/grpc_health_probe
8+
9+
COPY build/linux-amd64/rpc /rpc
10+
11+
EXPOSE 8085
12+
EXPOSE 8086
13+
14+
HEALTHCHECK --interval=15s --timeout=5s --retries=3 CMD /bin/grpc_health_probe -addr=localhost:8086 -connect-timeout 250ms -rpc-timeout 100ms || exit 1
15+
16+
CMD ["/rpc"]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Code Payments
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
GO_OS := $(shell go env GOOS)
2+
GO_ARCH := $(shell go env GOARCH)
3+
4+
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
5+
6+
.PHONY: all
7+
all: generate test
8+
9+
.PHONY: test
10+
test:
11+
@go test -cover ./...
12+
13+
# todo: Currently broken with Geyser proto definitions due to optional fields
14+
.PHONY: generate
15+
generate:
16+
@rm -rf generated/*
17+
@docker run --rm -v $(PWD)/proto:/proto -v $(PWD)/generated:/generated/go code-protobuf-api-builder-go
18+
@mv $(PWD)/generated/github.com/code-payments/code-vm-indexer/generated/go/* $(PWD)/generated
19+
@rm -rf $(PWD)/generated/github.com
20+
21+
.PHONY: build-rpc
22+
build-rpc:
23+
@GOOS=$(GO_OS) GOARCH=$(GO_ARCH) CGO_ENABLED=0 go build -o build/$(GO_OS)-$(GO_ARCH)/rpc github.com/code-payments/code-vm-indexer/app/rpc
24+
25+
.PHONY: build-geyser
26+
build-geyser:
27+
@GOOS=$(GO_OS) GOARCH=$(GO_ARCH) CGO_ENABLED=0 go build -o build/$(GO_OS)-$(GO_ARCH)/geyser github.com/code-payments/code-vm-indexer/app/geyser
28+
29+
.PHONY: build-rpc-image
30+
build-rpc-image: GO_OS := linux
31+
build-rpc-image: GO_ARCH := amd64
32+
build-rpc-image: build-rpc
33+
@docker build -f Dockerfile.rpc --platform linux/amd64 -t code-vm-indexer-rpc:$(GIT_BRANCH) .
34+
35+
.PHONY: build-geyser-image
36+
build-geyser-image: GO_OS := linux
37+
build-geyser-image: GO_ARCH := amd64
38+
build-geyser-image: build-geyser
39+
@docker build -f Dockerfile.geyser --platform linux/amd64 -t code-vm-indexer-geyser:$(GIT_BRANCH) .
40+
41+
.PHONY: run-rpc
42+
run-rpc: GO_OS := linux
43+
run-rpc: GO_ARCH := amd64
44+
run-rpc: build-rpc build-rpc-image
45+
@docker run \
46+
--rm \
47+
-e APP_NAME=code-vm-indexer-rpc \
48+
-e DATA_STORAGE_TYPE=$(DATA_STORAGE_TYPE) \
49+
-e INSECURE_LISTEN_ADDRESS=:8086 \
50+
-e LOG_LEVEL=trace \
51+
-e POSTGRES_USER=$(POSTGRES_USER) \
52+
-e POSTGRES_PASSWORD=$(POSTGRES_PASSWORD) \
53+
-e POSTGRES_HOST=$(POSTGRES_HOST) \
54+
-e POSTGRES_PORT=$(POSTGRES_PORT) \
55+
-e POSTGRES_DB_NAME=$(POSTGRES_DB_NAME) \
56+
-e RAM_TABLE_NAME=$(RAM_TABLE_NAME) \
57+
-p 8086:8086 \
58+
code-vm-indexer-rpc:$(GIT_BRANCH)
59+
60+
.PHONY: run-geyser
61+
run-geyser: GO_OS := linux
62+
run-geyser: GO_ARCH := amd64
63+
run-geyser: build-geyser build-geyser-image
64+
@docker run \
65+
--rm \
66+
-e APP_NAME=code-vm-indexer-geyser \
67+
-e DATA_STORAGE_TYPE=$(DATA_STORAGE_TYPE) \
68+
-e GEYSER_WORKER_GRPC_PLUGIN_ENDPOINT=$(GEYSER_WORKER_GRPC_PLUGIN_ENDPOINT) \
69+
-e GEYSER_WORKER_PROGRAM_UPDATE_WORKER_COUNT=4 \
70+
-e GEYSER_WORKER_VM_ACCOUNT=$(GEYSER_WORKER_VM_ACCOUNT) \
71+
-e POSTGRES_USER=$(POSTGRES_USER) \
72+
-e POSTGRES_PASSWORD=$(POSTGRES_PASSWORD) \
73+
-e POSTGRES_HOST=$(POSTGRES_HOST) \
74+
-e POSTGRES_PORT=$(POSTGRES_PORT) \
75+
-e POSTGRES_DB_NAME=$(POSTGRES_DB_NAME) \
76+
-e RAM_TABLE_NAME=$(RAM_TABLE_NAME) \
77+
-e SOLANA_RPC_ENDPOINT=$(SOLANA_RPC_ENDPOINT) \
78+
-e LOG_LEVEL=trace \
79+
code-vm-indexer-geyser:$(GIT_BRANCH)

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Code VM Indexer
2+
3+
[![Release](https://img.shields.io/github/v/release/code-payments/code-vm-indexer.svg)](https://github.com/code-payments/code-vm-indexer/releases/latest)
4+
[![PkgGoDev](https://pkg.go.dev/badge/github.com/code-payments/code-vm-indexer)](https://pkg.go.dev/github.com/code-payments/code-vm-indexer)
5+
[![Tests](https://github.com/code-payments/code-vm-indexer/actions/workflows/test.yml/badge.svg)](https://github.com/code-payments/code-vm-indexer/actions/workflows/test.yml)
6+
[![GitHub License](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/code-payments/code-vm-indexer/blob/main/LICENSE)
7+
8+
Indexer Service for the [Code VM](https://github.com/code-payments/code-vm). It has two main components:
9+
1. Geyser worker that maintains up-to-date state against instances of the Code VM.
10+
2. RPC service to fetch virtual account state, as well as relevant storage metadata.
11+
12+
The Indexer Service is designed to be run independently, enabling anyone to have full self-custodial access to their funds using the next iteration of the Timelock Explorer.
13+
14+
**Important Note**: Only memory storage is currently supported. Compressed storage support will be added at a later date.
15+
16+
## What is Code?
17+
18+
[Code](https://getcode.com) is a mobile wallet app leveraging self-custodial blockchain technology to provide an instant, global, and private payments experience.
19+
20+
## Quick Start
21+
22+
1. Install Go. See the [official documentation](https://go.dev/doc/install).
23+
24+
2. Download the source code.
25+
26+
```bash
27+
git clone [email protected]:code-payments/code-vm-indexer.git
28+
```
29+
30+
3. Run the test suite:
31+
32+
```bash
33+
make test
34+
```
35+
36+
4. Run the RPC service locally:
37+
38+
```bash
39+
DATA_STORAGE_TYPE=memory make run-rpc
40+
```
41+
42+
5. Run the Geyser worker locally:
43+
44+
```bash
45+
DATA_STORAGE_TYPE=memory GEYSER_WORKER_GRPC_PLUGIN_ENDPOINT=localhost:10000 GEYSER_WORKER_VM_ACCOUNT=<VM_PUBLIC_KEY> SOLANA_RPC_ENDPOINT=http://localhost:8899 make run-geyser
46+
```
47+
48+
## Getting Help
49+
50+
If you have any questions or need help, please reach out to us on [Discord](https://discord.gg/T8Tpj8DBFp) or [Twitter](https://twitter.com/getcode).
51+
52+
## Security and Issue Disclosures
53+
54+
In the interest of protecting the security of our users and their funds, we ask that if you discover any security vulnerabilities please report them using this [Report a Vulnerability](https://github.com/code-wallet/code-program-library/security/advisories/new) link.

app/data.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/code-payments/code-server/pkg/config/env"
8+
pg "github.com/code-payments/code-server/pkg/database/postgres"
9+
"github.com/pkg/errors"
10+
11+
"github.com/code-payments/code-vm-indexer/data/ram"
12+
memory_ram_store "github.com/code-payments/code-vm-indexer/data/ram/memory"
13+
postgres_ram_store "github.com/code-payments/code-vm-indexer/data/ram/postgres"
14+
)
15+
16+
type dataStorageType string
17+
18+
const (
19+
dataStorateTypeMemory dataStorageType = "memory"
20+
dataStorateTypePostgres dataStorageType = "postgres"
21+
)
22+
23+
const (
24+
dataStorageTypeConfigEnvName = "DATA_STORAGE_TYPE"
25+
defaultDataStorageType = dataStorateTypeMemory
26+
27+
postgresUserConfigEnvName = "POSTGRES_USER"
28+
postgresPasswordConfigEnvName = "POSTGRES_PASSWORD"
29+
postgresHostConfigEnvName = "POSTGRES_HOST"
30+
postgresPortConfigEnvName = "POSTGRES_PORT"
31+
postgresDbConfigEnvName = "POSTGRES_DB_NAME"
32+
33+
ramTableNameEnvName = "RAM_TABLE_NAME"
34+
)
35+
36+
type DataProvider struct {
37+
Ram ram.Store
38+
}
39+
40+
// NewDataProvider dynamically selects storage implementations for use within
41+
// a Code VM indexer application using environment configs.
42+
func NewDataProvider() (*DataProvider, error) {
43+
ctx := context.TODO()
44+
45+
dataStoreTypeConfig := env.NewStringConfig(dataStorageTypeConfigEnvName, string(defaultDataStorageType))
46+
selectedDataStorageType := dataStorageType(dataStoreTypeConfig.Get(ctx))
47+
48+
dp := &DataProvider{}
49+
50+
switch selectedDataStorageType {
51+
case dataStorateTypeMemory:
52+
dp.Ram = memory_ram_store.New()
53+
case dataStorateTypePostgres:
54+
userConfig := env.NewStringConfig(postgresUserConfigEnvName, "")
55+
passwordConfig := env.NewStringConfig(postgresPasswordConfigEnvName, "")
56+
hostConfig := env.NewStringConfig(postgresHostConfigEnvName, "")
57+
portConfig := env.NewInt64Config(postgresPortConfigEnvName, 0)
58+
dbConfig := env.NewStringConfig(postgresDbConfigEnvName, "")
59+
ramTableNameConfig := env.NewStringConfig(ramTableNameEnvName, "")
60+
61+
db, err := pg.NewWithUsernameAndPassword(
62+
userConfig.Get(ctx),
63+
passwordConfig.Get(ctx),
64+
hostConfig.Get(ctx),
65+
fmt.Sprint(portConfig.Get(ctx)),
66+
dbConfig.Get(ctx),
67+
)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
dp.Ram = postgres_ram_store.New(db, ramTableNameConfig.Get(ctx))
73+
default:
74+
return nil, errors.Errorf("invalid data storage type: %s", selectedDataStorageType)
75+
}
76+
77+
return dp, nil
78+
}

app/geyser/main.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/code-payments/code-server/pkg/config/env"
8+
grpcapp "github.com/code-payments/code-server/pkg/grpc/app"
9+
"github.com/code-payments/code-server/pkg/solana"
10+
"github.com/newrelic/go-agent/v3/newrelic"
11+
"github.com/sirupsen/logrus"
12+
"google.golang.org/grpc"
13+
14+
indexerapp "github.com/code-payments/code-vm-indexer/app"
15+
"github.com/code-payments/code-vm-indexer/geyser"
16+
)
17+
18+
const (
19+
solanaRpcEndpointConfigEnvName = "SOLANA_RPC_ENDPOINT"
20+
defaultSolanaRpcEndpoint = "http://localhost:8899"
21+
)
22+
23+
type app struct {
24+
geyserWorker *geyser.Worker
25+
26+
shutdown sync.Once
27+
shutdownCh chan struct{}
28+
workerCancelFunc context.CancelFunc
29+
}
30+
31+
// Init implements grpcapp.App.Init
32+
//
33+
// todo: Cleanup gRPC app package (ie. no hardcoded metrics provider)
34+
func (a *app) Init(_ grpcapp.Config, metricsProvider *newrelic.Application) error {
35+
ctx, cancel := context.WithCancel(context.Background())
36+
a.workerCancelFunc = cancel
37+
a.shutdownCh = make(chan struct{})
38+
39+
solanaRpcEndpointConfig := env.NewStringConfig(solanaRpcEndpointConfigEnvName, defaultSolanaRpcEndpoint)
40+
41+
solanaClient := solana.New(solanaRpcEndpointConfig.Get(ctx))
42+
43+
dataProvider, err := indexerapp.NewDataProvider()
44+
if err != nil {
45+
return err
46+
}
47+
48+
a.geyserWorker = geyser.NewWorker(ctx, solanaClient, dataProvider.Ram, geyser.WithEnvConfigs())
49+
go a.geyserWorker.Run(ctx)
50+
51+
return nil
52+
}
53+
54+
// RegisterWithGRPC implements grpcapp.App.RegisterWithGRPC
55+
func (a *app) RegisterWithGRPC(server *grpc.Server) {
56+
}
57+
58+
// ShutdownChan implements grpcapp.App.ShutdownChan
59+
func (a *app) ShutdownChan() <-chan struct{} {
60+
return a.shutdownCh
61+
}
62+
63+
// Stop implements grpcapp.App.Stop
64+
func (a *app) Stop() {
65+
a.shutdown.Do(func() {
66+
close(a.shutdownCh)
67+
a.workerCancelFunc()
68+
})
69+
}
70+
71+
func main() {
72+
if err := grpcapp.Run(
73+
&app{},
74+
); err != nil {
75+
logrus.WithError(err).Fatal("error running indexer geyser worker")
76+
}
77+
}

0 commit comments

Comments
 (0)