|
4 | 4 | "crypto/ed25519" |
5 | 5 | "errors" |
6 | 6 | "fmt" |
| 7 | + "github.com/tonkeeper/tongo/ton" |
7 | 8 |
|
8 | 9 | "github.com/tonkeeper/tongo/boc" |
9 | 10 | "github.com/tonkeeper/tongo/tlb" |
@@ -245,6 +246,12 @@ func ExtractRawMessages(ver Version, msg *boc.Cell) ([]RawMessage, error) { |
245 | 246 | return nil, err |
246 | 247 | } |
247 | 248 | return hl.RawMessages, nil |
| 249 | + case HighLoadV3R1: |
| 250 | + hl, err := DecodeHighloadV3Message(msg) |
| 251 | + if err != nil { |
| 252 | + return nil, err |
| 253 | + } |
| 254 | + return hl.Messages, nil |
248 | 255 | default: |
249 | 256 | return nil, fmt.Errorf("wallet version is not supported: %v", ver) |
250 | 257 | } |
@@ -552,3 +559,208 @@ func (extendedActions W5ExtendedActions) MarshalTLB(c *boc.Cell, encoder *tlb.En |
552 | 559 | } |
553 | 560 | return nil |
554 | 561 | } |
| 562 | + |
| 563 | +// HighloadV3InternalTransfer TLB: internal_transfer#ae42e5a4 {n:#} query_id:uint64 actions:^(OutList n) = InternalMsgBody n; |
| 564 | +type HighloadV3InternalTransfer struct { |
| 565 | + Magic tlb.Magic `tlb:"#ae42e5a4"` |
| 566 | + QueryId uint64 |
| 567 | + Actions W5Actions `tlb:"^"` |
| 568 | +} |
| 569 | + |
| 570 | +type HighloadV3MsgInner struct { |
| 571 | + SubwalletID uint32 |
| 572 | + MessageToSend *boc.Cell `tlb:"^"` |
| 573 | + SendMode uint8 |
| 574 | + QueryID tlb.Uint23 // _ shift:uint13 bit_number:(## 10) { bit_number >= 0 } { bit_number < 1023 } = QueryId; |
| 575 | + CreatedAt uint64 |
| 576 | + Timeout tlb.Uint22 |
| 577 | +} |
| 578 | + |
| 579 | +type HighloadV3Message struct { |
| 580 | + SubwalletID uint32 |
| 581 | + Messages []RawMessage |
| 582 | + SendMode uint8 |
| 583 | + QueryID tlb.Uint23 |
| 584 | + CreatedAt uint64 |
| 585 | + Timeout tlb.Uint22 |
| 586 | + wallet ton.AccountID // technical field for storing the wallet address for forming attached messages |
| 587 | +} |
| 588 | + |
| 589 | +func (p HighloadV3Message) MarshalTLB(c *boc.Cell, encoder *tlb.Encoder) error { |
| 590 | + var ( |
| 591 | + msg RawMessage |
| 592 | + err error |
| 593 | + ) |
| 594 | + ln := len(p.Messages) |
| 595 | + switch { |
| 596 | + case ln > 254*254: |
| 597 | + return fmt.Errorf("PayloadHighloadV3 supports only up to 254*254 messages") |
| 598 | + case ln < 1: |
| 599 | + return fmt.Errorf("must be at least one message") |
| 600 | + case ln == 1: |
| 601 | + var m tlb.Message |
| 602 | + err := tlb.Unmarshal(p.Messages[0].Message, &m) |
| 603 | + if err != nil { |
| 604 | + return err |
| 605 | + } |
| 606 | + // IntMsg with state init and extOutMsg must be packed because of message validation |
| 607 | + // throw_if(error::invalid_message_to_send, maybe_state_init); ;; throw if state-init included (state-init not supported) |
| 608 | + // throw_if(error::invalid_message_to_send, message_slice~load_uint(1)); ;; int_msg_info$0 |
| 609 | + if !m.Init.Exists && m.Info.SumType == "IntMsgInfo" { // no need to pack |
| 610 | + msg = p.Messages[0] |
| 611 | + } else { |
| 612 | + msg, err = packHighloadV3Messages(uint64(p.QueryID), p.wallet, p.Messages, p.SendMode) |
| 613 | + if err != nil { |
| 614 | + return err |
| 615 | + } |
| 616 | + } |
| 617 | + default: |
| 618 | + msg, err = packHighloadV3Messages(uint64(p.QueryID), p.wallet, p.Messages, p.SendMode) |
| 619 | + if err != nil { |
| 620 | + return err |
| 621 | + } |
| 622 | + } |
| 623 | + return tlb.Marshal(c, HighloadV3MsgInner{ |
| 624 | + SubwalletID: p.SubwalletID, |
| 625 | + MessageToSend: msg.Message, |
| 626 | + SendMode: msg.Mode, |
| 627 | + QueryID: p.QueryID, |
| 628 | + CreatedAt: p.CreatedAt, |
| 629 | + Timeout: p.Timeout, |
| 630 | + }) |
| 631 | +} |
| 632 | + |
| 633 | +func packHighloadV3Messages(queryID uint64, wallet ton.AccountID, msgs []RawMessage, mode uint8) (RawMessage, error) { |
| 634 | + const messagesPerPack = 253 |
| 635 | + var ( |
| 636 | + totalAmount uint64 = 0 |
| 637 | + actions W5Actions |
| 638 | + ) |
| 639 | + rawMsgs := make([]RawMessage, len(msgs)) |
| 640 | + copy(rawMsgs, msgs) // to prevent corruption of msgs |
| 641 | + if len(rawMsgs) > messagesPerPack { |
| 642 | + rest, err := packHighloadV3Messages(queryID, wallet, rawMsgs[messagesPerPack:], mode) |
| 643 | + if err != nil { |
| 644 | + return RawMessage{}, err |
| 645 | + } |
| 646 | + rawMsgs = append(rawMsgs[:messagesPerPack], rest) |
| 647 | + } |
| 648 | + for _, rawMsg := range rawMsgs { |
| 649 | + var m tlb.Message |
| 650 | + err := tlb.Unmarshal(rawMsg.Message, &m) |
| 651 | + if err != nil { |
| 652 | + return RawMessage{}, err |
| 653 | + } |
| 654 | + if m.Info.SumType == "IntMsgInfo" { |
| 655 | + totalAmount += uint64(m.Info.IntMsgInfo.Value.Grams) |
| 656 | + } else { |
| 657 | + totalAmount += uint64(1_000_000) // add some amount for execution |
| 658 | + } |
| 659 | + actions = append(actions, W5SendMessageAction{ |
| 660 | + Mode: rawMsg.Mode, |
| 661 | + Msg: rawMsg.Message, |
| 662 | + }) |
| 663 | + } |
| 664 | + body := boc.NewCell() |
| 665 | + err := tlb.Marshal(body, HighloadV3InternalTransfer{ |
| 666 | + QueryId: queryID, |
| 667 | + Actions: actions, |
| 668 | + }) |
| 669 | + if err != nil { |
| 670 | + return RawMessage{}, err |
| 671 | + } |
| 672 | + msgInt, _, err := Message{ |
| 673 | + Amount: tlb.Grams(totalAmount), |
| 674 | + Bounce: false, |
| 675 | + Address: wallet, |
| 676 | + Body: body, |
| 677 | + }.ToInternal() |
| 678 | + if err != nil { |
| 679 | + return RawMessage{}, err |
| 680 | + } |
| 681 | + c := boc.NewCell() |
| 682 | + err = tlb.Marshal(c, msgInt) |
| 683 | + if err != nil { |
| 684 | + return RawMessage{}, err |
| 685 | + } |
| 686 | + return RawMessage{ |
| 687 | + Mode: mode, |
| 688 | + Message: c, |
| 689 | + }, nil |
| 690 | +} |
| 691 | + |
| 692 | +const highloadV3InternalTransferOp = 0xae42e5a4 |
| 693 | + |
| 694 | +func (p *HighloadV3Message) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error { |
| 695 | + var msgInner HighloadV3MsgInner |
| 696 | + err := tlb.Unmarshal(c, &msgInner) |
| 697 | + if err != nil { |
| 698 | + return err |
| 699 | + } |
| 700 | + res := HighloadV3Message{ |
| 701 | + SubwalletID: msgInner.SubwalletID, |
| 702 | + SendMode: msgInner.SendMode, |
| 703 | + QueryID: msgInner.QueryID, |
| 704 | + CreatedAt: msgInner.CreatedAt, |
| 705 | + Timeout: msgInner.Timeout, |
| 706 | + } |
| 707 | + var msgs []RawMessage |
| 708 | + msgs, err = unpackHighloadV3Messages(msgInner.MessageToSend, msgInner.QueryID, msgInner.SendMode, msgs) |
| 709 | + if err != nil { |
| 710 | + return err |
| 711 | + } |
| 712 | + res.Messages = msgs |
| 713 | + *p = res |
| 714 | + return nil |
| 715 | +} |
| 716 | + |
| 717 | +func unpackHighloadV3Messages(msg *boc.Cell, queryID tlb.Uint23, mode uint8, messages []RawMessage) ([]RawMessage, error) { |
| 718 | + var m tlb.Message |
| 719 | + err := tlb.Unmarshal(msg, &m) |
| 720 | + if err != nil { |
| 721 | + return nil, err |
| 722 | + } |
| 723 | + if m.Info.SumType != "IntMsgInfo" { |
| 724 | + // TODO: reset counters for msgInner.MessageToSend ? |
| 725 | + messages = append(messages, RawMessage{msg, mode}) |
| 726 | + return messages, nil |
| 727 | + } |
| 728 | + body := boc.Cell(m.Body.Value) |
| 729 | + op, err := body.PickUint(32) |
| 730 | + if err != nil || op != highloadV3InternalTransferOp { |
| 731 | + messages = append(messages, RawMessage{msg, mode}) |
| 732 | + return messages, nil |
| 733 | + } |
| 734 | + var intTransfer HighloadV3InternalTransfer |
| 735 | + err = tlb.Unmarshal(&body, &intTransfer) |
| 736 | + if err != nil { |
| 737 | + return nil, err |
| 738 | + } |
| 739 | + if intTransfer.QueryId != uint64(queryID) { |
| 740 | + return nil, errors.New("mismatch queryID for internal transfer") // TODO: need to check? |
| 741 | + } |
| 742 | + for _, a := range intTransfer.Actions { |
| 743 | + messages, err = unpackHighloadV3Messages(a.Msg, queryID, a.Mode, messages) |
| 744 | + if err != nil { |
| 745 | + return nil, err |
| 746 | + } |
| 747 | + } |
| 748 | + return messages, nil |
| 749 | +} |
| 750 | + |
| 751 | +func DecodeHighloadV3Message(msg *boc.Cell) (*HighloadV3Message, error) { |
| 752 | + var m tlb.Message |
| 753 | + if err := tlb.Unmarshal(msg, &m); err != nil { |
| 754 | + return nil, err |
| 755 | + } |
| 756 | + c := boc.Cell(m.Body.Value) |
| 757 | + payloadCell, err := c.NextRef() |
| 758 | + if err != nil { |
| 759 | + return nil, err |
| 760 | + } |
| 761 | + var res HighloadV3Message |
| 762 | + if err := tlb.Unmarshal(payloadCell, &res); err != nil { |
| 763 | + return nil, err |
| 764 | + } |
| 765 | + return &res, nil |
| 766 | +} |
0 commit comments