github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/main/shared/voucher.go (about) 1 package shared 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "strings" 9 10 "github.com/ethereum/go-ethereum/rlp" 11 ) 12 13 /////////////////// 14 // Voucher Structs 15 /////////////////// 16 17 type ProposalKey struct { 18 Address string `json:"address"` 19 KeyId uint `json:"keyId"` 20 SequenceNum uint `json:"sequenceNum"` 21 } 22 type PayloadSig struct { 23 Address string `json:"address"` 24 KeyId uint `json:"keyId"` 25 Sig string `json:"sig"` 26 } 27 type Voucher struct { 28 Cadence string `json:"cadence"` 29 RefBlock string `json:"refBlock"` 30 ComputeLimit uint `json:"computeLimit"` 31 Arguments []map[string]string `json:"arguments"` 32 Payer string `json:"payer"` 33 Authorizers []string `json:"authorizers"` 34 ProposalKey ProposalKey `json:"proposalKey"` 35 PayloadSigs []PayloadSig `json:"payloadSigs"` 36 EnvelopeSigs []PayloadSig `json:"envelopeSigs"` 37 } 38 39 func rightPaddedBuffer(s string, numBytes uint) string { 40 format := "%-" + fmt.Sprintf("%d", numBytes*2) + "s" 41 _rightPaddedStr := fmt.Sprintf(format, s) 42 rightPaddedStr := strings.Replace(_rightPaddedStr, " ", "0", int(numBytes*2)) 43 return rightPaddedStr 44 } 45 func leftPaddedBuffer(s string, numBytes uint) []byte { 46 format := "%0" + fmt.Sprintf("%d", numBytes*2) + "s" 47 leftPaddedStr := fmt.Sprintf(format, s) 48 data, _ := hex.DecodeString(leftPaddedStr) 49 return data 50 } 51 func blockBuffer(s string) []byte { 52 return leftPaddedBuffer(s, 32) 53 } 54 func addressBuffer(s string) []byte { 55 return leftPaddedBuffer(s, 8) 56 } 57 func sansPrefix(addr string) string { 58 return strings.TrimPrefix(addr, "0x") 59 } 60 func rlpEncode(p interface{}) string { 61 b := new(bytes.Buffer) 62 _ = rlp.Encode(b, p) 63 return hex.EncodeToString(b.Bytes()) 64 } 65 66 func EncodeMessageFromVoucher(v *Voucher) string { 67 var toEncode []interface{} 68 69 // CADENCE 70 toEncode = append(toEncode, v.Cadence) 71 72 // ARGUMENTS 73 // Stringify tx args 74 args := make([]string, len(v.Arguments)) 75 for i, arg := range v.Arguments { 76 jsonStrArg, _ := json.Marshal(arg) 77 args[i] = string(jsonStrArg) 78 } 79 toEncode = append(toEncode, args) 80 81 // REF BLOCK 82 toEncode = append(toEncode, blockBuffer(v.RefBlock)) 83 84 // COMPUTE LIMIT 85 toEncode = append(toEncode, v.ComputeLimit) 86 87 // PROPOSAL KEY ADDRESS 88 proposalAddrData := addressBuffer(sansPrefix(v.ProposalKey.Address)) 89 toEncode = append(toEncode, proposalAddrData) 90 91 // PROPOSAL KEY ID 92 toEncode = append(toEncode, v.ProposalKey.KeyId) 93 94 // PROPOSAL KEY SEQ NUM 95 toEncode = append(toEncode, v.ProposalKey.SequenceNum) 96 97 // PAYER 98 payerData := leftPaddedBuffer(sansPrefix(v.Payer), 8) 99 toEncode = append(toEncode, payerData) // 8 bytes left padded w/ 0s 100 101 // AUTHORIZERS 102 authorizers := make([][]byte, len(v.Authorizers)) 103 // pad authorizer addresses 104 for i, addr := range v.Authorizers { 105 authorizers[i] = addressBuffer(sansPrefix(addr)) 106 } 107 toEncode = append(toEncode, authorizers) 108 109 // We only want to generate the message signed by the user. 110 // 111 // If wallet is custodial, then the user will sign the tx payload 112 // and the custodian will sign the envelope. In this case, only generate 113 // the encoded tx payload, i.e. the message signed by the user. 114 // 115 // If wallet is non-custodial, the user only needs to sign the envelope, so 116 // payloadSigs will be empty. In this case, generate the encoded tx envelope, 117 // i.e. the message signed by the user 118 if len(v.PayloadSigs) > 0 { 119 // Go straight to envelope payload if there are no payload sigs 120 return rlpEncode(toEncode) 121 122 } else { 123 // Encode Transaction Envelope: 124 // If payload sigs are present, only encode the payload message/sig, not the envelope 125 // - create new array to rlpEncode using transaction Payload and payload sigs 126 var envelopePayloadToEncode []interface{} 127 envelopePayloadToEncode = append(envelopePayloadToEncode, toEncode) 128 envelopePayloadToEncode = append(envelopePayloadToEncode, v.PayloadSigs) 129 envelopePayload := rlpEncode(envelopePayloadToEncode) 130 return envelopePayload 131 } 132 } 133 134 // Non-custodial wallets will only contain one signer, so payloadSigs 135 // will be empty, and only the envelope will be signed. 136 // For Custodial wallets (Blocto/Dapper), the user signs the tx payload 137 // and envelope is signed by custodian. 138 // 139 // This function returns only the signature generated by the user 140 func GetUserCompositeSignatureFromVoucher(v *Voucher) *[]CompositeSignature { 141 var compositeSig CompositeSignature 142 if len(v.PayloadSigs) > 0 { 143 compositeSig = CompositeSignature{ 144 Addr: v.PayloadSigs[0].Address, 145 Key_id: v.PayloadSigs[0].KeyId, 146 Signature: v.PayloadSigs[0].Sig, 147 } 148 } else { 149 compositeSig = CompositeSignature{ 150 Addr: v.EnvelopeSigs[0].Address, 151 Key_id: v.EnvelopeSigs[0].KeyId, 152 Signature: v.EnvelopeSigs[0].Sig, 153 } 154 } 155 156 return &[]CompositeSignature{compositeSig} 157 }