feat: support creating secp256k1 instructions with eth address (#15626)
This commit is contained in:
@ -13,12 +13,12 @@ import {toBuffer} from './util/to-buffer';
|
||||
const {publicKeyCreate, ecdsaSign} = secp256k1;
|
||||
|
||||
const PRIVATE_KEY_BYTES = 32;
|
||||
const ETHEREUM_ADDRESS_BYTES = 20;
|
||||
const PUBLIC_KEY_BYTES = 64;
|
||||
const HASHED_PUBKEY_SERIALIZED_SIZE = 20;
|
||||
const SIGNATURE_OFFSETS_SERIALIZED_SIZE = 11;
|
||||
|
||||
/**
|
||||
* Create a Secp256k1 instruction using a public key params
|
||||
* Params for creating an secp256k1 instruction using a public key
|
||||
* @typedef {Object} CreateSecp256k1InstructionWithPublicKeyParams
|
||||
* @property {Buffer | Uint8Array | Array<number>} publicKey
|
||||
* @property {Buffer | Uint8Array | Array<number>} message
|
||||
@ -33,7 +33,22 @@ export type CreateSecp256k1InstructionWithPublicKeyParams = {|
|
||||
|};
|
||||
|
||||
/**
|
||||
* Create a Secp256k1 instruction using a private key params
|
||||
* Params for creating an secp256k1 instruction using an Ethereum address
|
||||
* @typedef {Object} CreateSecp256k1InstructionWithEthAddressParams
|
||||
* @property {Buffer | Uint8Array | Array<number>} ethAddress
|
||||
* @property {Buffer | Uint8Array | Array<number>} message
|
||||
* @property {Buffer | Uint8Array | Array<number>} signature
|
||||
* @property {number} recoveryId
|
||||
*/
|
||||
export type CreateSecp256k1InstructionWithEthAddressParams = {|
|
||||
ethAddress: Buffer | Uint8Array | Array<number> | string,
|
||||
message: Buffer | Uint8Array | Array<number>,
|
||||
signature: Buffer | Uint8Array | Array<number>,
|
||||
recoveryId: number,
|
||||
|};
|
||||
|
||||
/**
|
||||
* Params for creating an secp256k1 instruction using a private key
|
||||
* @typedef {Object} CreateSecp256k1InstructionWithPrivateKeyParams
|
||||
* @property {Buffer | Uint8Array | Array<number>} privateKey
|
||||
* @property {Buffer | Uint8Array | Array<number>} message
|
||||
@ -59,32 +74,72 @@ const SECP256K1_INSTRUCTION_LAYOUT = BufferLayout.struct([
|
||||
|
||||
export class Secp256k1Program {
|
||||
/**
|
||||
* Public key that identifies the Secp256k program
|
||||
* Public key that identifies the secp256k1 program
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey('KeccakSecp256k11111111111111111111111111111');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a secp256k1 instruction with public key
|
||||
* Construct an Ethereum address from a secp256k1 public key buffer.
|
||||
* @param {Buffer} publicKey a 64 byte secp256k1 public key buffer
|
||||
*/
|
||||
static publicKeyToEthAddress(
|
||||
publicKey: Buffer | Uint8Array | Array<number>,
|
||||
): Buffer {
|
||||
assert(
|
||||
publicKey.length === PUBLIC_KEY_BYTES,
|
||||
`Public key must be ${PUBLIC_KEY_BYTES} bytes but received ${publicKey.length} bytes`,
|
||||
);
|
||||
|
||||
try {
|
||||
return Buffer.from(keccak_256.update(toBuffer(publicKey)).digest()).slice(
|
||||
-ETHEREUM_ADDRESS_BYTES,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(`Error constructing Ethereum address: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an secp256k1 instruction with a public key. The public key
|
||||
* must be a buffer that is 64 bytes long.
|
||||
*/
|
||||
static createInstructionWithPublicKey(
|
||||
params: CreateSecp256k1InstructionWithPublicKeyParams,
|
||||
): TransactionInstruction {
|
||||
const {publicKey, message, signature, recoveryId} = params;
|
||||
return Secp256k1Program.createInstructionWithEthAddress({
|
||||
ethAddress: Secp256k1Program.publicKeyToEthAddress(publicKey),
|
||||
message,
|
||||
signature,
|
||||
recoveryId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an secp256k1 instruction with an Ethereum address. The address
|
||||
* must be a hex string or a buffer that is 20 bytes long.
|
||||
*/
|
||||
static createInstructionWithEthAddress(
|
||||
params: CreateSecp256k1InstructionWithEthAddressParams,
|
||||
): TransactionInstruction {
|
||||
const {ethAddress: rawAddress, message, signature, recoveryId} = params;
|
||||
|
||||
let ethAddress = rawAddress;
|
||||
if (typeof rawAddress === 'string') {
|
||||
if (rawAddress.startsWith('0x')) {
|
||||
ethAddress = Buffer.from(rawAddress.substr(2), 'hex');
|
||||
} else {
|
||||
ethAddress = Buffer.from(rawAddress, 'hex');
|
||||
}
|
||||
}
|
||||
|
||||
assert(
|
||||
publicKey.length === PUBLIC_KEY_BYTES,
|
||||
`Public key must be ${PUBLIC_KEY_BYTES} bytes`,
|
||||
ethAddress.length === ETHEREUM_ADDRESS_BYTES,
|
||||
`Address must be ${ETHEREUM_ADDRESS_BYTES} bytes but received ${ethAddress.length} bytes`,
|
||||
);
|
||||
|
||||
let ethAddress;
|
||||
try {
|
||||
ethAddress = constructEthAddress(publicKey);
|
||||
} catch (error) {
|
||||
throw new Error(`Error constructing ethereum public key: ${error}`);
|
||||
}
|
||||
|
||||
const dataStart = 1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE;
|
||||
const ethAddressOffset = dataStart;
|
||||
const signatureOffset = dataStart + ethAddress.length;
|
||||
@ -106,7 +161,7 @@ export class Secp256k1Program {
|
||||
messageDataSize: message.length,
|
||||
messageInstructionIndex: 0,
|
||||
signature: toBuffer(signature),
|
||||
ethAddress,
|
||||
ethAddress: toBuffer(ethAddress),
|
||||
recoveryId,
|
||||
},
|
||||
instructionData,
|
||||
@ -122,7 +177,8 @@ export class Secp256k1Program {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a secp256k1 instruction with private key
|
||||
* Create an secp256k1 instruction with a private key. The private key
|
||||
* must be a buffer that is 32 bytes long.
|
||||
*/
|
||||
static createInstructionWithPrivateKey(
|
||||
params: CreateSecp256k1InstructionWithPrivateKeyParams,
|
||||
@ -131,7 +187,7 @@ export class Secp256k1Program {
|
||||
|
||||
assert(
|
||||
privateKey.length === PRIVATE_KEY_BYTES,
|
||||
`Private key must be ${PRIVATE_KEY_BYTES} bytes`,
|
||||
`Private key must be ${PRIVATE_KEY_BYTES} bytes but received ${privateKey.length} bytes`,
|
||||
);
|
||||
|
||||
try {
|
||||
@ -152,11 +208,3 @@ export class Secp256k1Program {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function constructEthAddress(
|
||||
publicKey: Buffer | Uint8Array | Array<number>,
|
||||
): Buffer {
|
||||
return Buffer.from(keccak_256.update(toBuffer(publicKey)).digest()).slice(
|
||||
-HASHED_PUBKEY_SERIALIZED_SIZE,
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user