Conversation
Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
…into StorageMap Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su> # Conflicts: # pallets/utxo/src/lib.rs # pallets/utxo/src/tests.rs
Signed-off-by: sinitcin <antony@email.su> # Conflicts: # pallets/utxo/src/lib.rs # pallets/utxo/src/tests.rs
Signed-off-by: sinitcin <antony@email.su>
…PointerToIssueToken Signed-off-by: sinitcin <antony@email.su>
…now is an OptionQuery. Signed-off-by: sinitcin <antony@email.su>
Signed-off-by: sinitcin <antony@email.su>
| * `calculating_reward` | ||
| * Just collecting MLT for a transaction reward. |
There was a problem hiding this comment.
I've understood that the reward can be paid in any token, is that still correct or is that a different kind of reward?
Btw, how do the code changes you've introduced in this PR relate to this proposal?
There was a problem hiding this comment.
I've understood that the reward can be paid in any token, is that still correct or is that a different kind of reward?
The last thing that I heard, for the test-net we should use only MLT as a reward.
Btw, how do the code changes you've introduced in this PR relate to this proposal?
Added the comment at the top. We need just discus over here.
|
Thanks for the write-up. In general, the breakdown into smaller validation steps you posted looks fine to me. I have, however, a couple of concerns about this approach. In particular, the First, values for some of the fields are not available from the start so we would have to come up with default values or them. This is error prone because the validation methods may accidentally rely on fields that have not been properly populated yet at the point. Second, the validation methods are passed more data than necessary. For example, the reward calculation only depends on the total input amounts and total output amounts, other fields are not relevant. It is a good practice to only let functions depend on information that they require. As a first step, I'd propose a simpler approach of just splitting the big monolithic function into a bunch of smaller ones, roughly like so: fn validate_transaction(tx) -> Result<ValidTransaction, &str> {
let output_total = check_outputs(tx.outputs)?;
let utxos = lookup_utxos(tx.inputs)?;
let input_total = check_inputs(tx.inputs, utxos)?;
check_destinations(tx.inputs, utxos)?;
let reward = calculate_reward(input_total, output_total).ok_or("sum outputs > sum inputs")?;
ensure!(check_time_lock(tx), "too soon");
// ... more stuff
ValidTransaction { tx, ... }
}All these functions return either GIven approaching testnet release, we need to think about how to get there without much disruption. Something like the following protocol may be reasonably good:
Thoughts/comments? |
Thank you, Lukas! To be honest, Ben and Sam have already mentioned that we do have no time for huge refactoring of this place. And today, I start splitting it almost like you explain. For the long run, I thought we might make priorities for each checking fn and then call them depending on it. But implementing stages of validation looks better. |
|
Commenting on what Lukas said:
This is what I meant when I said in the standup that this is "stateful programming". Generally speaking, stateful programming is the paradigm where you initialize a state and keep updating it. It's basically what "object oriented programming" tries to do. Unfortunately, most of the time in security applications, this is the worst way of doing things unless there's a good reason to do it that way. Why? Because testing such a design is basically hell on earth. There's theoretically an infinite possible combinations of how the fields of such a complex class/struct can be populated, in both order and values. You can never test everything! The better way is what Lukas mentioned. It's a form of functional programming. In functional programming, your program consists of inputs that go into functions, and outputs of these functions that go into other functions, and all functions are preferably pure (i.e., shouldn't depend on a global state). This way, we can test every individual functions through all possible inputs and outputs as much as possible, and everything is bounded in terms of possible states, and hence manageable. Thank you guys for the effort and discussion, and like Anton mentioned, this is not something we're gonna do now anyway. But I appreciate the initiative and efforts. |
Thank you too! Tokens would be impossible without your patronage. |
This description is still approximate and not accurate, we need to define an approach and agree on checks. DO NOT INSPECT THE CODE. LET'S JUST DISCUSS.
Draft TransactionVerifier
I suggest adding a structure that will contain:
This struct we will use this way in the pallet utxo:
When creating a new instance of this structure, we must initialize the fields.
Each subsequent check adds a new instance of the function to
set_of_checks, which will be called incollect_result.At the moment we can split the verification function for these parts:
checking_inputsu32::MAXchecking_outputsu32::MAXchecking_utxos_existschecking_signatureschecking_tokens_transferringchecking_tokens_issuedmetadata_uriandtickervalueanddecimalchecking_nft_mintmetadata_urichecking_assets_burncalculating_rewardcollect_resultQuestions
fn pick_utxoandfn send_to_address. Isn't that?I'm glad to see any suggestions or critics.