|
9 | 9 | "fmt"
|
10 | 10 | "log"
|
11 | 11 | "math/big"
|
| 12 | + "slices" |
12 | 13 | "time"
|
13 | 14 |
|
14 | 15 | "filippo.io/edwards25519"
|
@@ -564,36 +565,107 @@ func RequestGhostRecipientsWithTraceId(ctx context.Context, recipients []*Transa
|
564 | 565 | return gkm, nil
|
565 | 566 | }
|
566 | 567 |
|
567 |
| -func ConsolidationUnspentOutputs(ctx context.Context, assetId string, count int, su *SafeUser) (*SequencerTransactionRequest, error) { |
568 |
| - if count <= 0 || count > 256 { |
569 |
| - return nil, fmt.Errorf("invalid count %d", count) |
570 |
| - } |
| 568 | +func ConsolidationUnspentOutputs(ctx context.Context, assetId string, su *SafeUser) (*SequencerTransactionRequest, int, error) { |
571 | 569 | membersHash := HashMembers([]string{su.UserId})
|
572 |
| - utxos, err := ListOutputs(ctx, membersHash, 1, assetId, "unspent", 0, count, su) |
573 |
| - if err != nil { |
574 |
| - return nil, err |
575 |
| - } |
576 |
| - if len(utxos) <= 1 { |
577 |
| - return nil, fmt.Errorf("insufficient unspent outputs %d", len(utxos)) |
578 |
| - } |
579 |
| - if len(utxos) >= 256 { |
580 |
| - return nil, fmt.Errorf("too many unspent outputs %d", len(utxos)) |
581 |
| - } |
582 |
| - amount := common.Zero |
583 |
| - for _, o := range utxos { |
584 |
| - if o.AssetId != assetId { |
585 |
| - return nil, fmt.Errorf("unspent outputs with different asset id %s != %s", o.AssetId, assetId) |
| 570 | + |
| 571 | + var lastStr *SequencerTransactionRequest |
| 572 | + var pendingTxHashes []string |
| 573 | + var consolidatedCount int |
| 574 | + |
| 575 | + for { |
| 576 | + utxos, err := ListOutputs(ctx, membersHash, 1, assetId, "unspent", 0, 255, su) |
| 577 | + if err != nil { |
| 578 | + return nil, consolidatedCount, err |
| 579 | + } |
| 580 | + if len(utxos) <= 0 { |
| 581 | + break |
| 582 | + } |
| 583 | + if len(utxos) == 1 && len(pendingTxHashes) == 0 { |
| 584 | + // no pending transactions, no need to consolidate |
| 585 | + break |
| 586 | + } |
| 587 | + amount := common.Zero |
| 588 | + for _, o := range utxos { |
| 589 | + if o.AssetId != assetId { |
| 590 | + return nil, consolidatedCount, fmt.Errorf("unspent outputs with different asset id %s != %s", o.AssetId, assetId) |
| 591 | + } |
| 592 | + if o.State != "unspent" { |
| 593 | + return nil, consolidatedCount, fmt.Errorf("unspent outputs with different state %s != unspent", o.State) |
| 594 | + } |
| 595 | + if slices.Contains(pendingTxHashes, o.TransactionHash) { |
| 596 | + pendingTxHashes = slices.DeleteFunc(pendingTxHashes, func(s string) bool { |
| 597 | + return s == o.TransactionHash |
| 598 | + }) |
| 599 | + } else { |
| 600 | + consolidatedCount++ |
| 601 | + } |
| 602 | + amount = amount.Add(common.NewIntegerFromString(o.Amount)) |
| 603 | + } |
| 604 | + trace := UuidNewV4().String() |
| 605 | + str, err := SendTransactionWithOutputs(ctx, assetId, []*TransactionRecipient{ |
| 606 | + { |
| 607 | + MixAddress: NewUUIDMixAddress([]string{su.UserId}, 1), |
| 608 | + Amount: amount.String(), |
| 609 | + }, |
| 610 | + }, utxos, trace, nil, nil, su) |
| 611 | + if err != nil { |
| 612 | + return nil, consolidatedCount, fmt.Errorf("error consolidating outputs: %w", err) |
586 | 613 | }
|
587 |
| - if o.State != "unspent" { |
588 |
| - return nil, fmt.Errorf("unspent outputs with different state %s != unspent", o.State) |
| 614 | + lastStr = str |
| 615 | + pendingTxHashes = append(pendingTxHashes, str.TransactionHash) |
| 616 | + } |
| 617 | + |
| 618 | + // still have pending transactions, wait for them to be confirmed |
| 619 | + if len(pendingTxHashes) > 1 { |
| 620 | + pendingOutputs := make([]*Output, len(pendingTxHashes)) |
| 621 | + var completedOutputs int |
| 622 | + for { |
| 623 | + for i, txHash := range pendingTxHashes { |
| 624 | + if pendingOutputs[i] != nil { |
| 625 | + continue |
| 626 | + } |
| 627 | + output, err := GetOutput(ctx, UniqueObjectId(fmt.Sprintf("%s:%d", txHash, 0)), su) |
| 628 | + var apiErr Error |
| 629 | + if errors.As(err, &apiErr) && apiErr.Code == 404 { |
| 630 | + continue |
| 631 | + } else if err != nil { |
| 632 | + return nil, consolidatedCount, fmt.Errorf("error getting pending output %s: %w", txHash, err) |
| 633 | + } |
| 634 | + pendingOutputs[i] = output |
| 635 | + completedOutputs++ |
| 636 | + } |
| 637 | + if completedOutputs == len(pendingOutputs) { |
| 638 | + break |
| 639 | + } |
| 640 | + time.Sleep(8 * time.Second) |
589 | 641 | }
|
590 |
| - amount = amount.Add(common.NewIntegerFromString(o.Amount)) |
591 |
| - } |
592 |
| - trace := UuidNewV4().String() |
593 |
| - return SendTransactionWithOutputs(ctx, assetId, []*TransactionRecipient{ |
594 |
| - { |
595 |
| - MixAddress: NewUUIDMixAddress([]string{su.UserId}, 1), |
596 |
| - Amount: amount.String(), |
597 |
| - }, |
598 |
| - }, utxos, trace, nil, nil, su) |
| 642 | + var amount common.Integer |
| 643 | + for _, o := range pendingOutputs { |
| 644 | + if o == nil { |
| 645 | + return nil, consolidatedCount, fmt.Errorf("pending output is nil") |
| 646 | + } |
| 647 | + if o.AssetId != assetId { |
| 648 | + return nil, consolidatedCount, fmt.Errorf("pending output with different asset id %s != %s", o.AssetId, assetId) |
| 649 | + } |
| 650 | + if o.State != "unspent" { |
| 651 | + return nil, consolidatedCount, fmt.Errorf("pending output with different state %s != unspent", o.State) |
| 652 | + } |
| 653 | + amount = amount.Add(common.NewIntegerFromString(o.Amount)) |
| 654 | + } |
| 655 | + requestId := UuidNewV4().String() |
| 656 | + str, err := SendTransactionWithOutputs(ctx, assetId, []*TransactionRecipient{ |
| 657 | + { |
| 658 | + MixAddress: NewUUIDMixAddress([]string{su.UserId}, 1), |
| 659 | + Amount: amount.String(), |
| 660 | + }, |
| 661 | + }, pendingOutputs, requestId, nil, nil, su) |
| 662 | + if err != nil { |
| 663 | + return nil, consolidatedCount, fmt.Errorf("error consolidating pending outputs: %w", err) |
| 664 | + } |
| 665 | + lastStr = str |
| 666 | + } |
| 667 | + if lastStr == nil { |
| 668 | + return nil, consolidatedCount, fmt.Errorf("no transaction created during consolidation") |
| 669 | + } |
| 670 | + return lastStr, consolidatedCount, nil |
599 | 671 | }
|
0 commit comments