proto: 无法解析无效的线格式数据

huangapple go评论106阅读模式
英文:

proto: cannot parse invalid wire-format data

问题

我是你的中文翻译助手,以下是翻译好的内容:

我对protobufs还不熟悉,目前正在编写一个从NATS服务器读取数据的客户端。从NATS服务器发送的数据是一个protobuf。

我正在使用Go语言编写客户端。这是我编写的.proto文件:

syntax = "proto3";

package execution;

option go_package = "./protos/execution";

enum OrderStatus {
  Working = 0;
  Rejected = 1;
  Cancelled = 2;
  Completed = 3;
}

enum OrderType {
  Limit = 0;
  Market = 1;
  StopLimit = 2;
  StopMarket = 3;
}

enum OrderSide {
  Buy = 0;
  Sell = 1;
}

enum RejectReason {
  NoRejection = 0;
  InstrumentNotFound = 1;
  OrderNotFound = 2;
  InvalidOrderType = 3;
  InvalidAccount = 4;
  InvalidSide = 5;
  InvalidAmount = 6;
  InvalidLimitPrice = 7;
  InvalidQuoteLimit = 8;
  InvalidActivationPrice = 9;
  InvalidTimeInForce = 10;
  MarketHalted = 11;
  MarketPaused = 12;
  NoCounterOrders = 13;
  MissingExpirationTime = 14;
  IncorrectExpirationTime = 15;
  InternalError = 16;
  IllegalStatusSwitch = 17;
  OrderAlreadyExists = 18;
  InstrumentNotReady = 19;
  ExternalSystemError = 20;
}

enum ReportCause {
  NONE = 0;
  NewOrder = 1;
  CancelOrder = 2;
  MassCancel = 3;
  Expiration = 4;
  Trigger = 5;
  MarketStatusChange = 6;
}

enum TimeInForce {
  GoodTillCancel = 0;
  ImmediateOrCancel = 1;
  FillOrKill = 2;
}

enum CancelReason {
  NotCancelled = 0;
  CancelledByTrader = 1;
  CancelledBySystem = 2;
  SelfMatchPrevention = 3;
  OrderTimeInForce = 4;
  Liquidation = 100;
}

message TradeData {
  int64 TradeId = 1;
  string Amount = 4;
  string ExecutionPrice = 5;
  OrderStatus OrderStatus = 7;
  int64 AccountId = 11;
  string MatchedOrderExternalId = 14;
  int64 MatchedOrderId = 16;
  string RemainingAmount = 17;
}

message Execution {
  string Origin = 4;
  OrderSide Side = 7;
  string RequestedPrice = 8;
  string RequestedAmount = 9;
  string RemainingAmount = 10;
  int64 ExecutedAt = 13;
  OrderStatus OrderStatus = 14;
  repeated TradeData Trades = 16;
  OrderType OrderType = 20;
  int64 Version = 22;
  int64 AccountId = 23;
  RejectReason RejectReason = 25;
  ReportCause ReportCause = 26;
  string InstructionId = 27;
  string ExternalOrderId = 28;
  int32 ExecutionEngineMarketId = 29;
  int64 OrderId = 30;
  CancelReason CancelReason = 31;
  int64 TxId = 32;
  TimeInForce TimeInForce = 34;
  string CancelledBy = 35;
}

发布服务器是用C#编写的,他们的proto消息的代码如下:

[ProtoContract]
public class ExecutionReport : IMarketResponse, IInstructionMessage, IOrderMatcherResponse
{
    [ProtoIgnore]
    FeedMessageType IFeedMessage.Type => FeedMessageType.ExecutionReport;

