Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the problem of not being able to send all ETH #175

Merged
merged 3 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions integration-tests/test/fee-payment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,65 @@ describe('Fee Payment Integration Tests', async () => {
}
})

it('{tag:other} should transfer all ETH with correct gas limit', async () => {
await setPrices(env, 1000)
const randomWallet = ethers.Wallet.createRandom().connect(env.l2Provider)

await env.l2Wallet.sendTransaction({
to: randomWallet.address,
value: ethers.utils.parseEther('1'),
})

// estimate gas fee
const estimatedGas = await randomWallet.estimateGas({
to: env.l2Wallet.address,
value: ethers.utils.parseEther('0.1'),
})
const gasPrice = await env.l2Provider.getGasPrice()
const estimatedGasFee = estimatedGas.mul(gasPrice)

// should transfer funds back
const balance = await randomWallet.getBalance()
await randomWallet.sendTransaction({
to: env.l2Wallet.address,
value: balance.sub(estimatedGasFee),
gasLimit: estimatedGas.toNumber(),
})

const postBalance = await randomWallet.getBalance()
expect(postBalance).to.be.eq(ethers.BigNumber.from('0'))

await env.l2Wallet.sendTransaction({
to: randomWallet.address,
value: ethers.utils.parseEther('1'),
})

// the gas fee should be constant
await randomWallet.sendTransaction({
to: env.l2Wallet.address,
value: balance.sub(estimatedGasFee).sub(BigNumber.from('10')),
gasLimit: estimatedGas.toNumber(),
})

const postBalance2 = await randomWallet.getBalance()
expect(postBalance2).to.be.eq(ethers.BigNumber.from('10'))

// should reject tx if gas limit is not provided
await env.l2Wallet.sendTransaction({
to: randomWallet.address,
value: ethers.utils.parseEther('1'),
})

await expect(
randomWallet.sendTransaction({
to: env.l2Wallet.address,
value: balance.sub(estimatedGasFee),
})
).to.be.rejected

await setPrices(env, 1)
})

