-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmarshal.go
242 lines (226 loc) · 6.9 KB
/
marshal.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package rct
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ipld/go-ipld-prime"
dageth "github.com/vulcanize/go-codec-dageth"
"github.com/vulcanize/go-codec-dageth/shared"
)
// Encode provides an IPLD codec encode interface for eth receipt IPLDs.
// This function is registered via the go-ipld-prime link loader for multicodec
// code 0x95 when this package is invoked via init.
func Encode(node ipld.Node, w io.Writer) error {
// 1KiB can be allocated on the stack, and covers most small nodes
// without having to grow the buffer and cause allocations.
enc := make([]byte, 0, 1024)
enc, err := AppendEncode(enc, node)
if err != nil {
return err
}
_, err = w.Write(enc)
return err
}
// AppendEncode is like Encode, but it uses a destination buffer directly.
// This means less copying of bytes, and if the destination has enough capacity,
// fewer allocations.
func AppendEncode(enc []byte, inNode ipld.Node) ([]byte, error) {
rct := new(receiptRLP)
txType, err := packReceiptRLP(rct, inNode)
if err != nil {
return enc, fmt.Errorf("unable to encode receiptRLP (%v)", err)
}
wbs := shared.NewWriteableByteSlice(&enc)
switch txType {
case types.LegacyTxType:
if err := rlp.Encode(wbs, rct); err != nil {
return enc, fmt.Errorf("invalid DAG-ETH Receipt form (%v)", err)
}
return enc, nil
case types.AccessListTxType, types.DynamicFeeTxType:
enc = append(enc, txType)
if err := rlp.Encode(wbs, rct); err != nil {
return enc, fmt.Errorf("invalid DAG-ETH Receipt form (%v)", err)
}
return enc, nil
default:
return enc, fmt.Errorf("invalid DAG-ETH Receipt form (unrecognized TxType %d)", txType)
}
}
var (
receiptStatusFailedRLP = []byte{}
receiptStatusSuccessfulRLP = []byte{0x01}
receiptStatusFailed = []byte{0, 0, 0, 0, 0, 0, 0, 0}
receiptStatusSuccessful = []byte{0, 0, 0, 0, 0, 0, 0, 1}
)
// EncodeReceipt packs the node into the go-ethereum Receipt
func EncodeReceipt(receipt *types.Receipt, inNode ipld.Node) error {
rct := new(receiptRLP)
txType, err := packReceiptRLP(rct, inNode)
if err != nil {
return fmt.Errorf("unable to pack receiptRLP struct: %v", err)
}
receipt.Type = txType
receipt.Bloom = rct.Bloom
receipt.CumulativeGasUsed = rct.CumulativeGasUsed
receipt.Logs = rct.Logs
switch {
case bytes.Equal(rct.PostStateOrStatus, receiptStatusSuccessfulRLP):
receipt.Status = types.ReceiptStatusSuccessful
case bytes.Equal(rct.PostStateOrStatus, receiptStatusFailedRLP):
receipt.Status = types.ReceiptStatusFailed
case len(rct.PostStateOrStatus) == len(common.Hash{}):
receipt.PostState = rct.PostStateOrStatus
default:
return fmt.Errorf("invalid DAG-ETH Receipt PostStateOrStatus %x", rct.PostStateOrStatus)
}
return nil
}
func packReceiptRLP(rct *receiptRLP, inNode ipld.Node) (uint8, error) {
// Wrap in a typed node for some basic schema form checking
builder := dageth.Type.Receipt.NewBuilder()
if err := builder.AssignNode(inNode); err != nil {
return 0, err
}
node := builder.Build()
txType, err := shared.GetTxType(node)
if err != nil {
return 0, fmt.Errorf("unable to get TxType from receiptRLP (%v)", err)
}
for _, pFunc := range requiredPackFuncs {
if err := pFunc(rct, node); err != nil {
return 0, fmt.Errorf("invalid DAG-ETH Receipt form (%v)", err)
}
}
return txType, nil
}
// the consensus struct for a receipt is not an exported type from go-ethereum
// so until types.Receipt has a MarshalBinary method we will pack and RLP encode a custom struct
type receiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom types.Bloom
Logs []*types.Log
}
var requiredPackFuncs = []func(*receiptRLP, ipld.Node) error{
packPostStateOrStatus,
packCumulativeGasUsed,
packBloom,
packLogs,
}
func packPostStateOrStatus(rct *receiptRLP, node ipld.Node) error {
psNode, err := node.LookupByString("PostState")
if err != nil {
return fmt.Errorf("receipt is missing a PostState node: %v", err)
}
if !psNode.IsNull() {
psBytes, err := psNode.AsBytes()
if err != nil {
return fmt.Errorf("receipt PostState should be of type Bytes")
}
rct.PostStateOrStatus = psBytes
return nil
}
sNode, err := node.LookupByString("Status")
if err != nil {
return fmt.Errorf("receipt is missing a Status node: %v", err)
}
if sNode.IsNull() {
return fmt.Errorf("receipt Node must have either PostState or Status")
}
sBytes, err := sNode.AsBytes()
if err != nil {
return fmt.Errorf("receipt Status should be of type Bytes")
}
switch {
case bytes.Equal(sBytes, receiptStatusFailed):
rct.PostStateOrStatus = receiptStatusFailedRLP
case bytes.Equal(sBytes, receiptStatusSuccessful):
rct.PostStateOrStatus = receiptStatusSuccessfulRLP
default:
return fmt.Errorf("unrecognized tx type")
}
return nil
}
func packCumulativeGasUsed(rct *receiptRLP, node ipld.Node) error {
cguNode, err := node.LookupByString("CumulativeGasUsed")
if err != nil {
return fmt.Errorf("receipt is missing a CumulativeGasUsed node: %v", err)
}
cguBytes, err := cguNode.AsBytes()
if err != nil {
return err
}
rct.CumulativeGasUsed = binary.BigEndian.Uint64(cguBytes)
return nil
}
func packBloom(rct *receiptRLP, node ipld.Node) error {
bloomNode, err := node.LookupByString("Bloom")
if err != nil {
return fmt.Errorf("receipt is missing a Bloom node: %v", err)
}
bloomBytes, err := bloomNode.AsBytes()
if err != nil {
return err
}
rct.Bloom = types.BytesToBloom(bloomBytes)
return nil
}
func packLogs(rct *receiptRLP, node ipld.Node) error {
logsNode, err := node.LookupByString("Logs")
if err != nil {
return fmt.Errorf("receipt is missing a Logs node: %v", err)
}
logs := make([]*types.Log, logsNode.Length())
logsIt := logsNode.ListIterator()
for !logsIt.Done() {
logIndex, logNode, err := logsIt.Next()
if err != nil {
return err
}
addrNode, err := logNode.LookupByString("Address")
if err != nil {
return fmt.Errorf("receipt log is missing an Address node: %v", err)
}
addrBytes, err := addrNode.AsBytes()
if err != nil {
return err
}
topicsNode, err := logNode.LookupByString("Topics")
if err != nil {
return fmt.Errorf("receipt log is missing a Topics node: %v", err)
}
topics := make([]common.Hash, topicsNode.Length())
topicsIt := topicsNode.ListIterator()
for !topicsIt.Done() {
topicIndex, topicNode, err := topicsIt.Next()
if err != nil {
return err
}
topicBytes, err := topicNode.AsBytes()
if err != nil {
return err
}
topics[topicIndex] = common.BytesToHash(topicBytes)
}
dataNode, err := logNode.LookupByString("Data")
if err != nil {
return fmt.Errorf("receipt log is missing a Data node: %v", err)
}
data, err := dataNode.AsBytes()
if err != nil {
return err
}
logs[logIndex] = &types.Log{
Address: common.BytesToAddress(addrBytes),
Topics: topics,
Data: data,
}
}
rct.Logs = logs
return nil
}