-
Notifications
You must be signed in to change notification settings - Fork 0
CI rehearsal only (do not merge) #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| use std::{collections::HashSet, vec}; | ||
| use std::{collections::HashSet, str::FromStr, vec}; | ||
|
|
||
| use agave_reserved_account_keys::ReservedAccountKeys; | ||
| use base64::{Engine, prelude::BASE64_STANDARD}; | ||
|
|
@@ -51,15 +51,17 @@ use solana_zk_sdk_pod::encryption::{ | |
| use spl_token_2022_interface::extension::{ | ||
| BaseStateWithExtensions, BaseStateWithExtensionsMut, ExtensionType, StateWithExtensions, | ||
| StateWithExtensionsMut, | ||
| confidential_transfer::{ConfidentialTransferAccount, PENDING_BALANCE_LO_BIT_LENGTH}, | ||
| confidential_transfer::{ | ||
| ConfidentialTransferAccount, ConfidentialTransferMint, PENDING_BALANCE_LO_BIT_LENGTH, | ||
| }, | ||
| confidential_transfer_fee::ConfidentialTransferFeeAmount, | ||
| interest_bearing_mint::InterestBearingConfig, | ||
| scaled_ui_amount::ScaledUiAmountConfig, | ||
| transfer_fee::TransferFeeConfig, | ||
| }; | ||
| use surfpool_types::types::{ | ||
| ConfidentialBalanceKeys, ConfidentialTransferAccountUpdate, DeriveConfidentialKeysResponse, | ||
| GetConfidentialBalanceResponse, | ||
| ConfidentialBalanceKeys, ConfidentialTransferAccountUpdate, ConfidentialTransferMintUpdate, | ||
| DeriveConfidentialKeysResponse, GetConfidentialBalanceResponse, | ||
| }; | ||
| use txtx_addon_kit::indexmap::IndexMap; | ||
|
|
||
|
|
@@ -1444,6 +1446,119 @@ pub fn build_confidential_token_account_data( | |
| Ok(buffer) | ||
| } | ||
|
|
||
| /// Build the raw account data for a Token-2022 mint that carries the | ||
| /// confidential-transfer mint extension. | ||
| /// | ||
| /// Test-only helper backing the `surfnet_setMint` cheatcode: it writes the | ||
| /// extension directly, bypassing the on-chain | ||
| /// `ConfidentialTransferInitializeMint`. An absent `authority` or | ||
| /// `auditor_elgamal_pubkey` is written as the extension's null value. | ||
| pub fn build_confidential_mint_data( | ||
| base: &spl_token_2022_interface::state::Mint, | ||
| conf: &ConfidentialTransferMintUpdate, | ||
| ) -> Result<Vec<u8>, String> { | ||
| let authority = conf | ||
| .authority | ||
| .as_deref() | ||
| .map(Pubkey::from_str) | ||
| .transpose() | ||
| .map_err(|e| format!("authority: {e}"))?; | ||
|
|
||
| let auditor_elgamal_pubkey = conf | ||
| .auditor_elgamal_pubkey | ||
| .as_deref() | ||
| .map(|key| { | ||
| let bytes = decode_confidential_key(key, 32) | ||
| .map_err(|e| format!("auditorElgamalPubkey: {e}"))?; | ||
| ElGamalPubkey::try_from(bytes.as_slice()) | ||
| .map_err(|e| format!("auditorElgamalPubkey: invalid ElGamal public key ({e})")) | ||
| }) | ||
| .transpose()?; | ||
|
|
||
| let extension_types = [ExtensionType::ConfidentialTransferMint]; | ||
| let mint_len = | ||
| ExtensionType::try_calculate_account_len::<spl_token_2022_interface::state::Mint>( | ||
| &extension_types, | ||
| ) | ||
| .map_err(|e| format!("failed to size confidential mint: {e}"))?; | ||
|
|
||
| let mut buffer = vec![0u8; mint_len]; | ||
| let mut state = | ||
| StateWithExtensionsMut::<spl_token_2022_interface::state::Mint>::unpack_uninitialized( | ||
| &mut buffer, | ||
| ) | ||
| .map_err(|e| format!("failed to init confidential mint buffer: {e}"))?; | ||
|
|
||
| state.base = *base; | ||
| state.pack_base(); | ||
| state | ||
| .init_account_type() | ||
| .map_err(|e| format!("failed to set account type: {e}"))?; | ||
|
|
||
| { | ||
| let ct = state | ||
| .init_extension::<ConfidentialTransferMint>(false) | ||
| .map_err(|e| format!("failed to init confidential mint extension: {e}"))?; | ||
| ct.authority = authority.map(Into::into).unwrap_or_default(); | ||
| ct.auto_approve_new_accounts = conf.auto_approve_new_accounts.unwrap_or(true).into(); | ||
| ct.auditor_elgamal_pubkey = auditor_elgamal_pubkey | ||
| .map(|key| PodElGamalPubkey::from(key).into()) | ||
| .unwrap_or_default(); | ||
|
Comment on lines
+1502
to
+1506
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On an existing confidential mint, supplying just a new |
||
| } | ||
|
|
||
| drop(state); | ||
| Ok(buffer) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod confidential_mint_tests { | ||
| use solana_zk_sdk::encryption::elgamal::ElGamalKeypair; | ||
|
|
||
| use super::*; | ||
|
|
||
| /// The auditor key has to be real ElGamal material: a well-formed 32-byte | ||
| /// string that is not a curve point is refused just like a short one, so a | ||
| /// mint never ships an auditor key no client can encrypt to. | ||
| #[test] | ||
| fn an_auditor_key_that_is_not_elgamal_material_is_refused() { | ||
| let base = spl_token_2022_interface::state::Mint { | ||
| is_initialized: true, | ||
| ..Default::default() | ||
| }; | ||
|
|
||
| for auditor in [ | ||
| bs58::encode([7u8; 16]).into_string(), | ||
| bs58::encode([0xffu8; 32]).into_string(), | ||
| ] { | ||
| let error = build_confidential_mint_data( | ||
| &base, | ||
| &ConfidentialTransferMintUpdate { | ||
| auditor_elgamal_pubkey: Some(auditor), | ||
| ..Default::default() | ||
| }, | ||
| ) | ||
| .unwrap_err(); | ||
| assert!(error.starts_with("auditorElgamalPubkey:"), "got: {error}"); | ||
| } | ||
|
|
||
| let auditor = ElGamalKeypair::new_rand(); | ||
| assert!( | ||
| build_confidential_mint_data( | ||
| &base, | ||
| &ConfidentialTransferMintUpdate { | ||
| auditor_elgamal_pubkey: Some( | ||
| bs58::encode(bytes_of(&PodElGamalPubkey::from(auditor.pubkey_owned()))) | ||
| .into_string() | ||
| ), | ||
| ..Default::default() | ||
| }, | ||
| ) | ||
| .is_ok(), | ||
| "a real ElGamal pubkey is accepted" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// Decrypt the confidential balances held on a Token-2022 token account. | ||
| /// | ||
| /// Backs the `surfnet_getConfidentialBalance` cheatcode, the read half of the | ||
|
|
@@ -1692,12 +1807,76 @@ impl MintAccount { | |
| } | ||
| } | ||
|
|
||
| pub fn new(token_program_id: &Pubkey) -> Self { | ||
| if token_program_id == &spl_token_2022_interface::id() { | ||
| Self::SplToken2022(spl_token_2022_interface::state::Mint { | ||
| is_initialized: true, | ||
| ..Default::default() | ||
| }) | ||
| } else { | ||
| Self::SplToken(spl_token_interface::state::Mint { | ||
| is_initialized: true, | ||
| ..Default::default() | ||
| }) | ||
|
Comment on lines
+1816
to
+1820
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A caller can supply a program ID other than SPL Token or Token-2022, but this fallback still creates an SPL Token-shaped mint owned by that program and reports success. SPL Token cannot use an account it does not own, while the supplied program need not understand this data. Reject unsupported IDs so callers do not end up with an unusable mint. |
||
| } | ||
| } | ||
|
|
||
| pub fn decimals(&self) -> u8 { | ||
| match self { | ||
| Self::SplToken2022(mint) => mint.decimals, | ||
| Self::SplToken(mint) => mint.decimals, | ||
| } | ||
| } | ||
|
|
||
| pub fn set_decimals(&mut self, decimals: u8) { | ||
| match self { | ||
| Self::SplToken2022(mint) => mint.decimals = decimals, | ||
| Self::SplToken(mint) => mint.decimals = decimals, | ||
| } | ||
| } | ||
|
|
||
| pub fn set_supply(&mut self, supply: u64) { | ||
| match self { | ||
| Self::SplToken2022(mint) => mint.supply = supply, | ||
| Self::SplToken(mint) => mint.supply = supply, | ||
| } | ||
| } | ||
|
|
||
| pub fn set_mint_authority(&mut self, mint_authority: COption<Pubkey>) { | ||
| match self { | ||
| Self::SplToken2022(mint) => mint.mint_authority = mint_authority, | ||
| Self::SplToken(mint) => mint.mint_authority = mint_authority, | ||
| } | ||
| } | ||
|
|
||
| pub fn pack_into_vec(&self) -> Vec<u8> { | ||
| match self { | ||
| Self::SplToken2022(mint) => { | ||
| let mut dst = [0u8; spl_token_2022_interface::state::Mint::LEN]; | ||
| mint.pack_into_slice(&mut dst); | ||
| dst.to_vec() | ||
| } | ||
| Self::SplToken(mint) => { | ||
| let mut dst = [0u8; spl_token_interface::state::Mint::LEN]; | ||
| mint.pack_into_slice(&mut dst); | ||
| dst.to_vec() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub fn pack_into_preserving_extensions(&self, original: &[u8]) -> SurfpoolResult<Vec<u8>> { | ||
| let base_len = spl_token_interface::state::Mint::LEN; | ||
| if original.len() < base_len { | ||
| return Err(SurfpoolError::unpack_mint_account()); | ||
| } | ||
|
|
||
| let mut data = original.to_vec(); | ||
| match self { | ||
| Self::SplToken2022(mint) => mint.pack_into_slice(&mut data[..base_len]), | ||
| Self::SplToken(mint) => mint.pack_into_slice(&mut data[..base_len]), | ||
| } | ||
| Ok(data) | ||
| } | ||
| } | ||
|
|
||
| pub struct GeyserAccountUpdate { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // @generated by ts-rs from the Rust types in crates/types. | ||
| // Do not edit; run `npm run generate:kit-types` in crates/sdk-node instead. | ||
| // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. | ||
|
|
||
| /** | ||
| * Writes the Token-2022 `ConfidentialTransferMint` extension on a mint created | ||
| * via `surfnet_setMint`. | ||
| * | ||
| * This is a test-only cheatcode: it writes the extension directly, bypassing | ||
| * the real on-chain `ConfidentialTransferInitializeMint` instruction, so a mint | ||
| * with a chosen auditor can be created without forking one from mainnet. | ||
| */ | ||
| export type ConfidentialTransferMintUpdate = { | ||
| /** | ||
| * The authority that approves new confidential accounts and updates this | ||
| * config (base58). Omitted leaves it null. | ||
| */ | ||
| authority?: string, | ||
| /** | ||
| * The auditor's ElGamal public key (base58 or base64, 32 bytes), which every | ||
| * confidential transfer on this mint also encrypts its amount to. Omitted | ||
| * leaves it null, i.e. no auditor. | ||
| */ | ||
| auditorElgamalPubkey?: string, | ||
| /** | ||
| * Whether new confidential accounts are approved on creation (default true). | ||
| */ | ||
| autoApproveNewAccounts?: boolean, }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // @generated by ts-rs from the Rust types in crates/types. | ||
| // Do not edit; run `npm run generate:kit-types` in crates/sdk-node instead. | ||
| // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. | ||
| import type { ConfidentialTransferMintUpdate } from "./ConfidentialTransferMintUpdate.js"; | ||
|
|
||
| export type MintUpdate = { | ||
| /** | ||
| * providing this value sets the number of decimals of the mint | ||
| */ | ||
| decimals?: number, | ||
| /** | ||
| * providing this value sets the mint authority: a base58 pubkey, or the | ||
| * literal string "null" to clear the authority | ||
| */ | ||
| mintAuthority?: string, | ||
| /** | ||
| * providing this value sets the total supply of the mint | ||
| */ | ||
| supply?: number | bigint, | ||
| /** | ||
| * providing this value writes the Token-2022 confidential-transfer mint | ||
| * extension (Token-2022 only) | ||
| */ | ||
| confidential?: ConfidentialTransferMintUpdate, }; |
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.
When
setMintadds confidential support to an existing Token-2022 mint, this builder creates data containing only the confidential extension. If the mint had a transfer-fee configuration, that configuration disappears, so subsequent account setup and transfers no longer use the configured fee. Preserve the existing extensions or reject this update.