    // ReSharper disable FieldCanBeMadeReadOnly.Global
    [ProtoMember(4)] public string Origin;
    [ProtoMember(7)] public OrderSide Side;
    [ProtoMember(8)] public decimal RequestedPrice;
    [ProtoMember(9)] public decimal RequestedAmount;
    [ProtoMember(10)] public decimal RemainingAmount;
    [ProtoMember(13)] public long ExecutedAt;
    [ProtoMember(14)] public OrderStatus OrderStatus;
    [ProtoMember(16)] public List<TradeData> Trades = new List<TradeData>();
    [ProtoMember(20)] public OrderType OrderType;
    [ProtoMember(22)] public long Version { get; set; }
    [ProtoMember(23)] public long AccountId;
    [ProtoMember(25)] public RejectReason RejectReason;
    [ProtoMember(26)] public ReportCause ReportCause;
    [ProtoMember(27)] public Guid InstructionId { get; set; }
    [ProtoMember(28)] public Guid ExternalOrderId;
    [ProtoMember(29)] public int ExecutionEngineMarketId { get; set; }
    [ProtoMember(30)] public long OrderId;
    [ProtoMember(31)] public CancelReason CancelReason;
    [ProtoMember(32)] public long TxId;
    [ProtoMember(34)] public TimeInForce TimeInForce;
    [ProtoMember(35)] public string CancelledBy;
}

[ProtoContract]
[StructLayout(LayoutKind.Sequential)]
public struct TradeData
{
    [ProtoMember(1)] public long TradeId;
    [ProtoMember(4)] public decimal Amount;
    [ProtoMember(5)] public decimal ExecutionPrice;
    [ProtoMember(7)] public OrderStatus OrderStatus;
    [ProtoMember(11)] public long AccountId;
    [ProtoMember(14)] public Guid MatchedOrderExternalId;
    [ProtoMember(16)] public long MatchedOrderId;
    [ProtoMember(17)] public decimal RemainingAmount;
}

在尝试解组数据时,我遇到了以下错误:

proto: cannot parse invalid wire-format data

这是我解组数据的方式:

_, err = sc.Subscribe("EXEC", func(m *stan.Msg) {
    varr := &protos.Execution{}
    err = proto.Unmarshal(m.Data, varr)
    if err != nil {
        fmt.Printf("Err unmarshalling!: %v\n\n", err.Error())
    } else {
        fmt.Printf("Received a message: %+v\n", varr)
    }
})

我收到的服务器发送的示例字节数据如下:

[5 85 0 0 0 56 1 66 3 8 144 78 74 2 8 1 82 2 8 1 104 197 192 132 194 159 143 219 237 8 176 1 25 184 1 11 208 1 1 218 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 226 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 232 1 1 240 1 25 128 2 25]

添加更多细节:

这是C#发送数据的方式:

public async Task SendAsync(IFeedMessage msg)
{
    var subject = FeedSubject.ForMessage(msg);
    var data = msg.SerializeToArray();
    using (_metrics.FeedSendLatency.Start(new MetricTags("subject", subject.Value)))
    {
        await _connection.PublishAsync(subject, data);
    }
}

这是FeedMessage的结构(ExecutionReport也间接继承了它):

public interface IFeedMessage
{
    FeedMessageType Type { get; }
    IFeedMessage Clone();
    void Reset();
}

这是SerializeToArray()的工作方式:

public static ArraySegment<byte> SerializeToArray(this IFeedMessage message)
{
    return message.SerializeToMemory(new MemoryStream());
}

public static ArraySegment<byte> SerializeToMemory(this IFeedMessage message, MemoryStream stream)
{
    var start = stream.Position;
    message.Serialize(stream);
    return new ArraySegment<byte>(stream.GetBuffer(), (int)start, (int)(stream.Position - start));
}

public static void Serialize(this IFeedMessage message, Stream stream)
{
    stream.WriteByte((byte)message.Type);
    RuntimeTypeModel.Default.SerializeWithLengthPrefix(stream, message, message.GetType(), PrefixStyle.Fixed32, 0);
}

我不确定确切的原因是什么。但似乎我编写的.proto文件是错误的。我阅读了一些遇到相同错误的帖子,但大多数都没有解决相同的问题。如果需要其他详细信息,请告诉我。

请帮助我解决这个问题。

英文:

I'm new to protobufs and currently writing a client which reads from a NATS server. The data being sent from the NATS server is a protobuf.

The client I'm writing is in Go. This is the .proto file I've written:

