The Auth Store Quest we solved on the last post was quite the feat! We will now tackle the next quest: Reverse Engineer.
Understanding README.md
As always, let's open the README.md
file in the 3-reverse-engineer
folder and take a look.
This quest is quite different! We are tasked with finding the contract ID of the already deployed contract. We have an important tip though, we are given the deployer account's public key, which is GAC7HE4PQWI7H34JQN6QLQ7Y7NTAUZDWJZ4SJ6GXVC4K2DN7N65K5NLI
.
The Concept of Public Ledgers
Since we are transacting on a blockchain, all of our transactions are fully inspectable by anyone! Aside from privacy chains (Monero, Zcash, etc.), all of the transactions, meaning contract deployments, coin transfers, account creations are all easily inspectable! This is the case, as the validators should check the integrity of all the data that the blockchain has processed to achieve consensus between all nodes. Of course, there are some other ways such as zk-proofs, which hide the details of the transactions while making them still verifiable!
The so called "privacy" components of the blockchains comes from the idea that we do not know who the public key's owner is. This can be quite handy for privacy, but is also not the exact solution to the privacy problem as the transactions of a user can be mapped to a person with enough information, such as IP addresses, their social media sharings and so on.
Setting Up Our Stellar Quest Account
As always, use sq login
if you haven't, and use sq play 2
to start questing. Don't forget to fund your account!
Examining the Contract
Let's check out the lib.rs
file in the src
folder. The contract is quite short, but has some different concepts than previous contracts. Let's check it out.
pub struct ReverseEngineerContract;
const SECRET : Symbol = symbol!("dancinRaph");
#[contractimpl]
impl ReverseEngineerContract {
pub fn submit(_: Env, secret: Symbol) -> bool{
secret == SECRET
}
}
We see that there is only one function, submit
, which takes the argument secret
of type Symbol
. If you remember, the Symbol
type can be thought as a string
with a maximum length of 10.
Focus on the following line:
const SECRET : Symbol = symbol!("dancinRaph");
We can see that a constant value is defined here, which is not modifiable by any means using any functions in the contract, or elsewhere. The SECRET
constant is also of type Symbol
, and has the value of "dancinRaph"
.
Constants and Secrets in Blockchains
Since the blockchain ledgers are actually public, we can't have secret values by just naming them "SECRET" and not telling anyone. Having the source code of course makes guessing the value trivial, but anyone can reverse engineer any contract's source code, and with some simple bytecode translations, view all of your values.
There are some ways of putting actual secret data on blockchains, such as hashing the values off-chain, and then submitting them. We can also check if another supplied value is correct, we can hash them on-chain, and compare the results. The wonders of cryptography!
Examining the Test
We only have one test function for this quest.
#[test]
fn test_q3() {
let env = Env::default();
let contract_id = env.register_contract(None, ReverseEngineerContract);
let client = ReverseEngineerContractClient::new(&env, &contract_id);
assert_eq!(client.submit(&symbol!("wrong")), false);
assert_eq!(client.submit(&SECRET), true);
}
What this test does is quite simple. It checks the output value by giving it a false input, and checks it again by giving it the correct input. We did not expect the contract to panic when we supply an invalid value, as it just simply returns the false value.
Solving the Quest
To even attempt solving the quest, we should first find the contract ID of the quest contract. After finding the contract ID, we should supply the secret keyword that we found as the secret
argument of the submit function, and solve the quest.
Stellar Laboratory
The Stellar Laboratory is a place for everything Stellar! You can check out all the endpoints of any Stellar network, even custom ones, and Build, Sign, and Decode transactions!
The Laboratory is definitely a very helpful place, but since we are programmers, and programmers are lazy, we don't want to leave our cozy terminal to solve the quests! Jokes aside, do check out the Stellar Laboratory to learn more about the Horizon API and other Stellar concepts.
Finding the Contract ID
Since the only information we have about the contract is the deployer account, let's check what the deployer account has done on the Futurenet. Let's communicate with the Horizon API from our terminal. Use
curl https://horizon-futurenet.stellar.org/accounts/GAC7HE4PQWI7H34JQN6QLQ7Y7NTAUZDWJZ4SJ6GXVC4K2DN7N65K5NLI/operations
To get some information about the deployer account. The output is quite large, but the interesting part of the JSON response is as follows.
{
--snip--
"transaction_successful": true,
"source_account": "GAC7HE4PQWI7H34JQN6QLQ7Y7NTAUZDWJZ4SJ6GXVC4K2DN7N65K5NLI",
--snip--
"transaction_hash": "b4ead070088ad86a32c341907d9944a4f3def6c7f1e42cd6e1789d10ca344c38",
--snip--
"function": "HostFunctionHostFnCreateContractWithSourceAccount",
"footprint": "AAAAAAAAAAEAAAAGTNbD4mfKK1nhwm9oUMF+GpFYPmEFgv/ujOvINx6UVzoAAAADAAAAAw=="
}
We see that the account GAC7HE4PQWI7H34JQN6QLQ7Y7NTAUZDWJZ4SJ6GXVC4K2DN7N65K5NLI
has used the function HostFunctionHostFnCreateContractWithSourceAccount
, which means that they successfully deployed a contract on the Soroban network. If we inspect the footprint using the soroban
CLI, we can get more information about the transaction.
soroban xdr dec \
--type LedgerFootprint \
--xdr AAAAAAAAAAEAAAAGTNbD4mfKK1nhwm9oUMF+GpFYPmEFgv/ujOvINx6UVzoAAAADAAAAAw== \
--output json | grep contractId
We got the contract ID!
XDR Types
eXternal Data Representation (XDR) is a format that is quite compact, reliable, and rich. Stellar ledger uses XDR formatted data to communicate with clients and store all kinds of data. They are shown base64 encoded, which compresses the data even more when communicating with clients. Check out Stellar Docs on XDR for more information.
Invoking the submit
Function
Now that we have found the contract ID, we can call the submit
function on the ReverseEngineerContract
.
soroban invoke \
--id 4cd6c3e267ca2b59e1c26f6850c17e1a91583e610582ffee8cebc8371e94573a \
--fn submit --arg dancinRaph
Success! Since we got true as the response, we know that we supplied the correct parameter as an argument. Use sq check 3
to claim your reward 🎉!
Top comments (1)
Hope you enjoyed this post!
If you want to support my work, you can send me some XLM at
GB2M73QXDA7NTXEAQ4T2EGBE7IYMZVUX4GZEOFQKRWJEMC2AVHRYL55V
Thanks for all your support!