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

types, rawdb, core, miner: add block location fields to receipt #17662

Merged
merged 3 commits into from
Mar 27, 2019

Conversation

bmperrea
Copy link
Contributor

@bmperrea bmperrea commented Sep 12, 2018

Solves #15210 without changing consensus, in a backwards compatible way, by adding the block location fields from ReadReceipt to the Receipt struct.

Update: In particular this improves consistency between the go client interface and the json rpc spec and allows go clients to use transaction receipts to watch for block confirmations for particular transactions - see #15210.

@bmperrea
Copy link
Contributor Author

@karalabe @holiman any chance for a review on this 32 line pr?

@bmperrea
Copy link
Contributor Author

bmperrea commented Oct 1, 2018

@karalabe @holiman are there any concerns with getting this through as it is now?

@favadi
Copy link
Contributor

favadi commented Oct 2, 2018

@bmperrea probably they just had no time to do a review yet.

@jgiles
Copy link

jgiles commented Oct 12, 2018

@karalabe any chance for a review on this?

Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I approve, but I don't know enough about how the different kinds of marshalling occurs, and whether this may break something. I'll have to defer to @fjl or @karalabe

@@ -47,6 +54,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
TransactionIndex *hexutil.Big `json:"transactionIndex"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps good to have omitEmpty on the json-part?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. Adding it now.

core/types/receipt.go Outdated Show resolved Hide resolved
@bmperrea bmperrea force-pushed the bmperrea/receipt-with-blocknumber branch from 5d00090 to 444dd6f Compare October 15, 2018 17:30
@bmperrea
Copy link
Contributor Author

Thanks for the helpful review @holiman ! Do you still want to wait on a response from @fjl or @karalabe ?

@holiman
Copy link
Contributor

holiman commented Oct 25, 2018

That would be good, yes, ping @fjl

@bmperrea
Copy link
Contributor Author

@karalabe @fjl @holiman Could we get a decisive review on this one? Thanks!

Copy link
Member

@karalabe karalabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a few fields to the receipt which only ever get initialized when read from the database. What happens when we receive a receipt from the network? The fields will be empty. What happens if we mine and generate a receipt ourselves? The fields will be empty.

These incomplete receipts will end up in various caches and will be returned on our APIs. Unless you ensure all paths that ever create a receipt also correctly initializes these fields, it's not possible to accept with PR.

You also updated the JSON output with extra fields, but the JSON is adhering to the standard Ethereum JSON RPC spec. Adding anything only in Geth will fragment the ecosystem because all of a sudden we have information other clients don't report. Or alternatively, our ethclient expects fields that other clients don't provide. The correct way here is to open an EIP that chnges the RPC spec so all clients implement it and we can also enforce it properly.

@bmperrea
Copy link
Contributor Author

thanks @karalabe 👍. Is the rpc spec you're referring to documented somewhere? I see block location fields are included here https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt

@bmperrea bmperrea force-pushed the bmperrea/receipt-with-blocknumber branch from 444dd6f to 9b2bbe8 Compare November 17, 2018 05:03
@bmperrea bmperrea changed the title types, rawdb: add block location fields to receipt types, rawdb, core, miner: add block location fields to receipt Nov 17, 2018
@bmperrea
Copy link
Contributor Author

Great point about mined and downloaded receipts @karalabe . I'm excited to improve the geth go-client interface to make sure we keep consistent with the rpc spec which includes these location fields. I've updated the PR to add code to take care of these cases in miner/worker.go and core/blockchain.go respectively. These are the same place where location-based fields are added to the log objects within the receipt.

This pr makes the receipt object in the go-client interface more consistent with the rpc spec while also keeping consistent with how location fields are set for log entries.

@bmperrea
Copy link
Contributor Author

In general there are significant differences between the rpc api and the ethclient go client api. For instance
TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) of the go client (interfaces.go) vs
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction (ethapi/api.go)

@karalabe is right that these missing block location fields, as well as the differences between RPCTransaction and types.Transaction are a problem. They are fragmenting the ecosystem because we are missing information that other clients report.

This PR is only the beginning of fixing the broad issue.
We should put together and EIP to discuss the best way to match the ethclient interface to the RPC spec. One long-term option would be to make the json rpc a serialized version of the same struct returned by the ethclient.

@holiman
Copy link
Contributor

holiman commented Dec 13, 2018

@karalabe it's your call, please decide on how to move forward

@rjl493456442
Copy link
Member

Hi, actually I don't think we need to add these additional fields into the receipt struct. We can retrieve these data in txLookup structure. Maybe we need this additional information in the RPC side, we can just retrieve these from the database and assemble the response.

And actually what we are doing now is "try to cut some unnecessary fields" to save disk usage. Like in your PR you add three new fields which occupy 48bytes for each receipt. It can abuse the disk storage. For more information check #17106

@holiman holiman added this to the 1.9.0 milestone Feb 21, 2019
@holiman
Copy link
Contributor

holiman commented Feb 21, 2019

We'll add this after #17106 is in

Copy link
Member

@karalabe karalabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In core/state_processor.go we are creating new receipts when applying a transaction. You need to add these computed fields there too.

@bmperrea bmperrea force-pushed the bmperrea/receipt-with-blocknumber branch from 9b2bbe8 to d797d25 Compare February 21, 2019 19:36
@bmperrea
Copy link
Contributor Author

Thanks @holiman @karalabe . I rebased and added the block location fields in state_processor.

To add the transaction index in core/state_processor.go I ended up exposing the current transaction index from the statedb. The other option would be to add that index as an argument to statedb.ApplyTransaction since each caller of that function needs to have the transaction index to call statedb.Prepare right before to setup that data. I think exposing from statedb is a reasonable approach given that the current pattern passes the transaction index in Prepare and the getter is not so different from other methods in core/state/statedb.go.

Good work @rjl493456442 on #17106 . The separation of Receipt struct from the corresponding rlp and db objects allows for storage optimization while still allowing us to add context that a client would expect to have available when a Receipt object is returned, and the same goes for other types returned by ethclient.

@fjl
Copy link
Contributor

fjl commented Mar 21, 2019

This change is going to help with ethclient because it will make it possible to fetch inclusion info through TransactionReceipt

// Block location fields - not part of the rlp
BlockHash common.Hash `json:"blockHash,omitempty"`
BlockNumber *big.Int `json:"blockNumber,omitempty"`
TransactionIndex *big.Int `json:"transactionIndex,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TransactionIndex can just be a regular uint. Maybe it should be called BlockIndex to make it clear that it's the index in the block.

