-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathoperation.go
134 lines (121 loc) · 3.69 KB
/
operation.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
package tezosprotocol
import (
"bytes"
"encoding"
"fmt"
"golang.org/x/crypto/blake2b"
"golang.org/x/xerrors"
)
// Field lengths
const (
// BlockHashLen is the length in bytes of a serialized block hash
BlockHashLen = 32
// OperationHashLen is the length in bytes of a serialized operation hash
OperationHashLen = 32
// OperationSignatureLen is the length in bytes of a serialized operation signature
OperationSignatureLen = 64
)
// OperationContents models one of multiple contents of a tezos operation.
// Reference: http://tezos.gitlab.io/mainnet/api/p2p.html#operation-alpha-contents-determined-from-data-8-bit-tag
type OperationContents interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
fmt.Stringer
GetTag() ContentsTag
}
// Operation models a tezos operation with variable length contents.
type Operation struct {
Branch BranchID
Contents []OperationContents
}
func (o *Operation) String() string {
return fmt.Sprintf("Branch: %s, Contents: %s", o.Branch, o.Contents)
}
// MarshalBinary implements encoding.BinaryMarshaler. It encodes the operation
// unsigned, in the format suitable for signing and transmission.
func (o *Operation) MarshalBinary() ([]byte, error) {
buf := bytes.Buffer{}
branchIDBytes, err := o.Branch.MarshalBinary()
if err != nil {
return nil, xerrors.Errorf("failed to write branch: %w", err)
}
buf.Write(branchIDBytes)
if len(o.Contents) == 0 {
return nil, xerrors.New("expected non-zero list of contents in an operation")
}
for _, content := range o.Contents {
contentBytes, err := content.MarshalBinary()
if err != nil {
return nil, xerrors.Errorf("failed to marshal operation contents: %#v: %w", content, err)
}
buf.Write(contentBytes)
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Operation) UnmarshalBinary(data []byte) (err error) {
// cleanly recover from out of bounds exceptions
defer func() {
if err == nil {
if r := recover(); r != nil {
err = catchOutOfRangeExceptions(r)
}
}
}()
*o = Operation{}
dataPtr := data
err = o.Branch.UnmarshalBinary(dataPtr[:BlockHashLen])
if err != nil {
return err
}
dataPtr = dataPtr[BlockHashLen:]
for len(dataPtr) > 0 {
tag := ContentsTag(dataPtr[0])
var content OperationContents
switch tag {
case ContentsTagRevelation:
content = &Revelation{}
err = content.UnmarshalBinary(dataPtr)
if err != nil {
return xerrors.Errorf("failed to unmarshal revelation: %w", err)
}
case ContentsTagTransaction:
content = &Transaction{}
err = content.UnmarshalBinary(dataPtr)
if err != nil {
return xerrors.Errorf("failed to unmarshal transaction: %w", err)
}
case ContentsTagOrigination:
content = &Origination{}
err = content.UnmarshalBinary(dataPtr)
if err != nil {
return xerrors.Errorf("failed to unmarshal origination: %w", err)
}
case ContentsTagDelegation:
content = &Delegation{}
err = content.UnmarshalBinary(dataPtr)
if err != nil {
return xerrors.Errorf("failed to unmarshal delegation: %w", err)
}
default:
return xerrors.Errorf("unexpected content tag %d", tag)
}
o.Contents = append(o.Contents, content)
marshaled, err := content.MarshalBinary()
if err != nil {
return err
}
dataPtr = dataPtr[len(marshaled):]
}
return nil
}
// SignatureHash returns the hash of the operation to be signed, including watermark
func (o *Operation) SignatureHash() ([]byte, error) {
operationBytes, err := o.MarshalBinary()
if err != nil {
return nil, xerrors.Errorf("failed to marshal operation: %s: %w", o, err)
}
bytesWithWatermark := append([]byte{byte(OperationWatermark)}, operationBytes...)
sigHash := blake2b.Sum256(bytesWithWatermark)
return sigHash[:], nil
}