Conversation
| //! ```text | ||
| //! ┌──────────────────────┬──────────────────────────────┐ | ||
| //! │ account_table │ instructions │ | ||
| //! │ count:u8 + addresses │ count:u8 + CpiInstructions │ |
There was a problem hiding this comment.
rather than referencing the addresses directly, i was thinking that we just commit to their hash
| pub nonce: U32, | ||
| /// Unix timestamp after which the message expires. | ||
| /// Zero means the message does not expire. | ||
| pub deadline: I64, |
There was a problem hiding this comment.
the timelock feature could theoretically be a standalone cpi-passthrough program
if we want to keep it here, consider Option<NonZeroI64>
| #[derive(Clone, Debug, PartialEq, Eq, SchemaRead, SchemaWrite)] | ||
| pub struct SignatureEntry { | ||
| /// Index into [`AuthorityPolicy::members`](crate::state::AuthorityPolicy::members). | ||
| pub signer_index: u8, |
There was a problem hiding this comment.
any reason not to assert that the index is implied by signer position in the instruction's accounts list, like we do with transactions
| /// account table along with the signer and writable privileges the authority | ||
| /// approved for it. | ||
| #[derive(Clone, Debug, PartialEq, Eq, SchemaRead, SchemaWrite)] | ||
| pub struct AccountMeta { |
There was a problem hiding this comment.
any reason not to recover these from AccountInfo?
| pub is_signer: bool, | ||
| /// Whether the authority approved this account as writable for the CPI. | ||
| pub is_writable: bool, |
There was a problem hiding this comment.
if we decide to keep the struct, it may pay to serialize the vec as something like this to avoid the 14bit overhead
Encoded {
indexes: Vec<u8>,
roles: BitField,
}|
|
||
| #[inline(always)] | ||
| pub fn derive_address(program_id: &Address, authority_policy: &AuthorityPolicy) -> Address { | ||
| Self::derive_address_and_bump(program_id, authority_policy).0 |
There was a problem hiding this comment.
nit: tuple index notation is poison
| Self::derive_address_and_bump(program_id, authority_policy).0 | |
| let (address, _bump = | |
| Self::derive_address_and_bump(program_id, authority_policy); | |
| address |
| #[inline(always)] | ||
| pub fn derive_address_and_bump( | ||
| program_id: &Address, | ||
| authority_policy: &AuthorityPolicy, |
There was a problem hiding this comment.
i think forcing the signer to be derived from the authority policy risks compromising authority should sufficient signers become malicious/unavailable? it certainly seems to preclude modifying the signer set in the future
|
|
||
| #[inline(always)] | ||
| pub fn derive_address(program_id: &Address, authority_policy: &AuthorityPolicy) -> Address { | ||
| Self::derive_address_and_bump(program_id, authority_policy).0 |
| segments.push(&threshold); | ||
| segments.push(&member_count); | ||
| segments.extend(self.members.iter().map(Address::as_ref)); | ||
| hashv(&segments).to_bytes() |
There was a problem hiding this comment.
wow i was going to recommend dumping the phone guy abstraction for a normal hashing interface and we can avoid the vec construction, but somehow we chose to keep the dumbest interface. if we were any good at interfaces, you'd be able to do something like this 🫠
let mut hasher = Hasher::new();
hasher.update(&threshold);
hasher.update(&member_count);
self.members.iter().for_each(hasher.update);
hasher.finalize().to_bytes();| #[derive(Clone, Debug, PartialEq, Eq, SchemaRead, SchemaWrite)] | ||
| pub struct AuthorityPolicy { | ||
| /// Number of member approvals required to authorize execution. | ||
| pub threshold: u8, |
There was a problem hiding this comment.
multisig could also be a standalone cpi-passthrough program. imagine this glorious future 🤓
sign(sign(sign(timelock(multisig(transfer())))))
This PR adds base interfaces for the program: instructions, state, message schema, and PDA types.
Overall objective
Write a program that can serve as a functional replacement for the durable nonce usecase:
The difference is that durable nonces do this via special runtime functionality (that folks are interested in removing), while this program does it through a signed program message.
Instead of signing a full transaction offline, authority members sign a structured
SignedMessagethat commits to the exact action being approved. It doesn't require any special features and can work today. Even further, it can enable additional features like signature expiry.Inspiration
Trent's durable nonce replacement proposal: solana-foundation/solana-improvement-documents#456
High-level design
NonceState: which stores the current nonce plus theAuthorityPolicyNonceStatePda: the PDA that stores stateNonceAuthorityPda: the PDA the program signs as during CPIInstructionData/SignedMessage: the offline-signed authorization formatSignedAction: Supported actions that can be executed by the programThe intended flow is:
SignedMessageSubmitDivergences from original proposal
This follows the spirit of Trent's original proposal, but there are a few divergences:
AuthorityPolicywith threshold plus ordered members, rather than a single authority keyWhat's next