github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/transaction_id.go (about)

     1  package hedera
     2  
     3  /*-
     4   *
     5   * Hedera Go SDK
     6   *
     7   * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC
     8   *
     9   * Licensed under the Apache License, Version 2.0 (the "License");
    10   * you may not use this file except in compliance with the License.
    11   * You may obtain a copy of the License at
    12   *
    13   *      http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing, software
    16   * distributed under the License is distributed on an "AS IS" BASIS,
    17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    18   * See the License for the specific language governing permissions and
    19   * limitations under the License.
    20   *
    21   */
    22  
    23  import (
    24  	"fmt"
    25  	"math/rand"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/pkg/errors"
    31  	protobuf "google.golang.org/protobuf/proto"
    32  
    33  	"github.com/hashgraph/hedera-protobufs-go/services"
    34  )
    35  
    36  // TransactionID is the id used to identify a Transaction on the Hedera _Network. It consists of an AccountID and a
    37  // a valid start time.
    38  type TransactionID struct {
    39  	AccountID  *AccountID
    40  	ValidStart *time.Time
    41  	scheduled  bool
    42  	Nonce      *int32
    43  }
    44  
    45  // NewTransactionID constructs a new Transaction id struct with the provided AccountID and the valid start time set
    46  // to the current time - 10 seconds.
    47  func TransactionIDGenerate(accountID AccountID) TransactionID {
    48  	allowance := -(time.Duration(rand.Int63n(5*int64(time.Second))) + (8 * time.Second)) // nolint
    49  	validStart := time.Now().UTC().Add(allowance)
    50  
    51  	return TransactionID{&accountID, &validStart, false, nil}
    52  }
    53  
    54  // NewTransactionIDWithValidStart constructs a new Transaction id struct with the provided AccountID and the valid start
    55  // time set to a provided time.
    56  func NewTransactionIDWithValidStart(accountID AccountID, validStart time.Time) TransactionID {
    57  	return TransactionID{&accountID, &validStart, false, nil}
    58  }
    59  
    60  // GetReceipt queries the _Network for a receipt corresponding to the TransactionID's transaction. If the status of the
    61  // receipt is exceptional an ErrHederaReceiptStatus will be returned alongside the receipt, otherwise only the receipt
    62  // will be returned.
    63  func (id TransactionID) GetReceipt(client *Client) (TransactionReceipt, error) {
    64  	return NewTransactionReceiptQuery().
    65  		SetTransactionID(id).
    66  		Execute(client)
    67  }
    68  
    69  // GetRecord queries the _Network for a record corresponding to the TransactionID's transaction. If the status of the
    70  // record's receipt is exceptional an ErrHederaRecordStatus will be returned alongside the record, otherwise, only the
    71  // record will be returned. If consensus has not been reached, this function will return a HederaReceiptError with a
    72  // status of StatusBusy.
    73  func (id TransactionID) GetRecord(client *Client) (TransactionRecord, error) {
    74  	_, err := NewTransactionReceiptQuery().
    75  		SetTransactionID(id).
    76  		Execute(client)
    77  
    78  	if err != nil {
    79  		return TransactionRecord{}, err
    80  	}
    81  
    82  	return NewTransactionRecordQuery().
    83  		SetTransactionID(id).
    84  		Execute(client)
    85  }
    86  
    87  // String returns a string representation of the TransactionID in `AccountID@ValidStartSeconds.ValidStartNanos?scheduled_bool/nonce` format
    88  func (id TransactionID) String() string {
    89  	var pb *services.Timestamp
    90  	var returnString string
    91  	if id.AccountID != nil && id.ValidStart != nil {
    92  		pb = _TimeToProtobuf(*id.ValidStart)
    93  		// Use fmt.Sprintf to format the string with leading zeros
    94  		returnString = id.AccountID.String() + "@" + fmt.Sprintf("%d.%09d", pb.Seconds, pb.Nanos)
    95  	}
    96  
    97  	if id.scheduled {
    98  		returnString += "?scheduled"
    99  	}
   100  
   101  	if id.Nonce != nil {
   102  		returnString += "/" + fmt.Sprint(*id.Nonce)
   103  	}
   104  
   105  	return returnString
   106  }
   107  
   108  // TransactionIDFromString constructs a TransactionID from a string representation
   109  func TransactionIdFromString(data string) (TransactionID, error) { // nolint
   110  	parts := strings.SplitN(data, "/", 2)
   111  
   112  	var nonce *int32
   113  	if len(parts) == 2 {
   114  		temp, _ := strconv.ParseInt(parts[1], 10, 32)
   115  		temp32 := int32(temp)
   116  		nonce = &temp32
   117  	}
   118  	parts = strings.SplitN(parts[0], "?", 2)
   119  
   120  	var accountID *AccountID
   121  	var validStart *time.Time
   122  	scheduled := len(parts) == 2 && strings.Compare(parts[1], "scheduled") == 0
   123  
   124  	parts = strings.SplitN(parts[0], "@", 2)
   125  
   126  	if len(parts) != 2 {
   127  		return TransactionID{}, errors.New("expecting [{account}@{seconds}.{nanos}|{nonce}][?scheduled]")
   128  	}
   129  
   130  	temp, err := AccountIDFromString(parts[0])
   131  	accountID = &temp
   132  	if err != nil {
   133  		return TransactionID{}, err
   134  	}
   135  
   136  	validStartParts := strings.SplitN(parts[1], ".", 2)
   137  
   138  	if len(validStartParts) != 2 {
   139  		return TransactionID{}, errors.New("expecting {account}@{seconds}.{nanos}")
   140  	}
   141  
   142  	sec, err := strconv.ParseInt(validStartParts[0], 10, 64)
   143  	if err != nil {
   144  		return TransactionID{}, err
   145  	}
   146  
   147  	nano, err := strconv.ParseInt(validStartParts[1], 10, 64)
   148  	if err != nil {
   149  		return TransactionID{}, err
   150  	}
   151  
   152  	temp2 := time.Unix(sec, nano)
   153  	validStart = &temp2
   154  
   155  	return TransactionID{
   156  		AccountID:  accountID,
   157  		ValidStart: validStart,
   158  		scheduled:  scheduled,
   159  		Nonce:      nonce,
   160  	}, nil
   161  }
   162  
   163  func (id TransactionID) _ToProtobuf() *services.TransactionID {
   164  	var validStart *services.Timestamp
   165  	if id.ValidStart != nil {
   166  		validStart = _TimeToProtobuf(*id.ValidStart)
   167  	}
   168  
   169  	var accountID *services.AccountID
   170  	if id.AccountID != nil {
   171  		accountID = id.AccountID._ToProtobuf()
   172  	}
   173  
   174  	var nonce int32
   175  	if id.Nonce != nil {
   176  		nonce = *id.Nonce
   177  	}
   178  
   179  	return &services.TransactionID{
   180  		TransactionValidStart: validStart,
   181  		AccountID:             accountID,
   182  		Scheduled:             id.scheduled,
   183  		Nonce:                 nonce,
   184  	}
   185  }
   186  
   187  func _TransactionIDFromProtobuf(pb *services.TransactionID) TransactionID {
   188  	if pb == nil {
   189  		return TransactionID{}
   190  	}
   191  	var validStart time.Time
   192  	if pb.TransactionValidStart != nil {
   193  		validStart = _TimeFromProtobuf(pb.TransactionValidStart)
   194  	}
   195  
   196  	var accountID AccountID
   197  	if pb.AccountID != nil {
   198  		accountID = *_AccountIDFromProtobuf(pb.AccountID)
   199  	}
   200  
   201  	var nonce *int32
   202  	if pb.Nonce != 0 {
   203  		nonce = &pb.Nonce
   204  	}
   205  
   206  	return TransactionID{&accountID, &validStart, pb.Scheduled, nonce}
   207  }
   208  
   209  // ToBytes returns a byte array representation of the TransactionID
   210  func (id TransactionID) ToBytes() []byte {
   211  	data, err := protobuf.Marshal(id._ToProtobuf())
   212  	if err != nil {
   213  		return make([]byte, 0)
   214  	}
   215  
   216  	return data
   217  }
   218  
   219  // TransactionIDFromBytes constructs a TransactionID from a byte array
   220  func TransactionIDFromBytes(data []byte) (TransactionID, error) {
   221  	if data == nil {
   222  		return TransactionID{}, errByteArrayNull
   223  	}
   224  	pb := services.TransactionID{}
   225  	err := protobuf.Unmarshal(data, &pb)
   226  	if err != nil {
   227  		return TransactionID{}, err
   228  	}
   229  
   230  	return _TransactionIDFromProtobuf(&pb), nil
   231  }
   232  
   233  // SetScheduled sets the scheduled flag on the TransactionID
   234  func (id TransactionID) SetScheduled(scheduled bool) TransactionID {
   235  	id.scheduled = scheduled
   236  	return id
   237  }
   238  
   239  // GetScheduled returns the scheduled flag on the TransactionID
   240  func (id TransactionID) GetScheduled() bool {
   241  	return id.scheduled
   242  }
   243  
   244  // SetNonce sets the nonce on the TransactionID
   245  func (id TransactionID) SetNonce(nonce int32) TransactionID {
   246  	id.Nonce = &nonce
   247  	return id
   248  }
   249  
   250  // GetNonce returns the nonce on the TransactionID
   251  func (id TransactionID) GetNonce() int32 {
   252  	if id.Nonce != nil {
   253  		return *id.Nonce
   254  	}
   255  	return 0
   256  }