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.Printfis calling the generatedString()method for your proto struct, which uses theprototextpackage 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 anint32value of0and an empty field.