forked from algorand/go-algorand
-
Notifications
You must be signed in to change notification settings - Fork 0
/
topics.go
147 lines (128 loc) · 4.11 KB
/
topics.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (C) 2019-2024 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
package network
import (
"encoding/binary"
"fmt"
"github.com/algorand/go-algorand/crypto"
)
// Constant strings used as keys for topics
const (
requestHashKey = "RequestHash"
ErrorKey = "Error" // used for passing an error message
)
// Topic is a key-value pair
//
//msgp:ignore Topic
type Topic struct {
key string
data []byte
}
// MakeTopic Creates a Topic
func MakeTopic(key string, data []byte) Topic {
return Topic{key: key, data: data}
}
// Topics is an array of type Topic
// The maximum number of topics allowed is 32
// Each topic key can be 64 characters long and cannot be size 0
//
//msgp:ignore Topics
type Topics []Topic
// MarshallTopics serializes the topics into a byte array
func (ts Topics) MarshallTopics() (b []byte) {
// Calculate the total buffer size required to store the topics
bufferSize := binary.MaxVarintLen32 // store topic array size
for _, val := range ts {
bufferSize += 2 * binary.MaxVarintLen32 // store key size and the data size
bufferSize += len(val.key)
bufferSize += len(val.data)
}
buffer := make([]byte, bufferSize)
bidx := binary.PutUvarint(buffer, uint64(len(ts)))
for _, val := range ts {
// write the key size
n := binary.PutUvarint(buffer[bidx:], uint64(len(val.key)))
bidx += n
// write the key
n = copy(buffer[bidx:], []byte(val.key))
bidx += n
// write the data size
n = binary.PutUvarint(buffer[bidx:], uint64(len(val.data)))
bidx += n
// write the data
n = copy(buffer[bidx:], val.data)
bidx += n
}
return buffer[:bidx]
}
// UnmarshallTopics unmarshalls the topics from the byte array
func UnmarshallTopics(buffer []byte) (ts Topics, err error) {
// Get the number of topics
var idx int
numTopics, nr := binary.Uvarint(buffer[idx:])
if nr <= 0 {
return nil, fmt.Errorf("UnmarshallTopics: could not read the number of topics")
}
if numTopics > 32 { // numTopics is uint64
return nil, fmt.Errorf("UnmarshallTopics: number of topics %d is greater than 32", numTopics)
}
idx += nr
topics := make([]Topic, numTopics)
for x := 0; x < int(numTopics); x++ {
// read the key length
strlen, nr := binary.Uvarint(buffer[idx:])
if nr <= 0 {
return nil, fmt.Errorf("UnmarshallTopics: could not read the key length")
}
idx += nr
// read the key
if len(buffer) < idx+int(strlen) || strlen > 64 || strlen == 0 {
return nil, fmt.Errorf("UnmarshallTopics: could not read the key")
}
topics[x].key = string(buffer[idx : idx+int(strlen)])
idx += int(strlen)
// read the data length
dataLen, nr := binary.Uvarint(buffer[idx:])
if nr <= 0 || dataLen > MaxMessageLength {
return nil, fmt.Errorf("UnmarshallTopics: could not read the data length")
}
idx += nr
// read the data
if len(buffer) < idx+int(dataLen) {
return nil, fmt.Errorf("UnmarshallTopics: data larger than buffer size")
}
topics[x].data = make([]byte, dataLen)
copy(topics[x].data, buffer[idx:idx+int(dataLen)])
idx += int(dataLen)
}
return topics, nil
}
// hashTopics returns the hash of serialized topics.
// Expects the nonce to be already added as a topic
func hashTopics(topics []byte) (partialHash uint64) {
digest := crypto.Hash(topics)
partialHash = digest.TrimUint64()
return partialHash
}
// GetValue returns the value of the key if the key is found in the topics
func (ts *Topics) GetValue(key string) (val []byte, found bool) {
for _, t := range *ts {
if t.key == key {
return t.data, true
}
}
return
}