/
voucher.go
157 lines (138 loc) · 4.71 KB
/
voucher.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
package shared
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/rlp"
)
///////////////////
// Voucher Structs
///////////////////
type ProposalKey struct {
Address string `json:"address"`
KeyId uint `json:"keyId"`
SequenceNum uint `json:"sequenceNum"`
}
type PayloadSig struct {
Address string `json:"address"`
KeyId uint `json:"keyId"`
Sig string `json:"sig"`
}
type Voucher struct {
Cadence string `json:"cadence"`
RefBlock string `json:"refBlock"`
ComputeLimit uint `json:"computeLimit"`
Arguments []map[string]string `json:"arguments"`
Payer string `json:"payer"`
Authorizers []string `json:"authorizers"`
ProposalKey ProposalKey `json:"proposalKey"`
PayloadSigs []PayloadSig `json:"payloadSigs"`
EnvelopeSigs []PayloadSig `json:"envelopeSigs"`
}
func rightPaddedBuffer(s string, numBytes uint) string {
format := "%-" + fmt.Sprintf("%d", numBytes*2) + "s"
_rightPaddedStr := fmt.Sprintf(format, s)
rightPaddedStr := strings.Replace(_rightPaddedStr, " ", "0", int(numBytes*2))
return rightPaddedStr
}
func leftPaddedBuffer(s string, numBytes uint) []byte {
format := "%0" + fmt.Sprintf("%d", numBytes*2) + "s"
leftPaddedStr := fmt.Sprintf(format, s)
data, _ := hex.DecodeString(leftPaddedStr)
return data
}
func blockBuffer(s string) []byte {
return leftPaddedBuffer(s, 32)
}
func addressBuffer(s string) []byte {
return leftPaddedBuffer(s, 8)
}
func sansPrefix(addr string) string {
return strings.TrimPrefix(addr, "0x")
}
func rlpEncode(p interface{}) string {
b := new(bytes.Buffer)
_ = rlp.Encode(b, p)
return hex.EncodeToString(b.Bytes())
}
func EncodeMessageFromVoucher(v *Voucher) string {
var toEncode []interface{}
// CADENCE
toEncode = append(toEncode, v.Cadence)
// ARGUMENTS
// Stringify tx args
args := make([]string, len(v.Arguments))
for i, arg := range v.Arguments {
jsonStrArg, _ := json.Marshal(arg)
args[i] = string(jsonStrArg)
}
toEncode = append(toEncode, args)
// REF BLOCK
toEncode = append(toEncode, blockBuffer(v.RefBlock))
// COMPUTE LIMIT
toEncode = append(toEncode, v.ComputeLimit)
// PROPOSAL KEY ADDRESS
proposalAddrData := addressBuffer(sansPrefix(v.ProposalKey.Address))
toEncode = append(toEncode, proposalAddrData)
// PROPOSAL KEY ID
toEncode = append(toEncode, v.ProposalKey.KeyId)
// PROPOSAL KEY SEQ NUM
toEncode = append(toEncode, v.ProposalKey.SequenceNum)
// PAYER
payerData := leftPaddedBuffer(sansPrefix(v.Payer), 8)
toEncode = append(toEncode, payerData) // 8 bytes left padded w/ 0s
// AUTHORIZERS
authorizers := make([][]byte, len(v.Authorizers))
// pad authorizer addresses
for i, addr := range v.Authorizers {
authorizers[i] = addressBuffer(sansPrefix(addr))
}
toEncode = append(toEncode, authorizers)
// We only want to generate the message signed by the user.
//
// If wallet is custodial, then the user will sign the tx payload
// and the custodian will sign the envelope. In this case, only generate
// the encoded tx payload, i.e. the message signed by the user.
//
// If wallet is non-custodial, the user only needs to sign the envelope, so
// payloadSigs will be empty. In this case, generate the encoded tx envelope,
// i.e. the message signed by the user
if len(v.PayloadSigs) > 0 {
// Go straight to envelope payload if there are no payload sigs
return rlpEncode(toEncode)
} else {
// Encode Transaction Envelope:
// If payload sigs are present, only encode the payload message/sig, not the envelope
// - create new array to rlpEncode using transaction Payload and payload sigs
var envelopePayloadToEncode []interface{}
envelopePayloadToEncode = append(envelopePayloadToEncode, toEncode)
envelopePayloadToEncode = append(envelopePayloadToEncode, v.PayloadSigs)
envelopePayload := rlpEncode(envelopePayloadToEncode)
return envelopePayload
}
}
// Non-custodial wallets will only contain one signer, so payloadSigs
// will be empty, and only the envelope will be signed.
// For Custodial wallets (Blocto/Dapper), the user signs the tx payload
// and envelope is signed by custodian.
//
// This function returns only the signature generated by the user
func GetUserCompositeSignatureFromVoucher(v *Voucher) *[]CompositeSignature {
var compositeSig CompositeSignature
if len(v.PayloadSigs) > 0 {
compositeSig = CompositeSignature{
Addr: v.PayloadSigs[0].Address,
Key_id: v.PayloadSigs[0].KeyId,
Signature: v.PayloadSigs[0].Sig,
}
} else {
compositeSig = CompositeSignature{
Addr: v.EnvelopeSigs[0].Address,
Key_id: v.EnvelopeSigs[0].KeyId,
Signature: v.EnvelopeSigs[0].Sig,
}
}
return &[]CompositeSignature{compositeSig}
}