Skip to content

Commit

Permalink
feat: raw tx (ethereum#255)
Browse files Browse the repository at this point in the history
* Add raw transaction to tx meta

* Add data to client

* Remove encoding from to OVM message

* Correctly encode apply transaction

* Transform v to 1 byte

* Fix lint err

* rpc: add rawTransaction

* syncservice: clean up serialization

* enums: temp fix

* enums: safety

Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com>
  • Loading branch information
karlfloersch and tynes authored Mar 9, 2021
1 parent dc2d104 commit 31b5deb
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 124 deletions.
4 changes: 2 additions & 2 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ func TestBlockMetaStorage(t *testing.T) {

index1 := uint64(1)
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
tx1Meta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, &index1, nil)
tx1Meta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, &index1, nil, nil)
tx1.SetTransactionMeta(tx1Meta)

WriteTransactionMeta(db, index1, tx1.GetMeta())
Expand Down Expand Up @@ -464,7 +464,7 @@ func TestBlockMetaStorage(t *testing.T) {

index2 := uint64(2)
tx2 := types.NewTransaction(2, common.HexToAddress("0x02"), big.NewInt(2), 2, big.NewInt(2), nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber, 0, &addr, types.SighashEthSign, types.QueueOriginSequencer, nil, nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber, 0, &addr, types.SighashEthSign, types.QueueOriginSequencer, nil, nil, nil)
tx2.SetTransactionMeta(tx2Meta)

WriteTransactionMeta(db, index2, tx2.GetMeta())
Expand Down
6 changes: 3 additions & 3 deletions core/rawdb/accessors_indexes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ func TestLookupStorage(t *testing.T) {
l1BlockNumber2 := big.NewInt(2)

tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx1Meta := types.NewTransactionMeta(l1BlockNumber1, 0, &sender1, types.SighashEIP155, types.QueueOriginSequencer, nil, nil)
tx1Meta := types.NewTransactionMeta(l1BlockNumber1, 0, &sender1, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx1.SetTransactionMeta(tx1Meta)

tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
tx2Meta := types.NewTransactionMeta(l1BlockNumber2, 0, &sender2, types.SighashEIP155, types.QueueOriginSequencer, nil, nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber2, 0, &sender2, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx2.SetTransactionMeta(tx2Meta)

tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
tx3Meta := types.NewTransactionMeta(l1BlockNumber1, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil)
tx3Meta := types.NewTransactionMeta(l1BlockNumber1, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx3.SetTransactionMeta(tx3Meta)

txs := []*types.Transaction{tx1, tx2, tx3}
Expand Down
72 changes: 1 addition & 71 deletions core/state_transition_ovm.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package core

import (
"bytes"
"fmt"
"math/big"

Expand Down Expand Up @@ -79,56 +78,13 @@ func AsOvmMessage(tx *types.Transaction, signer types.Signer, decompressor commo
return msg, nil
}

v, r, s := tx.RawSignatureValues()

// V parameter here will include the chain ID, so we need to recover the original V. If the V
// does not equal zero or one, we have an invalid parameter and need to throw an error.
// This is technically a duplicate check because it happens inside of
// `tx.AsMessage` as well.
v = new(big.Int).SetUint64(v.Uint64() - 35 - 2*tx.ChainId().Uint64())
if v.Uint64() != 0 && v.Uint64() != 1 {
index := tx.GetMeta().Index
if index == nil {
return msg, fmt.Errorf("invalid signature v parameter: %d", v.Uint64())
}
}

// Since we use a fixed encoding, we need to insert some placeholder address to represent that
// the user wants to create a contract (in this case, the zero address).
var target common.Address
if tx.To() == nil {
target = ZeroAddress
} else {
target = *tx.To()
}

// Divide the gas price by one million to compress it
// before it is send to the sequencer entrypoint. This is to save
// space on calldata.
gasPrice := new(big.Int).Div(msg.GasPrice(), new(big.Int).SetUint64(1000000))

// Sequencer uses a custom encoding structure --
// We originally receive sequencer transactions encoded in this way, but we decode them before
// inserting into Geth so we can make transactions easily parseable. However, this means that
// we need to re-encode the transactions before executing them.
var data = new(bytes.Buffer)
data.WriteByte(getSignatureType(msg)) // 1 byte: 00 == EIP 155, 02 == ETH Sign Message
data.Write(fillBytes(r, 32)) // 32 bytes: Signature `r` parameter
data.Write(fillBytes(s, 32)) // 32 bytes: Signature `s` parameter
data.Write(fillBytes(v, 1)) // 1 byte: Signature `v` parameter
data.Write(fillBytes(big.NewInt(int64(msg.Gas())), 3)) // 3 bytes: Gas limit
data.Write(fillBytes(gasPrice, 3)) // 3 bytes: Gas price
data.Write(fillBytes(big.NewInt(int64(msg.Nonce())), 3)) // 3 bytes: Nonce
data.Write(target.Bytes()) // 20 bytes: Target address
data.Write(msg.Data()) // ?? bytes: Transaction data

// Sequencer transactions get sent to the "sequencer entrypoint," a contract that decompresses
// the incoming transaction data.
outmsg, err := modMessage(
msg,
msg.From(),
&decompressor,
data.Bytes(),
tx.GetMeta().RawTransaction,
msg.Gas(),
)

Expand Down Expand Up @@ -201,18 +157,6 @@ func modMessage(
return outmsg, nil
}

func getSignatureType(
msg Message,
) uint8 {
if msg.SignatureHashType() == 0 {
return 0
} else if msg.SignatureHashType() == 1 {
return 2
} else {
return 1
}
}

func getQueueOrigin(
queueOrigin *big.Int,
) (types.QueueOrigin, error) {
Expand All @@ -226,17 +170,3 @@ func getQueueOrigin(
return types.QueueOriginSequencer, fmt.Errorf("invalid queue origin: %d", queueOrigin)
}
}

func fillBytes(x *big.Int, size int) []byte {
b := x.Bytes()
switch {
case len(b) > size:
panic("math/big: value won't fit requested size")
case len(b) == size:
return b
default:
buf := make([]byte, size)
copy(buf[size-len(b):], b)
return buf
}
}
2 changes: 1 addition & 1 deletion core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit
data = common.CopyBytes(data)
}

meta := NewTransactionMeta(nil, 0, nil, SighashEIP155, QueueOriginSequencer, nil, nil)
meta := NewTransactionMeta(nil, 0, nil, SighashEIP155, QueueOriginSequencer, nil, nil, nil)

d := txdata{
AccountNonce: nonce,
Expand Down
21 changes: 19 additions & 2 deletions core/types/transaction_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ type TransactionMeta struct {
// The canonical transaction chain index
Index *uint64 `json:"index" gencodec:"required"`
// The queue index, nil for queue origin sequencer transactions
QueueIndex *uint64 `json:"queueIndex" gencodec:"required"`
QueueIndex *uint64 `json:"queueIndex" gencodec:"required"`
RawTransaction []byte `json:"rawTransaction" gencodec:"required"`
}

// NewTransactionMeta creates a TransactionMeta
func NewTransactionMeta(l1BlockNumber *big.Int, l1timestamp uint64, l1MessageSender *common.Address, sighashType SignatureHashType, queueOrigin QueueOrigin, index *uint64, queueIndex *uint64) *TransactionMeta {
func NewTransactionMeta(l1BlockNumber *big.Int, l1timestamp uint64, l1MessageSender *common.Address, sighashType SignatureHashType, queueOrigin QueueOrigin, index *uint64, queueIndex *uint64, rawTransaction []byte) *TransactionMeta {
return &TransactionMeta{
L1BlockNumber: l1BlockNumber,
L1Timestamp: l1timestamp,
Expand All @@ -44,6 +45,7 @@ func NewTransactionMeta(l1BlockNumber *big.Int, l1timestamp uint64, l1MessageSen
QueueOrigin: big.NewInt(int64(queueOrigin)),
Index: index,
QueueIndex: queueIndex,
RawTransaction: rawTransaction,
}
}

Expand Down Expand Up @@ -121,6 +123,14 @@ func TxMetaDecode(input []byte) (*TransactionMeta, error) {
meta.QueueIndex = &queueIndex
}

raw, err := common.ReadVarBytes(b, 0, 130000, "RawTransaction")
if err != nil {
return nil, err
}
if !isNullValue(raw) {
meta.RawTransaction = raw
}

return &meta, nil
}

Expand Down Expand Up @@ -181,6 +191,13 @@ func TxMetaEncode(meta *TransactionMeta) []byte {
common.WriteVarBytes(b, 0, qi.Bytes())
}

rawTransaction := meta.RawTransaction
if rawTransaction == nil {
common.WriteVarBytes(b, 0, getNullValue())
} else {
common.WriteVarBytes(b, 0, rawTransaction)
}

return b.Bytes()
}

Expand Down
81 changes: 44 additions & 37 deletions core/types/transaction_meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,60 @@ var (
l1BlockNumber = big.NewInt(0)

txMetaSerializationTests = []struct {
l1BlockNumber *big.Int
l1Timestamp uint64
msgSender *common.Address
sighashType SignatureHashType
queueOrigin QueueOrigin
l1BlockNumber *big.Int
l1Timestamp uint64
msgSender *common.Address
sighashType SignatureHashType
queueOrigin QueueOrigin
rawTransaction []byte
}{
{
l1BlockNumber: l1BlockNumber,
l1Timestamp: 100,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
l1BlockNumber: l1BlockNumber,
l1Timestamp: 100,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{255, 255, 255, 255},
},
{
l1BlockNumber: nil,
l1Timestamp: 45,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
l1BlockNumber: nil,
l1Timestamp: 45,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{42, 69, 42, 69},
},
{
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
rawTransaction: []byte{0, 0, 0, 0},
},
{
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
rawTransaction: []byte{0, 0, 0, 0},
},
{
l1BlockNumber: nil,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
l1BlockNumber: nil,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{0, 0, 0, 0},
},
{
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{0, 0, 0, 0},
},
}

Expand All @@ -81,7 +88,7 @@ var (

func TestTransactionMetaEncode(t *testing.T) {
for _, test := range txMetaSerializationTests {
txmeta := NewTransactionMeta(test.l1BlockNumber, test.l1Timestamp, test.msgSender, test.sighashType, test.queueOrigin, nil, nil)
txmeta := NewTransactionMeta(test.l1BlockNumber, test.l1Timestamp, test.msgSender, test.sighashType, test.queueOrigin, nil, nil, test.rawTransaction)

encoded := TxMetaEncode(txmeta)
decoded, err := TxMetaDecode(encoded)
Expand All @@ -98,7 +105,7 @@ func TestTransactionMetaEncode(t *testing.T) {

func TestTransactionSighashEncode(t *testing.T) {
for _, test := range txMetaSighashEncodeTests {
txmeta := NewTransactionMeta(l1BlockNumber, 0, &addr, test.input, QueueOriginSequencer, nil, nil)
txmeta := NewTransactionMeta(l1BlockNumber, 0, &addr, test.input, QueueOriginSequencer, nil, nil, nil)
encoded := TxMetaEncode(txmeta)
decoded, err := TxMetaDecode(encoded)

Expand Down
6 changes: 3 additions & 3 deletions core/types/transaction_signing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestOVMSigner(t *testing.T) {
key, _ := defaultTestKey()

tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil, nil)
tx.SetTransactionMeta(txMeta)

var err error
Expand Down Expand Up @@ -175,7 +175,7 @@ func TestOVMSignerHash(t *testing.T) {

// The signature hash should be different when using `SighashEthSign`
txEthSign := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil, nil)
txEthSign.SetTransactionMeta(txMeta)

hashEthSign := signer.Hash(txEthSign)
Expand Down Expand Up @@ -216,7 +216,7 @@ func TestOVMSignerSender(t *testing.T) {
// Create a transaction with EthSign signature hash, sign the transaction,
// recover the address and assert that the address matches the key.
txEthSign := NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil)
txMeta := NewTransactionMeta(nil, 0, nil, SighashEthSign, QueueOriginSequencer, nil, nil, nil)
txEthSign.SetTransactionMeta(txMeta)

txEthSign, err = SignTx(txEthSign, signer, key)
Expand Down
10 changes: 6 additions & 4 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,7 @@ type RPCTransaction struct {
L1Timestamp hexutil.Uint64 `json:"l1Timestamp"`
Index *hexutil.Uint64 `json:"index"`
QueueIndex *hexutil.Uint64 `json:"queueIndex"`
RawTransaction hexutil.Bytes `json:"rawTransaction"`
}

// newRPCTransaction returns a transaction that will serialize to the RPC
Expand Down Expand Up @@ -1210,6 +1211,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
}

if meta := tx.GetMeta(); meta != nil {
result.RawTransaction = meta.RawTransaction
result.L1TxOrigin = meta.L1MessageSender
result.L1Timestamp = (hexutil.Uint64)(meta.L1Timestamp)
if meta.L1BlockNumber != nil {
Expand Down Expand Up @@ -1547,12 +1549,12 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
}
if args.To == nil {
tx := types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx.SetTransactionMeta(txMeta)
return tx
}
tx := types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, args.L1MessageSender, args.SignatureHashType, types.QueueOriginSequencer, nil, nil)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, args.L1MessageSender, args.SignatureHashType, types.QueueOriginSequencer, nil, nil, nil)
tx.SetTransactionMeta(txMeta)
return tx
}
Expand Down Expand Up @@ -1651,7 +1653,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
return common.Hash{}, errors.New("Gas price must be a multiple of 1,000,000 wei")
}
// L1Timestamp and L1BlockNumber will be set by the miner
txMeta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil)
txMeta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx.SetTransactionMeta(txMeta)
return SubmitTransaction(ctx, s.b, tx)
}
Expand All @@ -1678,7 +1680,7 @@ func (s *PublicTransactionPoolAPI) SendRawEthSignTransaction(ctx context.Context
return common.Hash{}, errors.New("Gas price must be a multiple of 1,000,000 wei")
}
// L1Timestamp and L1BlockNumber will be set by the miner
txMeta := types.NewTransactionMeta(nil, 0, nil, types.SighashEthSign, types.QueueOriginSequencer, nil, nil)
txMeta := types.NewTransactionMeta(nil, 0, nil, types.SighashEthSign, types.QueueOriginSequencer, nil, nil, nil)
tx.SetTransactionMeta(txMeta)
return SubmitTransaction(ctx, s.b, tx)
}
Expand Down
Loading

0 comments on commit 31b5deb

Please sign in to comment.