Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contract creation transactions don't specify a salt #326

Closed
jannikluhn opened this issue Jan 29, 2018 · 18 comments
Closed

Contract creation transactions don't specify a salt #326

jannikluhn opened this issue Jan 29, 2018 · 18 comments
Labels

Comments

@jannikluhn
Copy link
Contributor

What is wrong?

Contract creation transactions don't specify a salt (see https://github.com/ethereum/py-evm/blob/sharding/evm/vm/forks/sharding/vm_state.py#L60-L63). As a result, the address of the created contract is given by the bytecode only and thus it's impossible to deploy the same contract twice.

How can it be fixed

Specify the salt somehow. Potential candidates could be

  • the first/last 32 bytes of transaction.code
  • the first/last 32 bytes of transaction.data
  • transaction.to (which is meaningless for contract creation transactions right now)
@NIC619
Copy link
Contributor

NIC619 commented Jan 29, 2018

One can append random bytes(which won't affect contract initialization process) to the bytecode as salt.

@NIC619
Copy link
Contributor

NIC619 commented Jan 31, 2018

@pipermerriam @hwwhww @jannikluhn
It seems cleaner if we just add salt to transaction format, as suggested by @vbuterin and to conform with suggested transaction format of main chain in this EIP.

@NIC619
Copy link
Contributor

NIC619 commented Feb 1, 2018

I will make the PR that together removes gas_price and add salt to Sharding transaction format once #285 (PAYGAS) is merged.

@jannikluhn
Copy link
Contributor Author

What's wrong with reusing transaction.to/target for this? The salt actually defines the address (at least in part), so target isn't too forced. I think this would be better than adding another field which is empty for most transactions.

@NIC619
Copy link
Contributor

NIC619 commented Feb 1, 2018

Since transaction.to is where the contract will be deployed(EVM will check if transaction.to == generate_CREATE2_contract_address(*salt*, transaction.code) ), if we reuse transaction.to as salt it will result in cyclic dependency, i.e, transaction.to is used as salt for calculating address but also has to be the calculated address.

If I understand correct, transaction.target is not part of the formal transaction format. Hence it seems to me that it's not a reliable source as a salt.

@jannikluhn
Copy link
Contributor Author

Yes, that check would have to be removed. But it's not necessary anyway, right now transaction.to is redundant information.

@NIC619
Copy link
Contributor

NIC619 commented Feb 1, 2018

If I understand correctly, transaction.to is still needed especially if it's not a contract deploying transaction?

@jannikluhn
Copy link
Contributor Author

Yeah, sorry, transaction.to is only redundant for contract deployments. For normal transactions it's still required of course. But for those we don't need a salt.

@hwwhww
Copy link
Contributor

hwwhww commented Feb 1, 2018

Reusing transaction.to looks economical. 😄 But kind of confusing for the normal users, like the reason why V want to rename transaction.to to transaction.target. On the other hand, a little optimization in Transaction would improve a lot...

One thing different is the length of salt for randomness, would that be an issue?

Vote for reusing if we can well-documented that. Also, ping @vbuterin to take a look.

By the way, we have to open an issue for renaming BaseShardingTransaction.to to BaseShardingTransaction.target right?

@NIC619
Copy link
Contributor

NIC619 commented Feb 1, 2018

@hwwhww You can't reuse transaction.to as salt because that would result in cyclic dependency. IMO a salt field is more straightforward to users and they don't have to spend time on crafting the data which is to be reused.

I can update transaction.to along with other transaction fields that are to be added/removed.

@pipermerriam
Copy link
Member

pipermerriam commented Feb 1, 2018

EIP232 proposes an alternate format for transactions which might be the right solution for this. The following is a simplified version of the proposed change.

txn = [version, data_elements]
if txn.version == 1:
    nonce, gas_price, gas, to, value, data, v, r, s = data_elements
elif txn.version == 2:
    nonce, gas_price, gas, to, value, data, v, r, s, chain_id = data_elements
elif txn.version == 3:
    gas, to, target, whatever, other_fields = data_elements

I know it adds one-more-protocol-change to sharding, but it is also the solution I've seen for handling this issue which will only get worse over time.

@hwwhww
Copy link
Contributor

hwwhww commented Feb 1, 2018

@NIC619 sorry for that I missed one comment...! You're right!

@vbuterin
Copy link

vbuterin commented Feb 2, 2018

I'm ok with adding salt into the transaction; either adding it as an extra data field or the EIP 232 approach.

@jannikluhn
Copy link
Contributor Author

@pipermerriam I'm not sure I understand how EIP232 would be applied here. Like this:

normal_transaction = [0, [chain_id, shard_id, to, data, gas, access_list]]
cc_transaction = [1, [chain_id, shard_id, code, data, salt, gas, access_list]]

? What I'd like about this is that we don't ever have any unused fields. What I wouldn't like is that it's called "version", "format" may be better. I think with some changes to rlp.Serializable to automate the "expansion" this approach could work quite well.

@pipermerriam
Copy link
Member

I'm good with renaming version to something else. I agree that automating the inner expansion also makes a lot of sense and should make this API quite transparent, allowing us to even access the inner properties from a singular outer generic transaction class.

Here is a rough sketch of what I'm thinking of for this.

class EIP232Transaction(rlp.Serializable):
    fields = (
        ('version', ...),
        ('inner_data', sedes.binary),
    (
    transaction_formats = {
        1: TransactionFormatA,
        2: TransactionFormatB,
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.txn_data = rlp.decode(self.inner_data, sedes=self.transaction_formats[self.version])

    def __getattr__(self, name):
        return getattr(self.txn_data, name)

We have an outer transaction class which contains a mapping of version numbers to inner transaction classes. Then, during initialization, we decode the outer data packet, and then lookup the appropriate inner transaction class and use it to decode the inner data packet. Then we put some convenience APIs on the outer class to allow direct property access to the inner transaction properties.

@NIC619
Copy link
Contributor

NIC619 commented Feb 9, 2018

EIP232 looks like a cleaner and better approach in the long term but I suppose it's not going to be as trivial to implement as adding a salt field? What about adding salt field for now(short term) and starts an issue about implementing EIP232?

@pipermerriam
Copy link
Member

@NIC619 sounds good to me.

@hwwhww
Copy link
Contributor

hwwhww commented Mar 27, 2018

close via #422

@hwwhww hwwhww closed this as completed Mar 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants