Skip to content

Commit 2afa3ab

Browse files
committed
Decode *cell.Cell (generic) fields recursively
1 parent c4530b5 commit 2afa3ab

File tree

1 file changed

+78
-8
lines changed

1 file changed

+78
-8
lines changed

pkg/ton/debug/lib/lib.go

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,35 @@ func NewMessageInfo(name string, msg any) (MessageInfo, error) {
7171

7272
// NewMessageInfoFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
7373
func NewMessageInfoFromCell(t cldf.ContractType, msg *cell.Cell, tlbs map[uint64]interface{}) (MessageInfo, error) {
74+
typeName, m, err := DecodeJSONMapFromCell(t, msg, tlbs)
75+
if err != nil {
76+
return nil, fmt.Errorf("failed to decode message for contract %s: %w", t, err)
77+
}
78+
79+
name := fmt.Sprintf("%s:%s", t, typeName)
80+
// 4.4 Finally, marshal the final map[string]interface{} as JSON string
81+
return NewMessageInfo(name, m)
82+
}
83+
84+
// DecodeJSONMapFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
85+
func DecodeJSONMapFromCell(t cldf.ContractType, msg *cell.Cell, tlbs map[uint64]interface{}) (string, map[string]interface{}, error) {
86+
// 1.1 Try to decode *cell.Cell as one of the TLBs type by reading the opcode
87+
if msg == nil {
88+
return "", nil, &UnknownMessageError{}
89+
}
90+
7491
r := msg.BeginParse()
7592
if r.BitsLeft() == 0 {
76-
return nil, &UnknownMessageError{}
93+
return "", nil, &UnknownMessageError{}
7794
}
7895
opCode, err := r.PreloadUInt(32)
7996
if err != nil {
80-
return nil, fmt.Errorf("failed to preload opcode: %w", err)
97+
return "", nil, fmt.Errorf("failed to preload opcode: %w", err)
8198
}
8299

83100
i, ok := tlbs[opCode]
84101
if !ok {
85-
return nil, &UnknownMessageError{}
102+
return "", nil, &UnknownMessageError{}
86103
}
87104

88105
// create new instance of the candidate type
@@ -91,11 +108,60 @@ func NewMessageInfoFromCell(t cldf.ContractType, msg *cell.Cell, tlbs map[uint64
91108

92109
// attempt decode - replace tlb.FromCell with the actual decode API you have
93110
if err := tlb.LoadFromCell(inst, r); err != nil {
94-
return nil, fmt.Errorf("failed to decode OnRamp message for opcode 0x%X: %w", opCode, err)
111+
return "", nil, fmt.Errorf("failed to decode message for opcode 0x%X: %w", opCode, err)
112+
}
113+
114+
// Now decode internal *cell.Cell fields recursively
115+
// 2.1. Iterate over the fields of the struct (reflect)
116+
ckeys := make([]string, 0)
117+
for i := 0; i < rt.NumField(); i++ {
118+
f := rt.Field(i)
119+
120+
// 2.2. For each field, check if it's of type *cell.Cell
121+
if f.Type == reflect.TypeOf(&cell.Cell{}) {
122+
// 2.3. If so, check the json tag to determine the expected key
123+
k := f.Name
124+
jsonTag := f.Tag.Get("json")
125+
if jsonTag != "" {
126+
k = strings.Split(jsonTag, ",")[0] // parse json tag options (key)
127+
}
128+
129+
// 2.4. Source a set of keys that we need to decode recursively
130+
ckeys = append(ckeys, k)
131+
}
132+
}
133+
134+
// 3.1. Decode the struct as JSON map[string]interface{} (default *cell.Cell marshalling)
135+
var rawMap map[string]interface{}
136+
rawBytes, err := json.Marshal(inst)
137+
if err != nil {
138+
return "", nil, fmt.Errorf("failed to marshal decoded message to JSON: %w", err)
139+
}
140+
err = json.Unmarshal(rawBytes, &rawMap)
141+
if err != nil {
142+
return "", nil, fmt.Errorf("failed to unmarshal decoded message JSON to map: %w", err)
95143
}
96144

97-
name := fmt.Sprintf("%s:%s", t, rt.Name())
98-
return NewMessageInfo(name, inst)
145+
// 3.2. For each key in the sourced set, get the *cell.Cell value (decode from BOC)
146+
for _, ck := range ckeys {
147+
cBOC := rawMap[ck]
148+
149+
cVal := &cell.Cell{}
150+
if err := json.Unmarshal([]byte(strconv.Quote(cBOC.(string))), cVal); err != nil {
151+
return "", nil, fmt.Errorf("failed to unmarshal BOC to cell: %s: %s: %w", ck, cBOC, err)
152+
}
153+
154+
// 3.3. Try to decode recursively using NewMessageInfoFromCell
155+
_, cMap, err := DecodeJSONMapFromCell(t, cVal, tlbs)
156+
if err != nil {
157+
// fallback to original BOC representation if fails
158+
continue
159+
}
160+
rawMap[ck] = cMap
161+
}
162+
163+
return rt.Name(), rawMap, nil
164+
// 4.4 Finally, marshal the final map[string]interface{} as JSON string
99165
}
100166

101167
func MustNewTLBMap(types []interface{}) map[uint64]interface{} {
@@ -106,14 +172,18 @@ func MustNewTLBMap(types []interface{}) map[uint64]interface{} {
106172
return tlbs
107173
}
108174

109-
// NewTLBMap creates a map of TL-B magic numbers to their corresponding types.
110-
// The input is a slice of TL-B struct instances.
175+
// NewTLBMap creates a map of TL-B magic numbers (opcodes) to their corresponding types
176+
// from a set of TL-B annotated struct instances.
111177
func NewTLBMap(types []interface{}) (map[uint64]interface{}, error) {
112178
tlbs := make(map[uint64]interface{})
113179
for _, typ := range types {
114180
// Use reflection to get the magic number from the type
115181
rt := reflect.TypeOf(typ)
116182

183+
if rt.Field(0).Type != reflect.TypeOf(tlb.Magic{}) {
184+
return nil, fmt.Errorf("first field of %s is not of type Magic", rt.Name())
185+
}
186+
117187
magicTag := rt.Field(0).Tag.Get("tlb")
118188
magic, err := loadMagic(magicTag)
119189
if err != nil {

0 commit comments

Comments
 (0)