Skip to content
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

Delegated Attestations as authorities or signatures #12

Open
koolamusic opened this issue Oct 14, 2024 · 3 comments
Open

Delegated Attestations as authorities or signatures #12

koolamusic opened this issue Oct 14, 2024 · 3 comments

Comments

@koolamusic
Copy link
Member

Currently exploring two implementations for delegated attestations. either as authorities or via signatures

In rust based blockchains, signatures can easily be compute intensive and slow.
However there is the functional requirement for delegated attestations to have some form of signature. Unlike their EVM some rust based chains already sign transactions, before they are even executed, and in solana PDAs or Program Derived Addresses are a public key that can be used to create delegated authorities via a seed or a combination of variables, "signer address + unique string"

Look at the following code examples to get insight into both options.

Delegated Signatories

pub fn delegated_attest(
    ctx: Context<DelegatedAttestation>,
    attestation_data: AttestationData,
    attester_signature: [u8; 64],
) -> Result<()> {
    let attester_pubkey = ctx.accounts.attester.key;

    // Verify the signature
    let verified = verify_signature(
        &ctx.accounts.instruction_sysvar,
        &attester_pubkey,
        &attestation_data,
        attester_signature,
    )?;
    if !verified {
        return Err(AttestationError::InvalidSignature.into());
    }

    let schema_data = &ctx.accounts.schema_data;
    let attestation = &mut ctx.accounts.attestation;

   internal_attest_function(attestation, schema_data);

    Ok(())
}



fn verify_signature(
    instruction_sysvar: &AccountInfo,
    attester_pubkey: &Pubkey,
    attestation_data: &AttestationData,
    attester_signature: [u8; 64],
) -> Result<()> {
    // Load the instructions from the instruction sysvar
    let instruction_sysvar_data = instruction_sysvar.try_borrow_data()?;
    let current_index = solana_program::sysvar::instructions::load_current_index(&instruction_sysvar_data);
    // The ed25519 instruction should be just before the current instruction
    let ed25519_ix_index = current_index.saturating_sub(1);
    let ed25519_ix = solana_program::sysvar::instructions::load_instruction_at(ed25519_ix_index as usize, &instruction_sysvar_data)?;

    // Check that the program ID is the ed25519 program
    if ed25519_ix.program_id != solana_program::ed25519_program::id() {
        return Err(AttestationError::InvalidSignature.into());
    }

    // Parse the ed25519 instruction data
    let ed25519_instruction = ed25519_program::Ed25519Instruction::unpack(&ed25519_ix.data)
        .map_err(|_| AttestationError::InvalidSignature)?;

    // Verify that the public key matches the attester's public key
    if ed25519_instruction.public_key != attester_pubkey.to_bytes() {
        return Err(AttestationError::InvalidSignature.into());
    }
  // Serialize the attestation data
    let expected_message = attestation_data.try_to_vec().map_err(|_| AttestationError::InvalidSignature)?;

    // Verify that the message matches
    if ed25519_instruction.message != expected_message {
        return Err(AttestationError::InvalidSignature.into());
    }

    // Optionally, verify that the signature matches the one provided
    if ed25519_instruction.signature != attester_signature {
        return Err(AttestationError::InvalidSignature.into());
    }

    // All checks passed
    Ok(())
}

In the above we really on a signature verification mechanism, where a delegator can sign an attestation off-chain and we can verify that they signed that attestation, by confirming the signature on-chain.

This approach is permission less, as any delegator does not have to interact with our system to create authorities. the ubiquity also enables these signature implementation to be relied on across several other processes.


Alternatively, taking into account the quirks of the protocols we're designing for like Stellar and the Solana blockchain, there exist a couple other authentication mechanisms that can be used to create delegates.

In solana for example, we can do this using PDA with a delegate.

pub struct DelegatedAttest<'info> {
    #[account(mut)]
    /// The delegate who is submitting the attestation.
    pub delegate: Signer<'info>,
    /// CHECK: The attester's public key; no data needed.
    pub attester: UncheckedAccount<'info>,
    #[account(
        seeds = [b"delegation", attester.key.as_ref(), delegate.key.as_ref()],
        bump,
        constraint = delegation.attester == attester.key() @ AttestationError::InvalidDelegation,
        constraint = delegation.delegate == delegate.key() @ AttestationError::InvalidDelegation,
        constraint = !delegation.revoked @ AttestationError::InvalidDelegation,
    )]
    /// The Delegation account authorizing the delegate.
    pub delegation: Account<'info, Delegation>,
    /// CHECK: The recipient's public key; no data needed.
    pub recipient: UncheckedAccount<'info>,
    #[account(
        constraint = schema_data.to_account_info().owner == &schema_registry_program.key() @ AttestationError::InvalidSchema,
    )]
    /// The schema data account; must match the schema UID.
    pub schema_data: Account<'info, SchemaData>,
    #[account(
        init,
        payer = delegate,
        space = Attestation::LEN,
        seeds = [b"attestation", schema_data.key().as_ref(), recipient.key.as_ref(), attester.key.as_ref()],
        bump
    )]
    /// The attestation account to be created.
    pub attestation: Account<'info, Attestation>,
    /// The Schema Registry program account for CPI.
    pub schema_registry_program: Program<'info, SchemaRegistry>,
    pub system_program: Program<'info, System>,
}

The key to creating a delegated Authority is in the struct above.
However, a delegate must interact with out protocol to create their first delegate using Program Derived Address.

pub struct CreateDelegation<'info> {
    #[account(mut)]
    /// The attester who is creating the delegation.
    pub attester: Signer<'info>,
    /// CHECK: The delegate's public key; no data needed.
    pub delegate: UncheckedAccount<'info>,
    #[account(
        init,
        payer = attester,
        space = Delegation::LEN,
        seeds = [b"delegation", attester.key.as_ref(), delegate.key.as_ref()],
        bump
    )]
    /// The Delegation account to be created.
    pub delegation: Account<'info, Delegation>,
    pub system_program: Program<'info, System>,
}


pub fn create_delegation(
    ctx: Context<CreateDelegation>,
    expiration_time: Option<i64>,
) -> Result<()> {
    let delegation = &mut ctx.accounts.delegation;
    let current_time = Clock::get()?.unix_timestamp;

    // Ensure expiration time is in the future, if provided
    if let Some(exp_time) = expiration_time {
        if exp_time <= current_time {
            return Err(AttestationError::InvalidExpirationTime.into());
        }
    }

    delegation.attester = ctx.accounts.attester.key();
    delegation.delegate = ctx.accounts.delegate.key();
    delegation.expiration_time = expiration_time;
    delegation.revoked = false;

    Ok(())
}

This approach means, an organization can authority more than one delegate to act on their behalf, a feature that could open up more possibilities, but limited in cryptographic proof, as the promise of attestations, when they are not signed on-chain is that there must be some cryptographic proof about the attestation that we can trust,,, but verify

@koolamusic
Copy link
Member Author

Resolving to work with delegated_signatories
cc @kohasummons @raybaann

@kohasummons
Copy link
Contributor

Fair!

@koolamusic
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants