A short definition
Here’s what Solana’s terminology page says:
Program derived account
An account whose owner is a program and thus is not controlled by a private key like other accounts.
Usually, account addresses are usually the public key of a keypair. A program derived address is NOT a public key, and thus has no associated private key. Note that every account has an owner that’s a program, so that part of the definition is a bit redundant.
Here’s another good definition:
A Program Derived Address (PDA) is home to an account that’s designed to be controlled by a specific program. With PDAs, programs can programatically sign for certain addresses without needing a private key. At the same time, PDAs ensure that no external user could also generate a valid signature for the same address. These addresses serve as the foundation for Cross-Program Invocation, which allows Solana apps to be compossible with one another.
How is a PDA generated?
Again, let’s consult the docs:
Program addresses are deterministically derived from a collection of seeds and a program id using a 256-bit pre-image resistant hash function. Program address must not lie on the ed25519 curve to ensure there is no associated private key. During generation an error will be returned if the address is found to lie on the curve. There is about a 50/50 chance of this happening for a given collection of seeds and program id. If this occurs a different set of seeds or a seed bump (additional 8 bit seed) can be used to find a valid program address off the curve.
Basically, you just keep running the function findProgramDerivedAddress(programId, seeds, seedBump)
until you get a string that does not lie on the ed25519 curve (which means it doesn’t have an associated private key).
Since there is no associated private key, an external user cannot generate a valid signature for the PDA.
What’s an example use case?
This tutorial walks through a use case in more detail. I’ll simplify it here.
Let’s say you have a Solana program that lets users increment a counter. That counter needs to be stored in some account. The naive approach is to generate a keypair, create an account with the public key, and store the counter in that account (e.g. example1, example2). The main problem with this approach is that you need to store the public key somewhere. In this tutorial, it’s stored on a centralized server.
A better approach is to use a program derived address. Then, when some client wants to read/write the account that holds that counter’s state, they just have to do something like this:
await anchor.web3.PublicKey.findProgramAddress(
[Buffer.from("some_seed_string")],
program.programId
);
This way, you don’t need to store a public key—instead, you can easily derive an address.