Skip to content

Commit

Permalink
Merge pull request #54 from SiaFoundation/nate/api-restructure
Browse files Browse the repository at this point in the history
API Restructure
  • Loading branch information
lukechampine authored Feb 24, 2024
2 parents f1f2295 + 2f1a478 commit e284fbc
Show file tree
Hide file tree
Showing 18 changed files with 718 additions and 639 deletions.
26 changes: 14 additions & 12 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"encoding/json"
"time"

"go.sia.tech/core/types"
Expand Down Expand Up @@ -31,44 +32,45 @@ type TxpoolTransactionsResponse struct {
V2Transactions []types.V2Transaction `json:"v2transactions"`
}

// BalanceResponse is the response type for /wallets/:name/balance.
// BalanceResponse is the response type for /wallets/:id/balance.
type BalanceResponse wallet.Balance

// WalletOutputsResponse is the response type for /wallets/:name/outputs.
type WalletOutputsResponse struct {
SiacoinOutputs []types.SiacoinElement `json:"siacoinOutputs"`
SiafundOutputs []types.SiafundElement `json:"siafundOutputs"`
}

// WalletReserveRequest is the request type for /wallets/:name/reserve.
// WalletReserveRequest is the request type for /wallets/:id/reserve.
type WalletReserveRequest struct {
SiacoinOutputs []types.SiacoinOutputID `json:"siacoinOutputs"`
SiafundOutputs []types.SiafundOutputID `json:"siafundOutputs"`
Duration time.Duration `json:"duration"`
}

// WalletReleaseRequest is the request type for /wallets/:name/release.
// A WalletUpdateRequest is a request to update a wallet
type WalletUpdateRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Metadata json.RawMessage `json:"metadata"`
}

// WalletReleaseRequest is the request type for /wallets/:id/release.
type WalletReleaseRequest struct {
SiacoinOutputs []types.SiacoinOutputID `json:"siacoinOutputs"`
SiafundOutputs []types.SiafundOutputID `json:"siafundOutputs"`
}

// WalletFundRequest is the request type for /wallets/:name/fund.
// WalletFundRequest is the request type for /wallets/:id/fund.
type WalletFundRequest struct {
Transaction types.Transaction `json:"transaction"`
Amount types.Currency `json:"amount"`
ChangeAddress types.Address `json:"changeAddress"`
}

// WalletFundSFRequest is the request type for /wallets/:name/fundsf.
// WalletFundSFRequest is the request type for /wallets/:id/fundsf.
type WalletFundSFRequest struct {
Transaction types.Transaction `json:"transaction"`
Amount uint64 `json:"amount"`
ChangeAddress types.Address `json:"changeAddress"`
ClaimAddress types.Address `json:"claimAddress"`
}

