From 83c7d841eba4deec481fabe12750b84836e98351 Mon Sep 17 00:00:00 2001 From: Will Meister Date: Tue, 23 Jun 2020 16:50:40 -0500 Subject: [PATCH] Ingest Block Batches (#8) Handling BlockBatches in Geth at `SendBlockBatches` endpoint (eth_sendBlockBatches) Other: * Adding PR template * Adding ability to set timestamp and making blocks use configured timestamp * Adding ability to encode original tx nonce in calldata * Adding L1MessageSender to Contract Creation Txs --- .github/CONTRIBUTING.md | 125 ++++-- .github/PULL_REQUEST_TEMPLATE.md | 17 + accounts/abi/bind/backends/simulated_test.go | 2 +- accounts/abi/bind/base.go | 2 +- accounts/abi/bind/util_test.go | 2 +- consensus/ethash/consensus.go | 2 +- core/blockchain.go | 16 + core/blockchain_test.go | 10 +- core/types/receipt_test.go | 2 +- core/types/transaction.go | 28 +- core/types/transaction_test.go | 2 +- crypto/signature_cgo.go | 8 + eth/api_backend.go | 8 + internal/ethapi/api.go | 96 ++++- internal/ethapi/api_test.go | 396 +++++++++++++++++++ internal/ethapi/backend.go | 9 +- les/api_backend.go | 8 + les/test_helper.go | 4 +- light/odr_test.go | 2 +- light/txpool.go | 10 +- miner/worker.go | 23 +- miner/worker_test.go | 2 +- mobile/types.go | 8 +- params/config.go | 11 +- signer/core/types.go | 2 +- 25 files changed, 708 insertions(+), 87 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 internal/ethapi/api_test.go diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f87996cdcb94..8c6fca7d2d08 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,40 +1,89 @@ # Contributing -Thank you for considering to help out with the source code! We welcome -contributions from anyone on the internet, and are grateful for even the -smallest of fixes! - -If you'd like to contribute to go-ethereum, please fork, fix, commit and send a -pull request for the maintainers to review and merge into the main code base. If -you wish to submit more complex changes though, please check up with the core -devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum) to -ensure those changes are in line with the general philosophy of the project -and/or get some early feedback which can make both your efforts much lighter as -well as our review and merge procedures quick and simple. - -## Coding guidelines - -Please make sure your contributions adhere to our coding guidelines: - - * Code must adhere to the official Go -[formatting](https://golang.org/doc/effective_go.html#formatting) guidelines -(i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). - * Code must be documented adhering to the official Go -[commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. - * Pull requests need to be based on and opened against the `master` branch. - * Commit messages should be prefixed with the package(s) they modify. - * E.g. "eth, rpc: make trace configs optional" - -## Can I have feature X - -Before you submit a feature request, please check and make sure that it isn't -possible through some other means. The JavaScript-enabled console is a powerful -feature in the right hands. Please check our -[Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info -and help. - -## Configuration, dependencies, and tests - -Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) -for more details on configuring your environment, managing project dependencies -and testing procedures. +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure that tests pass and code is lint free: `make all && make test && make lint` +2. Update the README.md if any changes invalidate its current content. +3. Include any tests for new functionality. +4. Reference any relevant issues in your PR comment. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at contributing@optimism.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] and from the [Angular Seed Contributing Guide][angular-contrib]. + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ +[angular-contrib]: https://github.com/mgechev/angular-seed/blob/master/.github/CONTRIBUTING.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..774a91bb7d9b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +## Description + +## Questions +- +- +- + +## Metadata +### Fixes +- Fixes # [Link to Issue] + +## Contributing Agreement + + +- [ ] I have read and understood the [Optimism Contributing Guide and Code of Conduct](./CONTRIBUTING.md) and am following those guidelines in this pull request. \ No newline at end of file diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index e0cbd999d119..8c6a5d68e2cd 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -58,7 +58,7 @@ func TestSimulatedBackend(t *testing.T) { // generate a transaction and confirm you can retrieve it code := `6060604052600a8060106000396000f360606040526008565b00` var gas uint64 = 3000000 - tx := types.NewContractCreation(0, big.NewInt(0), gas, big.NewInt(1), common.FromHex(code)) + tx := types.NewContractCreation(0, big.NewInt(0), gas, big.NewInt(1), common.FromHex(code), nil) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key) err = sim.SendTransaction(context.Background(), tx) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 54f0df13d0e9..9f4dfe5a08fd 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -227,7 +227,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i // Create the transaction, sign it and schedule it for execution var rawTx *types.Transaction if contract == nil { - rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) + rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input, nil) } else { rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input, nil) } diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index e0141f46e06f..9dd39617cbf7 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -62,7 +62,7 @@ func TestWaitDeployed(t *testing.T) { defer backend.Close() // Create the transaction. - tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) + tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code), nil) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) // Wait for it to get mined in the background. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 317bdfefece3..dba3a451f659 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -254,7 +254,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * return consensus.ErrFutureBlock } } - if header.Time <= parent.Time { + if header.Time < parent.Time { return errOlderBlockTime } // Verify the block's difficulty based on its timestamp and parent's difficulty diff --git a/core/blockchain.go b/core/blockchain.go index f083f53e0496..84d8b8a1f70e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -151,6 +151,8 @@ type BlockChain struct { chainmu sync.RWMutex // blockchain insertion lock + currentTimestamp atomic.Value // Timestamp to be used when mining the current block. + currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) @@ -234,6 +236,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par bc.currentBlock.Store(nilBlock) bc.currentFastBlock.Store(nilBlock) + // TODO: Make default current timestamp configurable & make 0 if genesis else load from last block? + bc.SetCurrentTimestamp(int64(0)) + // Initialize the chain with ancient data if it isn't empty. if bc.empty() { rawdb.InitDatabaseFromFreezer(bc.db) @@ -495,6 +500,17 @@ func (bc *BlockChain) GasLimit() uint64 { return bc.CurrentBlock().GasLimit() } +// SetCurrentTimestamp sets the timestamp for blocks added to the canonical chain. +func (bc *BlockChain) SetCurrentTimestamp(timestamp int64) { + bc.currentTimestamp.Store(×tamp) +} + +// CurrentTimestamp retrieves the timestamp used for blocks added to the canonical chain. +func (bc *BlockChain) CurrentTimestamp() int64 { + // Note: Can never be nil + return *bc.currentTimestamp.Load().(*int64) +} + // CurrentBlock retrieves the current head block of the canonical chain. The // block is retrieved from the blockchain's internal cache. func (bc *BlockChain) CurrentBlock() *types.Block { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 29a404685624..0dbc9a4e23c7 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -949,7 +949,7 @@ func TestLogReorgs(t *testing.T) { blockchain.SubscribeRemovedLogsEvent(rmLogsCh) chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code, nil), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1042,7 +1042,7 @@ func TestLogRebirth(t *testing.T) { chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code, nil), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1062,7 +1062,7 @@ func TestLogRebirth(t *testing.T) { // Generate long reorg chain forkChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code, nil), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1162,7 +1162,7 @@ func TestSideLogRebirth(t *testing.T) { // Generate side chain with lower difficulty sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code, nil), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1207,7 +1207,7 @@ func TestReorgSideEvent(t *testing.T) { } replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil), signer, key1) if i == 2 { gen.OffsetTime(-9) } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index b507d8c48f94..4baf98d783b1 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -155,7 +155,7 @@ func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) { func TestDeriveFields(t *testing.T) { // Create a few transactions to have receipts for txs := Transactions{ - NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil), + NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil, nil), NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil, nil), } // Create the corresponding receipts diff --git a/core/types/transaction.go b/core/types/transaction.go index bcb0b3cd3f33..de1fd7b2d5ec 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -19,6 +19,7 @@ package types import ( "container/heap" "errors" + "fmt" "io" "math/big" "sync/atomic" @@ -76,8 +77,8 @@ func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit u return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data, l1MessageSender) } -func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data, nil) +func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, l1MessageSender *common.Address) *Transaction { + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data, l1MessageSender) } func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, l1MessageSender *common.Address) *Transaction { @@ -106,6 +107,29 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit return &Transaction{data: d} } +// Appends the provided 64-bit nonce to this Transaction's calldata as the last 4 bytes +func (t *Transaction) AddNonceToWrappedTransaction(nonce uint64) { + bytes := make([]byte, 8) + for i := range bytes { + bytes[i] = 0xFF & byte(nonce>>(56-i)) + } + t.data.Payload = append(t.data.Payload, bytes...) +} + +// Parses the encoded nonce from this Transaction's calldata and returns it as well as the calldata without the encoded nonce. +func (t *Transaction) GetNonceAndCalldataFromWrappedTransaction() (uint64, []byte, error) { + if len(t.data.Payload) < 8 { + return 0, nil, fmt.Errorf("Cannot parse encoded nonce out of calldata of less than 8 bytes in length. Calldata: %x", t.data.Payload) + } + + nonceBytes := t.data.Payload[len(t.data.Payload)-8:] + nonce := uint64(0) + for i := range nonceBytes { + nonce += uint64(nonceBytes[i] << (56 - i)) + } + return nonce, t.data.Payload[:len(t.data.Payload)-8], nil +} + // ChainId returns which chain id this transaction was signed for (if at all) func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 0bfd580563a8..694ad4f30bf2 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -190,7 +190,7 @@ func TestTransactionJSON(t *testing.T) { case 0: tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef"), &sender) case 1: - tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef")) + tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"), nil) } transactions = append(transactions, tx) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index 1fe84509e76f..62120cec4390 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -60,6 +60,14 @@ func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { return secp256k1.Sign(digestHash, seckey) } +func VerifyMessageSignature(pubKey, unhashedMessage, signature []byte) bool { + if len(signature) < 64 || len(signature) > 65 { + // signature format may be [R || S] or [R || S || V] + return false + } + return VerifySignature(pubKey, Keccak256(unhashedMessage), signature[0:64]) +} + // VerifySignature checks that the given public key created signature over digest. // The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format. // The signature should have the 64 byte [R || S] format. diff --git a/eth/api_backend.go b/eth/api_backend.go index 0ad2d57fd2e2..45049c87b231 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -226,6 +226,14 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) return b.eth.txPool.AddLocal(signedTx) } +func (b *EthAPIBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error { + return b.eth.txPool.AddLocals(signedTxs) +} + +func (b *EthAPIBackend) SetTimestamp(timestamp int64) { + b.eth.blockchain.SetCurrentTimestamp(timestamp) +} + func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { pending, err := b.eth.txPool.Pending() if err != nil { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 796727627e8f..cd255acee539 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -19,6 +19,8 @@ package ethapi import ( "bytes" "context" + "crypto/ecdsa" + "encoding/json" "errors" "fmt" "math/big" @@ -1179,13 +1181,19 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa // PublicTransactionPoolAPI exposes methods for the RPC interface type PublicTransactionPoolAPI struct { - b Backend - nonceLock *AddrLocker + b Backend + nonceLock *AddrLocker + batchSigner *ecdsa.PrivateKey } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { - return &PublicTransactionPoolAPI{b, nonceLock} +func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker, batchSignerPrivKey *ecdsa.PrivateKey) *PublicTransactionPoolAPI { + if batchSignerPrivKey == nil { + // should only be the case in unused code and some unit tests + key, _ := crypto.GenerateKey() + return &PublicTransactionPoolAPI{b, nonceLock, key} + } + return &PublicTransactionPoolAPI{b, nonceLock, batchSignerPrivKey} } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. @@ -1440,11 +1448,40 @@ func (args *SendTxArgs) toTransaction() *types.Transaction { input = *args.Data } if args.To == nil { - return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) + return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, nil) } return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, args.L1MessageSender) } +type RollupTransaction struct { + Nonce *hexutil.Uint64 `json:"nonce"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + Sender *common.Address `json:"sender"` + Target *common.Address `json:"target"` + Calldata *hexutil.Bytes `json:"calldata"` +} + +// Creates a wrapped tx (internal tx that wraps an OVM tx) from the RollupTransaction. +// The only part of the wrapped tx that has anything to do with the RollupTransaction is the calldata. +func (r *RollupTransaction) toTransaction(txNonce uint64) *types.Transaction { + var tx *types.Transaction + c, _ := r.Calldata.MarshalText() + if r.Target == nil { + tx = types.NewContractCreation(txNonce, big.NewInt(0), uint64(*r.GasLimit), big.NewInt(0), c, r.Sender) + } else { + tx = types.NewTransaction(txNonce, *r.Target, big.NewInt(0), uint64(*r.GasLimit), big.NewInt(0), c, r.Sender) + } + tx.AddNonceToWrappedTransaction(uint64(*r.Nonce)) + return tx +} + +// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. +type BlockBatches struct { + Timestamp *hexutil.Uint64 `json:"timestamp"` + BlockNumber *hexutil.Uint64 `json:"blockNumber"` + Batches [][]*RollupTransaction `json:"batches"` +} + // SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) { if err := b.SendTx(ctx, tx); err != nil { @@ -1522,6 +1559,55 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return SubmitTransaction(ctx, s.b, tx) } +// SendBlockBatches will: +// * Verify the batches are signed by the BlockBatchesSender +// * Update the Geth timestamp to the provided timestamp +// * handle the RollupTransaction Batches contained in the provided Block atomically +func (s *PublicTransactionPoolAPI) SendBlockBatches(ctx context.Context, messageAndSig []hexutil.Bytes) []error { + if len(messageAndSig) != 2 { + return []error{fmt.Errorf("incorrect number of arguments. Expected 2, got %d", len(messageAndSig))} + } + if !crypto.VerifyMessageSignature(crypto.FromECDSAPub(s.b.ChainConfig().BlockBatchesSender), messageAndSig[0], messageAndSig[1]) { + return []error{fmt.Errorf("signature does not match Block Batch Sender address %x", crypto.PubkeyToAddress(*s.b.ChainConfig().BlockBatchesSender))} + } + var blockBatches BlockBatches + if err := json.Unmarshal(messageAndSig[0], &blockBatches); err != nil { + return []error{fmt.Errorf("incorrect format for BlockBatches type. Received: %s", messageAndSig[0])} + } + + txCount := 0 + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + + wrappedTxNonce, _ := s.b.GetPoolNonce(ctx, crypto.PubkeyToAddress(s.batchSigner.PublicKey)) + signedBatches := make([][]*types.Transaction, len(blockBatches.Batches)) + for bi, rollupTxs := range blockBatches.Batches { + signedBatches[bi] = make([]*types.Transaction, len(rollupTxs)) + for i, rollupTx := range rollupTxs { + tx := rollupTx.toTransaction(wrappedTxNonce) + wrappedTxNonce++ + tx, err := types.SignTx(tx, signer, s.batchSigner) + if err != nil { + return []error{fmt.Errorf("error signing transaction in batch %d, index %d", bi, i)} + } + signedBatches[bi][i] = tx + txCount++ + } + } + + s.b.SetTimestamp(int64(*blockBatches.Timestamp)) + + i := 0 + errs := make([]error, txCount) + for _, signedTxs := range signedBatches { + // TODO: Eventually make sure each batch is handled atomically + for _, e := range s.b.SendTxs(ctx, signedTxs) { + errs[i] = e + i++ + } + } + return errs +} + // Sign calculates an ECDSA signature for: // keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). // diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go new file mode 100644 index 000000000000..b6bcc1d00ab9 --- /dev/null +++ b/internal/ethapi/api_test.go @@ -0,0 +1,396 @@ +package ethapi + +import ( + "context" + "crypto/ecdsa" + "encoding/json" + "fmt" + "math/big" + "math/rand" + "testing" + + "github.com/aws/aws-sdk-go/awstesting" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + internalTxNonce = hexutil.Uint64(uint64(rand.Int())) + internalTxCalldata = hexutil.Bytes{0, 1, 2, 3, 4, 5, 6, 7} + internalTxSender = common.Address{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + internalTxTarget = common.Address{9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + backendTimestamp = int64(0) +) + +type testCase struct { + backendContext backendContext + inputCtx context.Context + inputMessageAndSig []hexutil.Bytes + hasErrors bool + resultingTimestamp int64 + multipleBatches bool +} + +func getTestCases(pk *ecdsa.PrivateKey) []testCase { + return []testCase{ + // Bad input -- message and sig not of length 2 + {inputCtx: getFakeContext(), inputMessageAndSig: []hexutil.Bytes{}, hasErrors: true}, + {inputCtx: getFakeContext(), inputMessageAndSig: []hexutil.Bytes{[]byte{1, 2, 3}}, hasErrors: true}, + {inputCtx: getFakeContext(), inputMessageAndSig: []hexutil.Bytes{[]byte{1}, []byte{2}, []byte{3}}, hasErrors: true}, + + // Bad input -- message not signed + {inputCtx: getFakeContext(), inputMessageAndSig: []hexutil.Bytes{[]byte{1}, []byte{2}}, hasErrors: true}, + + // Bad input -- message is signed but incorrect format + {inputCtx: getFakeContext(), inputMessageAndSig: getInputMessageAndSignature([]byte{1}, pk), hasErrors: true}, + + // Returns 0 errors if no transactions but timestamp updated + {inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 0, 1, []int{})}, + {inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 1, []int{}), resultingTimestamp: 1}, + + // Handles one transaction and updates timestamp + {inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 1, []int{1}), resultingTimestamp: 1}, + {backendContext: backendContext{sendTxsErrors: getDummyErrors([]int{0}, 1)}, inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 1, []int{1}), hasErrors: true, resultingTimestamp: 1}, + + // Handles one batch of multiple transaction and updates timestamp + {inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 1, []int{2}), resultingTimestamp: 1}, + {backendContext: backendContext{sendTxsErrors: getDummyErrors([]int{1}, 2)}, inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 2, []int{2}), hasErrors: true, resultingTimestamp: 1}, + + // Handles multiple transactions and updates timestamp + {inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 2, 1, []int{1, 2, 3}), resultingTimestamp: 2}, + {backendContext: backendContext{sendTxsErrors: getDummyErrors([]int{0, 2}, 3)}, inputCtx: getFakeContext(), inputMessageAndSig: getBlockBatchesInputMessageAndSignature(pk, 1, 1, []int{1, 2, 3}), hasErrors: true, resultingTimestamp: 1, multipleBatches: true}, + } +} + +func TestSendBlockBatches(t *testing.T) { + blockBatchSenderPrivKey, _ := crypto.GenerateKey() + txSignerPrivKey, _ := crypto.GenerateKey() + + for testNum, testCase := range getTestCases(blockBatchSenderPrivKey) { + backendTimestamp = 0 + api := getTestPublicTransactionPoolAPI(txSignerPrivKey, blockBatchSenderPrivKey, testCase.backendContext) + res := api.SendBlockBatches(testCase.inputCtx, testCase.inputMessageAndSig) + h := func(r []error) bool { + for _, e := range r { + if e != nil { + return true + } + } + return false + } + hasErrors := h(res) + + // For debugging and verification: + fmt.Printf("test case %d had output errors: %v\n", testNum, res) + if testCase.hasErrors && !hasErrors { + t.Fatalf("test case %d expected output errors but did not result in any. Errors: %v", testNum, res) + } + if !testCase.hasErrors && hasErrors { + t.Fatalf("test case %d did not expect output errors but resulted in %d. Errors: %v", testNum, len(res), res) + } + if hasErrors && len(testCase.backendContext.sendTxsErrors) > 0 { + // Note: Cannot handle test cases with multiple batches the same way because errors are aggregated from the endpoint and not from sendTxsErrors + if testCase.multipleBatches { + errorCount := func(r []error) int { + c := 0 + for _, e := range r { + if e != nil { + c++ + } + } + return c + } + if errorCount(res) != errorCount(testCase.backendContext.sendTxsErrors) { + t.Fatalf("test case %d expected %d errors but resulted in %d", testNum, errorCount(res), errorCount(testCase.backendContext.sendTxsErrors)) + } + + } else { + if len(res) != len(testCase.backendContext.sendTxsErrors) { + t.Fatalf("test case %d expected %d output errors but received %d. Errors: %v", testNum, len(testCase.backendContext.sendTxsErrors), len(res), res) + } + for i, err := range res { + if err != nil && testCase.backendContext.sendTxsErrors[i] == nil { + t.Fatalf("test case %d had an error output mismatch. Received error at index %d when one wasn't expected. Expected output: %v, output: %v", testNum, i, testCase.backendContext.sendTxsErrors, res) + } + if err == nil && testCase.backendContext.sendTxsErrors[i] != nil { + t.Fatalf("test case %d had an error output mismatch. Did not receive an error at index %d when one was expected. Expected output: %v, output: %v", testNum, i, testCase.backendContext.sendTxsErrors, res) + } + } + } + } + if backendTimestamp != testCase.resultingTimestamp { + t.Fatalf("test case %d should have updated timestamp to %d but it was %d after execution.", testNum, testCase.resultingTimestamp, backendTimestamp) + } + } +} + +func getDummyErrors(errorIndicies []int, outputSize int) []error { + errs := make([]error, outputSize) + for _, i := range errorIndicies { + errs[i] = fmt.Errorf("error %d", i) + } + return errs +} + +func getRandomRollupTransaction() *RollupTransaction { + gasLimit := hexutil.Uint64(uint64(0)) + return &RollupTransaction{ + Nonce: &internalTxNonce, + GasLimit: &gasLimit, + Sender: &internalTxSender, + Target: &internalTxTarget, + Calldata: &internalTxCalldata, + } +} + +func getBlockBatchesInputMessageAndSignature(privKey *ecdsa.PrivateKey, timestamp int64, blockNumber int, batchSizes []int) []hexutil.Bytes { + ts := hexutil.Uint64(uint64(timestamp)) + blockNum := hexutil.Uint64(uint64(blockNumber)) + + batches := make([][]*RollupTransaction, len(batchSizes)) + for i, s := range batchSizes { + batches[i] = make([]*RollupTransaction, s) + for index := 0; index < s; index++ { + batches[i][index] = getRandomRollupTransaction() + } + } + bb := &BlockBatches{ + Timestamp: &ts, + BlockNumber: &blockNum, + Batches: batches, + } + + message, _ := json.Marshal(bb) + return getInputMessageAndSignature(message, privKey) +} + +func getInputMessageAndSignature(message []byte, privKey *ecdsa.PrivateKey) []hexutil.Bytes { + sig, _ := crypto.Sign(crypto.Keccak256(message), privKey) + return []hexutil.Bytes{message, sig} +} + +func getFakeContext() context.Context { + return &awstesting.FakeContext{ + Error: fmt.Errorf("fake error%s", "!"), + DoneCh: make(chan struct{}, 1), + } +} + +func getTestPublicTransactionPoolAPI(txSignerPrivKey *ecdsa.PrivateKey, blockBatchSenderPrivKey *ecdsa.PrivateKey, backendContext backendContext) *PublicTransactionPoolAPI { + backend := newMockBackend(&blockBatchSenderPrivKey.PublicKey, backendContext) + return NewPublicTransactionPoolAPI(backend, nil, txSignerPrivKey) +} + +type backendContext struct { + currentBlockNumber int64 + signerNonce uint64 + sendTxsErrors []error +} + +type mockBackend struct { + blockBatchSender *ecdsa.PublicKey + testContext backendContext + timestamp int64 +} + +func newMockBackend(blockBatchSender *ecdsa.PublicKey, backendContext backendContext) mockBackend { + return mockBackend{ + blockBatchSender: blockBatchSender, + testContext: backendContext, + } +} + +func (m mockBackend) Downloader() *downloader.Downloader { + panic("not implemented") +} + +func (m mockBackend) ProtocolVersion() int { + panic("not implemented") +} + +func (m mockBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { + panic("not implemented") +} + +func (m mockBackend) ChainDb() ethdb.Database { + panic("not implemented") +} + +func (m mockBackend) AccountManager() *accounts.Manager { + panic("not implemented") +} + +func (m mockBackend) ExtRPCEnabled() bool { + panic("not implemented") +} + +func (m mockBackend) RPCGasCap() *big.Int { + panic("not implemented") +} + +func (m mockBackend) SetHead(number uint64) { + panic("not implemented") +} + +func (m mockBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + panic("not implemented") +} + +func (m mockBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + panic("not implemented") +} + +func (m mockBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { + panic("not implemented") +} + +func (m mockBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + panic("not implemented") +} + +func (m mockBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + panic("not implemented") +} + +func (m mockBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { + panic("not implemented") +} + +func (m mockBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { + panic("not implemented") +} + +func (m mockBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { + panic("not implemented") +} + +func (m mockBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { + panic("not implemented") +} + +func (m mockBackend) GetTd(hash common.Hash) *big.Int { + panic("not implemented") +} + +func (m mockBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) { + panic("not implemented") +} + +func (m mockBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { + panic("not implemented") +} + +func (m mockBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { + panic("not implemented") +} + +func (m mockBackend) GetPoolTransactions() (types.Transactions, error) { + panic("not implemented") +} + +func (m mockBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { + panic("not implemented") +} + +func (m mockBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + return m.testContext.signerNonce, nil +} + +func (m mockBackend) Stats() (pending int, queued int) { + panic("not implemented") +} + +func (m mockBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + panic("not implemented") +} + +func (m mockBackend) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) BloomStatus() (uint64, uint64) { + panic("not implemented") +} + +func (m mockBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) { + panic("not implemented") +} + +func (m mockBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { + panic("not implemented") +} + +func (m mockBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { + panic("not implemented") +} + +func (m mockBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error { + if len(m.testContext.sendTxsErrors) == 0 || len(m.testContext.sendTxsErrors) != len(signedTxs) { + return make([]error, len(signedTxs)) + } + return m.testContext.sendTxsErrors +} + +func (m mockBackend) SetTimestamp(timestamp int64) { + backendTimestamp = timestamp +} + +func (m mockBackend) ChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{ + BlockBatchesSender: m.blockBatchSender, + } +} + +func (m mockBackend) CurrentBlock() *types.Block { + header := &types.Header{ + ParentHash: common.Hash{}, + UncleHash: common.Hash{}, + Coinbase: common.Address{}, + Root: common.Hash{}, + TxHash: common.Hash{}, + ReceiptHash: common.Hash{}, + Bloom: types.Bloom{}, + Difficulty: nil, + Number: big.NewInt(m.testContext.currentBlockNumber), + GasLimit: 0, + GasUsed: 0, + Time: 0, + Extra: nil, + MixDigest: common.Hash{}, + Nonce: types.BlockNonce{}, + } + + return types.NewBlock(header, []*types.Transaction{}, []*types.Header{}, []*types.Receipt{}) +} diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 245091df35a3..ef8a070b1e9c 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -82,6 +82,10 @@ type Backend interface { SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription + // Optimism-specific API + SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error + SetTimestamp(timestamp int64) + ChainConfig() *params.ChainConfig CurrentBlock() *types.Block } @@ -102,8 +106,9 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: NewPublicTransactionPoolAPI(apiBackend, nonceLock), - Public: true, + // TODO: Instantiate Private Key from env var here when we know it + Service: NewPublicTransactionPoolAPI(apiBackend, nonceLock, nil), + Public: true, }, { Namespace: "txpool", Version: "1.0", diff --git a/les/api_backend.go b/les/api_backend.go index 5627c34d6a32..c11c44c9e103 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -177,6 +177,14 @@ func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) return b.eth.txPool.Add(ctx, signedTx) } +func (b *LesApiBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error { + return b.eth.txPool.AddBatch(ctx, signedTxs) +} + +func (b *LesApiBackend) SetTimestamp(timestamp int64) { + // Intentionally empty because this is not needed for LightChain +} + func (b *LesApiBackend) RemoveTx(txHash common.Hash) { b.eth.txPool.RemoveTx(txHash) } diff --git a/les/test_helper.go b/les/test_helper.go index cc7ec6f9fef4..a864ebfdaddb 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -127,12 +127,12 @@ func prepare(n int, backend *backends.SimulatedBackend) { backend.SendTransaction(ctx, tx2) // user1 deploys a test contract - tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, userKey1) + tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode, nil), signer, userKey1) backend.SendTransaction(ctx, tx3) testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1) // user1 deploys a event contract - tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, userKey1) + tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode, nil), signer, userKey1) backend.SendTransaction(ctx, tx4) case 2: // bankUser transfer some ether to signer diff --git a/light/odr_test.go b/light/odr_test.go index a3f7fbc4af0d..00e85ddd5357 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -222,7 +222,7 @@ func testChainGen(i int, block *core.BlockGen) { nonce := block.TxNonce(acc1Addr) tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil, nil), signer, acc1Key) nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode), signer, acc1Key) + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode, nil), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) block.AddTx(tx1) block.AddTx(tx2) diff --git a/light/txpool.go b/light/txpool.go index 11a0e76ae01e..309d078afad7 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -446,21 +446,25 @@ func (pool *TxPool) Add(ctx context.Context, tx *types.Transaction) error { return nil } -// AddTransactions adds all valid transactions to the pool and passes them to +// AddBatch adds all valid transactions to the pool and passes them to // the tx relay backend -func (pool *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { +func (pool *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) []error { pool.mu.Lock() defer pool.mu.Unlock() var sendTx types.Transactions - for _, tx := range txs { + errors := make([]error, len(txs)) + for i, tx := range txs { if err := pool.add(ctx, tx); err == nil { sendTx = append(sendTx, tx) + } else { + errors[i] = err } } if len(sendTx) > 0 { pool.relay.Send(sendTx) } + return errors } // GetTransaction returns a transaction if it is contained in the pool diff --git a/miner/worker.go b/miner/worker.go index abfb7b36e638..6933fcb75c16 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -345,12 +345,12 @@ func (w *worker) newWorkLoop(recommit time.Duration) { select { case <-w.startCh: clearPending(w.chain.CurrentBlock().NumberU64()) - timestamp = time.Now().Unix() + timestamp = w.chain.CurrentTimestamp() commit(false, commitInterruptNewHead) case head := <-w.chainHeadCh: clearPending(head.Block.NumberU64()) - timestamp = time.Now().Unix() + timestamp = w.chain.CurrentTimestamp() commit(false, commitInterruptNewHead) case <-timer.C: @@ -482,7 +482,7 @@ func (w *worker) mainLoop() { // If clique is running in dev mode(period is 0), disable // advance sealing here. if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 { - w.commitNewWork(nil, true, time.Now().Unix()) + w.commitNewWork(nil, true, w.chain.CurrentTimestamp()) } } atomic.AddInt32(&w.newTxs, int32(len(ev.Txs))) @@ -834,15 +834,16 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) tstart := time.Now() parent := w.chain.CurrentBlock() - if parent.Time() >= uint64(timestamp) { - timestamp = int64(parent.Time() + 1) - } + // TODO: Is there some other sensible check that we can do in place of the below code with timestamps set from L1? + //if parent.Time() >= uint64(timestamp) { + // timestamp = int64(parent.Time() + 1) + //} // this will ensure we're not going off too far in the future - if now := time.Now().Unix(); timestamp > now+1 { - wait := time.Duration(timestamp-now) * time.Second - log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) - time.Sleep(wait) - } + //if now := time.Now().Unix(); timestamp > now+1 { + // wait := time.Duration(timestamp-now) * time.Second + // log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) + // time.Sleep(wait) + //} num := parent.Number() header := &types.Header{ diff --git a/miner/worker_test.go b/miner/worker_test.go index 78a037f16cec..d173d2f463fe 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -169,7 +169,7 @@ func (b *testWorkerBackend) newRandomUncle() *types.Block { func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { var tx *types.Transaction if creation { - tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, nil, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey) + tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, nil, common.FromHex(testCode), nil), types.HomesteadSigner{}, testBankKey) } else { tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, nil, nil, nil), types.HomesteadSigner{}, testBankKey) } diff --git a/mobile/types.go b/mobile/types.go index f359b7ed02af..39184ad59a96 100644 --- a/mobile/types.go +++ b/mobile/types.go @@ -197,17 +197,11 @@ type Transaction struct { tx *types.Transaction } -// NewContractCreation creates a new transaction for deploying a new contract with -// the given properties. -func NewContractCreation(nonce int64, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { - return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} -} - // NewTransaction creates a new transaction with the given properties. Contracts // can be created by transacting with a nil recipient. func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { if to == nil { - return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} + return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data), nil)} } return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data), nil)} } diff --git a/params/config.go b/params/config.go index 6d8c3caeb72d..a015b15ac3bc 100644 --- a/params/config.go +++ b/params/config.go @@ -17,6 +17,7 @@ package params import ( + "crypto/ecdsa" "encoding/binary" "fmt" "math/big" @@ -217,21 +218,23 @@ var ( Threshold: 2, } + // TODO: Fill out BlockBatchesSender Address when we know it + // AllEthashProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Ethash consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(108), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(108), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(108), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(108), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -307,6 +310,8 @@ type ChainConfig struct { // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` + + BlockBatchesSender *ecdsa.PublicKey `json:"blockBatchSender,omitempty"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing. diff --git a/signer/core/types.go b/signer/core/types.go index b188f2ff7976..2c39a19459e6 100644 --- a/signer/core/types.go +++ b/signer/core/types.go @@ -95,7 +95,7 @@ func (args *SendTxArgs) toTransaction() *types.Transaction { input = *args.Input } if args.To == nil { - return types.NewContractCreation(uint64(args.Nonce), (*big.Int)(&args.Value), uint64(args.Gas), (*big.Int)(&args.GasPrice), input) + return types.NewContractCreation(uint64(args.Nonce), (*big.Int)(&args.Value), uint64(args.Gas), (*big.Int)(&args.GasPrice), input, nil) } var l1MessageSender *common.Address = nil if args.L1MessageSender != nil {