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 }