-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Add SystemInstruction::CreateAccountWithSeed #7390
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe something to consider in the future - Transactions could encode a seed vector as well, then we could verify signatures for seed addresses.
return Err(InstructionError::MissingRequiredSignature); | ||
} | ||
|
||
// re-derive the address, must match to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// re-derive the address, must match to | |
// re-derive the address, must match `to` |
space: u64, | ||
program_id: &Pubkey, | ||
) -> Result<(), InstructionError> { | ||
// from is the source of the derived address, the caller must have |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// from is the source of the derived address, the caller must have | |
// `from` is the source of the derived address, the caller must have |
/// * lamports - number of lamports to transfer to the new account | ||
/// * space - memory to allocate if greater then zero | ||
/// * program_id - the program id of the new account | ||
CreateAccountWithSeed { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to adjust solana-web3.js once this lands, do you want a hand with that @rob-solana?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lemme take a crack first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I failed: solana-labs/solana-web3.js#571
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just the one design Q
fn create_account_with_seed( | ||
from: &mut KeyedAccount, | ||
to: &mut KeyedAccount, | ||
seed: &str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for the restriction to 32B ASCII string? [u8; 32]
could support some internationalization and hash digests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah good point, we don't care that the seed is valid utf-8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to keep the space smaller than 256 bytes of seed to deter grinding attacks. 32 ascii chars takes away 32 bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rob-solana grinding on what though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trying to DoS a guy you think wants his authorized staker+"shrill conveyance 23/50"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rob-solana you can always grind with pubkey generation. I think we should either have a Hash/[u8;32], or u64.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requiring from
's signature effectively makes griefing the address space a second-preimage attack on SHA256. We should probably be OK with that. Though fewer bits do minimize opportunities for shenanigans. Would it make sense to restrict the seed to something as small as a u16
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
were it as small as a u16, mnemonics get harder. ascii is nice for mnemonics (in English). opening to utf-8 would make it look less amateur-hour.
The grinding attack is picking another source from
(obs having the keypair), and griefing some other dude's from
. The goal is to make that griefing look hard or futile.
I think we can do this with:
- u64, maybe low 8 bytes of hash(mnemonic string)?
- hashing a limited-length string as proposed. Upgrading to utf-8 hurts the deterrent a little as you can cover 256 bits of space with only 13-ish utf-8 chars (at 20bits/char).
We can also say "who cares? grind away!"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm planning to remove the ascii stuff, appreciate input here @t-nelson @aeyakovenko @garious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
were it as small as a u16, mnemonics get harder. ascii is nice for mnemonics (in English). opening to utf-8 would make it look less amateur-hour.
Right, the u16
suggestion would discard the mnemonic for an index (from
gets I
accounts under program_id
). Which gives cheap introspection, but no pseudonymity.
The grinding attack is picking another source
from
(obs having the keypair), and griefing some other dude'sfrom
. The goal is to make that griefing look hard or futile.
Yep. A SHA256 second preimage search is pretty futile IMO. Restricting free bits per from
is about the most effective deterrent we can enact. Grinding from
s is always possible and slower than the hashing step. You've already covered the other speedup. That is, requiring fixed bits at the end of the input data, preventing working from a midstate.
I think we can do this with:
1. u64, maybe low 8 bytes of hash(mnemonic string)? 2. hashing a limited-length string as proposed. Upgrading to utf-8 hurts the deterrent a little as you can cover 256 bits of space with only 13-ish utf-8 chars (at 20bits/char).
I think (valid) utf-8 would actually be more restricted. For non-ASCII code points, the top two bits of every byte are fixed and the first byte also consumes up to three more bits to encode the length. Not all code pages are in use or full either, further restricting valid bitstrings
We can also say "who cares? grind away!"
Probably fine. See, https://lbc.cryptoguru.org/about
The implications here might be more far-reaching than intended. Without derived addresses, we can assert all account addresses are ed25519 pubkeys. And with that invariant we can say every account creator at one time possessed its private key. Is that a valuable invariant? Hard to say. BIP32 would preserve it, but not exactly an option for ed25519. |
does Pubkey::new_rand() (a perfectly acceptable |
I've always been quietly uncomfortable with |
@garious there is no way to prevent a transfer to a random address. Destinations don’t require a proof. |
There may be a way to enforce the address is a valid pubkey. We should consider locking it down and replacing new_rand() with Keypair::new().pubkey() |
I'm not saying we should go one way or another on this, but it's generally not a great idea to define a data type as the bits of either one data type or another, depending on some usage (semantic coupling). Effectively, that makes Pubkey a C-style union representing either a public key or a random string of bits. You might argue, no, it's just a random string of bits, but in fact we interpret it as a public key quite often (i.e. System::Transfer). And if random bits, why are we using Pubkey? I'd suggest sleeping on it. There's probably a more elegant solution here - maybe defining a new type AccountAddress as an enum instead of a Pubkey. |
Pull request has been modified.
@@ -50,11 +92,18 @@ fn create_system_account( | |||
return Err(SystemError::ResultWithNegativeLamports.into()); | |||
} | |||
|
|||
assign_account_to_program(to, program_id)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not calling assign_account_to_program
anymore for normal accounts starts to skip this existing check for account.signer_key. Is that OK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that check is already present in create_account()
and is not enforced by create_account_with_seed()
this change was intentional
note that no tests needed to change, we're still requiring to
signature in the non-seed case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks for clarifying!
Codecov Report
@@ Coverage Diff @@
## master #7390 +/- ##
=========================================
- Coverage 80.6% 68% -12.6%
=========================================
Files 245 245
Lines 48408 57525 +9117
=========================================
+ Hits 39025 39135 +110
- Misses 9383 18390 +9007 |
sleeping on it good. I wonder if we can force the resulting generated addresses to be unreachable pubkeys in ed25519 |
@rob-solana @garious we can set a bit in the Account to indicate that its not a real pubkey and cannot be signed with. that makes more sense than changing the address type. |
Perhaps the |
the target customer for this work found |
I think just recasting Pubkey::new_rand() to do that would work? |
@garious seed as an existing account address won’t prevent a easy to factor pubkey. I don’t see a problem of relaxing the account address = pubkey relationship. We have no way to force addresses to be strong public keys. |
In this discussion we should keep in mind that builtin |
I think our best path forward is a new 32 byte AccountAddress type with a From instance for Pubkey. |
Bonus points for a potential curve migration mechanism |
86915b6
to
e00a910
Compare
Pull request has been modified.
updated to use a 32-char utf-8 string, which is equivalently or possibly more restrictive in grinding from seed as ascii |
Pull request has been modified.
Problem
support for multiple accounts with the same authority exists, but no provision for
creating accounts with addresses derived from the authority exists. it'd be really nice to define a space of addresses reachable for create_account() that don't require generating an ephemeral keypair.
Summary of Changes
add a system instruction that allows signers to create and assign in a subset of the
pubkey address space as derived addresses
Fixes #