ethereum / EIPs

The Ethereum Improvement Proposal repository
https://eips.ethereum.org/
Creative Commons Zero v1.0 Universal
12.9k stars 5.29k forks source link

ERC: Standard URI scheme with metadata, value and byte code #67

Closed alexvandesande closed 6 years ago

alexvandesande commented 8 years ago

This proposal is inspired by BIP 21 and could apply to IBAN address format but can be extended to other proposed addresses formats. Imagine these scenarios:

In all these scenarios, the provider wants to set up internally a transaction, with a recipient, an associated number of ethers (or none) and optional byte code, all without requiring any fuss from the end user that is expected simply to choose a sender and authorise the transaction.

Currently implementations for this are wonky: shape shift creates tons of temporary addresses and uses an internal system to check which one correspond to which metadata, there isn't any standard way for stores that want payment in ether to put specific metadata about price on the call and any app implementing contracts will have to use different solutions depending on the client they are targeting.

I propose adding, beyond address, also optional byte code and value to any proposed address standard. Of course this would make the link longer, but it should not be something visible to the user, instead it should be shown as a visual code (QR or otherwise), a link or some other way to pass the information.

If properly implemented in all wallets, this should make execution of contracts directly from wallets much simpler as the wallet client only needs to put the byte code by reading the qr code.

If we follow the bitcoin standard, the result would be:

 ethereum:<address>[?value=<value>][?gas=<suggestedGas>][?data=<bytecode>]

Other data could be added, but ideally the client should take them from elsewhere in the blockchain, so instead of having a label or a message to be displayed to the users, these should be read from an identity system or metadata on the transaction itself.

Example:

Clicking this link would open a transaction that would try to send 5 unicorns to address deadbeef. The user would then simply to approve, based on each wallet UI.

 ethereum:0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7?gas=100000&data=0xa9059cbb00000000000000000000000000000000000000000000000000000000deadbeef0000000000000000000000000000000000000000000000000000000000000005

Without byte code

Alternatively, the byte code could be generated by the client and the request would be in plain text:

 ethereum:<address>[?value=<value>][?gas=<suggestedGas>][?function=nameOfFunction(param)]

Example:

This is the same function as above, to send 5 unicorns from he sender to deadbeef, but now with a more readable function, which the client converts to byte code.

 ethereum:0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7?gas=100000&function=transfer(address 0xdeadbeef, uint 5)
Gustav-Simonsson commented 8 years ago

Sounds similar to https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki

alexvandesande commented 8 years ago

Thanks @Gustav-Simonsson , I added the reference to the main text and modified the title to reflect that!

axic commented 8 years ago

Why call the data field bytecode? It is less likely to be bytecode in such a transaction (although it could be), more likely to be arguments to a method.

Perhaps suggestedgas / mingas could be a useful optional field in that case.

alexvandesande commented 8 years ago

I just don't want to call the variable data. All variables hold data, it's as specific as calling it stuff.. ;-)

axic commented 8 years ago

@alexvandesande lets call it stuff or things :)

data might sound generic, yet that is how it is called in every interface. I just don't think bytecode is a good choice. Any other ideas? How about command/commands/cmd/arguments/args?

My reasoning is it only asks as (EVM) bytecode when deploying a contract, in every other scenario it cannot be bytecode as far as I know.

ethernomad commented 8 years ago

In the JavaScript API the parameter is called data. Probably best just stick with that.

How should the data field be encoded? In base64 like https://en.wikipedia.org/wiki/Data_URI_scheme ?

Smithgift commented 8 years ago

Two wei: I think we should use hexidecimal for these URIs if possible. Everywhere else we use hex, and it'll be easier to convert to and back.

axic commented 8 years ago

@Smithgift I'm all for the hexadecimal representation.

The above proposal uses the ICAP as the address. Would it make sense defining support multiple address formats? ICAP, address (incl. checksummed), etc?

killerstorm commented 8 years ago

Have you considered security implications of URIs with byte code?

Using an URI with a data field is akin to signing a message written by another party: you should either trust that party, or verify the message yourself.

Otherwise this can be used for CSRF-style attacks. E.g. suppose that you want to buy a donut, you get an URI from the merchant, check that value is reasonable and submit it.

But this transaction will steal your gold/shares/house, as it might encode an instruction to send your assets to someone else.

One way to address this is to segregate different assets into different addresses. If you use an address which only holds ether, nothing can be stolen. But this cannot be enforced by a wallet as a wallet might be unaware of a "special powers" an address might have.

E.g perhaps an address doesn't hold any assets, but is associated with your identity, which will give a third party to sign a petition on your behalf.

