diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..98ccea5 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +BITCOIN_HOST= +BITCOIN_PORT= +BITCOIN_USER= +BITCOIN_PASS= + +POSTGRES_FULL="postgresql://user:password@127.0.0.1:5432/1sats?sslmode=disable" + +JUNGLEBUS=https://junglebus.gorillapool.io + +## the subscription id, go https://junglebus.gorillapool.io/ to got one +ONESAT= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 699eef0..6dabcdb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ mempool/mempool ord/ord .env app.yaml -.gcloudignore \ No newline at end of file +.gcloudignore +*.exe \ No newline at end of file diff --git a/README.md b/README.md index cd8ebf3..dea9de5 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,57 @@ If a satoshi is subsequently packaged up in an output of more than one satoshi, ## Setup instructions depricated. Please reach out -### Environment Variables +## Environment Variables - POSTGRES_FULL=`` - JUNGLEBUS=https://junglebus.gorillapool.io - ARC=https://arc.gorillapool.io - REDIS=`:` - TAAL_TOKEN=`` +- copy `.env.example` as `.env` +- go https://junglebus.gorillapool.io to get a subscription id + + create a with such config: + ![](./images/screenshot_2024-01-10_at_8.01.33___am.png) + +## Start docker + + +``` +docker-compose up +``` + + ## Run DB migrations ``` + +# cd migrations go run . ``` +## Run Ord +``` +cd cmd/ord +go build +./ord -t [subscription id] -s 825885 +``` + + +## Run BSV20v2 +``` +cd cmd/bsv20v2 +go build +./bsv20v2 -t [subscription id] -s 825885 +``` + +## Run Http server +``` +cd cmd/server +go build +./server +``` +## Start APIs +https://github.com/sCrypt-Inc/1sat-server-js diff --git a/cmd/bsv20-analysis-v2/.gitignore b/cmd/bsv20-analysis-v2/.gitignore new file mode 100644 index 0000000..ea96d4b --- /dev/null +++ b/cmd/bsv20-analysis-v2/.gitignore @@ -0,0 +1 @@ +bsv20-analysis-v2 \ No newline at end of file diff --git a/cmd/bsv20-analysis-v2/bsv20-analysis-v2.go b/cmd/bsv20-analysis-v2/bsv20-analysis-v2.go new file mode 100644 index 0000000..70512fa --- /dev/null +++ b/cmd/bsv20-analysis-v2/bsv20-analysis-v2.go @@ -0,0 +1,122 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/joho/godotenv" + "github.com/redis/go-redis/v9" + "github.com/shruggr/1sat-indexer/indexer" + "github.com/shruggr/1sat-indexer/lib" + "github.com/shruggr/1sat-indexer/ordinals" +) + +var POSTGRES string +var db *pgxpool.Pool +var rdb *redis.Client +var INDEXER string +var TOPIC string +var FROM_BLOCK uint +var VERBOSE int +var CONCURRENCY int = 64 + +func init() { + wd, _ := os.Getwd() + log.Println("CWD:", wd) + godotenv.Load(fmt.Sprintf(`%s/../../.env`, wd)) + + flag.StringVar(&INDEXER, "id", "", "Indexer name") + flag.StringVar(&TOPIC, "t", "", "Junglebus SubscriptionID") + flag.UintVar(&FROM_BLOCK, "s", uint(lib.TRIGGER), "Start from block") + flag.IntVar(&CONCURRENCY, "c", 64, "Concurrency Limit") + flag.IntVar(&VERBOSE, "v", 0, "Verbose") + flag.Parse() + + if POSTGRES == "" { + POSTGRES = os.Getenv("POSTGRES_FULL") + } + var err error + log.Println("POSTGRES:", POSTGRES) + db, err = pgxpool.New(context.Background(), POSTGRES) + if err != nil { + log.Panic(err) + } + + rdb = redis.NewClient(&redis.Options{ + Addr: os.Getenv("REDIS"), + Password: "", // no password set + DB: 0, // use default DB + }) + + err = indexer.Initialize(db, rdb) + if err != nil { + log.Panic(err) + } + +} + +func main() { + + err := indexer.Exec( + true, + false, + func(ctx *lib.IndexContext) error { + ordinals.ParseInscriptions(ctx) + ids := map[string]uint64{} + for _, txo := range ctx.Txos { + if bsv20, ok := txo.Data["bsv20"].(*ordinals.Bsv20); ok { + if bsv20.Id == nil { + continue + } + id := bsv20.Id.String() + if txouts, ok := ids[id]; !ok { + ids[id] = 1 + } else { + ids[id] = txouts + 1 + } + } + } + for idstr, txouts := range ids { + id, err := lib.NewOutpointFromString(idstr) + if err != nil { + log.Printf("Err: %s %x %d\n", idstr, ctx.Txid, txouts) + return err + } else { + _, err = db.Exec(context.Background(), ` + INSERT INTO bsv20v2_txns(txid, id, height, idx, txouts) + VALUES($1, $2, $3, $4, $5) + ON CONFLICT(txid, id) DO NOTHING`, + ctx.Txid, + id, + ctx.Height, + ctx.Idx, + txouts, + ) + } + if err != nil { + log.Printf("Err: %s %x %d\n", idstr, ctx.Txid, txouts) + return err + } + } + return nil + }, + func(height uint32) error { + + return nil + }, + INDEXER, + TOPIC, + FROM_BLOCK, + CONCURRENCY, + false, + false, + VERBOSE, + ) + if err != nil { + log.Panicln(err) + } +} diff --git a/cmd/bsv20-analysis/bsv20-analysis.go b/cmd/bsv20-analysis/bsv20-analysis.go index 680f3e4..51f42cf 100644 --- a/cmd/bsv20-analysis/bsv20-analysis.go +++ b/cmd/bsv20-analysis/bsv20-analysis.go @@ -61,6 +61,11 @@ func init() { if err != nil { log.Panic(err) } + + err = ordinals.Initialize(indexer.Db, indexer.Rdb) + if err != nil { + log.Panic(err) + } } func main() { @@ -89,6 +94,7 @@ func main() { ticks := map[string]uint64{} for _, txo := range ctx.Txos { if bsv20, ok := txo.Data["bsv20"].(*ordinals.Bsv20); ok { + bsv20.Save(txo) ticker := bsv20.Ticker if ticker == "" { if bsv20.Id == nil { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d0061e9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +# Use postgres/example user/password credentials +version: '3.1' + +services: + + db: + image: postgres + restart: always + ports: + - 5432:5432 + environment: + POSTGRES_USER: $DB_USERNAME + POSTGRES_PASSWORD: $DB_PASSWORD + POSTGRES_DB: $DB_NAME + + redis: + image: redis:latest + command: redis-server #--requirepass $REDIS_PASSWORD + ports: + - 6379:6379 + + adminer: + image: adminer + restart: always + ports: + - 8080:8080 \ No newline at end of file diff --git a/images/screenshot_2024-01-10_at_8.01.33___am.png b/images/screenshot_2024-01-10_at_8.01.33___am.png new file mode 100644 index 0000000..8999c98 Binary files /dev/null and b/images/screenshot_2024-01-10_at_8.01.33___am.png differ diff --git a/lib/data.go b/lib/data.go index 8cbfc1b..ed27797 100644 --- a/lib/data.go +++ b/lib/data.go @@ -2,6 +2,7 @@ package lib import ( "context" + "encoding/hex" "fmt" "io" "log" @@ -60,6 +61,7 @@ func LoadRawtx(txid string) (rawtx []byte, err error) { rawtx, _ = Rdb.Get(context.Background(), txid).Bytes() if len(rawtx) > 0 { + // log.Println("Requesting tx from redis", txid) return rawtx, nil } @@ -78,6 +80,11 @@ func LoadRawtx(txid string) (rawtx []byte, err error) { } } + if len(rawtx) == 0 { + log.Println("Requesting tx from WOC", txid) + rawtx, err = LoadTxFromWOC(txid, os.Getenv("NETWORK")) + } + if len(rawtx) == 0 { err = fmt.Errorf("missing-txn %s", txid) return @@ -119,3 +126,36 @@ func GetSpend(outpoint *Outpoint) (spend []byte, err error) { } return io.ReadAll(resp.Body) } + +func LoadTxFromWOC(txid string, network string) (rawtx []byte, err error) { + + if len(network) == 0 { + network = "main" + } + + url := fmt.Sprintf("https://api.whatsonchain.com/v1/bsv/%s/tx/%s/hex", network, txid) + // log.Println("Requesting txo", url) + resp, err := http.Get(url) + if err != nil { + return + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + err = fmt.Errorf("missing-txn %s", txid) + return + } + + b, err := io.ReadAll(resp.Body) + + if err != nil { + return + } + + rawtx, err = hex.DecodeString(string(b)) + if err != nil { + panic(err) + } + + return +} diff --git a/lib/data_test.go b/lib/data_test.go new file mode 100644 index 0000000..bdc5308 --- /dev/null +++ b/lib/data_test.go @@ -0,0 +1,21 @@ +package lib + +import ( + "encoding/hex" + "testing" +) + +// TestHelloName calls greetings.Hello with a name, checking +// for a valid return value. +func TestLoadTxFromWOC(t *testing.T) { + network := "main" + txid := "a9b3fa37e171ffe0e79b507a8a4486f691918a876321e21a85d7a26c8a02fc15" + rawtx, err := LoadTxFromWOC(txid, network) + if err != nil { + t.Fatalf(`LoadTxFromWOC err: %v`, err) + } + + if hex.EncodeToString(rawtx) != "303130303030303030313030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303066666666666666663137303331626138306332663731363436633665366232663235376330393366333732646434613832383030303030306666666666666666303133373031343132353030303030303030313937366139313436626163643735376162663039393731356331323531653761333338386561636236343738316361383861633030303030303030" { + t.Fatalf(`The tx not as expected`) + } +} diff --git a/lib/outpoint.go b/lib/outpoint.go index cd557d3..d13859d 100644 --- a/lib/outpoint.go +++ b/lib/outpoint.go @@ -56,6 +56,10 @@ func (o *Outpoint) Txid() []byte { return (*o)[:32] } +func (o *Outpoint) TxidStr() string { + return hex.EncodeToString((*o).Txid()) +} + func (o *Outpoint) Vout() uint32 { return binary.BigEndian.Uint32((*o)[32:]) }