Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add past revisions #98

Merged
merged 3 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ func (c *Client) ContractsKey(key types.PublicKey) (resp []explorer.FileContract
return
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (c *Client) ContractRevisions(id types.FileContractID) (resp []types.FileContractElement, err error) {
err = c.c.GET(fmt.Sprintf("/contracts/%s/revisions", id), &resp)
return
}

// Host returns information about the host with a given ed25519 key.
func (c *Client) Host(key types.PublicKey) (resp explorer.Host, err error) {
err = c.c.GET(fmt.Sprintf("/pubkey/%s/host", key), &resp)
Expand Down
74 changes: 49 additions & 25 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type (
AddressEvents(address types.Address, offset, limit uint64) (events []explorer.Event, err error)
Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error)
ContractRevisions(id types.FileContractID) (result []types.FileContractElement, err error)
Search(id types.Hash256) (explorer.SearchType, error)

Hosts(pks []types.PublicKey) ([]explorer.Host, error)
Expand All @@ -73,7 +74,26 @@ const (
)

var (
errTooManyIDs = fmt.Errorf("too many IDs provided (provide less than %d)", maxIDs)
// ErrTransactionNotFound is returned by /transactions/:id when we are
// unable to find the transaction with that `id`.
ErrTransactionNotFound = errors.New("no transaction found")
// ErrSiacoinOutputNotFound is returned by /outputs/siacoin/:id when we
// are unable to find the siacoin output with that `id`.
ErrSiacoinOutputNotFound = errors.New("no siacoin output found")
// ErrSiafundOutputNotFound is returned by /outputs/siafund/:id when we
// are unable to find the siafund output with that `id`.
ErrSiafundOutputNotFound = errors.New("no siafund output found")
// ErrHostNotFound is returned by /pubkey/:key/host when we are unable to
// find the host with the pubkey `key`.
ErrHostNotFound = errors.New("no host found")

// ErrNoSearchResults is returned by /search/:id when we do not find any
// elements with that ID.
ErrNoSearchResults = errors.New("no search results found")

// ErrTooManyIDs is returned by the batch transaction and contract
// endpoints when more than maxIDs IDs are specified.
ErrTooManyIDs = fmt.Errorf("too many IDs provided (provide less than %d)", maxIDs)
)

type server struct {
Expand Down Expand Up @@ -247,8 +267,6 @@ func (s *server) blocksIDHandler(jc jape.Context) {
}

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

var id types.TransactionID
if jc.DecodeParam("id", &id) != nil {
return
Expand All @@ -257,7 +275,7 @@ func (s *server) transactionsIDHandler(jc jape.Context) {
if jc.Check("failed to get transaction", err) != nil {
return
} else if len(txns) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(ErrTransactionNotFound, http.StatusNotFound)
return
}
jc.Encode(txns[0])
Expand Down Expand Up @@ -291,7 +309,7 @@ func (s *server) transactionsBatchHandler(jc jape.Context) {
if jc.Decode(&ids) != nil {
return
} else if len(ids) > maxIDs {
jc.Error(errTooManyIDs, http.StatusBadRequest)
jc.Error(ErrTooManyIDs, http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -379,8 +397,6 @@ func (s *server) addressessAddressEventsHandler(jc jape.Context) {
}

func (s *server) outputsSiacoinHandler(jc jape.Context) {
errNotFound := errors.New("no siacoin output found")

var id types.SiacoinOutputID
if jc.DecodeParam("id", &id) != nil {
return
Expand All @@ -390,16 +406,14 @@ func (s *server) outputsSiacoinHandler(jc jape.Context) {
if jc.Check("failed to get siacoin elements", err) != nil {
return
} else if len(outputs) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(ErrSiacoinOutputNotFound, http.StatusNotFound)
return
}

jc.Encode(outputs[0])
}

func (s *server) outputsSiafundHandler(jc jape.Context) {
errNotFound := errors.New("no siafund output found")

var id types.SiafundOutputID
if jc.DecodeParam("id", &id) != nil {
return
Expand All @@ -409,15 +423,13 @@ func (s *server) outputsSiafundHandler(jc jape.Context) {
if jc.Check("failed to get siafund elements", err) != nil {
return
} else if len(outputs) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(ErrSiafundOutputNotFound, http.StatusNotFound)
return
}

jc.Encode(outputs[0])
}
func (s *server) contractsIDHandler(jc jape.Context) {
errNotFound := errors.New("no contract found")

var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
return
Expand All @@ -426,18 +438,34 @@ func (s *server) contractsIDHandler(jc jape.Context) {
if jc.Check("failed to get contract", err) != nil {
return
} else if len(fcs) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(explorer.ErrContractNotFound, http.StatusNotFound)
return
}
jc.Encode(fcs[0])
}

func (s *server) contractsIDRevisionsHandler(jc jape.Context) {
var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
return
}

fcs, err := s.e.ContractRevisions(id)
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
if errors.Is(err, explorer.ErrContractNotFound) {
jc.Error(fmt.Errorf("%w: %v", err, id), http.StatusNotFound)
return
} else if jc.Check("failed to fetch contract revisions", err) != nil {
return
}
jc.Encode(fcs)
}

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

Expand All @@ -449,8 +477,6 @@ func (s *server) contractsBatchHandler(jc jape.Context) {
}

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

var key types.PublicKey
if jc.DecodeParam("key", &key) != nil {
return
Expand All @@ -459,15 +485,13 @@ func (s *server) pubkeyContractsHandler(jc jape.Context) {
if jc.Check("failed to get contracts", err) != nil {
return
} else if len(fcs) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(explorer.ErrContractNotFound, http.StatusNotFound)
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
return
}
jc.Encode(fcs)
}

func (s *server) pubkeyHostHandler(jc jape.Context) {
errNotFound := errors.New("host not found")

var key types.PublicKey
if jc.DecodeParam("key", &key) != nil {
return
Expand All @@ -476,14 +500,13 @@ func (s *server) pubkeyHostHandler(jc jape.Context) {
if jc.Check("failed to get host", err) != nil {
return
} else if len(hosts) == 0 {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(ErrHostNotFound, http.StatusNotFound)
return
}
jc.Encode(hosts[0])
}

func (s *server) searchIDHandler(jc jape.Context) {
errNotFound := errors.New("no contract found")
const maxLen = len(types.Hash256{})

// get everything after separator if there is one
Expand All @@ -498,7 +521,7 @@ func (s *server) searchIDHandler(jc jape.Context) {
if jc.Check("failed to search ID", err) != nil {
return
} else if result == explorer.SearchTypeInvalid {
jc.Error(errNotFound, http.StatusNotFound)
jc.Error(ErrNoSearchResults, http.StatusNotFound)
return
}
jc.Encode(result)
Expand Down Expand Up @@ -543,8 +566,9 @@ func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
"GET /outputs/siacoin/:id": srv.outputsSiacoinHandler,
"GET /outputs/siafund/:id": srv.outputsSiafundHandler,

"GET /contracts/:id": srv.contractsIDHandler,
"POST /contracts": srv.contractsBatchHandler,
"GET /contracts/:id": srv.contractsIDHandler,
"GET /contracts/:id/revisions": srv.contractsIDRevisionsHandler,
"POST /contracts": srv.contractsBatchHandler,

"GET /pubkey/:key/contracts": srv.pubkeyContractsHandler,
"GET /pubkey/:key/host": srv.pubkeyHostHandler,
Expand Down
11 changes: 11 additions & 0 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ var (
// database and thus there is no tip. It does not mean there was an
// error in the underlying database.
ErrNoTip = errors.New("no tip found")

// ErrContractNotFound is returned when ContractRevisions is unable to find
// the specified contract ID.
ErrContractNotFound = errors.New("contract not found")
)

// A ChainManager manages the consensus state
Expand Down Expand Up @@ -52,6 +56,7 @@ type Store interface {
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
Contracts(ids []types.FileContractID) (result []FileContract, err error)
ContractsKey(key types.PublicKey) (result []FileContract, err error)
ContractRevisions(id types.FileContractID) (result []types.FileContractElement, err error)
SiacoinElements(ids []types.SiacoinOutputID) (result []SiacoinOutput, err error)
SiafundElements(ids []types.SiafundOutputID) (result []SiafundOutput, err error)

Expand Down Expand Up @@ -236,6 +241,12 @@ func (e *Explorer) ContractsKey(key types.PublicKey) (result []FileContract, err
return e.s.ContractsKey(key)
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (e *Explorer) ContractRevisions(id types.FileContractID) (result []types.FileContractElement, err error) {
return e.s.ContractRevisions(id)
}

// SiacoinElements returns the siacoin elements with the specified IDs.
func (e *Explorer) SiacoinElements(ids []types.SiacoinOutputID) (result []SiacoinOutput, err error) {
return e.s.SiacoinElements(ids)
Expand Down
Loading
Loading