I have a question about versioning of smart contracts in Ethereum. Lots of articles have been written about how to decouple your logic and data, how to use interfaces when referencing other contracts, how to use generic key value stores to store data and retrieving it through library functions in a strongly typed way.
What about querying transactions and event log from old versions of the smart contract? Transactions and emitted events cannot be migrated. They are immutable. And perhaps you need to keep track of them in the client app for history. Events can be added, removed or have their signature changed in different contract versions. How do you deal with querying and decoding transaction input and event data for multiple versions of a contract?
Each event in Solidity is internally represented by a hash of its signature. Functions are also presented by the hash.
let encodedFunctionSignature = web3.eth.abi.encodeFunctionSignature('sendMessage(string,address)');
console.log(encodedFunctionSignature);
// => 0xc48d6d5e
The signatuare is first 32 bytes of keccak256 hash taken over by the function signature string, spaces removed.
If the event signature changes, the hash changes as well and you can distinct different ABIs for events.
Then you just need to have the matching version of ABI file to decode the event to human readable symbolic form.
Related
The Uniswap router contract has a few methods ending in *SupportingFeeOnTransferTokens, e.g. swapExactTokensForETHSupportingFeeOnTransferTokens.
https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router02.sol
I vaguely (think I) understand that these methods are supposed to be called for swapping tokens that somehow take a tax/rake during transfers.
I have a few questions regarding these:
In a nutshell, what is the difference in behavior for these functions different from the regular swap functions? Do they somehow interact with the minOut parameters?
How does the Dex UI know which method to call? How does it know the SupportingFee version needs to be called instead of the standard swap?
What happens if my web3 script calls the wrong version of the method? Failures? Wrong amounts out?
I am taking a udemy course and I encounter a code like this
https://github.com/acloudfan/Blockchain-Course-Basic-Solidity/blob/93ca256bcf8c436c144425291257dcff5c3b269f/test/constants_payable.js#L45
I am confuse why the call to a method is called directly instead of using .call or something, wherein if I do google, the way to call a method of a contract is either using .call or .send but at this point the author just calls it directly, is this allowed, why?
here is the contract code
https://github.com/acloudfan/Blockchain-Course-Basic-Solidity/blob/master/contracts/ConstantsPayable.sol
More or less, what is the context of calling smart contract method from a truffle test here? is it like the real environment where it waits for the transaction to be mined before returning or do tests just directly calls it like an ordinary function?
I am posting it here since the author of the udemy course is non responsive and its almost a week and more than a dozen Q&A question are not answered, so the author probably is busy or forgets about the course already (as it is kinda old course but reviewed well).
Before Truffle returns the contract instance (line 41), it uses the ABI interface (provided by the Solidity compiler) to build a map of JS functions for interacting with the contract, including receiveEthers().
what is the context of calling smart contract method from a truffle test here
Even though Truffle JS tests can be connected to a public testnet or mainnet, it's usually used together with another Truffle tool - local EVM and blockchain emulator called Ganache (see the config file where the author defines connection to a local blockchain). By default, Ganache mines a block after each transaction so that you (as a developer or a tester) don't need to worry about mining and other processes in setting up the network, and the response from the local blockchain it returned almost instantly.
if I do google, the way to call a method of a contract is either using .call or .send
Answering only about Truffle. Other packages such as Web3js or Ethers.js might have slightly different rules. And there are .call() and .send() methods in Solidity (for interacting with other contracts or addresses), that also behave differently than explained here:
You can interact with a contract in two different ways:
transactions (can make state changes - change contract storage, emit events)
calls (only read the contract data - no state changes)
By default, if you don't specify whether you want to make a transaction or a call, Truffle makes a transaction. You can override this decision and make a call instead by using the .call() method.
The .send() method is only used for low-level built transactions. A common use case is sending ETH - you need to build the transaction data field, fill the (ETH) value, and call the .send() method (assuming you have configured Truffle to use your private key to sign the transaction).
I have a Google Assistant action with fulfilment through Firebase Cloud Functions. I understand that Cloud Functions may share instances between invocations, and that you can use the Global scope to do heavy lifting and preparation. My function instantiates a global class that has serialised some JSON and handles returning data and other tasks in my function. I have variables in this class that are set when the function is called, and I have been careful to make sure that the variables are all set using the conv.data session data object that is unique to the current conversation. The hope is that although the class instance may exist between different invocations, and possibly by different users, it will still be contextualised to the local scope, and I wont see any variables being overwritten by other sessions.
Which brings me to the question, which is, how can I test this? I have tried to test on my mobile device using the Google Assistant app, at the same time as testing in the browser console. I witnessed the two sessions getting merged together, and it was an unholy mess, but I am not sure if that was the global scope, or just that I was testing two sessions with the same user account.
Can anyone enlighten me on whether it is possible to run two of the same action using the same user account? It looked like the conv.data object had a mix of the two different sessions I was running which suggests it was using the same conversation token for both sessions.
Another question would be, do you think using a global class to store state across invocations is going to be an issue with different users? The docs do state that only one invocation of the function can ever happen at a time. So there shouldn't be any race condition type scenarios.
Dialogflow should keep the data in conv.data isolated to a single session, even sessions from the same user. When you're using Dialogflow, this data is stored in a Context, which is session specific.
You can verify this by turning StackDriver logging on, which will let you examine the exact request and response that Dialogflow is using with your fulfillment, and this will include the session ID for tracking. (And if you think it is mixing the two, posting the request and response details would help figure out what is going on.)
Very roughly, it sounds like you're getting something mixed into your global, or possibly something set in one session that isn't cleared or overwritten by a different one. Again - seeing the exact requests and responses should help you (and/or us) figure that out.
My attitude is that a global such as this should be treated as read-only. If you want to have some environment object that contains the relevant information for just this session - I'd keep that separate, just from a philosophical design.
Certainly I wouldn't use this global state to store information between sessions. While a function will only be invoked, I'm not sure how that would work with Promises - which you'll need once you start any async operations. It also runs the risk that subsequent invocations might be on different instances.
My approach, in short, (which I make pretty firm in multivocal):
Store all state in a Context (which conv.data should so).
Access this via the request, conv, or some other request-specific object that you create.
Global information / configuration should be read-only.
I would like to store some sensitive data within my contract in form of mapping
Using Solidity ^0.5
mapping(someKey => someStruct) someMap;
I'm aware of the privacy issue within smart contracts, the compiler automatically creates getter functions for all public state variables, but when it comes to mappings (or any dynamically sized variables), how can someone access them (by default), if not, is there any risk to reverse engineer the EVM in order to read the data.
Thanks for help!
When building a blockchain application what is the best practice for persisting the data in a non-ethereum app for the purpose of displaying state on a website (such as a backing postgres database).
Some specific questions:
Why is Bulk access to lists/arrays/etc is described as being painful in Solidity? relevant
What is the best way to capture blockchain events and update an off-chain database.
What is the best way to verify the integrity of your off-chain database (how frequently etc.)
Can you avoid using a backing database and query the blockchain directly?
In the truffle pet shop tutorial they have a view that returns the entire chain
getAdopters() public view returns (address[16])
http://truffleframework.com/tutorials/pet-shop
Why is Bulk access to lists/arrays/etc is described as being painful in Solidity?
There's a few reasons for this:
There is no built in mechanism for iterating through collections in Solidity. You can do a standard for loop with an array using it's length, but you can't do this with mappings.
Returning complex objects is messy. If your list contains a struct, you can't simply return the struct itself to your client. You have to return the item decomposed. Converting the struct to a decomposed array is ugly:
struct MyStruct {
uint256 id;
bytes32 name;
}
MyStruct[] _structs;
function getAllStructs() public constant returns (uint256[], bytes32[]) {
uint256[] memory ids = new uint256[](_structs.length);
bytes32[] memory names = new bytes32[](_structs.length);
for (uint i = 0; i < _structs.length; i++) {
ids[i] = _structs[i].id;
names[i] = _structs[i].name;
}
return (ids, names);
}
Iterating through arrays still requires gas. Even if you're not paying for the gas (when performing this in a constant function), you can still have out of gas exceptions when your arrays become very large. The Truffle pet shop example gets away with this because it explicitly limits the array to 16 elements.
What is the best way to capture blockchain events and update an off-chain database.
"Best" way depends on your business goals and tolerance for stale data. But, probably the most common way is to set up a watch on contract events. When you receive an event, you update your DB with the custom data you stuff into the event. However, you have to make sure to handle orphaned blocks as well (there is a field in the metadata called removed which tells you if an event is no longer valid because of a block being orphaned). When you receive an orphaned event, remove it from the DB. After 12 subsequent block verifications come in, you can safely assume the event will not be orphaned.
What is the best way to verify the integrity of your off-chain database (how frequently etc.)
Again, this depends on tolerance levels of your business requirements. If you can delay using information from the DB until you're certain of block verifications, then you would simply persist block or timestamp information that lets your app know that the data in your DB mirrors what is verified on the blockchain. If you're concerned about a client process responsible for watching events has failed, you need to have failover watch clients, or allow duplicate persistence (with subsequent deduping), or track verified block numbers as the come in (or some combination of the 3). I'm sure there are plenty of other options you can architect for this as well.
Can you avoid using a backing database and query the blockchain directly?
Yes, it possible, assuming you can avoid gas limit issues as mentioned above with constant functions and you don't have to do any complex post processing of your data inside your contract for your application. Since constant functions run within the local EVM, you just need to make sure your dedicated node is up and running. To do this, you'd most likely want multiple servers running as fully synced nodes.