Skip to content

Commit

Permalink
Merge pull request #168 from SiaFoundation/nate/add-debug-endpoints
Browse files Browse the repository at this point in the history
Add debug flag and endpoints
  • Loading branch information
n8maninger authored Aug 16, 2024
2 parents 570d7ef + ec260a1 commit cd52e7b
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 35 deletions.
6 changes: 6 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,9 @@ type ConsensusUpdatesResponse struct {
Applied []ApplyUpdate `json:"applied"`
Reverted []RevertUpdate `json:"reverted"`
}

// DebugMineRequest is the request type for /debug/mine.
type DebugMineRequest struct {
Blocks int `json:"blocks"`
Address types.Address `json:"address"`
}
108 changes: 85 additions & 23 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,24 @@ func testNetwork() (*consensus.Network, types.Block) {
return n, genesisBlock
}

func runServer(cm api.ChainManager, s api.Syncer, wm api.WalletManager) (*api.Client, func()) {
func runServer(t *testing.T, cm api.ChainManager, s api.Syncer, wm api.WalletManager) *api.Client {
t.Helper()

l, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}
go func() {
srv := api.NewServer(cm, s, wm)
http.Serve(l, jape.BasicAuth("password")(srv))
}()
c := api.NewClient("http://"+l.Addr().String(), "password")
return c, func() { l.Close() }
t.Fatal("failed to listen:", err)
}
t.Cleanup(func() { l.Close() })

server := &http.Server{
Handler: jape.BasicAuth("password")(api.NewServer(cm, s, wm, api.WithDebug(), api.WithLogger(zaptest.NewLogger(t)))),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
}
t.Cleanup(func() { server.Close() })

go server.Serve(l)
return api.NewClient("http://"+l.Addr().String(), "password")
}

