diff --git a/execution-core/src/transfer/moonlight.rs b/execution-core/src/transfer/moonlight.rs index c12ffa481c..2ca103cb7f 100644 --- a/execution-core/src/transfer/moonlight.rs +++ b/execution-core/src/transfer/moonlight.rs @@ -87,6 +87,22 @@ impl Transaction { Ok(Self { payload, signature }) } + /// Create a transaction by signing a previously generated payload with a + /// given secret-key. + /// + /// Note that this transaction will be invalid if the secret-key used for + /// signing doesn't form a valid key-pair with the public-key of the + /// `from_account`. + pub fn new_from_payload( + from_sk: &AccountSecretKey, + payload: Payload, + ) -> Self { + let digest = payload.signature_message(); + let signature = from_sk.sign(&digest); + + Self { payload, signature } + } + /// The proof of the transaction. #[must_use] pub fn signature(&self) -> &AccountSignature { @@ -267,7 +283,7 @@ impl Transaction { /// The payload for a moonlight transaction. #[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)] #[archive_attr(derive(CheckBytes))] -struct Payload { +pub struct Payload { /// ID of the chain for this transaction to execute on. pub chain_id: u8, /// Key of the sender of this transaction. @@ -293,6 +309,48 @@ struct Payload { } impl Payload { + /// Create a new unsigned transaction-payload. + /// This can be used to generate a transaction payload without the + /// secret-key of the sender. + /// + /// To generate a valid transaction, the sender will need to sign the + /// transaction at a later point, using [`Transaction::new_from_payload`] + /// with the secret-key that forms a valid key-pair with the public-key + /// of the `from_account`. + /// + /// # Errors + /// The creation of a transaction is not possible and will error if: + /// - the memo, if given, is too large + pub fn new_unsigned( + from_account: AccountPublicKey, + deposit: u64, + gas_limit: u64, + gas_price: u64, + nonce: u64, + chain_id: u8, + data: Option>, + ) -> Result { + let data = data.map(Into::into); + + if let Some(TransactionData::Memo(memo)) = data.as_ref() { + if memo.len() > MAX_MEMO_SIZE { + return Err(Error::MemoTooLarge(memo.len())); + } + } + + Ok(Self { + chain_id, + from_account, + to_account: None, + value: 0, + deposit, + gas_limit, + gas_price, + nonce, + data, + }) + } + /// Serialize the payload into a byte buffer. #[must_use] pub fn to_var_bytes(&self) -> Vec {