github.com/0chain/gosdk@v1.17.11/zcncore/transaction_mobile.go (about) 1 //go:build mobile 2 // +build mobile 3 4 package zcncore 5 6 import ( 7 "context" 8 "encoding/json" 9 stderrors "errors" 10 "fmt" 11 "net/http" 12 "strconv" 13 "time" 14 15 "github.com/0chain/errors" 16 "github.com/0chain/gosdk/core/block" 17 "github.com/0chain/gosdk/core/common" 18 "github.com/0chain/gosdk/core/encryption" 19 "github.com/0chain/gosdk/core/node" 20 "github.com/0chain/gosdk/core/transaction" 21 "github.com/0chain/gosdk/core/util" 22 "github.com/0chain/gosdk/core/zcncrypto" 23 ) 24 25 const ( 26 Undefined int = iota 27 Success 28 29 // ChargeableError is an error that still charges the user for the transaction. 30 ChargeableError 31 ) 32 33 // Provider represents the type of provider. 34 type Provider int 35 36 const ( 37 ProviderMiner Provider = iota + 1 38 ProviderSharder 39 ProviderBlobber 40 ProviderValidator 41 ProviderAuthorizer 42 ) 43 44 type stakePoolRequest struct { 45 ProviderType int `json:"provider_type,omitempty"` 46 ProviderID string `json:"provider_id,omitempty"` 47 } 48 49 // compiler time check 50 var ( 51 _ TransactionScheme = (*Transaction)(nil) 52 _ TransactionScheme = (*TransactionWithAuth)(nil) 53 ) 54 55 type TransactionCommon interface { 56 ExecuteSmartContract(address string, methodName string, input string, val string) error 57 58 Send(toClientID string, val string, desc string) error 59 60 VestingAdd(ar VestingAddRequest, value string) error 61 62 MinerSCLock(providerId string, providerType int, lock string) error 63 MinerSCUnlock(providerId string, providerType int) error 64 MinerSCCollectReward(providerId string, providerType int) error 65 StorageSCCollectReward(providerId string, providerType int) error 66 67 FinalizeAllocation(allocID string) error 68 CancelAllocation(allocID string) error 69 CreateAllocation(car *CreateAllocationRequest, lock string) error 70 CreateReadPool() error 71 ReadPoolLock(allocID string, blobberID string, duration int64, lock string) error 72 ReadPoolUnlock() error 73 StakePoolLock(providerId string, providerType int, lock string) error 74 StakePoolUnlock(providerId string, providerType int) error 75 UpdateBlobberSettings(blobber Blobber) error 76 UpdateAllocation(allocID string, sizeDiff int64, expirationDiff int64, lock string) error 77 WritePoolLock(allocID string, lock string) error 78 WritePoolUnlock(allocID string) error 79 80 VestingUpdateConfig(InputMap) error 81 MinerScUpdateConfig(InputMap) error 82 MinerScUpdateGlobals(InputMap) error 83 StorageScUpdateConfig(InputMap) error 84 FaucetUpdateConfig(InputMap) error 85 ZCNSCUpdateGlobalConfig(InputMap) error 86 87 MinerSCMinerSettings(MinerSCMinerInfo) error 88 MinerSCSharderSettings(MinerSCMinerInfo) error 89 MinerSCDeleteMiner(MinerSCMinerInfo) error 90 MinerSCDeleteSharder(MinerSCMinerInfo) error 91 92 // ZCNSCUpdateAuthorizerConfig updates authorizer config by ID 93 ZCNSCUpdateAuthorizerConfig(AuthorizerNode) error 94 // ZCNSCAddAuthorizer adds authorizer 95 ZCNSCAddAuthorizer(AddAuthorizerPayload) error 96 97 GetVerifyConfirmationStatus() int 98 } 99 100 // TransactionScheme implements few methods for block chain. 101 // 102 // Note: to be buildable on MacOSX all arguments should have names. 103 type TransactionScheme interface { 104 TransactionCommon 105 // SetTransactionCallback implements storing the callback 106 // used to call after the transaction or verification is completed 107 SetTransactionCallback(cb TransactionCallback) error 108 // StoreData implements store the data to blockchain 109 StoreData(data string) error 110 // ExecuteFaucetSCWallet implements the `Faucet Smart contract` for a given wallet 111 ExecuteFaucetSCWallet(walletStr string, methodName string, input []byte) error 112 // GetTransactionHash implements retrieval of hash of the submitted transaction 113 GetTransactionHash() string 114 // SetTransactionHash implements verify a previous transaction status 115 SetTransactionHash(hash string) error 116 // SetTransactionNonce implements method to set the transaction nonce 117 SetTransactionNonce(txnNonce int64) error 118 // Verify implements verify the transaction 119 Verify() error 120 // GetVerifyOutput implements the verification output from sharders 121 GetVerifyOutput() string 122 // GetTransactionError implements error string in case of transaction failure 123 GetTransactionError() string 124 // GetVerifyError implements error string in case of verify failure error 125 GetVerifyError() string 126 // GetTransactionNonce returns nonce 127 GetTransactionNonce() int64 128 129 // GetDetails returns transaction scheme details. 130 GetDetails() *transaction.Transaction 131 132 // Output of transaction. 133 Output() []byte 134 135 // Hash Transaction status regardless of status 136 Hash() string 137 138 // Vesting SC 139 140 VestingTrigger(poolID string) error 141 VestingStop(sr *VestingStopRequest) error 142 VestingUnlock(poolID string) error 143 VestingDelete(poolID string) error 144 } 145 146 // priceRange represents a price range allowed by user to filter blobbers. 147 type priceRange struct { 148 Min int64 `json:"min"` 149 Max int64 `json:"max"` 150 } 151 152 // createAllocationRequest is information to create allocation. 153 type createAllocationRequest struct { 154 DataShards int `json:"data_shards"` 155 ParityShards int `json:"parity_shards"` 156 Size int64 `json:"size"` 157 Expiration int64 `json:"expiration_date"` 158 Owner string `json:"owner_id"` 159 OwnerPublicKey string `json:"owner_public_key"` 160 Blobbers []string `json:"blobbers"` 161 ReadPriceRange priceRange `json:"read_price_range"` 162 WritePriceRange priceRange `json:"write_price_range"` 163 } 164 165 type CreateAllocationRequest struct { 166 DataShards int 167 ParityShards int 168 Size int64 169 Expiration int64 170 Owner string 171 OwnerPublicKey string 172 ReadPriceMin int64 173 ReadPriceMax int64 174 WritePriceMin int64 175 WritePriceMax int64 176 177 blobbers []string 178 } 179 180 func (car *CreateAllocationRequest) AddBlobber(blobber string) { 181 car.blobbers = append(car.blobbers, blobber) 182 } 183 184 func (car *CreateAllocationRequest) toCreateAllocationSCInput() *createAllocationRequest { 185 return &createAllocationRequest{ 186 DataShards: car.DataShards, 187 ParityShards: car.ParityShards, 188 Size: car.Size, 189 Expiration: car.Expiration, 190 Owner: car.Owner, 191 OwnerPublicKey: car.OwnerPublicKey, 192 Blobbers: car.blobbers, 193 ReadPriceRange: priceRange{Min: car.ReadPriceMin, Max: car.ReadPriceMax}, 194 WritePriceRange: priceRange{Min: car.WritePriceMin, Max: car.WritePriceMax}, 195 } 196 } 197 198 type StakePoolSettings struct { 199 DelegateWallet string `json:"delegate_wallet"` 200 NumDelegates int `json:"num_delegates"` 201 ServiceCharge float64 `json:"service_charge"` 202 } 203 204 type Terms struct { 205 ReadPrice int64 `json:"read_price"` // tokens / GB 206 WritePrice int64 `json:"write_price"` // tokens / GB 207 MaxOfferDuration int64 `json:"max_offer_duration"` 208 } 209 210 type Blobber interface { 211 SetTerms(readPrice int64, writePrice int64, minLockDemand float64, maxOfferDuration int64) 212 SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) 213 SetAvailable(bool) 214 } 215 216 func NewBlobber(id, baseUrl string, capacity, allocated, lastHealthCheck int64) Blobber { 217 return &blobber{ 218 ID: id, 219 BaseURL: baseUrl, 220 Capacity: capacity, 221 Allocated: allocated, 222 LastHealthCheck: lastHealthCheck, 223 } 224 } 225 226 type blobber struct { 227 ID string `json:"id"` 228 BaseURL string `json:"url"` 229 Capacity int64 `json:"capacity"` 230 Allocated int64 `json:"allocated"` 231 LastHealthCheck int64 `json:"last_health_check"` 232 Terms Terms `json:"terms"` 233 StakePoolSettings StakePoolSettings `json:"stake_pool_settings"` 234 NotAvailable bool `json:"not_available"` 235 } 236 237 func (b *blobber) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) { 238 b.StakePoolSettings = StakePoolSettings{ 239 DelegateWallet: delegateWallet, 240 NumDelegates: numDelegates, 241 ServiceCharge: serviceCharge, 242 } 243 } 244 245 func (b *blobber) SetTerms(readPrice int64, writePrice int64, minLockDemand float64, maxOfferDuration int64) { 246 b.Terms = Terms{ 247 ReadPrice: readPrice, 248 WritePrice: writePrice, 249 MaxOfferDuration: maxOfferDuration, 250 } 251 } 252 253 func (b *blobber) SetAvailable(availability bool) { 254 b.NotAvailable = availability 255 } 256 257 type Validator interface { 258 SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) 259 } 260 261 func NewValidator(id string, baseUrl string) Validator { 262 return &validator{ 263 ID: common.Key(id), 264 BaseURL: baseUrl, 265 } 266 } 267 268 type validator struct { 269 ID common.Key `json:"id"` 270 BaseURL string `json:"url"` 271 StakePoolSettings StakePoolSettings `json:"stake_pool_settings"` 272 } 273 274 func (v *validator) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) { 275 v.StakePoolSettings = StakePoolSettings{ 276 DelegateWallet: delegateWallet, 277 NumDelegates: numDelegates, 278 ServiceCharge: serviceCharge, 279 } 280 } 281 282 // AddAuthorizerPayload is the interface gathering the functions to add a new authorizer. 283 type AddAuthorizerPayload interface { 284 // SetStakePoolSettings sets the stake pool settings for the authorizer. 285 SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) 286 } 287 288 // NewAddAuthorizerPayload creates a new AddAuthorizerPayload concrete instance. 289 func NewAddAuthorizerPayload(pubKey, url string) AddAuthorizerPayload { 290 return &addAuthorizerPayload{ 291 PublicKey: pubKey, 292 URL: url, 293 } 294 } 295 296 type addAuthorizerPayload struct { 297 PublicKey string `json:"public_key"` 298 URL string `json:"url"` 299 StakePoolSettings AuthorizerStakePoolSettings `json:"stake_pool_settings"` // Used to initially create stake pool 300 } 301 302 // SetStakePoolSettings sets the stake pool settings for the authorizer. 303 func (a *addAuthorizerPayload) SetStakePoolSettings(delegateWallet string, numDelegates int, serviceCharge float64) { 304 a.StakePoolSettings = AuthorizerStakePoolSettings{ 305 DelegateWallet: delegateWallet, 306 NumDelegates: numDelegates, 307 ServiceCharge: serviceCharge, 308 } 309 } 310 311 type AuthorizerHealthCheckPayload struct { 312 ID string `json:"id"` // authorizer ID 313 } 314 315 // AuthorizerStakePoolSettings represents configuration of an authorizer stake pool. 316 type AuthorizerStakePoolSettings struct { 317 DelegateWallet string `json:"delegate_wallet"` 318 NumDelegates int `json:"num_delegates"` 319 ServiceCharge float64 `json:"service_charge"` 320 } 321 322 // AuthorizerConfig represents configuration of an authorizer node. 323 type AuthorizerConfig struct { 324 Fee int64 `json:"fee"` 325 } 326 327 type VestingDest struct { 328 ID string `json:"id"` // destination ID 329 Amount int64 `json:"amount"` // amount to vest for the destination 330 } 331 332 type VestingAddRequest interface { 333 AddDestinations(id string, amount int64) 334 } 335 336 func NewVestingAddRequest(desc string, startTime int64, duration int64) VestingAddRequest { 337 return &vestingAddRequest{ 338 Description: desc, 339 StartTime: startTime, 340 Duration: duration, 341 } 342 } 343 344 type vestingAddRequest struct { 345 Description string `json:"description"` // allow empty 346 StartTime int64 `json:"start_time"` // 347 Duration int64 `json:"duration"` // 348 Destinations []*VestingDest `json:"destinations"` // 349 } 350 351 func (vr *vestingAddRequest) AddDestinations(id string, amount int64) { 352 vr.Destinations = append(vr.Destinations, &VestingDest{ID: id, Amount: amount}) 353 } 354 355 // InputMap represents an interface of functions to add fields to a map. 356 type InputMap interface { 357 // AddField adds a field to the map. 358 // - key: field key 359 // - value: field value 360 AddField(key, value string) 361 } 362 363 type inputMap struct { 364 Fields map[string]string `json:"fields"` 365 } 366 367 // NewInputMap creates a new InputMap concrete instance. 368 func NewInputMap() InputMap { 369 return &inputMap{ 370 Fields: make(map[string]string), 371 } 372 } 373 374 func (im *inputMap) AddField(key, value string) { 375 im.Fields[key] = value 376 } 377 378 func parseCoinStr(vs string) (uint64, error) { 379 if vs == "" { 380 return 0, nil 381 } 382 383 v, err := strconv.ParseUint(vs, 10, 64) 384 if err != nil { 385 return 0, fmt.Errorf("invalid token value: %v, err: %v", vs, err) 386 } 387 388 return v, nil 389 } 390 391 func newTransaction(cb TransactionCallback, txnFee string, nonce int64) (*Transaction, error) { 392 t := &Transaction{} 393 t.txn = transaction.NewTransactionEntity(_config.wallet.ClientID, _config.chain.ChainID, _config.wallet.ClientKey, nonce) 394 t.txnStatus, t.verifyStatus = StatusUnknown, StatusUnknown 395 t.txnCb = cb 396 t.txn.TransactionNonce = nonce 397 t.txn.TransactionFee = txnFee 398 return t, nil 399 } 400 401 // NewTransaction new generic transaction object for any operation 402 // - cb: callback for transaction state 403 // - txnFee: Transaction fees (in SAS tokens) 404 // - nonce: latest nonce of current wallet. please set it with 0 if you don't know the latest value 405 func NewTransaction(cb TransactionCallback, txnFee string, nonce int64) (TransactionScheme, error) { 406 err := CheckConfig() 407 if err != nil { 408 return nil, err 409 } 410 411 if _config.isSplitWallet { 412 if _config.authUrl == "" { 413 return nil, errors.New("", "auth url not set") 414 } 415 logging.Info("New transaction interface with auth") 416 return newTransactionWithAuth(cb, txnFee, nonce) 417 } 418 419 logging.Info("New transaction interface") 420 t, err := newTransaction(cb, txnFee, nonce) 421 return t, err 422 } 423 424 func (t *Transaction) GetDetails() *transaction.Transaction { 425 return t.txn 426 } 427 428 func (t *Transaction) createSmartContractTxn(address, methodName string, input interface{}, value string, opts ...FeeOption) error { 429 sn := transaction.SmartContractTxnData{Name: methodName, InputArgs: input} 430 snBytes, err := json.Marshal(sn) 431 if err != nil { 432 return errors.Wrap(err, "create smart contract failed due to invalid data") 433 } 434 435 t.txn.TransactionType = transaction.TxnTypeSmartContract 436 t.txn.ToClientID = address 437 t.txn.TransactionData = string(snBytes) 438 t.txn.Value = value 439 440 transactionFeeRaw, err := strconv.ParseUint(t.txn.TransactionFee, 0, 64) 441 if err != nil { 442 return err 443 } 444 445 if transactionFeeRaw > 0 { 446 return nil 447 } 448 449 tf := &TxnFeeOption{} 450 for _, opt := range opts { 451 opt(tf) 452 } 453 454 if tf.noEstimateFee { 455 return nil 456 } 457 458 // TODO: check if transaction is exempt to avoid unnecessary fee estimation 459 minFee, err := transaction.EstimateFee(t.txn, _config.chain.Miners, 0.2) 460 if err != nil { 461 return err 462 } 463 464 t.txn.TransactionFee = strconv.FormatUint(minFee, 10) 465 466 return nil 467 } 468 469 func (t *Transaction) createFaucetSCWallet(walletStr string, methodName string, input []byte) (*zcncrypto.Wallet, error) { 470 w, err := getWallet(walletStr) 471 if err != nil { 472 fmt.Printf("Error while parsing the wallet. %v\n", err) 473 return nil, err 474 } 475 err = t.createSmartContractTxn(FaucetSmartContractAddress, methodName, input, "0") 476 if err != nil { 477 return nil, err 478 } 479 return w, nil 480 } 481 482 // ExecuteSmartContract prepare and send a smart contract transaction to the blockchain 483 func (t *Transaction) ExecuteSmartContract(address string, methodName string, input string, val string) error { 484 err := t.createSmartContractTxn(address, methodName, input, val) 485 if err != nil { 486 return err 487 } 488 go func() { 489 t.setNonceAndSubmit() 490 }() 491 492 return nil 493 } 494 495 // Send to send a transaction to a given clientID 496 func (t *Transaction) Send(toClientID string, val string, desc string) error { 497 txnData, err := json.Marshal(SendTxnData{Note: desc}) 498 if err != nil { 499 return errors.New("", "Could not serialize description to transaction_data") 500 } 501 go func() { 502 t.txn.TransactionType = transaction.TxnTypeSend 503 t.txn.ToClientID = toClientID 504 t.txn.Value = val 505 t.txn.TransactionData = string(txnData) 506 t.setNonceAndSubmit() 507 }() 508 return nil 509 } 510 511 // SendWithSignatureHash to send a transaction to a given clientID with a signature hash 512 // - toClientID: client ID in the To field of the transaction 513 // - val: amount of tokens to send 514 // - desc: description of the transaction 515 // - sig: signature hash 516 // - CreationDate: creation date of the transaction 517 // - hash: hash of the transaction 518 func (t *Transaction) SendWithSignatureHash(toClientID string, val string, desc string, sig string, CreationDate int64, hash string) error { 519 txnData, err := json.Marshal(SendTxnData{Note: desc}) 520 if err != nil { 521 return errors.New("", "Could not serialize description to transaction_data") 522 } 523 go func() { 524 t.txn.TransactionType = transaction.TxnTypeSend 525 t.txn.ToClientID = toClientID 526 t.txn.Value = val 527 t.txn.Hash = hash 528 t.txn.TransactionData = string(txnData) 529 t.txn.Signature = sig 530 t.txn.CreationDate = CreationDate 531 t.setNonceAndSubmit() 532 }() 533 return nil 534 } 535 536 func (t *Transaction) VestingAdd(ar VestingAddRequest, value string) ( 537 err error) { 538 err = t.createSmartContractTxn(VestingSmartContractAddress, 539 transaction.VESTING_ADD, ar, value) 540 if err != nil { 541 logging.Error(err) 542 return 543 } 544 go func() { t.setNonceAndSubmit() }() 545 return 546 } 547 548 func (t *Transaction) VestingStop(sr *VestingStopRequest) (err error) { 549 err = t.createSmartContractTxn(VestingSmartContractAddress, 550 transaction.VESTING_STOP, sr, "0") 551 if err != nil { 552 logging.Error(err) 553 return 554 } 555 go func() { t.setNonceAndSubmit() }() 556 return 557 } 558 559 func (t *Transaction) vestingPoolTxn(function string, poolID string, value string) error { 560 561 return t.createSmartContractTxn(VestingSmartContractAddress, 562 function, vestingRequest{PoolID: common.Key(poolID)}, value) 563 } 564 565 func (t *Transaction) VestingTrigger(poolID string) (err error) { 566 err = t.vestingPoolTxn(transaction.VESTING_TRIGGER, poolID, "0") 567 if err != nil { 568 logging.Error(err) 569 return 570 } 571 go func() { t.setNonceAndSubmit() }() 572 return 573 } 574 575 func (t *Transaction) VestingUnlock(poolID string) (err error) { 576 err = t.vestingPoolTxn(transaction.VESTING_UNLOCK, poolID, "0") 577 if err != nil { 578 logging.Error(err) 579 return 580 } 581 go func() { t.setNonceAndSubmit() }() 582 return 583 } 584 585 func (t *Transaction) VestingDelete(poolID string) (err error) { 586 err = t.vestingPoolTxn(transaction.VESTING_DELETE, poolID, "0") 587 if err != nil { 588 logging.Error(err) 589 return 590 } 591 go func() { t.setNonceAndSubmit() }() 592 return 593 } 594 595 func (t *Transaction) MinerSCLock(providerId string, providerType int, lock string) error { 596 pr := stakePoolRequest{ 597 ProviderType: providerType, 598 ProviderID: providerId, 599 } 600 err := t.createSmartContractTxn(MinerSmartContractAddress, 601 transaction.MINERSC_LOCK, pr, lock) 602 if err != nil { 603 logging.Error(err) 604 return err 605 } 606 go func() { t.setNonceAndSubmit() }() 607 return err 608 } 609 610 func (t *Transaction) MinerSCUnlock(providerId string, providerType int) error { 611 pr := &stakePoolRequest{ 612 ProviderID: providerId, 613 ProviderType: providerType, 614 } 615 err := t.createSmartContractTxn(MinerSmartContractAddress, 616 transaction.MINERSC_UNLOCK, pr, "0") 617 if err != nil { 618 logging.Error(err) 619 return err 620 } 621 go func() { t.setNonceAndSubmit() }() 622 return err 623 } 624 625 func (t *Transaction) MinerSCCollectReward(providerId string, providerType int) error { 626 pr := &scCollectReward{ 627 ProviderId: providerId, 628 ProviderType: providerType, 629 } 630 631 err := t.createSmartContractTxn(MinerSmartContractAddress, 632 transaction.MINERSC_COLLECT_REWARD, pr, "0") 633 if err != nil { 634 logging.Error(err) 635 return err 636 } 637 go func() { t.setNonceAndSubmit() }() 638 return err 639 } 640 641 func (t *Transaction) StorageSCCollectReward(providerId string, providerType int) error { 642 pr := &scCollectReward{ 643 ProviderId: providerId, 644 ProviderType: providerType, 645 } 646 err := t.createSmartContractTxn(StorageSmartContractAddress, 647 transaction.STORAGESC_COLLECT_REWARD, pr, "0") 648 if err != nil { 649 logging.Error(err) 650 return err 651 } 652 go t.setNonceAndSubmit() 653 return err 654 } 655 656 // FinalizeAllocation transaction. 657 func (t *Transaction) FinalizeAllocation(allocID string) (err error) { 658 type finiRequest struct { 659 AllocationID string `json:"allocation_id"` 660 } 661 err = t.createSmartContractTxn(StorageSmartContractAddress, 662 transaction.STORAGESC_FINALIZE_ALLOCATION, &finiRequest{ 663 AllocationID: allocID, 664 }, "0") 665 if err != nil { 666 logging.Error(err) 667 return 668 } 669 go func() { t.setNonceAndSubmit() }() 670 return 671 } 672 673 // CancelAllocation transaction. 674 func (t *Transaction) CancelAllocation(allocID string) error { 675 type cancelRequest struct { 676 AllocationID string `json:"allocation_id"` 677 } 678 err := t.createSmartContractTxn(StorageSmartContractAddress, 679 transaction.STORAGESC_CANCEL_ALLOCATION, &cancelRequest{ 680 AllocationID: allocID, 681 }, "0") 682 if err != nil { 683 logging.Error(err) 684 return err 685 } 686 go func() { t.setNonceAndSubmit() }() 687 return nil 688 } 689 690 // CreateAllocation transaction. 691 func (t *Transaction) CreateAllocation(car *CreateAllocationRequest, lock string) error { 692 err := t.createSmartContractTxn(StorageSmartContractAddress, 693 transaction.STORAGESC_CREATE_ALLOCATION, car.toCreateAllocationSCInput(), lock) 694 if err != nil { 695 logging.Error(err) 696 return err 697 } 698 go func() { t.setNonceAndSubmit() }() 699 return nil 700 } 701 702 // CreateReadPool for current user. 703 func (t *Transaction) CreateReadPool() error { 704 err := t.createSmartContractTxn(StorageSmartContractAddress, 705 transaction.STORAGESC_CREATE_READ_POOL, nil, "0") 706 if err != nil { 707 logging.Error(err) 708 return err 709 } 710 go func() { t.setNonceAndSubmit() }() 711 return nil 712 } 713 714 // ReadPoolLock locks tokens for current user and given allocation, using given 715 // duration. If blobberID is not empty, then tokens will be locked for given 716 // allocation->blobber only. 717 func (t *Transaction) ReadPoolLock(allocID, blobberID string, 718 duration int64, lock string) error { 719 720 type lockRequest struct { 721 Duration time.Duration `json:"duration"` 722 AllocationID string `json:"allocation_id"` 723 BlobberID string `json:"blobber_id,omitempty"` 724 } 725 726 var lr lockRequest 727 lr.Duration = time.Duration(duration) 728 lr.AllocationID = allocID 729 lr.BlobberID = blobberID 730 731 err := t.createSmartContractTxn(StorageSmartContractAddress, 732 transaction.STORAGESC_READ_POOL_LOCK, &lr, lock) 733 if err != nil { 734 logging.Error(err) 735 return err 736 } 737 go func() { t.setNonceAndSubmit() }() 738 return nil 739 } 740 741 // ReadPoolUnlock for current user and given pool. 742 func (t *Transaction) ReadPoolUnlock() error { 743 err := t.createSmartContractTxn(StorageSmartContractAddress, 744 transaction.STORAGESC_READ_POOL_UNLOCK, nil, "0") 745 if err != nil { 746 logging.Error(err) 747 return err 748 } 749 go func() { t.setNonceAndSubmit() }() 750 return nil 751 } 752 753 // StakePoolLock used to lock tokens in a stake pool of a blobber. 754 func (t *Transaction) StakePoolLock(providerId string, providerType int, lock string) error { 755 spr := stakePoolRequest{ 756 ProviderType: providerType, 757 ProviderID: providerId, 758 } 759 760 err := t.createSmartContractTxn(StorageSmartContractAddress, 761 transaction.STORAGESC_STAKE_POOL_LOCK, &spr, lock) 762 if err != nil { 763 logging.Error(err) 764 return err 765 } 766 go func() { t.setNonceAndSubmit() }() 767 return nil 768 } 769 770 // StakePoolUnlock by blobberID 771 func (t *Transaction) StakePoolUnlock(providerId string, providerType int) error { 772 spr := stakePoolRequest{ 773 ProviderType: providerType, 774 ProviderID: providerId, 775 } 776 777 err := t.createSmartContractTxn(StorageSmartContractAddress, transaction.STORAGESC_STAKE_POOL_UNLOCK, &spr, "0") 778 if err != nil { 779 logging.Error(err) 780 return err 781 } 782 go func() { t.setNonceAndSubmit() }() 783 return nil 784 } 785 786 // UpdateBlobberSettings update settings of a blobber. 787 func (t *Transaction) UpdateBlobberSettings(b Blobber) error { 788 err := t.createSmartContractTxn(StorageSmartContractAddress, 789 transaction.STORAGESC_UPDATE_BLOBBER_SETTINGS, b, "0") 790 if err != nil { 791 logging.Error(err) 792 return err 793 } 794 go func() { t.setNonceAndSubmit() }() 795 return nil 796 } 797 798 // UpdateAllocation transaction. 799 func (t *Transaction) UpdateAllocation(allocID string, sizeDiff int64, 800 expirationDiff int64, lock string) error { 801 type updateAllocationRequest struct { 802 ID string `json:"id"` // allocation id 803 Size int64 `json:"size"` // difference 804 Expiration int64 `json:"expiration_date"` // difference 805 } 806 807 var uar updateAllocationRequest 808 uar.ID = allocID 809 uar.Size = sizeDiff 810 uar.Expiration = expirationDiff 811 812 err := t.createSmartContractTxn(StorageSmartContractAddress, 813 transaction.STORAGESC_UPDATE_ALLOCATION, &uar, lock) 814 if err != nil { 815 logging.Error(err) 816 return err 817 } 818 go func() { t.setNonceAndSubmit() }() 819 return nil 820 } 821 822 // WritePoolLock locks tokens for current user and given allocation, using given 823 // duration. If blobberID is not empty, then tokens will be locked for given 824 // allocation->blobber only. 825 func (t *Transaction) WritePoolLock(allocID, lock string) error { 826 var lr = struct { 827 AllocationID string `json:"allocation_id"` 828 }{ 829 AllocationID: allocID, 830 } 831 832 err := t.createSmartContractTxn(StorageSmartContractAddress, 833 transaction.STORAGESC_WRITE_POOL_LOCK, &lr, lock) 834 if err != nil { 835 logging.Error(err) 836 return err 837 } 838 go func() { t.setNonceAndSubmit() }() 839 return nil 840 } 841 842 // WritePoolUnlock for current user and given pool. 843 func (t *Transaction) WritePoolUnlock(allocID string) error { 844 var ur = struct { 845 AllocationID string `json:"allocation_id"` 846 }{ 847 AllocationID: allocID, 848 } 849 850 err := t.createSmartContractTxn(StorageSmartContractAddress, 851 transaction.STORAGESC_WRITE_POOL_UNLOCK, &ur, "0") 852 if err != nil { 853 logging.Error(err) 854 return err 855 } 856 go func() { t.setNonceAndSubmit() }() 857 return nil 858 } 859 860 func (t *Transaction) VestingUpdateConfig(vscc InputMap) (err error) { 861 err = t.createSmartContractTxn(VestingSmartContractAddress, 862 transaction.VESTING_UPDATE_SETTINGS, vscc, "0") 863 if err != nil { 864 logging.Error(err) 865 return 866 } 867 go func() { t.setNonceAndSubmit() }() 868 return 869 } 870 871 // faucet smart contract 872 873 func (t *Transaction) FaucetUpdateConfig(ip InputMap) (err error) { 874 err = t.createSmartContractTxn(FaucetSmartContractAddress, 875 transaction.FAUCETSC_UPDATE_SETTINGS, ip, "0") 876 if err != nil { 877 logging.Error(err) 878 return 879 } 880 go func() { t.setNonceAndSubmit() }() 881 return 882 } 883 884 // 885 // miner SC 886 // 887 888 func (t *Transaction) MinerScUpdateConfig(ip InputMap) (err error) { 889 err = t.createSmartContractTxn(MinerSmartContractAddress, 890 transaction.MINERSC_UPDATE_SETTINGS, ip, "0") 891 if err != nil { 892 logging.Error(err) 893 return 894 } 895 go func() { t.setNonceAndSubmit() }() 896 return 897 } 898 899 func (t *Transaction) MinerScUpdateGlobals(ip InputMap) (err error) { 900 err = t.createSmartContractTxn(MinerSmartContractAddress, 901 transaction.MINERSC_UPDATE_GLOBALS, ip, "0") 902 if err != nil { 903 logging.Error(err) 904 return 905 } 906 go func() { t.setNonceAndSubmit() }() 907 return 908 } 909 910 func (t *Transaction) StorageScUpdateConfig(ip InputMap) (err error) { 911 err = t.createSmartContractTxn(StorageSmartContractAddress, 912 transaction.STORAGESC_UPDATE_SETTINGS, ip, "0") 913 if err != nil { 914 logging.Error(err) 915 return 916 } 917 go func() { t.setNonceAndSubmit() }() 918 return 919 } 920 921 func (t *Transaction) ZCNSCUpdateGlobalConfig(ip InputMap) (err error) { 922 err = t.createSmartContractTxn(ZCNSCSmartContractAddress, 923 transaction.ZCNSC_UPDATE_GLOBAL_CONFIG, ip, "0") 924 if err != nil { 925 logging.Error(err) 926 return 927 } 928 go t.setNonceAndSubmit() 929 return 930 } 931 932 func (t *Transaction) GetVerifyConfirmationStatus() int { 933 return int(t.verifyConfirmationStatus) 934 } 935 936 // MinerSCMinerInfo interface for miner info functions on miner smart contract. 937 type MinerSCMinerInfo interface { 938 // GetID returns the ID of the miner 939 GetID() string 940 } 941 942 // NewMinerSCMinerInfo creates a new miner info. 943 // - id: miner ID 944 // - delegateWallet: delegate wallet 945 // - minStake: minimum stake 946 // - maxStake: maximum stake 947 // - numDelegates: number of delegates 948 // - serviceCharge: service charge 949 func NewMinerSCMinerInfo(id string, delegateWallet string, 950 minStake int64, maxStake int64, numDelegates int, serviceCharge float64) MinerSCMinerInfo { 951 return &minerSCMinerInfo{ 952 simpleMiner: simpleMiner{ID: id}, 953 minerSCDelegatePool: minerSCDelegatePool{ 954 Settings: StakePoolSettings{ 955 DelegateWallet: delegateWallet, 956 NumDelegates: numDelegates, 957 ServiceCharge: serviceCharge, 958 }, 959 }, 960 } 961 } 962 963 type minerSCMinerInfo struct { 964 simpleMiner `json:"simple_miner"` 965 minerSCDelegatePool `json:"stake_pool"` 966 } 967 968 func (mi *minerSCMinerInfo) GetID() string { 969 return mi.ID 970 } 971 972 type minerSCDelegatePool struct { 973 Settings StakePoolSettings `json:"settings"` 974 } 975 976 type simpleMiner struct { 977 ID string `json:"id"` 978 } 979 980 func (t *Transaction) MinerSCMinerSettings(info MinerSCMinerInfo) (err error) { 981 err = t.createSmartContractTxn(MinerSmartContractAddress, 982 transaction.MINERSC_MINER_SETTINGS, info, "0") 983 if err != nil { 984 logging.Error(err) 985 return 986 } 987 go func() { t.setNonceAndSubmit() }() 988 return 989 } 990 991 func (t *Transaction) MinerSCSharderSettings(info MinerSCMinerInfo) (err error) { 992 err = t.createSmartContractTxn(MinerSmartContractAddress, 993 transaction.MINERSC_SHARDER_SETTINGS, info, "0") 994 if err != nil { 995 logging.Error(err) 996 return 997 } 998 go func() { t.setNonceAndSubmit() }() 999 return 1000 } 1001 1002 func (t *Transaction) MinerSCDeleteMiner(info MinerSCMinerInfo) (err error) { 1003 err = t.createSmartContractTxn(MinerSmartContractAddress, 1004 transaction.MINERSC_MINER_DELETE, info, "0") 1005 if err != nil { 1006 logging.Error(err) 1007 return 1008 } 1009 go func() { t.setNonceAndSubmit() }() 1010 return 1011 } 1012 1013 func (t *Transaction) MinerSCDeleteSharder(info MinerSCMinerInfo) (err error) { 1014 err = t.createSmartContractTxn(MinerSmartContractAddress, 1015 transaction.MINERSC_SHARDER_DELETE, info, "0") 1016 if err != nil { 1017 logging.Error(err) 1018 return 1019 } 1020 go func() { t.setNonceAndSubmit() }() 1021 return 1022 } 1023 1024 // AuthorizerNode interface for authorizer node functions. 1025 type AuthorizerNode interface { 1026 // GetID returns the ID of the authorizer node. 1027 GetID() string 1028 } 1029 1030 // NewAuthorizerNode creates a new authorizer node. 1031 func NewAuthorizerNode(id string, fee int64) AuthorizerNode { 1032 return &authorizerNode{ 1033 ID: id, 1034 Config: &AuthorizerConfig{Fee: fee}, 1035 } 1036 } 1037 1038 type authorizerNode struct { 1039 ID string `json:"id"` 1040 Config *AuthorizerConfig `json:"config"` 1041 } 1042 1043 func (a *authorizerNode) GetID() string { 1044 return a.ID 1045 } 1046 1047 func (t *Transaction) ZCNSCUpdateAuthorizerConfig(ip AuthorizerNode) (err error) { 1048 err = t.createSmartContractTxn(ZCNSCSmartContractAddress, transaction.ZCNSC_UPDATE_AUTHORIZER_CONFIG, ip, "0") 1049 if err != nil { 1050 logging.Error(err) 1051 return 1052 } 1053 go t.setNonceAndSubmit() 1054 return 1055 } 1056 1057 func (t *Transaction) Verify() error { 1058 if t.txnHash == "" && t.txnStatus == StatusUnknown { 1059 return errors.New("", "invalid transaction. cannot be verified.") 1060 } 1061 if t.txnHash == "" && t.txnStatus == StatusSuccess { 1062 h := t.GetTransactionHash() 1063 if h == "" { 1064 node.Cache.Evict(t.txn.ClientID) 1065 return errors.New("", "invalid transaction. cannot be verified.") 1066 } 1067 } 1068 // If transaction is verify only start from current time 1069 if t.txn.CreationDate == 0 { 1070 t.txn.CreationDate = int64(common.Now()) 1071 } 1072 1073 tq, err := newTransactionQuery(Sharders.Healthy()) 1074 if err != nil { 1075 logging.Error(err) 1076 return err 1077 } 1078 1079 go func() { 1080 1081 for { 1082 1083 tq.Reset() 1084 // Get transaction confirmationBlock from a random sharder 1085 confirmBlockHeader, confirmationBlock, lfbBlockHeader, err := tq.getFastConfirmation(t.txnHash, nil) 1086 1087 if err != nil { 1088 now := int64(common.Now()) 1089 1090 // maybe it is a network or server error 1091 if lfbBlockHeader == nil { 1092 logging.Info(err, " now: ", now) 1093 } else { 1094 logging.Info(err, " now: ", now, ", LFB creation time:", lfbBlockHeader.CreationDate) 1095 } 1096 1097 // transaction is done or expired. it means random sharder might be outdated, try to query it from s/S sharders to confirm it 1098 if util.MaxInt64(lfbBlockHeader.getCreationDate(now), now) >= (t.txn.CreationDate + int64(defaultTxnExpirationSeconds)) { 1099 logging.Info("falling back to ", getMinShardersVerify(), " of ", len(_config.chain.Sharders), " Sharders", len(Sharders.Healthy()), "Healthy sharders") 1100 confirmBlockHeader, confirmationBlock, lfbBlockHeader, err = tq.getConsensusConfirmation(getMinShardersVerify(), t.txnHash, nil) 1101 } 1102 1103 // txn not found in fast confirmation/consensus confirmation 1104 if err != nil { 1105 1106 if lfbBlockHeader == nil { 1107 // no any valid lfb on all sharders. maybe they are network/server errors. try it again 1108 continue 1109 } 1110 1111 // it is expired 1112 if t.isTransactionExpired(lfbBlockHeader.getCreationDate(now), now) { 1113 t.completeVerify(StatusError, "", errors.New("", `{"error": "verify transaction failed"}`)) 1114 return 1115 } 1116 continue 1117 } 1118 1119 } 1120 1121 valid := validateChain(confirmBlockHeader) 1122 if valid { 1123 output, err := json.Marshal(confirmationBlock) 1124 if err != nil { 1125 t.completeVerify(StatusError, "", errors.New("", `{"error": "transaction confirmation json marshal error"`)) 1126 return 1127 } 1128 confJson := confirmationBlock["confirmation"] 1129 1130 var conf map[string]json.RawMessage 1131 if err := json.Unmarshal(confJson, &conf); err != nil { 1132 return 1133 } 1134 txnJson := conf["txn"] 1135 1136 var tr map[string]json.RawMessage 1137 if err := json.Unmarshal(txnJson, &tr); err != nil { 1138 return 1139 } 1140 1141 txStatus := tr["transaction_status"] 1142 switch string(txStatus) { 1143 case "1": 1144 t.completeVerifyWithConStatus(StatusSuccess, Success, string(output), nil) 1145 case "2": 1146 txOutput := tr["transaction_output"] 1147 t.completeVerifyWithConStatus(StatusSuccess, ChargeableError, string(txOutput), nil) 1148 default: 1149 t.completeVerify(StatusError, string(output), nil) 1150 } 1151 return 1152 } 1153 } 1154 }() 1155 return nil 1156 } 1157 1158 func (t *Transaction) ZCNSCAddAuthorizer(ip AddAuthorizerPayload) (err error) { 1159 err = t.createSmartContractTxn(ZCNSCSmartContractAddress, transaction.ZCNSC_ADD_AUTHORIZER, ip, "0") 1160 if err != nil { 1161 logging.Error(err) 1162 return 1163 } 1164 go t.setNonceAndSubmit() 1165 return 1166 } 1167 1168 // EstimateFee estimates transaction fee 1169 func (t *Transaction) EstimateFee(reqPercent float32) (int64, error) { 1170 fee, err := transaction.EstimateFee(t.txn, _config.chain.Miners, reqPercent) 1171 return int64(fee), err 1172 } 1173 1174 // ConvertTokenToSAS converts ZCN tokens to SAS tokens 1175 // # Inputs 1176 // - token: ZCN tokens 1177 func ConvertTokenToSAS(token float64) uint64 { 1178 return uint64(token * common.TokenUnit) 1179 } 1180 1181 // ConvertToValue converts ZCN tokens to SAS tokens with string format 1182 // - token: ZCN tokens 1183 func ConvertToValue(token float64) string { 1184 return strconv.FormatUint(ConvertTokenToSAS(token), 10) 1185 } 1186 1187 func makeTimeoutContext(tm RequestTimeout) (context.Context, func()) { 1188 1189 if tm != nil && tm.Get() > 0 { 1190 return context.WithTimeout(context.Background(), time.Millisecond*time.Duration(tm.Get())) 1191 1192 } 1193 return context.Background(), func() {} 1194 1195 } 1196 1197 func GetLatestFinalized(numSharders int, timeout RequestTimeout) (b *BlockHeader, err error) { 1198 var result = make(chan *util.GetResponse, numSharders) 1199 defer close(result) 1200 1201 ctx, cancel := makeTimeoutContext(timeout) 1202 defer cancel() 1203 1204 numSharders = len(Sharders.Healthy()) // overwrite, use all 1205 Sharders.QueryFromShardersContext(ctx, numSharders, GET_LATEST_FINALIZED, result) 1206 1207 var ( 1208 maxConsensus int 1209 roundConsensus = make(map[string]int) 1210 ) 1211 1212 for i := 0; i < numSharders; i++ { 1213 var rsp = <-result 1214 if rsp == nil { 1215 logging.Error("nil response") 1216 continue 1217 } 1218 1219 logging.Debug(rsp.Url, rsp.Status) 1220 1221 if rsp.StatusCode != http.StatusOK { 1222 logging.Error(rsp.Body) 1223 continue 1224 } 1225 1226 if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil { 1227 logging.Error("block parse error: ", err) 1228 err = nil 1229 continue 1230 } 1231 1232 var h = encryption.FastHash([]byte(b.Hash)) 1233 if roundConsensus[h]++; roundConsensus[h] > maxConsensus { 1234 maxConsensus = roundConsensus[h] 1235 } 1236 } 1237 1238 if maxConsensus == 0 { 1239 return nil, errors.New("", "block info not found") 1240 } 1241 1242 return 1243 } 1244 1245 // GetLatestFinalizedMagicBlock gets latest finalized magic block 1246 // - numSharders: number of sharders 1247 // - timeout: request timeout 1248 func GetLatestFinalizedMagicBlock(numSharders int, timeout RequestTimeout) ([]byte, error) { 1249 var result = make(chan *util.GetResponse, numSharders) 1250 defer close(result) 1251 1252 ctx, cancel := makeTimeoutContext(timeout) 1253 defer cancel() 1254 1255 numSharders = len(Sharders.Healthy()) // overwrite, use all 1256 Sharders.QueryFromShardersContext(ctx, numSharders, GET_LATEST_FINALIZED_MAGIC_BLOCK, result) 1257 1258 var ( 1259 maxConsensus int 1260 roundConsensus = make(map[string]int) 1261 m *block.MagicBlock 1262 err error 1263 ) 1264 1265 type respObj struct { 1266 MagicBlock *block.MagicBlock `json:"magic_block"` 1267 } 1268 1269 for i := 0; i < numSharders; i++ { 1270 var rsp = <-result 1271 if rsp == nil { 1272 logging.Error("nil response") 1273 continue 1274 } 1275 1276 logging.Debug(rsp.Url, rsp.Status) 1277 1278 if rsp.StatusCode != http.StatusOK { 1279 logging.Error(rsp.Body) 1280 continue 1281 } 1282 1283 var respo respObj 1284 if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil { 1285 logging.Error(" magic block parse error: ", err) 1286 err = nil 1287 continue 1288 } 1289 1290 m = respo.MagicBlock 1291 var h = encryption.FastHash([]byte(respo.MagicBlock.Hash)) 1292 if roundConsensus[h]++; roundConsensus[h] > maxConsensus { 1293 maxConsensus = roundConsensus[h] 1294 } 1295 } 1296 1297 if maxConsensus == 0 { 1298 return nil, errors.New("", "magic block info not found") 1299 } 1300 1301 if m != nil { 1302 return json.Marshal(m) 1303 } 1304 1305 return nil, err 1306 } 1307 1308 // GetChainStats gets chain stats with time out 1309 // timeout in milliseconds 1310 func GetChainStats(timeout RequestTimeout) ([]byte, error) { 1311 var result = make(chan *util.GetResponse, 1) 1312 defer close(result) 1313 1314 ctx, cancel := makeTimeoutContext(timeout) 1315 defer cancel() 1316 1317 var ( 1318 b *block.ChainStats 1319 err error 1320 ) 1321 1322 var numSharders = len(Sharders.Healthy()) // overwrite, use all 1323 Sharders.QueryFromShardersContext(ctx, numSharders, GET_CHAIN_STATS, result) 1324 var rsp *util.GetResponse 1325 for i := 0; i < numSharders; i++ { 1326 var x = <-result 1327 if x == nil { 1328 logging.Error("nil response") 1329 continue 1330 } 1331 if x.StatusCode != http.StatusOK { 1332 continue 1333 } 1334 rsp = x 1335 } 1336 1337 if rsp == nil { 1338 return nil, errors.New("http_request_failed", "Request failed with status not 200") 1339 } 1340 1341 if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil { 1342 return nil, err 1343 } 1344 1345 return []byte(rsp.Body), nil 1346 } 1347 1348 func GetFeeStats(timeout RequestTimeout) ([]byte, error) { 1349 1350 var numMiners = 4 1351 1352 if numMiners > len(_config.chain.Miners) { 1353 numMiners = len(_config.chain.Miners) 1354 } 1355 1356 var result = make(chan *util.GetResponse, numMiners) 1357 1358 ctx, cancel := makeTimeoutContext(timeout) 1359 defer cancel() 1360 1361 var ( 1362 b *block.FeeStats 1363 err error 1364 ) 1365 1366 queryFromMinersContext(ctx, numMiners, GET_FEE_STATS, result) 1367 var rsp *util.GetResponse 1368 1369 loop: 1370 for i := 0; i < numMiners; i++ { 1371 select { 1372 case x := <-result: 1373 if x.StatusCode != http.StatusOK { 1374 continue 1375 } 1376 rsp = x 1377 if rsp != nil { 1378 break loop 1379 } 1380 case <-ctx.Done(): 1381 return nil, ctx.Err() 1382 } 1383 } 1384 if rsp == nil { 1385 return nil, errors.New("http_request_failed", "Request failed with status not 200") 1386 } 1387 1388 if err = json.Unmarshal([]byte(rsp.Body), &b); err != nil { 1389 return nil, err 1390 } 1391 1392 return []byte(rsp.Body), nil 1393 } 1394 1395 type BlockHeader struct { 1396 Version string `json:"version,omitempty"` 1397 CreationDate int64 `json:"creation_date,omitempty"` 1398 Hash string `json:"hash,omitempty"` 1399 MinerID string `json:"miner_id,omitempty"` 1400 Round int64 `json:"round,omitempty"` 1401 RoundRandomSeed int64 `json:"round_random_seed,omitempty"` 1402 MerkleTreeRoot string `json:"merkle_tree_root,omitempty"` 1403 StateHash string `json:"state_hash,omitempty"` 1404 ReceiptMerkleTreeRoot string `json:"receipt_merkle_tree_root,omitempty"` 1405 NumTxns int64 `json:"num_txns,omitempty"` 1406 } 1407 1408 type Block struct { 1409 MinerID string `json:"miner_id"` 1410 Round int64 `json:"round"` 1411 RoundRandomSeed int64 `json:"round_random_seed"` 1412 RoundTimeoutCount int `json:"round_timeout_count"` 1413 1414 Hash string `json:"hash"` 1415 Signature string `json:"signature"` 1416 ChainID string `json:"chain_id"` 1417 ChainWeight float64 `json:"chain_weight"` 1418 RunningTxnCount int64 `json:"running_txn_count"` 1419 1420 Version string `json:"version"` 1421 CreationDate int64 `json:"creation_date"` 1422 1423 MagicBlockHash string `json:"magic_block_hash"` 1424 PrevHash string `json:"prev_hash"` 1425 1426 ClientStateHash string `json:"state_hash"` 1427 1428 // unexported fields 1429 header *BlockHeader `json:"-"` 1430 txns []*transaction.Transaction `json:"transactions,omitempty"` 1431 } 1432 1433 func (b *Block) GetHeader() *BlockHeader { 1434 return b.header 1435 } 1436 1437 type IterTxnFunc func(idx int, txn *transaction.Transaction) 1438 1439 type Transactions struct { 1440 txns []*transaction.Transaction 1441 } 1442 1443 func (tm *Transactions) Len() int { 1444 return len(tm.txns) 1445 } 1446 1447 func (tm *Transactions) Get(idx int) (*transaction.Transaction, error) { 1448 if idx < 0 && idx >= len(tm.txns) { 1449 return nil, stderrors.New("index out of bounds") 1450 } 1451 1452 return tm.txns[idx], nil 1453 } 1454 1455 func (b *Block) GetTxns() *Transactions { 1456 return &Transactions{ 1457 txns: b.txns, 1458 } 1459 } 1460 1461 func toMobileBlock(b *block.Block) *Block { 1462 lb := &Block{ 1463 header: &BlockHeader{ 1464 Version: b.Header.Version, 1465 CreationDate: b.Header.CreationDate, 1466 Hash: b.Header.Hash, 1467 MinerID: b.Header.MinerID, 1468 Round: b.Header.Round, 1469 RoundRandomSeed: b.Header.RoundRandomSeed, 1470 MerkleTreeRoot: b.Header.MerkleTreeRoot, 1471 StateHash: b.Header.StateHash, 1472 ReceiptMerkleTreeRoot: b.Header.ReceiptMerkleTreeRoot, 1473 NumTxns: b.Header.NumTxns, 1474 }, 1475 MinerID: string(b.MinerID), 1476 Round: b.Round, 1477 RoundRandomSeed: b.RoundRandomSeed, 1478 RoundTimeoutCount: b.RoundTimeoutCount, 1479 1480 Hash: string(b.Hash), 1481 Signature: b.Signature, 1482 ChainID: string(b.ChainID), 1483 ChainWeight: b.ChainWeight, 1484 RunningTxnCount: b.RunningTxnCount, 1485 1486 Version: b.Version, 1487 CreationDate: int64(b.CreationDate), 1488 1489 MagicBlockHash: b.MagicBlockHash, 1490 PrevHash: b.PrevHash, 1491 1492 ClientStateHash: string(b.ClientStateHash), 1493 } 1494 1495 lb.txns = make([]*transaction.Transaction, len(b.Txns)) 1496 for i, txn := range b.Txns { 1497 lb.txns[i] = txn 1498 } 1499 1500 return lb 1501 } 1502 1503 // RequestTimeout will be used for setting requests with timeout 1504 type RequestTimeout interface { 1505 Set(int64) // milliseconds 1506 Get() int64 // milliseconds 1507 } 1508 1509 type timeoutCtx struct { 1510 millisecond int64 1511 } 1512 1513 func NewRequestTimeout(timeout int64) RequestTimeout { 1514 return &timeoutCtx{millisecond: timeout} 1515 } 1516 1517 func (t *timeoutCtx) Set(tm int64) { 1518 t.millisecond = tm 1519 } 1520 1521 func (t *timeoutCtx) Get() int64 { 1522 return t.millisecond 1523 } 1524 1525 func GetBlockByRound(numSharders int, round int64, timeout RequestTimeout) (b *Block, err error) { 1526 var result = make(chan *util.GetResponse, numSharders) 1527 defer close(result) 1528 1529 ctx, cancel := makeTimeoutContext(timeout) 1530 defer cancel() 1531 1532 numSharders = len(Sharders.Healthy()) // overwrite, use all 1533 Sharders.QueryFromShardersContext(ctx, numSharders, 1534 fmt.Sprintf("%sround=%d&content=full,header", GET_BLOCK_INFO, round), 1535 result) 1536 1537 var ( 1538 maxConsensus int 1539 roundConsensus = make(map[string]int) 1540 ) 1541 1542 type respObj struct { 1543 Block *block.Block `json:"block"` 1544 Header *block.Header `json:"header"` 1545 } 1546 1547 for i := 0; i < numSharders; i++ { 1548 var rsp = <-result 1549 if rsp == nil { 1550 logging.Error("nil response") 1551 continue 1552 } 1553 logging.Debug(rsp.Url, rsp.Status) 1554 1555 if rsp.StatusCode != http.StatusOK { 1556 logging.Error(rsp.Body) 1557 continue 1558 } 1559 1560 var respo respObj 1561 if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil { 1562 logging.Error("block parse error: ", err) 1563 err = nil 1564 continue 1565 } 1566 1567 if respo.Block == nil { 1568 logging.Debug(rsp.Url, "no block in response:", rsp.Body) 1569 continue 1570 } 1571 1572 if respo.Header == nil { 1573 logging.Debug(rsp.Url, "no block header in response:", rsp.Body) 1574 continue 1575 } 1576 1577 if respo.Header.Hash != string(respo.Block.Hash) { 1578 logging.Debug(rsp.Url, "header and block hash mismatch:", rsp.Body) 1579 continue 1580 } 1581 1582 b = toMobileBlock(respo.Block) 1583 1584 b.header = &BlockHeader{ 1585 Version: respo.Header.Version, 1586 CreationDate: respo.Header.CreationDate, 1587 Hash: respo.Header.Hash, 1588 MinerID: respo.Header.MinerID, 1589 Round: respo.Header.Round, 1590 RoundRandomSeed: respo.Header.RoundRandomSeed, 1591 MerkleTreeRoot: respo.Header.MerkleTreeRoot, 1592 StateHash: respo.Header.StateHash, 1593 ReceiptMerkleTreeRoot: respo.Header.ReceiptMerkleTreeRoot, 1594 NumTxns: respo.Header.NumTxns, 1595 } 1596 1597 var h = encryption.FastHash([]byte(b.Hash)) 1598 if roundConsensus[h]++; roundConsensus[h] > maxConsensus { 1599 maxConsensus = roundConsensus[h] 1600 } 1601 } 1602 1603 if maxConsensus == 0 { 1604 return nil, errors.New("", "round info not found") 1605 } 1606 1607 return 1608 } 1609 1610 func GetMagicBlockByNumber(numSharders int, number int64, timeout RequestTimeout) ([]byte, error) { 1611 var result = make(chan *util.GetResponse, numSharders) 1612 defer close(result) 1613 1614 ctx, cancel := makeTimeoutContext(timeout) 1615 defer cancel() 1616 1617 numSharders = len(Sharders.Healthy()) // overwrite, use all 1618 Sharders.QueryFromShardersContext(ctx, numSharders, 1619 fmt.Sprintf("%smagic_block_number=%d", GET_MAGIC_BLOCK_INFO, number), 1620 result) 1621 1622 var ( 1623 maxConsensus int 1624 roundConsensus = make(map[string]int) 1625 ret []byte 1626 err error 1627 ) 1628 1629 type respObj struct { 1630 MagicBlock *block.MagicBlock `json:"magic_block"` 1631 } 1632 1633 for i := 0; i < numSharders; i++ { 1634 var rsp = <-result 1635 if rsp == nil { 1636 logging.Error("nil response") 1637 continue 1638 } 1639 1640 logging.Debug(rsp.Url, rsp.Status) 1641 1642 if rsp.StatusCode != http.StatusOK { 1643 logging.Error(rsp.Body) 1644 continue 1645 } 1646 1647 var respo respObj 1648 if err = json.Unmarshal([]byte(rsp.Body), &respo); err != nil { 1649 logging.Error(" magic block parse error: ", err) 1650 err = nil 1651 continue 1652 } 1653 1654 ret = []byte(rsp.Body) 1655 var h = encryption.FastHash([]byte(respo.MagicBlock.Hash)) 1656 if roundConsensus[h]++; roundConsensus[h] > maxConsensus { 1657 maxConsensus = roundConsensus[h] 1658 } 1659 } 1660 1661 if maxConsensus == 0 { 1662 return nil, errors.New("", "magic block info not found") 1663 } 1664 1665 if err != nil { 1666 return nil, err 1667 } 1668 1669 return ret, nil 1670 } 1671 1672 // GetFeesTable get fee tables 1673 func GetFeesTable(reqPercent float32) (string, error) { 1674 1675 fees, err := transaction.GetFeesTable(_config.chain.Miners, reqPercent) 1676 if err != nil { 1677 return "", err 1678 } 1679 1680 js, err := json.Marshal(fees) 1681 if err != nil { 1682 return "", err 1683 } 1684 1685 return string(js), nil 1686 }