func waitForBlock(tb testing.TB, cm *chain.Manager, ws wallet.Store) {
Expand Down Expand Up @@ -95,8 +102,7 @@ func TestWalletAdd(t *testing.T) {
}
defer wm.Close()

c, shutdown := runServer(cm, nil, wm)
defer shutdown()
c := runServer(t, cm, nil, wm)

checkWalletResponse := func(wr api.WalletUpdateRequest, w wallet.Wallet, isUpdate bool) error {
// check wallet
Expand Down Expand Up @@ -287,8 +293,7 @@ func TestWallet(t *testing.T) {
sav := wallet.NewSeedAddressVault(wallet.NewSeed(), 0, 20)

// run server
c, shutdown := runServer(cm, s, wm)
defer shutdown()
c := runServer(t, cm, s, wm)
w, err := c.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -506,8 +511,7 @@ func TestAddresses(t *testing.T) {
defer wm.Close()

sav := wallet.NewSeedAddressVault(wallet.NewSeed(), 0, 20)
c, shutdown := runServer(cm, nil, wm)
defer shutdown()
c := runServer(t, cm, nil, wm)
w, err := c.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -702,8 +706,7 @@ func TestV2(t *testing.T) {
}
defer wm.Close()

c, shutdown := runServer(cm, nil, wm)
defer shutdown()
c := runServer(t, cm, nil, wm)
primaryWallet, err := c.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -942,8 +945,7 @@ func TestP2P(t *testing.T) {
})
go s1.Run(context.Background())
defer s1.Close()
c1, shutdown := runServer(cm1, s1, wm1)
defer shutdown()
c1 := runServer(t, cm1, s1, wm1)
w1, err := c1.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -986,8 +988,7 @@ func TestP2P(t *testing.T) {
}, syncer.WithLogger(zaptest.NewLogger(t)))
go s2.Run(context.Background())
defer s2.Close()
c2, shutdown2 := runServer(cm2, s2, wm2)
defer shutdown2()
c2 := runServer(t, cm2, s2, wm2)

w2, err := c2.AddWallet(api.WalletUpdateRequest{Name: "secondary"})
if err != nil {
Expand Down Expand Up @@ -1248,8 +1249,7 @@ func TestConsensusUpdates(t *testing.T) {
}
defer wm.Close()

c, shutdown := runServer(cm, nil, wm)
defer shutdown()
c := runServer(t, cm, nil, wm)

for i := 0; i < 10; i++ {
b, ok := coreutils.MineBlock(cm, types.VoidAddress, time.Second)
Expand Down Expand Up @@ -1283,3 +1283,65 @@ func TestConsensusUpdates(t *testing.T) {
}
}
}

func TestDebugMine(t *testing.T) {
log := zaptest.NewLogger(t)
n, genesisBlock := testNetwork()

// create wallets
dbstore, tipState, err := chain.NewDBStore(chain.NewMemDB(), n, genesisBlock)
if err != nil {
t.Fatal(err)
}
cm := chain.NewManager(dbstore, tipState)

l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
defer l.Close()

ws, err := sqlite.OpenDatabase(filepath.Join(t.TempDir(), "wallets.db"), log.Named("sqlite3"))
if err != nil {
t.Fatal(err)
}
defer ws.Close()

ps, err := sqlite.NewPeerStore(ws)
if err != nil {
t.Fatal(err)
}

s := syncer.New(l, cm, ps, gateway.Header{
GenesisID: genesisBlock.ID(),
UniqueID: gateway.GenerateUniqueID(),
NetAddress: l.Addr().String(),
})
defer s.Close()
go s.Run(context.Background())

wm, err := wallet.NewManager(cm, ws, wallet.WithLogger(log.Named("wallet")))
if err != nil {
t.Fatal(err)
}
defer wm.Close()

c := runServer(t, cm, s, wm)

jc := jape.Client{
BaseURL: c.BaseURL(),
Password: "password",
}

err = jc.POST("/debug/mine", api.DebugMineRequest{
Blocks: 5,
Address: types.VoidAddress,
}, nil)
if err != nil {
t.Fatal(err)
}

if cm.Tip().Height != 5 {
t.Fatalf("expected tip height to be 5, got %v", cm.Tip().Height)
}
}
5 changes: 5 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func (c *Client) getNetwork() (*consensus.Network, error) {
return c.n, nil
}

// BaseURL returns the URL of the walletd server.
func (c *Client) BaseURL() string {
return c.c.BaseURL
}

// State returns information about the current state of the walletd daemon.
func (c *Client) State() (resp StateResponse, err error) {
err = c.c.GET("/state", &resp)
Expand Down
81 changes: 81 additions & 0 deletions api/mine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package api

import (
"context"
"encoding/binary"
"errors"

"go.sia.tech/core/types"
)

// mineBlock constructs a block from the provided address and the transactions
// in the txpool, and attempts to find a nonce for it that meets the PoW target.
func mineBlock(ctx context.Context, cm ChainManager, addr types.Address) (types.Block, error) {
cs := cm.TipState()
txns := cm.PoolTransactions()
v2Txns := cm.V2PoolTransactions()

b := types.Block{
ParentID: cs.Index.ID,
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{
Value: cs.BlockReward(),
Address: addr,
}},
}

if cs.Index.Height >= cs.Network.HardforkV2.AllowHeight {
b.V2 = &types.V2BlockData{
Height: cs.Index.Height + 1,
}
}

var weight uint64
for _, txn := range txns {
if weight += cs.TransactionWeight(txn); weight > cs.MaxBlockWeight() {
break
}
b.Transactions = append(b.Transactions, txn)
b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.TotalFees())
}
for _, txn := range v2Txns {
if weight += cs.V2TransactionWeight(txn); weight > cs.MaxBlockWeight() {
break
}
b.V2.Transactions = append(b.V2.Transactions, txn)
b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.MinerFee)
}
if b.V2 != nil {
b.V2.Commitment = cs.Commitment(cs.TransactionsCommitment(b.Transactions, b.V2Transactions()), addr)
}

b.Nonce = 0
buf := make([]byte, 32+8+8+32)
binary.LittleEndian.PutUint64(buf[32:], b.Nonce)
binary.LittleEndian.PutUint64(buf[40:], uint64(b.Timestamp.Unix()))
if b.V2 != nil {
copy(buf[:32], "sia/id/block|")
copy(buf[48:], b.V2.Commitment[:])
} else {
root := b.MerkleRoot()
copy(buf[:32], b.ParentID[:])
copy(buf[48:], root[:])
}
factor := cs.NonceFactor()
for types.BlockID(types.HashBytes(buf)).CmpWork(cs.ChildTarget) < 0 {
select {
case <-ctx.Done():
return types.Block{}, ctx.Err()
default:
}

// tip changed, abort mining
if cm.Tip() != cs.Index {
return types.Block{}, errors.New("tip changed")
}

b.Nonce += factor
binary.LittleEndian.PutUint64(buf[32:], b.Nonce)
}
return b, nil
}
Loading

0 comments on commit cd52e7b

Please sign in to comment.