| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | // @flow
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 10:41:18 -07:00
										 |  |  | import fs from 'mz/fs'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   Connection, | 
					
						
							|  |  |  |   BpfLoader, | 
					
						
							|  |  |  |   Transaction, | 
					
						
							|  |  |  |   sendAndConfirmTransaction, | 
					
						
							| 
									
										
										
										
											2020-04-15 21:26:19 +08:00
										 |  |  |   Account, | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | } from '../src'; | 
					
						
							|  |  |  | import {mockRpcEnabled} from './__mocks__/node-fetch'; | 
					
						
							|  |  |  | import {url} from './url'; | 
					
						
							| 
									
										
										
										
											2019-03-05 17:52:13 -08:00
										 |  |  | import {newAccountWithLamports} from './new-account-with-lamports'; | 
					
						
							| 
									
										
										
										
											2020-08-25 09:05:33 -07:00
										 |  |  | import {BPF_LOADER_PROGRAM_ID} from '../src/bpf-loader'; | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | if (!mockRpcEnabled) { | 
					
						
							|  |  |  |   // The default of 5 seconds is too slow for live testing sometimes
 | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  |   jest.setTimeout(120000); | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-22 15:16:12 -07:00
										 |  |  | const NUM_RETRIES = 100; /* allow some number of retries */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  | test('load BPF C program', async () => { | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  |   if (mockRpcEnabled) { | 
					
						
							|  |  |  |     console.log('non-live test skipped'); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  |   const data = await fs.readFile('test/fixtures/noop-c/noop.so'); | 
					
						
							| 
									
										
										
										
											2019-10-22 15:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 13:01:10 -05:00
										 |  |  |   const connection = new Connection(url, 'recent'); | 
					
						
							| 
									
										
										
										
											2020-01-15 14:04:26 -07:00
										 |  |  |   const {feeCalculator} = await connection.getRecentBlockhash(); | 
					
						
							| 
									
										
										
										
											2019-10-22 15:16:12 -07:00
										 |  |  |   const fees = | 
					
						
							|  |  |  |     feeCalculator.lamportsPerSignature * | 
					
						
							|  |  |  |     (BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES); | 
					
						
							| 
									
										
										
										
											2019-12-11 00:38:20 -07:00
										 |  |  |   const balanceNeeded = await connection.getMinimumBalanceForRentExemption( | 
					
						
							|  |  |  |     data.length, | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const from = await newAccountWithLamports(connection, fees + balanceNeeded); | 
					
						
							| 
									
										
										
										
											2019-10-22 15:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-15 21:26:19 +08:00
										 |  |  |   const program = new Account(); | 
					
						
							| 
									
										
										
										
											2020-08-25 09:05:33 -07:00
										 |  |  |   await BpfLoader.load(connection, from, program, data, BPF_LOADER_PROGRAM_ID); | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  |   const transaction = new Transaction().add({ | 
					
						
							| 
									
										
										
										
											2019-11-06 10:42:01 -07:00
										 |  |  |     keys: [{pubkey: from.publicKey, isSigner: true, isWritable: true}], | 
					
						
							| 
									
										
										
										
											2020-05-20 11:57:51 -07:00
										 |  |  |     programId: program.publicKey, | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-06-03 19:55:42 +08:00
										 |  |  |   await sendAndConfirmTransaction(connection, transaction, [from], { | 
					
						
							| 
									
										
										
										
											2020-09-08 13:12:47 +08:00
										 |  |  |     commitment: 'single', | 
					
						
							| 
									
										
										
										
											2020-06-03 19:55:42 +08:00
										 |  |  |     skipPreflight: true, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  | describe('load BPF Rust program', () => { | 
					
						
							| 
									
										
										
										
											2019-05-10 14:16:35 -07:00
										 |  |  |   if (mockRpcEnabled) { | 
					
						
							|  |  |  |     console.log('non-live test skipped'); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 13:01:10 -05:00
										 |  |  |   const connection = new Connection(url, 'recent'); | 
					
						
							| 
									
										
										
										
											2019-10-22 15:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |   let program: Account; | 
					
						
							|  |  |  |   let signature: string; | 
					
						
							|  |  |  |   let payerAccount: Account; | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |   let programData: Buffer; | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   beforeAll(async () => { | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |     programData = await fs.readFile( | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |       'test/fixtures/noop-rust/solana_bpf_rust_noop.so', | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const {feeCalculator} = await connection.getRecentBlockhash(); | 
					
						
							|  |  |  |     const fees = | 
					
						
							|  |  |  |       feeCalculator.lamportsPerSignature * | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |       (BpfLoader.getMinNumSignatures(programData.length) + NUM_RETRIES); | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     const balanceNeeded = await connection.getMinimumBalanceForRentExemption( | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |       programData.length, | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     payerAccount = await newAccountWithLamports( | 
					
						
							|  |  |  |       connection, | 
					
						
							|  |  |  |       fees + balanceNeeded, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |     // Create program account with low balance
 | 
					
						
							|  |  |  |     program = await newAccountWithLamports(connection, balanceNeeded - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // First load will fail part way due to lack of funds
 | 
					
						
							|  |  |  |     const insufficientPayerAccount = await newAccountWithLamports( | 
					
						
							|  |  |  |       connection, | 
					
						
							|  |  |  |       2 * feeCalculator.lamportsPerSignature * 8, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const failedLoad = BpfLoader.load( | 
					
						
							|  |  |  |       connection, | 
					
						
							|  |  |  |       insufficientPayerAccount, | 
					
						
							|  |  |  |       program, | 
					
						
							|  |  |  |       programData, | 
					
						
							|  |  |  |       BPF_LOADER_PROGRAM_ID, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await expect(failedLoad).rejects.toThrow('Transaction was not confirmed'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Second load will succeed
 | 
					
						
							| 
									
										
										
										
											2020-08-25 09:05:33 -07:00
										 |  |  |     await BpfLoader.load( | 
					
						
							|  |  |  |       connection, | 
					
						
							|  |  |  |       payerAccount, | 
					
						
							|  |  |  |       program, | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  |       programData, | 
					
						
							| 
									
										
										
										
											2020-08-25 09:05:33 -07:00
										 |  |  |       BPF_LOADER_PROGRAM_ID, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-09-08 13:12:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     const transaction = new Transaction().add({ | 
					
						
							|  |  |  |       keys: [ | 
					
						
							|  |  |  |         {pubkey: payerAccount.publicKey, isSigner: true, isWritable: true}, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       programId: program.publicKey, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-08 13:12:47 +08:00
										 |  |  |     signature = await sendAndConfirmTransaction( | 
					
						
							|  |  |  |       connection, | 
					
						
							|  |  |  |       transaction, | 
					
						
							|  |  |  |       [payerAccount], | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         skipPreflight: true, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   test('get confirmed transaction', async () => { | 
					
						
							|  |  |  |     const parsedTx = await connection.getParsedConfirmedTransaction(signature); | 
					
						
							|  |  |  |     if (parsedTx === null) { | 
					
						
							|  |  |  |       expect(parsedTx).not.toBeNull(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const {signatures, message} = parsedTx.transaction; | 
					
						
							|  |  |  |     expect(signatures[0]).toEqual(signature); | 
					
						
							|  |  |  |     const ix = message.instructions[0]; | 
					
						
							|  |  |  |     if (ix.parsed) { | 
					
						
							|  |  |  |       expect('parsed' in ix).toBe(false); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       expect(ix.programId.equals(program.publicKey)).toBe(true); | 
					
						
							|  |  |  |       expect(ix.data).toEqual(''); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-03 19:55:42 +08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-06 19:16:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |   test('simulate transaction', async () => { | 
					
						
							|  |  |  |     const simulatedTransaction = new Transaction().add({ | 
					
						
							|  |  |  |       keys: [ | 
					
						
							|  |  |  |         {pubkey: payerAccount.publicKey, isSigner: true, isWritable: true}, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       programId: program.publicKey, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-08-06 19:16:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     const {err, logs} = ( | 
					
						
							|  |  |  |       await connection.simulateTransaction(simulatedTransaction, [payerAccount]) | 
					
						
							|  |  |  |     ).value; | 
					
						
							|  |  |  |     expect(err).toBeNull(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (logs === null) { | 
					
						
							|  |  |  |       expect(logs).not.toBeNull(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(logs.length).toBeGreaterThanOrEqual(2); | 
					
						
							|  |  |  |     expect(logs[0]).toEqual(`Call BPF program ${program.publicKey.toBase58()}`); | 
					
						
							|  |  |  |     expect(logs[logs.length - 1]).toEqual( | 
					
						
							|  |  |  |       `BPF program ${program.publicKey.toBase58()} success`, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   test('simulate transaction without signature verification', async () => { | 
					
						
							|  |  |  |     const simulatedTransaction = new Transaction().add({ | 
					
						
							|  |  |  |       keys: [ | 
					
						
							|  |  |  |         {pubkey: payerAccount.publicKey, isSigner: true, isWritable: true}, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       programId: program.publicKey, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 14:04:09 +08:00
										 |  |  |     simulatedTransaction.setSigners(payerAccount.publicKey); | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     const {err, logs} = ( | 
					
						
							|  |  |  |       await connection.simulateTransaction(simulatedTransaction) | 
					
						
							|  |  |  |     ).value; | 
					
						
							|  |  |  |     expect(err).toBeNull(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (logs === null) { | 
					
						
							|  |  |  |       expect(logs).not.toBeNull(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(logs.length).toBeGreaterThanOrEqual(2); | 
					
						
							|  |  |  |     expect(logs[0]).toEqual(`Call BPF program ${program.publicKey.toBase58()}`); | 
					
						
							|  |  |  |     expect(logs[logs.length - 1]).toEqual( | 
					
						
							|  |  |  |       `BPF program ${program.publicKey.toBase58()} success`, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   test('simulate transaction with bad programId', async () => { | 
					
						
							|  |  |  |     const simulatedTransaction = new Transaction().add({ | 
					
						
							|  |  |  |       keys: [ | 
					
						
							|  |  |  |         {pubkey: payerAccount.publicKey, isSigner: true, isWritable: true}, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       programId: new Account().publicKey, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 14:04:09 +08:00
										 |  |  |     simulatedTransaction.setSigners(payerAccount.publicKey); | 
					
						
							| 
									
										
										
										
											2020-08-11 14:35:56 +08:00
										 |  |  |     const {err, logs} = ( | 
					
						
							|  |  |  |       await connection.simulateTransaction(simulatedTransaction) | 
					
						
							|  |  |  |     ).value; | 
					
						
							|  |  |  |     expect(err).toEqual('ProgramAccountNotFound'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (logs === null) { | 
					
						
							|  |  |  |       expect(logs).not.toBeNull(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(logs.length).toEqual(0); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-09-23 22:54:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   test('reload program', async () => { | 
					
						
							|  |  |  |     expect( | 
					
						
							|  |  |  |       await BpfLoader.load( | 
					
						
							|  |  |  |         connection, | 
					
						
							|  |  |  |         payerAccount, | 
					
						
							|  |  |  |         program, | 
					
						
							|  |  |  |         programData, | 
					
						
							|  |  |  |         BPF_LOADER_PROGRAM_ID, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ).toBe(false); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-23 20:56:54 -07:00
										 |  |  | }); |