I'm trying to parse the data from the ADNL lib by tonutils-go and deliver the data by GRPC.
The data I need is structured as
// struct from tonutils-go
type Transaction struct {
_ Magic `tlb:"$0111"`
AccountAddr []byte `tlb:"bits 256"`
LT uint64 `tlb:"## 64"`
PrevTxHash []byte `tlb:"bits 256"`
PrevTxLT uint64 `tlb:"## 64"`
Now uint32 `tlb:"## 32"`
OutMsgCount uint16 `tlb:"## 15"`
OrigStatus AccountStatus `tlb:"."`
EndStatus AccountStatus `tlb:"."`
IO struct {
In *Message `tlb:"maybe ^"`
Out *MessagesList `tlb:"maybe ^"`
} `tlb:"^"`
TotalFees CurrencyCollection `tlb:"."`
StateUpdate HashUpdate `tlb:"^"` // of Account
Description TransactionDescription `tlb:"^"`
// not in scheme, but will be filled based on request data for flexibility
Hash []byte `tlb:"-"`
}
type TransactionDescription struct {
Description any `tlb:"."`
}
type TransactionDescriptionOrdinary struct {
_ Magic `tlb:"$0000"`
CreditFirst bool `tlb:"bool"`
StoragePhase *StoragePhase `tlb:"maybe ."`
CreditPhase *CreditPhase `tlb:"maybe ."`
ComputePhase ComputePhase `tlb:"."`
ActionPhase *ActionPhase `tlb:"maybe ^"`
Aborted bool `tlb:"bool"`
BouncePhase *BouncePhase `tlb:"maybe ."`
Destroyed bool `tlb:"bool"`
}
type ComputePhase struct {
Phase any `tlb:"."`
}
type ComputePhaseVM struct {
_ Magic `tlb:"$1"`
Success bool `tlb:"bool"`
MsgStateUsed bool `tlb:"bool"`
AccountActivated bool `tlb:"bool"`
GasFees Coins `tlb:"."`
Details struct {
GasUsed *big.Int `tlb:"var uint 7"`
GasLimit *big.Int `tlb:"var uint 7"`
GasCredit *big.Int `tlb:"maybe var uint 3"`
Mode int8 `tlb:"## 8"`
ExitCode int32 `tlb:"## 32"`
ExitArg *int32 `tlb:"maybe ## 32"`
VMSteps uint32 `tlb:"## 32"`
VMInitStateHash []byte `tlb:"bits 256"`
VMFinalStateHash []byte `tlb:"bits 256"`
} `tlb:"^"`
}
The protobuf of TxTest:
message TxTest {
int32 exitCode = 1;
}
The data I want to parse is ExitCode and the code as following:
list, err := api.ListTransactions(context.Background(), addr, 1, uint64(txInfo.TxLT), data)
if err != nil {
log.Printf("send err: %s", err.Error())
return nil, err
}
for _, t := range list {
a := t.Description.Description
var result tlb.TransactionDescriptionOrdinary
b, err := json.MarshalIndent(a, "", " ")
if err != nil {
fmt.Println("error:", err)
}
json.Unmarshal([]byte(string(b)), &result)
var computePhase tlb.ComputePhaseVM
c, err := json.MarshalIndent(result.ComputePhase.Phase, "", " ")
if err != nil {
fmt.Println("error:", err)
}
json.Unmarshal([]byte(string(c)), &computePhase)
detail := &pb.TxTest{
ExitCode: computePhase.Details.ExitCode,
}
detail2 := struct {
ExitCode int32 `json:"exit_code"`
}{
ExitCode: computePhase.Details.ExitCode,
}
fmt.Printf("detail: %+v\n", detail)
fmt.Printf("detail2: %+v\n", detail2)
}
The data struct of one transaction is:
{
"AccountAddr": "HYmM1/kK6GB2DLV3zVkIRyEpKoHRTF/jG8K7tTG91sQ=",
"LT": 11898016000001,
"PrevTxHash": "iKGjsxdT0gzIJXGNIlvxy0+a1gGEQDED4f7ZAJ9dlmc=",
"PrevTxLT": 11897712000001,
"Now": 1685602760,
"OutMsgCount": 1,
"OrigStatus": "ACTIVE",
"EndStatus": "ACTIVE",
"IO": {
"In": {
"MsgType": "EXTERNAL_IN",
"Msg": {
"SrcAddr": "NONE",
"DstAddr": "EQAdiYzX-QroYHYMtXfNWQhHISkqgdFMX-Mbwru1Mb3WxN5D",
"ImportFee": "0",
"StateInit": null,
"Body": {}
}
},
"Out": {
"List": {}
}
},
"TotalFees": {
"Coins": "22324812",
"ExtraCurrencies": {}
},
"StateUpdate": {
"OldHash": "WUkeXyOS8hsWyQYRLHPmJHpfMUSID8oDTAq6fY20pyQ=",
"NewHash": "e8tn4cP4lAkFwvDcGc/VqBZ7lZeB4mhjMbRFE8rpsQA="
},
"Description": {
"Description": {
"CreditFirst": true,
"StoragePhase": {
"StorageFeesCollected": "484",
"StorageFeesDue": null,
"StatusChange": {
"Type": "UNCHANGED"
}
},
"CreditPhase": null,
"ComputePhase": {
"Phase": {
"Success": true,
"MsgStateUsed": false,
"AccountActivated": false,
"GasFees": "19862000",
"Details": {
"GasUsed": 19862,
"GasLimit": 0,
"GasCredit": 10000,
"Mode": 0,
"ExitCode": 0,
"ExitArg": null,
"VMSteps": 404,
"VMInitStateHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"VMFinalStateHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
}
},
"ActionPhase": {
"Success": true,
"Valid": true,
"NoFunds": false,
"StatusChange": {
"Type": "UNCHANGED"
},
"TotalFwdFees": "1000000",
"TotalActionFees": "333328",
"ResultCode": 0,
"ResultArg": null,
"TotalActions": 1,
"SpecActions": 0,
"SkippedActions": 0,
"MessagesCreated": 1,
"ActionListHash": "Fkoo2xX9jU4YxTGJlrUjvaYrRWTrYHEBkUywUYg9AV4=",
"TotalMsgSize": {
"Cells": 1,
"Bits": 697
}
},
"Aborted": false,
"BouncePhase": null,
"Destroyed": false
}
},
"Hash": "VMXfRKfIEtmAiHm3brTvhSkAidE1CkgRW8RQBVarJtQ="
}
and output of detail
and detail2
are:
detail:
detail2: {ExitCode:0}
My question is why the struct generated by protoc
couldn't parse the exitCode
from the transaction
but the struct I defined could work well?
How can I inject the data into the struct TxTest
which is generated by protoc
, and let me transmit the data by GRPC
?
Try this with an exit code value other than 0 and I'm guessing you'll get a different result.
Have a look at your generated code. I believe what's happening is that fmt.Printf
is calling the generated String()
method for your proto struct, which uses the prototext
package to print the contents of your message rather than the default Go struct printer.
This is expected behavior for for proto3
; there's no distinction between an int32
value of 0
and an empty field.