The only way to address this is to transfer funds to a fresh address first, which sounds kinda inefficient (now you need two transactions).

Other way to address this is to enable 'data' payments only to known whitelisted addresses which wallet can understand.

In principle it might be enough to display 'the meaning' of addresses which are known to the wallet. E.g. user wants to buy a donut, gets URI from a merchant and observes that it wants to do something with a gold contract. Understanding that buying a donut shouldn't in any way involve gold, user will cancel a transaction. Ideally if contract is recognized it should parse data and display details of the call being made.

But in this case there is a risk that a keypair is shared between several different wallets, and while wallet A knows that address X is a gold contract, wallet B doesn't, so it will happily sign a gold-stealing transaction thinking it's something new and harmless.

ShockiTV commented 8 years ago

I dont get what has 'data' with stealing anything from your account. You specify value and gas and it can play with just these on receiving end.

killerstorm commented 8 years ago

Data can specify instructions to transfer assets held using smart contracts. value is irrelevant here. Perhaps you should read more on CSRF, confused deputy attack, how Ethereum works etc.

It's basically like signing a document prepared by someone else without reading it: it can do arbitrary nasty things.

edmundedgar commented 8 years ago

We did a bit of this on the Reddit thread but I think the issue @killerstorm is talking about is common to any means of sharing the address of a contract, rather than being particular to sharing that address along with parameters. If you're signing what you think is a contract to sign up for a credit card and it's really a contract to give somebody your house, you're going to have a bad day.

Bringing parameters along too potentially makes it slightly easier to exploit, because if the "transfer your house" signature happened to be along a dotted line marked "function transferYourHouse()" that'll give the game away if you have to do some work to specify it, but whatever method we end up with for identifying contracts can't safely rely on everything important having an unambiguous function name.

axic commented 8 years ago

@killerstorm wouldn't any implementation display all these details and let them be approved by the user before doing anything? Wallets could decide to reject any URL which contains bytecode. Others could display what it does if it's for a known purpose (e.g. it would be easy to parse if it is for calls on the token interface).

killerstorm commented 8 years ago

@axic EIP specification should cover wallet's behavior. You can't just leave it up to wallet implementors to decide. Some of them might be not aware of security concerns.

alexvandesande commented 8 years ago

I don't think this issue is at all related to the uri scheme or bytecode but it's a general problem to any Ethereum client that works with dapps: it has to, as much as it can, display information about what the contract you are about to execute does.

There are many ways of doing those that wel be exploring on the wallet, and I hope the community comes with many others:

All these apply either we are talking about a transaction has been initiated by a js call in a dapp, a link or a qrcode.

On Apr 5, 2016, at 10:59, Edmund Edgar notifications@github.com wrote:

We did a bit of this on the Reddit thread but I think the issue @killerstorm is talking about is common to any means of sharing the address of a contract, rather than being particular to sharing that address along with parameters. If you're signing what you think is a contract to sign up for a credit card and it's really a contract to give somebody your house, you're going to have a bad day.

Bringing parameters along too potentially makes it slightly easier to exploit, because if the "transfer your house" signature happened to be along a dotted line marked "function transferYourHouse()" that'll give the game away if you have to do some work to specify it, but whatever method we end up with for identifying contracts can't safely rely on everything important having an unambiguous function name.

β€” You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub

niran commented 8 years ago

We probably need to add a way for apps to suggest a sending address with a from parameter. Authenticators that handle multiple accounts need a way to know which account was active in the app that generated the URL.

alexvandesande commented 8 years ago

But the whole point was that the "from" field would be set by the client. But I suppose "from" and some custom parameters could be useful

On Apr 25, 2016, at 21:54, Niran Babalola notifications@github.com wrote:

We probably need to add a way for apps to suggest a sending address with a from parameter. Authenticators that handle multiple accounts need a way to know which account was active in the app that generated the URL.

β€” You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub

niran commented 8 years ago

The client sets all the fields. They're just hints that can be ignored. from is particularly useful when keys are managed in a separate process from a running dapp, which will be a common choice for its security benefits. Simple payments don't need from, but voting for a DAO proposal needs to be done from the account that holds tokens, which the dapp already knows.

killerstorm commented 8 years ago

@alexvandesande You have been warned about security implications. If people will lose their property because of the way these URIs are handled you will be responsible for that.

@edmundedgar I think normally contracts are structured in such a way that sending ether to that contract without attached data cannot result in unexpected loss (aside from loss of the sent ether which is expected). Thus it's normally safe to sign a simple send transaction as potential risk is well understood and contained.

