diff --git a/p2pool/sidechain/poolblock.go b/p2pool/sidechain/poolblock.go index ff3507d..e46e156 100644 --- a/p2pool/sidechain/poolblock.go +++ b/p2pool/sidechain/poolblock.go @@ -16,6 +16,7 @@ import ( "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/consensus/v3/utils" fasthex "github.com/tmthrgd/go-hex" + "io" "net/netip" "slices" "sync/atomic" @@ -102,6 +103,77 @@ type PoolBlockReceptionMetadata struct { SoftwareVersion uint32 `json:"software_version"` } +func (m *PoolBlockReceptionMetadata) UnmarshalBinary(buf []byte) error { + r := bytes.NewReader(buf) + s, err := binary.ReadUvarint(r) + if err != nil { + return err + } + ns, err := binary.ReadUvarint(r) + if err != nil { + return err + } + + m.LocalTime = time.Unix(int64(s), int64(ns)).UTC() + + l, err := binary.ReadUvarint(r) + if err != nil { + return err + } + ip := make([]byte, l) + _, err = io.ReadFull(r, ip) + if err != nil { + return err + } + + err = m.AddressPort.UnmarshalBinary(ip) + if err != nil { + return err + } + + m.PeerId, err = binary.ReadUvarint(r) + if err != nil { + return err + } + + sId, err := binary.ReadUvarint(r) + if err != nil { + return err + } + + sVer, err := binary.ReadUvarint(r) + if err != nil { + return err + } + + m.SoftwareId = uint32(sId) + m.SoftwareVersion = uint32(sVer) + + return nil +} + +func (m *PoolBlockReceptionMetadata) MarshalBinary() ([]byte, error) { + s := uint64(m.LocalTime.Unix()) + ns := uint64(m.LocalTime.Nanosecond()) + + ip, _ := m.AddressPort.MarshalBinary() + out := make([]byte, 0, + utils.UVarInt64Size(s)+utils.UVarInt64Size(ns)+ + utils.UVarInt64Size(len(ip))+len(ip)+ + utils.UVarInt64Size(m.PeerId)+ + utils.UVarInt64Size(uint64(m.SoftwareId))+ + utils.UVarInt64Size(uint64(m.SoftwareVersion))) + + out = binary.AppendUvarint(out, s) + out = binary.AppendUvarint(out, ns) + out = binary.AppendUvarint(out, uint64(len(ip))) + out = append(out, ip...) + out = binary.AppendUvarint(out, m.PeerId) + out = binary.AppendUvarint(out, uint64(m.SoftwareId)) + out = binary.AppendUvarint(out, uint64(m.SoftwareVersion)) + return out, nil +} + func (b *PoolBlock) iteratorGetParent(getByTemplateId GetByTemplateIdFunc) *PoolBlock { if b.iterationCache == nil { return getByTemplateId(b.Side.Parent) diff --git a/p2pool/sidechain/poolblock_test.go b/p2pool/sidechain/poolblock_test.go index b366e94..8186040 100644 --- a/p2pool/sidechain/poolblock_test.go +++ b/p2pool/sidechain/poolblock_test.go @@ -5,9 +5,13 @@ import ( "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + types2 "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types" "git.gammaspectra.live/P2Pool/consensus/v3/types" + unsafeRandom "math/rand/v2" + "net/netip" "os" "testing" + "time" ) func testPoolBlock(b *PoolBlock, t *testing.T, expectedBufferLength int, majorVersion, minorVersion uint8, sideHeight uint64, nonce uint32, templateId, mainId, expectedPowHash, privateKeySeed, coinbaseId types.Hash, privateKey crypto.PrivateKeyBytes) { @@ -77,6 +81,31 @@ func testPoolBlock(b *PoolBlock, t *testing.T, expectedBufferLength int, majorVe } } +func TestPoolBlockMetadata(t *testing.T) { + meta := PoolBlockReceptionMetadata{ + LocalTime: time.Now().UTC(), + AddressPort: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 1234), + PeerId: unsafeRandom.Uint64(), + SoftwareId: uint32(types2.CurrentSoftwareId), + SoftwareVersion: uint32(types2.CurrentSoftwareVersion), + } + + blob, err := meta.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + var meta2 PoolBlockReceptionMetadata + err = meta2.UnmarshalBinary(blob) + if err != nil { + t.Fatal(err) + } + + if meta != meta2 { + t.Errorf("different metadata: %v, %v", meta, meta2) + } +} + func TestPoolBlockDecode(t *testing.T) { if buf, err := os.ReadFile("testdata/block.dat"); err != nil {