github.com/0chain/gosdk@v1.17.11/zcncore/transaction_mobile.go (about)

     1  //go:build mobile
     2  // +build mobile
     3  
     4  package zcncore
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	stderrors "errors"
    10  	"fmt"
    11  	"net/http"
    12  	"strconv"
    13  	"time"
    14  
    15  	"github.com/0chain/errors"
    16  	"github.com/0chain/gosdk/core/block"
    17  	"github.com/0chain/gosdk/core/common"
    18  	"github.com/0chain/gosdk/core/encryption"
    19  	"github.com/0chain/gosdk/core/node"
    20  	"github.com/0chain/gosdk/core/transaction"
    21  	"github.com/0chain/gosdk/core/util"
    22  	"github.com/0chain/gosdk/core/zcncrypto"
    23  )
    24  
    25  const (
    26  	Undefined int = iota
    27  	Success
    28  
    29  	// ChargeableError is an error that still charges the user for the transaction.
    30  	ChargeableError
    31  )
    32  
    33  // Provider represents the type of provider.
    34  type Provider int
    35  
    36  const (
    37  	ProviderMiner Provider = iota + 1
    38  	ProviderSharder
    39  	ProviderBlobber
    40  	ProviderValidator
    41  	ProviderAuthorizer
    42  )
    43  
    44  type stakePoolRequest struct {
    45  	ProviderType int    `json:"provider_type,omitempty"`
    46  	ProviderID   string `json:"provider_id,omitempty"`
    47  }
    48  
    49  // compiler time check
    50  var (
    51  	_ TransactionScheme = (*Transaction)(nil)
    52  	_ TransactionScheme = (*TransactionWithAuth)(nil)
    53  )
    54  
    55  type TransactionCommon interface {
    56  	ExecuteSmartContract(address string, methodName string, input string, val string) error
    57  
    58  	Send(toClientID string, val string, desc string) error
    59  
    60  	VestingAdd(ar VestingAddRequest, value string) error
    61  
    62  	MinerSCLock(providerId string, providerType int, lock string) error
    63  	MinerSCUnlock(providerId string, providerType int) error
    64  	MinerSCCollectReward(providerId string, providerType int) error
    65  	StorageSCCollectReward(providerId string, providerType int) error
    66  
    67  	FinalizeAllocation(allocID string) error
    68  	CancelAllocation(allocID string) error
    69  	CreateAllocation(car *CreateAllocationRequest, lock string) error
    70  	CreateReadPool() error
    71  	ReadPoolLock(allocID string, blobberID string, duration int64, lock string) error
    72  	ReadPoolUnlock() error
    73  	StakePoolLock(providerId string, providerType int, lock string) error
    74  	StakePoolUnlock(providerId string, providerType int) error
    75  	UpdateBlobberSettings(blobber Blobber) error
    76  	UpdateAllocation(allocID string, sizeDiff int64, expirationDiff int64, lock string) error
    77  	WritePoolLock(allocID string, lock string) error
    78  	WritePoolUnlock(allocID string) error
    79  
    80  	VestingUpdateConfig(InputMap) error
    81  	MinerScUpdateConfig(InputMap) error
    82  	MinerScUpdateGlobals(InputMap) error
    83  	StorageScUpdateConfig(InputMap) error
    84  	FaucetUpdateConfig(InputMap) error
    85  	ZCNSCUpdateGlobalConfig(InputMap) error
    86  
    87  	MinerSCMinerSettings(MinerSCMinerInfo) error
    88  	MinerSCSharderSettings(MinerSCMinerInfo) error
    89  	MinerSCDeleteMiner(MinerSCMinerInfo) error
    90  	MinerSCDeleteSharder(MinerSCMinerInfo) error
    91  
    92  	// ZCNSCUpdateAuthorizerConfig updates authorizer config by ID
    93  	ZCNSCUpdateAuthorizerConfig(AuthorizerNode) error
    94  	// ZCNSCAddAuthorizer adds authorizer
    95  	ZCNSCAddAuthorizer(AddAuthorizerPayload) error
    96  
    97  	GetVerifyConfirmationStatus() int
    98  }
    99  
   100  // TransactionScheme implements few methods for block chain.
   101  //
   102  // Note: to be buildable on MacOSX all arguments should have names.
   103  type TransactionScheme interface {
   104  	TransactionCommon
   105  	// SetTransactionCallback implements storing the callback
   106  	// used to call after the transaction or verification is completed
   107  	SetTransactionCallback(cb TransactionCallback) error
   108  	// StoreData implements store the data to blockchain
   109  	StoreData(data string) error
   110  	// ExecuteFaucetSCWallet implements the `Faucet Smart contract` for a given wallet
   111  	ExecuteFaucetSCWallet(walletStr string, methodName string, input []byte) error
   112  	// GetTransactionHash implements retrieval of hash of the submitted transaction
   113  	GetTransactionHash() string
   114  	// SetTransactionHash implements verify a previous transaction status
   115  	SetTransactionHash(hash string) error
   116  	// SetTransactionNonce implements method to set the transaction nonce
   117  	SetTransactionNonce(txnNonce int64) error
   118  	// Verify implements verify the transaction
   119  	Verify() error
   120  	// GetVerifyOutput implements the verification output from sharders
   121  	GetVerifyOutput() string
   122  	// GetTransactionError implements error string in case of transaction failure
   123  	GetTransactionError() string
   124  	// GetVerifyError implements error string in case of verify failure error
   125  	GetVerifyError() string
   126  	// GetTransactionNonce returns nonce
   127  	GetTransactionNonce() int64
   128  
   129  	// GetDetails returns transaction scheme details.
   130  	GetDetails() *transaction.Transaction
   131  
   132  	// Output of transaction.
   133  	Output() []byte
   134  
   135  	// Hash Transaction status regardless of status
   136  	Hash() string
   137  
   138  	// Vesting SC
   139  
   140  	VestingTrigger(poolID string) error
   141  	VestingStop(sr *VestingStopRequest) error
   142  	VestingUnlock(poolID string) error
   143  	VestingDelete(poolID string) error
   144  }
   145  
   146  // priceRange represents a price range allowed by user to filter blobbers.
   147  type priceRange struct {
   148  	Min int64 `json:"min"`
   149  	Max int64 `json:"max"`
   150  }
   151  
   152  // createAllocationRequest is information to create allocation.
   153  type createAllocationRequest struct {
   154  	DataShards      int        `json:"data_shards"`
   155  	ParityShards    int        `json:"parity_shards"`
   156  	Size            int64      `json:"size"`
   157  	Expiration      int64      `json:"expiration_date"`
   158  	Owner           string     `json:"owner_id"`
   159  	OwnerPublicKey  string     `json:"owner_public_key"`
   160  	Blobbers        []string   `json:"blobbers"`
   161  	ReadPriceRange  priceRange `json:"read_price_range"`
   162  	WritePriceRange priceRange `json:"write_price_range"`
   163  }
   164  
   165  type CreateAllocationRequest struct {
   166  	DataShards     int
   167  	ParityShards   int
   168  	Size           int64
   169  	Expiration     int64
   170  	Owner          string
   171  	OwnerPublicKey string
   172  	ReadPriceMin   int64
   173  	ReadPriceMax   int64
   174  	WritePriceMin  int64
   175  	WritePriceMax  int64
   176  
   177  	blobbers []string
   178  }
   179  
   180  func (car *CreateAllocationRequest) AddBlobber(blobber string) {
   181  	car.blobbers = append(car.blobbers, blobber)
   182  }
   183  
   184  func (car *CreateAllocationRequest) toCreateAllocationSCInput() *createAllocationRequest {
   185  	return &createAllocationRequest{
   186  		DataShards:      car.DataShards,
   187  		ParityShards:    car.ParityShards,
   188  		Size:            car.Size,
   189  		Expiration:      car.Expiration,
   190  		Owner:           car.Owner,
   191  		OwnerPublicKey:  car.OwnerPublicKey,
   192  		Blobbers:        car.blobbers,
   193  		ReadPriceRange:  priceRange{Min: car.ReadPriceMin, Max: car.ReadPriceMax},
   194  		WritePriceRange: priceRange{Min: car.WritePriceMin, Max: car.WritePriceMax},
   195  	}
   196  }
   197  
   198  type StakePoolSettings struct {
   199  	DelegateWallet string  `json:"delegate_wallet"`
   200  	NumDelegates   int     `json:"num_delegates"`
   201  	ServiceCharge  float64 `json:"service_charge"`
   202  }
   203  
   204  type Terms struct {
   205  	ReadPrice        int64 `json:"read_price"`  // tokens / GB
   206  	WritePrice       int64 `json:"write_price"` // tokens / GB
   207  	MaxOfferDuration int64 `json:"max_offer_duration"`
   208  }
   209  
   210  type Blobber interface {
   211  	SetTerms(readPrice int64, writePrice int64, minLockDemand float64, maxOfferDuration int64)
   212  	SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64)
   213  	SetAvailable(bool)
   214  }
   215  
   216  func NewBlobber(id, baseUrl string, capacity, allocated, lastHealthCheck int64) Blobber {
   217  	return &blobber{
   218  		ID:              id,
   219  		BaseURL:         baseUrl,
   220  		Capacity:        capacity,
   221  		Allocated:       allocated,
   222  		LastHealthCheck: lastHealthCheck,
   223  	}
   224  }
   225  
   226  type blobber struct {
   227  	ID                string            `json:"id"`
   228  	BaseURL           string            `json:"url"`
   229  	Capacity          int64             `json:"capacity"`
   230  	Allocated         int64             `json:"allocated"`
   231  	LastHealthCheck   int64             `json:"last_health_check"`
   232  	Terms             Terms             `json:"terms"`
   233  	StakePoolSettings StakePoolSettings `json:"stake_pool_settings"`
   234  	NotAvailable      bool              `json:"not_available"`
   235  }
   236  
   237  func (b *blobber) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) {
   238  	b.StakePoolSettings = StakePoolSettings{
   239  		DelegateWallet: delegateWallet,
   240  		NumDelegates:   numDelegates,
   241  		ServiceCharge:  serviceCharge,
   242  	}
   243  }
   244  
   245  func (b *blobber) SetTerms(readPrice int64, writePrice int64, minLockDemand float64, maxOfferDuration int64) {
   246  	b.Terms = Terms{
   247  		ReadPrice:        readPrice,
   248  		WritePrice:       writePrice,
   249  		MaxOfferDuration: maxOfferDuration,
   250  	}
   251  }
   252  
   253  func (b *blobber) SetAvailable(availability bool) {
   254  	b.NotAvailable = availability
   255  }
   256  
   257  type Validator interface {
   258  	SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64)
   259  }
   260  
   261  func NewValidator(id string, baseUrl string) Validator {
   262  	return &validator{
   263  		ID:      common.Key(id),
   264  		BaseURL: baseUrl,
   265  	}
   266  }
   267  
   268  type validator struct {
   269  	ID                common.Key        `json:"id"`
   270  	BaseURL           string            `json:"url"`
   271  	StakePoolSettings StakePoolSettings `json:"stake_pool_settings"`
   272  }
   273  
   274  func (v *validator) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) {
   275  	v.StakePoolSettings = StakePoolSettings{
   276  		DelegateWallet: delegateWallet,
   277  		NumDelegates:   numDelegates,
   278  		ServiceCharge:  serviceCharge,
   279  	}
   280  }
   281  
   282  // AddAuthorizerPayload is the interface gathering the functions to add a new authorizer.
   283  type AddAuthorizerPayload interface {
   284  	// SetStakePoolSettings sets the stake pool settings for the authorizer.
   285  	SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64)
   286  }
   287  
   288  // NewAddAuthorizerPayload creates a new AddAuthorizerPayload concrete instance.
   289  func NewAddAuthorizerPayload(pubKey, url string) AddAuthorizerPayload {
   290  	return &addAuthorizerPayload{
   291  		PublicKey: pubKey,
   292  		URL:       url,
   293  	}
   294  }
   295  
   296  type addAuthorizerPayload struct {
   297  	PublicKey         string                      `json:"public_key"`
   298  	URL               string                      `json:"url"`
   299  	StakePoolSettings AuthorizerStakePoolSettings `json:"stake_pool_settings"` // Used to initially create stake pool
   300  }
   301  
   302  // SetStakePoolSettings sets the stake pool settings for the authorizer.
   303  func (a *addAuthorizerPayload) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) {
   304  	a.StakePoolSettings = AuthorizerStakePoolSettings{
   305  		DelegateWallet: delegateWallet,
   306  		NumDelegates:   numDelegates,
   307  		ServiceCharge:  serviceCharge,
   308  	}
   309  }
   310  
   311  type AuthorizerHealthCheckPayload struct {
   312  	ID string `json:"id"` // authorizer ID
   313  }
   314  
   315  // AuthorizerStakePoolSettings represents configuration of an authorizer stake pool.
   316  type AuthorizerStakePoolSettings struct {
   317  	DelegateWallet string  `json:"delegate_wallet"`
   318  	NumDelegates   int     `json:"num_delegates"`
   319  	ServiceCharge  float64 `json:"service_charge"`
   320  }
   321  
   322  // AuthorizerConfig represents configuration of an authorizer node.
   323  type AuthorizerConfig struct {
   324  	Fee int64 `json:"fee"`
   325  }
   326  
   327  type VestingDest struct {
   328  	ID     string `json:"id"`     // destination ID
   329  	Amount int64  `json:"amount"` // amount to vest for the destination
   330  }
   331  
   332  type VestingAddRequest interface {
   333  	AddDestinations(id string, amount int64)
   334  }
   335  
   336  func NewVestingAddRequest(desc string, startTime int64, duration int64) VestingAddRequest {
   337  	return &vestingAddRequest{
   338  		Description: desc,
   339  		StartTime:   startTime,
   340  		Duration:    duration,
   341  	}
   342  }
   343  
   344  type vestingAddRequest struct {
   345  	Description  string         `json:"description"`  // allow empty
   346  	StartTime    int64          `json:"start_time"`   //
   347  	Duration     int64          `json:"duration"`     //
   348  	Destinations []*VestingDest `json:"destinations"` //
   349  }
   350  
   351  func (vr *vestingAddRequest) AddDestinations(id string, amount int64) {
   352  	vr.Destinations = append(vr.Destinations, &VestingDest{ID: id, Amount: amount})
   353  }
   354  
   355  // InputMap represents an interface of functions to add fields to a map.
   356  type InputMap interface {
   357  	// AddField adds a field to the map.
   358  	// 		- key: field key
   359  	// 		- value: field value
   360  	AddField(key, value string)
   361  }
   362  
   363  type inputMap struct {
   364  	Fields map[string]string `json:"fields"`
   365  }
   366  
   367  // NewInputMap creates a new InputMap concrete instance.
   368  func NewInputMap() InputMap {
   369  	return &inputMap{
   370  		Fields: make(map[string]string),
   371  	}
   372  }
   373  
   374  func (im *inputMap) AddField(key, value string) {
   375  	im.Fields[key] = value
   376  }
   377  
   378  func parseCoinStr(vs string) (uint64, error) {
   379  	if vs == "" {
   380  		return 0, nil
   381  	}
   382  
   383  	v, err := strconv.ParseUint(vs, 10, 64)
   384  	if err != nil {
   385  		return 0, fmt.Errorf("invalid token value: %v, err: %v", vs, err)
   386  	}
   387  
   388  	return v, nil
   389  }
   390  
   391  func newTransaction(cb TransactionCallback, txnFee string, nonce int64) (*Transaction, error) {
   392  	t := &Transaction{}
   393  	t.txn = transaction.NewTransactionEntity(_config.wallet.ClientID, _config.chain.ChainID, _config.wallet.ClientKey, nonce)
   394  	t.txnStatus, t.verifyStatus = StatusUnknown, StatusUnknown
   395  	t.txnCb = cb
   396  	t.txn.TransactionNonce = nonce
   397  	t.txn.TransactionFee = txnFee
   398  	return t, nil
   399  }
   400  
   401  // NewTransaction new generic transaction object for any operation
   402  //   - cb: callback for transaction state
   403  //   - txnFee: Transaction fees (in SAS tokens)
   404  //   - nonce: latest nonce of current wallet. please set it with 0 if you don't know the latest value
   405  func NewTransaction(cb TransactionCallback, txnFee string, nonce int64) (TransactionScheme, error) {
   406  	err := CheckConfig()
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  
   411  	if _config.isSplitWallet {
   412  		if _config.authUrl == "" {
   413  			return nil, errors.New("", "auth url not set")
   414  		}
   415  		logging.Info("New transaction interface with auth")
   416  		return newTransactionWithAuth(cb, txnFee, nonce)
   417  	}
   418  
   419  	logging.Info("New transaction interface")
   420  	t, err := newTransaction(cb, txnFee, nonce)
   421  	return t, err
   422  }
   423  
   424  func (t *Transaction) GetDetails() *transaction.Transaction {
   425  	return t.txn
   426  }
   427  
   428  func (t *Transaction) createSmartContractTxn(address, methodName string, input interface{}, value string, opts ...FeeOption) error {
   429  	sn := transaction.SmartContractTxnData{Name: methodName, InputArgs: input}
   430  	snBytes, err := json.Marshal(sn)
   431  	if err != nil {
   432  		return errors.Wrap(err, "create smart contract failed due to invalid data")
   433  	}
   434  
   435  	t.txn.TransactionType = transaction.TxnTypeSmartContract
   436  	t.txn.ToClientID = address
   437  	t.txn.TransactionData = string(snBytes)
   438  	t.txn.Value = value
   439  
   440  	transactionFeeRaw, err := strconv.ParseUint(t.txn.TransactionFee, 0, 64)
   441  	if err != nil {
   442  		return err
   443  	}
   444  
   445  	if transactionFeeRaw > 0 {
   446  		return nil
   447  	}
   448  
   449  	tf := &TxnFeeOption{}
   450  	for _, opt := range opts {
   451  		opt(tf)
   452  	}
   453  
   454  	if tf.noEstimateFee {
   455  		return nil
   456  	}
   457  
   458  	// TODO: check if transaction is exempt to avoid unnecessary fee estimation
   459  	minFee, err := transaction.EstimateFee(t.txn, _config.chain.Miners, 0.2)
   460  	if err != nil {
   461  		return err
   462  	}
   463  
   464  	t.txn.TransactionFee = strconv.FormatUint(minFee, 10)
   465  
   466  	return nil
   467  }
   468  
   469  func (t *Transaction) createFaucetSCWallet(walletStr string, methodName string, input []byte) (*zcncrypto.Wallet, error) {
   470  	w, err := getWallet(walletStr)
   471  	if err != nil {
   472  		fmt.Printf("Error while parsing the wallet. %v\n", err)
   473  		return nil, err
   474  	}
   475  	err = t.createSmartContractTxn(FaucetSmartContractAddress, methodName, input, "0")
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  	return w, nil
   480  }
   481  
   482  // ExecuteSmartContract prepare and send a smart contract transaction to the blockchain
   483  func (t *Transaction) ExecuteSmartContract(address string, methodName string, input string, val string) error {
   484  	err := t.createSmartContractTxn(address, methodName, input, val)
   485  	if err != nil {
   486  		return err
   487  	}
   488  	go func() {
   489  		t.setNonceAndSubmit()
   490  	}()
   491  
   492  	return nil
   493  }
   494  
   495  // Send to send a transaction to a given clientID
   496  func (t *Transaction) Send(toClientID string, val string, desc string) error {
   497  	txnData, err := json.Marshal(SendTxnData{Note: desc})
   498  	if err != nil {
   499  		return errors.New("", "Could not serialize description to transaction_data")
   500  	}
   501  	go func() {
   502  		t.txn.TransactionType = transaction.TxnTypeSend
   503  		t.txn.ToClientID = toClientID
   504  		t.txn.Value = val
   505  		t.txn.TransactionData = string(txnData)
   506  		t.setNonceAndSubmit()
   507  	}()
   508  	return nil
   509  }
   510  
   511  // SendWithSignatureHash to send a transaction to a given clientID with a signature hash
   512  //   - toClientID: client ID in the To field of the transaction
   513  //   - val: amount of tokens to send
   514  //   - desc: description of the transaction
   515  //   - sig: signature hash
   516  //   - CreationDate: creation date of the transaction
   517  //   - hash: hash of the transaction
   518  func (t *Transaction) SendWithSignatureHash(toClientID string, val string, desc string, sig string, CreationDate int64, hash string) error {
   519  	txnData, err := json.Marshal(SendTxnData{Note: desc})
   520  	if err != nil {
   521  		return errors.New("", "Could not serialize description to transaction_data")
   522  	}
   523  	go func() {
   524  		t.txn.TransactionType = transaction.TxnTypeSend
   525  		t.txn.ToClientID = toClientID
   526  		t.txn.Value = val
   527  		t.txn.Hash = hash
   528  		t.txn.TransactionData = string(txnData)
   529  		t.txn.Signature = sig
   530  		t.txn.CreationDate = CreationDate
   531  		t.setNonceAndSubmit()
   532  	}()
   533  	return nil
   534  }
   535  
   536  func (t *Transaction) VestingAdd(ar VestingAddRequest, value string) (
   537  	err error) {
   538  	err = t.createSmartContractTxn(VestingSmartContractAddress,
   539  		transaction.VESTING_ADD, ar, value)
   540  	if err != nil {
   541  		logging.Error(err)
   542  		return
   543  	}
   544  	go func() { t.setNonceAndSubmit() }()
   545  	return
   546  }
   547  
   548  func (t *Transaction) VestingStop(sr *VestingStopRequest) (err error) {
   549  	err = t.createSmartContractTxn(VestingSmartContractAddress,
   550  		transaction.VESTING_STOP, sr, "0")
   551  	if err != nil {
   552  		logging.Error(err)
   553  		return
   554  	}
   555  	go func() { t.setNonceAndSubmit() }()
   556  	return
   557  }
   558  
   559  func (t *Transaction) vestingPoolTxn(function string, poolID string, value string) error {
   560  
   561  	return t.createSmartContractTxn(VestingSmartContractAddress,
   562  		function, vestingRequest{PoolID: common.Key(poolID)}, value)
   563  }
   564  
   565  func (t *Transaction) VestingTrigger(poolID string) (err error) {
   566  	err = t.vestingPoolTxn(transaction.VESTING_TRIGGER, poolID, "0")
   567  	if err != nil {
   568  		logging.Error(err)
   569  		return
   570  	}
   571  	go func() { t.setNonceAndSubmit() }()
   572  	return
   573  }
   574  
   575  func (t *Transaction) VestingUnlock(poolID string) (err error) {
   576  	err = t.vestingPoolTxn(transaction.VESTING_UNLOCK, poolID, "0")
   577  	if err != nil {
   578  		logging.Error(err)
   579  		return
   580  	}
   581  	go func() { t.setNonceAndSubmit() }()
   582  	return
   583  }
   584  
   585  func (t *Transaction) VestingDelete(poolID string) (err error) {
   586  	err = t.vestingPoolTxn(transaction.VESTING_DELETE, poolID, "0")
   587  	if err != nil {
   588  		logging.Error(err)
   589  		return
   590  	}
   591  	go func() { t.setNonceAndSubmit() }()
   592  	return
   593  }
   594  
   595  func (t *Transaction) MinerSCLock(providerId string, providerType int, lock string) error {
   596  	pr := stakePoolRequest{
   597  		ProviderType: providerType,
   598  		ProviderID:   providerId,
   599  	}
   600  	err := t.createSmartContractTxn(MinerSmartContractAddress,
   601  		transaction.MINERSC_LOCK, pr, lock)
   602  	if err != nil {
   603  		logging.Error(err)
   604  		return err
   605  	}
   606  	go func() { t.setNonceAndSubmit() }()
   607  	return err
   608  }
   609  
   610  func (t *Transaction) MinerSCUnlock(providerId string, providerType int) error {
   611  	pr := &stakePoolRequest{
   612  		ProviderID:   providerId,
   613  		ProviderType: providerType,
   614  	}
   615  	err := t.createSmartContractTxn(MinerSmartContractAddress,
   616  		transaction.MINERSC_UNLOCK, pr, "0")
   617  	if err != nil {
   618  		logging.Error(err)
   619  		return err
   620  	}
   621  	go func() { t.setNonceAndSubmit() }()
   622  	return err
   623  }
   624  
   625  func (t *Transaction) MinerSCCollectReward(providerId string, providerType int) error {
   626  	pr := &scCollectReward{
   627  		ProviderId:   providerId,
   628  		ProviderType: providerType,
   629  	}
   630  
   631  	err := t.createSmartContractTxn(MinerSmartContractAddress,
   632  		transaction.MINERSC_COLLECT_REWARD, pr, "0")
   633  	if err != nil {
   634  		logging.Error(err)
   635  		return err
   636  	}
   637  	go func() { t.setNonceAndSubmit() }()
   638  	return err
   639  }
   640  
   641  func (t *Transaction) StorageSCCollectReward(providerId string, providerType int) error {
   642  	pr := &scCollectReward{
   643  		ProviderId:   providerId,
   644  		ProviderType: providerType,
   645  	}
   646  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   647  		transaction.STORAGESC_COLLECT_REWARD, pr, "0")
   648  	if err != nil {
   649  		logging.Error(err)
   650  		return err
   651  	}
   652  	go t.setNonceAndSubmit()
   653  	return err
   654  }
   655  
   656  // FinalizeAllocation transaction.
   657  func (t *Transaction) FinalizeAllocation(allocID string) (err error) {
   658  	type finiRequest struct {
   659  		AllocationID string `json:"allocation_id"`
   660  	}
   661  	err = t.createSmartContractTxn(StorageSmartContractAddress,
   662  		transaction.STORAGESC_FINALIZE_ALLOCATION, &finiRequest{
   663  			AllocationID: allocID,
   664  		}, "0")
   665  	if err != nil {
   666  		logging.Error(err)
   667  		return
   668  	}
   669  	go func() { t.setNonceAndSubmit() }()
   670  	return
   671  }
   672  
   673  // CancelAllocation transaction.
   674  func (t *Transaction) CancelAllocation(allocID string) error {
   675  	type cancelRequest struct {
   676  		AllocationID string `json:"allocation_id"`
   677  	}
   678  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   679  		transaction.STORAGESC_CANCEL_ALLOCATION, &cancelRequest{
   680  			AllocationID: allocID,
   681  		}, "0")
   682  	if err != nil {
   683  		logging.Error(err)
   684  		return err
   685  	}
   686  	go func() { t.setNonceAndSubmit() }()
   687  	return nil
   688  }
   689  
   690  // CreateAllocation transaction.
   691  func (t *Transaction) CreateAllocation(car *CreateAllocationRequest, lock string) error {
   692  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   693  		transaction.STORAGESC_CREATE_ALLOCATION, car.toCreateAllocationSCInput(), lock)
   694  	if err != nil {
   695  		logging.Error(err)
   696  		return err
   697  	}
   698  	go func() { t.setNonceAndSubmit() }()
   699  	return nil
   700  }
   701  
   702  // CreateReadPool for current user.
   703  func (t *Transaction) CreateReadPool() error {
   704  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   705  		transaction.STORAGESC_CREATE_READ_POOL, nil, "0")
   706  	if err != nil {
   707  		logging.Error(err)
   708  		return err
   709  	}
   710  	go func() { t.setNonceAndSubmit() }()
   711  	return nil
   712  }
   713  
   714  // ReadPoolLock locks tokens for current user and given allocation, using given
   715  // duration. If blobberID is not empty, then tokens will be locked for given
   716  // allocation->blobber only.
   717  func (t *Transaction) ReadPoolLock(allocID, blobberID string,
   718  	duration int64, lock string) error {
   719  
   720  	type lockRequest struct {
   721  		Duration     time.Duration `json:"duration"`
   722  		AllocationID string        `json:"allocation_id"`
   723  		BlobberID    string        `json:"blobber_id,omitempty"`
   724  	}
   725  
   726  	var lr lockRequest
   727  	lr.Duration = time.Duration(duration)
   728  	lr.AllocationID = allocID
   729  	lr.BlobberID = blobberID
   730  
   731  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   732  		transaction.STORAGESC_READ_POOL_LOCK, &lr, lock)
   733  	if err != nil {
   734  		logging.Error(err)
   735  		return err
   736  	}
   737  	go func() { t.setNonceAndSubmit() }()
   738  	return nil
   739  }
   740  
   741  // ReadPoolUnlock for current user and given pool.
   742  func (t *Transaction) ReadPoolUnlock() error {
   743  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   744  		transaction.STORAGESC_READ_POOL_UNLOCK, nil, "0")
   745  	if err != nil {
   746  		logging.Error(err)
   747  		return err
   748  	}
   749  	go func() { t.setNonceAndSubmit() }()
   750  	return nil
   751  }
   752  
   753  // StakePoolLock used to lock tokens in a stake pool of a blobber.
   754  func (t *Transaction) StakePoolLock(providerId string, providerType int, lock string) error {
   755  	spr := stakePoolRequest{
   756  		ProviderType: providerType,
   757  		ProviderID:   providerId,
   758  	}
   759  
   760  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   761  		transaction.STORAGESC_STAKE_POOL_LOCK, &spr, lock)
   762  	if err != nil {
   763  		logging.Error(err)
   764  		return err
   765  	}
   766  	go func() { t.setNonceAndSubmit() }()
   767  	return nil
   768  }
   769  
   770  // StakePoolUnlock by blobberID
   771  func (t *Transaction) StakePoolUnlock(providerId string, providerType int) error {
   772  	spr := stakePoolRequest{
   773  		ProviderType: providerType,
   774  		ProviderID:   providerId,
   775  	}
   776  
   777  	err := t.createSmartContractTxn(StorageSmartContractAddress, transaction.STORAGESC_STAKE_POOL_UNLOCK, &spr, "0")
   778  	if err != nil {
   779  		logging.Error(err)
   780  		return err
   781  	}
   782  	go func() { t.setNonceAndSubmit() }()
   783  	return nil
   784  }
   785  
   786  // UpdateBlobberSettings update settings of a blobber.
   787  func (t *Transaction) UpdateBlobberSettings(b Blobber) error {
   788  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   789  		transaction.STORAGESC_UPDATE_BLOBBER_SETTINGS, b, "0")
   790  	if err != nil {
   791  		logging.Error(err)
   792  		return err
   793  	}
   794  	go func() { t.setNonceAndSubmit() }()
   795  	return nil
   796  }
   797  
   798  // UpdateAllocation transaction.
   799  func (t *Transaction) UpdateAllocation(allocID string, sizeDiff int64,
   800  	expirationDiff int64, lock string) error {
   801  	type updateAllocationRequest struct {
   802  		ID         string `json:"id"`              // allocation id
   803  		Size       int64  `json:"size"`            // difference
   804  		Expiration int64  `json:"expiration_date"` // difference
   805  	}
   806  
   807  	var uar updateAllocationRequest
   808  	uar.ID = allocID
   809  	uar.Size = sizeDiff
   810  	uar.Expiration = expirationDiff
   811  
   812  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   813  		transaction.STORAGESC_UPDATE_ALLOCATION, &uar, lock)
   814  	if err != nil {
   815  		logging.Error(err)
   816  		return err
   817  	}
   818  	go func() { t.setNonceAndSubmit() }()
   819  	return nil
   820  }
   821  
   822  // WritePoolLock locks tokens for current user and given allocation, using given
   823  // duration. If blobberID is not empty, then tokens will be locked for given
   824  // allocation->blobber only.
   825  func (t *Transaction) WritePoolLock(allocID, lock string) error {
   826  	var lr = struct {
   827  		AllocationID string `json:"allocation_id"`
   828  	}{
   829  		AllocationID: allocID,
   830  	}
   831  
   832  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   833  		transaction.STORAGESC_WRITE_POOL_LOCK, &lr, lock)
   834  	if err != nil {
   835  		logging.Error(err)
   836  		return err
   837  	}
   838  	go func() { t.setNonceAndSubmit() }()
   839  	return nil
   840  }
   841  
   842  // WritePoolUnlock for current user and given pool.
   843  func (t *Transaction) WritePoolUnlock(allocID string) error {
   844  	var ur = struct {
   845  		AllocationID string `json:"allocation_id"`
   846  	}{
   847  		AllocationID: allocID,
   848  	}
   849  
   850  	err := t.createSmartContractTxn(StorageSmartContractAddress,
   851  		transaction.STORAGESC_WRITE_POOL_UNLOCK, &ur, "0")
   852  	if err != nil {
   853  		logging.Error(err)
   854  		return err
   855  	}
   856  	go func() { t.setNonceAndSubmit() }()
   857  	return nil
   858  }
   859  
   860  func (t *Transaction) VestingUpdateConfig(vscc InputMap) (err error) {
   861  	err = t.createSmartContractTxn(VestingSmartContractAddress,
   862  		transaction.VESTING_UPDATE_SETTINGS, vscc, "0")
   863  	if err != nil {
   864  		logging.Error(err)
   865  		return
   866  	}
   867  	go func() { t.setNonceAndSubmit() }()
   868  	return
   869  }
   870  
   871  // faucet smart contract
   872  
   873  func (t *Transaction) FaucetUpdateConfig(ip InputMap) (err error) {
   874  	err = t.createSmartContractTxn(FaucetSmartContractAddress,
   875  		transaction.FAUCETSC_UPDATE_SETTINGS, ip, "0")
   876  	if err != nil {
   877  		logging.Error(err)
   878  		return
   879  	}
   880  	go func() { t.setNonceAndSubmit() }()
   881  	return
   882  }
   883  
   884  //
   885  // miner SC
   886  //
   887  
   888  func (t *Transaction) MinerScUpdateConfig(ip InputMap) (err error) {
   889  	err = t.createSmartContractTxn(MinerSmartContractAddress,
   890  		transaction.MINERSC_UPDATE_SETTINGS, ip, "0")
   891  	if err != nil {
   892  		logging.Error(err)
   893  		return
   894  	}
   895  	go func() { t.setNonceAndSubmit() }()
   896  	return
   897  }
   898  
   899  func (t *Transaction) MinerScUpdateGlobals(ip InputMap) (err error) {
   900  	err = t.createSmartContractTxn(MinerSmartContractAddress,
   901  		transaction.MINERSC_UPDATE_GLOBALS, ip, "0")
   902  	if err != nil {
   903  		logging.Error(err)
   904  		return
   905  	}
   906  	go func() { t.setNonceAndSubmit() }()
   907  	return
   908  }
   909  
   910  func (t *Transaction) StorageScUpdateConfig(ip InputMap) (err error) {
   911  	err = t.createSmartContractTxn(StorageSmartContractAddress,
   912  		transaction.STORAGESC_UPDATE_SETTINGS, ip, "0")
   913  	if err != nil {
   914  		logging.Error(err)
   915  		return
   916  	}
   917  	go func() { t.setNonceAndSubmit() }()
   918  	return
   919  }
   920  
   921  func (t *Transaction) ZCNSCUpdateGlobalConfig(ip InputMap) (err error) {
   922  	err = t.createSmartContractTxn(ZCNSCSmartContractAddress,
   923  		transaction.ZCNSC_UPDATE_GLOBAL_CONFIG, ip, "0")
   924  	if err != nil {
   925  		logging.Error(err)
   926  		return
   927  	}
   928  	go t.setNonceAndSubmit()
   929  	return
   930  }
   931  
   932  func (t *Transaction) GetVerifyConfirmationStatus() int {
   933  	return int(t.verifyConfirmationStatus)
   934  }
   935  
   936  // MinerSCMinerInfo interface for miner info functions on miner smart contract.
   937  type MinerSCMinerInfo interface {
   938  	// GetID returns the ID of the miner
   939  	GetID() string
   940  }
   941  
   942  // NewMinerSCMinerInfo creates a new miner info.
   943  //   - id: miner ID
   944  //   - delegateWallet: delegate wallet
   945  //   - minStake: minimum stake
   946  //   - maxStake: maximum stake
   947  //   - numDelegates: number of delegates
   948  //   - serviceCharge: service charge
   949  func NewMinerSCMinerInfo(id string, delegateWallet string,
   950  	minStake int64, maxStake int64, numDelegates int, serviceCharge float64) MinerSCMinerInfo {
   951  	return &minerSCMinerInfo{
   952  		simpleMiner: simpleMiner{ID: id},
   953  		minerSCDelegatePool: minerSCDelegatePool{
   954  			Settings: StakePoolSettings{
   955  				DelegateWallet: delegateWallet,
   956  				NumDelegates:   numDelegates,
   957  				ServiceCharge:  serviceCharge,
   958  			},
   959  		},
   960  	}
   961  }
   962  
   963  type minerSCMinerInfo struct {
   964  	simpleMiner         `json:"simple_miner"`
   965  	minerSCDelegatePool `json:"stake_pool"`
   966  }
   967  
   968  func (mi *minerSCMinerInfo) GetID() string {
   969  	return mi.ID
   970  }
   971  
   972  type minerSCDelegatePool struct {
   973  	Settings StakePoolSettings `json:"settings"`
   974  }
   975  
   976  type simpleMiner struct {
   977  	ID string `json:"id"`
   978  }
   979  
   980  func (t *Transaction) MinerSCMinerSettings(info MinerSCMinerInfo) (err error) {
   981  	err = t.createSmartContractTxn(MinerSmartContractAddress,
   982  		transaction.MINERSC_MINER_SETTINGS, info, "0")
   983  	if err != nil {
   984  		logging.Error(err)
   985  		return
   986  	}
   987  	go func() { t.setNonceAndSubmit() }()
   988  	return
   989  }
   990  
   991  func (t *Transaction) MinerSCSharderSettings(info MinerSCMinerInfo) (err error) {
   992  	err = t.createSmartContractTxn(MinerSmartContractAddress,
   993  		transaction.MINERSC_SHARDER_SETTINGS, info, "0")
   994  	if err != nil {
   995  		logging.Error(err)
   996  		return
   997  	}
   998  	go func() { t.setNonceAndSubmit() }()
   999  	return
  1000  }
  1001  
  1002  func (t *Transaction) MinerSCDeleteMiner(info MinerSCMinerInfo) (err error) {
  1003  	err = t.createSmartContractTxn(MinerSmartContractAddress,
  1004  		transaction.MINERSC_MINER_DELETE, info, "0")
  1005  	if err != nil {
  1006  		logging.Error(err)
  1007  		return
  1008  	}
  1009  	go func() { t.setNonceAndSubmit() }()
  1010  	return
  1011  }
  1012  
  1013  func (t *Transaction) MinerSCDeleteSharder(info MinerSCMinerInfo) (err error) {
  1014  	err = t.createSmartContractTxn(MinerSmartContractAddress,
  1015  		transaction.MINERSC_SHARDER_DELETE, info, "0")
  1016  	if err != nil {
  1017  		logging.Error(err)
  1018  		return
  1019  	}
  1020  	go func() { t.setNonceAndSubmit() }()
  1021  	return
  1022  }
  1023  
  1024  // AuthorizerNode interface for authorizer node functions.
  1025  type AuthorizerNode interface {
  1026  	// GetID returns the ID of the authorizer node.
  1027  	GetID() string
  1028  }
  1029  
  1030  // NewAuthorizerNode creates a new authorizer node.
  1031  func NewAuthorizerNode(id string, fee int64) AuthorizerNode {
  1032  	return &authorizerNode{
  1033  		ID:     id,
  1034  		Config: &AuthorizerConfig{Fee: fee},
  1035  	}
  1036  }
  1037  
  1038  type authorizerNode struct {
  1039  	ID     string            `json:"id"`
  1040  	Config *AuthorizerConfig `json:"config"`
  1041  }
  1042  
  1043  func (a *authorizerNode) GetID() string {
  1044  	return a.ID
  1045  }
  1046  
  1047  func (t *Transaction) ZCNSCUpdateAuthorizerConfig(ip AuthorizerNode) (err error) {
  1048  	err = t.createSmartContractTxn(ZCNSCSmartContractAddress, transaction.ZCNSC_UPDATE_AUTHORIZER_CONFIG, ip, "0")
  1049  	if err != nil {
  1050  		logging.Error(err)
  1051  		return
  1052  	}
  1053  	go t.setNonceAndSubmit()
  1054  	return
  1055  }
  1056  
  1057  func (t *Transaction) Verify() error {
  1058  	if t.txnHash == "" && t.txnStatus == StatusUnknown {
  1059  		return errors.New("", "invalid transaction. cannot be verified.")
  1060  	}
  1061  	if t.txnHash == "" && t.txnStatus == StatusSuccess {
  1062  		h := t.GetTransactionHash()
  1063  		if h == "" {
  1064  			node.Cache.Evict(t.txn.ClientID)
  1065  			return errors.New("", "invalid transaction. cannot be verified.")
  1066  		}
  1067  	}
  1068  	// If transaction is verify only start from current time
  1069  	if t.txn.CreationDate == 0 {
  1070  		t.txn.CreationDate = int64(common.Now())
  1071  	}
  1072  
  1073  	tq, err := newTransactionQuery(Sharders.Healthy())
  1074  	if err != nil {
  1075  		logging.Error(err)
  1076  		return err
  1077  	}
  1078  
  1079  	go func() {
  1080  
  1081  		for {
  1082  
  1083  			tq.Reset()
  1084  			// Get transaction confirmationBlock from a random sharder
  1085  			confirmBlockHeader, confirmationBlock, lfbBlockHeader, err := tq.getFastConfirmation(t.txnHash, nil)
  1086  
  1087  			if err != nil {
  1088  				now := int64(common.Now())
  1089  
  1090  				// maybe it is a network or server error
  1091  				if lfbBlockHeader == nil {
  1092  					logging.Info(err, " now: ", now)
  1093  				} else {
  1094  					logging.Info(err, " now: ", now, ", LFB creation time:", lfbBlockHeader.CreationDate)
  1095  				}
  1096  
  1097  				// transaction is done or expired. it means random sharder might be outdated, try to query it from s/S sharders to confirm it
  1098  				if util.MaxInt64(lfbBlockHeader.getCreationDate(now), now) >= (t.txn.CreationDate + int64(defaultTxnExpirationSeconds)) {
  1099  					logging.Info("falling back to ", getMinShardersVerify(), " of ", len(_config.chain.Sharders), " Sharders", len(Sharders.Healthy()), "Healthy sharders")
  1100  					confirmBlockHeader, confirmationBlock, lfbBlockHeader, err = tq.getConsensusConfirmation(getMinShardersVerify(), t.txnHash, nil)
  1101  				}
  1102  
  1103  				// txn not found in fast confirmation/consensus confirmation
  1104  				if err != nil {
  1105  
  1106  					if lfbBlockHeader == nil {
  1107  						// no any valid lfb on all sharders. maybe they are network/server errors. try it again
  1108  						continue
  1109  					}
  1110  
  1111  					// it is expired
  1112  					if t.isTransactionExpired(lfbBlockHeader.getCreationDate(now), now) {
  1113  						t.completeVerify(StatusError, "", errors.New("", `{"error": "verify transaction failed"}`))
  1114  						return
  1115  					}
  1116  					continue
  1117  				}
  1118  
  1119  			}
  1120  
  1121  			valid := validateChain(confirmBlockHeader)
  1122  			if valid {
  1123  				output, err := json.Marshal(confirmationBlock)
  1124  				if err != nil {
  1125  					t.completeVerify(StatusError, "", errors.New("", `{"error": "transaction confirmation json marshal error"`))
  1126  					return
  1127  				}
  1128  				confJson := confirmationBlock["confirmation"]
  1129  
  1130  				var conf map[string]json.RawMessage
  1131  				if err := json.Unmarshal(confJson, &conf); err != nil {
  1132  					return
  1133  				}
  1134  				txnJson := conf["txn"]
  1135  
  1136  				var tr map[string]json.RawMessage
  1137  				if err := json.Unmarshal(txnJson, &tr); err != nil {
  1138  					return
  1139  				}
  1140  
  1141  				txStatus := tr["transaction_status"]
  1142  				switch string(txStatus) {
  1143  				case "1":
  1144  					t.completeVerifyWithConStatus(StatusSuccess, Success, string(output), nil)
  1145  				case "2":
  1146  					txOutput := tr["transaction_output"]
  1147  					t.completeVerifyWithConStatus(StatusSuccess, ChargeableError, string(txOutput), nil)
  1148  				default:
  1149  					t.completeVerify(StatusError, string(output), nil)
  1150  				}
  1151  				return
  1152  			}
  1153  		}
  1154  	}()
  1155  	return nil
  1156  }
  1157  
  1158  func (t *Transaction) ZCNSCAddAuthorizer(ip AddAuthorizerPayload) (err error) {
  1159  	err = t.createSmartContractTxn(ZCNSCSmartContractAddress, transaction.ZCNSC_ADD_AUTHORIZER, ip, "0")
  1160  	if err != nil {
  1161  		logging.Error(err)
  1162  		return
  1163  	}
  1164  	go t.setNonceAndSubmit()
  1165  	return
  1166  }
  1167  
  1168  // EstimateFee estimates transaction fee
  1169  func (t *Transaction) EstimateFee(reqPercent float32) (int64, error) {
  1170  	fee, err := transaction.EstimateFee(t.txn, _config.chain.Miners, reqPercent)
  1171  	return int64(fee), err
  1172  }
  1173  
  1174  // ConvertTokenToSAS converts ZCN tokens to SAS tokens
  1175  // # Inputs
  1176  //   - token: ZCN tokens
  1177  func ConvertTokenToSAS(token float64) uint64 {
  1178  	return uint64(token * common.TokenUnit)
  1179  }
  1180  
  1181  // ConvertToValue converts ZCN tokens to SAS tokens with string format
  1182  //   - token: ZCN tokens
  1183  func ConvertToValue(token float64) string {
  1184  	return strconv.FormatUint(ConvertTokenToSAS(token), 10)
  1185  }
  1186  
  1187  func makeTimeoutContext(tm RequestTimeout) (context.Context, func()) {
  1188  
  1189  	if tm != nil && tm.Get() > 0 {
  1190  		return context.WithTimeout(context.Background(), time.Millisecond*time.Duration(tm.Get()))
  1191  
  1192  	}
  1193  	return context.Background(), func() {}
  1194  
  1195  }
  1196  
  1197  func GetLatestFinalized(numSharders int, timeout RequestTimeout) (b *BlockHeader, err error) {
  1198  	var result = make(chan *util.GetResponse, numSharders)
  1199  	defer close(result)
  1200  
  1201  	ctx, cancel := makeTimeoutContext(timeout)
  1202  	defer cancel()
  1203  
  1204  	numSharders = len(Sharders.Healthy()) // overwrite, use all
  1205  	Sharders.QueryFromShardersContext(ctx, numSharders, GET_LATEST_FINALIZED, result)
  1206  
  1207  	var (
  1208  		maxConsensus   int
  1209  		roundConsensus = make(map[string]int)
  1210  	)
  1211  
  1212  	for i := 0; i < numSharders; i++ {
  1213  		var rsp = <-result
  1214  		if rsp == nil {
  1215  			logging.Error("nil response")
  1216  			continue
  1217  		}
  1218  
  1219  		logging.Debug(rsp.Url, rsp.Status)
  1220  
  1221  		if rsp.StatusCode != http.StatusOK {
  1222  			logging.Error(rsp.Body)
  1223  			continue
  1224  		}
  1225  
  1226  		if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil {
  1227  			logging.Error("block parse error: ", err)
  1228  			err = nil
  1229  			continue
  1230  		}
  1231  
  1232  		var h = encryption.FastHash([]byte(b.Hash))
  1233  		if roundConsensus[h]++; roundConsensus[h] > maxConsensus {
  1234  			maxConsensus = roundConsensus[h]
  1235  		}
  1236  	}
  1237  
  1238  	if maxConsensus == 0 {
  1239  		return nil, errors.New("", "block info not found")
  1240  	}
  1241  
  1242  	return
  1243  }
  1244  
  1245  // GetLatestFinalizedMagicBlock gets latest finalized magic block
  1246  //   - numSharders: number of sharders
  1247  //   - timeout: request timeout
  1248  func GetLatestFinalizedMagicBlock(numSharders int, timeout RequestTimeout) ([]byte, error) {
  1249  	var result = make(chan *util.GetResponse, numSharders)
  1250  	defer close(result)
  1251  
  1252  	ctx, cancel := makeTimeoutContext(timeout)
  1253  	defer cancel()
  1254  
  1255  	numSharders = len(Sharders.Healthy()) // overwrite, use all
  1256  	Sharders.QueryFromShardersContext(ctx, numSharders, GET_LATEST_FINALIZED_MAGIC_BLOCK, result)
  1257  
  1258  	var (
  1259  		maxConsensus   int
  1260  		roundConsensus = make(map[string]int)
  1261  		m              *block.MagicBlock
  1262  		err            error
  1263  	)
  1264  
  1265  	type respObj struct {
  1266  		MagicBlock *block.MagicBlock `json:"magic_block"`
  1267  	}
  1268  
  1269  	for i := 0; i < numSharders; i++ {
  1270  		var rsp = <-result
  1271  		if rsp == nil {
  1272  			logging.Error("nil response")
  1273  			continue
  1274  		}
  1275  
  1276  		logging.Debug(rsp.Url, rsp.Status)
  1277  
  1278  		if rsp.StatusCode != http.StatusOK {
  1279  			logging.Error(rsp.Body)
  1280  			continue
  1281  		}
  1282  
  1283  		var respo respObj
  1284  		if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil {
  1285  			logging.Error(" magic block parse error: ", err)
  1286  			err = nil
  1287  			continue
  1288  		}
  1289  
  1290  		m = respo.MagicBlock
  1291  		var h = encryption.FastHash([]byte(respo.MagicBlock.Hash))
  1292  		if roundConsensus[h]++; roundConsensus[h] > maxConsensus {
  1293  			maxConsensus = roundConsensus[h]
  1294  		}
  1295  	}
  1296  
  1297  	if maxConsensus == 0 {
  1298  		return nil, errors.New("", "magic block info not found")
  1299  	}
  1300  
  1301  	if m != nil {
  1302  		return json.Marshal(m)
  1303  	}
  1304  
  1305  	return nil, err
  1306  }
  1307  
  1308  // GetChainStats gets chain stats with time out
  1309  // timeout in milliseconds
  1310  func GetChainStats(timeout RequestTimeout) ([]byte, error) {
  1311  	var result = make(chan *util.GetResponse, 1)
  1312  	defer close(result)
  1313  
  1314  	ctx, cancel := makeTimeoutContext(timeout)
  1315  	defer cancel()
  1316  
  1317  	var (
  1318  		b   *block.ChainStats
  1319  		err error
  1320  	)
  1321  
  1322  	var numSharders = len(Sharders.Healthy()) // overwrite, use all
  1323  	Sharders.QueryFromShardersContext(ctx, numSharders, GET_CHAIN_STATS, result)
  1324  	var rsp *util.GetResponse
  1325  	for i := 0; i < numSharders; i++ {
  1326  		var x = <-result
  1327  		if x == nil {
  1328  			logging.Error("nil response")
  1329  			continue
  1330  		}
  1331  		if x.StatusCode != http.StatusOK {
  1332  			continue
  1333  		}
  1334  		rsp = x
  1335  	}
  1336  
  1337  	if rsp == nil {
  1338  		return nil, errors.New("http_request_failed", "Request failed with status not 200")
  1339  	}
  1340  
  1341  	if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil {
  1342  		return nil, err
  1343  	}
  1344  
  1345  	return []byte(rsp.Body), nil
  1346  }
  1347  
  1348  func GetFeeStats(timeout RequestTimeout) ([]byte, error) {
  1349  
  1350  	var numMiners = 4
  1351  
  1352  	if numMiners > len(_config.chain.Miners) {
  1353  		numMiners = len(_config.chain.Miners)
  1354  	}
  1355  
  1356  	var result = make(chan *util.GetResponse, numMiners)
  1357  
  1358  	ctx, cancel := makeTimeoutContext(timeout)
  1359  	defer cancel()
  1360  
  1361  	var (
  1362  		b   *block.FeeStats
  1363  		err error
  1364  	)
  1365  
  1366  	queryFromMinersContext(ctx, numMiners, GET_FEE_STATS, result)
  1367  	var rsp *util.GetResponse
  1368  
  1369  loop:
  1370  	for i := 0; i < numMiners; i++ {
  1371  		select {
  1372  		case x := <-result:
  1373  			if x.StatusCode != http.StatusOK {
  1374  				continue
  1375  			}
  1376  			rsp = x
  1377  			if rsp != nil {
  1378  				break loop
  1379  			}
  1380  		case <-ctx.Done():
  1381  			return nil, ctx.Err()
  1382  		}
  1383  	}
  1384  	if rsp == nil {
  1385  		return nil, errors.New("http_request_failed", "Request failed with status not 200")
  1386  	}
  1387  
  1388  	if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil {
  1389  		return nil, err
  1390  	}
  1391  
  1392  	return []byte(rsp.Body), nil
  1393  }
  1394  
  1395  type BlockHeader struct {
  1396  	Version               string `json:"version,omitempty"`
  1397  	CreationDate          int64  `json:"creation_date,omitempty"`
  1398  	Hash                  string `json:"hash,omitempty"`
  1399  	MinerID               string `json:"miner_id,omitempty"`
  1400  	Round                 int64  `json:"round,omitempty"`
  1401  	RoundRandomSeed       int64  `json:"round_random_seed,omitempty"`
  1402  	MerkleTreeRoot        string `json:"merkle_tree_root,omitempty"`
  1403  	StateHash             string `json:"state_hash,omitempty"`
  1404  	ReceiptMerkleTreeRoot string `json:"receipt_merkle_tree_root,omitempty"`
  1405  	NumTxns               int64  `json:"num_txns,omitempty"`
  1406  }
  1407  
  1408  type Block struct {
  1409  	MinerID           string `json:"miner_id"`
  1410  	Round             int64  `json:"round"`
  1411  	RoundRandomSeed   int64  `json:"round_random_seed"`
  1412  	RoundTimeoutCount int    `json:"round_timeout_count"`
  1413  
  1414  	Hash            string  `json:"hash"`
  1415  	Signature       string  `json:"signature"`
  1416  	ChainID         string  `json:"chain_id"`
  1417  	ChainWeight     float64 `json:"chain_weight"`
  1418  	RunningTxnCount int64   `json:"running_txn_count"`
  1419  
  1420  	Version      string `json:"version"`
  1421  	CreationDate int64  `json:"creation_date"`
  1422  
  1423  	MagicBlockHash string `json:"magic_block_hash"`
  1424  	PrevHash       string `json:"prev_hash"`
  1425  
  1426  	ClientStateHash string `json:"state_hash"`
  1427  
  1428  	// unexported fields
  1429  	header *BlockHeader               `json:"-"`
  1430  	txns   []*transaction.Transaction `json:"transactions,omitempty"`
  1431  }
  1432  
  1433  func (b *Block) GetHeader() *BlockHeader {
  1434  	return b.header
  1435  }
  1436  
  1437  type IterTxnFunc func(idx int, txn *transaction.Transaction)
  1438  
  1439  type Transactions struct {
  1440  	txns []*transaction.Transaction
  1441  }
  1442  
  1443  func (tm *Transactions) Len() int {
  1444  	return len(tm.txns)
  1445  }
  1446  
  1447  func (tm *Transactions) Get(idx int) (*transaction.Transaction, error) {
  1448  	if idx < 0 && idx >= len(tm.txns) {
  1449  		return nil, stderrors.New("index out of bounds")
  1450  	}
  1451  
  1452  	return tm.txns[idx], nil
  1453  }
  1454  
  1455  func (b *Block) GetTxns() *Transactions {
  1456  	return &Transactions{
  1457  		txns: b.txns,
  1458  	}
  1459  }
  1460  
  1461  func toMobileBlock(b *block.Block) *Block {
  1462  	lb := &Block{
  1463  		header: &BlockHeader{
  1464  			Version:               b.Header.Version,
  1465  			CreationDate:          b.Header.CreationDate,
  1466  			Hash:                  b.Header.Hash,
  1467  			MinerID:               b.Header.MinerID,
  1468  			Round:                 b.Header.Round,
  1469  			RoundRandomSeed:       b.Header.RoundRandomSeed,
  1470  			MerkleTreeRoot:        b.Header.MerkleTreeRoot,
  1471  			StateHash:             b.Header.StateHash,
  1472  			ReceiptMerkleTreeRoot: b.Header.ReceiptMerkleTreeRoot,
  1473  			NumTxns:               b.Header.NumTxns,
  1474  		},
  1475  		MinerID:           string(b.MinerID),
  1476  		Round:             b.Round,
  1477  		RoundRandomSeed:   b.RoundRandomSeed,
  1478  		RoundTimeoutCount: b.RoundTimeoutCount,
  1479  
  1480  		Hash:            string(b.Hash),
  1481  		Signature:       b.Signature,
  1482  		ChainID:         string(b.ChainID),
  1483  		ChainWeight:     b.ChainWeight,
  1484  		RunningTxnCount: b.RunningTxnCount,
  1485  
  1486  		Version:      b.Version,
  1487  		CreationDate: int64(b.CreationDate),
  1488  
  1489  		MagicBlockHash: b.MagicBlockHash,
  1490  		PrevHash:       b.PrevHash,
  1491  
  1492  		ClientStateHash: string(b.ClientStateHash),
  1493  	}
  1494  
  1495  	lb.txns = make([]*transaction.Transaction, len(b.Txns))
  1496  	for i, txn := range b.Txns {
  1497  		lb.txns[i] = txn
  1498  	}
  1499  
  1500  	return lb
  1501  }
  1502  
  1503  // RequestTimeout will be used for setting requests with timeout
  1504  type RequestTimeout interface {
  1505  	Set(int64)  // milliseconds
  1506  	Get() int64 // milliseconds
  1507  }
  1508  
  1509  type timeoutCtx struct {
  1510  	millisecond int64
  1511  }
  1512  
  1513  func NewRequestTimeout(timeout int64) RequestTimeout {
  1514  	return &timeoutCtx{millisecond: timeout}
  1515  }
  1516  
  1517  func (t *timeoutCtx) Set(tm int64) {
  1518  	t.millisecond = tm
  1519  }
  1520  
  1521  func (t *timeoutCtx) Get() int64 {
  1522  	return t.millisecond
  1523  }
  1524  
  1525  func GetBlockByRound(numSharders int, round int64, timeout RequestTimeout) (b *Block, err error) {
  1526  	var result = make(chan *util.GetResponse, numSharders)
  1527  	defer close(result)
  1528  
  1529  	ctx, cancel := makeTimeoutContext(timeout)
  1530  	defer cancel()
  1531  
  1532  	numSharders = len(Sharders.Healthy()) // overwrite, use all
  1533  	Sharders.QueryFromShardersContext(ctx, numSharders,
  1534  		fmt.Sprintf("%sround=%d&content=full,header", GET_BLOCK_INFO, round),
  1535  		result)
  1536  
  1537  	var (
  1538  		maxConsensus   int
  1539  		roundConsensus = make(map[string]int)
  1540  	)
  1541  
  1542  	type respObj struct {
  1543  		Block  *block.Block  `json:"block"`
  1544  		Header *block.Header `json:"header"`
  1545  	}
  1546  
  1547  	for i := 0; i < numSharders; i++ {
  1548  		var rsp = <-result
  1549  		if rsp == nil {
  1550  			logging.Error("nil response")
  1551  			continue
  1552  		}
  1553  		logging.Debug(rsp.Url, rsp.Status)
  1554  
  1555  		if rsp.StatusCode != http.StatusOK {
  1556  			logging.Error(rsp.Body)
  1557  			continue
  1558  		}
  1559  
  1560  		var respo respObj
  1561  		if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil {
  1562  			logging.Error("block parse error: ", err)
  1563  			err = nil
  1564  			continue
  1565  		}
  1566  
  1567  		if respo.Block == nil {
  1568  			logging.Debug(rsp.Url, "no block in response:", rsp.Body)
  1569  			continue
  1570  		}
  1571  
  1572  		if respo.Header == nil {
  1573  			logging.Debug(rsp.Url, "no block header in response:", rsp.Body)
  1574  			continue
  1575  		}
  1576  
  1577  		if respo.Header.Hash != string(respo.Block.Hash) {
  1578  			logging.Debug(rsp.Url, "header and block hash mismatch:", rsp.Body)
  1579  			continue
  1580  		}
  1581  
  1582  		b = toMobileBlock(respo.Block)
  1583  
  1584  		b.header = &BlockHeader{
  1585  			Version:               respo.Header.Version,
  1586  			CreationDate:          respo.Header.CreationDate,
  1587  			Hash:                  respo.Header.Hash,
  1588  			MinerID:               respo.Header.MinerID,
  1589  			Round:                 respo.Header.Round,
  1590  			RoundRandomSeed:       respo.Header.RoundRandomSeed,
  1591  			MerkleTreeRoot:        respo.Header.MerkleTreeRoot,
  1592  			StateHash:             respo.Header.StateHash,
  1593  			ReceiptMerkleTreeRoot: respo.Header.ReceiptMerkleTreeRoot,
  1594  			NumTxns:               respo.Header.NumTxns,
  1595  		}
  1596  
  1597  		var h = encryption.FastHash([]byte(b.Hash))
  1598  		if roundConsensus[h]++; roundConsensus[h] > maxConsensus {
  1599  			maxConsensus = roundConsensus[h]
  1600  		}
  1601  	}
  1602  
  1603  	if maxConsensus == 0 {
  1604  		return nil, errors.New("", "round info not found")
  1605  	}
  1606  
  1607  	return
  1608  }
  1609  
  1610  func GetMagicBlockByNumber(numSharders int, number int64, timeout RequestTimeout) ([]byte, error) {
  1611  	var result = make(chan *util.GetResponse, numSharders)
  1612  	defer close(result)
  1613  
  1614  	ctx, cancel := makeTimeoutContext(timeout)
  1615  	defer cancel()
  1616  
  1617  	numSharders = len(Sharders.Healthy()) // overwrite, use all
  1618  	Sharders.QueryFromShardersContext(ctx, numSharders,
  1619  		fmt.Sprintf("%smagic_block_number=%d", GET_MAGIC_BLOCK_INFO, number),
  1620  		result)
  1621  
  1622  	var (
  1623  		maxConsensus   int
  1624  		roundConsensus = make(map[string]int)
  1625  		ret            []byte
  1626  		err            error
  1627  	)
  1628  
  1629  	type respObj struct {
  1630  		MagicBlock *block.MagicBlock `json:"magic_block"`
  1631  	}
  1632  
  1633  	for i := 0; i < numSharders; i++ {
  1634  		var rsp = <-result
  1635  		if rsp == nil {
  1636  			logging.Error("nil response")
  1637  			continue
  1638  		}
  1639  
  1640  		logging.Debug(rsp.Url, rsp.Status)
  1641  
  1642  		if rsp.StatusCode != http.StatusOK {
  1643  			logging.Error(rsp.Body)
  1644  			continue
  1645  		}
  1646  
  1647  		var respo respObj
  1648  		if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil {
  1649  			logging.Error(" magic block parse error: ", err)
  1650  			err = nil
  1651  			continue
  1652  		}
  1653  
  1654  		ret = []byte(rsp.Body)
  1655  		var h = encryption.FastHash([]byte(respo.MagicBlock.Hash))
  1656  		if roundConsensus[h]++; roundConsensus[h] > maxConsensus {
  1657  			maxConsensus = roundConsensus[h]
  1658  		}
  1659  	}
  1660  
  1661  	if maxConsensus == 0 {
  1662  		return nil, errors.New("", "magic block info not found")
  1663  	}
  1664  
  1665  	if err != nil {
  1666  		return nil, err
  1667  	}
  1668  
  1669  	return ret, nil
  1670  }
  1671  
  1672  // GetFeesTable get fee tables
  1673  func GetFeesTable(reqPercent float32) (string, error) {
  1674  
  1675  	fees, err := transaction.GetFeesTable(_config.chain.Miners, reqPercent)
  1676  	if err != nil {
  1677  		return "", err
  1678  	}
  1679  
  1680  	js, err := json.Marshal(fees)
  1681  	if err != nil {
  1682  		return "", err
  1683  	}
  1684  
  1685  	return string(js), nil
  1686  }