-
Notifications
You must be signed in to change notification settings - Fork 175
ethereumProvider API #16
Comments
I suggest that Why did you include those? Any thoughts on returning a Promise in Looks good! |
small nitpick |
changed to Hm yeah, if the provider always takes care of connecting and re-connecting automatically we wouldn't need Concerning promise. i would like to keep this lib small and a promise is an extra dependency. |
here's a version with the connection semantics in a separate subclass EthereumProvider : EventEmitter// payload: is a valid jsonrpc 2.0 object
// callback: a error first callback
ethereumProvider.send(payload, callback)
> undefined ethereumProvider.on('data', handler)
> undefined
// The subscription received in the handler:
{
"jsonrpc":"2.0",
"method":"eth_subscription",
"params": {
"subscription":"0x2acffa11d68280c2954daeb77cb849d9",
"result": {...}
}
} EthereumWebsocketProvider : EthereumProvider// Will try to connect, the ethereumProvider should be already connected when the webpage is loaded and firse the "connect" event at start.
ethereumProvider.connect()
> undefined // react on connection start
ethereumProvider.on('connect', handler) // react on connection end
ethereumProvider.on('end', handler) |
My biggest ask is that dapp developers not have to code for specific provider types. For instance, a dapp developer should not have to maintain a specific code block for Metamask-style providers where no connection information needs to be managed, and another code block for Mist that, say hypothetically issues a provider where connection information does need to be managed. (If unclear, I interpret @kumavis's suggestion above as requiring dapp developers to do exactly that). Creating a library that supports that ask would require either we:
My vote is on a modified hybrid approach, as described below, as I think the current connection abstraction is too specific to a certain connection type, and exposes too many temporary error states. Instead, I think we should focus on whether a provider has been "started" and "stopped" -- i.e., is it ready to provide you the information you need, or not. Here's what I propose: // Implemented by all providers; is a no-op for providers that don't need to
// connect to anything or don't require any type of polling. If there's base
// code we decide on, it could implement this no-op by default.
//
// The callback is fired when the provider is successfully started.
// This is important for providers that work over an established connection
// or that are in-process and connect to a database backend.
ethereumProvider.start(callback) Similarly, we could implement a stop method that can also be a no-op if not necessary. // The callback is fired when the connection is successfully stopped.
// Important for established connections as well as database backends.
ethereumProvider.stop(callback) Sending data is the same as the examples above, but the handler has changed (more on that below): // payload: is a valid jsonrpc 2.0 object
// callback: see below
ethereumProvider.send(payload, callback) Also, // returns payload and response, see handler details below
ethereumProvider.on('data', handler) As well, the // Will return an error as a result of any payload; this includes connection
// errors as well as rpc errors. See below for handler details.
ethereumProvider.on('error', handler) All callbacks and handlers on function(err, payload, response) {
// connection error
// payload (request)
// response: response body returned by the RPC server
} Note that An http provider pointed to an invalid non-responsive or incorrect url, for example, would expose connection errors in the I see all of this discussion as akin to |
@tcoulter you seemed to have misunderstood my proposal. I specified a standard |
its not a specific transport, its a feature of the ethereum json rpc built on top of json rpc notifications.
its nice having just a handler for server sent notifications, but interesting idea |
for the sake of simplicity I advocate for |
I went down that path too when I originally started writing my original comment. If you as a dapp developer want anything extra it means you have to explicitly code for a specific environment (i.e., the websocket provider), which is why I later chose the hybrid/more general abstraction approach. However, if the custom provider classes are meant to only be used as a fallback, then I could get into that.
👍 Good to know, thanks. I find the general |
Ok sorry for the late response, but i was on a holiday. @tcoulter @kumavis So concerning exposing no connection data: Or the dapp developer itself must be able to act on connection drops as well. Its not feasable to try to mitigate all of that under the hood in the EthereumProvider itself. Concerning the generic This makes building libs like web3.js a lot more complicated, as each of those libraries needs to handle the mapping between request and responses and do the mapping of JSON rpc IDs themselves. This is easily taken away by Concerning the This is necessary to force regaining connection tries, but we can get rid of it, if we make sure to call the Concerning Separate WS provider The ethereumProvider above is on purpose generic for all type of providers, be it HTTP, IPC or WS under the hood. Its kind of its own type and can be used in web3 like libs, as they would a WS provider. If So given the above feedback i would suggest the following API: // payload: is a valid jsonrpc 2.0 object
// callback: a error first callback
ethereumProvider.send(payload, callback)
> undefined // returns subscription notification,
ethereumProvider.on('data', handler)
// The subscription received in the callback:
> {
"jsonrpc":"2.0",
"method":"eth_subscription",
"params": {
"subscription":"0x2acffa11d68280c2954daeb77cb849d9",
"result": {...}
}
} // react on connection start
ethereumProvider.on('connect', handler) // react on connection end
ethereumProvider.on('end', handler) |
What happened to your I'd much rather that each individual query is subscribable, allowing reactive state updates. Example:
Also, I think a single PR is too simplistic a form to discuss an entire spec. I would propose the spec proposal be a repository, and then individual features & changes could be pull requests, discussed individually. |
This here is about a basic provider provided by mist, metamask and others, not how web3.js or any other library on top will work ;) Off Topic: |
To get back to this and finalise it soon. @kumavis @tcoulter Here is an idea from @MaiaVictor which is what you @kumavis probably mentioned in one of these calls, to have a simple function object which can handle the calls, without exposing formatting the RPC calls yourself. I could imagine something like this, but with the subscription addition: |
@kumavis @tcoulter i talked to @MaiaVictor and took in his suggestions to strip away the RPC itself, so that we can in the future change the transport layer without the need for changes in this API here. The provider would take care of generating valid JSON and IDs and you only pass in RPC method names and get results back. The So i would propose the following: // payload: is a valid jsonrpc 2.0 object
// callback: a error first callback
ethereumProvider.send(methodName, params, callback)
> undefined
// CALLBACK return
> err, result // returns subscription notification,
ethereumProvider.on(subscriptionType, handler)
subscriptionType = 'eth_subscription' or 'shh_subscription'
// HANDLER return
> {
"subscription":"0x2acffa11d68280c2954daeb77cb849d9",
"result": {...}
} // react on connection start
ethereumProvider.on('connect', handler)
// HANDLER return
null // react on connection end
ethereumProvider.on('end', handler)
// HANDLER return
Error Object with message (timeout, error, drop, etc) |
I agree with that spec. I understood Just a small refinement, what you think of:
Or something like that? So it has a direct pairing with ~~ Looking forward to see what the other guys think, so we can just go ahead and work on top of this standard. |
I like the idea of |
As I'm thinking about this, all the proposals above (even mine) seem to extremely complicate the task of making an
If I wanted to create a simple provider for testing, i.e., as a mock provider within my application tests, there's a lot I'd have to create just for it to be compliant. I think this is too much of a burden. Instead, we should rally around one method, and have it be the only requirement to write an Ethereum provider: I agree with your concerns that transport level information is important. To get around this, I suggest we do two things:
var inherits = require("util").inherits;
var EventEmitter = require('events');
inherits(EventedProvider, EventEmitter);
function EventedProvider(provider) {
this.provider = provider;
}
EventedProvider.prototype.send = function(method, params, callback) {
var self = this;
this.provider.send(method, params, function(err, result) {
callback(err, result);
// Send all the information just in case a handler could use it.
self.emit(method, params, err, result);
});
} Of course, the above code is for Node, but it's very easily bundled for the browser. The above code would provide subscription events for As an added bonus of this much simpler specification, it means providers can be composed. For instance, I can make a caching provider that takes a different provider as input, and suddenly I have a caching layer with no extra work. Adding an event system and a mechanism for transport state change handling makes this much harder, as if you were to compose them you'd have to propagate those events all the way up. To sum up, the features desired by the above spec are good, but if we simplify the spec further we can reduce overhead and get a lot more benefit. We can still have all our cake and eat it too with regards to transport level changes, though, by adding the |
I agree with all you said. I mean, if you read my proposal, it is exactly what you proposed, Fabian, though, argued we need |
So, in short, I'm 99% in favor of your proposal (1% is that additional I'll be waiting to see what everyone else thinks. |
here is what i think most stacks will look like
web3.js has been more of the (high level userspace library) variety, with a lot of API surface and a lot of change. This meant cool features (like web3.Contract) but breaking changes. When standard APIs do this they break the web. So web3.js should continue to grow and experiment, and the standard should be at some lower level (I think we all agree so far). The goal here is to determine what the API standard for Ethereum Browser Environments should be, and thus the actual transport between the app using this API and the logic that process those requests is out of scope. That narrows our layer options to:
providing something at the the current web3 provider behaves like the The So what do we do? If our goal is to establish an standard Ethereum Browser API, then the The Which of those two im leaning towards changes day to day. |
I agree that it's unusual just how pure this new proposed standard is. It's almost an over-reaction to criticisms of the web3 library's size. No other browser standard wraps multiple other methods, from callbacks to events, into a single function. This method would make the API almost completely useless to people not importing external client libraries, which is a new direction for a web API. That said, it definitely achieves the goal of "minimal surface area" for user-space libraries, and gives developers a lot of room for future iteration, and keeps page load time minimal. Maybe it's worth considering: Which minimal API is more useful to developers? While an So while I can't really complain with the current proposal as a very low-level access method, I think at least slightly longer term I will be in favor of some kind of global object that has methods for each current feature. |
I think @tcoulter's concern is also valid, part of this spec is to make it easy for people to develop clients against it. Since that layer is easier to implement, and the other layer can be built on top of it, maybe we really can just make the pure-function API first, standardize it, and then start experimenting with other APIs to inject as well. |
Thanks for the analysis, @kumavis. I agree with your categories. In an ideal world, where the RPC API wasn't supposed to ever change, the best choice would be to standardize the eth api layer, as you put it. But, as you said, it isn't as stable and new RPC methods will be added. This is confusing me, though. We're assuming the request handler layer (what we're proposing here) is somehow more stable and flexible than eth api. Why, though? There are two types of changes. Non-backwards compatible changes (e.g., removing To be honest, at this point, I'm deeply confused with all the options and mindsets here. If I was alone in doing that I'd, before anything, focus on designing an RPC API that wouldn't need to change. I'd then just expose the API to the browser (i.e., something like kumavi's Now I feel like I'm being more destructive than productive on this conversation. I don't want to interfere anymore. I'm happy with most of those solutions. In the end, none of those details is a major issue. But losing time is. Let's not have our own blockchain size debate. I'd say, let's go with Fabian's last proposal. He has good reasons to think it is good, and, perfect or not, it is clearly sufficient. I just hope we can find some consensus soon. |
@MaiaVictor you are entirely correct about breaking changes with method removal ( and method behavior change ). One difference though is that if a new rpc method is introduced, it would be accessible via the json-rpc interface but not the eth API. Perhaps the happy medium is something very close to what we have now:
🤣 yes I agree -- though its damn hard to change things once people start using them so lets try to get it right this time |
Ok now read through all of that. So first of a few informations, because i think not everybody caught that yet. The reason why is to standardise notification streams, which in the new API we already have:
And we could add a lot more. The old filter was not flexible to add more and unclear to understand, we all just got used to. The current way means, duplex streams are the new default, e.g. WS and IPC So given that fact we have now push, and this needs to be part of the standard a I also really like @MaiaVictor idea to strip away the RPC layer, as its not really necessary to know how methods on nodes are called, but just that you can call them. I also like the last approach i posted, eg. Therefore i really like the last approach. Its simple clean and allows for flexibility. Concerning the
|
Yeah I'm basically fine with that. The bonus method that @kumavis mentioned that I'd support is if the RPCs also had an But that could be its own proposal. In the meanwhile this is a nice low level provider api. |
Sounds good, let's plan a time. |
Hey @frozeman, we had a conversation with @wanderer about this proposal, and our long-term goals of enabling connecting to multiple shards/VMs over the main API, and we came up with a single very simple change to this proposal that would enable those goals. Introducing MetaProvider (name up for discussion)MetaProvider is one additional object, and this is the object that owns the // .connect() could default to the main Ethereum network.
var ethereumProvider = metaProvider.connect(`blockchain://${genesisHash}/block/${laterHash}`)
ethereumProvider.on(‘connect’, () => {
ethereumProvider.send(payload, handleSendResult)
})
ethereumProvider.on('error', (reason) => {
// reason.message could be "The requested blockchain is not supported by this client.")
}) |
Sounds interesting, but mist only allows for one blockchain to run in the background, due to the nature of local nodes, and it would be too resource intensive to run multiple testbetworks as well. How would we keep cross platform compatibility? if some dapps have to check for metaProvider and other for ethereumProvider? don't you think the start logic should be that big (which is that this provider wants to simplify) I also choose the name "ethereumProvider" to allow for future additions of other providers. Maybe better than would be a: ethereumProvider.connect(`blockchain://${genesisHash}/block/${laterHash}`, function(err, worked)) While its connected by default to the network of the browser, dapps can request to change it. the "connect" and "end" events, are mainly for cases when the user switches the network in the browser (outside of the dapp), to inform the dapps that connections changed. And to check for the network, dapps or libs have to provide a function, not the provider (see: https://github.com/ethereum/web3.js/blob/1.0/packages/web3-eth/src/getNetworkType.js#L27) Scratch the above ^ EDIT: maybe another important point is maybe we should just create a custom The reason is that this provider: This provider is not only to connect to an ethereum node, but gives access to the WHOLE ethereum ecosystem, like swarm, whisper and ethereum ( Whisper and swarm don't care about the blockchain they are on, they work differently. I don't think the ethereum network should get a special position on the provider. By simply using a custom RPC method ( @kumavis @wanderer @FlySwatter @tcoulter |
Also very important to me is that dapps wont need to call any connect event. The connection is already available by default when the dapps starts, so that they can immediately call any function like: ethereumProvider.send('eth_accounts', [], function) |
I updated the title post API |
Another change could be we name it ethereum.shh
> true
ethereum.bzz
> "http://localhost:8500"
ethereum.eth
> true
ethereum.personal
> false
ethereum.noYetKnown
> undefined
ethereum.send
> function
ethereum.on
> function
...
// all other event listener functions like `once` etc should also be there
**EDIT** As swarm works mainly through HTTP, we can expose the HTTP endpoint on its property. If there is no `ethereum` or `ethereum.bzz` then users can connect to the gateway at `http://swarm-gateways.net` |
One of our hopes is that eventually we could have multiple providers in the same window, like multiple shard clients. Maybe the connect method could return a fresh provider for the requested chain, or error? |
👍 for the rename. |
@FlySwatter The problem is that this will be hard to solve in Mist and status.im which actually run nodes. You also don't load a website and tell the browser to connect to the internet or some ethernet. If so you would do that in some custom logic, which is what a custom rpc function like |
I edited the comment, above to reflect handling of swarm endpoints. As they don't go through the provider, we need to add the url there. I will talk to @zelig how spam if your local node is prevented. |
Hello all, Good discussion. I would agree with the two proposed things:
However it is important to differ between both and discuss them separately. To the first point you suggest an Regarding the second point, in |
Ok maybe i didn't explain fully. This way the subscription don't differ from other function calls, except that there is a way to receive notifications from the node. To unsubscribe call |
Ok. Which advantage do we gain by splitting up the process? |
thats what it does, but you need the |
@kumavis @tcoulter @danfinlay @MaiaVictor @kaikun213 As i just now wanted to finally add the Im thinking along this lines: // callback: a error first callback
ethereum.sendBatch(requests, callback)
> undefined
requests = [{method: 'eth_something', parameters: [1, 2]}, {...}, {...}]
// CALLBACK return
> err, result
result = [{result: result1}, {error: Error('some error')}, {result: result3}, ....] Matching on the client (library) side happens here through the array indexes. The What you guys think? |
Initial reactions are 👍. I was thinking we could alternatively (or also?) overwrite Otherwise looks fine from here! |
@tcoulter pinged us Trufflers to weigh in. For those who don't know me, I maintain Ganache, and have done so since October 2017. As such I'm missing some of the legacy of Ethereum development, but I've definitely been around the block w.r.t these sort of discussions. From my perspective, I'm not sure I fully understand the goal of the provider. Taking the provider interface required by web3.js, it doesn't expose anything specific to Ethereum's RPC API, so transport encapsulation is really the only responsibility which fits its current incarnation. However, it exposes nothing useful about the transport itself. Most proposals on this thread seem to follow suit, and much of the argument seems to be around whether or not to expose those transport-level details. My question is, if this thing isn't exposing details specific to Ethereum's RPC API, and if it exists solely to encapsulate the transport layer, then where else would transport-level details go? If I get a vote, it's on the branch of proposals which expose common methods for transport control which is mostly agnostic to the transport mechanism in question, like I know this isn't meant to be defining the web3.js library, but since that's presently the most canonical example, I offer it up as illustration. Today the On the topic of whether or not exposing an EventEmitter interface should be required, I think it's very likely that we'll continue to expand upon the limited existing push mechanisms (i.e. Pub/Sub), so I think it's reasonable to include this in the baseline spec, as well. Personally I'd prefer it if the provider abstracted away just a little bit more (e.g. |
Hi, a lot of text :) not sure if i got all of it. We could surely think of even making those providers as abstract as this one. The main idea is that The other advantage of the Concerning the subscription its more like: So i would say subscriptions, are already a first class citizen according to this spec. I am exposing the connection events, as this could be relevant to the DAPP, to warn, or stop functionality. But wouldn't expose the control over the connection itself. Currently in my implementation, if it disconnects, and you send again, it tries to reconnect automatically. Ideally Mist, or MetaMask will hide the connection complexity away, and show warnings to users themselves. |
Hey @frozeman Still catching up on this thread, and will probably have to read it a few times, but love the overall idea. Quick background on how ethers.js handles connections. First of all, complete separation of a Provider and Signer; a Signer can "somehow" sign things and a Provider can "somehow" talk to the blockchain. Currently there is full support for providers that can use Etherscan, INFURA, JSON-RPC, an existing Web3 Provider, and then meta Providers (like fallback and round-robin). The Web3Provider, which wraps a Web3-style provider and exposes it as an Ethers Provider, no calls are batched. For the Web3 Bridge which allows a standard Ethers Provider and Signer to be used inside a Web3 instance I return batch requests in the same way Web3 Providers do to be compatible. The Web3 Bridge is used on projects like Ethers Wallet (iOS) and ethers.io where I need to create an injected Web3 Object for customers that code against the Web3 API, but the backend is an Ethers Provider. I still need to read through the whole document, but is the idea of EthereumProvider to be what instances of Web3.providers.HTTPProvder (for example) would be? I was wondering the other day whether we even need an explicit Just an idea; here is the discussion so far. Kind of outdated, but this thread made me think of it, so maybe it makes sense to prototype with the Web3Provider. |
Really like the idea - just as a note from a mobile side: it would be awesome if EthereumProvider will (as far as I see continue) to consist only of methods. No variables. No nested classes. Then we can use WebView.addJavascriptInterface to add the provider and not have to use hackish workarounds ;-) |
I talked with @ligi and i think its better to not add the Im going to implement the ethereum provider as described in the specs above in Mist and encourage @danfinlay @kumavis @ricmoo to do the same for their ethereum browsers. |
This sounds good to me, I look forward to catching up to your API! |
Hey everyone, Thanks to Fabian's ideas and everyone else's discussions and contributions in this thread, Marc and I on the Mist team have designed an EIP for the Ethereum Provider API called EIP 1193. Document: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md We would like to invite everyone to take a look and continue any discussion, feedback, improvements, etc. in the ethereum-magicians thread to help finalize the proposal. We look forward to your input! |
Abstract
This draft is to discuss the
ethereum
API, an object exposed by ethereum native browsers and tools like Mist, MetaMask and status.im.The API should be small and clean and allow all kind of libraries to attach to it.
The class of the object should be
EthereumProvider
, the object nameethereum
.API
The
ethereum
should inherit from a EventEmitter type class, and provideadd/removeListener
,on/once/off
etc.The text was updated successfully, but these errors were encountered: