-
Notifications
You must be signed in to change notification settings - Fork 8
/
program.go
150 lines (124 loc) · 3.31 KB
/
program.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
148
149
150
package types
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"io"
)
// SerializedProgram An opaque representation of a clvm program. It has a more limited interface than a full SExp
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/program.py#L232
type SerializedProgram Bytes
// MaxSingleByte Max single byte
const MaxSingleByte byte = 0x7f
// BackReference back referencee marker
const BackReference byte = 0xfe
// ConsBoxMarker cons box marker
const ConsBoxMarker byte = 0xff
const (
badEncErr = "bad encoding"
internalErr = "internal error"
)
// MarshalJSON custom hex marshaller
func (g SerializedProgram) MarshalJSON() ([]byte, error) {
return json.Marshal(Bytes(g))
}
// UnmarshalJSON custom hex unmarshaller
func (g *SerializedProgram) UnmarshalJSON(data []byte) error {
b := Bytes{}
err := json.Unmarshal(data, &b)
if err != nil {
return err
}
*g = SerializedProgram(b)
return nil
}
// SerializedLengthFromBytesTrusted returns the length
func SerializedLengthFromBytesTrusted(b []byte) (uint64, error) {
reader := bytes.NewReader(b)
var opsCounter uint64 = 1
for opsCounter > 0 {
opsCounter--
var currentByte byte
err := binary.Read(reader, binary.BigEndian, ¤tByte)
if err != nil {
if err == io.EOF {
return 0, errors.New("unexpected end of input")
}
return 0, err
}
if currentByte == ConsBoxMarker {
opsCounter += 2
} else if currentByte == BackReference {
var firstByte byte
err = binary.Read(reader, binary.BigEndian, &firstByte)
if err != nil {
return 0, errors.New("unexpected end of input")
}
if firstByte > MaxSingleByte {
pathSize, err := decodeSize(reader, firstByte)
if err != nil {
return 0, err
}
_, err = reader.Seek(int64(pathSize), io.SeekCurrent)
if err != nil {
return 0, errors.New("bad encoding")
}
}
} else if currentByte == 0x80 || currentByte <= MaxSingleByte {
// This one byte we just read was the whole atom or the special case of NIL.
} else {
blobSize, err := decodeSize(reader, currentByte)
if err != nil {
return 0, err
}
_, err = reader.Seek(int64(blobSize), io.SeekCurrent)
if err != nil {
return 0, errors.New("bad encoding")
}
}
}
position, err := reader.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
return uint64(position), nil
}
func decodeSize(reader *bytes.Reader, initialB byte) (uint64, error) {
_, length, err := decodeSizeWithOffset(reader, initialB)
return length, err
}
func decodeSizeWithOffset(reader *bytes.Reader, initialB byte) (uint64, uint64, error) {
bitMask := byte(0x80)
if (initialB & bitMask) == 0 {
return 0, 0, errors.New(internalErr)
}
var atomStartOffset uint64 = 0
b := initialB
for (b & bitMask) != 0 {
atomStartOffset++
b &= 0xff ^ bitMask
bitMask >>= 1
}
sizeBlob := make([]byte, atomStartOffset)
sizeBlob[0] = b
if atomStartOffset > 1 {
// We need to read atomStartOffset-1 more bytes
_, err := io.ReadFull(reader, sizeBlob[1:])
if err != nil {
return 0, 0, err
}
}
var atomSize uint64 = 0
if len(sizeBlob) > 6 {
return 0, 0, errors.New(badEncErr)
}
for _, b := range sizeBlob {
atomSize <<= 8
atomSize += uint64(b)
}
if atomSize >= 0x400000000 {
return 0, 0, errors.New(badEncErr)
}
return atomStartOffset, atomSize, nil
}