syntax = &quot;proto3&quot;;
package execution;
option go_package = &quot;./protos/execution&quot;;
enum OrderStatus {
Working = 0;
Rejected = 1;
Cancelled = 2;
Completed = 3;
}
enum OrderType {
Limit = 0;
Market = 1;
StopLimit = 2;
StopMarket = 3;
}
enum OrderSide {
Buy = 0;
Sell = 1;
}
enum RejectReason {
NoRejection = 0;
InstrumentNotFound = 1;
OrderNotFound = 2;
InvalidOrderType = 3;
InvalidAccount = 4;
InvalidSide = 5;
InvalidAmount = 6;
InvalidLimitPrice = 7;
InvalidQuoteLimit = 8;
InvalidActivationPrice = 9;
InvalidTimeInForce = 10;
MarketHalted = 11;
MarketPaused = 12;
NoCounterOrders = 13;
MissingExpirationTime = 14;
IncorrectExpirationTime = 15;
InternalError = 16;
IllegalStatusSwitch = 17;
OrderAlreadyExists = 18;
InstrumentNotReady = 19;
ExternalSystemError = 20;
}
enum ReportCause {
NONE = 0;
NewOrder = 1;
CancelOrder = 2;
MassCancel = 3;
Expiration = 4;
Trigger = 5;
MarketStatusChange = 6;
}
enum TimeInForce {
GoodTillCancel = 0;
ImmediateOrCancel = 1;
FillOrKill = 2;
}
enum CancelReason {
NotCancelled = 0;
CancelledByTrader = 1;
CancelledBySystem = 2;
SelfMatchPrevention = 3;
OrderTimeInForce = 4;
Liquidation = 100;
}
message TradeData {
int64 TradeId = 1;
string Amount = 4;
string ExecutionPrice = 5;
OrderStatus OrderStatus = 7;
int64 AccountId = 11;
string MatchedOrderExternalId = 14;
int64 MatchedOrderId = 16;
string RemainingAmount = 17;
}
message Execution {
string Origin = 4;
OrderSide Side = 7;
string RequestedPrice = 8;
string RequestedAmount = 9;
string RemainingAmount = 10;
int64 ExecutedAt = 13;
OrderStatus OrderStatus = 14;
repeated TradeData Trades = 16;
OrderType OrderType = 20;
int64 Version = 22;
int64 AccountId = 23;
RejectReason RejectReason = 25;
ReportCause ReportCause = 26;
string InstructionId = 27;
string ExternalOrderId = 28;
int32 ExecutionEngineMarketId = 29;
int64 OrderId = 30;
CancelReason CancelReason = 31;
int64 TxId = 32;
TimeInForce TimeInForce = 34;
string CancelledBy = 35;
}

The publishing server is written in C# and the code for their proto message is this:

[ProtoContract]
public class ExecutionReport : IMarketResponse, IInstructionMessage, IOrderMatcherResponse
{
[ProtoIgnore]
FeedMessageType IFeedMessage.Type =&gt; FeedMessageType.ExecutionReport;
// ReSharper disable FieldCanBeMadeReadOnly.Global
[ProtoMember(4)] public string Origin;
[ProtoMember(7)] public OrderSide Side;
[ProtoMember(8)] public decimal RequestedPrice;
[ProtoMember(9)] public decimal RequestedAmount;
[ProtoMember(10)] public decimal RemainingAmount;
[ProtoMember(13)] public long ExecutedAt;
[ProtoMember(14)] public OrderStatus OrderStatus;
[ProtoMember(16)] public List&lt;TradeData&gt; Trades = new List&lt;TradeData&gt;();
[ProtoMember(20)] public OrderType OrderType;
[ProtoMember(22)] public long Version { get; set; }
[ProtoMember(23)] public long AccountId;
[ProtoMember(25)] public RejectReason RejectReason;
[ProtoMember(26)] public ReportCause ReportCause;
[ProtoMember(27)] public Guid InstructionId { get; set; }
[ProtoMember(28)] public Guid ExternalOrderId;
[ProtoMember(29)] public int ExecutionEngineMarketId { get; set; }
[ProtoMember(30)] public long OrderId;
[ProtoMember(31)] public CancelReason CancelReason;
[ProtoMember(32)] public long TxId;
[ProtoMember(34)] public TimeInForce TimeInForce;
[ProtoMember(35)] public string CancelledBy;
}
[ProtoContract]
[StructLayout(LayoutKind.Sequential)]
public struct TradeData
{
[ProtoMember(1)] public long TradeId;
[ProtoMember(4)] public decimal Amount;
[ProtoMember(5)] public decimal ExecutionPrice;
[ProtoMember(7)] public OrderStatus OrderStatus;
[ProtoMember(11)] public long AccountId;
[ProtoMember(14)] public Guid MatchedOrderExternalId;
[ProtoMember(16)] public long MatchedOrderId;
[ProtoMember(17)] public decimal RemainingAmount;
}

