def test_transaction_invalid_accounts(self): keys = generate_keys(2) tx = Transaction.new( keys[0].public_key, [Instruction( keys[1].public_key, bytes([1, 2, 3]), [ AccountMeta.new(keys[0].public_key, True) ], )], ) tx.message.instructions[0].program_index = 2 # invalid index with pytest.raises(ValueError): Transaction.unmarshal(tx.marshal()) tx = Transaction.new( keys[0].public_key, [Instruction( keys[1].public_key, bytes([1, 2, 3]), [ AccountMeta.new(keys[0].public_key, True) ], )], ) tx.message.instructions[0].accounts = bytes([2]) # invalid index with pytest.raises(ValueError): Transaction.unmarshal(tx.marshal())
def create_account(subsidizer: PublicKey, address: PublicKey, owner: PublicKey, lamports: int, size: int) -> Instruction: """ Account references 0. [WRITE, SIGNER] Funding account 1. [WRITE, SIGNER] New account CreateAccount { // Number of lamports to transfer to the new account lamports: u64, // Number of bytes of memory to allocate space: u64, // Address of program that will own the new account owner: Pubkey, } """ data = bytearray() data.extend(Command.CREATE_ACCOUNT.to_bytes(4, 'little')) data.extend(lamports.to_bytes(8, 'little')) data.extend(size.to_bytes(8, 'little')) data.extend(owner.raw) return Instruction( _PROGRAM_KEY, data, [ AccountMeta.new(subsidizer, True), AccountMeta.new(address, True), ], )
def transfer( source: PublicKey, dest: PublicKey, owner: PublicKey, amount: int, token_program: PublicKey ) -> Instruction: """ // Accounts expected by this instruction: // // * Single owner/delegate // 0. `[writable]` The source account. // 1. `[writable]` The destination account. // 2. `[signer]` The source account's owner/delegate. // // * Multisignature owner/delegate // 0. `[writable]` The source account. // 1. `[writable]` The destination account. // 2. `[]` The source account's multisignature owner/delegate. // 3. ..3+M `[signer]` M signer accounts. :return: """ data = bytearray() data.append(Command.TRANSFER) data.extend(amount.to_bytes(8, 'little')) return Instruction( token_program, data, [ AccountMeta.new(source, False), AccountMeta.new(dest, False), AccountMeta.new(owner, True), ] )
def close_account(account: PublicKey, dest: PublicKey, owner: PublicKey) -> Instruction: return Instruction(PROGRAM_KEY, bytes([Command.CLOSE_ACCOUNT]), [ AccountMeta.new(account, False), AccountMeta.new(dest, False), AccountMeta.new_read_only(owner, True), ])
def test_transaction_duplicate_keys(self): payer, program = generate_keys(2) keys = generate_keys(4) data = bytes([1, 2, 3]) # Key[0]: ReadOnlySigner -> WritableSigner # Key[1]: ReadOnly -> ReadOnlySigner # Key[2]: Writable -> Writable (ReadOnly,noop) # Key[3]: WritableSigner -> WritableSigner (ReadOnly,noop) tx = Transaction.new( payer.public_key, [ Instruction( program.public_key, data, [ AccountMeta.new_read_only(keys[0].public_key, True), # 0 ReadOnlySigner AccountMeta.new_read_only(keys[1].public_key, False), # 1 ReadOnly AccountMeta.new(keys[2].public_key, False), # Writable AccountMeta.new(keys[3].public_key, True), # WritableSigner # Upgrade keys [0] and [1] AccountMeta.new(keys[0].public_key, False), # Writable (promote to WritableSigner) AccountMeta.new_read_only(keys[1].public_key, True), # Signer (promote to ReadOnlySigner) # 'Downgrade' keys [2] and [3] (should be noop) AccountMeta.new_read_only(keys[2].public_key, False), # ReadOnly; still Writable AccountMeta.new_read_only(keys[3].public_key, False) # Readonly; still a WritableSigner ], ), ] ) # Intentionally sign out of order to ensure ordering is fixed tx.sign([keys[0], keys[1], keys[3], payer]) assert len(tx.signatures) == 4 assert len(tx.message.accounts) == 6 assert tx.message.header.num_signatures == 3 assert tx.message.header.num_read_only_signed == 1 assert tx.message.header.num_read_only == 1 message = tx.message.marshal() for idx, key in enumerate([payer, keys[0], keys[3], keys[1]]): assert key.public_key.verify(message, tx.signatures[idx]) expected_keys = [payer, keys[0], keys[3], keys[1], keys[2], program] for idx, account in enumerate(expected_keys): assert tx.message.accounts[idx] == account.public_key assert tx.message.instructions[0].program_index == 5 assert tx.message.instructions[0].data == data assert tx.message.instructions[0].accounts == bytes([1, 3, 4, 2, 1, 3, 4, 2])
def test_transaction_missing_blockhash(self): payer, program = generate_keys(2) tx = Transaction.new( payer.public_key, [Instruction( program.public_key, bytes([1, 2, 3]), [ AccountMeta.new(payer.public_key, True), ], )], ) assert Transaction.unmarshal(tx.marshal()) == tx
def set_authority(account: PublicKey, current_authority: PublicKey, authority_type: AuthorityType, new_authority: Optional[PublicKey] = None) -> Instruction: data = bytearray([Command.SET_AUTHORITY, authority_type]) if not new_authority: data.append(0) else: data.append(1) data.extend(new_authority.raw) return Instruction(PROGRAM_KEY, data, [ AccountMeta.new(account, False), AccountMeta.new_read_only(current_authority, True), ])
def initialize_account(account: PublicKey, mint: PublicKey, owner: PublicKey) -> Instruction: """ // Accounts expected by this instruction: // // 0. `[writable]` The account to initialize. // 1. `[]` The mint this account will be associated with. // 2. `[]` The new account's owner/multisignature. // 3. `[]` Rent sysvar :return: """ return Instruction(PROGRAM_KEY, bytes([Command.INITIALIZE_ACCOUNT]), [ AccountMeta.new(account, False), AccountMeta.new_read_only(mint, False), AccountMeta.new_read_only(owner, False), AccountMeta.new_read_only(system.RENT_SYS_VAR, False), ])
def create_associated_token_account( subsidizer: PublicKey, wallet: PublicKey, mint: PublicKey, ) -> Tuple[Instruction, PublicKey]: addr = get_associated_account(wallet, mint) return Instruction( ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_KEY, bytes(), [ AccountMeta.new(subsidizer, True), AccountMeta.new(addr, False), AccountMeta.new_read_only(wallet, False), AccountMeta.new_read_only(mint, False), AccountMeta.new_read_only(system.PROGRAM_KEY, False), AccountMeta.new_read_only(PROGRAM_KEY, False), AccountMeta.new_read_only(system.RENT_SYS_VAR, False), ], ), addr
def test_transaction_single_instruction(self): payer, program = generate_keys(2) keys = generate_keys(4) data = bytes([1, 2, 3]) tx = Transaction.new( payer.public_key, [Instruction( program.public_key, data, [ AccountMeta.new_read_only(keys[0].public_key, True), AccountMeta.new_read_only(keys[1].public_key, False), AccountMeta.new(keys[2].public_key, False), AccountMeta.new(keys[3].public_key, True), ], )], ) tx.sign([keys[0], keys[3], payer]) assert len(tx.signatures) == 3 assert len(tx.message.accounts) == 6 assert tx.message.header.num_signatures == 2 assert tx.message.header.num_read_only_signed == 1 assert tx.message.header.num_read_only == 2 message = tx.message.marshal() assert payer.public_key.verify(message, tx.signatures[0]) assert keys[3].public_key.verify(message, tx.signatures[1]) assert keys[0].public_key.verify(message, tx.signatures[2]) expected_keys = [payer, keys[3], keys[0], keys[2], keys[1], program] for idx, key in enumerate(expected_keys): assert tx.message.accounts[idx] == key.public_key assert tx.message.instructions[0].program_index == 5 assert tx.message.instructions[0].data == data assert tx.message.instructions[0].accounts == bytes([2, 4, 3, 1])
def test_transaction_cross_impl(self): pk = PrivateKey(bytes([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 255, 101, 36, 24, 124, 23, 167, 21, 132, 204, 155, 5, 185, 58, 121, 75])) program_id = PublicKey(bytes([2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2])) to = PublicKey(bytes([1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1])) tx = Transaction.new( pk.public_key, [ Instruction( program_id, bytes([1, 2, 3]), [AccountMeta.new(pk.public_key, True), AccountMeta.new(to, False)], ), ], ) tx.sign([pk]) generated = base64.b64decode(_RUST_GENERATED_ADJUSTED) assert tx.marshal() == generated assert Transaction.unmarshal(generated) == tx
def test_transaction_multi_instruction(self): payer, program, program2 = generate_keys(3) keys = generate_keys(6) data = bytes([1, 2, 3]) data2 = bytes([3, 4, 5]) # Key[0]: ReadOnlySigner -> WritableSigner # Key[1]: ReadOnly -> WritableSigner # Key[2]: Writable -> Writable(ReadOnly, noop) # Key[3]: WritableSigner -> WritableSigner(ReadOnly, noop) # Key[4]: n / a -> WritableSigner # Key[5]: n / a -> ReadOnly tx = Transaction.new( payer.public_key, [ Instruction( program.public_key, data, [ AccountMeta.new_read_only(keys[0].public_key, True), # ReadOnlySigner AccountMeta.new_read_only(keys[1].public_key, False), # ReadOnly AccountMeta.new(keys[2].public_key, False), # Writable AccountMeta.new(keys[3].public_key, True), # WritableSigner ], ), Instruction( program2.public_key, data2, [ # Ensure keys don't get downgraded in permissions AccountMeta.new_read_only(keys[3].public_key, False), # ReadOnly, still WriteableSigner AccountMeta.new_read_only(keys[2].public_key, False), # ReadOnly, still Writable # Ensure upgrading works AccountMeta.new(keys[0].public_key, False), # Writable (promote to WritableSigner) AccountMeta.new(keys[1].public_key, True), # WritableSigner (promote to WritableSigner) # Ensure other accounts get added AccountMeta.new(keys[4].public_key, True), # WritableSigner AccountMeta.new_read_only(keys[5].public_key, False), # ReadOnly ], ), ] ) tx.sign([payer, keys[0], keys[1], keys[3], keys[4]]) assert len(tx.signatures) == 5 assert len(tx.message.accounts) == 9 assert tx.message.header.num_signatures == 5 assert tx.message.header.num_read_only_signed == 0 assert tx.message.header.num_read_only == 3 message = tx.message.marshal() for idx, key in enumerate([payer, keys[0], keys[1], keys[3], keys[4]]): assert key.public_key.verify(message, tx.signatures[idx]) expected_keys = [payer, keys[0], keys[1], keys[3], keys[4], keys[2], keys[5], program, program2] for idx, account in enumerate(expected_keys): assert tx.message.accounts[idx] == account.public_key assert tx.message.instructions[0].program_index == 7 assert tx.message.instructions[0].data == data assert tx.message.instructions[0].accounts == bytes([1, 2, 5, 3]) assert tx.message.instructions[1].program_index == 8 assert tx.message.instructions[1].data == data2 assert tx.message.instructions[1].accounts == bytes([3, 5, 1, 2, 4, 6])
def memo_instruction(data: str) -> Instruction: return Instruction( PROGRAM_KEY, bytes(data, 'utf-8'), )