I am currently experimenting a bit with Solana accounts and I am wondering if it is possible to change the owner of an account.
I am curious it is possible since the security of some programs are relying on this owner check, as explained here
I am also looking into the assign function and on how it works, but I was not able to make it work yet.
Am I misunderstanding something or just was not able to make it work?
Here the Python code is used:
tx = Transaction().add(
create_account(CreateAccountParams(
from_pubkey=attacker_keypair.public_key,
new_account_pubkey=account_keypair.public_key,
lamports=client.get_minimum_balance_for_rent_exemption(0)["result"],
space=0,
program_id=attacker_keypair.public_key,
))
)
send_and_confirm_tx(client, tx, attacker_keypair, account_keypair)
print('Sending 1st tx to program')
tx = Transaction().add(TransactionInstruction(
keys=[
AccountMeta(pubkey=account_keypair.public_key, is_signer=False, is_writable=False),
],
program_id=PROGRAM_ID,
))
send_and_confirm_tx(client, tx, attacker_keypair)
print('Sending 2nd tx to program')
tx = Transaction().add(assign(AssignParams(
account_pubkey=account_keypair.public_key,
program_id=attacker2_keypair.public_key
)))
send_and_confirm_tx(client, tx, account_keypair)
Error message is: InvalidAccountForFee
account_keypair was already assigned to attacker_keypair, so when you try to use it as the fee payer in 2nd tx, the runtime yells at you, since account_keypair can only have its lamports reduced by the program living at attacker_keypair. To quickly fix your current issue, you can do:
print('Sending 2nd tx to program')
tx = Transaction().add(assign(AssignParams(
account_pubkey=account_keypair.public_key,
program_id=attacker2_keypair.public_key
)))
send_and_confirm_tx(client, tx, attacker_keypair, account_keypair)
This will cause another problem however. account_keypair is already owned by attacker_keypair, so only a program deployed to attacker_keypair can reassign ownership of account_keypair. You can read up more on the Solana Account model at: https://docs.solana.com/developing/programming-model/accounts#ownership-and-assignment-to-programs
The part that's interesting to you is:
The owner is a program id. The runtime grants the program write access to the account if its id matches the owner. For the case of the System program, the runtime allows clients to transfer lamports and importantly assign account ownership, meaning changing the owner to a different program id. If an account is not owned by a program, the program is only permitted to read its data and credit the account.
This means that to reassign ownership, you need to write an on-chain program that reassigns the ownership to attacker2_keypair, deploy it to attacker_keypair, and then send a transaction containing an instruction to attacker_keypair.
Here are some example programs that perform the assign on the AccountInfo: https://github.com/solana-labs/solana/blob/85a2e599bbbf3d51f201167f921718e52c7ce59f/programs/bpf/rust/realloc/src/processor.rs#L54
Related
Try to transfer tokens to one of the account declared in remaining_accounts list.
Here's the way I create CpiContext:
let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.account_x.to_account_info(),
to: ctx.remaining_accounts.first().unwrap().to_account_info(),
authority: ctx.accounts.owner.to_account_info().clone()
});
I got error related to CpiContext lifetime mismatch. Exact log error: lifetime mismatch ...but data from ctx flows into ctx here.
Why I want to use remaining accounts to transfer tokens? This transfer is optional depending on whether user decides to pass the account (referral links/affiliation). Other methods than passing account as remaining accounts to implement the optional transfer will be also highly appreciated.
Make sure that you help out Rust's lifetime inference in your instruction:
pub fn your_instruction<'info>(ctx: Context<'_, '_, '_, 'info, YourContext<'info>>, ...)
My smart contract owns the SFT HAT-a1a1a1-01.
The SFT HAT-a1a1a1-02 also exists but isn't owned by the SC.
When I add local quantity to the SFT with the 02 nonce through a function, my transaction fails with this error:
new NFT data on sender
Do I need to own the SFT HAT-a1a1a1-02 to mint it?
I got an answer in another group:
Yes, you need to own at least 1 to AddQuantity, since otherwise, you wouldn't have the attributes. When you send an NFT/SFT, if your balance after is 0, the metadata is erased from your account.
I want to delete the current contract and burn its NEAR balance when a condition is triggered.
Here's the Solidity version:
selfdestruct(address(0));
I found Promise::delete_account in the Rust SDK but it has a beneficiary_address field. Ideally the funds should be gone forever and not transferred to an owned address.
Promise::new(env::current_account_id()).delete_account(beneficiary_address);
address(0) is address 0x0, a black hole address used to burn Ether.
Currently there is no API to burn NEAR tokens directly. One workaround is to set the beneficiary account id to system. system is an account that can never be created and is used internally for refunds. When the beneficiary account does not exist, the tokens transferred through account deletion are automatically burnt.
I think that is like this:
#[payable]
pub fn burn() {
Promise::new("system".to_string()).transfer(env::attached_deposit());
}
First importing:
use near_sdk::{Promise};
I want to store all the blockchain data in offchain database.
rpc has a function called EXPERIMENTAL_changes, I was told that I can do that by http polling of this method but I am unable to find out how to use it.
http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=EXPERIMENTAL_changes \ params:='{ "changes_type": "data_changes", "account_ids": ["guest-book.testnet"], "key_prefix_base64": "", "block_id": 19450732 }'
For example here the results give:
"change": { "account_id": "guest-book.testnet", "key_base64": "bTo6Mzk=", "value_base64": "eyJwcmVtaXVtIjpmYWxzZSwic2VuZGVyIjoiZmhyLnRlc3RuZXQiLCJ0ZXh0IjoiSGkifQ==" }
What is key_base64?
Decoding it to string gives m::39
What is m::39?
For example, I have the following state data in the rust structure.
pub struct Demo {
user_profile_map: TreeMap<u128, User>,
user_products_map: TreeMap<u128, UnorderedSet<u128>>, // (user_id, set<product_id>)
product_reviews_map: TreeMap<u128, UnorderedSet<u128>>, // (product_id, set<review_id>)
product_check_bounty: LookupMap<u128, Vector<u64>>
}
How to know anything gets changed in these variables?
Will I have to check every block id for the point the contract is deployed, to know where there is the change?
I want to store all the blockchain data in offchain database.
If so, I recommend you take a look at the Indexer Framework, which allows you to get a stream of blocks and handle them. We use it to build Indexer for Wallet (keeps track of every added and deleted access key, and stores those into Postgres) and Indexer for Explorer (keeps track of every block, chunk, transaction, receipt, execution outcome, state changes, accounts, and access keys, and stores all of that in Postgres)
What is m::39?
Contracts in NEAR Protocol have access to the key-value storage (state), so at the lowest-level, you operate with key-value operations (NEAR SDK for AssemblyScript defines Storage class with get and set operations, and NEAR SDK for Rust has storage_read and storage_write calls to preserve data).
Guest Book example uses a high-level abstraction called PersistentVector, which automatically reads and writes its records from/to NEAR key-value storage (state). As you can see:
export const messages = new PersistentVector<PostedMessage>("m");
Guest Book defines the messages to be stored in the storage with m prefix, hense you see m::39, which basically means it is messages[39] stored in the key-value storage.
What is key_base64?
As key-value storage implies, the data is stored and accessed by keys, and the key can be binary, so base64 encoding is used to enable JSON-RPC API users with a way to query those binary keys as well (there is no way you can pass a raw binary blob in JSON).
How to know anything gets changed in these variables? Will I have to check every block id for the point the contract is deployed, to know where there is the change?
Correct, you need to follow every block, and check the changes. That is why we have built the Indexer Framework in order to enable community building services on top of that (we chose to build applications Indexer for Wallet and Indexer for Explorer, but others may decide to build GraphQL service like TheGraph)
I need an approach for switching from current fe_user into another fe_user (similar to backend "simulate user" does) - and back to origin user. The switching into another fe_user itself is not part of the problem. The question is, where to store the origin user (which user to switch back) information? The workflow looks like:
If fe_user X (uid: 123) is allowed to "switch" (e.g. member of a certain fe_usergroup)
Switch to user A
Switch to user B
...and so on...
Now switch back to "origin" user X (uid: 123)
The information about the origin user should not be stored into $GLOBALS['TSFE']->fe_user or subarray, i guess. Because that would make user A itself possible to switch back to X (uid: 123), although this user never "was" this user before. So the "switch back" information must be stored anywhere else. But where?
Got the answer myself. The trick is to store the information in session data of type "ses" (as "user" gets overwritten on each switch).
if(!$frontendUserAuthentication->getKey('ses', 'tx_ext_originuser')) {
$frontendUserAuthentication->setKey('ses', 'tx_ext_originuser', $frontendUserAuthentication->user['uid']);
$frontendUserAuthentication->storeSessionData();
}
You can use the session storage for getting the information saved. You can save the information for the main fe user in a user session key, which you can use as information, that this is a double session. By logging out, you are able to get the old user and re login.