Skip to content

Commit

Permalink
api: Add /fundsf endpoint for funding siafund transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
lukechampine committed Aug 31, 2023
1 parent 3cab9de commit c2b4988
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 0 deletions.
8 changes: 8 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ type WalletFundRequest struct {
ChangeAddress types.Address `json:"changeAddress"`
}

// WalletFundSFRequest is the request type for /wallets/:name/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.
type WalletFundResponse struct {
Transaction types.Transaction `json:"transaction"`
Expand Down
21 changes: 21 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,27 @@ func (c *WalletClient) Release(sc []types.SiacoinOutputID, sf []types.SiafundOut
return
}

// 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{
Transaction: txn,
Amount: amount,
ChangeAddress: changeAddr,
}, &resp)
return
}

// 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{
Transaction: txn,
Amount: amount,
ChangeAddress: changeAddr,
ClaimAddress: claimAddr,
}, &resp)
return
}

// NewClient returns a client that communicates with a walletd server listening
// on the specified address.
func NewClient(addr, password string) *Client {
Expand Down
72 changes: 72 additions & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,77 @@ func (s *server) walletsFundHandler(jc jape.Context) {
})
}

func (s *server) walletsFundSFHandler(jc jape.Context) {
fundTxn := func(txn *types.Transaction, amount uint64, utxos []wallet.SiafundElement, changeAddr, claimAddr types.Address, pool []types.Transaction) ([]types.Hash256, error) {
s.mu.Lock()
defer s.mu.Unlock()
if amount == 0 {
return nil, nil
}
inPool := make(map[types.Hash256]bool)
for _, ptxn := range pool {
for _, in := range ptxn.SiafundInputs {
inPool[types.Hash256(in.ParentID)] = true
}
}
frand.Shuffle(len(utxos), reflect.Swapper(utxos))
var outputSum uint64
var fundingElements []wallet.SiafundElement
for _, sfe := range utxos {
if s.used[types.Hash256(sfe.ID)] || inPool[types.Hash256(sfe.ID)] {
continue
}
fundingElements = append(fundingElements, sfe)
outputSum += sfe.Value
if outputSum >= amount {
break
}
}
if outputSum < amount {
return nil, errors.New("insufficient balance")
} else if outputSum > amount {
txn.SiafundOutputs = append(txn.SiafundOutputs, types.SiafundOutput{
Value: outputSum - amount,
Address: changeAddr,
})
}

toSign := make([]types.Hash256, len(fundingElements))
for i, sfe := range fundingElements {
txn.SiafundInputs = append(txn.SiafundInputs, types.SiafundInput{
ParentID: types.SiafundOutputID(sfe.ID),
ClaimAddress: claimAddr,
// UnlockConditions left empty for client to fill in
})
toSign[i] = types.Hash256(sfe.ID)
s.used[types.Hash256(sfe.ID)] = true
}

return toSign, nil
}

var name string
var wfr WalletFundSFRequest
if jc.DecodeParam("name", &name) != nil || jc.Decode(&wfr) != nil {
return
}
_, utxos, err := s.wm.UnspentOutputs(name)
if jc.Check("couldn't get utxos to fund transaction", err) != nil {
return
}

txn := wfr.Transaction
toSign, err := fundTxn(&txn, wfr.Amount, utxos, wfr.ChangeAddress, wfr.ClaimAddress, s.cm.PoolTransactions())
if jc.Check("couldn't fund transaction", err) != nil {
return
}
jc.Encode(WalletFundResponse{
Transaction: txn,
ToSign: toSign,
DependsOn: s.cm.UnconfirmedParents(txn),
})
}

// NewServer returns an HTTP handler that serves the walletd API.
func NewServer(cm ChainManager, s Syncer, wm WalletManager) http.Handler {
srv := server{
Expand Down Expand Up @@ -416,5 +487,6 @@ func NewServer(cm ChainManager, s Syncer, wm WalletManager) http.Handler {
"POST /wallets/:name/reserve": srv.walletsReserveHandler,
"POST /wallets/:name/release": srv.walletsReleaseHandler,
"POST /wallets/:name/fund": srv.walletsFundHandler,
"POST /wallets/:name/fundsf": srv.walletsFundSFHandler,
})
}

0 comments on commit c2b4988

Please sign in to comment.