In other words, only an insane contract will do something destructive upon receiving ether. However, potential loss will be restricted to the value held within such an insane contract, which is normally zero.

So yes, adding an ability to attach data has serious security implication. Just because things aren't perfect from security perspectives right now doesn't mean it's OK to create new holes.

alexvandesande commented 8 years ago

@killerstorm the security implications you are suggesting also apply to the current model where the site builds a transaction in javascript and the client just confirms it. How is it specific to this EIP?

taoteh1221 commented 8 years ago

I agree with @killerstorm that data / bytecode should not be a URI parameter. You'd most likely be patching newly-discovered vulnerabilities on a monthly basis until the end of time...that's a hacker's field day to be able to add executable data to a URI. That's why web2 browsers don't allow it.

alexvandesande commented 8 years ago

It's not a general executable data, it's data meant to be executed only in the context of an EVM. Any vulnerability there that allow execution outside of that environment is a big issue for the ecosystem as a whole that should be patched, independently of the particular way the transaction was initiated.

killerstorm commented 8 years ago

@alexvandesande The difference is that when a site builds a transaction, wallet knows the context. It might associate a particular account with a particular context.

It can work similarly to the same-origin policy on the web: a web site can only work with its own data. E.g. code which is executed in the context of gmail.com is able to send emails in my name, but code is executed in the context of bash.org cannot.

If one uses URIs, the origin/context is not known to the wallet, so it doesn't know what account will be safe to use. It might ask the user about the context, e.g. "Did you receive this URI from a trusted source?", but users are generally bad with that kind of stuff.

alexvandesande commented 8 years ago

@killerstorm in that case, clients could implement variations of same origin policies. Mist will know where the link was clicked. If someone builds an ethereum extension, it would know the page origin too. If someone is building an app that accepts any external links, then it's up to that app to handle their policies.

