Skip to content

Commit f8e7de0

Browse files
fix: update logic to encode and calculate block hash (#720)
1 parent a980b0c commit f8e7de0

File tree

2 files changed

+232
-33
lines changed

2 files changed

+232
-33
lines changed

ledger/verify_block_body.go

Lines changed: 222 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,32 @@ package ledger
2020

2121
import (
2222
"bytes"
23+
"encoding/binary"
2324
"encoding/hex"
2425
"fmt"
25-
"strconv"
26-
2726
"github.com/blinklabs-io/gouroboros/cbor"
27+
_cbor "github.com/fxamacker/cbor/v2"
2828
"golang.org/x/crypto/blake2b"
29+
"math"
30+
"reflect"
31+
"strconv"
32+
)
33+
34+
const (
35+
maxAdditionalInformationWithoutArgument = 23
36+
additionalInformationWith1ByteArgument = 24
37+
additionalInformationWith2ByteArgument = 25
38+
additionalInformationWith4ByteArgument = 26
39+
additionalInformationWith8ByteArgument = 27
2940
)
3041

3142
const (
3243
LOVELACE_TOKEN = "lovelace"
3344
BLOCK_BODY_HASH_ZERO_TX_HEX = "29571d16f081709b3c48651860077bebf9340abb3fc7133443c54f1f5a5edcf1"
45+
MAX_LIST_LENGTH_CBOR = 23
46+
CBOR_TYPE_MAP = 0xa0
47+
CBOR_TYPE_MAP_INDEF = 0xbf
48+
CBOR_BREAK_TAG = 0xff
3449
)
3550

3651
func VerifyBlockBody(data string, blockBodyHash string) (bool, error) {
@@ -74,11 +89,204 @@ func VerifyBlockBody(data string, blockBodyHash string) (bool, error) {
7489
return bytes.Equal(calculateBlockBodyHashByte[:32], blockBodyHashByte), nil
7590
}
7691

92+
func CustomTagSet() _cbor.TagSet {
93+
customTagSet := _cbor.NewTagSet()
94+
tagOpts := _cbor.TagOptions{
95+
EncTag: _cbor.EncTagRequired,
96+
DecTag: _cbor.DecTagRequired,
97+
}
98+
// Wrapped CBOR
99+
if err := customTagSet.Add(
100+
tagOpts,
101+
reflect.TypeOf(cbor.WrappedCbor{}),
102+
cbor.CborTagCbor,
103+
); err != nil {
104+
panic(err)
105+
}
106+
// Rational numbers
107+
if err := customTagSet.Add(
108+
tagOpts,
109+
reflect.TypeOf(cbor.Rat{}),
110+
cbor.CborTagRational,
111+
); err != nil {
112+
panic(err)
113+
}
114+
// Sets
115+
if err := customTagSet.Add(
116+
tagOpts,
117+
reflect.TypeOf(cbor.Set{}),
118+
cbor.CborTagSet,
119+
); err != nil {
120+
panic(err)
121+
}
122+
// Maps
123+
if err := customTagSet.Add(
124+
tagOpts,
125+
reflect.TypeOf(cbor.Map{}),
126+
cbor.CborTagMap,
127+
); err != nil {
128+
panic(err)
129+
}
130+
131+
return customTagSet
132+
}
133+
134+
func GetEncMode() (_cbor.EncMode, error) {
135+
opts := _cbor.EncOptions{
136+
Sort: _cbor.SortNone,
137+
}
138+
customTagSet := CustomTagSet()
139+
em, err := opts.EncModeWithTags(customTagSet)
140+
if err != nil {
141+
return nil, err
142+
}
143+
return em, nil
144+
}
145+
146+
func EncodeCborList(data []cbor.RawMessage) ([]byte, error) {
147+
// Cardano base consider list more than 23 will be ListLenIndef
148+
// https://github.com/IntersectMBO/cardano-base/blob/e86a25c54389ddd0f77fdbc3f3615c57bd91d543/cardano-binary/src/Cardano/Binary/ToCBOR.hs#L708C10-L708C28
149+
if len(data) <= MAX_LIST_LENGTH_CBOR {
150+
return cbor.Encode(data)
151+
}
152+
buf := bytes.NewBuffer(nil)
153+
em, err := GetEncMode()
154+
if err != nil {
155+
return nil, err
156+
}
157+
enc := em.NewEncoder(buf)
158+
159+
if err := enc.StartIndefiniteArray(); err != nil {
160+
return nil, err
161+
}
162+
for _, item := range data {
163+
err = enc.Encode(item)
164+
if err != nil {
165+
return nil, err
166+
}
167+
}
168+
if err := enc.EndIndefinite(); err != nil {
169+
return nil, err
170+
}
171+
return buf.Bytes(), err
172+
}
173+
174+
func EncodeCborTxSeq(data []uint) ([]byte, error) {
175+
// Cardano base consider list more than 23 will be ListLenIndef
176+
// https://github.com/IntersectMBO/cardano-base/blob/e86a25c54389ddd0f77fdbc3f3615c57bd91d543/cardano-binary/src/Cardano/Binary/ToCBOR.hs#L708C10-L708C28
177+
178+
if len(data) <= MAX_LIST_LENGTH_CBOR {
179+
return cbor.Encode(data)
180+
}
181+
buf := bytes.NewBuffer(nil)
182+
em, err := GetEncMode()
183+
if err != nil {
184+
return nil, err
185+
}
186+
enc := em.NewEncoder(buf)
187+
188+
if err := enc.StartIndefiniteArray(); err != nil {
189+
return nil, err
190+
}
191+
for _, item := range data {
192+
err = enc.Encode(item)
193+
if err != nil {
194+
return nil, err
195+
}
196+
}
197+
if err := enc.EndIndefinite(); err != nil {
198+
return nil, err
199+
}
200+
return buf.Bytes(), err
201+
}
202+
203+
type AuxData struct {
204+
index uint64
205+
data []byte
206+
}
207+
208+
// encodeHead writes CBOR head of specified type t and returns number of bytes written.
209+
// copy from https://github.com/fxamacker/cbor/blob/46c3919161ecd1beff1b80867e08efb37d43f27c/encode.go#L1728
210+
func encodeHead(e *bytes.Buffer, t byte, n uint64) int {
211+
if n <= maxAdditionalInformationWithoutArgument {
212+
const headSize = 1
213+
e.WriteByte(t | byte(n))
214+
return headSize
215+
}
216+
217+
if n <= math.MaxUint8 {
218+
const headSize = 2
219+
scratch := [headSize]byte{
220+
t | byte(additionalInformationWith1ByteArgument),
221+
byte(n),
222+
}
223+
e.Write(scratch[:])
224+
return headSize
225+
}
226+
227+
if n <= math.MaxUint16 {
228+
const headSize = 3
229+
var scratch [headSize]byte
230+
scratch[0] = t | byte(additionalInformationWith2ByteArgument)
231+
binary.BigEndian.PutUint16(scratch[1:], uint16(n))
232+
e.Write(scratch[:])
233+
return headSize
234+
}
235+
236+
if n <= math.MaxUint32 {
237+
const headSize = 5
238+
var scratch [headSize]byte
239+
scratch[0] = t | byte(additionalInformationWith4ByteArgument)
240+
binary.BigEndian.PutUint32(scratch[1:], uint32(n))
241+
e.Write(scratch[:])
242+
return headSize
243+
}
244+
245+
const headSize = 9
246+
var scratch [headSize]byte
247+
scratch[0] = t | byte(additionalInformationWith8ByteArgument)
248+
binary.BigEndian.PutUint64(scratch[1:], n)
249+
e.Write(scratch[:])
250+
return headSize
251+
}
252+
253+
// EncodeCborMap manual build aux bytes data
254+
func EncodeCborMap(data []AuxData) ([]byte, error) {
255+
dataLen := len(data)
256+
if dataLen == 0 {
257+
txSeqMetadata := make(map[uint64]interface{})
258+
return cbor.Encode(txSeqMetadata)
259+
}
260+
var dataBuffer bytes.Buffer
261+
var dataBytes []byte
262+
if dataLen <= MAX_LIST_LENGTH_CBOR {
263+
encodeHead(&dataBuffer, byte(CBOR_TYPE_MAP), uint64(dataLen))
264+
dataBytes = dataBuffer.Bytes()
265+
} else {
266+
dataBytes = []byte{
267+
uint8(CBOR_TYPE_MAP_INDEF),
268+
}
269+
}
270+
271+
for _, aux := range data {
272+
dataIndex := aux.index
273+
dataValue := aux.data
274+
indexBytes, _ := cbor.Encode(dataIndex)
275+
dataBytes = append(dataBytes, indexBytes...)
276+
dataBytes = append(dataBytes, dataValue...)
277+
}
278+
if dataLen > MAX_LIST_LENGTH_CBOR {
279+
dataBytes = append(dataBytes, CBOR_BREAK_TAG)
280+
}
281+
282+
return dataBytes, nil
283+
}
284+
77285
func CalculateBlockBodyHash(txsRaw [][]string) ([]byte, error) {
78-
var txSeqBody []cbor.RawMessage
79-
var txSeqWit []cbor.RawMessage
80-
txSeqMetadata := make(map[uint64]interface{}, len(txsRaw))
81-
txSeqNonValid := []uint{}
286+
txSeqBody := make([]cbor.RawMessage, 0)
287+
txSeqWit := make([]cbor.RawMessage, 0)
288+
auxRawData := make([]AuxData, 0)
289+
txSeqNonValid := make([]uint, 0)
82290
for index, tx := range txsRaw {
83291
if len(tx) != 3 {
84292
return nil, fmt.Errorf(
@@ -120,22 +328,14 @@ func CalculateBlockBodyHash(txsRaw [][]string) ([]byte, error) {
120328
auxBytesError.Error(),
121329
)
122330
}
123-
124-
var auxInterface interface{}
125-
_, auxDecodeError := cbor.Decode(auxBytes, &auxInterface)
126-
if auxDecodeError != nil {
127-
return nil, fmt.Errorf(
128-
"CalculateBlockBodyHash: decode aux tx[%v] error, %v",
129-
index,
130-
auxDecodeError.Error(),
131-
)
132-
}
133-
134-
txSeqMetadata[uint64(index)] = auxInterface
331+
auxRawData = append(auxRawData, AuxData{
332+
index: uint64(index),
333+
data: auxBytes,
334+
})
135335
}
136336
// TODO: should form nonValid TX here
137337
}
138-
txSeqBodyBytes, txSeqBodyBytesError := cbor.Encode(txSeqBody)
338+
txSeqBodyBytes, txSeqBodyBytesError := EncodeCborList(txSeqBody)
139339
if txSeqBodyBytesError != nil {
140340
return nil, fmt.Errorf(
141341
"CalculateBlockBodyHash: encode txSeqBody error, %v",
@@ -146,7 +346,7 @@ func CalculateBlockBodyHash(txsRaw [][]string) ([]byte, error) {
146346
txSeqBodySum32Bytes := blake2b.Sum256(txSeqBodyBytes)
147347
txSeqBodySumBytes := txSeqBodySum32Bytes[:]
148348

149-
txSeqWitsBytes, txSeqWitsBytesError := cbor.Encode(txSeqWit)
349+
txSeqWitsBytes, txSeqWitsBytesError := EncodeCborList(txSeqWit)
150350
if txSeqWitsBytesError != nil {
151351
return nil, fmt.Errorf(
152352
"CalculateBlockBodyHash: encode txSeqWit error, %v",
@@ -156,7 +356,7 @@ func CalculateBlockBodyHash(txsRaw [][]string) ([]byte, error) {
156356
txSeqWitsSum32Bytes := blake2b.Sum256(txSeqWitsBytes)
157357
txSeqWitsSumBytes := txSeqWitsSum32Bytes[:]
158358

159-
txSeqMetadataBytes, txSeqMetadataBytesError := cbor.Encode(txSeqMetadata)
359+
txSeqMetadataBytes, txSeqMetadataBytesError := EncodeCborMap(auxRawData)
160360
if txSeqMetadataBytesError != nil {
161361
return nil, fmt.Errorf(
162362
"CalculateBlockBodyHash: encode txSeqMetadata error, %v",
@@ -166,7 +366,7 @@ func CalculateBlockBodyHash(txsRaw [][]string) ([]byte, error) {
166366
txSeqMetadataSum32Bytes := blake2b.Sum256(txSeqMetadataBytes)
167367
txSeqMetadataSumBytes := txSeqMetadataSum32Bytes[:]
168368

169-
txSeqNonValidBytes, txSeqNonValidBytesError := cbor.Encode(txSeqNonValid)
369+
txSeqNonValidBytes, txSeqNonValidBytesError := EncodeCborTxSeq(txSeqNonValid)
170370
if txSeqNonValidBytesError != nil {
171371
return nil, fmt.Errorf(
172372
"CalculateBlockBodyHash: encode txSeqNonValid error, %v",

0 commit comments

Comments
 (0)