github.com/algorand/go-algorand-sdk@v1.24.0/crypto/account.go (about) 1 package crypto 2 3 import ( 4 "crypto/sha512" 5 "errors" 6 "fmt" 7 8 "golang.org/x/crypto/ed25519" 9 10 "github.com/algorand/go-algorand-sdk/types" 11 ) 12 13 // prefix for multisig transaction signing 14 const msigAddrPrefix = "MultisigAddr" 15 16 // Account holds both the public and private information associated with an 17 // Algorand address 18 type Account struct { 19 PublicKey ed25519.PublicKey 20 PrivateKey ed25519.PrivateKey 21 Address types.Address 22 } 23 24 func init() { 25 addrLen := len(types.Address{}) 26 pkLen := ed25519.PublicKeySize 27 if addrLen != pkLen { 28 panic("address and public key are different sizes") 29 } 30 } 31 32 // GenerateAccount generates a random Account 33 func GenerateAccount() (kp Account) { 34 // Generate an ed25519 keypair. This should never fail 35 pk, sk, err := ed25519.GenerateKey(nil) 36 if err != nil { 37 panic(err) 38 } 39 40 // Convert the public key to an address 41 var a types.Address 42 n := copy(a[:], pk) 43 if n != ed25519.PublicKeySize { 44 panic("generated public key is the wrong size") 45 } 46 47 // Build the account 48 kp.PublicKey = pk 49 kp.PrivateKey = sk 50 kp.Address = a 51 return 52 } 53 54 // AccountFromPrivateKey derives the remaining Account fields from only a 55 // private key. The argument sk must have a length equal to 56 // ed25519.PrivateKeySize. 57 func AccountFromPrivateKey(sk ed25519.PrivateKey) (account Account, err error) { 58 if len(sk) != ed25519.PrivateKeySize { 59 err = errInvalidPrivateKey 60 return 61 } 62 63 // copy sk 64 account.PrivateKey = make(ed25519.PrivateKey, len(sk)) 65 copy(account.PrivateKey, sk) 66 67 account.PublicKey = sk.Public().(ed25519.PublicKey) 68 if len(account.PublicKey) != ed25519.PublicKeySize { 69 err = errors.New("generated public key is the wrong size") 70 return 71 } 72 73 copy(account.Address[:], account.PublicKey) 74 75 return 76 } 77 78 /* Multisig Support */ 79 80 // MultisigAccount is a convenience type for holding multisig preimage data 81 type MultisigAccount struct { 82 // Version is the version of this multisig 83 Version uint8 84 // Threshold is how many signatures are needed to fully sign as this address 85 Threshold uint8 86 // Pks is an ordered list of public keys that could potentially sign a message 87 Pks []ed25519.PublicKey 88 } 89 90 // MultisigAccountWithParams creates a MultisigAccount with the given parameters 91 func MultisigAccountWithParams(version uint8, threshold uint8, addrs []types.Address) (ma MultisigAccount, err error) { 92 ma.Version = version 93 ma.Threshold = threshold 94 ma.Pks = make([]ed25519.PublicKey, len(addrs)) 95 for i := 0; i < len(addrs); i++ { 96 ma.Pks[i] = addrs[i][:] 97 } 98 err = ma.Validate() 99 return 100 } 101 102 // MultisigAccountFromSig is a convenience method that creates an account 103 // from a sig in a signed tx. Useful for getting addresses from signed msig txs, etc. 104 func MultisigAccountFromSig(sig types.MultisigSig) (ma MultisigAccount, err error) { 105 ma.Version = sig.Version 106 ma.Threshold = sig.Threshold 107 ma.Pks = make([]ed25519.PublicKey, len(sig.Subsigs)) 108 for i := 0; i < len(sig.Subsigs); i++ { 109 c := make([]byte, len(sig.Subsigs[i].Key)) 110 copy(c, sig.Subsigs[i].Key) 111 ma.Pks[i] = c 112 } 113 err = ma.Validate() 114 return 115 } 116 117 // Address takes this multisig preimage data, and generates the corresponding identifying 118 // address, committing to the exact group, version, and public keys that it requires to sign. 119 // Hash("MultisigAddr" || version uint8 || threshold uint8 || PK1 || PK2 || ...) 120 func (ma MultisigAccount) Address() (addr types.Address, err error) { 121 // See go-algorand/crypto/multisig.go 122 err = ma.Validate() 123 if err != nil { 124 return 125 } 126 buffer := append([]byte(msigAddrPrefix), byte(ma.Version), byte(ma.Threshold)) 127 for _, pki := range ma.Pks { 128 buffer = append(buffer, pki[:]...) 129 } 130 return sha512.Sum512_256(buffer), nil 131 } 132 133 // Validate ensures that this multisig setup is a valid multisig account 134 func (ma MultisigAccount) Validate() (err error) { 135 if ma.Version != 1 { 136 err = errMsigUnknownVersion 137 return 138 } 139 if ma.Threshold == 0 || len(ma.Pks) == 0 || int(ma.Threshold) > len(ma.Pks) { 140 err = errMsigInvalidThreshold 141 return 142 } 143 return 144 } 145 146 // Blank return true if MultisigAccount is empty 147 // struct containing []ed25519.PublicKey cannot be compared 148 func (ma MultisigAccount) Blank() bool { 149 if ma.Version != 0 { 150 return false 151 } 152 if ma.Threshold != 0 { 153 return false 154 } 155 if ma.Pks != nil { 156 return false 157 } 158 return true 159 } 160 161 /* LogicSig support */ 162 163 // LogicSigAddress returns the contract (escrow) address for a LogicSig. 164 // 165 // NOTE: If the LogicSig is delegated to another account this will not 166 // return the delegated address of the LogicSig. 167 func LogicSigAddress(lsig types.LogicSig) types.Address { 168 toBeSigned := programToSign(lsig.Logic) 169 checksum := sha512.Sum512_256(toBeSigned) 170 171 var addr types.Address 172 n := copy(addr[:], checksum[:]) 173 if n != ed25519.PublicKeySize { 174 panic(fmt.Sprintf("Generated public key has length of %d, expected %d", n, ed25519.PublicKeySize)) 175 } 176 return addr 177 } 178 179 // LogicSigAccount represents an account that can sign with a LogicSig program. 180 type LogicSigAccount struct { 181 _struct struct{} `codec:",omitempty,omitemptyarray"` 182 183 // The underlying LogicSig object 184 Lsig types.LogicSig `codec:"lsig"` 185 186 // The key that provided Lsig.Sig, if any 187 SigningKey ed25519.PublicKey `codec:"sigkey"` 188 } 189 190 // MakeLogicSigAccountEscrow creates a new escrow LogicSigAccount. The address 191 // of this account will be a hash of its program. 192 // Deprecated: This method is deprecated for not applying basic sanity check over program bytes, 193 // use `MakeLogicSigAccountEscrowChecked` instead. 194 func MakeLogicSigAccountEscrow(program []byte, args [][]byte) LogicSigAccount { 195 return LogicSigAccount{ 196 Lsig: types.LogicSig{ 197 Logic: program, 198 Args: args, 199 }, 200 } 201 } 202 203 // MakeLogicSigAccountEscrowChecked creates a new escrow LogicSigAccount. 204 // The address of this account will be a hash of its program. 205 func MakeLogicSigAccountEscrowChecked(program []byte, args [][]byte) (LogicSigAccount, error) { 206 lsig, err := MakeLogicSig(program, args, nil, MultisigAccount{}) 207 if err != nil { 208 return LogicSigAccount{}, err 209 } 210 return LogicSigAccount{Lsig: lsig}, nil 211 } 212 213 // MakeLogicSigAccountDelegated creates a new delegated LogicSigAccount. This 214 // type of LogicSig has the authority to sign transactions on behalf of another 215 // account, called the delegating account. If the delegating account is a 216 // multisig account, use MakeLogicSigAccountDelegated instead. 217 // 218 // The parameter signer is the private key of the delegating account. 219 func MakeLogicSigAccountDelegated(program []byte, args [][]byte, signer ed25519.PrivateKey) (lsa LogicSigAccount, err error) { 220 var ma MultisigAccount 221 lsig, err := MakeLogicSig(program, args, signer, ma) 222 if err != nil { 223 return 224 } 225 226 signerAccount, err := AccountFromPrivateKey(signer) 227 if err != nil { 228 return 229 } 230 231 lsa = LogicSigAccount{ 232 Lsig: lsig, 233 // attach SigningKey to remember which account the signature belongs to 234 SigningKey: signerAccount.PublicKey, 235 } 236 return 237 } 238 239 // MakeLogicSigAccountDelegatedMsig creates a new delegated LogicSigAccount. 240 // This type of LogicSig has the authority to sign transactions on behalf of 241 // another account, called the delegating account. Use this function if the 242 // delegating account is a multisig account, otherwise use 243 // MakeLogicSigAccountDelegated. 244 // 245 // The parameter msigAccount is the delegating multisig account. 246 // 247 // The parameter signer is the private key of one of the members of the 248 // delegating multisig account. Use the method AppendMultisigSignature on the 249 // returned LogicSigAccount to add additional signatures from other members. 250 func MakeLogicSigAccountDelegatedMsig(program []byte, args [][]byte, msigAccount MultisigAccount, signer ed25519.PrivateKey) (lsa LogicSigAccount, err error) { 251 lsig, err := MakeLogicSig(program, args, signer, msigAccount) 252 if err != nil { 253 return 254 } 255 lsa = LogicSigAccount{ 256 Lsig: lsig, 257 // do not attach SigningKey, since that doesn't apply to an msig signature 258 } 259 return 260 } 261 262 // AppendMultisigSignature adds an additional signature from a member of the 263 // delegating multisig account. 264 // 265 // The LogicSigAccount must represent a delegated LogicSig backed by a multisig 266 // account. 267 func (lsa *LogicSigAccount) AppendMultisigSignature(signer ed25519.PrivateKey) error { 268 return AppendMultisigToLogicSig(&lsa.Lsig, signer) 269 } 270 271 // LogicSigAccountFromLogicSig creates a LogicSigAccount from an existing 272 // LogicSig object. 273 // 274 // The parameter signerPublicKey must be present if the LogicSig is delegated 275 // and the delegating account is backed by a single private key (i.e. not a 276 // multisig account). In this case, signerPublicKey must be the public key of 277 // the delegating account. In all other cases, an error will be returned if 278 // signerPublicKey is present. 279 func LogicSigAccountFromLogicSig(lsig types.LogicSig, signerPublicKey *ed25519.PublicKey) (lsa LogicSigAccount, err error) { 280 hasSig := lsig.Sig != (types.Signature{}) 281 hasMsig := !lsig.Msig.Blank() 282 283 if hasSig && hasMsig { 284 err = errLsigTooManySignatures 285 return 286 } 287 288 if hasSig { 289 if signerPublicKey == nil { 290 err = errLsigNoPublicKey 291 return 292 } 293 294 toBeSigned := programToSign(lsig.Logic) 295 valid := ed25519.Verify(*signerPublicKey, toBeSigned, lsig.Sig[:]) 296 if !valid { 297 err = errLsigInvalidPublicKey 298 return 299 } 300 301 lsa.Lsig = lsig 302 lsa.SigningKey = make(ed25519.PublicKey, len(*signerPublicKey)) 303 copy(lsa.SigningKey, *signerPublicKey) 304 return 305 } 306 307 if signerPublicKey != nil { 308 err = errLsigAccountPublicKeyNotNeeded 309 return 310 } 311 312 lsa.Lsig = lsig 313 return 314 } 315 316 // IsDelegated returns true if and only if the LogicSig has been delegated to 317 // another account with a signature. 318 // 319 // Note this function only checks for the presence of a delegation signature. To 320 // verify the delegation signature, use VerifyLogicSig. 321 func (lsa LogicSigAccount) IsDelegated() bool { 322 hasSig := lsa.Lsig.Sig != (types.Signature{}) 323 hasMsig := !lsa.Lsig.Msig.Blank() 324 return hasSig || hasMsig 325 } 326 327 // Address returns the address of this LogicSigAccount. 328 // 329 // If the LogicSig is delegated to another account, this will return the address 330 // of that account. 331 // 332 // If the LogicSig is not delegated to another account, this will return an 333 // escrow address that is the hash of the LogicSig's program code. 334 func (lsa LogicSigAccount) Address() (addr types.Address, err error) { 335 hasSig := lsa.Lsig.Sig != (types.Signature{}) 336 hasMsig := !lsa.Lsig.Msig.Blank() 337 338 // require at most one sig 339 if hasSig && hasMsig { 340 err = errLsigTooManySignatures 341 return 342 } 343 344 if hasSig { 345 n := copy(addr[:], lsa.SigningKey) 346 if n != ed25519.PublicKeySize { 347 err = fmt.Errorf("Generated public key has length of %d, expected %d", n, ed25519.PublicKeySize) 348 } 349 return 350 } 351 352 if hasMsig { 353 var msigAccount MultisigAccount 354 msigAccount, err = MultisigAccountFromSig(lsa.Lsig.Msig) 355 if err != nil { 356 return 357 } 358 addr, err = msigAccount.Address() 359 return 360 } 361 362 addr = LogicSigAddress(lsa.Lsig) 363 return 364 }