Copy link
Contributor Author

@bmperrea bmperrea Mar 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change the type Thanks for changing the type 👍

I got the name TransactionIndex from this RPC spec. I'm hoping we can continue toward ethclient implementing that entire spec.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it can be called TransactionIndex, it's fine. ethclient will never implement the full RPC API because it is based on core/types and some of the concepts in the web3 RPC API cannot be represented using the types. One example is transaction sender and inclusion info: the RPC API returns inclusion info and sender in the transaction object, but it would be silly to add those fields to types.Transaction).

Copy link
Contributor Author

@bmperrea bmperrea Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather see us separate the interface struct from the storage struct than compromise the interface. ethclient could return everything in rpcTransaction. Otherwise it's dropping fields that are returned by the geth server.

It would be good to have a go client that is as functional as rpc clients in other languages and I think ethclient should be that interface.

Copy link
Contributor

@fjl fjl Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to explain the motivation for the current API in a different way: when making ethclient I wanted an API that could be implemented on the server side as well as the client side. The interfaces in the root directory should be general enough to allow efficient implementation on both sides. From that PoV the web3 RPC interface is stupid: it requires us to dig up receipts when transactions are queried to add inclusion info, etc. If you are building an app in Go, you should just be able to embed the light client in your app and access the blockchain using efficient operations. ethclient was supposed to be the drop-in replacement for the light client in cases where you want to get the same info from an RPC provider like Infura.

Unfortunately we never got around to implementing the interfaces natively, without RPC in the middle. That's why we're stuck with an API that doesn't match the RPC API.

Copy link
Contributor

@fjl fjl Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still want to get to the native implementation done. If you're seriously interested in improving Go developer experience using go-ethereum as a library, I'd be super happy if you could help us make that happen by implementing the existing interfaces on the server side.

@@ -121,6 +121,9 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Set the receipt logs and create a bloom for filtering
receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
receipt.BlockHash = header.Hash()
receipt.BlockNumber = header.Number
receipt.TransactionIndex = uint(statedb.TxIndex())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better solution might be to set them in Process, since you can reuse the hash instead of recalculating the header hash for every tx.

The drawback, I guess, is that direct usage of ApplytTransaction won't get it, which I guess applies to the miner, the txpool (?) and various users of chain_makers in tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@holiman fixed it now by adding an accessor for block hash to StateDB. The PR already adds an accessor for TxIndex, one more can't hurt.

@holiman
Copy link
Contributor

holiman commented Mar 25, 2019

Lgtm!

@fjl fjl merged commit 7fb8969 into ethereum:master Mar 27, 2019
@zulhfreelancer
Copy link

Finally. Thank you guys!

kiku-jw pushed a commit to kiku-jw/go-ethereum that referenced this pull request Mar 29, 2019
Solves ethereum#15210 without changing consensus, in a backwards compatible way,
by adding tx inclusion information to the Receipt struct.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants