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  }