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 './bpf-loader';
|
||||||
export * from './connection';
|
export * from './connection';
|
||||||
export * from './epoch-schedule';
|
export * from './epoch-schedule';
|
||||||
|
export * from './ed25519-program';
|
||||||
export * from './fee-calculator';
|
export * from './fee-calculator';
|
||||||
export * from './keypair';
|
export * from './keypair';
|
||||||
export * from './loader';
|
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