Skip to content

Commit

Permalink
refactor: deriveAddressSeed and deriveAddress utilities
Browse files Browse the repository at this point in the history
**Problems**

- The distinction between `derive_address_seed` and `derive_address` was
  unclear and we were inconsistent in it:
  - We ended up applying address Merkle tree public key in both
    functions, which is confusing.
- Before this change, there was no TypeScript function for deriving
  **address seed**. There was only `deriveAddress`, but deriving the
  unified seed was a mystery for developers.
- We have two utilities for hashing and truncating to BN254:
  - `hash_to_bn254_field_size_be` - the older one, which:
    - Searches for a bump in a loop, adds it to the hash inputs and then
      truncates the hash. That doesn't make sense, because truncating
      the hash should be sufficient, adding a bump is unnecessary.
    - Another limitation is that it takes only one sequence of bytes,
      making it difficult to provide multiple inputs without
      concatenating them.
  - `hashv_to_bn254_field_size` - the newer one, which:
    - Just truncates the hash result, without the bump mechanism.
    - Takes 2D byte slice as input, making it possible to pass multiple
      inputs.

**Changes**

- Don't add MT pubkey in `derive_address_seed`. It's not a correct place
  for it to be applied. The distinction between `derive_address_seed`
  and `derive_address` should be:
  - `derive_address_seed` takes provided seeds (defined by the
    developer) and hashes them together with the program ID. This
    operation is done only in the third-party program.
  - `derive_address` takes the address seed (result of
    `address_address_seed`) and hashes it together with the address
    Merkle tree public key. This is done both in the third-party program
    and in light-system-program. light-system-program does that as a
    check whether the correct Merkle tree is used.
- Adjust the stateless.js API:
  - Provide `deriveAddressSeed` function.
  - Add unit tests, make sure that `deriveAddressSeed` and
    `deriveAddress` provide the same results as the equivalent functions
    in Rust SDK.
  • Loading branch information
vadorovsky committed Sep 30, 2024
1 parent 12f1750 commit 1d4c8b8
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 313 deletions.
6 changes: 1 addition & 5 deletions examples/name-service/programs/name-service/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@ async fn test_name_service() {
address_queue_pubkey: env.address_merkle_tree_queue_pubkey,
};

let address_seed = derive_address_seed(
&[b"name-service", name.as_bytes()],
&name_service::ID,
&address_merkle_context,
);
let address_seed = derive_address_seed(&[b"name-service", name.as_bytes()], &name_service::ID);
let address = derive_address(&address_seed, &address_merkle_context);