While trying to unmarshal the data I'm getting this error

proto: cannot parse invalid wire-format data

This is how I'm parsing the data:

_, err = sc.Subscribe(&quot;EXEC&quot;, func(m *stan.Msg) {
varr := &amp;protos.Execution{}
err = proto.Unmarshal(m.Data, varr)
if err != nil {
fmt.Printf(&quot;Err unmarshalling!: %v\n\n&quot;, err.Error())
} else {
fmt.Printf(&quot;Received a message: %+v\n&quot;, varr)
}

A sample byte data I'm receiving from the server:

[5 85 0 0 0 56 1 66 3 8 144 78 74 2 8 1 82 2 8 1 104 197 192 132 194 159 143 219 237 8 176 1 25 184 1 11 208 1 1 218 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 226 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 232 1 1 240 1 25 128 2 25]

Adding more details:

This is how C# is sending data:

public async Task SendAsync(IFeedMessage msg)
{
var subject = FeedSubject.ForMessage(msg);
var data = msg.SerializeToArray();
using (_metrics.FeedSendLatency.Start(new MetricTags(&quot;subject&quot;, subject.Value)))
{
await _connection.PublishAsync(subject, data);
}
}

This is the structure of FeedMessage (which ExecutionReport is also inheriting indirectly)

public interface IFeedMessage
{
FeedMessageType Type { get; }
IFeedMessage Clone();
void Reset();
}

This is how SerializeToArray() works:

public static ArraySegment&lt;byte&gt; SerializeToArray(this IFeedMessage message)
{
return message.SerializeToMemory(new MemoryStream());
}
public static ArraySegment&lt;byte&gt; SerializeToMemory(this IFeedMessage message, MemoryStream stream)
{
var start = stream.Position;
message.Serialize(stream);
return new ArraySegment&lt;byte&gt;(stream.GetBuffer(), (int)start, (int)(stream.Position - start));
}
public static void Serialize(this IFeedMessage message, Stream stream)
{
stream.WriteByte((byte)message.Type);
RuntimeTypeModel.Default.SerializeWithLengthPrefix(stream, message, message.GetType(), PrefixStyle.Fixed32, 0);
}

I'm not sure what is the exact reason. But it seems that the proto file that I've written is wrong. I went through few posts facing the same error but most don't address the same issue. Let me know if any other detail is needed.

Please help me with this.

答案1

得分: 4

根据评论中的讨论,我已经成功解析了数据。

注意事项

  1. 数据以5个字节为前缀(这是完全不必要的):
    • 1个字节用于消息类型
    • 4个字节用于数据长度
  2. C#实现使用了特定于C#的decimalGuid数据类型(如在bcl.proto中注释的那样,跨平台代码通常应完全避免使用它们)。

以下是文件夹结构:

├── bcl.proto
├── execution.proto
├── go.mod
├── go.sum
├── main.go
└── protos
├── bcl.pb.go
└── execution.pb.go

bcl.proto

此文件从github.com/protobuf-net/protobuf-net复制而来。它是必需的,因为.NET实现使用了该proto文件中的DecimalGuid

// 这里的类型指示了protobuf-net在使用protobuf-net特定库功能时如何表示某些类型。请注意,不是*必须*使用这些类型,跨平台代码通常应完全避免使用它们(理想情况下,从.proto模式开始)

// 这些类型中有一些很丑陋,抱歉。这里的TimeSpan / DateTime日期早于Timestamp和Duration的引入,“众所周知”的类型应该是首选的。Guid特别棘手-事实证明,有多个guid表示方式,我无意中使用了一种我只能称之为...“疯狂的字节序”。只需确保检查顺序!

// 在使用protobuf-net的代码中,不应该使用bcl.proto是不必要的

syntax = "proto3";

option csharp_namespace = "ProtoBuf.Bcl";
option go_package = "./protos";

package bcl;

message TimeSpan {
  sint64 value = 1; // 时间跨度的大小(以所选刻度单位计)
  TimeSpanScale scale = 2; // 时间跨度的刻度[默认值=天]
  enum TimeSpanScale {
    DAYS = 0;
    HOURS = 1;
    MINUTES = 2;
    SECONDS = 3;
    MILLISECONDS = 4;
	TICKS = 5;

    MINMAX = 15; // 可疑的
  }
}

message DateTime {
  sint64 value = 1; // 与1970/01/01的偏移量(以所选刻度单位计)
  TimeSpanScale scale = 2; // 时间跨度的刻度[默认值=天]
  DateTimeKind kind = 3; // 表示的日期/时间的种类[默认值=未指定]
  enum TimeSpanScale {
    DAYS = 0;
    HOURS = 1;
    MINUTES = 2;
    SECONDS = 3;
    MILLISECONDS = 4;
	TICKS = 5;

    MINMAX = 15; // 可疑的
  }
  enum DateTimeKind
  {
     // 未指定为本地时间或协调世界时(UTC)。
     UNSPECIFIED = 0;
     // 表示的时间是UTC。
     UTC = 1;
     // 表示的时间是本地时间。
     LOCAL = 2;
   }
}

message NetObjectProxy {
  int32 existingObjectKey = 1; // 对于已跟踪的对象,此对象首次出现的键
  int32 newObjectKey = 2; // 对于已跟踪的对象,此对象首次出现的**新**键
  int32 existingTypeKey = 3; // 对于动态类型,此类型首次出现的键
  int32 newTypeKey = 4; // 对于动态类型,此类型首次出现的**新**键
  string typeName = 8; // 对于动态类型,类型的名称(仅在newTypeKey存在时出现)
  bytes payload = 10; // 新的字符串/值(仅在newObjectKey存在时出现)
}

message Guid {
  fixed64 lo = 1; // guid的前8个字节(注意:疯狂的字节序)
  fixed64 hi = 2; // guid的后8个字节(注意:疯狂的字节序)
}

message Decimal {
  uint64 lo = 1; // 底层值的前64位
  uint32 hi = 2; // 底层值的后32位
  uint32 signScale = 3; // 十进制数字的位数(位1-16)和符号(位0)
}

execution.proto

syntax = "proto3";

package execution;

option go_package = "./protos";

import "bcl.proto";

enum OrderStatus {
  Working = 0;
  Rejected = 1;
  Cancelled = 2;
  Completed = 3;
}

enum OrderType {
  Limit = 0;
  Market = 1;
  StopLimit = 2;
  StopMarket = 3;
}

enum OrderSide {
  Buy = 0;
  Sell = 1;
}

enum RejectReason {
  NoRejection = 0;
  InstrumentNotFound = 1;
  OrderNotFound = 2;
  InvalidOrderType = 3;
  InvalidAccount = 4;
  InvalidSide = 5;
  InvalidAmount = 6;
  InvalidLimitPrice = 7;
  InvalidQuoteLimit = 8;
  InvalidActivationPrice = 9;
  InvalidTimeInForce = 10;
  MarketHalted = 11;
  MarketPaused = 12;
  NoCounterOrders = 13;
  MissingExpirationTime = 14;
  IncorrectExpirationTime = 15;
  InternalError = 16;
  IllegalStatusSwitch = 17;
  OrderAlreadyExists = 18;
  InstrumentNotReady = 19;
  ExternalSystemError = 20;
}

enum ReportCause {
  NONE = 0;
  NewOrder = 1;
  CancelOrder = 2;
  MassCancel = 3;
  Expiration = 4;
  Trigger = 5;
  MarketStatusChange = 6;
}

enum TimeInForce {
  GoodTillCancel = 0;
  ImmediateOrCancel = 1;
  FillOrKill = 2;
}

enum CancelReason {
  NotCancelled = 0;
  CancelledByTrader = 1;
  CancelledBySystem = 2;
  SelfMatchPrevention = 3;
  OrderTimeInForce = 4;
  Liquidation = 100;
}


message TradeData {
  int64 TradeId = 1;
  bcl.Decimal Amount = 4;
  bcl.Decimal ExecutionPrice = 5;
  OrderStatus OrderStatus = 7;
  int64 AccountId = 11;
  bcl.Guid MatchedOrderExternalId = 14;
  int64 MatchedOrderId = 16;
  bcl.Decimal RemainingAmount = 17;
}

message Execution {
  bytes Origin = 4;
  OrderSide Side = 7;
  bcl.Decimal RequestedPrice = 8;
  bcl.Decimal RequestedAmount = 9;
  bcl.Decimal RemainingAmount = 10;
  int64 ExecutedAt = 13;
  OrderStatus OrderStatus = 14;
  repeated TradeData Trades = 16;
  OrderType OrderType = 20;
  int64 Version = 22;
  int64 AccountId = 23;
  RejectReason RejectReason = 25;
  ReportCause ReportCause = 26;
  bcl.Guid InstructionId = 27;
  bcl.Guid ExternalOrderId = 28;
  int32 ExecutionEngineMarketId = 29;
  int64 OrderId = 30;
  CancelReason CancelReason = 31;
  int64 TxId = 32;
  TimeInForce TimeInForce = 34;
  string CancelledBy = 35;
}

**protos/**文件夹

此文件夹中的文件是使用以下命令从proto文件生成的:

protoc --go_out=protos --go_opt=paths=source_relative bcl.proto execution.proto

go.mod

module mymodule.local

go 1.20

require google.golang.org/protobuf v1.30.0

main.go

package main

import (
	"encoding/binary"
	"log"

	"google.golang.org/protobuf/proto"

	"mymodule.local/protos"
)

func main() {
	data := []byte{5, 85, 0, 0, 0, 56, 1, 66, 3, 8, 144, 78, 74, 2, 8, 1, 82, 2, 8, 1, 104, 197, 192, 132, 194, 159, 143, 219, 237, 8, 176, 1, 25, 184, 1, 11, 208, 1, 1, 218, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 226, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 232, 1, 1, 240, 1, 25, 128, 2, 25}
	if len(data) < 5 {
		log.Fatal("data should contain at least 5 bytes")
	}
	messageType := data[0]
	length := binary.LittleEndian.Uint32(data[1:5])
	data = data[5:]
	if length != uint32(len(data)) {
		log.Fatalf("invalid data length: %d", length)
	}
	execution := &protos.Execution{}

	err := proto.Unmarshal(data, execution)
	if err != nil {
		log.Fatalf("Err unmarshalling!: %v", err)
	}

	log.Printf("message type: %d, message: %+v", messageType, execution)
}

提供的数据的输出:

2023/06/15 17:50:58 message type: 5, message: Side:Sell  RequestedPrice:{lo:10000}  RequestedAmount:{lo:1}  RemainingAmount:{lo:1}  ExecutedAt:638223043314917445  Version:25  AccountId:11  ReportCause:NewOrder  InstructionId:{lo:5574686611683820165  hi:10500929413443338416}  ExternalOrderId:{lo:5574686611683820165  hi:10500929413443338416}  ExecutionEngineMarketId:1  OrderId:25  TxId:25
英文:

Based on the discussion in the comments, I have managed to unmarshal the data.

Notes:

  1. the data is prefixed with 5 bytes (this is totally unnecessary):
    • 1 byte for the message type
    • 4 bytes for the data length
  2. the C# implementation uses the decimal and Guid data types that are specific to C#. (as commented in bcl.proto, cross-platform code should usually avoid them completely).

Here is the folder structure:

├── bcl.proto
├── execution.proto
├── go.mod
├── go.sum
├── main.go
└── protos
├── bcl.pb.go
└── execution.pb.go

bcl.proto:

This file is copied from github.com/protobuf-net/protobuf-net. It's required because the .NET implementation use Decimal and Guid from this proto file.

// The types in here indicate how protobuf-net represents certain types when using protobuf-net specific
// library features. Note that it is not *required* to use any of these types, and cross-platform code
// should usually avoid them completely (ideally starting from a .proto schema)

// Some of these are ugly, sorry. The TimeSpan / DateTime dates here pre-date the introduction of Timestamp
// and Duration, and the &quot;well known&quot; types should be preferred when possible. Guids are particularly
// awkward - it turns out that there are multiple guid representations, and I accidentally used one that
// I can only call... &quot;crazy-endian&quot;. Just make sure you check the order!

// It should not be necessary to use bcl.proto from code that uses protobuf-net

syntax = &quot;proto3&quot;;

option csharp_namespace = &quot;ProtoBuf.Bcl&quot;;
option go_package = &quot;./protos&quot;;

package bcl;

message TimeSpan {
  sint64 value = 1; // the size of the timespan (in units of the selected scale)
  TimeSpanScale scale = 2; // the scale of the timespan [default = DAYS]
  enum TimeSpanScale {
    DAYS = 0;
    HOURS = 1;
    MINUTES = 2;
    SECONDS = 3;
    MILLISECONDS = 4;
	TICKS = 5;

    MINMAX = 15; // dubious
  }
}

message DateTime {
  sint64 value = 1; // the offset (in units of the selected scale) from 1970/01/01
  TimeSpanScale scale = 2; // the scale of the timespan [default = DAYS]
  DateTimeKind kind = 3; // the kind of date/time being represented [default = UNSPECIFIED]
  enum TimeSpanScale {
    DAYS = 0;
    HOURS = 1;
    MINUTES = 2;
    SECONDS = 3;
    MILLISECONDS = 4;
	TICKS = 5;

    MINMAX = 15; // dubious
  }
  enum DateTimeKind
  {
     // The time represented is not specified as either local time or Coordinated Universal Time (UTC).
     UNSPECIFIED = 0;
     // The time represented is UTC.
     UTC = 1;
     // The time represented is local time.
     LOCAL = 2;
   }
}

message NetObjectProxy {
  int32 existingObjectKey = 1; // for a tracked object, the key of the **first** time this object was seen
  int32 newObjectKey = 2; // for a tracked object, a **new** key, the first time this object is seen
  int32 existingTypeKey = 3; // for dynamic typing, the key of the **first** time this type was seen
  int32 newTypeKey = 4; // for dynamic typing, a **new** key, the first time this type is seen
  string typeName = 8; // for dynamic typing, the name of the type (only present along with newTypeKey)
  bytes payload = 10; // the new string/value (only present along with newObjectKey)
}

message Guid {
  fixed64 lo = 1; // the first 8 bytes of the guid (note:crazy-endian)
  fixed64 hi = 2; // the second 8 bytes of the guid (note:crazy-endian)
}

message Decimal {
  uint64 lo = 1; // the first 64 bits of the underlying value
  uint32 hi = 2; // the last 32 bis of the underlying value
  uint32 signScale = 3; // the number of decimal digits (bits 1-16), and the sign (bit 0)
}

execution.proto

syntax = &quot;proto3&quot;;

package execution;

option go_package = &quot;./protos&quot;;

import &quot;bcl.proto&quot;;

enum OrderStatus {
  Working = 0;
  Rejected = 1;
  Cancelled = 2;
  Completed = 3;
}

enum OrderType {
  Limit = 0;
  Market = 1;
  StopLimit = 2;
  StopMarket = 3;
}

enum OrderSide {
  Buy = 0;
  Sell = 1;
}

enum RejectReason {
  NoRejection = 0;
  InstrumentNotFound = 1;
  OrderNotFound = 2;
  InvalidOrderType = 3;
  InvalidAccount = 4;
  InvalidSide = 5;
  InvalidAmount = 6;
  InvalidLimitPrice = 7;
  InvalidQuoteLimit = 8;
  InvalidActivationPrice = 9;
  InvalidTimeInForce = 10;
  MarketHalted = 11;
  MarketPaused = 12;
  NoCounterOrders = 13;
  MissingExpirationTime = 14;
  IncorrectExpirationTime = 15;
  InternalError = 16;
  IllegalStatusSwitch = 17;
  OrderAlreadyExists = 18;
  InstrumentNotReady = 19;
  ExternalSystemError = 20;
}

enum ReportCause {
  NONE = 0;
  NewOrder = 1;
  CancelOrder = 2;
  MassCancel = 3;
  Expiration = 4;
  Trigger = 5;
  MarketStatusChange = 6;
}

enum TimeInForce {
  GoodTillCancel = 0;
  ImmediateOrCancel = 1;
  FillOrKill = 2;
}

enum CancelReason {
  NotCancelled = 0;
  CancelledByTrader = 1;
  CancelledBySystem = 2;
  SelfMatchPrevention = 3;
  OrderTimeInForce = 4;
  Liquidation = 100;
}


message TradeData {
  int64 TradeId = 1;
  bcl.Decimal Amount = 4;
  bcl.Decimal ExecutionPrice = 5;
  OrderStatus OrderStatus = 7;
  int64 AccountId = 11;
  bcl.Guid MatchedOrderExternalId = 14;
  int64 MatchedOrderId = 16;
  bcl.Decimal RemainingAmount = 17;
}

message Execution {
  bytes Origin = 4;
  OrderSide Side = 7;
  bcl.Decimal RequestedPrice = 8;
  bcl.Decimal RequestedAmount = 9;
  bcl.Decimal RemainingAmount = 10;
  int64 ExecutedAt = 13;
  OrderStatus OrderStatus = 14;
  repeated TradeData Trades = 16;
  OrderType OrderType = 20;
  int64 Version = 22;
  int64 AccountId = 23;
  RejectReason RejectReason = 25;
  ReportCause ReportCause = 26;
  bcl.Guid InstructionId = 27;
  bcl.Guid ExternalOrderId = 28;
  int32 ExecutionEngineMarketId = 29;
  int64 OrderId = 30;
  CancelReason CancelReason = 31;
  int64 TxId = 32;
  TimeInForce TimeInForce = 34;
  string CancelledBy = 35;
}

protos/

The files in this folder are generated from the proto files with this command:

protoc --go_out=protos --go_opt=paths=source_relative bcl.proto execution.proto

go.mod

module mymodule.local
go 1.20
require google.golang.org/protobuf v1.30.0

main.go

package main

import (
	&quot;encoding/binary&quot;
	&quot;log&quot;

	&quot;google.golang.org/protobuf/proto&quot;

	&quot;mymodule.local/protos&quot;
)

func main() {
	data := []byte{5, 85, 0, 0, 0, 56, 1, 66, 3, 8, 144, 78, 74, 2, 8, 1, 82, 2, 8, 1, 104, 197, 192, 132, 194, 159, 143, 219, 237, 8, 176, 1, 25, 184, 1, 11, 208, 1, 1, 218, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 226, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 232, 1, 1, 240, 1, 25, 128, 2, 25}
	if len(data) &lt; 5 {
		log.Fatal(&quot;data should contain at least 5 bytes&quot;)
	}
	messageType := data[0]
	length := binary.LittleEndian.Uint32(data[1:5])
	data = data[5:]
	if length != uint32(len(data)) {
		log.Fatalf(&quot;invalid data length: %d&quot;, length)
	}
	execution := &amp;protos.Execution{}

	err := proto.Unmarshal(data, execution)
	if err != nil {
		log.Fatalf(&quot;Err unmarshalling!: %v&quot;, err)
	}

	log.Printf(&quot;message type: %d, message: %+v&quot;, messageType, execution)
}

Output of the data provided in the quetion:

2023/06/15 17:50:58 message type: 5, message: Side:Sell  RequestedPrice:{lo:10000}  RequestedAmount:{lo:1}  RemainingAmount:{lo:1}  ExecutedAt:638223043314917445  Version:25  AccountId:11  ReportCause:NewOrder  InstructionId:{lo:5574686611683820165  hi:10500929413443338416}  ExternalOrderId:{lo:5574686611683820165  hi:10500929413443338416}  ExecutionEngineMarketId:1  OrderId:25  TxId:25

huangapple
  • 本文由 发表于 2023年6月14日 10:31:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76469783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定