diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js new file mode 100644 index 00000000000000..40e7b5dd2f1e4c --- /dev/null +++ b/web3.js/module.flow.js @@ -0,0 +1,907 @@ +/** + * Flow Library definition for @solana/web3.js + * + * This file is manually maintained + * + * Usage: add the following line under the [libs] section of your project's + * .flowconfig: + * [libs] + * node_modules/@solana/web3.js/module.flow.js + * + */ + +import {Buffer} from 'buffer'; +import * as BufferLayout from 'buffer-layout'; + +declare module '@solana/web3.js' { + // === src/publickey.js === + declare export type PublicKeyNonce = [PublicKey, number]; + declare export class PublicKey { + constructor( + value: number | string | Buffer | Uint8Array | Array, + ): PublicKey; + static createWithSeed( + fromPublicKey: PublicKey, + seed: string, + programId: PublicKey, + ): Promise; + static createProgramAddress( + seeds: Array, + programId: PublicKey, + ): Promise; + static findProgramAddress( + seeds: Array, + programId: PublicKey, + ): Promise; + equals(publickey: PublicKey): boolean; + toBase58(): string; + toBuffer(): Buffer; + toString(): string; + } + + // === src/blockhash.js === + declare export type Blockhash = string; + + // === src/account.js === + declare export class Account { + constructor(secretKey?: Buffer | Uint8Array | Array): Account; + publicKey: PublicKey; + secretKey: Buffer; + } + + // === src/fee-calculator.js === + declare export type FeeCalculator = { + lamportsPerSignature: number, + }; + + // === src/budget-program.js === + /* TODO */ + + // === src/connection.js === + declare export type Context = { + slot: number, + }; + + declare export type SendOptions = { + skipPreflight: ?boolean, + }; + + declare export type ConfirmOptions = { + confirmations: ?number, + skipPreflight: ?boolean, + }; + + declare export type ConfirmedSignaturesForAddress2Options = { + before?: TransactionSignature, + limit?: number, + }; + + declare export type TokenAccountsFilter = + | { + mint: PublicKey, + } + | { + programId: PublicKey, + }; + + declare export type RpcResponseAndContext = { + context: Context, + value: T, + }; + + declare export type Commitment = + | 'max' + | 'recent' + | 'root' + | 'single' + | 'singleGossip'; + + declare export type LargestAccountsFilter = 'circulating' | 'nonCirculating'; + + declare export type GetLargestAccountsConfig = { + commitment: ?Commitment, + filter: ?LargestAccountsFilter, + }; + + declare export type SignatureStatusConfig = { + searchTransactionHistory: boolean, + }; + + declare export type SignatureStatus = { + slot: number, + err: TransactionError | null, + confirmations: number | null, + }; + + declare export type ConfirmedSignatureInfo = { + signature: string, + slot: number, + err: TransactionError | null, + memo: string | null, + }; + + declare export type BlockhashAndFeeCalculator = { + blockhash: Blockhash, + feeCalculator: FeeCalculator, + }; + + declare export type PublicKeyAndAccount = { + pubkey: PublicKey, + account: AccountInfo, + }; + + declare export type AccountInfo = { + executable: boolean, + owner: PublicKey, + lamports: number, + data: Buffer, + rentEpoch: number | null, + }; + + declare export type ContactInfo = { + pubkey: string, + gossip: string | null, + tpu: string | null, + rpc: string | null, + version: string | null, + }; + + declare export type ConfirmedTransactionMeta = { + fee: number, + preBalances: Array, + postBalances: Array, + err: TransactionError | null, + }; + + declare export type ConfirmedBlock = { + blockhash: Blockhash, + previousBlockhash: Blockhash, + parentSlot: number, + transactions: Array<{ + transaction: Transaction, + meta: ConfirmedTransactionMeta | null, + }>, + }; + + declare export type ConfirmedTransaction = { + slot: number, + transaction: Transaction, + meta: ConfirmedTransactionMeta | null, + }; + + declare export type KeyedAccountInfo = { + accountId: PublicKey, + accountInfo: AccountInfo, + }; + + declare export type Version = { + 'solana-core': string, + }; + + declare export type VoteAccountInfo = { + votePubkey: string, + nodePubkey: string, + stake: number, + commission: number, + }; + + declare export type SlotInfo = { + parent: number, + slot: number, + root: number, + }; + + declare export type TokenAmount = { + uiAmount: number, + decimals: number, + amount: string, + }; + + declare type AccountChangeCallback = ( + accountInfo: AccountInfo, + context: Context, + ) => void; + declare type ProgramAccountChangeCallback = ( + keyedAccountInfo: KeyedAccountInfo, + context: Context, + ) => void; + declare type SlotChangeCallback = (slotInfo: SlotInfo) => void; + declare type SignatureResultCallback = ( + signatureResult: SignatureResult, + context: Context, + ) => void; + declare type RootChangeCallback = (root: number) => void; + + declare export type TransactionError = {}; + declare export type SignatureResult = {| + err: TransactionError | null, + |}; + + declare export type InflationGovernor = { + foundation: number, + foundationTerm: number, + initial: number, + taper: number, + terminal: number, + }; + + declare export type EpochSchedule = { + slotsPerEpoch: number, + leaderScheduleSlotOffset: number, + warmup: boolean, + firstNormalEpoch: number, + firstNormalSlot: number, + }; + + declare export type EpochInfo = { + epoch: number, + slotIndex: number, + slotsInEpoch: number, + absoluteSlot: number, + blockHeight: ?number, + }; + + declare export type LeaderSchedule = { + [address: string]: number[], + }; + + declare export type Supply = { + total: number, + circulating: number, + nonCirculating: number, + nonCirculatingAccounts: Array, + }; + + declare export type AccountBalancePair = { + address: PublicKey, + lamports: number, + }; + + declare export type VoteAccountStatus = { + current: Array, + delinquent: Array, + }; + + declare export class Connection { + constructor(endpoint: string, commitment: ?Commitment): Connection; + commitment: ?Commitment; + getAccountInfoAndContext( + publicKey: PublicKey, + commitment: ?Commitment, + ): Promise>; + getAccountInfo( + publicKey: PublicKey, + commitment: ?Commitment, + ): Promise; + getProgramAccounts( + programId: PublicKey, + commitment: ?Commitment, + ): Promise>; + getBalanceAndContext( + publicKey: PublicKey, + commitment: ?Commitment, + ): Promise>; + getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise; + getBlockTime(slot: number): Promise; + getMinimumLedgerSlot(): Promise; + getFirstAvailableBlock(): Promise; + getSupply(commitment: ?Commitment): Promise>; + getTokenSupply( + tokenMintAddress: PublicKey, + commitment: ?Commitment, + ): Promise>; + getTokenAccountBalance( + tokenAddress: PublicKey, + commitment: ?Commitment, + ): Promise>; + getTokenAccountsByOwner( + ownerAddress: PublicKey, + filter: TokenAccountsFilter, + commitment: ?Commitment, + ): Promise< + RpcResponseAndContext>, + >; + getLargestAccounts( + config: ?GetLargestAccountsConfig, + ): Promise>>; + getClusterNodes(): Promise>; + getConfirmedBlock(slot: number): Promise; + getConfirmedTransaction( + signature: TransactionSignature, + ): Promise; + getConfirmedSignaturesForAddress( + address: PublicKey, + startSlot: number, + endSlot: number, + ): Promise>; + getVoteAccounts(commitment: ?Commitment): Promise; + confirmTransaction( + signature: TransactionSignature, + confirmations: ?number, + ): Promise>; + getSlot(commitment: ?Commitment): Promise; + getSlotLeader(commitment: ?Commitment): Promise; + getSignatureStatus( + signature: TransactionSignature, + config: ?SignatureStatusConfig, + ): Promise>; + getSignatureStatuses( + signatures: Array, + config: ?SignatureStatusConfig, + ): Promise>>; + getTransactionCount(commitment: ?Commitment): Promise; + getTotalSupply(commitment: ?Commitment): Promise; + getVersion(): Promise; + getInflationGovernor(commitment: ?Commitment): Promise; + getLeaderSchedule(): Promise; + getEpochSchedule(): Promise; + getEpochInfo(commitment: ?Commitment): Promise; + getRecentBlockhashAndContext( + commitment: ?Commitment, + ): Promise>; + getFeeCalculatorForBlockhash( + blockhash: Blockhash, + commitment: ?Commitment, + ): Promise>; + getRecentBlockhash( + commitment: ?Commitment, + ): Promise; + requestAirdrop( + to: PublicKey, + amount: number, + ): Promise; + sendTransaction( + transaction: Transaction, + signers: Array, + options?: SendOptions, + ): Promise; + sendEncodedTransaction( + encodedTransaction: string, + options?: SendOptions, + ): Promise; + sendRawTransaction( + wireTransaction: Buffer | Uint8Array | Array, + options?: SendOptions, + ): Promise; + onAccountChange( + publickey: PublicKey, + callback: AccountChangeCallback, + commitment: ?Commitment, + ): number; + removeAccountChangeListener(id: number): Promise; + onProgramAccountChange( + programId: PublicKey, + callback: ProgramAccountChangeCallback, + commitment: ?Commitment, + ): number; + removeProgramAccountChangeListener(id: number): Promise; + onSlotChange(callback: SlotChangeCallback): number; + removeSlotChangeListener(id: number): Promise; + onSignature( + signature: TransactionSignature, + callback: SignatureResultCallback, + commitment: ?Commitment, + ): number; + removeSignatureListener(id: number): Promise; + onRootChange(callback: RootChangeCallback): number; + removeRootChangeListener(id: number): Promise; + validatorExit(): Promise; + getMinimumBalanceForRentExemption( + dataLength: number, + commitment: ?Commitment, + ): Promise; + getNonce( + nonceAccount: PublicKey, + commitment: ?Commitment, + ): Promise; + getNonceAndContext( + nonceAccount: PublicKey, + commitment: ?Commitment, + ): Promise>; + } + + // === src/nonce-account.js === + declare export class NonceAccount { + authorizedPubkey: PublicKey; + nonce: Blockhash; + feeCalculator: FeeCalculator; + static fromAccountData( + buffer: Buffer | Uint8Array | Array, + ): NonceAccount; + } + + declare export var NONCE_ACCOUNT_LENGTH: number; + + // === src/validator-info.js === + declare export var VALIDATOR_INFO_KEY; + declare export type Info = {| + name: string, + website?: string, + details?: string, + keybaseUsername?: string, + |}; + + declare export class ValidatorInfo { + key: PublicKey; + info: Info; + + constructor(key: PublicKey, info: Info): ValidatorInfo; + static fromConfigData( + buffer: Buffer | Uint8Array | Array, + ): ValidatorInfo | null; + } + + // === src/sysvar.js === + declare export var SYSVAR_CLOCK_PUBKEY; + declare export var SYSVAR_RENT_PUBKEY; + declare export var SYSVAR_REWARDS_PUBKEY; + declare export var SYSVAR_STAKE_HISTORY_PUBKEY; + + // === src/vote-account.js === + declare export var VOTE_PROGRAM_ID; + declare export type Lockout = {| + slot: number, + confirmationCount: number, + |}; + + declare export type EpochCredits = {| + epoch: number, + credits: number, + prevCredits: number, + |}; + + declare export class VoteAccount { + votes: Array; + nodePubkey: PublicKey; + authorizedVoterPubkey: PublicKey; + commission: number; + rootSlot: number | null; + epoch: number; + credits: number; + lastEpochCredits: number; + epochCredits: Array; + static fromAccountData( + buffer: Buffer | Uint8Array | Array, + ): VoteAccount; + } + + // === src/instruction.js === + declare export type InstructionType = {| + index: number, + layout: typeof BufferLayout, + |}; + + declare export function encodeData(type: InstructionType, fields: {}): Buffer; + + // === src/message.js === + declare export type MessageHeader = { + numRequiredSignatures: number, + numReadonlySignedAccounts: number, + numReadonlyUnsignedAccounts: number, + }; + + declare export type CompiledInstruction = { + programIdIndex: number, + accounts: number[], + data: string, + }; + + declare export type MessageArgs = { + header: MessageHeader, + accountKeys: string[], + recentBlockhash: Blockhash, + instructions: CompiledInstruction[], + }; + + declare export class Message { + header: MessageHeader; + accountKeys: PublicKey[]; + recentBlockhash: Blockhash; + instructions: CompiledInstruction[]; + + constructor(args: MessageArgs): Message; + isAccountWritable(index: number): boolean; + serialize(): Buffer; + } + + // === src/transaction.js === + declare export type TransactionSignature = string; + + declare export type AccountMeta = { + pubkey: PublicKey, + isSigner: boolean, + isWritable: boolean, + }; + + declare type TransactionInstructionCtorFields = {| + keys: ?Array, + programId?: PublicKey, + data?: Buffer, + |}; + + declare export class TransactionInstruction { + keys: Array; + programId: PublicKey; + data: Buffer; + + constructor( + opts?: TransactionInstructionCtorFields, + ): TransactionInstruction; + } + + declare type SignaturePubkeyPair = {| + signature: Buffer | null, + publicKey: PublicKey, + |}; + + declare type NonceInformation = {| + nonce: Blockhash, + nonceInstruction: TransactionInstruction, + |}; + + declare type TransactionCtorFields = {| + recentBlockhash?: Blockhash, + nonceInfo?: NonceInformation, + signatures?: Array, + |}; + + declare export class Transaction { + signatures: Array; + signature: ?Buffer; + instructions: Array; + recentBlockhash: ?Blockhash; + nonceInfo: ?NonceInformation; + + constructor(opts?: TransactionCtorFields): Transaction; + static from(buffer: Buffer | Uint8Array | Array): Transaction; + static populate(message: Message, signatures: Array): Transaction; + add( + ...items: Array< + Transaction | TransactionInstruction | TransactionInstructionCtorFields, + > + ): Transaction; + compileMessage(): Message; + serializeMessage(): Buffer; + sign(...signers: Array): void; + signPartial(...partialSigners: Array): void; + addSigner(signer: Account): void; + addSignature(pubkey: PublicKey, signature: Buffer): void; + verifySignatures(): boolean; + serialize(): Buffer; + } + + // === src/stake-program.js === + declare export type StakeAuthorizationType = {| + index: number, + |}; + + declare export class Authorized { + staker: PublicKey; + withdrawer: PublicKey; + constructor(staker: PublicKey, withdrawer: PublicKey): Authorized; + } + + declare export class Lockup { + unixTimestamp: number; + epoch: number; + custodian: PublicKey; + constructor( + unixTimestamp: number, + epoch: number, + custodian: PublicKey, + ): Lockup; + } + + declare export type CreateStakeAccountParams = {| + fromPubkey: PublicKey, + stakePubkey: PublicKey, + authorized: Authorized, + lockup: Lockup, + lamports: number, + |}; + + declare export type CreateStakeAccountWithSeedParams = {| + fromPubkey: PublicKey, + stakePubkey: PublicKey, + basePubkey: PublicKey, + seed: string, + authorized: Authorized, + lockup: Lockup, + lamports: number, + |}; + + declare export type InitializeStakeParams = {| + stakePubkey: PublicKey, + authorized: Authorized, + lockup: Lockup, + |}; + + declare export type DelegateStakeParams = {| + stakePubkey: PublicKey, + authorizedPubkey: PublicKey, + votePubkey: PublicKey, + |}; + + declare export type AuthorizeStakeParams = {| + stakePubkey: PublicKey, + authorizedPubkey: PublicKey, + newAuthorizedPubkey: PublicKey, + stakeAuthorizationType: StakeAuthorizationType, + |}; + + declare export type SplitStakeParams = {| + stakePubkey: PublicKey, + authorizedPubkey: PublicKey, + splitStakePubkey: PublicKey, + lamports: number, + |}; + + declare export type WithdrawStakeParams = {| + stakePubkey: PublicKey, + authorizedPubkey: PublicKey, + toPubkey: PublicKey, + lamports: number, + |}; + + declare export type DeactivateStakeParams = {| + stakePubkey: PublicKey, + authorizedPubkey: PublicKey, + |}; + + declare export class StakeProgram { + static programId: PublicKey; + static space: number; + static createAccount(params: CreateStakeAccountParams): Transaction; + static createAccountWithSeed( + params: CreateStakeAccountWithSeedParams, + ): Transaction; + static delegate(params: DelegateStakeParams): Transaction; + static authorize(params: AuthorizeStakeParams): Transaction; + static split(params: SplitStakeParams): Transaction; + static withdraw(params: WithdrawStakeParams): Transaction; + static deactivate(params: DeactivateStakeParams): Transaction; + } + + declare export type StakeInstructionType = + | 'Initialize' + | 'Authorize' + | 'Delegate' + | 'Split' + | 'Withdraw' + | 'Deactivate'; + + declare export var STAKE_INSTRUCTION_LAYOUTS: { + [StakeInstructionType]: InstructionType, + }; + + declare export class StakeInstruction { + static decodeInstructionType( + instruction: TransactionInstruction, + ): StakeInstructionType; + static decodeInitialize( + instruction: TransactionInstruction, + ): InitializeStakeParams; + static decodeDelegate( + instruction: TransactionInstruction, + ): DelegateStakeParams; + static decodeAuthorize( + instruction: TransactionInstruction, + ): AuthorizeStakeParams; + static decodeSplit(instruction: TransactionInstruction): SplitStakeParams; + static decodeWithdraw( + instruction: TransactionInstruction, + ): WithdrawStakeParams; + static decodeDeactivate( + instruction: TransactionInstruction, + ): DeactivateStakeParams; + } + + // === src/system-program.js === + declare export type CreateAccountParams = {| + fromPubkey: PublicKey, + newAccountPubkey: PublicKey, + lamports: number, + space: number, + programId: PublicKey, + |}; + + declare export type CreateAccountWithSeedParams = {| + fromPubkey: PublicKey, + newAccountPubkey: PublicKey, + basePubkey: PublicKey, + seed: string, + lamports: number, + space: number, + programId: PublicKey, + |}; + + declare export type AllocateParams = {| + accountPubkey: PublicKey, + space: number, + |}; + + declare export type AllocateWithSeedParams = {| + accountPubkey: PublicKey, + basePubkey: PublicKey, + seed: string, + space: number, + programId: PublicKey, + |}; + + declare export type AssignParams = {| + accountPubkey: PublicKey, + programId: PublicKey, + |}; + + declare export type AssignWithSeedParams = {| + accountPubkey: PublicKey, + basePubkey: PublicKey, + seed: string, + programId: PublicKey, + |}; + + declare export type TransferParams = {| + fromPubkey: PublicKey, + toPubkey: PublicKey, + lamports: number, + |}; + + declare export type CreateNonceAccountParams = {| + fromPubkey: PublicKey, + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + lamports: number, + |}; + + declare export type CreateNonceAccountWithSeedParams = {| + fromPubkey: PublicKey, + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + lamports: number, + basePubkey: PublicKey, + seed: string, + |}; + + declare export type InitializeNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + |}; + + declare export type AdvanceNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + |}; + + declare export type WithdrawNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + toPubkey: PublicKey, + lamports: number, + |}; + + declare export type AuthorizeNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + newAuthorizedPubkey: PublicKey, + |}; + + declare export class SystemProgram { + static programId: PublicKey; + + static createAccount(params: CreateAccountParams): Transaction; + static createAccountWithSeed( + params: CreateAccountWithSeedParams, + ): Transaction; + static allocate( + params: AllocateParams | AllocateWithSeedParams, + ): Transaction; + static assign(params: AssignParams | AssignWithSeedParams): Transaction; + static transfer(params: TransferParams): Transaction; + static createNonceAccount( + params: CreateNonceAccountParams | CreateNonceAccountWithSeedParams, + ): Transaction; + static nonceAdvance(params: AdvanceNonceParams): TransactionInstruction; + static nonceWithdraw(params: WithdrawNonceParams): Transaction; + static nonceAuthorize(params: AuthorizeNonceParams): Transaction; + } + + declare export type SystemInstructionType = + | 'Create' + | 'CreateWithSeed' + | 'Allocate' + | 'AllocateWithSeed' + | 'Assign' + | 'AssignWithSeed' + | 'Transfer' + | 'AdvanceNonceAccount' + | 'WithdrawNonceAccount' + | 'InitializeNonceAccount' + | 'AuthorizeNonceAccount'; + + declare export var SYSTEM_INSTRUCTION_LAYOUTS: { + [SystemInstructionType]: InstructionType, + }; + + declare export class SystemInstruction { + static decodeInstructionType( + instruction: TransactionInstruction, + ): SystemInstructionType; + static decodeCreateAccount( + instruction: TransactionInstruction, + ): CreateAccountParams; + static decodeCreateWithSeed( + instruction: TransactionInstruction, + ): CreateAccountWithSeedParams; + static decodeAllocate(instruction: TransactionInstruction): AllocateParams; + static decodeAllocateWithSeed( + instruction: TransactionInstruction, + ): AllocateWithSeedParams; + static decodeAssign(instruction: TransactionInstruction): AssignParams; + static decodeAssignWithSeed( + instruction: TransactionInstruction, + ): AssignWithSeedParams; + static decodeTransfer(instruction: TransactionInstruction): TransferParams; + static decodeNonceInitialize( + instruction: TransactionInstruction, + ): InitializeNonceParams; + static decodeNonceAdvance( + instruction: TransactionInstruction, + ): AdvanceNonceParams; + static decodeNonceWithdraw( + instruction: TransactionInstruction, + ): WithdrawNonceParams; + static decodeNonceAuthorize( + instruction: TransactionInstruction, + ): AuthorizeNonceParams; + } + + // === src/loader.js === + declare export class Loader { + static getMinNumSignatures(dataLength: number): number; + static load( + connection: Connection, + payer: Account, + program: Account, + programId: PublicKey, + data: Buffer | Uint8Array | Array, + ): Promise; + } + + // === src/bpf-loader.js === + declare export class BpfLoader { + static programId: PublicKey; + static getMinNumSignatures(dataLength: number): number; + static load( + connection: Connection, + payer: Account, + program: Account, + elfBytes: Buffer | Uint8Array | Array, + ): Promise; + } + + // === src/util/send-and-confirm-transaction.js === + declare export function sendAndConfirmTransaction( + connection: Connection, + transaction: Transaction, + signers: Array, + options: ?ConfirmOptions, + ): Promise; + + // === src/util/send-and-confirm-raw-transaction.js === + declare export function sendAndConfirmRawTransaction( + connection: Connection, + wireTransaction: Buffer, + options: ?ConfirmOptions, + ): Promise; + + // === src/util/cluster.js === + declare export type Cluster = 'devnet' | 'testnet' | 'mainnet-beta'; + + declare export function clusterApiUrl( + cluster?: Cluster, + tls?: boolean, + ): string; + + // === src/index.js === + declare export var LAMPORTS_PER_SOL: number; +} diff --git a/web3.js/src/publickey.js b/web3.js/src/publickey.js new file mode 100644 index 00000000000000..9c1e89382c1eb7 --- /dev/null +++ b/web3.js/src/publickey.js @@ -0,0 +1,219 @@ +// @flow + +import BN from 'bn.js'; +import bs58 from 'bs58'; +import nacl from 'tweetnacl'; +import {sha256} from 'crypto-hash'; + +//$FlowFixMe +let naclLowLevel = nacl.lowlevel; + +type PublicKeyNonce = [PublicKey, number]; // This type exists to workaround an esdoc parse error + +/** + * A public key + */ +export class PublicKey { + _bn: BN; + + /** + * Create a new PublicKey object + */ + constructor(value: number | string | Buffer | Uint8Array | Array) { + if (typeof value === 'string') { + // hexadecimal number + if (value.startsWith('0x')) { + this._bn = new BN(value.substring(2), 16); + } else { + // assume base 58 encoding by default + const decoded = bs58.decode(value); + if (decoded.length != 32) { + throw new Error(`Invalid public key input`); + } + this._bn = new BN(decoded); + } + } else { + this._bn = new BN(value); + } + + if (this._bn.byteLength() > 32) { + throw new Error(`Invalid public key input`); + } + } + + /** + * Checks if two publicKeys are equal + */ + equals(publicKey: PublicKey): boolean { + return this._bn.eq(publicKey._bn); + } + + /** + * Return the base-58 representation of the public key + */ + toBase58(): string { + return bs58.encode(this.toBuffer()); + } + + /** + * Return the Buffer representation of the public key + */ + toBuffer(): Buffer { + const b = this._bn.toArrayLike(Buffer); + if (b.length === 32) { + return b; + } + + const zeroPad = Buffer.alloc(32); + b.copy(zeroPad, 32 - b.length); + return zeroPad; + } + + /** + * Returns a string representation of the public key + */ + toString(): string { + return this.toBase58(); + } + + /** + * Derive a public key from another key, a seed, and a program ID. + */ + static async createWithSeed( + fromPublicKey: PublicKey, + seed: string, + programId: PublicKey, + ): Promise { + const buffer = Buffer.concat([ + fromPublicKey.toBuffer(), + Buffer.from(seed), + programId.toBuffer(), + ]); + const hash = await sha256(new Uint8Array(buffer)); + return new PublicKey('0x' + hash); + } + + /** + * Derive a program address from seeds and a program ID. + */ + static async createProgramAddress( + seeds: Array, + programId: PublicKey, + ): Promise { + let buffer = Buffer.alloc(0); + seeds.forEach(function (seed) { + buffer = Buffer.concat([buffer, Buffer.from(seed)]); + }); + buffer = Buffer.concat([ + buffer, + programId.toBuffer(), + Buffer.from('ProgramDerivedAddress'), + ]); + let hash = await sha256(new Uint8Array(buffer)); + let publicKeyBytes = new BN(hash, 16).toBuffer(); + if (is_on_curve(publicKeyBytes)) { + throw new Error(`Invalid seeds, address must fall off the curve`); + } + return new PublicKey(publicKeyBytes); + } + + /** + * Find a valid program address + * + * Valid program addresses must fall off the ed25519 curve. This function + * iterates a nonce until it finds one that when combined with the seeds + * results in a valid program address. + */ + static async findProgramAddress( + seeds: Array, + programId: PublicKey, + ): Promise { + let nonce = 255; + let address; + while (nonce != 0) { + try { + const seedsWithNonce = seeds.concat(Buffer.from([nonce])); + address = await this.createProgramAddress(seedsWithNonce, programId); + } catch (err) { + nonce--; + continue; + } + return [address, nonce]; + } + throw new Error(`Unable to find a viable program address nonce`); + } +} + +// Check that a pubkey is on the curve. +// This function and its dependents were sourced from: +// https://github.com/dchest/tweetnacl-js/blob/f1ec050ceae0861f34280e62498b1d3ed9c350c6/nacl.js#L792 +function is_on_curve(p) { + var r = [ + naclLowLevel.gf(), + naclLowLevel.gf(), + naclLowLevel.gf(), + naclLowLevel.gf(), + ]; + + var t = naclLowLevel.gf(), + chk = naclLowLevel.gf(), + num = naclLowLevel.gf(), + den = naclLowLevel.gf(), + den2 = naclLowLevel.gf(), + den4 = naclLowLevel.gf(), + den6 = naclLowLevel.gf(); + + naclLowLevel.set25519(r[2], gf1); + naclLowLevel.unpack25519(r[1], p); + naclLowLevel.S(num, r[1]); + naclLowLevel.M(den, num, naclLowLevel.D); + naclLowLevel.Z(num, num, r[2]); + naclLowLevel.A(den, r[2], den); + + naclLowLevel.S(den2, den); + naclLowLevel.S(den4, den2); + naclLowLevel.M(den6, den4, den2); + naclLowLevel.M(t, den6, num); + naclLowLevel.M(t, t, den); + + naclLowLevel.pow2523(t, t); + naclLowLevel.M(t, t, num); + naclLowLevel.M(t, t, den); + naclLowLevel.M(t, t, den); + naclLowLevel.M(r[0], t, den); + + naclLowLevel.S(chk, r[0]); + naclLowLevel.M(chk, chk, den); + if (neq25519(chk, num)) naclLowLevel.M(r[0], r[0], I); + + naclLowLevel.S(chk, r[0]); + naclLowLevel.M(chk, chk, den); + if (neq25519(chk, num)) return 0; + return 1; +} +let gf1 = naclLowLevel.gf([1]); +let I = naclLowLevel.gf([ + 0xa0b0, + 0x4a0e, + 0x1b27, + 0xc4ee, + 0xe478, + 0xad2f, + 0x1806, + 0x2f43, + 0xd7a7, + 0x3dfb, + 0x0099, + 0x2b4d, + 0xdf0b, + 0x4fc1, + 0x2480, + 0x2b83, +]); +function neq25519(a, b) { + var c = new Uint8Array(32), + d = new Uint8Array(32); + naclLowLevel.pack25519(c, a); + naclLowLevel.pack25519(d, b); + return naclLowLevel.crypto_verify_32(c, 0, d, 0); +} diff --git a/web3.js/test/publickey.test.js b/web3.js/test/publickey.test.js new file mode 100644 index 00000000000000..fbf60462f17f2b --- /dev/null +++ b/web3.js/test/publickey.test.js @@ -0,0 +1,305 @@ +// @flow +import {PublicKey} from '../src/publickey'; + +test('invalid', () => { + expect(() => { + new PublicKey([ + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + }).toThrow(); + + expect(() => { + new PublicKey( + '0x300000000000000000000000000000000000000000000000000000000000000000000', + ); + }).toThrow(); + + expect(() => { + new PublicKey( + '135693854574979916511997248057056142015550763280047535983739356259273198796800000', + ); + }).toThrow(); + + expect(() => { + new PublicKey('12345'); + }).toThrow(); +}); + +test('equals', () => { + const arrayKey = new PublicKey([ + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + const hexKey = new PublicKey( + '0x300000000000000000000000000000000000000000000000000000000000000', + ); + const base56Key = new PublicKey( + 'CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3', + ); + + expect(arrayKey.equals(hexKey)).toBe(true); + expect(arrayKey.equals(base56Key)).toBe(true); +}); + +test('toBase58', () => { + const key = new PublicKey( + '0x300000000000000000000000000000000000000000000000000000000000000', + ); + expect(key.toBase58()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3'); + expect(key.toString()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3'); + + const key2 = new PublicKey('1111111111111111111111111111BukQL'); + expect(key2.toBase58()).toBe('1111111111111111111111111111BukQL'); + expect(key2.toString()).toBe('1111111111111111111111111111BukQL'); + + const key3 = new PublicKey('11111111111111111111111111111111'); + expect(key3.toBase58()).toBe('11111111111111111111111111111111'); + + const key4 = new PublicKey([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + expect(key4.toBase58()).toBe('11111111111111111111111111111111'); +}); + +test('toBuffer', () => { + const key = new PublicKey( + '0x300000000000000000000000000000000000000000000000000000000000000', + ); + expect(key.toBuffer()).toHaveLength(32); + expect(key.toBase58()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3'); + + const key2 = new PublicKey( + '0x000000000000000000000000000000000000000000000000000000000000000', + ); + expect(key2.toBuffer()).toHaveLength(32); + expect(key2.toBase58()).toBe('11111111111111111111111111111111'); + + const key3 = new PublicKey(0); + expect(key3.toBuffer()).toHaveLength(32); + expect(key3.toBase58()).toBe('11111111111111111111111111111111'); + + const key4 = new PublicKey('0x0'); + expect(key4.toBuffer()).toHaveLength(32); + expect(key4.toBase58()).toBe('11111111111111111111111111111111'); +}); + +test('equals (II)', () => { + const key1 = new PublicKey([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ]); + const key2 = new PublicKey(key1.toBuffer()); + + expect(key1.equals(key2)).toBe(true); +}); + +test('createWithSeed', async () => { + const defaultPublicKey = new PublicKey('11111111111111111111111111111111'); + const derivedKey = await PublicKey.createWithSeed( + defaultPublicKey, + 'limber chicken: 4/45', + defaultPublicKey, + ); + + expect( + derivedKey.equals( + new PublicKey('9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq'), + ), + ).toBe(true); +}); + +test('createProgramAddress', async () => { + const programId = new PublicKey( + 'BPFLoader1111111111111111111111111111111111', + ); + const publicKey = new PublicKey( + 'SeedPubey1111111111111111111111111111111111', + ); + + let programAddress = await PublicKey.createProgramAddress( + [Buffer.from('', 'utf8'), Buffer.from([1])], + programId, + ); + expect( + programAddress.equals( + new PublicKey('3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT'), + ), + ).toBe(true); + + programAddress = await PublicKey.createProgramAddress( + [Buffer.from('☉', 'utf8')], + programId, + ); + expect( + programAddress.equals( + new PublicKey('7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7'), + ), + ).toBe(true); + + programAddress = await PublicKey.createProgramAddress( + [Buffer.from('Talking', 'utf8'), Buffer.from('Squirrels', 'utf8')], + programId, + ); + expect( + programAddress.equals( + new PublicKey('HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds'), + ), + ).toBe(true); + + programAddress = await PublicKey.createProgramAddress( + [publicKey.toBuffer()], + programId, + ); + expect( + programAddress.equals( + new PublicKey('GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K'), + ), + ).toBe(true); + + const programAddress2 = await PublicKey.createProgramAddress( + [Buffer.from('Talking', 'utf8')], + programId, + ); + expect(programAddress.equals(programAddress2)).toBe(false); +}); + +test('findProgramAddress', async () => { + const programId = new PublicKey( + 'BPFLoader1111111111111111111111111111111111', + ); + let [programAddress, nonce] = await PublicKey.findProgramAddress( + [Buffer.from('', 'utf8')], + programId, + ); + expect( + programAddress.equals( + await PublicKey.createProgramAddress( + [Buffer.from('', 'utf8'), Buffer.from([nonce])], + programId, + ), + ), + ).toBe(true); +});