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 test_create_associated_account(self): keys = [key.public_key for key in generate_keys(3)] expected_addr = get_associated_account(keys[1], keys[2]) instruction, addr = create_associated_token_account( keys[0], keys[1], keys[2]) assert addr == expected_addr assert len(instruction.data) == 0 assert len(instruction.accounts) == 7 assert instruction.accounts[0].is_signer assert instruction.accounts[0].is_writable assert not instruction.accounts[1].is_signer assert instruction.accounts[1].is_writable for i in range(2, len(instruction.accounts)): assert not instruction.accounts[i].is_signer assert not instruction.accounts[i].is_writable assert instruction.accounts[4].public_key == system.PROGRAM_KEY assert instruction.accounts[5].public_key == PROGRAM_KEY assert instruction.accounts[6].public_key == system.RENT_SYS_VAR tx = Transaction.unmarshal( Transaction.new(keys[0], [instruction]).marshal()) decompiled = decompile_create_associated_account(tx.message, 0) assert decompiled.subsidizer == keys[0] assert decompiled.owner == keys[1] assert decompiled.mint == keys[2]
def test_memo_progam(self): data = 'somedata' i = memo_instruction(data) assert i.data.decode('utf-8') == data tx = Transaction.unmarshal(Transaction.new(PublicKey(bytes(32)), [i]).marshal()) memo = decompile_memo(tx.message, 0) assert memo.data.decode('utf-8') == data
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 test_set_authority(self): public_keys = [key.public_key for key in generate_keys(3)] # With no new authority instruction = set_authority(public_keys[0], public_keys[1], AuthorityType.AccountHolder, _token_program) assert instruction.data[0] == Command.SET_AUTHORITY assert instruction.data[1] == AuthorityType.AccountHolder assert instruction.data[2] == 0 tx = Transaction.unmarshal(Transaction.new(public_keys[0], [instruction]).marshal()) decompiled = decompile_set_authority(tx.message, 0, _token_program) assert decompiled.account == public_keys[0] assert decompiled.current_authority == public_keys[1] assert decompiled.authority_type == AuthorityType.AccountHolder assert not decompiled.new_authority # With new authority instruction = set_authority(public_keys[0], public_keys[1], AuthorityType.CloseAccount, _token_program, new_authority=public_keys[2]) assert instruction.data[0] == Command.SET_AUTHORITY assert instruction.data[1] == AuthorityType.CloseAccount assert instruction.data[2] == 1 assert instruction.data[3:] == public_keys[2].raw assert not instruction.accounts[0].is_signer assert instruction.accounts[0].is_writable assert instruction.accounts[1].is_signer assert not instruction.accounts[1].is_writable tx = Transaction.unmarshal(Transaction.new(public_keys[0], [instruction]).marshal()) decompiled = decompile_set_authority(tx.message, 0, _token_program) assert decompiled.account == public_keys[0] assert decompiled.current_authority == public_keys[1] assert decompiled.authority_type == AuthorityType.CloseAccount assert decompiled.new_authority == public_keys[2]
def test_initialize_account(self): public_keys = [key.public_key for key in generate_keys(3)] instruction = initialize_account(public_keys[0], public_keys[1], public_keys[2], _token_program) assert instruction.data == bytes([Command.INITIALIZE_ACCOUNT]) assert instruction.accounts[0].is_signer assert instruction.accounts[0].is_writable for i in range(1, 4): assert not instruction.accounts[i].is_signer assert not instruction.accounts[i].is_writable tx = Transaction.unmarshal(Transaction.new(public_keys[0], [instruction]).marshal()) decompiled = decompile_initialize_account(tx.message, 0, _token_program) assert decompiled.account == public_keys[0] assert decompiled.mint == public_keys[1] assert decompiled.owner == public_keys[2]
def test_create_account(self): public_keys = [key.public_key for key in generate_keys(3)] instruction = create_account(public_keys[0], public_keys[1], public_keys[2], 12345, 67890) assert len(instruction.data) == 52 assert instruction.data[0:4] == Command.CREATE_ACCOUNT.to_bytes( 4, 'little') assert instruction.data[4:12] == (12345).to_bytes(8, 'little') assert instruction.data[12:20] == (67890).to_bytes(8, 'little') assert instruction.data[20:] == public_keys[2].raw tx = Transaction.unmarshal( Transaction.new(public_keys[0], [instruction]).marshal()) decompiled = decompile_create_account(tx.message, 0) assert decompiled.funder == public_keys[0] assert decompiled.address == public_keys[1] assert decompiled.owner == public_keys[2] assert decompiled.lamports == 12345 assert decompiled.size == 67890
def test_transfer(self): public_keys = [key.public_key for key in generate_keys(3)] instruction = transfer(public_keys[0], public_keys[1], public_keys[2], 123456789, _token_program) assert instruction.data[0] == Command.TRANSFER assert instruction.data[1:] == (123456789).to_bytes(8, 'little') assert not instruction.accounts[0].is_signer assert instruction.accounts[0].is_writable assert not instruction.accounts[1].is_signer assert instruction.accounts[1].is_writable assert instruction.accounts[2].is_signer assert instruction.accounts[2].is_writable tx = Transaction.unmarshal(Transaction.new(public_keys[0], [instruction]).marshal()) decompiled = decompile_transfer(tx.message, 0, _token_program) assert decompiled.source == public_keys[0] assert decompiled.dest == public_keys[1] assert decompiled.owner == public_keys[2] assert decompiled.amount == 123456789
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])