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
This commit is contained in:
		
							parent
							
								
									0a67cf87f3
								
							
						
					
					
						commit
						83c7d841eb
					
				
							
								
								
									
										109
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							| @ -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! | ||||
| 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.  | ||||
| 
 | ||||
| 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. | ||||
| Please note we have a code of conduct, please follow it in all your interactions with the project. | ||||
| 
 | ||||
| ## Coding guidelines | ||||
| ## Pull Request Process | ||||
| 
 | ||||
| Please make sure your contributions adhere to our coding guidelines: | ||||
| 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 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" | ||||
| ## Code of Conduct | ||||
| 
 | ||||
| ## Can I have feature X | ||||
| ### Our Pledge | ||||
| 
 | ||||
| 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. | ||||
| 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. | ||||
| 
 | ||||
| ## Configuration, dependencies, and tests | ||||
| ### Our Standards | ||||
| 
 | ||||
| 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. | ||||
| 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 | ||||
|  | ||||
							
								
								
									
										17
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| ## Description | ||||
| 
 | ||||
| ## Questions | ||||
| -  | ||||
| - | ||||
| - | ||||
| 
 | ||||
| ## Metadata | ||||
| ### Fixes | ||||
| - Fixes # [Link to Issue] | ||||
| 
 | ||||
| ## Contributing Agreement | ||||
| <!-- | ||||
| You *must* read and fully understand our Contributing Guide and Code of Conduct before submitting this pull request. Strong, healthy, and respectful communities are the best way to build great code 💖. | ||||
| --> | ||||
| 
 | ||||
| - [ ] I have read and understood the [Optimism Contributing Guide and Code of Conduct](./CONTRIBUTING.md) and am following those guidelines in this pull request. | ||||
| @ -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) | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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) | ||||
| 		} | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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) | ||||
| 
 | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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).
 | ||||
| //
 | ||||
|  | ||||
							
								
								
									
										396
									
								
								internal/ethapi/api_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								internal/ethapi/api_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -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{}) | ||||
| } | ||||
| @ -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", | ||||
|  | ||||
| @ -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) | ||||
| } | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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{ | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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)} | ||||
| } | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user