Skip to content

Commit

Permalink
Merge pull request #5 from SiaFoundation/store-file-contracts
Browse files Browse the repository at this point in the history
Store file contracts
  • Loading branch information
n8maninger authored Mar 14, 2024
2 parents 2e670d2 + 9a2ac52 commit ea1a3f4
Show file tree
Hide file tree
Showing 10 changed files with 727 additions and 82 deletions.
5 changes: 3 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type AddressUTXOsResponse struct {

// AddressBalanceResponse is the response for /addresses/:address/balance.
type AddressBalanceResponse struct {
UnspentSiacoins types.Currency `json:"unspentSiacoins"`
UnspentSiafunds uint64 `json:"unspentSiafunds"`
UnspentSiacoins types.Currency `json:"unspentSiacoins"`
ImmatureSiacoins types.Currency `json:"immatureSiacoins"`
UnspentSiafunds uint64 `json:"unspentSiafunds"`
}
12 changes: 12 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,15 @@ func (c *Client) AddressBalance(address types.Address) (resp AddressBalanceRespo
err = c.c.GET(fmt.Sprintf("/explorer/addresses/%s/balance", address), &resp)
return
}

// Contract returns the file contract with the specified ID.
func (c *Client) Contract(id types.FileContractID) (resp explorer.FileContract, err error) {
err = c.c.GET(fmt.Sprintf("/explorer/contracts/%s", id), &resp)
return
}

// Contracts returns the transactions with the specified IDs.
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.FileContract, err error) {
err = c.c.POST("/explorer/contracts", ids, &resp)
return
}
58 changes: 49 additions & 9 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,21 @@ type (
Block(id types.BlockID) (explorer.Block, error)
BestTip(height uint64) (types.ChainIndex, error)
Transactions(ids []types.TransactionID) ([]explorer.Transaction, error)
Balance(address types.Address) (sc types.Currency, sf uint64, err error)
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
UnspentSiacoinOutputs(address types.Address, limit, offset uint64) ([]explorer.SiacoinOutput, error)
UnspentSiafundOutputs(address types.Address, limit, offset uint64) ([]explorer.SiafundOutput, error)
Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error)
}
)

const (
maxIDs = 5000
)

var (
errTooManyIDs = fmt.Errorf("too many IDs provided (provide less than %d)", maxIDs)
)

type server struct {
cm ChainManager
e Explorer
Expand Down Expand Up @@ -171,11 +180,6 @@ func (s *server) explorerTransactionsIDHandler(jc jape.Context) {
}

func (s *server) explorerTransactionsHandler(jc jape.Context) {
const (
maxIDs = 5000
)
errTooManyIDs := fmt.Errorf("too many IDs provided (provide less than %d)", maxIDs)

var ids []types.TransactionID
if jc.Decode(&ids) != nil {
return
Expand Down Expand Up @@ -224,17 +228,51 @@ func (s *server) explorerAddressessAddressBalanceHandler(jc jape.Context) {
return
}

sc, sf, err := s.e.Balance(address)
sc, immatureSC, sf, err := s.e.Balance(address)
if jc.Check("failed to get balance", err) != nil {
return
}

jc.Encode(AddressBalanceResponse{
UnspentSiacoins: sc,
UnspentSiafunds: sf,
UnspentSiacoins: sc,
ImmatureSiacoins: immatureSC,
UnspentSiafunds: sf,
})
}

func (s *server) explorerContractIDHandler(jc jape.Context) {
errNotFound := errors.New("no contract found")

var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
return
}
fcs, err := s.e.Contracts([]types.FileContractID{id})
if jc.Check("failed to get contract", err) != nil {
return
} else if len(fcs) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
return
}
jc.Encode(fcs[0])
}

func (s *server) explorerContractsHandler(jc jape.Context) {
var ids []types.FileContractID
if jc.Decode(&ids) != nil {
return
} else if len(ids) > maxIDs {
jc.Error(errTooManyIDs, http.StatusBadRequest)
return
}

fcs, err := s.e.Contracts(ids)
if jc.Check("failed to get contracts", err) != nil {
return
}
jc.Encode(fcs)
}