And in all those cases it's essential for ethereum clients to show as much information as possible about the transaction as they can. A malicious app in Mist can also attempt to execute a transaction that will not do what it says it will do and we can't prevent it (if an app wants to receive payment in digix gold, it's acceptable for them to request a transaction from the digix contract, even thou they are not digix themselves).

pelle commented 8 years ago

I really like the function parameter. It's an elegant solution. I like others think it's a bad idea to even standardize a huge security hole like the bytecode parameter. The way tech journalists work once fraudsters figure out how to use it, it will unfortunately be a setback for Ethereum.

alexvandesande commented 8 years ago

@killerstorm would replacing pure data by the function parameters (recently added) address your concerns?

axic commented 8 years ago

If the bytecode field is a security risk, how are function parameters not? It surely helps a lot to show what the method name is without knowing the contract (as you cannot retrieve that from the method hash), but it wont reassure you that getBalance() is not actually a concealed transfer().

Wouldn't it make more sense having a security recommendation what wallets should and shouldn't do?

I do like having both data (bytecode) and function.

niran commented 8 years ago

Bytecode opens up the whole EVM and Solidity's function handling as an attack surface. I have a pretty good understanding of what can go wrong with malicious function parameters, but arbitrary malicious bytecode sent to a contract address isn't something I've ever looked into. If I were developing a key manager, I'd be doing my users a service by only signing transactions that I understand. The function interface should be the primary one, and I'd advise not supporting arbitrary bytecode until there's a use case for it. It's probably safe, but it's not just as safe as a function call.

niran commented 8 years ago

We should also consider extending the function syntax to allow nested functions. Consider the wallet contract's execute method. If my dapp wants to request approval for unicorn tokens, it can generate a URI with a function parameter of execute(address 0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7, uint 0, bytes 0x...). Authenticators can display more useful information if the nested transaction data is provided with the function syntax instead of raw bytes, like function approve(...). This doesn't improve security since arbitrary bytes will always be valid, but it allows authenticators to build a better user experience.

tayvano commented 8 years ago

@killerstorm

Using an URI with a data field is akin to signing a message written by another party: you should either trust that party, or verify the message yourself.

Maybe I'm confused, but how is this different than how people are handling it today? Right now if you have a contract where you want a user to send you ETH + data, or increase the gas, the contract owner posts the information and instructions on what to do. ie: Using Jaxx, do this. Using MyEtherWallet, do this. Using Mist, do this. Or command line do (eth.SendTransaction({ ... }). Paste this data. Change default gas to this. Etc.

By providing a URI you are providing a shortcut but it is not anything that isn't currently happening.

If there are security concerns with the data field, then they are concerns that should be addressed in a more broad scope than just this EIP.

killerstorm commented 8 years ago

@tayvano Well again, if people do it in a non-secure way now, the RIGHT thing to do is to find a way to fix it. (I.e. create UX which is both convenient and secure.) The WRONG thing to do to make it simpler to continue with non-secure practices. But that's just my opinion, feel free to ignore it. :)

@alexvandesande My gut feeling is that function parameters are much better, but the security can be assessed only in the context of wallet/browser security policies. Thus I think it will be better if URI scheme will be developed after wallet/browser developers describe their security policies in detail. Otherwise it might turn out that URI is fundamentally incompatible with security measures taken by the wallet, but users might quickly get used to such URIs so it will be hard to phase them out.

It might turn out that bytecode is actually fine if certain precautions are taken in the wallet (i.e. wallet will only allow bytecode for explicitly whitelisted contracts ABIs of which are known).

If you want to let people experiment with it and see what happens, perhaps you can add to the spec that a wallet MUST display a huge warning before signing a transaction with a provided bytecode. (i.e. wallet should ask user if URI comes from a trusted source and explain potential problems.)

But you can't let wallet developers to decide what to do, as they might simply ignore all security concerns and claim "oh, I just implemented the standard, I didn't know it enables this vulnerability".

Obviously, if the spec prescribes certain thing but wallets do another that's their problem...

edmundedgar commented 8 years ago

A middle way here might be to pass arbitrary data but only to a single, specially-named function. If you make the wallet pass arbitrary parameters but only to a method called callFromURLParameters() that makes this method of interacting with a contract opt-in by the contract developer.

hiddentao commented 8 years ago

Let's say wallet apps ask users for confirmation when a link is clicked. At some point users will probably ask for (and thus get) an option in apps to enable auto-confirmation of such URLs. So not having the ability to do bytecode but allowing plaintext functions because the latter is less risky makes no sense to me. Even if you just had the ability to send eth to an address the onus is still on the user and their wallet app to ensure that everything is kosher.

So all in all for me the security implications are a non-issue. I could give you a standard QR code which takes you a URL which shows a webpage that takes advantage of some browser flaw and installs malware on your machine. In other words a user should only click a link if they trust it in the first place.

I say the scheme should allow for maximum flexibility, i.e. bytecode and functions. The wallet app should then deciper as much as it can from the incoming URL and display the details to the user for confirmation. If a wallet app fails to do this then it's an insecure app and one would hope users would steer clear from it. We can atleast ensure that the Mist wallet does things as securely as possible, which I think will go some way towards encouraging other app developers to do the same.

alexvandesande commented 8 years ago

At some point users will probably ask for (and thus get) an option in apps to enable auto-confirmation of such URLs.

I disagree here, I don't see imagine in the future any situation where Mist would allow auto confirmations unless they were very strict (auto confirm these functions up to a certain level, but at that point this is just another way to get "pre-confirmation").

If a app developer needs to constantly sign messages and transactions (like swarm does) and this would be a burden to the user, then I would suggest them to refactor their app to create a private daemon that owns their own keys and runs autonomously, and request that the user only funds him enough.

danfinlay commented 8 years ago

Hi @alexvandesande, sorry for hopping in late, I felt like the major points were being made and needed some time to mature my own perspective.

Overall I like the format, and I think long term it could be a useful tool.

However, I also agree that at this early phase, we need to establish a baseline of trustworthiness of transactions before adding extra convenience. As a wallet designer, I think my users would benefit much more from having tools to conveniently analyze a transaction before approving it, than tools to give them more transactions to approve.

I've heard a lot of good ideas on that topic, here is my own personal brain dump on contract transparency.

Until we have tools to help users know exactly what they're signing in the first place, I think it is dangerously early to make it easier to submit transactions, so I'd prefer to advance on this front slowly.

Once we have at least a class of transactions that are easily verifiable (for example, maybe exchanging EIP-20 compatible tokens), we could start considering ways to warn/inform users when clicking less-transparent transaction links, and so we could start justifying new conveniences for the safer types of transactions.

A middle ground might be to allow the link format but without a data or params field at first, to give wallet developers time to build out the UIs to establish trust with data transactions first, since a basic eth transfer is about as transparent as it gets (as transparent as the to field's owner is known).

Also, on the params note: I think params can be deceptive, since they can be named whatever, so I'm a little uncomfortable about using them as a way to demonstrate trustworthiness, I'd personally hold them to the same security criteria as a data field.

alexvandesande commented 8 years ago

@flyswatter thanks for your comments. I agree on starting slowly and not building the data field at first. Having the function + params field will allow a wallet to have a small ABI that will enable it to know exactly which function is being called, as the function name and parameters are enough to build a byte code for the transaction.

Of course, someone could build a deceptive contract, that could fit standards like tokens but not execute the same way, so for example a transfer() could do some other code completely. The wallet could avoid this by whitelisting some addresses or trying to preview which changes that transaction would make on the blockchain.

danielmcclure commented 8 years ago

Found this discussion whilst looking to see if there was a Transfer URI scheme. At the very least the transfer function seems viable. For a non technical user all data interactions right now are a little daunting for independent verification. I can see why you wouldn't want to add automatic data fields just yet. Sites could just add copy/paste code alongside the URL in the meantime if they need data, which would add some friction for the average user to consider what they are doing.

ethereum:<address>[?value=<value>][?gas=<suggestedGas>]

coder5876 commented 8 years ago

I'm also very much in favor of being very explicit with function name, function parameters etc in the URL, rather than using a data field. The data field is inscrutable for the user, and I don't think there is ever a situation where the data field would signify anything else than a function call or a contract creation.

pelle commented 8 years ago

I would like to propose adding an optional callback_url parameter. This allows mobile wallets to integrate with dApps. I'm calling it callback_url as it's similar to what is used in OAuth2 flow.

Once a transaction is signed, the wallet can ask the user if he wants to return to the dApp. At this point the user is redirected with the full hex encoded transaction in a URL parameter tx.

I would recommend in this case that the wallet sends it to the network first, but it is out of scope I think to require that.

I have implemented this in a not yet public IOS app and it works very well.

alexvandesande commented 8 years ago

callback_url would be a great addition, I agree

On Mon, May 23, 2016 at 7:08 PM, Pelle Braendgaard <notifications@github.com

wrote:

I would like to propose adding an optional callback_url parameter. This allows mobile wallets to integrate with dApps. I'm calling it callback_url as it's similar to what is used in OAuth2 flow.

Once a transaction is signed, the wallet can ask the user if he wants to return to the dApp. At this point the user is redirected with the full hex encoded transaction in a URL parameter tx.

I would recommend in this case that the wallet sends it to the network first, but it is out of scope I think to require that.

I have implemented this in a not yet public IOS app and it works very well.

β€” You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/ethereum/EIPs/issues/67#issuecomment-221110882

Alex Van de Sande UX Designer

ajunge commented 8 years ago

Great idea!

tomw1808 commented 8 years ago

I like the idea. I don't need a callback_url tough, because I can wait for an event to be triggered - hope I am not mistaken there...

Question: Is there any progress ever since on that?

miohtama commented 8 years ago

Instead of callback_url check Web Payment API:

https://w3c.github.io/browser-payment-api/

It provides much richer interaction than URI handlers or callback, leading ultimately very smooth user experience. The downside is that browsers must offer specific API support.

Chrome is the first browser to support this for some credits card (?) https://developers.google.com/web/updates/2016/07/payment-request

chevdor commented 8 years ago

A little warning about QR Code and the size of the data expanding greatly with the bytecode: While they offer redundancy, the size of the data that can be encoded in a QRCode is limited for a given redundancy level. At lowest redundancy, the max size is 4,296 alphanum chars.

This is usually not a limitation for (even complex but reasonable) urls but adding bytecode may fo over this limit.

Some QR Code generators will allow you going over the limit and will automagically increase the number of rows/columns in the QR Code. While this allows storing the data you wished to store, this will end up with a QR Code harder to read for the low end devices.

Normal/common size static_qr_code_without_logo 2

Additional data (an extract of the DAO bytecode) static_qr_code_without_logo 4

jbenet commented 7 years ago

Is this still the latest on achieving a QR code standard? We need to use a QR code for easier payments. We see some wallets have started doing QR codes, but are different from each other. What's holding this standard up? this should be pushed or we'll have a fragmented space that's frustrating to deal with (for products and users).

tayvano commented 7 years ago

@jbenet I would actually really appreciate a dedicated issue / eip / pre eip / whatever specifically to discuss qr code specifics. We haven't finalized any decisions on MyEtherWallet but would love to discuss specs specifically relating to qr codes Bc there are a couple other considerations.

jbenet commented 7 years ago

@tayvano yeah-- i would be interested as well. I think solidifying the URI scheme should proceed in parallel and can probably happen now-- as most people are familiar with the uses/use cases.

tayvano commented 7 years ago

I agree 100%. Not sure if that final push will happen in this thread tho. πŸ˜‰

alex-miller-0 commented 7 years ago

+1 to dedicated QR code standardization. I think a cap on size of data would be acceptable, but would like to hear others' thoughts.

danfinlay commented 7 years ago

@frankiebee Did the QR research at MetaMask, and found a pretty compatible solution for us. Care to chime in?