let address_merkle_context =
Expand Down
17 changes: 9 additions & 8 deletions js/stateless.js/src/actions/create-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
NewAddressParams,
buildAndSignTx,
deriveAddress,
deriveAddressSeed,
sendAndConfirmTx,
} from '../utils';
import { defaultTestStateTreeAccounts } from '../constants';
Expand All @@ -25,7 +26,7 @@ import { BN } from '@coral-xyz/anchor';
*
* @param rpc RPC to use
* @param payer Payer of the transaction and initialization fees
* @param seed Seed to derive the new account address
* @param seeds Seeds to derive the new account address
* @param programId Owner of the new account
* @param addressTree Optional address tree. Defaults to a current shared
* address tree.
Expand All @@ -40,7 +41,7 @@ import { BN } from '@coral-xyz/anchor';
export async function createAccount(
rpc: Rpc,
payer: Signer,
seed: Uint8Array,
seeds: Uint8Array[],
programId: PublicKey,
addressTree?: PublicKey,
addressQueue?: PublicKey,
Expand All @@ -52,8 +53,8 @@ export async function createAccount(
addressTree = addressTree ?? defaultTestStateTreeAccounts().addressTree;
addressQueue = addressQueue ?? defaultTestStateTreeAccounts().addressQueue;

/// TODO: enforce program-derived
const address = await deriveAddress(seed, addressTree);
const seed = deriveAddressSeed(seeds, programId);
const address = deriveAddress(seed, addressTree);

const proof = await rpc.getValidityProofV0(undefined, [
{
Expand Down Expand Up @@ -96,7 +97,7 @@ export async function createAccount(
*
* @param rpc RPC to use
* @param payer Payer of the transaction and initialization fees
* @param seed Seed to derive the new account address
* @param seeds Seeds to derive the new account address
* @param lamports Number of compressed lamports to initialize the
* account with
* @param programId Owner of the new account
Expand All @@ -114,7 +115,7 @@ export async function createAccount(
export async function createAccountWithLamports(
rpc: Rpc,
payer: Signer,
seed: Uint8Array,
seeds: Uint8Array[],
lamports: number | BN,
programId: PublicKey,
addressTree?: PublicKey,
Expand All @@ -138,8 +139,8 @@ export async function createAccountWithLamports(
addressTree = addressTree ?? defaultTestStateTreeAccounts().addressTree;
addressQueue = addressQueue ?? defaultTestStateTreeAccounts().addressQueue;

/// TODO: enforce program-derived
const address = await deriveAddress(seed, addressTree);
const seed = deriveAddressSeed(seeds, programId);
const address = deriveAddress(seed, addressTree);

const proof = await rpc.getValidityProof(
inputAccounts.map(account => bn(account.hash)),
Expand Down
242 changes: 16 additions & 226 deletions js/stateless.js/src/idls/light_compressed_token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1549,128 +1549,23 @@ export type LightCompressedToken = {
errors: [
{
code: 6000;
name: 'PublicKeyAmountMissmatch';
msg: 'public keys and amounts must be of same length';
name: 'SignerCheckFailed';
msg: 'Signer check failed';
},
{
code: 6001;
name: 'ComputeInputSumFailed';
msg: 'ComputeInputSumFailed';
name: 'CreateTransferInstructionFailed';
msg: 'Create transfer instruction failed';
},
{
code: 6002;
name: 'ComputeOutputSumFailed';
msg: 'ComputeOutputSumFailed';
name: 'AccountNotFound';
msg: 'Account not found';
},
{
code: 6003;
name: 'ComputeCompressSumFailed';
msg: 'ComputeCompressSumFailed';
},
{
code: 6004;
name: 'ComputeDecompressSumFailed';
msg: 'ComputeDecompressSumFailed';
},
{
code: 6005;
name: 'SumCheckFailed';
msg: 'SumCheckFailed';
},
{
code: 6006;
name: 'DecompressRecipientUndefinedForDecompress';
msg: 'DecompressRecipientUndefinedForDecompress';
},
{
code: 6007;
name: 'CompressedPdaUndefinedForDecompress';
msg: 'CompressedPdaUndefinedForDecompress';
},
{
code: 6008;
name: 'DeCompressAmountUndefinedForDecompress';
msg: 'DeCompressAmountUndefinedForDecompress';
},
{
code: 6009;
name: 'CompressedPdaUndefinedForCompress';
msg: 'CompressedPdaUndefinedForCompress';
},
{
code: 6010;
name: 'DeCompressAmountUndefinedForCompress';
msg: 'DeCompressAmountUndefinedForCompress';
},
{
code: 6011;
name: 'DelegateSignerCheckFailed';
msg: 'DelegateSignerCheckFailed';
},
{
code: 6012;
name: 'MintTooLarge';
msg: 'Minted amount greater than u64::MAX';
},
{
code: 6013;
name: 'SplTokenSupplyMismatch';
msg: 'SplTokenSupplyMismatch';
},
{
code: 6014;
name: 'HeapMemoryCheckFailed';
msg: 'HeapMemoryCheckFailed';
},
{
code: 6015;
name: 'InstructionNotCallable';
msg: 'The instruction is not callable';
},
{
code: 6016;
name: 'ArithmeticUnderflow';
msg: 'ArithmeticUnderflow';
},
{
code: 6017;
name: 'HashToFieldError';
msg: 'HashToFieldError';
},
{
code: 6018;
name: 'InvalidAuthorityMint';
msg: 'Expected the authority to be also a mint authority';
},
{
code: 6019;
name: 'InvalidFreezeAuthority';
msg: 'Provided authority is not the freeze authority';
},
{
code: 6020;
name: 'InvalidDelegateIndex';
},
{
code: 6021;
name: 'TokenPoolPdaUndefined';
},
{
code: 6022;
name: 'IsTokenPoolPda';
msg: 'Compress or decompress recipient is the same account as the token pool pda.';
},
{
code: 6023;
name: 'InvalidTokenPoolPda';
},
{
code: 6024;
name: 'NoInputTokenAccountsProvided';
},
{
code: 6025;
name: 'NoInputsProvided';
name: 'SerializationError';
msg: 'Serialization error';
},
];
};
Expand Down Expand Up @@ -3230,128 +3125,23 @@ export const IDL: LightCompressedToken = {
errors: [
{
code: 6000,
name: 'PublicKeyAmountMissmatch',
msg: 'public keys and amounts must be of same length',
name: 'SignerCheckFailed',
msg: 'Signer check failed',
},
{
code: 6001,
name: 'ComputeInputSumFailed',
msg: 'ComputeInputSumFailed',
name: 'CreateTransferInstructionFailed',
msg: 'Create transfer instruction failed',
},
{
code: 6002,
name: 'ComputeOutputSumFailed',
msg: 'ComputeOutputSumFailed',
name: 'AccountNotFound',
msg: 'Account not found',
},
{
code: 6003,
name: 'ComputeCompressSumFailed',
msg: 'ComputeCompressSumFailed',
},
{
code: 6004,
name: 'ComputeDecompressSumFailed',
msg: 'ComputeDecompressSumFailed',
},
{
code: 6005,
name: 'SumCheckFailed',
msg: 'SumCheckFailed',
},
{
code: 6006,
name: 'DecompressRecipientUndefinedForDecompress',
msg: 'DecompressRecipientUndefinedForDecompress',
},
{
code: 6007,
name: 'CompressedPdaUndefinedForDecompress',
msg: 'CompressedPdaUndefinedForDecompress',
},
{
code: 6008,
name: 'DeCompressAmountUndefinedForDecompress',
msg: 'DeCompressAmountUndefinedForDecompress',
},
{
code: 6009,
name: 'CompressedPdaUndefinedForCompress',
msg: 'CompressedPdaUndefinedForCompress',
},
{
code: 6010,
name: 'DeCompressAmountUndefinedForCompress',
msg: 'DeCompressAmountUndefinedForCompress',
},
{
code: 6011,
name: 'DelegateSignerCheckFailed',
msg: 'DelegateSignerCheckFailed',
},
{
code: 6012,
name: 'MintTooLarge',
msg: 'Minted amount greater than u64::MAX',
},
{
code: 6013,
name: 'SplTokenSupplyMismatch',
msg: 'SplTokenSupplyMismatch',
},
{
code: 6014,
name: 'HeapMemoryCheckFailed',
msg: 'HeapMemoryCheckFailed',
},
{
code: 6015,
name: 'InstructionNotCallable',
msg: 'The instruction is not callable',
},
{
code: 6016,
name: 'ArithmeticUnderflow',
msg: 'ArithmeticUnderflow',
},
{
code: 6017,
name: 'HashToFieldError',
msg: 'HashToFieldError',
},
{
code: 6018,
name: 'InvalidAuthorityMint',
msg: 'Expected the authority to be also a mint authority',
},
{
code: 6019,
name: 'InvalidFreezeAuthority',
msg: 'Provided authority is not the freeze authority',
},
{
code: 6020,
name: 'InvalidDelegateIndex',
},
{
code: 6021,
name: 'TokenPoolPdaUndefined',
},
{
code: 6022,
name: 'IsTokenPoolPda',
msg: 'Compress or decompress recipient is the same account as the token pool pda.',
},
{
code: 6023,
name: 'InvalidTokenPoolPda',
},
{
code: 6024,
name: 'NoInputTokenAccountsProvided',
},
{
code: 6025,
name: 'NoInputsProvided',
name: 'SerializationError',
msg: 'Serialization error',
},
],
};
Loading

0 comments on commit 1d4c8b8

Please sign in to comment.