I came from a non-CS background, which means that my exposure to low-level programming was mediocre. It ranged from overclocking GPU’s to squeeze out more ETH when mining, to hacking Raspberry Pi’s for small electronic experiments. My first two major professional experiences were in the world of web development and cloud computing, where I learned about networking, security, and systems design from literally building all of them dozens of times for clients.
Here’s what I wish I had context on before learning Solidity:
Bytecode is another version of machine code, which is the code that an interpreter or a compiler outputs to store in RAM for when another program calls it to execute a function. When you get into function signatures (not private key signatures), you will need to compare the bytecode in your smart contract against the bytecode in your tests. Finding this out with Ethers JS is no easy feat. It took me 4 days to get the proper conversion between calldata as bytes in my Typescript tests and in the smart contract (yes, I did destroy a lot of things out of frustration).
At the end of the day, transpilers, compilers, interpreters, etc on all the “-ers”, all just translate one memory management system to another. They answer the question- well, how can I execute this program with my own? How can I translate my low-level memory operations to interface with your memory operations?
Each of these memory management systems (programming languages) have different types, and expect those “types” (which are just memory structures) to match up. I would say almost 75% of my time is just figuring out what the fuck data type the program needs, reading through the docs, and then filling it out. Absolute waste of time.
Most of the things you read about security are all derivative of the same thing- how do I make sure this data is non-replicable, or unreadable to someone who intercepts it. Hashing and signing are both parts of this process, though hashing can be used for a storage mechanism (like a key in an object/hash-map).
Signing is just verifying that the message came from the correct source, which boils down to identity verification. When you need to verify someone in the real world, you look at their ID and make sure that ID is real. In computers, you use their signature on the raw data and make sure that it is in-line with their public key.
Again, not a lot of low-level memory management experience, so I didn’t have any context coming into this on how to bit shift and move around memory. All told, offsets basically just mean the number of bytes that a section of program consumes- so if we have “hello”, it’ll be an “offset” of 5 bytes. This can also be called “length”, which makes way more sense. Should just be called length.
Opcodes exist in just a hash map between what we view as an action for the processor and its expected energy consumption. For instance, jumping to a location costs X amount in electricity or CPU clock cycles (easier to know at a computer level). The EVM opcodes are no different. When you type Solidity, it is actually being reduced to these operations, all of which have an associated cost (gas).