github.com/0chain/gosdk@v1.17.11/zcnbridge/transaction/txn.go (about)

     1  package transaction
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/0chain/gosdk/core/util"
    11  	"github.com/0chain/gosdk/zcnbridge/errors"
    12  	ctime "github.com/0chain/gosdk/zcnbridge/time"
    13  	"github.com/0chain/gosdk/zcncore"
    14  )
    15  
    16  var (
    17  	_ zcncore.TransactionCallback = (*callback)(nil)
    18  )
    19  
    20  type (
    21  	// TransactionProvider ...
    22  	TransactionProvider interface {
    23  		NewTransactionEntity(txnFee uint64) (Transaction, error)
    24  	}
    25  
    26  	// transactionProvider ...
    27  	transactionProvider struct{}
    28  
    29  	// Transaction interface describes transaction entity.
    30  	Transaction interface {
    31  		ExecuteSmartContract(ctx context.Context, address, funcName string, input interface{}, val uint64) (string, error)
    32  		Verify(ctx context.Context) error
    33  		GetScheme() zcncore.TransactionScheme
    34  		GetCallback() TransactionCallbackAwaitable
    35  		GetTransactionOutput() string
    36  		GetHash() string
    37  		SetHash(string)
    38  	}
    39  
    40  	// TransactionEntity entity that encapsulates the transaction related data and metadata.
    41  	transactionEntity struct {
    42  		Hash              string `json:"hash,omitempty"`
    43  		Version           string `json:"version,omitempty"`
    44  		TransactionOutput string `json:"transaction_output,omitempty"`
    45  		scheme            zcncore.TransactionScheme
    46  		callBack          TransactionCallbackAwaitable
    47  	}
    48  )
    49  
    50  type (
    51  	verifyOutput struct {
    52  		Confirmation confirmation `json:"confirmation"`
    53  	}
    54  
    55  	// confirmation represents the acceptance that a transaction is included into the blockchain.
    56  	confirmation struct {
    57  		Version               string             `json:"version"`
    58  		Hash                  string             `json:"hash"`
    59  		BlockHash             string             `json:"block_hash"`
    60  		PreviousBlockHash     string             `json:"previous_block_hash"`
    61  		Transaction           *transactionEntity `json:"txn,omitempty"`
    62  		CreationDate          ctime.Timestamp    `json:"creation_date"`
    63  		MinerID               string             `json:"miner_id"`
    64  		Round                 int64              `json:"round"`
    65  		Status                int                `json:"transaction_status"`
    66  		RoundRandomSeed       int64              `json:"round_random_seed"`
    67  		MerkleTreeRoot        string             `json:"merkle_tree_root"`
    68  		MerkleTreePath        *util.MTPath       `json:"merkle_tree_path"`
    69  		ReceiptMerkleTreeRoot string             `json:"receipt_merkle_tree_root"`
    70  		ReceiptMerkleTreePath *util.MTPath       `json:"receipt_merkle_tree_path"`
    71  	}
    72  )
    73  
    74  func NewTransactionProvider() TransactionProvider {
    75  	return &transactionProvider{}
    76  }
    77  
    78  func (t *transactionProvider) NewTransactionEntity(txnFee uint64) (Transaction, error) {
    79  	return NewTransactionEntity(txnFee)
    80  }
    81  
    82  // NewTransactionEntity creates Transaction with initialized fields.
    83  // Sets version, client ID, creation date, public key and creates internal zcncore.TransactionScheme.
    84  func NewTransactionEntity(txnFee uint64) (Transaction, error) {
    85  	txn := &transactionEntity{
    86  		callBack: NewStatus().(*callback),
    87  	}
    88  	zcntxn, err := zcncore.NewTransaction(txn.callBack, txnFee, 0)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	txn.scheme = zcntxn
    94  
    95  	return txn, nil
    96  }
    97  
    98  // ExecuteSmartContract executes function of smart contract with provided address.
    99  //
   100  // Returns hash of executed transaction.
   101  func (t *transactionEntity) ExecuteSmartContract(ctx context.Context, address, funcName string, input interface{},
   102  	val uint64) (string, error) {
   103  	const errCode = "transaction_send"
   104  
   105  	tran, err := t.scheme.ExecuteSmartContract(address, funcName, input, val)
   106  	t.Hash = tran.Hash
   107  
   108  	if err != nil {
   109  		msg := fmt.Sprintf("error while sending txn: %v", err)
   110  		return "", errors.New(errCode, msg)
   111  	}
   112  
   113  	if err := t.callBack.WaitCompleteCall(ctx); err != nil {
   114  		msg := fmt.Sprintf("error while sending txn: %v", err)
   115  		return "", errors.New(errCode, msg)
   116  	}
   117  
   118  	if len(t.scheme.GetTransactionError()) > 0 {
   119  		return "", errors.New(errCode, t.scheme.GetTransactionError())
   120  	}
   121  
   122  	return t.scheme.Hash(), nil
   123  }
   124  
   125  func (t *transactionEntity) Verify(ctx context.Context) error {
   126  	const errCode = "transaction_verify"
   127  
   128  	err := t.scheme.Verify()
   129  	if err != nil {
   130  		msg := fmt.Sprintf("error while verifying txn: %v; txn hash: %s", err, t.scheme.GetTransactionHash())
   131  		return errors.New(errCode, msg)
   132  	}
   133  
   134  	if err := t.callBack.WaitVerifyCall(ctx); err != nil {
   135  		msg := fmt.Sprintf("error while verifying txn: %v; txn hash: %s", err, t.scheme.GetTransactionHash())
   136  		return errors.New(errCode, msg)
   137  	}
   138  
   139  	switch t.scheme.GetVerifyConfirmationStatus() {
   140  	case zcncore.ChargeableError:
   141  		return errors.New(errCode, strings.Trim(t.scheme.GetVerifyOutput(), "\""))
   142  	case zcncore.Success:
   143  		fmt.Println("Executed smart contract successfully with txn: ", t.scheme.GetTransactionHash())
   144  	default:
   145  		msg := fmt.Sprint("\nExecute smart contract failed. Unknown status code: " +
   146  			strconv.Itoa(int(t.scheme.GetVerifyConfirmationStatus())))
   147  		return errors.New(errCode, msg)
   148  	}
   149  
   150  	vo := new(verifyOutput)
   151  	if err := json.Unmarshal([]byte(t.scheme.GetVerifyOutput()), vo); err != nil {
   152  		return errors.New(errCode, "error while unmarshalling confirmation: "+err.Error()+", json: "+t.scheme.GetVerifyOutput())
   153  	}
   154  
   155  	if vo.Confirmation.Transaction != nil {
   156  		t.Hash = vo.Confirmation.Transaction.GetHash()
   157  		t.TransactionOutput = vo.Confirmation.Transaction.GetTransactionOutput()
   158  	} else {
   159  		return errors.New(errCode, "got invalid confirmation (missing transaction)")
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // GetSheme returns transaction scheme
   166  func (t *transactionEntity) GetScheme() zcncore.TransactionScheme {
   167  	return t.scheme
   168  }
   169  
   170  // GetHash returns transaction hash
   171  func (t *transactionEntity) GetHash() string {
   172  	return t.Hash
   173  }
   174  
   175  // SetHash sets transaction hash
   176  func (t *transactionEntity) SetHash(hash string) {
   177  	t.Hash = hash
   178  }
   179  
   180  // GetTransactionOutput returns transaction output
   181  func (t *transactionEntity) GetTransactionOutput() string {
   182  	return t.TransactionOutput
   183  }
   184  
   185  func (t *transactionEntity) GetCallback() TransactionCallbackAwaitable {
   186  	return t.callBack
   187  }
   188  
   189  // GetVersion returns transaction version
   190  func (t *transactionEntity) GetVersion() string {
   191  	return t.Version
   192  }
   193  
   194  // Verify checks including of transaction in the blockchain.
   195  func Verify(ctx context.Context, hash string) (Transaction, error) {
   196  	t, err := NewTransactionEntity(0)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	scheme := t.GetScheme()
   202  
   203  	if err := scheme.SetTransactionHash(hash); err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	err = t.Verify(ctx)
   208  
   209  	return t, err
   210  }