forked from NethermindEth/starknet.go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
typed.go
172 lines (150 loc) · 4.55 KB
/
typed.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
package caigo
import (
"bytes"
"fmt"
"math/big"
"github.com/dontpanicdao/caigo/types"
)
type TypedData struct {
Types map[string]TypeDef
PrimaryType string
Domain Domain
Message TypedMessage
}
type Domain struct {
Name string
Version string
ChainId string
}
type TypeDef struct {
Encoding *big.Int
Definitions []Definition
}
type Definition struct {
Name string
Type string
}
type TypedMessage interface {
FmtDefinitionEncoding(string) []*big.Int
}
/*
encoding definition for standard StarkNet Domain messages
*/
func (dm Domain) FmtDefinitionEncoding(field string) (fmtEnc []*big.Int) {
switch field {
case "name":
fmtEnc = append(fmtEnc, types.StrToFelt(dm.Name).Big())
case "version":
fmtEnc = append(fmtEnc, types.StrToFelt(dm.Version).Big())
case "chainId":
fmtEnc = append(fmtEnc, types.StrToFelt(dm.ChainId).Big())
}
return fmtEnc
}
/*
'typedData' interface for interacting and signing typed data in accordance with https://github.com/0xs34n/starknet.js/tree/develop/src/utils/typedData
*/
func NewTypedData(types map[string]TypeDef, pType string, dom Domain) (td TypedData, err error) {
td = TypedData{
Types: types,
PrimaryType: pType,
Domain: dom,
}
if _, ok := td.Types[pType]; !ok {
return td, fmt.Errorf("invalid primary type: %s", pType)
}
for k, v := range td.Types {
enc, err := td.GetTypeHash(k)
if err != nil {
return td, fmt.Errorf("error encoding type hash: %s %w", enc.String(), err)
}
v.Encoding = enc
td.Types[k] = v
}
return td, nil
}
// (ref: https://github.com/0xs34n/starknet.js/blob/767021a203ac0b9cdb282eb6d63b33bfd7614858/src/utils/typedData/index.ts#L166)
func (td TypedData) GetMessageHash(account *big.Int, msg TypedMessage, sc StarkCurve) (hash *big.Int, err error) {
elements := []*big.Int{types.UTF8StrToBig("StarkNet Message")}
domEnc, err := td.GetTypedMessageHash("StarkNetDomain", td.Domain, sc)
if err != nil {
return hash, fmt.Errorf("could not hash domain: %w", err)
}
elements = append(elements, domEnc)
elements = append(elements, account)
msgEnc, err := td.GetTypedMessageHash(td.PrimaryType, msg, sc)
if err != nil {
return hash, fmt.Errorf("could not hash message: %w", err)
}
elements = append(elements, msgEnc)
hash, err = sc.ComputeHashOnElements(elements)
return hash, err
}
func (td TypedData) GetTypedMessageHash(inType string, msg TypedMessage, sc StarkCurve) (hash *big.Int, err error) {
prim := td.Types[inType]
elements := []*big.Int{prim.Encoding}
for _, def := range prim.Definitions {
if def.Type == "felt" {
fmtDefinitions := msg.FmtDefinitionEncoding(def.Name)
elements = append(elements, fmtDefinitions...)
continue
}
innerElements := []*big.Int{}
encType := td.Types[def.Type]
innerElements = append(innerElements, encType.Encoding)
fmtDefinitions := msg.FmtDefinitionEncoding(def.Name)
innerElements = append(innerElements, fmtDefinitions...)
innerElements = append(innerElements, big.NewInt(int64(len(innerElements))))
innerHash, err := sc.HashElements(innerElements)
if err != nil {
return hash, fmt.Errorf("error hashing internal elements: %v %w", innerElements, err)
}
elements = append(elements, innerHash)
}
hash, err = sc.ComputeHashOnElements(elements)
return hash, err
}
func (td TypedData) GetTypeHash(inType string) (ret *big.Int, err error) {
enc, err := td.EncodeType(inType)
if err != nil {
return ret, err
}
sel := types.GetSelectorFromName(enc)
return sel, nil
}
func (td TypedData) EncodeType(inType string) (enc string, err error) {
var typeDefs TypeDef
var ok bool
if typeDefs, ok = td.Types[inType]; !ok {
return enc, fmt.Errorf("can't parse type %s from types %v", inType, td.Types)
}
var buf bytes.Buffer
customTypes := make(map[string]TypeDef)
buf.WriteString(inType)
buf.WriteString("(")
for i, def := range typeDefs.Definitions {
if def.Type != "felt" {
var customTypeDef TypeDef
if customTypeDef, ok = td.Types[def.Type]; !ok {
return enc, fmt.Errorf("can't parse type %s from types %v", def.Type, td.Types)
}
customTypes[def.Type] = customTypeDef
}
buf.WriteString(fmt.Sprintf("%s:%s", def.Name, def.Type))
if i != (len(typeDefs.Definitions) - 1) {
buf.WriteString(",")
}
}
buf.WriteString(")")
for customTypeName, customType := range customTypes {
buf.WriteString(fmt.Sprintf("%s(", customTypeName))
for i, def := range customType.Definitions {
buf.WriteString(fmt.Sprintf("%s:%s", def.Name, def.Type))
if i != (len(customType.Definitions) - 1) {
buf.WriteString(",")
}
}
buf.WriteString(")")
}
return buf.String(), nil
}