// https://github.com/bobanetwork/boba/pull/22
it('{tag:other} should be able to configure l1 gas price in a rare situation', async () => {
// This blocks all txs, because the gas usage for the l1 security fee is too large
Expand Down
5 changes: 5 additions & 0 deletions l2geth/rollup/sync_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,11 @@ func (s *SyncService) verifyFee(tx *types.Transaction) error {
nextBlockNumber := new(big.Int).Add(s.bc.CurrentBlock().Number(), big.NewInt(1))
isFeeTokenUpdate := s.bc.Config().IsFeeTokenUpdate(nextBlockNumber)

// After fee token hard fork, tx.Gas() includes l1 security fee
if isFeeTokenUpdate {
fee = new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
}

state, err := s.bc.State()
if err != nil {
return err
Expand Down
93 changes: 90 additions & 3 deletions l2geth/rollup/sync_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,65 @@ func TestFeeGasPriceOracleOwnerTransactions(t *testing.T) {
}
}

func TestVerifyFee(t *testing.T) {
// Generate a key
key, _ := crypto.GenerateKey()
sender := crypto.PubkeyToAddress(key.PublicKey)
// Generate a new service
service, _, _, err := newTestSyncService(true, &sender)
if err != nil {
t.Fatal(err)
}

signer := types.NewEIP155Signer(big.NewInt(420))
// Fees must be enforced for this test
service.enforceFees = true
// Create a mock transaction and sign using the
// sender's key
tx := mockNoneZeroGasLimitGasPriceTx(100000000000000, big.NewInt(1))
// Make sure the gas price is not 0 on the dummy tx
if tx.GasPrice().Cmp(common.Big0) == 0 {
t.Fatal("gas price is 0")
}
// Make sure the balance is equal to gas price * gas limit
// Get state
state, err := service.bc.State()
if err != nil {
t.Fatal("Cannot get state db")
}
balance := state.GetBalance(sender)
if balance.Cmp(new(big.Int).Mul(tx.GasPrice(), big.NewInt(int64(tx.Gas())))) != 0 {
t.Fatal("balance mismatch")
}
// Sign the dummy tx with the sender key
signedTx, err := types.SignTx(tx, signer, key)
if err != nil {
t.Fatal(err)
}
// Verify the fee of the signed tx, ensure it does not error
if err := service.verifyFee(signedTx); err != nil {
t.Fatal(err)
}
badTx := mockNoneZeroGasLimitGasPriceTx(100000000000000, big.NewInt(2))
// Make sure the gas price is not 0 on the dummy tx
if tx.GasPrice().Cmp(common.Big0) == 0 {
t.Fatal("gas price is 0")
}
// Make sure the balance is not equal to gas price * gas limit
if balance.Cmp(new(big.Int).Mul(badTx.GasPrice(), big.NewInt(int64(badTx.Gas())))) > 0 {
t.Fatal("balance match")
}
// Sign the dummy tx with the sender key
signedTx, err = types.SignTx(badTx, signer, key)
if err != nil {
t.Fatal(err)
}
// Verify the fee of the signed tx, ensure it does not error
if err := service.verifyFee(signedTx); err == nil {
t.Fatal(err)
}
}

func TestZeroGasPriceTransactionsUsingBobaAsFeeToken(t *testing.T) {
service, _, _, err := newTestSyncService(false, nil)
if err != nil {
Expand Down Expand Up @@ -802,7 +861,7 @@ func TestInsufficientGasForL1SecurityFee(t *testing.T) {
t.Fatal(err)
}
// Create a mock transaction
tx := mockNoneZeroGasLimiteTx(100)
tx := mockNoneZeroGasLimitTx(100)
// Create oracle
service.RollupGpo = gasprice.NewRollupOracle()
// Get state
Expand Down Expand Up @@ -831,7 +890,7 @@ func TestInsufficientGasForL1SecurityFee(t *testing.T) {
t.Fatal("err is nil")
}
// Type 2 -- fee / l2GasPrice <= tx.Gas
tx = mockNoneZeroGasLimiteTx(30000)
tx = mockNoneZeroGasLimitTx(30000)
overhead = big.NewInt(1)
state.SetState(rcfg.L2GasPriceOracleAddress, rcfg.OverheadSlot, common.BigToHash(overhead))
_, _ = state.Commit(false)
Expand Down Expand Up @@ -1242,7 +1301,7 @@ func mockTx() *types.Transaction {
return tx
}

func mockNoneZeroGasLimiteTx(gasLimit uint64) *types.Transaction {
func mockNoneZeroGasLimitTx(gasLimit uint64) *types.Transaction {
address := make([]byte, 20)
rand.Read(address)

Expand Down Expand Up @@ -1270,6 +1329,34 @@ func mockNoneZeroGasLimiteTx(gasLimit uint64) *types.Transaction {
return tx
}

func mockNoneZeroGasLimitGasPriceTx(gasLimit uint64, gasPrice *big.Int) *types.Transaction {
address := make([]byte, 20)
rand.Read(address)

target := common.BytesToAddress(address)
timestamp := uint64(0)

rand.Read(address)
l1TxOrigin := common.BytesToAddress(address)

data := []byte{0x00, 0x00}
l1BlockNumber := big.NewInt(0)

tx := types.NewTransaction(0, target, big.NewInt(0), gasLimit, gasPrice, data)
meta := types.NewTransactionMeta(
l1BlockNumber,
timestamp,
[]byte{0},
&l1TxOrigin,
types.QueueOriginSequencer,
nil,
nil,
nil,
)
tx.SetTransactionMeta(meta)
return tx
}

func setMockTxL1Timestamp(tx *types.Transaction, ts uint64) *types.Transaction {
meta := tx.GetMeta()
meta.L1Timestamp = ts
Expand Down
81 changes: 53 additions & 28 deletions ops_boba/api/metatransaction-api/metaTransaction_getTestnetETH.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,8 @@ const ethers = require('ethers')
const YAML = require('yaml')
const fs = require('fs')

// Load env
const file = fs.readFileSync('./env.yml', 'utf8')
const env = YAML.parse(file)
const L2_NODE_WEB3_URL = env.L2_NODE_WEB3_TESTNET_URL === undefined ? env.L2_NODE_WEB3_URL : env.L2_NODE_WEB3_TESTNET_URL
const PRIVATE_KEY = env.PRIVATE_KEY_FAUCET
const BOBA_AUTHENTICATEDFAUCET_ADDRESS = env.BOBA_AUTHENTICATEDFAUCET_TESTNET_ADDRESS

// Get provider and wallet
const l2Provider = new ethers.providers.JsonRpcProvider(L2_NODE_WEB3_URL)
const l2Wallet = new ethers.Wallet(PRIVATE_KEY).connect(l2Provider)

// ABI
const TwitterAuthenticatedFaucetInterface = new ethers.utils.Interface([
'function sendFundsMeta(address,string,bytes32,bytes)',
])

// Load contracts
const Boba_AuthenticatedFaucet = new ethers.Contract(
BOBA_AUTHENTICATEDFAUCET_ADDRESS,
TwitterAuthenticatedFaucetInterface,
l2Wallet
)
// Support local tests
require('dotenv').config()

const headers = {
'Access-Control-Allow-Origin': '*',
Expand All @@ -36,14 +16,58 @@ const headers = {
'Permissions-Policy': '*',
}

// Load contracts
const loadContracts = () => {
// Load env
let env = process.env
if (fs.existsSync('./env.yml')) {
const file = fs.readFileSync('./env.yml', 'utf8')
env = YAML.parse(file)
}
const L2_NODE_WEB3_URL =
env.L2_NODE_WEB3_TESTNET_URL === undefined
? env.L2_NODE_WEB3_URL
: env.L2_NODE_WEB3_TESTNET_URL
const PRIVATE_KEY = env.PRIVATE_KEY_FAUCET
const BOBA_AUTHENTICATEDFAUCET_ADDRESS =
env.BOBA_AUTHENTICATEDFAUCET_TESTNET_ADDRESS

// Get provider and wallet
const l2Provider = new ethers.providers.JsonRpcProvider(L2_NODE_WEB3_URL)
const l2Wallet = new ethers.Wallet(PRIVATE_KEY).connect(l2Provider)

// ABI
const TwitterAuthenticatedFaucetInterface = new ethers.utils.Interface([
'function sendFundsMeta(address,string,bytes32,bytes)',
])

// Load contracts
const Boba_AuthenticatedFaucet = new ethers.Contract(
BOBA_AUTHENTICATEDFAUCET_ADDRESS,
TwitterAuthenticatedFaucetInterface,
l2Wallet
)
return Boba_AuthenticatedFaucet
}

// Verify message and send to node if it's correct
module.exports.mainnetHandler = async (event, context, callback) => {
const body = JSON.parse(event.body)

const { hashedMsg, signature, tweetId, walletAddress } = body

const Boba_AuthenticatedFaucet = loadContracts()

// Send transaction to node
try {
console.log("SendFundsMeta: ", walletAddress, tweetId, hashedMsg, signature, L2_NODE_WEB3_URL)
console.log(
'SendFundsMeta: ',
walletAddress,
tweetId,
hashedMsg,
signature,
L2_NODE_WEB3_URL
)

await Boba_AuthenticatedFaucet.estimateGas.sendFundsMeta(
walletAddress,
Expand All @@ -58,8 +82,7 @@ module.exports.mainnetHandler = async (event, context, callback) => {
hashedMsg,
signature
)
await execTx.wait();

await execTx.wait()
} catch (err) {
console.error(err)
return callback(null, {
Expand All @@ -81,9 +104,12 @@ module.exports.rinkebyHandler = async (event, context, callback) => {
const body = JSON.parse(event.body)

const { hashedMsg, signature, tweetId, walletAddress } = body

const Boba_AuthenticatedFaucet = loadContracts()

// Send transaction to node
try {
console.log("SendFundsMeta: ", walletAddress, tweetId, hashedMsg, signature)
console.log('SendFundsMeta: ', walletAddress, tweetId, hashedMsg, signature)

await Boba_AuthenticatedFaucet.estimateGas.sendFundsMeta(
walletAddress,
Expand All @@ -98,8 +124,7 @@ module.exports.rinkebyHandler = async (event, context, callback) => {
hashedMsg,
signature
)
await execTx.wait();

await execTx.wait()
} catch (err) {
console.error(err)
return callback(null, {
Expand Down