Skip to content

Commit

Permalink
[PAY-2747] Fix attestation decoding and add Secp256k1Program utils (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rickyrombo authored Apr 24, 2024
1 parent 342d610 commit deb3f2a
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/six-pens-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@audius/spl': patch
---

Add Secp256k1Program extensions to @audius/spl to aid in decoding and debugging secp256k1 instructions
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Request, Response, NextFunction } from 'express'
import type { RelayRequestBody } from '@audius/sdk'
import { Request, Response } from 'express'

export const health = async (req: Request, res: Response) => {
res.status(200).json({
isHealthy: true
})
export const health = async (_req: Request, res: Response) => {
res.status(200).json({
isHealthy: true
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
} from '@audius/spl'
import { InvalidRelayInstructionError } from './InvalidRelayInstructionError'
import { describe, it } from 'vitest'
import { AudiusLibs } from '@audius/sdk'

const CLAIMABLE_TOKEN_PROGRAM_ID = new PublicKey(config.claimableTokenProgramId)

Expand Down Expand Up @@ -55,28 +54,6 @@ const audioClaimableTokenAuthority = PublicKey.findProgramAddressSync(

const getRandomPublicKey = () => Keypair.generate().publicKey

const getInittedLibs = async () => {
// @ts-ignore
const libs = new AudiusLibs({
solanaWeb3Config: {
solanaClusterEndpoint: config.solanaEndpoint,
mintAddress: config.waudioMintAddress,
usdcMintAddress: config.usdcMintAddress,
solanaTokenAddress: TOKEN_PROGRAM_ID.toBase58(),
feePayerAddress: config.solanaFeePayerWallets[0].publicKey,
claimableTokenProgramAddress: config.claimableTokenProgramId,
rewardsManagerProgramId: config.rewardsManagerProgramId,
rewardsManagerProgramPDA: config.rewardsManagerAccountAddress,
rewardsManagerTokenPDA: '',
useRelay: false,
confirmationTimeout: 0,
paymentRouterProgramId: config.paymentRouterProgramId
}
})
await libs.init()
return libs
}

describe('Solana Relay', function () {
describe('Associated Token Account Program', function () {
it('should allow create token account with matching close for valid mints', async function () {
Expand Down Expand Up @@ -216,10 +193,8 @@ describe('Solana Relay', function () {
it('should allow USDC transfers to userbanks', async function () {
// Dummy eth address to make the encoder happy
const wallet = '0xe42b199d864489387bf64262874fc6472bcbc151'
const userbank = await (
await getInittedLibs()
).solanaWeb3Manager!.deriveUserBank({
mint: 'usdc',
const userbank = await ClaimableTokensProgram.deriveUserBank({
claimableTokensPDA: usdcClaimableTokenAuthority,
ethAddress: wallet
})

Expand Down Expand Up @@ -519,30 +494,34 @@ describe('Solana Relay', function () {
payer,
mint,
authority: usdcClaimableTokenAuthority,
userBank
userBank,
programId: CLAIMABLE_TOKEN_PROGRAM_ID
}),
ClaimableTokensProgram.createTransferInstruction({
payer,
sourceEthAddress: wallet,
sourceUserBank: userBank,
destination,
nonceAccount,
authority: usdcClaimableTokenAuthority
authority: usdcClaimableTokenAuthority,
programId: CLAIMABLE_TOKEN_PROGRAM_ID
}),
ClaimableTokensProgram.createAccountInstruction({
ethAddress: wallet,
payer,
mint,
authority: audioClaimableTokenAuthority,
userBank
userBank,
programId: CLAIMABLE_TOKEN_PROGRAM_ID
}),
ClaimableTokensProgram.createTransferInstruction({
payer,
sourceEthAddress: wallet,
sourceUserBank: userBank,
destination,
nonceAccount,
authority: audioClaimableTokenAuthority
authority: audioClaimableTokenAuthority,
programId: CLAIMABLE_TOKEN_PROGRAM_ID
})
]
await assertRelayAllowedInstructions(instructions)
Expand Down Expand Up @@ -917,7 +896,6 @@ describe('Solana Relay', function () {
})

it('should not allow transfers when not authenticated', async function () {
const feePayer = getRandomPublicKey()
const fromPubkey = getRandomPublicKey()
const toPubkey = getRandomPublicKey()
await assert.rejects(async () =>
Expand Down Expand Up @@ -975,20 +953,51 @@ describe('Solana Relay', function () {
})

describe('Other Programs', function () {
it('allows memo and SECP instructions', async function () {
it('allows memo instructions', async function () {
await assertRelayAllowedInstructions([
new TransactionInstruction({ programId: MEMO_PROGRAM_ID, keys: [] }),
new TransactionInstruction({ programId: MEMO_V2_PROGRAM_ID, keys: [] }),
new TransactionInstruction({ programId: MEMO_V2_PROGRAM_ID, keys: [] })
])
})

it('allows valid secp256k1 instructions', async function () {
await assertRelayAllowedInstructions([
Secp256k1Program.createInstructionWithEthAddress({
// Dummy eth address to make the encoder happy
ethAddress: '0xe42b199d864489387bf64262874fc6472bcbc151',
message: Buffer.from('some message', 'utf-8'),
signature: Buffer.alloc(64),
ethAddress: '0x8fcfa10bd3808570987dbb5b1ef4ab74400fbfda',
message: Buffer.from(
'68d5397bb16195ea47091010f3abb8fc6b5cdfa65f00e1f505000000005f623a33383639383d3e3530373431303135335f00b6462e955da5841b6d9e1e2529b830f00f31bf',
'hex'
),
signature: Buffer.from(
'f89b2e6f97f95f1306b468b10b1a18df9569b07d9d7b81b241d6fc99d9ec782e4e449f5c3c63836ed52c9344d3de5c3133fead711e421af545822f09bd78cb39',
'hex'
),
recoveryId: 0
})
])
})

it('rejects invalid secp256k1 instructions', async function () {
await assert.rejects(async () =>
assertRelayAllowedInstructions([
Secp256k1Program.createInstructionWithEthAddress({
// Dummy eth address to make the encoder happy
ethAddress: '0x00b6462e955da5841b6d9e1e2529b830f00f31bf',
message: Buffer.from(
'81729dc83c157f41de7df4b72fc7e90d8d64d5aa5f00e1f505000000005f72656665727265643a353339343735333137',
'hex'
),
signature: Buffer.from(
'00d405b277dc948f97d7b7db8648cb16590d66084ba49642fedb08380ce5027a95d0a895287a3331332e7ad13daba87eed5c70820a19ca2eb6cc0ea1eb4695ba',
'hex'
),
recoveryId: 0
})
])
)
})

it('does not allow other random programs', async function () {
await assert.rejects(
async () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import {
PublicKey,
TransactionInstruction,
Secp256k1Program,
SystemProgram,
SystemInstruction
} from '@solana/web3.js'
Expand All @@ -21,7 +20,8 @@ import {
ClaimableTokensProgram,
RewardManagerProgram,
isCreateAssociatedTokenAccountIdempotentInstruction,
isCreateAssociatedTokenAccountInstruction
isCreateAssociatedTokenAccountInstruction,
Secp256k1Program
} from '@audius/spl'
import { config } from '../../config'
import bs58 from 'bs58'
Expand Down Expand Up @@ -196,8 +196,7 @@ const assertAllowedRewardsManagerProgramInstruction = (
*/
const assertAllowedClaimableTokenProgramInstruction = async (
instructionIndex: number,
instruction: TransactionInstruction,
user?: { blockchainUserId?: number; handle?: string | null }
instruction: TransactionInstruction
) => {
const decodedInstruction =
ClaimableTokensProgram.decodeInstruction(instruction)
Expand Down Expand Up @@ -324,6 +323,24 @@ const assertAllowedSystemProgramInstruction = (
}
}

const assertValidSecp256k1ProgramInstruction = (
instructionIndex: number,
instruction: TransactionInstruction
) => {
try {
if (
!Secp256k1Program.verifySignature(Secp256k1Program.decode(instruction))
) {
throw new Error('Signer does not match')
}
} catch (e) {
throw new InvalidRelayInstructionError(
instructionIndex,
'Invalid Secp256k1Program instruction'
)
}
}

/**
* Checks each of the instructions to make sure it's something we want to relay.
* The main goals of the checks are to ensure the feePayer isn't abused.
Expand Down Expand Up @@ -368,11 +385,7 @@ export const assertRelayAllowedInstructions = async (
assertAllowedRewardsManagerProgramInstruction(i, instruction)
break
case CLAIMABLE_TOKEN_PROGRAM_ID:
await assertAllowedClaimableTokenProgramInstruction(
i,
instruction,
options?.user
)
await assertAllowedClaimableTokenProgramInstruction(i, instruction)
break
case JUPITER_AGGREGATOR_V6_PROGRAM_ID:
await assertAllowedJupiterProgramInstruction(
Expand All @@ -390,6 +403,8 @@ export const assertRelayAllowedInstructions = async (
)
break
case Secp256k1Program.programId.toBase58():
assertValidSecp256k1ProgramInstruction(i, instruction)
break
case MEMO_PROGRAM_ID:
case MEMO_V2_PROGRAM_ID:
case TRACK_LISTEN_COUNT_PROGRAM_ID:
Expand Down
1 change: 1 addition & 0 deletions packages/spl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"homepage": "https://github.com/AudiusProject/audius-protocol/tree/main/packages/spl",
"dependencies": {
"@coral-xyz/anchor": "0.29.0",
"@noble/hashes": "1.4.0",
"@solana/buffer-layout": "4.0.1",
"@solana/buffer-layout-utils": "0.2.0",
"@solana/spl-token": "0.3.8",
Expand Down
1 change: 1 addition & 0 deletions packages/spl/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { ClaimableTokensProgram } from './claimable-tokens/ClaimableTokensProgram'
export { RewardManagerInstruction } from './reward-manager/constants'
export { RewardManagerProgram } from './reward-manager/RewardManagerProgram'
export { Secp256k1Program } from './secp256k1/Secp256k1Program'
export { ethAddress } from './layout-utils'
export * from './associated-token'
export * from './payment-router'
Loading

0 comments on commit deb3f2a

Please sign in to comment.