Skip to content

Commit

Permalink
allowing BFGs to connect to another BFG to receive keystone notificat…
Browse files Browse the repository at this point in the history
…ions

allow a BFG to connect to another BFG.  the client BFG will receive L2 Keystone notifications via the public websocket endpoint, it will then query for new keystones when it gets a notification.

you must use BFG_BFG_URL + BFG_BTC_PRIVKEY to connect to another BFG.

BFG will request the latest 3 keystones upon each notification then save them in the DB and broadcast the keystones to its connected clients

add ON CONFLICT DO NOTHING to no-op with keystones we've already received

updated readme
  • Loading branch information
ClaytonNorthey92 committed Oct 21, 2024
1 parent 88ca612 commit b768be5
Show file tree
Hide file tree
Showing 8 changed files with 626 additions and 36 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ go run ./integrationtest
- A **PostgreSQL database**, bfgd expects the sql scripts in `./database/bfgd/scripts/` to be run to set up your schema.
- A **connection to an Electrs node** on the proper Bitcoin network (testnet or mainnet).

### Running your own bfgd and PoP mining with it

If you'd like to run your own `bfgd` and don't want to rely on Hemi Labs (or any third party) for _broadcasting transactions_, you may run `bfgd` and connect it to a _trusted_ `bfgd` run by a third party to _receive l2 keystones only_ (l2 keystones represent l2 state and are what are mined in PoP transactions). In this case, the third party `bfgd` will only send you l2 keystones, your `bfgd` can notify your local pop miner and this will broadcast them to your Electrs+bitcoind setup so you don't rely on Hemi Labs--or any third party--which may be congested.
If you'd like to do so, use the following environment variables when running bfgd:

* `BFG_BFG_URL`: the _trusted_ `bfgd`'s websocket url that you will connect to
* `BFG_BTC_PRIVKEY`: your btc private key. note that this can be an unfunded private key and you'll still receive l2 keystones to mine

then you may connect your local `popmd` to your aforementioned local `bfgd`

## ▶️ Running bssd

### 🏁 Prerequisites
Expand Down
12 changes: 12 additions & 0 deletions cmd/bfgd/bfgd.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ var (
Help: "list of headers used to obtain the client IP address (requires trusted proxies)",
Print: config.PrintAll,
},
"BFG_BFG_URL": config.Config{
Value: &cfg.BFGURL,
DefaultValue: "",
Help: "public websocket address of another BFG you'd like to receive L2Keystones from",
Print: config.PrintAll,
},
"BFG_BTC_PRIVKEY": config.Config{
Value: &cfg.BTCPrivateKey,
DefaultValue: "",
Help: "your btc private key, this is only needed when connecting to another BFG",
Print: config.PrintSecret,
},
}
)

Expand Down
87 changes: 85 additions & 2 deletions database/bfgd/database_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"log"
mathrand "math/rand/v2"
"net/url"
"os"
Expand Down Expand Up @@ -475,6 +476,79 @@ func TestL2KeystoneInsertMultipleSuccess(t *testing.T) {
}
}

func TestL2KeystoneInsertIgnoreDuplicates(t *testing.T) {
ctx, cancel := defaultTestContext()
defer cancel()

db, sdb, cleanup := createTestDB(ctx, t)
defer func() {
db.Close()
sdb.Close()
cleanup()
}()

l2Keystone := bfgd.L2Keystone{
Version: 1,
L1BlockNumber: 11,
L2BlockNumber: 22,
ParentEPHash: fillOutBytes("parentephash", 32),
PrevKeystoneEPHash: fillOutBytes("prevkeystoneephash", 32),
StateRoot: fillOutBytes("stateroot", 32),
EPHash: fillOutBytes("ephash", 32),
Hash: fillOutBytes("mockhash", 32),
}

otherL2Keystone := bfgd.L2Keystone{
Version: 1,
L1BlockNumber: 11,
L2BlockNumber: 22,
ParentEPHash: fillOutBytes("parentephash", 32),
PrevKeystoneEPHash: fillOutBytes("prevkeystoneephash", 32),
StateRoot: fillOutBytes("stateroot", 32),
EPHash: fillOutBytes("ephash", 32),
Hash: fillOutBytes("mockhashz", 32),
}

err := db.L2KeystonesInsert(ctx, []bfgd.L2Keystone{l2Keystone, otherL2Keystone})
if err != nil {
t.Fatal(err)
}

err = db.L2KeystonesInsert(ctx, []bfgd.L2Keystone{l2Keystone, otherL2Keystone})
if err != nil {
t.Fatal(err)
}

saved, err := db.L2KeystoneByAbrevHash(ctx, [32]byte(l2Keystone.Hash))
if err != nil {
t.Fatal(err)
}

diff := deep.Equal(saved, &l2Keystone)
if len(diff) != 0 {
t.Fatalf("unexpected diff %s", diff)
}

otherSaved, err := db.L2KeystoneByAbrevHash(ctx, [32]byte(otherL2Keystone.Hash))
if err != nil {
t.Fatal(err)
}

diff = deep.Equal(otherSaved, &otherL2Keystone)
if len(diff) != 0 {
t.Fatalf("unexpected diff %s", diff)
}

count, err := l2KeystonesCount(ctx, sdb)
if err != nil {
t.Fatal(err)
}

if count != 2 {
t.Fatalf("unexpected count %d", count)
}
}

func TestL2KeystoneInsertInvalidHashLength(t *testing.T) {
ctx, cancel := defaultTestContext()
defer cancel()
Expand Down Expand Up @@ -915,7 +989,7 @@ func TestL2KeystoneInsertMultipleAtomicFailure(t *testing.T) {
}
}

func TestL2KeystoneInsertMultipleDuplicateError(t *testing.T) {
func TestL2KeystoneInsertDuplicateOK(t *testing.T) {
ctx, cancel := defaultTestContext()
defer cancel()

Expand Down Expand Up @@ -949,9 +1023,18 @@ func TestL2KeystoneInsertMultipleDuplicateError(t *testing.T) {
}

err := db.L2KeystonesInsert(ctx, []bfgd.L2Keystone{l2Keystone, otherL2Keystone})
if err == nil || errors.Is(err, database.DuplicateError("")) == false {
if err != nil {
t.Fatalf("received unexpected error: %s", err)
}

l2Keystones, err := db.L2KeystonesMostRecentN(ctx, 5)
if err != nil {
t.Fatal(err)
}

if diff := deep.Equal([]bfgd.L2Keystone{l2Keystone}, l2Keystones); len(diff) != 0 {
log.Fatalf("unexpected diff %v", diff)
}
}

func TestPopBasisInsertNilMerklePath(t *testing.T) {
Expand Down
11 changes: 3 additions & 8 deletions database/bfgd/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,12 @@ func (p *pgdb) L2KeystonesInsert(ctx context.Context, l2ks []bfgd.L2Keystone) er
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT DO NOTHING
`

for _, v := range l2ks {
result, err := tx.ExecContext(ctx, qInsertL2Keystone, v.Hash,
_, err := tx.ExecContext(ctx, qInsertL2Keystone, v.Hash,
v.L1BlockNumber, v.L2BlockNumber, v.ParentEPHash,
v.PrevKeystoneEPHash, v.StateRoot, v.EPHash, v.Version)
if err != nil {
Expand All @@ -156,13 +158,6 @@ func (p *pgdb) L2KeystonesInsert(ctx context.Context, l2ks []bfgd.L2Keystone) er
}
return fmt.Errorf("insert l2 keystone: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("insert l2 keystone rows affected: %w", err)
}
if rows < 1 {
return fmt.Errorf("insert l2 keystone rows: %v", rows)
}
}

err = tx.Commit()
Expand Down
Loading

0 comments on commit b768be5

Please sign in to comment.