// WalletFundResponse is the response type for /wallets/:name/fund.
// WalletFundResponse is the response type for /wallets/:id/fund.
type WalletFundResponse struct {
Transaction types.Transaction `json:"transaction"`
ToSign []types.Hash256 `json:"toSign"`
Expand Down
68 changes: 38 additions & 30 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,11 @@ func TestWallet(t *testing.T) {
sav := wallet.NewSeedAddressVault(wallet.NewSeed(), 0, 20)
c, shutdown := runServer(cm, nil, wm)
defer shutdown()
if err := c.AddWallet("primary", nil); err != nil {
w, err := c.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
}
wc := c.Wallet("primary")
wc := c.Wallet(w.ID)
if err := c.Resubscribe(0); err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -112,17 +113,19 @@ func TestWallet(t *testing.T) {
}

// create and add an address
addr, info := sav.NewAddress("primary")
if err := wc.AddAddress(addr, info); err != nil {
addr := sav.NewAddress("primary")
if err := wc.AddAddress(addr); err != nil {
t.Fatal(err)
}

// should have an address now
addresses, err = wc.Addresses()
if err != nil {
t.Fatal(err)
} else if _, ok := addresses[addr]; !ok || len(addresses) != 1 {
t.Fatal("bad address list", addresses)
} else if len(addresses) != 1 {
t.Fatal("address list should have one address")
} else if addresses[0].Address != addr.Address {
t.Fatalf("address should be %v, got %v", addr, addresses[0])
}

// send gift to wallet
Expand All @@ -133,8 +136,8 @@ func TestWallet(t *testing.T) {
UnlockConditions: types.StandardUnlockConditions(giftPrivateKey.PublicKey()),
}},
SiacoinOutputs: []types.SiacoinOutput{
{Address: addr, Value: types.Siacoins(1).Div64(2)},
{Address: addr, Value: types.Siacoins(1).Div64(2)},
{Address: addr.Address, Value: types.Siacoins(1).Div64(2)},
{Address: addr.Address, Value: types.Siacoins(1).Div64(2)},
},
Signatures: []types.TransactionSignature{{
ParentID: types.Hash256(giftSCOID),
Expand Down Expand Up @@ -176,7 +179,7 @@ func TestWallet(t *testing.T) {
t.Error("transaction should appear in history")
}

outputs, _, err := wc.Outputs()
outputs, err := wc.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
} else if len(outputs) != 2 {
Expand All @@ -188,7 +191,7 @@ func TestWallet(t *testing.T) {
b = types.Block{
ParentID: cs.Index.ID,
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{Address: addr, Value: cs.BlockReward()}},
MinerPayouts: []types.SiacoinOutput{{Address: addr.Address, Value: cs.BlockReward()}},
}
for b.ID().CmpWork(cs.ChildTarget) < 0 {
b.Nonce += cs.NonceFactor()
Expand Down Expand Up @@ -265,18 +268,20 @@ func TestV2(t *testing.T) {
}
c, shutdown := runServer(cm, nil, wm)
defer shutdown()
if err := c.AddWallet("primary", nil); err != nil {
primaryWallet, err := c.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
}
primary := c.Wallet("primary")
if err := primary.AddAddress(primaryAddress, nil); err != nil {
primary := c.Wallet(primaryWallet.ID)
if err := primary.AddAddress(wallet.Address{Address: primaryAddress}); err != nil {
t.Fatal(err)
}
if err := c.AddWallet("secondary", nil); err != nil {
secondaryWallet, err := c.AddWallet(api.WalletUpdateRequest{Name: "secondary"})
if err != nil {
t.Fatal(err)
}
secondary := c.Wallet("secondary")
if err := secondary.AddAddress(secondaryAddress, nil); err != nil {
secondary := c.Wallet(secondaryWallet.ID)
if err := secondary.AddAddress(wallet.Address{Address: secondaryAddress}); err != nil {
t.Fatal(err)
}
if err := c.Resubscribe(0); err != nil {
Expand Down Expand Up @@ -324,12 +329,12 @@ func TestV2(t *testing.T) {
key := primaryPrivateKey
dest := secondaryAddress
pbal, sbal := types.ZeroCurrency, types.ZeroCurrency
sces, _, err := primary.Outputs()
sces, err := primary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
if len(sces) == 0 {
sces, _, err = secondary.Outputs()
sces, err = secondary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -370,12 +375,12 @@ func TestV2(t *testing.T) {
key := primaryPrivateKey
dest := secondaryAddress
pbal, sbal := types.ZeroCurrency, types.ZeroCurrency
sces, _, err := primary.Outputs()
sces, err := primary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
if len(sces) == 0 {
sces, _, err = secondary.Outputs()
sces, err = secondary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -487,11 +492,12 @@ func TestP2P(t *testing.T) {
go s1.Run()
c1, shutdown := runServer(cm1, s1, wm1)
defer shutdown()
if err := c1.AddWallet("primary", nil); err != nil {
w1, err := c1.AddWallet(api.WalletUpdateRequest{Name: "primary"})
if err != nil {
t.Fatal(err)
}
primary := c1.Wallet("primary")
if err := primary.AddAddress(primaryAddress, nil); err != nil {
primary := c1.Wallet(w1.ID)
if err := primary.AddAddress(wallet.Address{Address: primaryAddress}); err != nil {
t.Fatal(err)
}
if err := c1.Resubscribe(0); err != nil {
Expand Down Expand Up @@ -526,11 +532,13 @@ func TestP2P(t *testing.T) {
go s2.Run()
c2, shutdown2 := runServer(cm2, s2, wm2)
defer shutdown2()
if err := c2.AddWallet("secondary", nil); err != nil {

w2, err := c2.AddWallet(api.WalletUpdateRequest{Name: "secondary"})
if err != nil {
t.Fatal(err)
}
secondary := c2.Wallet("secondary")
if err := secondary.AddAddress(secondaryAddress, nil); err != nil {
secondary := c2.Wallet(w2.ID)
if err := secondary.AddAddress(wallet.Address{Address: secondaryAddress}); err != nil {
t.Fatal(err)
}
if err := c2.Resubscribe(0); err != nil {
Expand Down Expand Up @@ -606,15 +614,15 @@ func TestP2P(t *testing.T) {
key := primaryPrivateKey
dest := secondaryAddress
pbal, sbal := types.ZeroCurrency, types.ZeroCurrency
sces, _, err := primary.Outputs()
sces, err := primary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
if len(sces) == 0 {
c = c2
key = secondaryPrivateKey
dest = primaryAddress
sces, _, err = secondary.Outputs()
sces, err = secondary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -660,15 +668,15 @@ func TestP2P(t *testing.T) {
key := primaryPrivateKey
dest := secondaryAddress
pbal, sbal := types.ZeroCurrency, types.ZeroCurrency
sces, _, err := primary.Outputs()
sces, err := primary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
if len(sces) == 0 {
c = c2
key = secondaryPrivateKey
dest = primaryAddress
sces, _, err = secondary.Outputs()
sces, err = secondary.SiacoinOutputs(0, 100)
if err != nil {
t.Fatal(err)
}
Expand Down
61 changes: 36 additions & 25 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,27 @@ func (c *Client) Wallets() (ws map[string]json.RawMessage, err error) {
}

// AddWallet adds a wallet to the set of tracked wallets.
func (c *Client) AddWallet(name string, info json.RawMessage) (err error) {
err = c.c.PUT(fmt.Sprintf("/wallets/%v", name), info)
func (c *Client) AddWallet(uw WalletUpdateRequest) (w wallet.Wallet, err error) {
err = c.c.POST("/wallets", uw, &w)
return
}

// UpdateWallet updates a wallet.
func (c *Client) UpdateWallet(id wallet.ID, uw WalletUpdateRequest) (w wallet.Wallet, err error) {
err = c.c.POST(fmt.Sprintf("/wallets/%v", id), uw, &w)
return
}

// RemoveWallet deletes a wallet. If the wallet is currently subscribed, it will
// be unsubscribed.
func (c *Client) RemoveWallet(name string) (err error) {
err = c.c.DELETE(fmt.Sprintf("/wallets/%v", name))
func (c *Client) RemoveWallet(id wallet.ID) (err error) {
err = c.c.DELETE(fmt.Sprintf("/wallets/%v", id))
return
}

// Wallet returns a client for interacting with the specified wallet.
func (c *Client) Wallet(name string) *WalletClient {
return &WalletClient{c: c.c, name: name}
func (c *Client) Wallet(id wallet.ID) *WalletClient {
return &WalletClient{c: c.c, id: id}
}

// Resubscribe subscribes the wallet to consensus updates, starting at the
Expand All @@ -115,57 +121,62 @@ func (c *Client) Resubscribe(height uint64) (err error) {
// A WalletClient provides methods for interacting with a particular wallet on a
// walletd API server.
type WalletClient struct {
c jape.Client
name string
c jape.Client
id wallet.ID
}

// AddAddress adds the specified address and associated metadata to the
// wallet.
func (c *WalletClient) AddAddress(addr types.Address, info json.RawMessage) (err error) {
err = c.c.PUT(fmt.Sprintf("/wallets/%v/addresses/%v", c.name, addr), info)
func (c *WalletClient) AddAddress(a wallet.Address) (err error) {
err = c.c.PUT(fmt.Sprintf("/wallets/%v/addresses", c.id), a)
return
}

// RemoveAddress removes the specified address from the wallet.
func (c *WalletClient) RemoveAddress(addr types.Address) (err error) {
err = c.c.DELETE(fmt.Sprintf("/wallets/%v/addresses/%v", c.name, addr))
err = c.c.DELETE(fmt.Sprintf("/wallets/%v/addresses/%v", c.id, addr))
return
}

// Addresses the addresses controlled by the wallet.
func (c *WalletClient) Addresses() (resp map[types.Address]json.RawMessage, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/addresses", c.name), &resp)
func (c *WalletClient) Addresses() (resp []wallet.Address, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/addresses", c.id), &resp)
return
}

// Balance returns the current wallet balance.
func (c *WalletClient) Balance() (resp BalanceResponse, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/balance", c.name), &resp)
err = c.c.GET(fmt.Sprintf("/wallets/%v/balance", c.id), &resp)
return
}

// Events returns all events relevant to the wallet.
func (c *WalletClient) Events(offset, limit int) (resp []wallet.Event, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/events?offset=%d&limit=%d", c.name, offset, limit), &resp)
err = c.c.GET(fmt.Sprintf("/wallets/%v/events?offset=%d&limit=%d", c.id, offset, limit), &resp)
return
}

// PoolTransactions returns all txpool transactions relevant to the wallet.
func (c *WalletClient) PoolTransactions() (resp []wallet.PoolTransaction, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/txpool", c.name), &resp)
err = c.c.GET(fmt.Sprintf("/wallets/%v/txpool", c.id), &resp)
return
}

// Outputs returns the set of unspent outputs controlled by the wallet.
func (c *WalletClient) Outputs() (sc []types.SiacoinElement, sf []types.SiafundElement, err error) {
var resp WalletOutputsResponse
err = c.c.GET(fmt.Sprintf("/wallets/%v/outputs", c.name), &resp)
return resp.SiacoinOutputs, resp.SiafundOutputs, err
// SiacoinOutputs returns the set of unspent outputs controlled by the wallet.
func (c *WalletClient) SiacoinOutputs(offset, limit int) (sc []types.SiacoinElement, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/outputs/siacoin?offset=%d&limit=%d", c.id, offset, limit), &sc)
return
}

// SiafundOutputs returns the set of unspent outputs controlled by the wallet.
func (c *WalletClient) SiafundOutputs(offset, limit int) (sf []types.SiafundElement, err error) {
err = c.c.GET(fmt.Sprintf("/wallets/%v/outputs/siafund?offset=%d&limit=%d", c.id, offset, limit), &sf)
return
}

// Reserve reserves a set outputs for use in a transaction.
func (c *WalletClient) Reserve(sc []types.SiacoinOutputID, sf []types.SiafundOutputID, duration time.Duration) (err error) {
err = c.c.POST(fmt.Sprintf("/wallets/%v/reserve", c.name), WalletReserveRequest{
err = c.c.POST(fmt.Sprintf("/wallets/%v/reserve", c.id), WalletReserveRequest{
SiacoinOutputs: sc,
SiafundOutputs: sf,
Duration: duration,
Expand All @@ -175,7 +186,7 @@ func (c *WalletClient) Reserve(sc []types.SiacoinOutputID, sf []types.SiafundOut

// Release releases a set of previously-reserved outputs.
func (c *WalletClient) Release(sc []types.SiacoinOutputID, sf []types.SiafundOutputID) (err error) {
err = c.c.POST(fmt.Sprintf("/wallets/%v/release", c.name), WalletReleaseRequest{
err = c.c.POST(fmt.Sprintf("/wallets/%v/release", c.id), WalletReleaseRequest{
SiacoinOutputs: sc,
SiafundOutputs: sf,
}, nil)
Expand All @@ -184,7 +195,7 @@ func (c *WalletClient) Release(sc []types.SiacoinOutputID, sf []types.SiafundOut

// Fund funds a siacoin transaction.
func (c *WalletClient) Fund(txn types.Transaction, amount types.Currency, changeAddr types.Address) (resp WalletFundResponse, err error) {
err = c.c.POST(fmt.Sprintf("/wallets/%v/fund", c.name), WalletFundRequest{
err = c.c.POST(fmt.Sprintf("/wallets/%v/fund", c.id), WalletFundRequest{
Transaction: txn,
Amount: amount,
ChangeAddress: changeAddr,
Expand All @@ -194,7 +205,7 @@ func (c *WalletClient) Fund(txn types.Transaction, amount types.Currency, change

// FundSF funds a siafund transaction.
func (c *WalletClient) FundSF(txn types.Transaction, amount uint64, changeAddr, claimAddr types.Address) (resp WalletFundResponse, err error) {
err = c.c.POST(fmt.Sprintf("/wallets/%v/fundsf", c.name), WalletFundSFRequest{
err = c.c.POST(fmt.Sprintf("/wallets/%v/fundsf", c.id), WalletFundSFRequest{
Transaction: txn,
Amount: amount,
ChangeAddress: changeAddr,
Expand Down
Loading

0 comments on commit e284fbc

Please sign in to comment.