feat: support for builtin ed25519 program
This commit is contained in:
parent
c91519961c
commit
ee0b948903
|
@ -0,0 +1,140 @@
|
|||
import {Buffer} from 'buffer';
|
||||
import * as BufferLayout from '@solana/buffer-layout';
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
import {Keypair} from './keypair';
|
||||
import {PublicKey} from './publickey';
|
||||
import {TransactionInstruction} from './transaction';
|
||||
import assert from './util/assert';
|
||||
|
||||
const PRIVATE_KEY_BYTES = 64;
|
||||
const PUBLIC_KEY_BYTES = 32;
|
||||
const SIGNATURE_BYTES = 64;
|
||||
|
||||
/**
|
||||
* Params for creating an ed25519 instruction using a public key
|
||||
*/
|
||||
export type CreateEd25519InstructionWithPublicKeyParams = {
|
||||
publicKey: Uint8Array;
|
||||
message: Uint8Array;
|
||||
signature: Uint8Array;
|
||||
instructionIndex?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Params for creating an ed25519 instruction using a private key
|
||||
*/
|
||||
export type CreateEd25519InstructionWithPrivateKeyParams = {
|
||||
privateKey: Uint8Array;
|
||||
message: Uint8Array;
|
||||
instructionIndex?: number;
|
||||
};
|
||||
|
||||
const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct([
|
||||
BufferLayout.u8('numSignatures'),
|
||||
BufferLayout.u8('padding'),
|
||||
BufferLayout.u16('signatureOffset'),
|
||||
BufferLayout.u16('signatureInstructionIndex'),
|
||||
BufferLayout.u16('publicKeyOffset'),
|
||||
BufferLayout.u16('publicKeyInstructionIndex'),
|
||||
BufferLayout.u16('messageDataOffset'),
|
||||
BufferLayout.u16('messageDataSize'),
|
||||
BufferLayout.u16('messageInstructionIndex'),
|
||||
]);
|
||||
|
||||
export class Ed25519Program {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Public key that identifies the ed25519 program
|
||||
*/
|
||||
static programId: PublicKey = new PublicKey(
|
||||
'Ed25519SigVerify111111111111111111111111111',
|
||||
);
|
||||
|
||||
/**
|
||||
* Create an ed25519 instruction with a public key and signature. The
|
||||
* public key must be a buffer that is 32 bytes long, and the signature
|
||||
* must be a buffer of 64 bytes.
|
||||
*/
|
||||
static createInstructionWithPublicKey(
|
||||
params: CreateEd25519InstructionWithPublicKeyParams,
|
||||
): TransactionInstruction {
|
||||
const {publicKey, message, signature, instructionIndex} = params;
|
||||
|
||||
assert(
|
||||
publicKey.length === PUBLIC_KEY_BYTES,
|
||||
`Public Key must be ${PUBLIC_KEY_BYTES} bytes but received ${publicKey.length} bytes`,
|
||||
);
|
||||
|
||||
assert(
|
||||
signature.length === SIGNATURE_BYTES,
|
||||
`Signature must be ${SIGNATURE_BYTES} bytes but received ${signature.length} bytes`,
|
||||
);
|
||||
|
||||
const publicKeyOffset = ED25519_INSTRUCTION_LAYOUT.span;
|
||||
const signatureOffset = publicKeyOffset + publicKey.length;
|
||||
const messageDataOffset = signatureOffset + signature.length;
|
||||
const numSignatures = 1;
|
||||
|
||||
const instructionData = Buffer.alloc(messageDataOffset + message.length);
|
||||
|
||||
ED25519_INSTRUCTION_LAYOUT.encode(
|
||||
{
|
||||
numSignatures,
|
||||
padding: 0,
|
||||
signatureOffset,
|
||||
signatureInstructionIndex: instructionIndex,
|
||||
publicKeyOffset,
|
||||
publicKeyInstructionIndex: instructionIndex,
|
||||
messageDataOffset,
|
||||
messageDataSize: message.length,
|
||||
messageInstructionIndex: instructionIndex,
|
||||
},
|
||||
instructionData,
|
||||
);
|
||||
|
||||
instructionData.fill(publicKey, publicKeyOffset);
|
||||
instructionData.fill(signature, signatureOffset);
|
||||
instructionData.fill(message, messageDataOffset);
|
||||
|
||||
return new TransactionInstruction({
|
||||
keys: [],
|
||||
programId: Ed25519Program.programId,
|
||||
data: instructionData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ed25519 instruction with a private key. The private key
|
||||
* must be a buffer that is 64 bytes long.
|
||||
*/
|
||||
static createInstructionWithPrivateKey(
|
||||
params: CreateEd25519InstructionWithPrivateKeyParams,
|
||||
): TransactionInstruction {
|
||||
const {privateKey, message, instructionIndex} = params;
|
||||
|
||||
assert(
|
||||
privateKey.length === PRIVATE_KEY_BYTES,
|
||||
`Private key must be ${PRIVATE_KEY_BYTES} bytes but received ${privateKey.length} bytes`,
|
||||
);
|
||||
|
||||
try {
|
||||
const keypair = Keypair.fromSecretKey(privateKey);
|
||||
const publicKey = keypair.publicKey.toBytes();
|
||||
const signature = nacl.sign.detached(message, keypair.secretKey);
|
||||
|
||||
return this.createInstructionWithPublicKey({
|
||||
publicKey,
|
||||
message,
|
||||
signature,
|
||||
instructionIndex,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Error creating instruction; ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ export * from './bpf-loader-deprecated';
|
|||
export * from './bpf-loader';
|
||||
export * from './connection';
|
||||
export * from './epoch-schedule';
|
||||
export * from './ed25519-program';
|
||||
export * from './fee-calculator';
|
||||
export * from './keypair';
|
||||
export * from './loader';
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import {Buffer} from 'buffer';
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
import {
|
||||
Connection,
|
||||
Keypair,
|
||||
sendAndConfirmTransaction,
|
||||
LAMPORTS_PER_SOL,
|
||||
Transaction,
|
||||
Ed25519Program,
|
||||
} from '../src';
|
||||
import {url} from './url';
|
||||
|
||||
if (process.env.TEST_LIVE) {
|
||||
describe('ed25519', () => {
|
||||
const keypair = Keypair.generate();
|
||||
const privateKey = keypair.secretKey;
|
||||
const publicKey = keypair.publicKey.toBytes();
|
||||
const from = Keypair.generate();
|
||||
const connection = new Connection(url, 'confirmed');
|
||||
|
||||
before(async function () {
|
||||
await connection.confirmTransaction(
|
||||
await connection.requestAirdrop(from.publicKey, 10 * LAMPORTS_PER_SOL),
|
||||
);
|
||||
});
|
||||
|
||||
it('create ed25519 instruction', async () => {
|
||||
const message = Buffer.from('string address');
|
||||
const signature = nacl.sign.detached(message, privateKey);
|
||||
const transaction = new Transaction().add(
|
||||
Ed25519Program.createInstructionWithPublicKey({
|
||||
publicKey,
|
||||
message,
|
||||
signature,
|
||||
}),
|
||||
);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from]);
|
||||
});
|
||||
|
||||
it('create ed25519 instruction with private key', async () => {
|
||||
const message = Buffer.from('private key');
|
||||
const transaction = new Transaction().add(
|
||||
Ed25519Program.createInstructionWithPrivateKey({
|
||||
privateKey,
|
||||
message,
|
||||
}),
|
||||
);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from]);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue