github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/sdk.go (about)

     1  package sdk
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"math"
    10  	"net/http"
    11  	"strconv"
    12  
    13  	"github.com/0chain/common/core/currency"
    14  	"github.com/0chain/errors"
    15  	"github.com/0chain/gosdk/core/conf"
    16  	"github.com/0chain/gosdk/core/logger"
    17  	"github.com/0chain/gosdk/core/node"
    18  	"gopkg.in/natefinch/lumberjack.v2"
    19  
    20  	"github.com/0chain/gosdk/core/common"
    21  	enc "github.com/0chain/gosdk/core/encryption"
    22  	"github.com/0chain/gosdk/core/transaction"
    23  	"github.com/0chain/gosdk/core/version"
    24  	"github.com/0chain/gosdk/zboxcore/blockchain"
    25  	"github.com/0chain/gosdk/zboxcore/client"
    26  	"github.com/0chain/gosdk/zboxcore/encryption"
    27  	l "github.com/0chain/gosdk/zboxcore/logger"
    28  	"github.com/0chain/gosdk/zboxcore/marker"
    29  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    30  )
    31  
    32  const STORAGE_SCADDRESS = "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d7"
    33  const MINERSC_SCADDRESS = "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d9"
    34  const ZCNSC_SCADDRESS = "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712e0"
    35  
    36  var sdkNotInitialized = errors.New("sdk_not_initialized", "SDK is not initialised")
    37  var allocationNotFound = errors.New("couldnt_find_allocation", "Couldn't find the allocation required for update")
    38  
    39  const (
    40  	OpUpload            int = 0
    41  	OpDownload          int = 1
    42  	OpRepair            int = 2
    43  	OpUpdate            int = 3
    44  	opThumbnailDownload int = 4
    45  )
    46  
    47  type StatusCallback interface {
    48  	Started(allocationId, filePath string, op int, totalBytes int)
    49  	InProgress(allocationId, filePath string, op int, completedBytes int, data []byte)
    50  	Error(allocationID string, filePath string, op int, err error)
    51  	Completed(allocationId, filePath string, filename string, mimetype string, size int, op int)
    52  	RepairCompleted(filesRepaired int)
    53  }
    54  
    55  var (
    56  	numBlockDownloads         = 100
    57  	sdkInitialized            = false
    58  	networkWorkerTimerInHours = 1
    59  	singleClientMode          = false
    60  	shouldVerifyHash          = true
    61  )
    62  
    63  func SetSingleClietnMode(mode bool) {
    64  	singleClientMode = mode
    65  }
    66  
    67  func SetShouldVerifyHash(verify bool) {
    68  	shouldVerifyHash = verify
    69  }
    70  
    71  func SetSaveProgress(save bool) {
    72  	shouldSaveProgress = save
    73  }
    74  
    75  // GetVersion - returns version string
    76  func GetVersion() string {
    77  	return version.VERSIONSTR
    78  }
    79  
    80  // SetLogLevel set the log level.
    81  //   - lvl: 0 disabled; higher number (upto 4) more verbosity
    82  func SetLogLevel(lvl int) {
    83  	l.Logger.SetLevel(lvl)
    84  }
    85  
    86  // SetLogFile set the log file and verbosity levels
    87  //   - logFile: Log file
    88  //   - verbose: true - console output; false - no console output
    89  func SetLogFile(logFile string, verbose bool) {
    90  	var ioWriter = &lumberjack.Logger{
    91  		Filename:   logFile,
    92  		MaxSize:    100, // MB
    93  		MaxBackups: 5,   // number of backups
    94  		MaxAge:     28,  //days
    95  		LocalTime:  false,
    96  		Compress:   false, // disabled by default
    97  	}
    98  
    99  	l.Logger.SetLogFile(ioWriter, verbose)
   100  	l.Logger.Info("******* Storage SDK Version: ", version.VERSIONSTR, " *******")
   101  }
   102  
   103  // GetLogger retrieves logger instance
   104  func GetLogger() *logger.Logger {
   105  	return &l.Logger
   106  }
   107  
   108  // InitStorageSDK Initialize the storage SDK
   109  //
   110  //   - walletJSON: Client's wallet JSON
   111  //   - blockWorker: Block worker URL (block worker refers to 0DNS)
   112  //   - chainID: ID of the blokcchain network
   113  //   - signatureScheme: Signature scheme that will be used for signing transactions
   114  //   - preferredBlobbers: List of preferred blobbers to use when creating an allocation. This is usually configured by the client in the configuration files
   115  //   - nonce: Initial nonce value for the transactions
   116  //   - fee: Preferred value for the transaction fee, just the first value is taken
   117  func InitStorageSDK(walletJSON string,
   118  	blockWorker, chainID, signatureScheme string,
   119  	preferredBlobbers []string,
   120  	nonce int64,
   121  	fee ...uint64) error {
   122  	err := client.PopulateClient(walletJSON, signatureScheme)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	blockchain.SetChainID(chainID)
   128  	blockchain.SetBlockWorker(blockWorker)
   129  
   130  	err = InitNetworkDetails()
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	client.SetClientNonce(nonce)
   136  	if len(fee) > 0 {
   137  		client.SetTxnFee(fee[0])
   138  	}
   139  
   140  	go UpdateNetworkDetailsWorker(context.Background())
   141  	sdkInitialized = true
   142  	return nil
   143  }
   144  
   145  // GetNetwork retrieves the network details
   146  func GetNetwork() *Network {
   147  	return &Network{
   148  		Miners:   blockchain.GetMiners(),
   149  		Sharders: blockchain.GetAllSharders(),
   150  	}
   151  }
   152  
   153  // SetMaxTxnQuery set the maximum number of transactions to query
   154  func SetMaxTxnQuery(num int) {
   155  	blockchain.SetMaxTxnQuery(num)
   156  
   157  	cfg, _ := conf.GetClientConfig()
   158  	if cfg != nil {
   159  		cfg.MaxTxnQuery = num
   160  	}
   161  
   162  }
   163  
   164  // SetQuerySleepTime set the sleep time between queries
   165  func SetQuerySleepTime(time int) {
   166  	blockchain.SetQuerySleepTime(time)
   167  
   168  	cfg, _ := conf.GetClientConfig()
   169  	if cfg != nil {
   170  		cfg.QuerySleepTime = time
   171  	}
   172  
   173  }
   174  
   175  // SetMinSubmit set the minimum number of miners to submit the transaction
   176  func SetMinSubmit(num int) {
   177  	blockchain.SetMinSubmit(num)
   178  }
   179  
   180  // SetMinConfirmation set the minimum number of miners to confirm the transaction
   181  func SetMinConfirmation(num int) {
   182  	blockchain.SetMinConfirmation(num)
   183  }
   184  
   185  // SetNetwork set the network details, given the miners and sharders urls
   186  //   - miners: list of miner urls
   187  //   - sharders: list of sharder urls
   188  func SetNetwork(miners []string, sharders []string) {
   189  	blockchain.SetMiners(miners)
   190  	blockchain.SetSharders(sharders)
   191  	node.InitCache(blockchain.Sharders)
   192  }
   193  
   194  // CreateReadPool creates a read pool for the SDK client.
   195  // Read pool is used to lock tokens for read operations.
   196  // Currently, all read operations are free 🚀.
   197  func CreateReadPool() (hash string, nonce int64, err error) {
   198  	if !sdkInitialized {
   199  		return "", 0, sdkNotInitialized
   200  	}
   201  	hash, _, nonce, _, err = storageSmartContractTxn(transaction.SmartContractTxnData{
   202  		Name: transaction.STORAGESC_CREATE_READ_POOL,
   203  	})
   204  	return
   205  }
   206  
   207  type BackPool struct {
   208  	ID      string         `json:"id"`
   209  	Balance common.Balance `json:"balance"`
   210  }
   211  
   212  //
   213  // read pool
   214  //
   215  
   216  type ReadPool struct {
   217  	Balance common.Balance `json:"balance"`
   218  }
   219  
   220  // GetReadPoolInfo for given client, or, if the given clientID is empty,
   221  // for current client of the sdk.
   222  //   - clientID: client ID
   223  func GetReadPoolInfo(clientID string) (info *ReadPool, err error) {
   224  	if !sdkInitialized {
   225  		return nil, sdkNotInitialized
   226  	}
   227  
   228  	if clientID == "" {
   229  		clientID = client.GetClientID()
   230  	}
   231  
   232  	var b []byte
   233  	b, err = zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/getReadPoolStat",
   234  		map[string]string{"client_id": clientID}, nil)
   235  	if err != nil {
   236  		return nil, errors.Wrap(err, "error requesting read pool info")
   237  	}
   238  	if len(b) == 0 {
   239  		return nil, errors.New("", "empty response")
   240  	}
   241  
   242  	info = new(ReadPool)
   243  	if err = json.Unmarshal(b, info); err != nil {
   244  		return nil, errors.Wrap(err, "error decoding response:")
   245  	}
   246  
   247  	return
   248  }
   249  
   250  //
   251  // stake pool
   252  //
   253  
   254  // StakePoolOfferInfo represents stake pool offer information.
   255  type StakePoolOfferInfo struct {
   256  	Lock         common.Balance   `json:"lock"`
   257  	Expire       common.Timestamp `json:"expire"`
   258  	AllocationID common.Key       `json:"allocation_id"`
   259  	IsExpired    bool             `json:"is_expired"`
   260  }
   261  
   262  // StakePoolRewardsInfo represents stake pool rewards.
   263  type StakePoolRewardsInfo struct {
   264  	Charge    common.Balance `json:"charge"`    // total for all time
   265  	Blobber   common.Balance `json:"blobber"`   // total for all time
   266  	Validator common.Balance `json:"validator"` // total for all time
   267  }
   268  
   269  // StakePoolDelegatePoolInfo represents delegate pool of a stake pool info.
   270  type StakePoolDelegatePoolInfo struct {
   271  	ID         common.Key     `json:"id"`          // blobber ID
   272  	Balance    common.Balance `json:"balance"`     // current balance
   273  	DelegateID common.Key     `json:"delegate_id"` // wallet
   274  	Rewards    common.Balance `json:"rewards"`     // current
   275  	UnStake    bool           `json:"unstake"`     // want to unstake
   276  
   277  	TotalReward  common.Balance   `json:"total_reward"`
   278  	TotalPenalty common.Balance   `json:"total_penalty"`
   279  	Status       string           `json:"status"`
   280  	RoundCreated int64            `json:"round_created"`
   281  	StakedAt     common.Timestamp `json:"staked_at"`
   282  }
   283  
   284  // StakePool information of stake pool of a provider.
   285  type StakePoolInfo struct {
   286  	ID         common.Key     `json:"pool_id"` // pool ID
   287  	Balance    common.Balance `json:"balance"` // total balance
   288  	StakeTotal common.Balance `json:"stake_total"`
   289  	// delegate pools
   290  	Delegate []StakePoolDelegatePoolInfo `json:"delegate"`
   291  	// rewards
   292  	Rewards common.Balance `json:"rewards"`
   293  	// total rewards
   294  	TotalRewards common.Balance `json:"total_rewards"`
   295  	// Settings of the stake pool
   296  	Settings blockchain.StakePoolSettings `json:"settings"`
   297  }
   298  
   299  // GetStakePoolInfo retrieve stake pool info for the current client configured to the sdk, given provider type and provider ID.
   300  //   - providerType: provider type
   301  //   - providerID: provider ID
   302  func GetStakePoolInfo(providerType ProviderType, providerID string) (info *StakePoolInfo, err error) {
   303  	if !sdkInitialized {
   304  		return nil, sdkNotInitialized
   305  	}
   306  
   307  	var b []byte
   308  	b, err = zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/getStakePoolStat",
   309  		map[string]string{"provider_type": strconv.Itoa(int(providerType)), "provider_id": providerID}, nil)
   310  	if err != nil {
   311  		return nil, errors.Wrap(err, "error requesting stake pool info:")
   312  	}
   313  	if len(b) == 0 {
   314  		return nil, errors.New("", "empty response")
   315  	}
   316  
   317  	info = new(StakePoolInfo)
   318  	if err = json.Unmarshal(b, info); err != nil {
   319  		return nil, errors.Wrap(err, "error decoding response:")
   320  	}
   321  
   322  	return
   323  }
   324  
   325  // StakePoolUserInfo represents user stake pools statistic.
   326  type StakePoolUserInfo struct {
   327  	Pools map[common.Key][]*StakePoolDelegatePoolInfo `json:"pools"`
   328  }
   329  
   330  // GetStakePoolUserInfo obtains blobbers/validators delegate pools statistic for a user.
   331  // If given clientID is empty string, then current client used.
   332  //   - clientID: client ID
   333  //   - offset: offset
   334  //   - limit: limit
   335  func GetStakePoolUserInfo(clientID string, offset, limit int) (info *StakePoolUserInfo, err error) {
   336  	if !sdkInitialized {
   337  		return nil, sdkNotInitialized
   338  	}
   339  	if clientID == "" {
   340  		clientID = client.GetClientID()
   341  	}
   342  
   343  	var b []byte
   344  	params := map[string]string{
   345  		"client_id": clientID,
   346  		"offset":    strconv.FormatInt(int64(offset), 10),
   347  		"limit":     strconv.FormatInt(int64(limit), 10),
   348  	}
   349  	b, err = zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS,
   350  		"/getUserStakePoolStat", params, nil)
   351  	if err != nil {
   352  		return nil, errors.Wrap(err, "error requesting stake pool user info:")
   353  	}
   354  	if len(b) == 0 {
   355  		return nil, errors.New("", "empty response")
   356  	}
   357  
   358  	info = new(StakePoolUserInfo)
   359  	if err = json.Unmarshal(b, info); err != nil {
   360  		return nil, errors.Wrap(err, "error decoding response:")
   361  	}
   362  
   363  	return
   364  }
   365  
   366  type stakePoolRequest struct {
   367  	ProviderType ProviderType `json:"provider_type,omitempty"`
   368  	ProviderID   string       `json:"provider_id,omitempty"`
   369  }
   370  
   371  // stakePoolLock is stake pool unlock response in case where tokens
   372  // can't be unlocked due to opened offers.
   373  type stakePoolLock struct {
   374  	Client       string       `json:"client"`
   375  	ProviderId   string       `json:"provider_id"`
   376  	ProviderType ProviderType `json:"provider_type"`
   377  	Amount       int64        `json:"amount"`
   378  }
   379  
   380  //
   381  // challenge pool
   382  //
   383  
   384  // ChallengePoolInfo represents a challenge pool stat.
   385  type ChallengePoolInfo struct {
   386  	ID         string           `json:"id"`
   387  	Balance    common.Balance   `json:"balance"`
   388  	StartTime  common.Timestamp `json:"start_time"`
   389  	Expiration common.Timestamp `json:"expiration"`
   390  	Finalized  bool             `json:"finalized"`
   391  }
   392  
   393  // GetChallengePoolInfo retrieve challenge pool info for given allocation.
   394  //   - allocID: allocation ID
   395  func GetChallengePoolInfo(allocID string) (info *ChallengePoolInfo, err error) {
   396  	if !sdkInitialized {
   397  		return nil, sdkNotInitialized
   398  	}
   399  
   400  	var b []byte
   401  	b, err = zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS,
   402  		"/getChallengePoolStat", map[string]string{"allocation_id": allocID},
   403  		nil)
   404  	if err != nil {
   405  		return nil, errors.Wrap(err, "error requesting challenge pool info:")
   406  	}
   407  	if len(b) == 0 {
   408  		return nil, errors.New("", "empty response")
   409  	}
   410  
   411  	info = new(ChallengePoolInfo)
   412  	if err = json.Unmarshal(b, info); err != nil {
   413  		return nil, errors.Wrap(err, "error decoding response:")
   414  	}
   415  
   416  	return
   417  }
   418  
   419  // GetMptData retrieves mpt key data.
   420  func GetMptData(key string) ([]byte, error) {
   421  	if !sdkInitialized {
   422  		return nil, sdkNotInitialized
   423  	}
   424  
   425  	var b []byte
   426  	b, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS,
   427  		"/get_mpt_key", map[string]string{"key": key},
   428  		nil,
   429  	)
   430  	if err != nil {
   431  		return nil, errors.Wrap(err, "error requesting mpt key data:")
   432  	}
   433  	if len(b) == 0 {
   434  		return nil, errors.New("", "empty response")
   435  	}
   436  
   437  	return b, nil
   438  }
   439  
   440  //
   441  // storage SC configurations and blobbers
   442  //
   443  
   444  type InputMap struct {
   445  	Fields map[string]interface{} `json:"fields"`
   446  }
   447  
   448  // GetStorageSCConfig retrieves storage SC configurations.
   449  func GetStorageSCConfig() (conf *InputMap, err error) {
   450  	if !sdkInitialized {
   451  		return nil, sdkNotInitialized
   452  	}
   453  
   454  	var b []byte
   455  	b, err = zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/storage-config", nil,
   456  		nil)
   457  	if err != nil {
   458  		return nil, errors.Wrap(err, "error requesting storage SC configs:")
   459  	}
   460  	if len(b) == 0 {
   461  		return nil, errors.New("", "empty response")
   462  	}
   463  
   464  	conf = new(InputMap)
   465  	conf.Fields = make(map[string]interface{})
   466  	if err = json.Unmarshal(b, conf); err != nil {
   467  		return nil, errors.Wrap(err, "rror decoding response:")
   468  	}
   469  
   470  	return
   471  }
   472  
   473  // Blobber type represents blobber information.
   474  type Blobber struct {
   475  	// ID of the blobber
   476  	ID common.Key `json:"id"`
   477  
   478  	// BaseURL of the blobber
   479  	BaseURL string `json:"url"`
   480  
   481  	// Terms of the blobber
   482  	Terms Terms `json:"terms"`
   483  
   484  	// Capacity of the blobber
   485  	Capacity common.Size `json:"capacity"`
   486  
   487  	// Allocated size of the blobber
   488  	Allocated common.Size `json:"allocated"`
   489  
   490  	// LastHealthCheck of the blobber
   491  	LastHealthCheck common.Timestamp `json:"last_health_check"`
   492  
   493  	// PublicKey of the blobber
   494  	PublicKey string `json:"-"`
   495  
   496  	// StakePoolSettings settings of the blobber staking
   497  	StakePoolSettings blockchain.StakePoolSettings `json:"stake_pool_settings"`
   498  
   499  	// TotalStake of the blobber in SAS
   500  	TotalStake int64 `json:"total_stake"`
   501  
   502  	// UsedAllocation of the blobber in SAS
   503  	UsedAllocation int64 `json:"used_allocation"`
   504  
   505  	// TotalOffers of the blobber in SAS
   506  	TotalOffers int64 `json:"total_offers"`
   507  
   508  	// TotalServiceCharge of the blobber in SAS
   509  	TotalServiceCharge int64 `json:"total_service_charge"`
   510  
   511  	// UncollectedServiceCharge of the blobber in SAS
   512  	UncollectedServiceCharge int64 `json:"uncollected_service_charge"`
   513  
   514  	// IsKilled flag of the blobber, if true then the blobber is killed
   515  	IsKilled bool `json:"is_killed"`
   516  
   517  	// IsShutdown flag of the blobber, if true then the blobber is shutdown
   518  	IsShutdown bool `json:"is_shutdown"`
   519  
   520  	// NotAvailable flag of the blobber, if true then the blobber is not available
   521  	NotAvailable bool `json:"not_available"`
   522  
   523  	// IsRestricted flag of the blobber, if true then the blobber is restricted
   524  	IsRestricted bool `json:"is_restricted"`
   525  }
   526  
   527  // UpdateBlobber is used during update blobber settings calls.
   528  // Note the types are of pointer types with omitempty json property.
   529  // This is done to correctly identify which properties are actually changing.
   530  type UpdateBlobber struct {
   531  	ID                       common.Key                          `json:"id"`
   532  	BaseURL                  *string                             `json:"url,omitempty"`
   533  	Terms                    *UpdateTerms                        `json:"terms,omitempty"`
   534  	Capacity                 *common.Size                        `json:"capacity,omitempty"`
   535  	Allocated                *common.Size                        `json:"allocated,omitempty"`
   536  	LastHealthCheck          *common.Timestamp                   `json:"last_health_check,omitempty"`
   537  	StakePoolSettings        *blockchain.UpdateStakePoolSettings `json:"stake_pool_settings,omitempty"`
   538  	TotalStake               *int64                              `json:"total_stake,omitempty"`
   539  	UsedAllocation           *int64                              `json:"used_allocation,omitempty"`
   540  	TotalOffers              *int64                              `json:"total_offers,omitempty"`
   541  	TotalServiceCharge       *int64                              `json:"total_service_charge,omitempty"`
   542  	UncollectedServiceCharge *int64                              `json:"uncollected_service_charge,omitempty"`
   543  	IsKilled                 *bool                               `json:"is_killed,omitempty"`
   544  	IsShutdown               *bool                               `json:"is_shutdown,omitempty"`
   545  	NotAvailable             *bool                               `json:"not_available,omitempty"`
   546  	IsRestricted             *bool                               `json:"is_restricted,omitempty"`
   547  }
   548  
   549  // ResetBlobberStatsDto represents blobber stats reset request.
   550  type ResetBlobberStatsDto struct {
   551  	BlobberID     string `json:"blobber_id"`
   552  	PrevAllocated int64  `json:"prev_allocated"`
   553  	PrevSavedData int64  `json:"prev_saved_data"`
   554  	NewAllocated  int64  `json:"new_allocated"`
   555  	NewSavedData  int64  `json:"new_saved_data"`
   556  }
   557  
   558  // Validator represents validator information.
   559  type Validator struct {
   560  	ID                       common.Key       `json:"validator_id"`
   561  	BaseURL                  string           `json:"url"`
   562  	PublicKey                string           `json:"-"`
   563  	DelegateWallet           string           `json:"delegate_wallet"`
   564  	MinStake                 common.Balance   `json:"min_stake"`
   565  	MaxStake                 common.Balance   `json:"max_stake"`
   566  	NumDelegates             int              `json:"num_delegates"`
   567  	ServiceCharge            float64          `json:"service_charge"`
   568  	StakeTotal               int64            `json:"stake_total"`
   569  	TotalServiceCharge       int64            `json:"total_service_charge"`
   570  	UncollectedServiceCharge int64            `json:"uncollected_service_charge"`
   571  	LastHealthCheck          common.Timestamp `json:"last_health_check"`
   572  	IsKilled                 bool             `json:"is_killed"`
   573  	IsShutdown               bool             `json:"is_shutdown"`
   574  }
   575  
   576  // UpdateValidator is used during update validator settings calls.
   577  // Note the types are of pointer types with omitempty json property.
   578  // This is done to correctly identify which properties are actually changing.
   579  type UpdateValidator struct {
   580  	ID                       common.Key        `json:"validator_id"`
   581  	BaseURL                  *string           `json:"url,omitempty"`
   582  	DelegateWallet           *string           `json:"delegate_wallet,omitempty"`
   583  	MinStake                 *common.Balance   `json:"min_stake,omitempty"`
   584  	MaxStake                 *common.Balance   `json:"max_stake,omitempty"`
   585  	NumDelegates             *int              `json:"num_delegates,omitempty"`
   586  	ServiceCharge            *float64          `json:"service_charge,omitempty"`
   587  	StakeTotal               *int64            `json:"stake_total,omitempty"`
   588  	TotalServiceCharge       *int64            `json:"total_service_charge,omitempty"`
   589  	UncollectedServiceCharge *int64            `json:"uncollected_service_charge,omitempty"`
   590  	LastHealthCheck          *common.Timestamp `json:"last_health_check,omitempty"`
   591  	IsKilled                 *bool             `json:"is_killed,omitempty"`
   592  	IsShutdown               *bool             `json:"is_shutdown,omitempty"`
   593  }
   594  
   595  // ConvertToValidationNode converts UpdateValidator request to blockchain.UpdateValidationNode.
   596  func (v *UpdateValidator) ConvertToValidationNode() *blockchain.UpdateValidationNode {
   597  	blockValidator := &blockchain.UpdateValidationNode{
   598  		ID:      string(v.ID),
   599  		BaseURL: v.BaseURL,
   600  	}
   601  
   602  	sp := &blockchain.UpdateStakePoolSettings{
   603  		DelegateWallet: v.DelegateWallet,
   604  		NumDelegates:   v.NumDelegates,
   605  		ServiceCharge:  v.ServiceCharge,
   606  	}
   607  
   608  	if v.DelegateWallet != nil ||
   609  		v.MinStake != nil ||
   610  		v.MaxStake != nil ||
   611  		v.NumDelegates != nil ||
   612  		v.ServiceCharge != nil {
   613  		blockValidator.StakePoolSettings = sp
   614  	}
   615  
   616  	return blockValidator
   617  }
   618  
   619  func getBlobbersInternal(active, stakable bool, limit, offset int) (bs []*Blobber, err error) {
   620  	type nodes struct {
   621  		Nodes []*Blobber
   622  	}
   623  
   624  	url := fmt.Sprintf("/getblobbers?active=%s&limit=%d&offset=%d&stakable=%s",
   625  		strconv.FormatBool(active),
   626  		limit,
   627  		offset,
   628  		strconv.FormatBool(stakable),
   629  	)
   630  	b, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, url, nil, nil)
   631  	var wrap nodes
   632  	if err != nil {
   633  		return nil, errors.Wrap(err, "error requesting blobbers:")
   634  	}
   635  	if len(b) == 0 {
   636  		return nil, errors.New("", "empty response")
   637  	}
   638  
   639  	if err = json.Unmarshal(b, &wrap); err != nil {
   640  		return nil, errors.Wrap(err, "error decoding response:")
   641  	}
   642  
   643  	return wrap.Nodes, nil
   644  }
   645  
   646  // GetBlobbers returns list of blobbers.
   647  //   - active: if true then only active blobbers are returned
   648  //   - stakable: if true then only stakable blobbers are returned
   649  func GetBlobbers(active, stakable bool) (bs []*Blobber, err error) {
   650  	if !sdkInitialized {
   651  		return nil, sdkNotInitialized
   652  	}
   653  
   654  	limit, offset := 20, 0
   655  
   656  	blobbers, err := getBlobbersInternal(active, stakable, limit, offset)
   657  	if err != nil {
   658  		return nil, err
   659  	}
   660  
   661  	var blobbersSl []*Blobber
   662  	blobbersSl = append(blobbersSl, blobbers...)
   663  	for {
   664  		// if the len of output returned is less than the limit it means this is the last round of pagination
   665  		if len(blobbers) < limit {
   666  			break
   667  		}
   668  
   669  		// get the next set of blobbers
   670  		offset += 20
   671  		blobbers, err = getBlobbersInternal(active, stakable, limit, offset)
   672  		if err != nil {
   673  			return blobbers, err
   674  		}
   675  		blobbersSl = append(blobbersSl, blobbers...)
   676  
   677  	}
   678  	return blobbersSl, nil
   679  }
   680  
   681  // GetBlobber retrieve blobber by id.
   682  //   - blobberID: the id of blobber
   683  func GetBlobber(blobberID string) (blob *Blobber, err error) {
   684  	if !sdkInitialized {
   685  		return nil, sdkNotInitialized
   686  	}
   687  	var b []byte
   688  	b, err = zboxutil.MakeSCRestAPICall(
   689  		STORAGE_SCADDRESS,
   690  		"/getBlobber",
   691  		map[string]string{"blobber_id": blobberID},
   692  		nil)
   693  	if err != nil {
   694  		return nil, errors.Wrap(err, "requesting blobber:")
   695  	}
   696  	if len(b) == 0 {
   697  		return nil, errors.New("", "empty response from sharders")
   698  	}
   699  	blob = new(Blobber)
   700  	if err = json.Unmarshal(b, blob); err != nil {
   701  		return nil, errors.Wrap(err, "decoding response:")
   702  	}
   703  	return
   704  }
   705  
   706  // GetValidator retrieve validator instance by id.
   707  //   - validatorID: the id of validator
   708  func GetValidator(validatorID string) (validator *Validator, err error) {
   709  	if !sdkInitialized {
   710  		return nil, sdkNotInitialized
   711  	}
   712  	var b []byte
   713  	b, err = zboxutil.MakeSCRestAPICall(
   714  		STORAGE_SCADDRESS,
   715  		"/get_validator",
   716  		map[string]string{"validator_id": validatorID},
   717  		nil)
   718  	if err != nil {
   719  		return nil, errors.Wrap(err, "requesting validator:")
   720  	}
   721  	if len(b) == 0 {
   722  		return nil, errors.New("", "empty response from sharders")
   723  	}
   724  	validator = new(Validator)
   725  	if err = json.Unmarshal(b, validator); err != nil {
   726  		return nil, errors.Wrap(err, "decoding response:")
   727  	}
   728  	return
   729  }
   730  
   731  // GetValidators returns list of validators.
   732  //   - stakable: if true then only stakable validators are returned
   733  func GetValidators(stakable bool) (validators []*Validator, err error) {
   734  	if !sdkInitialized {
   735  		return nil, sdkNotInitialized
   736  	}
   737  	var b []byte
   738  	b, err = zboxutil.MakeSCRestAPICall(
   739  		STORAGE_SCADDRESS,
   740  		"/validators",
   741  		map[string]string{
   742  			"stakable": strconv.FormatBool(stakable),
   743  		},
   744  		nil)
   745  	if err != nil {
   746  		return nil, errors.Wrap(err, "requesting validator list")
   747  	}
   748  	if len(b) == 0 {
   749  		return nil, errors.New("", "empty response from sharders")
   750  	}
   751  	if err = json.Unmarshal(b, &validators); err != nil {
   752  		return nil, errors.Wrap(err, "decoding response:")
   753  	}
   754  	return
   755  }
   756  
   757  // GetClientEncryptedPublicKey - get the client's public key
   758  func GetClientEncryptedPublicKey() (string, error) {
   759  	if !sdkInitialized {
   760  		return "", sdkNotInitialized
   761  	}
   762  	encScheme := encryption.NewEncryptionScheme()
   763  	_, err := encScheme.Initialize(client.GetClient().Mnemonic)
   764  	if err != nil {
   765  		return "", err
   766  	}
   767  	return encScheme.GetPublicKey()
   768  }
   769  
   770  // GetAllocationFromAuthTicket - get allocation from given auth ticket hash.
   771  // AuthTicket is used to access free allocations, and it's generated by the Free Storage Assigner.
   772  //   - authTicket: the auth ticket hash
   773  //
   774  // returns the allocation instance and error if any
   775  func GetAllocationFromAuthTicket(authTicket string) (*Allocation, error) {
   776  	if !sdkInitialized {
   777  		return nil, sdkNotInitialized
   778  	}
   779  	sEnc, err := base64.StdEncoding.DecodeString(authTicket)
   780  	if err != nil {
   781  		return nil, errors.New("auth_ticket_decode_error", "Error decoding the auth ticket."+err.Error())
   782  	}
   783  	at := &marker.AuthTicket{}
   784  	err = json.Unmarshal(sEnc, at)
   785  	if err != nil {
   786  		return nil, errors.New("auth_ticket_decode_error", "Error unmarshaling the auth ticket."+err.Error())
   787  	}
   788  	return GetAllocation(at.AllocationID)
   789  }
   790  
   791  // GetAllocation - get allocation from given allocation id
   792  //
   793  //   - allocationID: the allocation id
   794  //
   795  // returns the allocation instance and error if any
   796  func GetAllocation(allocationID string) (*Allocation, error) {
   797  	if !sdkInitialized {
   798  		return nil, sdkNotInitialized
   799  	}
   800  	params := make(map[string]string)
   801  	params["allocation"] = allocationID
   802  	allocationBytes, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/allocation", params, nil)
   803  	if err != nil {
   804  		return nil, errors.New("allocation_fetch_error", "Error fetching the allocation."+err.Error())
   805  	}
   806  	allocationObj := &Allocation{}
   807  	err = json.Unmarshal(allocationBytes, allocationObj)
   808  	if err != nil {
   809  		return nil, errors.New("allocation_decode_error", "Error decoding the allocation: "+err.Error()+" "+string(allocationBytes))
   810  	}
   811  	hashdata := allocationObj.Tx
   812  	sig, ok := zboxutil.SignCache.Get(hashdata)
   813  	if !ok {
   814  		sig, err = client.Sign(enc.Hash(hashdata))
   815  		zboxutil.SignCache.Add(hashdata, sig)
   816  		if err != nil {
   817  			return nil, err
   818  		}
   819  	}
   820  
   821  	allocationObj.sig = sig
   822  	allocationObj.numBlockDownloads = numBlockDownloads
   823  	allocationObj.InitAllocation()
   824  	return allocationObj, nil
   825  }
   826  
   827  func GetAllocationUpdates(allocation *Allocation) error {
   828  	if allocation == nil {
   829  		return errors.New("allocation_not_initialized", "")
   830  	}
   831  
   832  	params := make(map[string]string)
   833  	params["allocation"] = allocation.ID
   834  	allocationBytes, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/allocation", params, nil)
   835  	if err != nil {
   836  		return errors.New("allocation_fetch_error", "Error fetching the allocation."+err.Error())
   837  	}
   838  
   839  	updatedAllocationObj := new(Allocation)
   840  	if err := json.Unmarshal(allocationBytes, updatedAllocationObj); err != nil {
   841  		return errors.New("allocation_decode_error", "Error decoding the allocation."+err.Error())
   842  	}
   843  
   844  	allocation.DataShards = updatedAllocationObj.DataShards
   845  	allocation.ParityShards = updatedAllocationObj.ParityShards
   846  	allocation.Size = updatedAllocationObj.Size
   847  	allocation.Expiration = updatedAllocationObj.Expiration
   848  	allocation.Payer = updatedAllocationObj.Payer
   849  	allocation.Blobbers = updatedAllocationObj.Blobbers
   850  	allocation.Stats = updatedAllocationObj.Stats
   851  	allocation.TimeUnit = updatedAllocationObj.TimeUnit
   852  	allocation.BlobberDetails = updatedAllocationObj.BlobberDetails
   853  	allocation.ReadPriceRange = updatedAllocationObj.ReadPriceRange
   854  	allocation.WritePriceRange = updatedAllocationObj.WritePriceRange
   855  	allocation.ChallengeCompletionTime = updatedAllocationObj.ChallengeCompletionTime
   856  	allocation.StartTime = updatedAllocationObj.StartTime
   857  	allocation.Finalized = updatedAllocationObj.Finalized
   858  	allocation.Canceled = updatedAllocationObj.Canceled
   859  	allocation.MovedToChallenge = updatedAllocationObj.MovedToChallenge
   860  	allocation.MovedBack = updatedAllocationObj.MovedBack
   861  	allocation.MovedToValidators = updatedAllocationObj.MovedToValidators
   862  	allocation.FileOptions = updatedAllocationObj.FileOptions
   863  	allocation.IsEnterprise = updatedAllocationObj.IsEnterprise
   864  	return nil
   865  }
   866  
   867  // SetNumBlockDownloads - set the number of block downloads, needs to be between 1 and 500 (inclusive). Default is 20.
   868  //   - num: the number of block downloads
   869  func SetNumBlockDownloads(num int) {
   870  	if num > 0 && num <= 500 {
   871  		numBlockDownloads = num
   872  	}
   873  }
   874  
   875  // GetAllocations - get all allocations for the current client
   876  //
   877  // returns the list of allocations and error if any
   878  func GetAllocations() ([]*Allocation, error) {
   879  	return GetAllocationsForClient(client.GetClientID())
   880  }
   881  
   882  func getAllocationsInternal(clientID string, limit, offset int) ([]*Allocation, error) {
   883  	params := make(map[string]string)
   884  	params["client"] = clientID
   885  	params["limit"] = fmt.Sprint(limit)
   886  	params["offset"] = fmt.Sprint(offset)
   887  	allocationsBytes, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/allocations", params, nil)
   888  	if err != nil {
   889  		return nil, errors.New("allocations_fetch_error", "Error fetching the allocations."+err.Error())
   890  	}
   891  	allocations := make([]*Allocation, 0)
   892  	err = json.Unmarshal(allocationsBytes, &allocations)
   893  	if err != nil {
   894  		return nil, errors.New("allocations_decode_error", "Error decoding the allocations."+err.Error())
   895  	}
   896  	return allocations, nil
   897  }
   898  
   899  // GetAllocationsForClient - get all allocations for given client id
   900  //
   901  //   - clientID: the client id
   902  //
   903  // returns the list of allocations and error if any
   904  func GetAllocationsForClient(clientID string) ([]*Allocation, error) {
   905  	if !sdkInitialized {
   906  		return nil, sdkNotInitialized
   907  	}
   908  	limit, offset := 20, 0
   909  
   910  	allocations, err := getAllocationsInternal(clientID, limit, offset)
   911  	if err != nil {
   912  		return nil, err
   913  	}
   914  
   915  	var allocationsFin []*Allocation
   916  	allocationsFin = append(allocationsFin, allocations...)
   917  	for {
   918  		// if the len of output returned is less than the limit it means this is the last round of pagination
   919  		if len(allocations) < limit {
   920  			break
   921  		}
   922  
   923  		// get the next set of blobbers
   924  		offset += 20
   925  		allocations, err = getAllocationsInternal(clientID, limit, offset)
   926  		if err != nil {
   927  			return allocations, err
   928  		}
   929  		allocationsFin = append(allocationsFin, allocations...)
   930  
   931  	}
   932  	return allocationsFin, nil
   933  }
   934  
   935  type FileOptionParam struct {
   936  	Changed bool
   937  	Value   bool
   938  }
   939  
   940  // FileOptionsParameters is used to specify the file options parameters for an allocation, which control the usage permissions of the files in the allocation.
   941  type FileOptionsParameters struct {
   942  	ForbidUpload FileOptionParam
   943  	ForbidDelete FileOptionParam
   944  	ForbidUpdate FileOptionParam
   945  	ForbidMove   FileOptionParam
   946  	ForbidCopy   FileOptionParam
   947  	ForbidRename FileOptionParam
   948  }
   949  
   950  // CreateAllocationOptions is used to specify the options for creating a new allocation.
   951  type CreateAllocationOptions struct {
   952  	DataShards           int
   953  	ParityShards         int
   954  	Size                 int64
   955  	ReadPrice            PriceRange
   956  	WritePrice           PriceRange
   957  	Lock                 uint64
   958  	BlobberIds           []string
   959  	BlobberAuthTickets   []string
   960  	ThirdPartyExtendable bool
   961  	IsEnterprise         bool
   962  	FileOptionsParams    *FileOptionsParameters
   963  	Force                bool
   964  }
   965  
   966  // CreateAllocationWith creates a new allocation with the given options for the current client using the SDK.
   967  // Similar ro CreateAllocationForOwner but uses an options struct instead of individual parameters.
   968  //   - options is the options struct instance for creating the allocation.
   969  //
   970  // returns the hash of the new_allocation_request transaction, the nonce of the transaction, the transaction object and an error if any.
   971  func CreateAllocationWith(options CreateAllocationOptions) (
   972  	string, int64, *transaction.Transaction, error) {
   973  
   974  	return CreateAllocationForOwner(client.GetClientID(),
   975  		client.GetClientPublicKey(), options.DataShards, options.ParityShards,
   976  		options.Size, options.ReadPrice, options.WritePrice, options.Lock,
   977  		options.BlobberIds, options.BlobberAuthTickets, options.ThirdPartyExtendable, options.IsEnterprise, options.Force, options.FileOptionsParams)
   978  }
   979  
   980  // GetAllocationBlobbers returns a list of blobber ids that can be used for a new allocation.
   981  //
   982  //   - datashards is the number of data shards for the allocation.
   983  //   - parityshards is the number of parity shards for the allocation.
   984  //   - size is the size of the allocation.
   985  //   - readPrice is the read price range for the allocation (Reads in Züs are free!).
   986  //   - writePrice is the write price range for the allocation.
   987  //   - force is a flag indicating whether to force the allocation to be created.
   988  //
   989  // returns the list of blobber ids and an error if any.
   990  func GetAllocationBlobbers(
   991  	datashards, parityshards int,
   992  	size int64,
   993  	isRestricted int,
   994  	readPrice, writePrice PriceRange,
   995  	force ...bool,
   996  ) ([]string, error) {
   997  	var allocationRequest = map[string]interface{}{
   998  		"data_shards":       datashards,
   999  		"parity_shards":     parityshards,
  1000  		"size":              size,
  1001  		"read_price_range":  readPrice,
  1002  		"write_price_range": writePrice,
  1003  		"is_restricted":     isRestricted,
  1004  	}
  1005  
  1006  	allocationData, _ := json.Marshal(allocationRequest)
  1007  
  1008  	params := make(map[string]string)
  1009  	params["allocation_data"] = string(allocationData)
  1010  	if len(force) > 0 && force[0] {
  1011  		params["force"] = strconv.FormatBool(force[0])
  1012  	}
  1013  
  1014  	allocBlobber, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/alloc_blobbers", params, nil)
  1015  	if err != nil {
  1016  		return nil, err
  1017  	}
  1018  	var allocBlobberIDs []string
  1019  
  1020  	err = json.Unmarshal(allocBlobber, &allocBlobberIDs)
  1021  	if err != nil {
  1022  		return nil, errors.Wrap(err, "failed to unmarshal blobber IDs")
  1023  	}
  1024  
  1025  	return allocBlobberIDs, nil
  1026  }
  1027  
  1028  func getNewAllocationBlobbers(
  1029  	datashards, parityshards int,
  1030  	size int64,
  1031  	readPrice, writePrice PriceRange,
  1032  	preferredBlobberIds, blobberAuthTickets []string, force bool,
  1033  ) (map[string]interface{}, error) {
  1034  	for _, authTicket := range blobberAuthTickets {
  1035  		if len(authTicket) > 0 {
  1036  			return map[string]interface{}{
  1037  				"data_shards":          datashards,
  1038  				"parity_shards":        parityshards,
  1039  				"size":                 size,
  1040  				"blobbers":             preferredBlobberIds,
  1041  				"blobber_auth_tickets": blobberAuthTickets,
  1042  				"read_price_range":     readPrice,
  1043  				"write_price_range":    writePrice,
  1044  			}, nil
  1045  		}
  1046  	}
  1047  
  1048  	allocBlobberIDs, err := GetAllocationBlobbers(
  1049  		datashards, parityshards, size, 2, readPrice, writePrice, force,
  1050  	)
  1051  	if err != nil {
  1052  		return nil, err
  1053  	}
  1054  
  1055  	blobbers := append(preferredBlobberIds, allocBlobberIDs...)
  1056  
  1057  	// filter duplicates
  1058  	ids := make(map[string]bool)
  1059  	uniqueBlobbers := []string{}
  1060  	uniqueBlobberAuthTickets := []string{}
  1061  
  1062  	for _, b := range blobbers {
  1063  		if !ids[b] {
  1064  			uniqueBlobbers = append(uniqueBlobbers, b)
  1065  			uniqueBlobberAuthTickets = append(uniqueBlobberAuthTickets, "")
  1066  			ids[b] = true
  1067  		}
  1068  	}
  1069  
  1070  	return map[string]interface{}{
  1071  		"data_shards":          datashards,
  1072  		"parity_shards":        parityshards,
  1073  		"size":                 size,
  1074  		"blobbers":             uniqueBlobbers,
  1075  		"blobber_auth_tickets": uniqueBlobberAuthTickets,
  1076  		"read_price_range":     readPrice,
  1077  		"write_price_range":    writePrice,
  1078  	}, nil
  1079  }
  1080  
  1081  // GetBlobberIds returns a list of blobber ids that can be used for a new allocation.
  1082  //
  1083  //   - blobberUrls is a list of blobber urls.
  1084  //
  1085  // returns a list of blobber ids that can be used for the new allocation and an error if any.
  1086  func GetBlobberIds(blobberUrls []string) ([]string, error) {
  1087  
  1088  	if len(blobberUrls) == 0 {
  1089  		return nil, nil
  1090  	}
  1091  
  1092  	urlsStr, err := json.Marshal(blobberUrls)
  1093  	if err != nil {
  1094  		return nil, err
  1095  	}
  1096  
  1097  	params := make(map[string]string)
  1098  	params["blobber_urls"] = string(urlsStr)
  1099  	idsStr, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/blobber_ids", params, nil)
  1100  	if err != nil {
  1101  		return nil, err
  1102  	}
  1103  
  1104  	var blobberIDs []string
  1105  	err = json.Unmarshal(idsStr, &blobberIDs)
  1106  	if err != nil {
  1107  		return nil, errors.Wrap(err, "failed to unmarshal preferred blobber IDs")
  1108  	}
  1109  
  1110  	return blobberIDs, nil
  1111  }
  1112  
  1113  // GetFreeAllocationBlobbers returns a list of blobber ids that can be used for a new free allocation.
  1114  //
  1115  //   - request is the request data for the free allocation.
  1116  //
  1117  // returns a list of blobber ids that can be used for the new free allocation and an error if any.
  1118  func GetFreeAllocationBlobbers(request map[string]interface{}) ([]string, error) {
  1119  	data, _ := json.Marshal(request)
  1120  
  1121  	params := make(map[string]string)
  1122  	params["free_allocation_data"] = string(data)
  1123  
  1124  	allocBlobber, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/free_alloc_blobbers", params, nil)
  1125  	if err != nil {
  1126  		return nil, err
  1127  	}
  1128  	var allocBlobberIDs []string
  1129  
  1130  	err = json.Unmarshal(allocBlobber, &allocBlobberIDs)
  1131  	if err != nil {
  1132  		return nil, errors.Wrap(err, "failed to unmarshal blobber IDs")
  1133  	}
  1134  
  1135  	return allocBlobberIDs, nil
  1136  }
  1137  
  1138  // AddFreeStorageAssigner adds a new free storage assigner (txn: `storagesc.add_free_allocation_assigner`).
  1139  // The free storage assigner is used to create free allocations. Can only be called by chain owner.
  1140  //
  1141  //   - name is the name of the assigner.
  1142  //   - publicKey is the public key of the assigner.
  1143  //   - individualLimit is the individual limit of the assigner for a single free allocation request
  1144  //   - totalLimit is the total limit of the assigner for all free allocation requests.
  1145  //
  1146  // returns the hash of the transaction, the nonce of the transaction and an error if any.
  1147  func AddFreeStorageAssigner(name, publicKey string, individualLimit, totalLimit float64) (string, int64, error) {
  1148  	if !sdkInitialized {
  1149  		return "", 0, sdkNotInitialized
  1150  	}
  1151  
  1152  	var input = map[string]interface{}{
  1153  		"name":             name,
  1154  		"public_key":       publicKey,
  1155  		"individual_limit": individualLimit,
  1156  		"total_limit":      totalLimit,
  1157  	}
  1158  
  1159  	var sn = transaction.SmartContractTxnData{
  1160  		Name:      transaction.ADD_FREE_ALLOCATION_ASSIGNER,
  1161  		InputArgs: input,
  1162  	}
  1163  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1164  
  1165  	return hash, n, err
  1166  }
  1167  
  1168  // FinalizeAllocation sends a finalize request for an allocation (txn: `storagesc.finalize_allocation`)
  1169  //
  1170  //   - allocID is the id of the allocation.
  1171  //
  1172  // returns the hash of the transaction, the nonce of the transaction and an error if any.
  1173  func FinalizeAllocation(allocID string) (hash string, nonce int64, err error) {
  1174  	if !sdkInitialized {
  1175  		return "", 0, sdkNotInitialized
  1176  	}
  1177  	var sn = transaction.SmartContractTxnData{
  1178  		Name:      transaction.STORAGESC_FINALIZE_ALLOCATION,
  1179  		InputArgs: map[string]interface{}{"allocation_id": allocID},
  1180  	}
  1181  	hash, _, nonce, _, err = storageSmartContractTxn(sn)
  1182  	return
  1183  }
  1184  
  1185  // CancelAllocation sends a cancel request for an allocation (txn: `storagesc.cancel_allocation`)
  1186  //
  1187  //   - allocID is the id of the allocation.
  1188  //
  1189  // returns the hash of the transaction, the nonce of the transaction and an error if any.
  1190  func CancelAllocation(allocID string) (hash string, nonce int64, err error) {
  1191  	if !sdkInitialized {
  1192  		return "", 0, sdkNotInitialized
  1193  	}
  1194  	var sn = transaction.SmartContractTxnData{
  1195  		Name:      transaction.STORAGESC_CANCEL_ALLOCATION,
  1196  		InputArgs: map[string]interface{}{"allocation_id": allocID},
  1197  	}
  1198  	hash, _, nonce, _, err = storageSmartContractTxn(sn)
  1199  	return
  1200  }
  1201  
  1202  // ProviderType is the type of the provider.
  1203  type ProviderType int
  1204  
  1205  const (
  1206  	ProviderMiner ProviderType = iota + 1
  1207  	ProviderSharder
  1208  	ProviderBlobber
  1209  	ProviderValidator
  1210  	ProviderAuthorizer
  1211  )
  1212  
  1213  // KillProvider kills a blobber or a validator (txn: `storagesc.kill_blobber` or `storagesc.kill_validator`)
  1214  //   - providerId is the id of the provider.
  1215  //   - providerType` is the type of the provider, either 3 for `ProviderBlobber` or 4 for `ProviderValidator.
  1216  func KillProvider(providerId string, providerType ProviderType) (string, int64, error) {
  1217  	if !sdkInitialized {
  1218  		return "", 0, sdkNotInitialized
  1219  	}
  1220  
  1221  	var input = map[string]interface{}{
  1222  		"provider_id": providerId,
  1223  	}
  1224  	var sn = transaction.SmartContractTxnData{
  1225  		InputArgs: input,
  1226  	}
  1227  	switch providerType {
  1228  	case ProviderBlobber:
  1229  		sn.Name = transaction.STORAGESC_KILL_BLOBBER
  1230  	case ProviderValidator:
  1231  		sn.Name = transaction.STORAGESC_KILL_VALIDATOR
  1232  	default:
  1233  		return "", 0, fmt.Errorf("kill provider type %v not implimented", providerType)
  1234  	}
  1235  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1236  	return hash, n, err
  1237  }
  1238  
  1239  // ShutdownProvider shuts down a blobber or a validator (txn: `storagesc.shutdown_blobber` or `storagesc.shutdown_validator`)
  1240  //   - providerId is the id of the provider.
  1241  //   - providerType` is the type of the provider, either 3 for `ProviderBlobber` or 4 for `ProviderValidator.
  1242  func ShutdownProvider(providerType ProviderType, providerID string) (string, int64, error) {
  1243  	if !sdkInitialized {
  1244  		return "", 0, sdkNotInitialized
  1245  	}
  1246  
  1247  	var input = map[string]interface{}{
  1248  		"provider_id": providerID,
  1249  	}
  1250  
  1251  	var sn = transaction.SmartContractTxnData{
  1252  		InputArgs: input,
  1253  	}
  1254  	switch providerType {
  1255  	case ProviderBlobber:
  1256  		sn.Name = transaction.STORAGESC_SHUTDOWN_BLOBBER
  1257  	case ProviderValidator:
  1258  		sn.Name = transaction.STORAGESC_SHUTDOWN_VALIDATOR
  1259  	default:
  1260  		return "", 0, fmt.Errorf("shutdown provider type %v not implimented", providerType)
  1261  	}
  1262  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1263  	return hash, n, err
  1264  }
  1265  
  1266  // CollectRewards collects the rewards for a provider (txn: `storagesc.collect_reward`)
  1267  //   - providerId is the id of the provider.
  1268  //   - providerType is the type of the provider.
  1269  func CollectRewards(providerId string, providerType ProviderType) (string, int64, error) {
  1270  	if !sdkInitialized {
  1271  		return "", 0, sdkNotInitialized
  1272  	}
  1273  
  1274  	var input = map[string]interface{}{
  1275  		"provider_id":   providerId,
  1276  		"provider_type": providerType,
  1277  	}
  1278  
  1279  	var sn = transaction.SmartContractTxnData{
  1280  		InputArgs: input,
  1281  	}
  1282  
  1283  	var scAddress string
  1284  	switch providerType {
  1285  	case ProviderBlobber, ProviderValidator:
  1286  		scAddress = STORAGE_SCADDRESS
  1287  		sn.Name = transaction.STORAGESC_COLLECT_REWARD
  1288  	case ProviderMiner, ProviderSharder:
  1289  		scAddress = MINERSC_SCADDRESS
  1290  		sn.Name = transaction.MINERSC_COLLECT_REWARD
  1291  	// case ProviderAuthorizer:
  1292  	// 	scAddress = ZCNSC_SCADDRESS
  1293  	// 	sn.Name = transaction.ZCNSC_COLLECT_REWARD
  1294  	default:
  1295  		return "", 0, fmt.Errorf("collect rewards provider type %v not implimented", providerType)
  1296  	}
  1297  
  1298  	hash, _, n, _, err := smartContractTxn(scAddress, sn)
  1299  	return hash, n, err
  1300  }
  1301  
  1302  // TransferAllocation transfers the ownership of an allocation to a new owner. (txn: `storagesc.update_allocation_request`)
  1303  //
  1304  //   - allocationId is the id of the allocation.
  1305  //   - newOwner is the client id of the new owner.
  1306  //   - newOwnerPublicKey is the public key of the new owner.
  1307  //
  1308  // returns the hash of the transaction, the nonce of the transaction and an error if any.
  1309  func TransferAllocation(allocationId, newOwner, newOwnerPublicKey string) (string, int64, error) {
  1310  	if !sdkInitialized {
  1311  		return "", 0, sdkNotInitialized
  1312  	}
  1313  
  1314  	alloc, err := GetAllocation(allocationId)
  1315  	if err != nil {
  1316  		return "", 0, allocationNotFound
  1317  	}
  1318  
  1319  	var allocationRequest = map[string]interface{}{
  1320  		"id":                         allocationId,
  1321  		"owner_id":                   newOwner,
  1322  		"owner_public_key":           newOwnerPublicKey,
  1323  		"size":                       0,
  1324  		"expiration_date":            0,
  1325  		"update_terms":               false,
  1326  		"add_blobber_id":             "",
  1327  		"remove_blobber_id":          "",
  1328  		"set_third_party_extendable": alloc.ThirdPartyExtendable,
  1329  		"file_options_changed":       false,
  1330  		"file_options":               alloc.FileOptions,
  1331  	}
  1332  	var sn = transaction.SmartContractTxnData{
  1333  		Name:      transaction.STORAGESC_UPDATE_ALLOCATION,
  1334  		InputArgs: allocationRequest,
  1335  	}
  1336  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1337  	return hash, n, err
  1338  }
  1339  
  1340  // UpdateBlobberSettings updates the settings of a blobber (txn: `storagesc.update_blobber_settings`)
  1341  //   - blob is the update blobber request inputs.
  1342  func UpdateBlobberSettings(blob *UpdateBlobber) (resp string, nonce int64, err error) {
  1343  	if !sdkInitialized {
  1344  		return "", 0, sdkNotInitialized
  1345  	}
  1346  	var sn = transaction.SmartContractTxnData{
  1347  		Name:      transaction.STORAGESC_UPDATE_BLOBBER_SETTINGS,
  1348  		InputArgs: blob,
  1349  	}
  1350  	resp, _, nonce, _, err = storageSmartContractTxn(sn)
  1351  	return
  1352  }
  1353  
  1354  // UpdateValidatorSettings updates the settings of a validator (txn: `storagesc.update_validator_settings`)
  1355  //   - v is the update validator request inputs.
  1356  func UpdateValidatorSettings(v *UpdateValidator) (resp string, nonce int64, err error) {
  1357  	if !sdkInitialized {
  1358  		return "", 0, sdkNotInitialized
  1359  	}
  1360  
  1361  	var sn = transaction.SmartContractTxnData{
  1362  		Name:      transaction.STORAGESC_UPDATE_VALIDATOR_SETTINGS,
  1363  		InputArgs: v.ConvertToValidationNode(),
  1364  	}
  1365  	resp, _, nonce, _, err = storageSmartContractTxn(sn)
  1366  	return
  1367  }
  1368  
  1369  // ResetBlobberStats resets the stats of a blobber (txn: `storagesc.reset_blobber_stats`)
  1370  //   - rbs is the reset blobber stats dto, contains the blobber id and its stats.
  1371  func ResetBlobberStats(rbs *ResetBlobberStatsDto) (string, int64, error) {
  1372  	if !sdkInitialized {
  1373  		return "", 0, sdkNotInitialized
  1374  	}
  1375  
  1376  	var sn = transaction.SmartContractTxnData{
  1377  		Name:      transaction.STORAGESC_RESET_BLOBBER_STATS,
  1378  		InputArgs: rbs,
  1379  	}
  1380  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1381  	return hash, n, err
  1382  }
  1383  
  1384  func ResetAllocationStats(allocationId string) (string, int64, error) {
  1385  	if !sdkInitialized {
  1386  		return "", 0, sdkNotInitialized
  1387  	}
  1388  
  1389  	var sn = transaction.SmartContractTxnData{
  1390  		Name:      transaction.STORAGESC_RESET_ALLOCATION_STATS,
  1391  		InputArgs: allocationId,
  1392  	}
  1393  	hash, _, n, _, err := storageSmartContractTxn(sn)
  1394  	return hash, n, err
  1395  }
  1396  
  1397  func CommitToFabric(metaTxnData, fabricConfigJSON string) (string, error) {
  1398  	if !sdkInitialized {
  1399  		return "", sdkNotInitialized
  1400  	}
  1401  	var fabricConfig struct {
  1402  		URL  string `json:"url"`
  1403  		Body struct {
  1404  			Channel          string   `json:"channel"`
  1405  			ChaincodeName    string   `json:"chaincode_name"`
  1406  			ChaincodeVersion string   `json:"chaincode_version"`
  1407  			Method           string   `json:"method"`
  1408  			Args             []string `json:"args"`
  1409  		} `json:"body"`
  1410  		Auth struct {
  1411  			Username string `json:"username"`
  1412  			Password string `json:"password"`
  1413  		} `json:"auth"`
  1414  	}
  1415  
  1416  	err := json.Unmarshal([]byte(fabricConfigJSON), &fabricConfig)
  1417  	if err != nil {
  1418  		return "", errors.New("fabric_config_decode_error", "Unable to decode fabric config json")
  1419  	}
  1420  
  1421  	// Clear if any existing args passed
  1422  	fabricConfig.Body.Args = fabricConfig.Body.Args[:0]
  1423  
  1424  	fabricConfig.Body.Args = append(fabricConfig.Body.Args, metaTxnData)
  1425  
  1426  	fabricData, err := json.Marshal(fabricConfig.Body)
  1427  	if err != nil {
  1428  		return "", errors.New("fabric_config_encode_error", "Unable to encode fabric config body")
  1429  	}
  1430  
  1431  	req, ctx, cncl, err := zboxutil.NewHTTPRequest(http.MethodPost, fabricConfig.URL, fabricData)
  1432  	if err != nil {
  1433  		return "", errors.New("fabric_commit_error", "Unable to create new http request with error "+err.Error())
  1434  	}
  1435  
  1436  	// Set basic auth
  1437  	req.SetBasicAuth(fabricConfig.Auth.Username, fabricConfig.Auth.Password)
  1438  
  1439  	var fabricResponse string
  1440  	err = zboxutil.HttpDo(ctx, cncl, req, func(resp *http.Response, err error) error {
  1441  		if err != nil {
  1442  			l.Logger.Error("Fabric commit error : ", err)
  1443  			return err
  1444  		}
  1445  		defer resp.Body.Close()
  1446  		respBody, err := ioutil.ReadAll(resp.Body)
  1447  		if err != nil {
  1448  			return errors.Wrap(err, "Error reading response :")
  1449  		}
  1450  		l.Logger.Debug("Fabric commit result:", string(respBody))
  1451  		if resp.StatusCode == http.StatusOK {
  1452  			fabricResponse = string(respBody)
  1453  			return nil
  1454  		}
  1455  		return errors.New(strconv.Itoa(resp.StatusCode), "Fabric commit status not OK!")
  1456  	})
  1457  	return fabricResponse, err
  1458  }
  1459  
  1460  // GetAllocationMinLock calculates and returns the minimum lock demand for creating a new allocation, which represents the cost of the creation process.
  1461  //   - datashards is the number of data shards for the allocation.
  1462  //   - parityshards is the number of parity shards for the allocation.
  1463  //   - size is the size of the allocation.
  1464  //   - writePrice is the write price range for the allocation.
  1465  //
  1466  // returns the minimum lock demand for the creation process and an error if any.
  1467  func GetAllocationMinLock(
  1468  	datashards, parityshards int,
  1469  	size int64,
  1470  	writePrice PriceRange,
  1471  ) (int64, error) {
  1472  	baSize := int64(math.Ceil(float64(size) / float64(datashards)))
  1473  	totalSize := baSize * int64(datashards+parityshards)
  1474  
  1475  	sizeInGB := float64(totalSize) / GB
  1476  
  1477  	cost := sizeInGB * float64(writePrice.Max)
  1478  	coin, err := currency.Float64ToCoin(cost)
  1479  	if err != nil {
  1480  		return 0, err
  1481  	}
  1482  	i, err := coin.Int64()
  1483  	if err != nil {
  1484  		return 0, err
  1485  	}
  1486  	return i, nil
  1487  }
  1488  
  1489  // GetUpdateAllocationMinLock returns the minimum lock demand for updating an allocation, which represents the cost of the update operation.
  1490  //
  1491  //   - allocationID is the id of the allocation.
  1492  //   - size is the new size of the allocation.
  1493  //   - extend is a flag indicating whether to extend the expiry of the allocation.
  1494  //   - addBlobberId is the id of the blobber to add to the allocation.
  1495  //   - removeBlobberId is the id of the blobber to remove from the allocation.
  1496  //
  1497  // returns the minimum lock demand for the update operation and an error if any.
  1498  func GetUpdateAllocationMinLock(
  1499  	allocationID string,
  1500  	size int64,
  1501  	extend bool,
  1502  	addBlobberId,
  1503  	removeBlobberId string) (int64, error) {
  1504  	updateAllocationRequest := make(map[string]interface{})
  1505  	updateAllocationRequest["owner_id"] = client.GetClientID()
  1506  	updateAllocationRequest["owner_public_key"] = ""
  1507  	updateAllocationRequest["id"] = allocationID
  1508  	updateAllocationRequest["size"] = size
  1509  	updateAllocationRequest["extend"] = extend
  1510  	updateAllocationRequest["add_blobber_id"] = addBlobberId
  1511  	updateAllocationRequest["remove_blobber_id"] = removeBlobberId
  1512  
  1513  	data, err := json.Marshal(updateAllocationRequest)
  1514  	if err != nil {
  1515  		return 0, errors.Wrap(err, "failed to encode request into json")
  1516  	}
  1517  
  1518  	params := make(map[string]string)
  1519  	params["data"] = string(data)
  1520  
  1521  	responseBytes, err := zboxutil.MakeSCRestAPICall(STORAGE_SCADDRESS, "/allocation-update-min-lock", params, nil)
  1522  	if err != nil {
  1523  		return 0, errors.Wrap(err, "failed to request allocation update min lock")
  1524  	}
  1525  
  1526  	var response = make(map[string]int64)
  1527  	if err = json.Unmarshal(responseBytes, &response); err != nil {
  1528  		return 0, errors.Wrap(err, fmt.Sprintf("failed to decode response: %s", string(responseBytes)))
  1529  	}
  1530  
  1531  	v, ok := response["min_lock_demand"]
  1532  	if !ok {
  1533  		return 0, errors.New("", "min_lock_demand not found in response")
  1534  	}
  1535  	return v, nil
  1536  }
  1537  
  1538  // calculateAllocationFileOptions calculates the FileOptions 16-bit mask given the user input
  1539  func calculateAllocationFileOptions(initial uint16, fop *FileOptionsParameters) (bool, uint16) {
  1540  	if fop == nil {
  1541  		return false, initial
  1542  	}
  1543  
  1544  	mask := initial
  1545  
  1546  	if fop.ForbidUpload.Changed {
  1547  		mask = updateMaskBit(mask, 0, !fop.ForbidUpload.Value)
  1548  	}
  1549  
  1550  	if fop.ForbidDelete.Changed {
  1551  		mask = updateMaskBit(mask, 1, !fop.ForbidDelete.Value)
  1552  	}
  1553  
  1554  	if fop.ForbidUpdate.Changed {
  1555  		mask = updateMaskBit(mask, 2, !fop.ForbidUpdate.Value)
  1556  	}
  1557  
  1558  	if fop.ForbidMove.Changed {
  1559  		mask = updateMaskBit(mask, 3, !fop.ForbidMove.Value)
  1560  	}
  1561  
  1562  	if fop.ForbidCopy.Changed {
  1563  		mask = updateMaskBit(mask, 4, !fop.ForbidCopy.Value)
  1564  	}
  1565  
  1566  	if fop.ForbidRename.Changed {
  1567  		mask = updateMaskBit(mask, 5, !fop.ForbidRename.Value)
  1568  	}
  1569  
  1570  	return mask != initial, mask
  1571  }
  1572  
  1573  // updateMaskBit Set/Clear (based on `value`) bit value of the bit of `mask` at `index` (starting with LSB as 0) and return the updated mask
  1574  func updateMaskBit(mask uint16, index uint8, value bool) uint16 {
  1575  	if value {
  1576  		return mask | uint16(1<<index)
  1577  	} else {
  1578  		return mask & ^uint16(1<<index)
  1579  	}
  1580  }