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

feat: support challenge admin v2 API #209

Merged
merged 4 commits into from
Oct 20, 2023
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
14 changes: 10 additions & 4 deletions client/api_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ func (c *Client) GetCreateBucketApproval(ctx context.Context, createBucketMsg *s
}

sendOpt := sendOptions{
method: http.MethodGet,
isAdminApi: true,
method: http.MethodGet,
adminInfo: AdminAPIInfo{
isAdminAPI: true,
adminVersion: types.AdminV1Version,
},
}

primarySPAddr := createBucketMsg.GetPrimarySpAddress()
Expand Down Expand Up @@ -872,8 +875,11 @@ func (c *Client) GetMigrateBucketApproval(ctx context.Context, migrateBucketMsg
}

sendOpt := sendOptions{
method: http.MethodGet,
isAdminApi: true,
method: http.MethodGet,
adminInfo: AdminAPIInfo{
isAdminAPI: true,
adminVersion: types.AdminV1Version,
},
}

primarySPID := migrateBucketMsg.DstPrimarySpId
Expand Down
85 changes: 67 additions & 18 deletions client/api_challenge.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package client

import (
"bytes"
"context"
"encoding/hex"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
Expand Down Expand Up @@ -79,10 +83,21 @@ func (c *Client) GetChallengeInfo(ctx context.Context, objectID string, pieceInd

sendOpt := sendOptions{
method: http.MethodGet,
isAdminApi: true,
disableCloseBody: true,
}

if opts.UseV2version {
sendOpt.adminInfo = AdminAPIInfo{
isAdminAPI: true,
adminVersion: types.AdminV2Version,
}
} else {
sendOpt.adminInfo = AdminAPIInfo{
isAdminAPI: true,
adminVersion: types.AdminV1Version,
}
}

var endpoint *url.URL
if opts.Endpoint != "" {
var useHttps bool
Expand Down Expand Up @@ -134,27 +149,61 @@ func (c *Client) GetChallengeInfo(ctx context.Context, objectID string, pieceInd
return types.ChallengeResult{}, err
}

// fetch integrity hash
integrityHash := resp.Header.Get(types.HTTPHeaderIntegrityHash)
// fetch piece hashes
pieceHashes := resp.Header.Get(types.HTTPHeaderPieceHash)
var result types.ChallengeResult

if integrityHash == "" || pieceHashes == "" {
utils.CloseResponse(resp)
return types.ChallengeResult{}, errors.New("fail to fetch hash info")
}
// if it is the v2 challenge API, fetch the info from the xml body
if opts.UseV2version {
challengeV2Info := types.ChallengeV2Result{}
// decode the xml content from response body
err = xml.NewDecoder(resp.Body).Decode(&challengeV2Info)
if err != nil {
utils.CloseResponse(resp)
return types.ChallengeResult{}, err
}

hashList := strings.Split(pieceHashes, ",")
// min hash num equals one segment hash plus EC dataShards, parityShards
if len(hashList) < 1 {
return types.ChallengeResult{}, errors.New("get piece hashes less than 1")
}
if challengeV2Info.IntegrityHash == "" || challengeV2Info.PieceHash == "" || challengeV2Info.PieceData == "" {
utils.CloseResponse(resp)
return types.ChallengeResult{}, errors.New("the challenge info of response is empty")
}

hashListV2 := strings.Split(challengeV2Info.PieceHash, ",")
if len(hashListV2) < 1 {
return types.ChallengeResult{}, errors.New("get piece hashes less than 1")
}

pieceData, decodeErr := hex.DecodeString(challengeV2Info.PieceData)
if decodeErr != nil {
return types.ChallengeResult{}, decodeErr
}

result = types.ChallengeResult{
PieceData: io.NopCloser(bytes.NewReader(pieceData)),
IntegrityHash: challengeV2Info.IntegrityHash,
PiecesHash: hashListV2,
}
} else {
// fetch integrity hash from header
integrityHash := resp.Header.Get(types.HTTPHeaderIntegrityHash)
// fetch piece hashes from header
pieceHashes := resp.Header.Get(types.HTTPHeaderPieceHash)

result := types.ChallengeResult{
PieceData: resp.Body,
IntegrityHash: integrityHash,
PiecesHash: hashList,
if integrityHash == "" || pieceHashes == "" {
utils.CloseResponse(resp)
return types.ChallengeResult{}, errors.New("fail to fetch hash info")
}

hashList := strings.Split(pieceHashes, ",")
if len(hashList) < 1 {
return types.ChallengeResult{}, errors.New("get piece hashes less than 1")
}

result = types.ChallengeResult{
PieceData: resp.Body,
IntegrityHash: integrityHash,
PiecesHash: hashList,
}
}

return result, nil
}

Expand Down
39 changes: 27 additions & 12 deletions client/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func New(chainID string, endpoint string, option Option) (IClient, error) {
}
}
}

return &c, nil
}

Expand Down Expand Up @@ -294,11 +295,17 @@ type requestMeta struct {

// SendOptions - options to use to send the http message
type sendOptions struct {
method string // request method
body interface{} // request body
disableCloseBody bool // indicate whether to disable automatic calls to resp.Body.Close()
txnHash string // the transaction hash info
isAdminApi bool // indicate if it is an admin api request
method string // request method
body interface{} // request body
disableCloseBody bool // indicate whether to disable automatic calls to resp.Body.Close()
txnHash string // the transaction hash info
adminInfo AdminAPIInfo // the admin API info
}

// AdminAPIInfo - the admin api info
type AdminAPIInfo struct {
isAdminAPI bool // indicate if it is an admin api request
adminVersion int // indicate the version of admin api, the default value is 1
}

// downloadSegmentHook is hook for test
Expand All @@ -312,13 +319,13 @@ func DefaultDownloadSegmentHook(seg int64) error {

// newRequest constructs the http request, set url, body and headers
func (c *Client) newRequest(ctx context.Context, method string, meta requestMeta,
body interface{}, txnHash string, isAdminAPi bool, endpoint *url.URL,
body interface{}, txnHash string, adminAPIInfo AdminAPIInfo, endpoint *url.URL,
) (req *http.Request, err error) {
isVirtualHost := c.isVirtualHostStyleUrl(*endpoint, meta.bucketName)

// construct the target url
desURL, err := c.generateURL(meta.bucketName, meta.objectName, meta.urlRelPath,
meta.urlValues, isAdminAPi, endpoint, isVirtualHost)
meta.urlValues, adminAPIInfo, endpoint, isVirtualHost)
if err != nil {
log.Error().Msg(fmt.Sprintf("generate request url on SP: %s fail, err: %s", endpoint.String(), err))
return nil, err
Expand Down Expand Up @@ -400,7 +407,7 @@ func (c *Client) newRequest(ctx context.Context, method string, meta requestMeta
req.Header.Set(types.HTTPHeaderPieceIndex, strconv.Itoa(info.PieceIndex))
}

if isAdminAPi {
if adminAPIInfo.isAdminAPI {
if meta.txnMsg != "" {
req.Header.Set(types.HTTPHeaderUnsignedMsg, meta.txnMsg)
}
Expand Down Expand Up @@ -499,7 +506,7 @@ func (c *Client) doAPI(ctx context.Context, req *http.Request, meta requestMeta,

// sendReq sends the message via REST and handles the response
func (c *Client) sendReq(ctx context.Context, metadata requestMeta, opt *sendOptions, endpoint *url.URL) (res *http.Response, err error) {
req, err := c.newRequest(ctx, opt.method, metadata, opt.body, opt.txnHash, opt.isAdminApi, endpoint)
req, err := c.newRequest(ctx, opt.method, metadata, opt.body, opt.txnHash, opt.adminInfo, endpoint)
if err != nil {
return nil, err
}
Expand All @@ -525,7 +532,7 @@ func (c *Client) SplitPartInfo(objectSize int64, configuredPartSize uint64) (tot

// generateURL constructs the target request url based on the parameters
func (c *Client) generateURL(bucketName string, objectName string, relativePath string,
queryValues url.Values, isAdminApi bool, endpoint *url.URL, isVirtualHost bool,
queryValues url.Values, adminInfo AdminAPIInfo, endpoint *url.URL, isVirtualHost bool,
) (*url.URL, error) {
host := endpoint.Host
scheme := endpoint.Scheme
Expand All @@ -541,8 +548,16 @@ func (c *Client) generateURL(bucketName string, objectName string, relativePath
}

var urlStr string
if isAdminApi {
prefix := types.AdminURLPrefix + types.AdminURLVersion
if adminInfo.isAdminAPI {
var prefix string
// check the version and generate the url by the version
if adminInfo.adminVersion == types.AdminV1Version {
prefix = types.AdminURLPrefix + types.AdminURLV1Version
} else if adminInfo.adminVersion == types.AdminV2Version {
prefix = types.AdminURLPrefix + types.AdminURLV2Version
} else {
return nil, fmt.Errorf("invalid admin version %d", adminInfo.adminVersion)
}
urlStr = scheme + "://" + host + prefix + "/"
} else {
urlStr = scheme + "://" + host + "/"
Expand Down
7 changes: 5 additions & 2 deletions client/api_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -985,8 +985,11 @@ func (c *Client) GetCreateObjectApproval(ctx context.Context, createObjectMsg *s
}

sendOpt := sendOptions{
method: http.MethodGet,
isAdminApi: true,
method: http.MethodGet,
adminInfo: AdminAPIInfo{
isAdminAPI: true,
adminVersion: types.AdminV1Version,
},
}

bucketName := createObjectMsg.BucketName
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
cosmossdk.io/errors v1.0.0-beta.7
cosmossdk.io/math v1.0.1
github.com/bnb-chain/greenfield v1.0.0
github.com/bnb-chain/greenfield-common/go v0.0.0-20230830120314-a54ffd6da39f
github.com/bnb-chain/greenfield-common/go v0.0.0-20230906132736-eb2f0efea228
github.com/cometbft/cometbft v0.37.2
github.com/consensys/gnark-crypto v0.7.0
github.com/cosmos/cosmos-sdk v0.47.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ github.com/bnb-chain/greenfield-cometbft v1.0.0 h1:0r6hOJWD/+es0gxP/exKuN/krgXAr
github.com/bnb-chain/greenfield-cometbft v1.0.0/go.mod h1:f35mk/r5ab6yvzlqEWZt68LfUje68sYgMpVlt2CUYMk=
github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1 h1:XcWulGacHVRiSCx90Q8Y//ajOrLNBQWR/KDB89dy3cU=
github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1/go.mod h1:ey1CiK4bYo1RBNJLRiVbYr5CMdSxci9S/AZRINLtppI=
github.com/bnb-chain/greenfield-common/go v0.0.0-20230830120314-a54ffd6da39f h1:zJvB2wCd80DQ9Nh/ZNQiP8MrHygSpDoav7OzHyIi/pM=
github.com/bnb-chain/greenfield-common/go v0.0.0-20230830120314-a54ffd6da39f/go.mod h1:it3JJVHeG9Wp4QED2GkY/7V9Qo3BuPdoC5/4/U6ecJM=
github.com/bnb-chain/greenfield-common/go v0.0.0-20230906132736-eb2f0efea228 h1:WywBaew30hZuqDTOQbkRV3uBg6XHjNIE1s3AXVXbG+8=
github.com/bnb-chain/greenfield-common/go v0.0.0-20230906132736-eb2f0efea228/go.mod h1:K9jK80fbahciC+FAvrch8Qsbw9ZkvVgjfKsqrzPTAVA=
github.com/bnb-chain/greenfield-cosmos-sdk v1.0.0 h1:hWRvYunA4Um19gwL1SVfMwN9l431ROC7XZ+A5+xM/Bk=
github.com/bnb-chain/greenfield-cosmos-sdk v1.0.0/go.mod h1:y3hDhQhil5hMIhwBTpu07RZBF30ZITkoE+GHhVZChtY=
github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230816082903-b48770f5e210 h1:GHPbV2bC+gmuO6/sG0Tm8oGal3KKSRlyE+zPscDjlA8=
Expand Down
7 changes: 5 additions & 2 deletions types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ const (
EmptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
Iso8601DateFormatSecond = "2006-01-02T15:04:05Z"

AdminURLPrefix = "/greenfield/admin"
AdminURLVersion = "/v1"
AdminURLPrefix = "/greenfield/admin"
AdminURLV1Version = "/v1"
AdminURLV2Version = "/v2"
AdminV1Version = 1
AdminV2Version = 2

CreateObjectAction = "CreateObject"
CreateBucketAction = "CreateBucket"
Expand Down
12 changes: 12 additions & 0 deletions types/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ type UploadOffset struct {
Offset uint64 `xml:"Offset"` // Offset defines the offset info of resumable uploading object
}

// ChallengeV2Result indicates the response info of challenge v2 API
type ChallengeV2Result struct {
XMLName xml.Name `xml:"GetChallengeInfo"`
Version string `xml:"version,attr"`
ObjectID string `xml:"ObjectID"` // ObjectID defines the object id of the challenge request
RedundancyIndex string `xml:"RedundancyIndex"` // RedundancyIndex defines the redundancy index of the challenge request
PieceIndex string `xml:"PieceIndex"` // PieceIndex defines the piece index of the challenge request
IntegrityHash string `xml:"IntegrityHash"` // IntegrityHash defines the integrity hash of the object
PieceHash string `xml:"PieceHash"` // PieceHash defines the return piece hashes of the object
PieceData string `xml:"PieceData"` // PieceData defines the return piece data of challenge request
}

// ListObjectsResult indicates the result of listObjects API.
type ListObjectsResult struct {
// Objects defines the list of object
Expand Down
5 changes: 3 additions & 2 deletions types/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,9 @@ type GetObjectOptions struct {

// GetChallengeInfoOptions contains the options for querying challenge data.
type GetChallengeInfoOptions struct {
Endpoint string // Endpoint indicates the endpoint of sp
SPAddress string // SPAddress indicates the HEX-encoded string of the sp address to be challenged.
Endpoint string // Endpoint indicates the endpoint of sp
SPAddress string // SPAddress indicates the HEX-encoded string of the sp address to be challenged.
UseV2version bool // UseV2version indicates whether using of the v2 version get-challenge API
}

// GetSecondaryPieceOptions contains the options for `GetSecondaryPiece` API.
Expand Down