// NewServer returns an HTTP handler that serves the explored API.
func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
srv := server{
Expand All @@ -257,5 +295,7 @@ func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
"POST /explorer/transactions": srv.explorerTransactionsHandler,
"GET /explorer/addresses/:address/utxos": srv.explorerAddressessAddressUtxosHandler,
"GET /explorer/addresses/:address/balance": srv.explorerAddressessAddressBalanceHandler,
"GET /explorer/contracts/:id": srv.explorerContractIDHandler,
"POST /explorer/contracts": srv.explorerContractsHandler,
})
}
10 changes: 8 additions & 2 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ type Store interface {
Transactions(ids []types.TransactionID) ([]Transaction, error)
UnspentSiacoinOutputs(address types.Address, limit, offset uint64) ([]SiacoinOutput, error)
UnspentSiafundOutputs(address types.Address, limit, offset uint64) ([]SiafundOutput, error)
Balance(address types.Address) (sc types.Currency, sf uint64, err error)
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
Contracts(ids []types.FileContractID) (result []FileContract, err error)

MerkleProof(leafIndex uint64) ([]types.Hash256, error)
}
Expand Down Expand Up @@ -69,6 +70,11 @@ func (e *Explorer) UnspentSiafundOutputs(address types.Address, limit, offset ui
}

// Balance returns the balance of an address.
func (e *Explorer) Balance(address types.Address) (sc types.Currency, sf uint64, err error) {
func (e *Explorer) Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error) {
return e.s.Balance(address)
}

// Contracts returns the contracts with the specified IDs.
func (e *Explorer) Contracts(ids []types.FileContractID) (result []FileContract, err error) {
return e.s.Contracts(ids)
}
51 changes: 46 additions & 5 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const (
SourceMinerPayout
// SourceTransaction means the source of the output is a transaction.
SourceTransaction
// SourceValidProofOutput me ans the source of the output is a valid proof
// output.
SourceValidProofOutput
// SourceMissedProofOutput me ans the source of the output is a missed
// proof output.
SourceMissedProofOutput
)

// MarshalJSON implements json.Marshaler.
Expand All @@ -44,13 +50,48 @@ type SiacoinOutput struct {
// A SiafundOutput is a types.SiafundElement.
type SiafundOutput types.SiafundElement

// A FileContract is a types.FileContractElement that uses wrapped types
// internally.
type FileContract struct {
types.StateElement

Resolved bool `json:"resolved"`
Valid bool `json:"valid"`

Filesize uint64 `json:"filesize"`
FileMerkleRoot types.Hash256 `json:"fileMerkleRoot"`
WindowStart uint64 `json:"windowStart"`
WindowEnd uint64 `json:"windowEnd"`
Payout types.Currency `json:"payout"`
ValidProofOutputs []types.SiacoinOutput `json:"validProofOutputs"`
MissedProofOutputs []types.SiacoinOutput `json:"missedProofOutputs"`
UnlockHash types.Hash256 `json:"unlockHash"`
RevisionNumber uint64 `json:"revisionNumber"`
}

// A FileContractRevision is a types.FileContractRevision that uses wrapped
// types internally.
type FileContractRevision struct {
ParentID types.FileContractID `json:"parentID"`
UnlockConditions types.UnlockConditions `json:"unlockConditions"`
// NOTE: the Payout field of the contract is not "really" part of a
// revision. A revision cannot change the total payout, so the original siad
// code defines FileContractRevision as an entirely separate struct without
// a Payout field. Here, we instead reuse the FileContract type, which means
// we must treat its Payout field as invalid. To guard against developer
// error, we set it to a sentinel value when decoding it.
FileContract
}

// A Transaction is a transaction that uses the wrapped types above.
type Transaction struct {
SiacoinInputs []types.SiacoinInput `json:"siacoinInputs,omitempty"`
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundInputs []types.SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
ArbitraryData [][]byte `json:"arbitraryData,omitempty"`
SiacoinInputs []types.SiacoinInput `json:"siacoinInputs,omitempty"`
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundInputs []types.SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
FileContracts []FileContract `json:"fileContracts,omitempty"`
FileContractRevisions []FileContractRevision `json:"fileContractRevisions,omitempty"`
ArbitraryData [][]byte `json:"arbitraryData,omitempty"`
}

// A Block is a block containing wrapped transactions and siacoin
Expand Down
Loading

0 comments on commit ea1a3f4

Please sign in to comment.