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

     1  package zcncore
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	stdErrors "errors"
     7  	"fmt"
     8  	"net/http"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/0chain/errors"
    13  	"github.com/0chain/gosdk/core/common"
    14  	"github.com/0chain/gosdk/core/node"
    15  	"github.com/0chain/gosdk/core/sys"
    16  	"github.com/0chain/gosdk/zboxcore/fileref"
    17  
    18  	"github.com/0chain/gosdk/core/conf"
    19  	"github.com/0chain/gosdk/core/encryption"
    20  	"github.com/0chain/gosdk/core/transaction"
    21  	"github.com/0chain/gosdk/core/util"
    22  	"github.com/0chain/gosdk/core/version"
    23  	"github.com/0chain/gosdk/core/zcncrypto"
    24  	"github.com/0chain/gosdk/zboxcore/blockchain"
    25  )
    26  
    27  var (
    28  	errNetwork          = errors.New("", "network error. host not reachable")
    29  	errUserRejected     = errors.New("", "rejected by user")
    30  	errAuthVerifyFailed = errors.New("", "verification failed for auth response")
    31  	errAuthTimeout      = errors.New("", "auth timed out")
    32  	errAddSignature     = errors.New("", "error adding signature")
    33  )
    34  
    35  // TransactionCallback needs to be implemented by the caller for transaction related APIs
    36  type TransactionCallback interface {
    37  	OnTransactionComplete(t *Transaction, status int)
    38  	OnVerifyComplete(t *Transaction, status int)
    39  	OnAuthComplete(t *Transaction, status int)
    40  }
    41  
    42  type localConfig struct {
    43  	chain         ChainConfig
    44  	wallet        zcncrypto.Wallet
    45  	authUrl       string
    46  	isConfigured  bool
    47  	isValidWallet bool
    48  	isSplitWallet bool
    49  }
    50  
    51  type ChainConfig struct {
    52  	ChainID                 string   `json:"chain_id,omitempty"`
    53  	BlockWorker             string   `json:"block_worker"`
    54  	Miners                  []string `json:"miners"`
    55  	Sharders                []string `json:"sharders"`
    56  	SignatureScheme         string   `json:"signature_scheme"`
    57  	MinSubmit               int      `json:"min_submit"`
    58  	MinConfirmation         int      `json:"min_confirmation"`
    59  	ConfirmationChainLength int      `json:"confirmation_chain_length"`
    60  	EthNode                 string   `json:"eth_node"`
    61  	SharderConsensous       int      `json:"sharder_consensous"`
    62  	IsSplitWallet           bool     `json:"is_split_wallet"`
    63  }
    64  
    65  var Sharders *node.NodeHolder
    66  
    67  // InitZCNSDK initializes the SDK given block worker and signature scheme provided.
    68  //   - blockWorker: block worker, which is the url for the DNS service for locating miners and sharders
    69  //   - signscheme: signature scheme to be used for signing the transactions
    70  //   - configs: configuration options
    71  func InitZCNSDK(blockWorker string, signscheme string, configs ...func(*ChainConfig) error) error {
    72  	if signscheme != "ed25519" && signscheme != "bls0chain" {
    73  		return errors.New("", "invalid/unsupported signature scheme")
    74  	}
    75  	_config.chain.BlockWorker = blockWorker
    76  	_config.chain.SignatureScheme = signscheme
    77  
    78  	err := UpdateNetworkDetails()
    79  	if err != nil {
    80  		fmt.Println("UpdateNetworkDetails:", err)
    81  		return err
    82  	}
    83  
    84  	go updateNetworkDetailsWorker(context.Background())
    85  
    86  	for _, conf := range configs {
    87  		err := conf(&_config.chain)
    88  		if err != nil {
    89  			return errors.Wrap(err, "invalid/unsupported options.")
    90  		}
    91  	}
    92  	_config.isSplitWallet = _config.chain.IsSplitWallet
    93  	assertConfig()
    94  	_config.isConfigured = true
    95  	logging.Info("******* Wallet SDK Version:", version.VERSIONSTR, " ******* (InitZCNSDK)")
    96  
    97  	cfg := &conf.Config{
    98  		BlockWorker:             _config.chain.BlockWorker,
    99  		MinSubmit:               _config.chain.MinSubmit,
   100  		MinConfirmation:         _config.chain.MinConfirmation,
   101  		ConfirmationChainLength: _config.chain.ConfirmationChainLength,
   102  		SignatureScheme:         _config.chain.SignatureScheme,
   103  		ChainID:                 _config.chain.ChainID,
   104  		EthereumNode:            _config.chain.EthNode,
   105  		SharderConsensous:       _config.chain.SharderConsensous,
   106  	}
   107  
   108  	conf.InitClientConfig(cfg)
   109  
   110  	return nil
   111  }
   112  
   113  func IsSplitWallet() bool {
   114  	return _config.isSplitWallet
   115  }
   116  
   117  /*Confirmation - a data structure that provides the confirmation that a transaction is included into the block chain */
   118  type confirmation struct {
   119  	Version               string                   `json:"version"`
   120  	Hash                  string                   `json:"hash"`
   121  	BlockHash             string                   `json:"block_hash"`
   122  	PreviousBlockHash     string                   `json:"previous_block_hash"`
   123  	Transaction           *transaction.Transaction `json:"txn,omitempty"`
   124  	CreationDate          int64                    `json:"creation_date,omitempty"`
   125  	MinerID               string                   `json:"miner_id"`
   126  	Round                 int64                    `json:"round"`
   127  	Status                int                      `json:"transaction_status" msgpack:"sot"`
   128  	RoundRandomSeed       int64                    `json:"round_random_seed"`
   129  	StateChangesCount     int                      `json:"state_changes_count"`
   130  	MerkleTreeRoot        string                   `json:"merkle_tree_root"`
   131  	MerkleTreePath        *util.MTPath             `json:"merkle_tree_path"`
   132  	ReceiptMerkleTreeRoot string                   `json:"receipt_merkle_tree_root"`
   133  	ReceiptMerkleTreePath *util.MTPath             `json:"receipt_merkle_tree_path"`
   134  }
   135  
   136  type blockHeader struct {
   137  	Version               string `json:"version,omitempty"`
   138  	CreationDate          int64  `json:"creation_date,omitempty"`
   139  	Hash                  string `json:"hash,omitempty"`
   140  	MinerId               string `json:"miner_id,omitempty"`
   141  	Round                 int64  `json:"round,omitempty"`
   142  	RoundRandomSeed       int64  `json:"round_random_seed,omitempty"`
   143  	StateChangesCount     int    `json:"state_changes_count"`
   144  	MerkleTreeRoot        string `json:"merkle_tree_root,omitempty"`
   145  	StateHash             string `json:"state_hash,omitempty"`
   146  	ReceiptMerkleTreeRoot string `json:"receipt_merkle_tree_root,omitempty"`
   147  	NumTxns               int64  `json:"num_txns,omitempty"`
   148  }
   149  
   150  func (bh *blockHeader) getCreationDate(defaultTime int64) int64 {
   151  	if bh == nil {
   152  		return defaultTime
   153  	}
   154  
   155  	return bh.CreationDate
   156  }
   157  
   158  // Transaction data structure that provides the transaction details
   159  type Transaction struct {
   160  	txn                      *transaction.Transaction
   161  	txnOut                   string
   162  	txnHash                  string
   163  	txnStatus                int
   164  	txnError                 error
   165  	txnCb                    TransactionCallback
   166  	verifyStatus             int
   167  	verifyConfirmationStatus int
   168  	verifyOut                string
   169  	verifyError              error
   170  }
   171  
   172  type SendTxnData struct {
   173  	Note string `json:"note"`
   174  }
   175  
   176  func Sign(hash string) (string, error) {
   177  	sigScheme := zcncrypto.NewSignatureScheme(_config.chain.SignatureScheme)
   178  	err := sigScheme.SetPrivateKey(_config.wallet.Keys[0].PrivateKey)
   179  	if err != nil {
   180  		return "", err
   181  	}
   182  	return sigScheme.Sign(hash)
   183  }
   184  
   185  func SignWithKey(privateKey, hash string) (string, error) {
   186  	sigScheme := zcncrypto.NewSignatureScheme("bls0chain")
   187  	err := sigScheme.SetPrivateKey(privateKey)
   188  	if err != nil {
   189  		return "", err
   190  	}
   191  	return sigScheme.Sign(hash)
   192  }
   193  
   194  func VerifyWithKey(pubKey, signature, hash string) (bool, error) {
   195  	sigScheme := zcncrypto.NewSignatureScheme("bls0chain")
   196  	err := sigScheme.SetPublicKey(pubKey)
   197  	if err != nil {
   198  		return false, err
   199  	}
   200  	return sigScheme.Verify(signature, hash)
   201  }
   202  
   203  var SignFn = func(hash string) (string, error) {
   204  	sigScheme := zcncrypto.NewSignatureScheme(_config.chain.SignatureScheme)
   205  	err := sigScheme.SetPrivateKey(_config.wallet.Keys[0].PrivateKey)
   206  	if err != nil {
   207  		return "", err
   208  	}
   209  	return sigScheme.Sign(hash)
   210  }
   211  
   212  var AddSignature = func(privateKey, signature string, hash string) (string, error) {
   213  	var (
   214  		ss  = zcncrypto.NewSignatureScheme(_config.chain.SignatureScheme)
   215  		err error
   216  	)
   217  
   218  	err = ss.SetPrivateKey(privateKey)
   219  	if err != nil {
   220  		return "", err
   221  	}
   222  
   223  	return ss.Add(signature, hash)
   224  }
   225  
   226  func signWithWallet(hash string, wi interface{}) (string, error) {
   227  	w, ok := wi.(*zcncrypto.Wallet)
   228  
   229  	if !ok {
   230  		fmt.Printf("Error in casting to wallet")
   231  		return "", errors.New("", "error in casting to wallet")
   232  	}
   233  	sigScheme := zcncrypto.NewSignatureScheme(_config.chain.SignatureScheme)
   234  	err := sigScheme.SetPrivateKey(w.Keys[0].PrivateKey)
   235  	if err != nil {
   236  		return "", err
   237  	}
   238  	return sigScheme.Sign(hash)
   239  }
   240  
   241  func txnTypeString(t int) string {
   242  	switch t {
   243  	case transaction.TxnTypeSend:
   244  		return "send"
   245  	case transaction.TxnTypeLockIn:
   246  		return "lock-in"
   247  	case transaction.TxnTypeData:
   248  		return "data"
   249  	case transaction.TxnTypeSmartContract:
   250  		return "smart contract"
   251  	default:
   252  		return "unknown"
   253  	}
   254  }
   255  
   256  // Output implements the output of transaction
   257  func (t *Transaction) Output() []byte {
   258  	return []byte(t.txnOut)
   259  }
   260  
   261  // Hash implements the hash of transaction
   262  func (t *Transaction) Hash() string {
   263  	return t.txn.Hash
   264  }
   265  
   266  func (t *Transaction) completeTxn(status int, out string, err error) {
   267  	t.txnStatus = status
   268  	t.txnOut = out
   269  	t.txnError = err
   270  	if t.txnCb != nil {
   271  		t.txnCb.OnTransactionComplete(t, t.txnStatus)
   272  	}
   273  }
   274  
   275  func (t *Transaction) completeVerify(status int, out string, err error) {
   276  	t.completeVerifyWithConStatus(status, 0, out, err)
   277  }
   278  
   279  func (t *Transaction) completeVerifyWithConStatus(status int, conStatus int, out string, err error) {
   280  	t.verifyStatus = status
   281  	t.verifyConfirmationStatus = conStatus
   282  	t.verifyOut = out
   283  	t.verifyError = err
   284  	if status == StatusError {
   285  		node.Cache.Evict(t.txn.ClientID)
   286  	}
   287  	if t.txnCb != nil {
   288  		t.txnCb.OnVerifyComplete(t, t.verifyStatus)
   289  	}
   290  }
   291  
   292  type getNonceCallBack struct {
   293  	nonceCh chan int64
   294  	err     error
   295  }
   296  
   297  func (g getNonceCallBack) OnNonceAvailable(status int, nonce int64, info string) {
   298  	if status != StatusSuccess {
   299  		g.err = errors.New("get_nonce", "failed respond nonce") //nolint
   300  	}
   301  
   302  	g.nonceCh <- nonce
   303  }
   304  
   305  func (t *Transaction) setNonceAndSubmit() {
   306  	t.setNonce()
   307  	t.submitTxn()
   308  }
   309  
   310  func (t *Transaction) setNonce() {
   311  	nonce := t.txn.TransactionNonce
   312  	if nonce < 1 {
   313  		nonce = node.Cache.GetNextNonce(t.txn.ClientID)
   314  	} else {
   315  		node.Cache.Set(t.txn.ClientID, nonce)
   316  	}
   317  	t.txn.TransactionNonce = nonce
   318  }
   319  
   320  func (t *Transaction) submitTxn() {
   321  	// Clear the status, in case transaction object reused
   322  	t.txnStatus = StatusUnknown
   323  	t.txnOut = ""
   324  	t.txnError = nil
   325  
   326  	// If Signature is not passed compute signature
   327  	if t.txn.Signature == "" {
   328  		err := t.txn.ComputeHashAndSign(SignFn)
   329  		if err != nil {
   330  			t.completeTxn(StatusError, "", err)
   331  			node.Cache.Evict(t.txn.ClientID)
   332  			return
   333  		}
   334  	}
   335  
   336  	var (
   337  		randomMiners = GetStableMiners()
   338  		minersN      = len(randomMiners)
   339  		failedCount  int32
   340  		failC        = make(chan struct{})
   341  		resultC      = make(chan *util.PostResponse, minersN)
   342  	)
   343  
   344  	for _, miner := range randomMiners {
   345  		go func(minerurl string) {
   346  			url := minerurl + PUT_TRANSACTION
   347  			logging.Info("Submitting ", txnTypeString(t.txn.TransactionType), " transaction to ", minerurl, " with JSON ", string(t.txn.DebugJSON()))
   348  			req, err := util.NewHTTPPostRequest(url, t.txn)
   349  			if err != nil {
   350  				logging.Error(minerurl, " new post request failed. ", err.Error())
   351  
   352  				if int(atomic.AddInt32(&failedCount, 1)) == minersN {
   353  					close(failC)
   354  				}
   355  				return
   356  			}
   357  
   358  			res, err := req.Post()
   359  			if err != nil {
   360  				logging.Error(minerurl, " submit transaction error. ", err.Error())
   361  				if int(atomic.AddInt32(&failedCount, 1)) == minersN {
   362  					close(failC)
   363  				}
   364  				return
   365  			}
   366  
   367  			if res.StatusCode != http.StatusOK {
   368  				logging.Error(minerurl, " submit transaction failed with status code ", res.StatusCode)
   369  				if int(atomic.AddInt32(&failedCount, 1)) == minersN {
   370  					resultC <- res
   371  				}
   372  				return
   373  			}
   374  
   375  			resultC <- res
   376  		}(miner)
   377  	}
   378  
   379  	select {
   380  	case <-failC:
   381  		logging.Error("failed to submit transaction")
   382  		t.completeTxn(StatusError, "", fmt.Errorf("failed to submit transaction to all miners"))
   383  		node.Cache.Evict(t.txn.ClientID)
   384  		ResetStableMiners()
   385  		return
   386  	case ret := <-resultC:
   387  		logging.Debug("finish txn submitting, ", ret.Url, ", Status: ", ret.Status, ", output:", ret.Body)
   388  		if ret.StatusCode == http.StatusOK {
   389  			t.completeTxn(StatusSuccess, ret.Body, nil)
   390  		} else {
   391  			t.completeTxn(StatusError, "", fmt.Errorf("submit transaction failed. %s", ret.Body))
   392  			node.Cache.Evict(t.txn.ClientID)
   393  			ResetStableMiners()
   394  		}
   395  	}
   396  }
   397  
   398  // SetTransactionCallback implements storing the callback
   399  func (t *Transaction) SetTransactionCallback(cb TransactionCallback) error {
   400  	if t.txnStatus != StatusUnknown {
   401  		return errors.New("", "transaction already exists. cannot set transaction hash.")
   402  	}
   403  	t.txnCb = cb
   404  	return nil
   405  }
   406  
   407  // SetTransactionNonce implements method to set the transaction nonce
   408  func (t *Transaction) SetTransactionNonce(txnNonce int64) error {
   409  	if t.txnStatus != StatusUnknown {
   410  		return errors.New("", "transaction already exists. cannot set transaction fee.")
   411  	}
   412  	t.txn.TransactionNonce = txnNonce
   413  	return nil
   414  }
   415  
   416  // StoreData implements store the data to blockchain
   417  func (t *Transaction) StoreData(data string) error {
   418  	go func() {
   419  		t.txn.TransactionType = transaction.TxnTypeData
   420  		t.txn.TransactionData = data
   421  		t.setNonceAndSubmit()
   422  	}()
   423  	return nil
   424  }
   425  
   426  type TxnFeeOption struct {
   427  	// stop estimate txn fee, usually if txn fee was 0, the createSmartContractTxn method would
   428  	// estimate the txn fee by calling API from 0chain network. With this option, we could force
   429  	// the txn to have zero fee for those exempt transactions.
   430  	noEstimateFee bool
   431  }
   432  
   433  // FeeOption represents txn fee related option type
   434  type FeeOption func(*TxnFeeOption)
   435  
   436  // WithNoEstimateFee would prevent txn fee estimation from remote
   437  func WithNoEstimateFee() FeeOption {
   438  	return func(o *TxnFeeOption) {
   439  		o.noEstimateFee = true
   440  	}
   441  }
   442  
   443  // ExecuteFaucetSCWallet implements the Faucet Smart contract for a given wallet
   444  func (t *Transaction) ExecuteFaucetSCWallet(walletStr string, methodName string, input []byte) error {
   445  	w, err := t.createFaucetSCWallet(walletStr, methodName, input)
   446  	if err != nil {
   447  		return err
   448  	}
   449  	go func() {
   450  		nonce := t.txn.TransactionNonce
   451  		if nonce < 1 {
   452  			nonce = node.Cache.GetNextNonce(t.txn.ClientID)
   453  		} else {
   454  			node.Cache.Set(t.txn.ClientID, nonce)
   455  		}
   456  		t.txn.TransactionNonce = nonce
   457  		err = t.txn.ComputeHashAndSignWithWallet(signWithWallet, w)
   458  		if err != nil {
   459  			return
   460  		}
   461  		fmt.Printf("submitted transaction\n")
   462  		t.submitTxn()
   463  	}()
   464  	return nil
   465  }
   466  
   467  // SetTransactionHash implements verify a previous transaction status
   468  //   - hash: transaction hash
   469  func (t *Transaction) SetTransactionHash(hash string) error {
   470  	if t.txnStatus != StatusUnknown {
   471  		return errors.New("", "transaction already exists. cannot set transaction hash.")
   472  	}
   473  	t.txnHash = hash
   474  	return nil
   475  }
   476  
   477  // GetTransactionHash implements retrieval of hash of the submitted transaction
   478  func (t *Transaction) GetTransactionHash() string {
   479  	if t.txnHash != "" {
   480  		return t.txnHash
   481  	}
   482  	if t.txnStatus != StatusSuccess {
   483  		return ""
   484  	}
   485  	var txnout map[string]json.RawMessage
   486  	err := json.Unmarshal([]byte(t.txnOut), &txnout)
   487  	if err != nil {
   488  		fmt.Println("Error in parsing", err)
   489  	}
   490  	var entity map[string]interface{}
   491  	err = json.Unmarshal(txnout["entity"], &entity)
   492  	if err != nil {
   493  		logging.Error("json unmarshal error on GetTransactionHash()")
   494  		return t.txnHash
   495  	}
   496  	if hash, ok := entity["hash"].(string); ok {
   497  		t.txnHash = hash
   498  	}
   499  	return t.txnHash
   500  }
   501  
   502  func queryFromMinersContext(ctx context.Context, numMiners int, query string, result chan *util.GetResponse) {
   503  
   504  	randomMiners := util.Shuffle(_config.chain.Miners)[:numMiners]
   505  	for _, miner := range randomMiners {
   506  		go func(minerurl string) {
   507  			logging.Info("Query from ", minerurl+query)
   508  			url := fmt.Sprintf("%v%v", minerurl, query)
   509  			req, err := util.NewHTTPGetRequestContext(ctx, url)
   510  			if err != nil {
   511  				logging.Error(minerurl, " new get request failed. ", err.Error())
   512  				return
   513  			}
   514  			res, err := req.Get()
   515  			if err != nil {
   516  				logging.Error(minerurl, " get error. ", err.Error())
   517  			}
   518  			result <- res
   519  		}(miner)
   520  	}
   521  
   522  }
   523  
   524  func getBlockHeaderFromTransactionConfirmation(txnHash string, cfmBlock map[string]json.RawMessage) (*blockHeader, error) {
   525  	block := &blockHeader{}
   526  	if cfmBytes, ok := cfmBlock["confirmation"]; ok {
   527  		var cfm confirmation
   528  		err := json.Unmarshal(cfmBytes, &cfm)
   529  		if err != nil {
   530  			return nil, errors.Wrap(err, "txn confirmation parse error.")
   531  		}
   532  		if cfm.Transaction == nil {
   533  			return nil, fmt.Errorf("missing transaction %s in block confirmation", txnHash)
   534  		}
   535  		if txnHash != cfm.Transaction.Hash {
   536  			return nil, fmt.Errorf("invalid transaction hash. Expected: %s. Received: %s", txnHash, cfm.Transaction.Hash)
   537  		}
   538  		if !util.VerifyMerklePath(cfm.Transaction.Hash, cfm.MerkleTreePath, cfm.MerkleTreeRoot) {
   539  			return nil, errors.New("", "txn merkle validation failed.")
   540  		}
   541  		txnRcpt := transaction.NewTransactionReceipt(cfm.Transaction)
   542  		if !util.VerifyMerklePath(txnRcpt.GetHash(), cfm.ReceiptMerkleTreePath, cfm.ReceiptMerkleTreeRoot) {
   543  			return nil, errors.New("", "txn receipt cmerkle validation failed.")
   544  		}
   545  		prevBlockHash := cfm.PreviousBlockHash
   546  		block.MinerId = cfm.MinerID
   547  		block.Hash = cfm.BlockHash
   548  		block.CreationDate = cfm.CreationDate
   549  		block.Round = cfm.Round
   550  		block.RoundRandomSeed = cfm.RoundRandomSeed
   551  		block.StateChangesCount = cfm.StateChangesCount
   552  		block.MerkleTreeRoot = cfm.MerkleTreeRoot
   553  		block.ReceiptMerkleTreeRoot = cfm.ReceiptMerkleTreeRoot
   554  		// Verify the block
   555  		if isBlockExtends(prevBlockHash, block) {
   556  			return block, nil
   557  		}
   558  
   559  		return nil, errors.New("", "block hash verification failed in confirmation")
   560  	}
   561  
   562  	return nil, errors.New("", "txn confirmation not found.")
   563  }
   564  
   565  func getBlockInfoByRound(round int64, content string) (*blockHeader, error) {
   566  	numSharders := len(Sharders.Healthy()) // overwrite, use all
   567  	resultC := make(chan *util.GetResponse, numSharders)
   568  	Sharders.QueryFromSharders(numSharders, fmt.Sprintf("%vround=%v&content=%v", GET_BLOCK_INFO, round, content), resultC)
   569  	var (
   570  		maxConsensus   int
   571  		roundConsensus = make(map[string]int)
   572  		waitTime       = time.NewTimer(10 * time.Second)
   573  		failedCount    int
   574  	)
   575  
   576  	type blockRound struct {
   577  		Header blockHeader `json:"header"`
   578  	}
   579  
   580  	for i := 0; i < numSharders; i++ {
   581  		select {
   582  		case <-waitTime.C:
   583  			return nil, stdErrors.New("failed to get block info by round with consensus, timeout")
   584  		case rsp := <-resultC:
   585  			if rsp == nil {
   586  				logging.Error("nil response")
   587  				continue
   588  			}
   589  			logging.Debug(rsp.Url, rsp.Status)
   590  			if failedCount*100/numSharders > 100-consensusThresh {
   591  				return nil, stdErrors.New("failed to get block info by round with consensus, too many failures")
   592  			}
   593  
   594  			if rsp.StatusCode != http.StatusOK {
   595  				logging.Debug(rsp.Url, "no round confirmation. Resp:", rsp.Body)
   596  				failedCount++
   597  				continue
   598  			}
   599  
   600  			var br blockRound
   601  			err := json.Unmarshal([]byte(rsp.Body), &br)
   602  			if err != nil {
   603  				logging.Error("round info parse error. ", err)
   604  				failedCount++
   605  				continue
   606  			}
   607  
   608  			if len(br.Header.Hash) == 0 {
   609  				failedCount++
   610  				continue
   611  			}
   612  
   613  			h := br.Header.Hash
   614  			roundConsensus[h]++
   615  			if roundConsensus[h] > maxConsensus {
   616  				maxConsensus = roundConsensus[h]
   617  				if maxConsensus*100/numSharders >= consensusThresh {
   618  					return &br.Header, nil
   619  				}
   620  			}
   621  		}
   622  	}
   623  
   624  	return nil, stdErrors.New("failed to get block info by round with consensus")
   625  }
   626  
   627  func isBlockExtends(prevHash string, block *blockHeader) bool {
   628  	data := fmt.Sprintf("%v:%v:%v:%v:%v:%v:%v:%v", block.MinerId, prevHash, block.CreationDate, block.Round,
   629  		block.RoundRandomSeed, block.StateChangesCount, block.MerkleTreeRoot, block.ReceiptMerkleTreeRoot)
   630  	h := encryption.Hash(data)
   631  	return block.Hash == h
   632  }
   633  
   634  func validateChain(confirmBlock *blockHeader) bool {
   635  	confirmRound := confirmBlock.Round
   636  	logging.Debug("Confirmation round: ", confirmRound)
   637  	currentBlockHash := confirmBlock.Hash
   638  	round := confirmRound + 1
   639  	for {
   640  		nextBlock, err := getBlockInfoByRound(round, "header")
   641  		if err != nil {
   642  			logging.Info(err, " after a second falling thru to ", getMinShardersVerify(), "of ", len(_config.chain.Sharders), "Sharders", len(Sharders.Healthy()), "Healthy sharders")
   643  			sys.Sleep(1 * time.Second)
   644  			nextBlock, err = getBlockInfoByRound(round, "header")
   645  			if err != nil {
   646  				logging.Error(err, " block chain stalled. waiting", defaultWaitSeconds, "...")
   647  				sys.Sleep(defaultWaitSeconds)
   648  				continue
   649  			}
   650  		}
   651  		if isBlockExtends(currentBlockHash, nextBlock) {
   652  			currentBlockHash = nextBlock.Hash
   653  			round++
   654  		}
   655  		if (round > confirmRound) && (round-confirmRound < getMinRequiredChainLength()) {
   656  			continue
   657  		}
   658  		if round < confirmRound {
   659  			return false
   660  		}
   661  		// Validation success
   662  		break
   663  	}
   664  	return true
   665  }
   666  func (t *Transaction) isTransactionExpired(lfbCreationTime, currentTime int64) bool {
   667  	// latest finalized block zero implies no response. use currentTime as lfb
   668  	if lfbCreationTime == 0 {
   669  		lfbCreationTime = currentTime
   670  	}
   671  	if util.MinInt64(lfbCreationTime, currentTime) > (t.txn.CreationDate + int64(defaultTxnExpirationSeconds)) {
   672  		return true
   673  	}
   674  	// Wait for next retry
   675  	sys.Sleep(defaultWaitSeconds)
   676  	return false
   677  }
   678  
   679  // GetVerifyOutput implements the verification output from sharders
   680  func (t *Transaction) GetVerifyOutput() string {
   681  	if t.verifyStatus == StatusSuccess {
   682  		return t.verifyOut
   683  	}
   684  	return ""
   685  }
   686  
   687  // GetTransactionError implements error string in case of transaction failure
   688  func (t *Transaction) GetTransactionError() string {
   689  	if t.txnStatus != StatusSuccess {
   690  		return t.txnError.Error()
   691  	}
   692  	return ""
   693  }
   694  
   695  // GetVerifyError implements error string in case of verify failure error
   696  func (t *Transaction) GetVerifyError() string {
   697  	if t.verifyStatus != StatusSuccess {
   698  		return t.verifyError.Error()
   699  	}
   700  	return ""
   701  }
   702  
   703  // GetTransactionNonce returns nonce
   704  func (t *Transaction) GetTransactionNonce() int64 {
   705  	return t.txn.TransactionNonce
   706  }
   707  
   708  // ========================================================================== //
   709  //                               vesting pool                                 //
   710  // ========================================================================== //
   711  
   712  type vestingRequest struct {
   713  	PoolID common.Key `json:"pool_id"`
   714  }
   715  
   716  type VestingStopRequest struct {
   717  	PoolID      string `json:"pool_id"`
   718  	Destination string `json:"destination"`
   719  }
   720  
   721  type scCollectReward struct {
   722  	ProviderId   string `json:"provider_id"`
   723  	ProviderType int    `json:"provider_type"`
   724  }
   725  
   726  type MinerSCLock struct {
   727  	ID string `json:"id"`
   728  }
   729  
   730  type MinerSCUnlock struct {
   731  	ID string `json:"id"`
   732  }
   733  
   734  type CommitMetaData struct {
   735  	CrudType string
   736  	MetaData *ConsolidatedFileMeta
   737  }
   738  
   739  type CommitMetaResponse struct {
   740  	TxnID    string
   741  	MetaData *ConsolidatedFileMeta
   742  }
   743  
   744  type ConsolidatedFileMeta struct {
   745  	Name            string
   746  	Type            string
   747  	Path            string
   748  	LookupHash      string
   749  	Hash            string
   750  	MimeType        string
   751  	Size            int64
   752  	NumBlocks       int64
   753  	ActualFileSize  int64
   754  	ActualNumBlocks int64
   755  	EncryptedKey    string
   756  
   757  	ActualThumbnailSize int64
   758  	ActualThumbnailHash string
   759  
   760  	Collaborators []fileref.Collaborator
   761  }
   762  
   763  func VerifyContentHash(metaTxnDataJSON string) (bool, error) {
   764  	var metaTxnData CommitMetaResponse
   765  	err := json.Unmarshal([]byte(metaTxnDataJSON), &metaTxnData)
   766  	if err != nil {
   767  		return false, errors.New("metaTxnData_decode_error", "Unable to decode metaTxnData json")
   768  	}
   769  
   770  	t, err := transaction.VerifyTransaction(metaTxnData.TxnID, blockchain.GetSharders())
   771  	if err != nil {
   772  		return false, errors.New("fetch_txm_details", "Unable to fetch txn details")
   773  	}
   774  
   775  	var metaOperation CommitMetaData
   776  	err = json.Unmarshal([]byte(t.TransactionData), &metaOperation)
   777  	if err != nil {
   778  		logging.Error("Unmarshal of transaction data to fileMeta failed, Maybe not a commit meta txn :", t.Hash)
   779  		return false, nil
   780  	}
   781  
   782  	return metaOperation.MetaData.Hash == metaTxnData.MetaData.Hash, nil
   783  }
   784  
   785  //
   786  // Storage SC transactions
   787  //