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 //