From 9c6ebb4cc354b74b92d6a7a259d567930ddd4f91 Mon Sep 17 00:00:00 2001 From: "aleksej.paschenko" Date: Fri, 15 Dec 2023 11:37:27 +0300 Subject: [PATCH] Add tongo.ParseTlbMessage() and change some funcs to accept tlb.Message --- liteapi/utils.go | 12 ++++++-- message.go | 68 +++++++++++++++++++++++++++++++++++++++++ wallet/messages.go | 33 +++++++++++++------- wallet/messages_test.go | 6 +++- wallet/models.go | 5 ++- 5 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 message.go diff --git a/liteapi/utils.go b/liteapi/utils.go index b4a31a46..b5d442c8 100644 --- a/liteapi/utils.go +++ b/liteapi/utils.go @@ -13,6 +13,14 @@ func VerifySendMessagePayload(payload []byte) error { return err } +// VerifySendMessage verifies that the given message is an external message ready to be sent to the blockchain. +func VerifySendMessage(msg *tlb.Message) error { + if msg.Info.SumType != "ExtInMsgInfo" { + return fmt.Errorf("external message must begin with ext_in_msg_info$10") + } + return nil +} + // ConvertSendMessagePayloadToMessage converts the given payload to a tlb.Message. // It also verifies that the message is an external message ready to be sent to the blockchain. func ConvertSendMessagePayloadToMessage(payload []byte) (*tlb.Message, error) { @@ -31,8 +39,8 @@ func ConvertSendMessagePayloadToMessage(payload []byte) (*tlb.Message, error) { if err := tlb.Unmarshal(root, &msg); err != nil { return nil, fmt.Errorf("external message is not a tlb.Message") } - if msg.Info.SumType != "ExtInMsgInfo" { - return nil, fmt.Errorf("external message must begin with ext_in_msg_info$10") + if err := VerifySendMessage(&msg); err != nil { + return nil, err } return &msg, nil } diff --git a/message.go b/message.go new file mode 100644 index 00000000..2c7c1e3d --- /dev/null +++ b/message.go @@ -0,0 +1,68 @@ +package tongo + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +func decodeBoc(bocStr string) ([]byte, error) { + bocData, err := base64.StdEncoding.DecodeString(bocStr) + if err != nil { + return hex.DecodeString(bocStr) + } + return bocData, nil +} + +// Message contains a tlb.Message, its boc representation and a Cell. +type Message struct { + Boc []byte + TlbMsg *tlb.Message + Cell *boc.Cell +} + +// ParseTlbMessage returns a Message unmarshalled from the given boc string. +// The boc string can be either in base64 or hex format. +func ParseTlbMessage(bocStr string) (*Message, error) { + b, err := decodeBoc(bocStr) + if err != nil { + return nil, err + } + cells, err := boc.DeserializeBoc(b) + if err != nil { + return nil, err + } + if len(cells) != 1 { + return nil, fmt.Errorf("invalid message boc") + } + var msg tlb.Message + if err := tlb.Unmarshal(cells[0], &msg); err != nil { + return nil, err + } + cells[0].ResetCounters() + return &Message{Boc: b, TlbMsg: &msg, Cell: cells[0]}, nil +} + +func (m *Message) DestinationAccountID() (ton.AccountID, error) { + var dest tlb.MsgAddress + switch m.TlbMsg.Info.SumType { + case "IntMsgInfo": + dest = m.TlbMsg.Info.IntMsgInfo.Dest + case "ExtInMsgInfo": + dest = m.TlbMsg.Info.ExtInMsgInfo.Dest + case "ExtOutMsgInfo": + dest = m.TlbMsg.Info.ExtOutMsgInfo.Dest + } + accountID, err := ton.AccountIDFromTlb(dest) + if err != nil { + return ton.AccountID{}, err + } + if accountID == nil { + return ton.AccountID{}, fmt.Errorf("failed to extract the destination address") + } + return ton.AccountID{}, nil +} diff --git a/wallet/messages.go b/wallet/messages.go index 4918f33f..21869d87 100644 --- a/wallet/messages.go +++ b/wallet/messages.go @@ -44,6 +44,15 @@ type RawMessage struct { Mode byte } +// ToTlbMessage converts a RawMessage to a tlb.Message. +func (rm *RawMessage) ToTlbMessage() (*tlb.Message, error) { + var msg tlb.Message + if err := tlb.Unmarshal(rm.Message, &msg); err != nil { + return nil, err + } + return &msg, nil +} + type PayloadV1toV4 []RawMessage type PayloadHighload []RawMessage @@ -59,20 +68,16 @@ func (body *SignedMsgBody) Verify(publicKey ed25519.PublicKey) error { return fmt.Errorf("failed to verify msg signature") } -func extractSignedMsgBody(msg *boc.Cell) (*SignedMsgBody, error) { - var m tlb.Message - if err := tlb.Unmarshal(msg, &m); err != nil { - return nil, err - } +func extractSignedMsgBody(msg *tlb.Message) (*SignedMsgBody, error) { + bodyCell := boc.Cell(msg.Body.Value) msgBody := SignedMsgBody{} - bodyCell := boc.Cell(m.Body.Value) if err := tlb.Unmarshal(&bodyCell, &msgBody); err != nil { return nil, err } return &msgBody, nil } -func DecodeMessageV4(msg *boc.Cell) (*MessageV4, error) { +func DecodeMessageV4(msg *tlb.Message) (*MessageV4, error) { signedMsgBody, err := extractSignedMsgBody(msg) if err != nil { return nil, err @@ -89,7 +94,7 @@ func decodeMessageV4(body *SignedMsgBody) (*MessageV4, error) { return &msgv4, nil } -func DecodeMessageV3(msg *boc.Cell) (*MessageV3, error) { +func DecodeMessageV3(msg *tlb.Message) (*MessageV3, error) { signedMsgBody, err := extractSignedMsgBody(msg) if err != nil { return nil, err @@ -106,7 +111,7 @@ func decodeMessageV3(body *SignedMsgBody) (*MessageV3, error) { return &msgv3, nil } -func DecodeHighloadV2Message(msg *boc.Cell) (*HighloadV2Message, error) { +func DecodeHighloadV2Message(msg *tlb.Message) (*HighloadV2Message, error) { signedMsgBody, err := extractSignedMsgBody(msg) if err != nil { return nil, err @@ -124,7 +129,10 @@ func decodeHighloadV2Message(body *SignedMsgBody) (*HighloadV2Message, error) { } // ExtractRawMessages extracts a list of RawMessages from an external message. -func ExtractRawMessages(ver Version, msg *boc.Cell) (PayloadV1toV4, error) { +func ExtractRawMessages(ver Version, msg *tlb.Message) (PayloadV1toV4, error) { + if msg == nil { + return nil, fmt.Errorf("msg is nil") + } switch ver { case V4R1, V4R2: v4, err := DecodeMessageV4(msg) @@ -154,7 +162,10 @@ func ExtractRawMessages(ver Version, msg *boc.Cell) (PayloadV1toV4, error) { // was signed by the given public key of a wallet contract. // On success, it returns nil. // Otherwise, it returns an error. -func VerifySignature(ver Version, msg *boc.Cell, publicKey ed25519.PublicKey) error { +func VerifySignature(ver Version, msg *tlb.Message, publicKey ed25519.PublicKey) error { + if msg == nil { + return fmt.Errorf("msg is nil") + } switch ver { case V3R1, V3R2, V4R1, V4R2, HighLoadV2R2: signedMsgBody, err := extractSignedMsgBody(msg) diff --git a/wallet/messages_test.go b/wallet/messages_test.go index 9cfe2010..f28e325e 100644 --- a/wallet/messages_test.go +++ b/wallet/messages_test.go @@ -81,7 +81,11 @@ func TestExtractRawMessages(t *testing.T) { if err != nil { t.Fatal(err) } - rawMessages, err := ExtractRawMessages(tt.ver, c[0]) + var msg tlb.Message + if err := tlb.Unmarshal(c[0], &msg); err != nil { + t.Fatal(err) + } + rawMessages, err := ExtractRawMessages(tt.ver, &msg) if err != nil { t.Fatal(err) } diff --git a/wallet/models.go b/wallet/models.go index ba41abd4..3bdd3471 100644 --- a/wallet/models.go +++ b/wallet/models.go @@ -68,8 +68,11 @@ func init() { // GetWalletVersion returns a wallet version by the given state of an account and an incoming message to the account. // An incoming message is needed in case when a wallet has not been initialized yet. // In this case, we take its code from the message's StateInit. -func GetWalletVersion(state tlb.ShardAccount, msg tlb.Message) (Version, bool, error) { +func GetWalletVersion(state tlb.ShardAccount, msg *tlb.Message) (Version, bool, error) { if state.Account.SumType == "AccountNone" || state.Account.Account.Storage.State.SumType == "AccountUninit" { + if msg == nil { + return 0, false, fmt.Errorf("account is not initialized") + } if !msg.Init.Exists { return 0, false, fmt.Errorf("account is not initialized") }