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 v2 contracts and endpoints #127

Merged
merged 9 commits into from
Oct 29, 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
27 changes: 26 additions & 1 deletion api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func (c *Client) Contract(id types.FileContractID) (resp explorer.FileContract,
return
}

// Contracts returns the transactions with the specified IDs.
// Contracts returns the contracts with the specified IDs.
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.FileContract, err error) {
err = c.c.POST("/contracts", ids, &resp)
return
Expand All @@ -210,6 +210,31 @@ func (c *Client) ContractRevisions(id types.FileContractID) (resp []explorer.Fil
return
}

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

// V2Contracts returns the v2 contracts with the specified IDs.
func (c *Client) V2Contracts(ids []types.FileContractID) (resp []explorer.V2FileContract, err error) {
err = c.c.POST("/v2/contracts", ids, &resp)
return
}

// V2ContractsKey returns the v2 contracts for a particular ed25519 key.
func (c *Client) V2ContractsKey(key types.PublicKey) (resp []explorer.V2FileContract, err error) {
err = c.c.GET(fmt.Sprintf("/pubkey/%s/v2/contracts", key), &resp)
return
}

// V2ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (c *Client) V2ContractRevisions(id types.FileContractID) (resp []explorer.V2FileContract, err error) {
err = c.c.GET(fmt.Sprintf("/v2/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
75 changes: 73 additions & 2 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type (
Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error)
ContractRevisions(id types.FileContractID) (result []explorer.FileContract, err error)
V2Contracts(ids []types.FileContractID) (result []explorer.V2FileContract, err error)
V2ContractsKey(key types.PublicKey) (result []explorer.V2FileContract, err error)
V2ContractRevisions(id types.FileContractID) (result []explorer.V2FileContract, err error)
Search(id types.Hash256) (explorer.SearchType, error)

Hosts(pks []types.PublicKey) ([]explorer.Host, error)
Expand Down Expand Up @@ -485,6 +488,7 @@ func (s *server) outputsSiafundHandler(jc jape.Context) {

jc.Encode(outputs[0])
}

func (s *server) contractsIDHandler(jc jape.Context) {
var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
Expand Down Expand Up @@ -532,6 +536,68 @@ func (s *server) contractsBatchHandler(jc jape.Context) {
jc.Encode(fcs)
}

func (s *server) v2ContractsIDHandler(jc jape.Context) {
var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
return
}
fcs, err := s.e.V2Contracts([]types.FileContractID{id})
if jc.Check("failed to get contract", err) != nil {
return
} else if len(fcs) == 0 {
jc.Error(explorer.ErrContractNotFound, http.StatusNotFound)
return
}
jc.Encode(fcs[0])
}

func (s *server) v2ContractsBatchHandler(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.V2Contracts(ids)
if jc.Check("failed to get contracts", err) != nil {
return
}
jc.Encode(fcs)
}

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

fcs, err := s.e.V2ContractRevisions(id)
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) pubkeyV2ContractsHandler(jc jape.Context) {
var key types.PublicKey
if jc.DecodeParam("key", &key) != nil {
return
}
fcs, err := s.e.V2ContractsKey(key)
if jc.Check("failed to get contracts", err) != nil {
return
} else if len(fcs) == 0 {
jc.Error(explorer.ErrContractNotFound, http.StatusNotFound)
return
}
jc.Encode(fcs)
}

func (s *server) pubkeyContractsHandler(jc jape.Context) {
var key types.PublicKey
if jc.DecodeParam("key", &key) != nil {
Expand Down Expand Up @@ -630,8 +696,13 @@ func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
"GET /contracts/:id/revisions": srv.contractsIDRevisionsHandler,
"POST /contracts": srv.contractsBatchHandler,

"GET /pubkey/:key/contracts": srv.pubkeyContractsHandler,
"GET /pubkey/:key/host": srv.pubkeyHostHandler,
"GET /v2/contracts/:id": srv.v2ContractsIDHandler,
"GET /v2/contracts/:id/revisions": srv.v2ContractsIDRevisionsHandler,
"POST /v2/contracts": srv.v2ContractsBatchHandler,

"GET /pubkey/:key/v2/contracts": srv.pubkeyV2ContractsHandler,
"GET /pubkey/:key/contracts": srv.pubkeyContractsHandler,
"GET /pubkey/:key/host": srv.pubkeyHostHandler,

"GET /metrics/block": srv.blocksMetricsHandler,
"GET /metrics/block/:id": srv.blocksMetricsIDHandler,
Expand Down
19 changes: 19 additions & 0 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ type Store interface {
Contracts(ids []types.FileContractID) (result []FileContract, err error)
ContractsKey(key types.PublicKey) (result []FileContract, err error)
ContractRevisions(id types.FileContractID) (result []FileContract, err error)
V2Contracts(ids []types.FileContractID) (result []V2FileContract, err error)
V2ContractsKey(key types.PublicKey) (result []V2FileContract, err error)
V2ContractRevisions(id types.FileContractID) (result []V2FileContract, err error)
SiacoinElements(ids []types.SiacoinOutputID) (result []SiacoinOutput, err error)
SiafundElements(ids []types.SiafundOutputID) (result []SiafundOutput, err error)

Expand Down Expand Up @@ -263,6 +266,22 @@ func (e *Explorer) ContractRevisions(id types.FileContractID) (result []FileCont
return e.s.ContractRevisions(id)
}

// V2Contracts returns the v2 contracts with the specified IDs.
func (e *Explorer) V2Contracts(ids []types.FileContractID) (result []V2FileContract, err error) {
return e.s.V2Contracts(ids)
}

// V2ContractsKey returns the v2 contracts for a particular ed25519 key.
func (e *Explorer) V2ContractsKey(key types.PublicKey) (result []V2FileContract, err error) {
return e.s.V2ContractsKey(key)
}

// V2ContractRevisions returns all the revisions of the v2 contract with the
// specified ID.
func (e *Explorer) V2ContractRevisions(id types.FileContractID) (result []V2FileContract, err error) {
return e.s.V2ContractRevisions(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
21 changes: 19 additions & 2 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ type Transaction struct {
HostAnnouncements []chain.HostAnnouncement `json:"hostAnnouncements,omitempty"`
}

// A V2FileContract is a v2 file contract.
type V2FileContract struct {
TransactionID types.TransactionID `json:"transactionID"`

ConfirmationIndex *types.ChainIndex `json:"confirmationIndex"`
ConfirmationTransactionID *types.TransactionID `json:"confirmationTransactionID"`

Resolution *types.V2FileContractResolutionType
ResolutionIndex *types.ChainIndex `json:"resolutionIndex"`
ResolutionTransactionID *types.TransactionID `json:"resolutionTransactionID"`

types.V2FileContractElement
}

// A V2Transaction is a v2 transaction that uses the wrapped types above.
type V2Transaction struct {
ID types.TransactionID `json:"id"`
Expand All @@ -175,8 +189,11 @@ type V2Transaction struct {
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundInputs []types.V2SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`

FileContracts []V2FileContract `json:"fileContracts,omitempty"`

Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`

NewFoundationAddress *types.Address `json:"newFoundationAddress,omitempty"`
MinerFee types.Currency `json:"minerFee"`
Expand Down
82 changes: 79 additions & 3 deletions explorer/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ type (
ProofTransactionID *types.TransactionID
}

// V2FileContractUpdate represents a v2 file contract from a consensus
// update.
V2FileContractUpdate struct {
FileContractElement types.V2FileContractElement
Revision *types.V2FileContractElement
Resolution types.V2FileContractResolutionType

ConfirmationTransactionID *types.TransactionID
ResolutionTransactionID *types.TransactionID
}

// A DBFileContract represents a file contract element in the DB.
DBFileContract struct {
ID types.FileContractID
Expand Down Expand Up @@ -48,7 +59,8 @@ type (
SpentSiafundElements []types.SiafundElement
EphemeralSiafundElements []types.SiafundElement

FileContractElements []FileContractUpdate
FileContractElements []FileContractUpdate
V2FileContractElements []V2FileContractUpdate
}

// An UpdateTx atomically updates the state of a store.
Expand Down Expand Up @@ -156,6 +168,37 @@ func applyChainUpdate(tx UpdateTx, cau chain.ApplyUpdate) error {
fces = append(fces, fce)
}

v2FceMap := make(map[types.FileContractID]V2FileContractUpdate)
cau.ForEachV2FileContractElement(func(fce types.V2FileContractElement, created bool, rev *types.V2FileContractElement, res types.V2FileContractResolutionType) {
v2FceMap[types.FileContractID(fce.ID)] = V2FileContractUpdate{
FileContractElement: fce,
Revision: rev,
Resolution: res,
}
})
for _, txn := range cau.Block.V2Transactions() {
txnID := txn.ID()
for i := range txn.FileContracts {
fcID := txn.V2FileContractID(txn.ID(), i)

v := v2FceMap[fcID]
v.ConfirmationTransactionID = &txnID
v2FceMap[fcID] = v
}
for _, fcr := range txn.FileContractResolutions {
fcID := types.FileContractID(fcr.Parent.ID)

v := v2FceMap[fcID]
v.ResolutionTransactionID = &txnID
v2FceMap[fcID] = v
}
}

var v2Fces []V2FileContractUpdate
for _, fce := range v2FceMap {
v2Fces = append(v2Fces, fce)
}

var treeUpdates []TreeNodeUpdate
cau.ForEachTreeNode(func(row, column uint64, hash types.Hash256) {
treeUpdates = append(treeUpdates, TreeNodeUpdate{
Expand All @@ -181,7 +224,8 @@ func applyChainUpdate(tx UpdateTx, cau chain.ApplyUpdate) error {
SpentSiafundElements: spentSiafundElements,
EphemeralSiafundElements: ephemeralSiafundElements,

FileContractElements: fces,
FileContractElements: fces,
V2FileContractElements: v2Fces,
}

var err error
Expand Down Expand Up @@ -275,6 +319,37 @@ func revertChainUpdate(tx UpdateTx, cru chain.RevertUpdate, revertedIndex types.
fces = append(fces, fce)
}

v2FceMap := make(map[types.FileContractID]V2FileContractUpdate)
cru.ForEachV2FileContractElement(func(fce types.V2FileContractElement, created bool, rev *types.V2FileContractElement, res types.V2FileContractResolutionType) {
v2FceMap[types.FileContractID(fce.ID)] = V2FileContractUpdate{
FileContractElement: fce,
Revision: rev,
Resolution: res,
}
})
for _, txn := range cru.Block.V2Transactions() {
txnID := txn.ID()
for i := range txn.FileContracts {
fcID := txn.V2FileContractID(txn.ID(), i)

v := v2FceMap[fcID]
v.ConfirmationTransactionID = &txnID
v2FceMap[fcID] = v
}
for _, fcr := range txn.FileContractResolutions {
fcID := types.FileContractID(fcr.Parent.ID)

v := v2FceMap[fcID]
v.ResolutionTransactionID = &txnID
v2FceMap[fcID] = v
}
}

var v2Fces []V2FileContractUpdate
for _, fce := range v2FceMap {
v2Fces = append(v2Fces, fce)
}

var treeUpdates []TreeNodeUpdate
cru.ForEachTreeNode(func(row, column uint64, hash types.Hash256) {
treeUpdates = append(treeUpdates, TreeNodeUpdate{
Expand All @@ -297,7 +372,8 @@ func revertChainUpdate(tx UpdateTx, cru chain.RevertUpdate, revertedIndex types.
SpentSiafundElements: spentSiafundElements,
EphemeralSiafundElements: ephemeralSiafundElements,

FileContractElements: fces,
FileContractElements: fces,
V2FileContractElements: v2Fces,
}
state.Metrics.Index = revertedIndex

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ toolchain go1.23.2

require (
github.com/mattn/go-sqlite3 v1.14.24
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97
go.sia.tech/core v0.5.0
go.sia.tech/coreutils v0.5.0
go.sia.tech/jape v0.12.1
go.uber.org/zap v1.27.0
gopkg.in/yaml.v3 v3.0.1
Expand Down
16 changes: 4 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,10 @@ github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gt
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.sia.tech/core v0.4.7 h1:UAyErZ3nk5/7N0gIG0OEEJJrxh7ru8lgGLlaNtT/Jq0=
go.sia.tech/core v0.4.7/go.mod h1:j2Ke8ihV8or7d2VDrFZWcCkwSVHO0DNMQJAGs9Qop2M=
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7 h1:xN8iVwYd5/qGYCBHejI5vzxXt8P7kAGelyFK+nKGfSc=
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7/go.mod h1:CpiFY0jL5OlU6sm/6fwd6/LQe6Ao8G6OtHtq21ggIoA=
go.sia.tech/coreutils v0.4.1 h1:ExQ9g6EtnFe70ptNBG+OtZyFU3aBoEzE/06rtbN6f4c=
go.sia.tech/coreutils v0.4.1/go.mod h1:v60kPqZERsb1ZS0PVe4S8hr2ArNEwTdp7XTzErXnV2U=
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97 h1:DPK4fA7HNdTgb02fsdFHbtaB2+ydy/78M1sHhznQkMw=
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97/go.mod h1:JIaR+zdGZsqPLBM5mVsnwWJ7hBsES+SAEDQg5EFBitM=
go.sia.tech/core v0.5.0 h1:feLC7DSCF+PhU157s/94106hFKyiGrGQ9HC3/dF/l7E=
go.sia.tech/core v0.5.0/go.mod h1:P3C1BWa/7J4XgdzWuaYHBvLo2RzZ0UBaJM4TG1GWB2g=
go.sia.tech/coreutils v0.5.0 h1:/xKxdw83iZy0jjLzI2NGHyG4azyjK5DJscxpkr6nIGQ=
go.sia.tech/coreutils v0.5.0/go.mod h1:VYM4FcmlhVrpDGvglLHjRW+gitoaxPNLvp5mL2quilo=
go.sia.tech/jape v0.12.1 h1:xr+o9V8FO8ScRqbSaqYf9bjj1UJ2eipZuNcI1nYousU=
go.sia.tech/jape v0.12.1/go.mod h1:wU+h6Wh5olDjkPXjF0tbZ1GDgoZ6VTi4naFw91yyWC4=
go.sia.tech/mux v1.3.0 h1:hgR34IEkqvfBKUJkAzGi31OADeW2y7D6Bmy/Jcbop9c=
Expand All @@ -37,16 +33,12 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
Expand Down
Loading
Loading