2021-09-03 00:57:35 -07:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2022-03-23 16:45:22 -07:00
|
|
|
const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct<
|
|
|
|
Readonly<{
|
|
|
|
messageDataOffset: number;
|
|
|
|
messageDataSize: number;
|
|
|
|
messageInstructionIndex: number;
|
|
|
|
numSignatures: number;
|
|
|
|
padding: number;
|
|
|
|
publicKeyInstructionIndex: number;
|
|
|
|
publicKeyOffset: number;
|
|
|
|
signatureInstructionIndex: number;
|
|
|
|
signatureOffset: number;
|
|
|
|
}>
|
|
|
|
>([
|
2021-09-03 00:57:35 -07:00
|
|
|
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);
|
|
|
|
|
2022-03-24 11:03:57 -07:00
|
|
|
const index =
|
|
|
|
instructionIndex == null
|
|
|
|
? 0xffff // An index of `u16::MAX` makes it default to the current instruction.
|
|
|
|
: instructionIndex;
|
|
|
|
|
2021-09-03 00:57:35 -07:00
|
|
|
ED25519_INSTRUCTION_LAYOUT.encode(
|
|
|
|
{
|
|
|
|
numSignatures,
|
|
|
|
padding: 0,
|
|
|
|
signatureOffset,
|
2022-03-23 16:45:22 -07:00
|
|
|
signatureInstructionIndex: index,
|
2021-09-03 00:57:35 -07:00
|
|
|
publicKeyOffset,
|
2022-03-23 16:45:22 -07:00
|
|
|
publicKeyInstructionIndex: index,
|
2021-09-03 00:57:35 -07:00
|
|
|
messageDataOffset,
|
|
|
|
messageDataSize: message.length,
|
2022-03-23 16:45:22 -07:00
|
|
|
messageInstructionIndex: index,
|
2021-09-03 00:57:35 -07:00
|
|
|
},
|
|
|
|
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}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|