Skip to content

Commit

Permalink
feat: add type to receipt and refactor reward calculation function
Browse files Browse the repository at this point in the history
  • Loading branch information
paologalligit committed Feb 20, 2025
1 parent 1b887cd commit 8daad08
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 43 deletions.
2 changes: 2 additions & 0 deletions api/transactions/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,14 @@ func getTxReceipt(t *testing.T) {
t.Fatal(err)
}
assert.Equal(t, receipt.GasUsed, legacyTx.Gas(), "receipt gas used not equal to transaction gas")
assert.Equal(t, receipt.Type, legacyTx.Type())

r = httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String()+"/receipt", 200)
if err := json.Unmarshal(r, &receipt); err != nil {
t.Fatal(err)
}
assert.Equal(t, receipt.GasUsed, legacyTx.Gas(), "receipt gas used not equal to transaction gas")
assert.Equal(t, receipt.Type, dynFeeTx.Type())
}

func sendLegacyTx(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions api/transactions/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type ReceiptMeta struct {

// Receipt for json marshal
type Receipt struct {
Type uint8 `json:"type,omitempty"`
GasUsed uint64 `json:"gasUsed"`
GasPayer thor.Address `json:"gasPayer"`
Paid *math.HexOrDecimal256 `json:"paid"`
Expand Down Expand Up @@ -121,6 +122,7 @@ func convertReceipt(txReceipt *tx.Receipt, header *block.Header, tx *tx.Transact
return nil, err
}
receipt := &Receipt{
Type: txReceipt.Type,
GasUsed: txReceipt.GasUsed,
GasPayer: txReceipt.GasPayer,
Paid: &paid,
Expand Down
22 changes: 7 additions & 15 deletions api/transactions/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func TestErrorWhileRetrievingTxOriginInConvertReceipt(t *testing.T) {
func TestConvertReceiptWhenTxHasNoClauseTo(t *testing.T) {
value := big.NewInt(100)
txs := []*tx.Transaction{
newLegacyTx(tx.NewClause(nil).WithValue(value)),
newDynFeeTx(tx.NewClause(nil).WithValue(value)),
newTx(tx.NewClause(nil).WithValue(value), tx.LegacyTxType),
newTx(tx.NewClause(nil).WithValue(value), tx.DynamicFeeTxType),
}
for _, tr := range txs {
b := new(block.Builder).Build()
Expand All @@ -64,8 +64,8 @@ func TestConvertReceipt(t *testing.T) {
addr := randAddress()

txs := []*tx.Transaction{
newLegacyTx(tx.NewClause(&addr).WithValue(value)),
newDynFeeTx(tx.NewClause(&addr).WithValue(value)),
newTx(tx.NewClause(&addr).WithValue(value), tx.LegacyTxType),
newTx(tx.NewClause(&addr).WithValue(value), tx.DynamicFeeTxType),
}
for _, tr := range txs {
b := new(block.Builder).Build()
Expand All @@ -75,6 +75,7 @@ func TestConvertReceipt(t *testing.T) {
convRec, err := convertReceipt(receipt, header, tr)

assert.NoError(t, err)
assert.Equal(t, receipt.Type, convRec.Type)
assert.Equal(t, 1, len(convRec.Outputs))
assert.Equal(t, 1, len(convRec.Outputs[0].Events))
assert.Equal(t, 1, len(convRec.Outputs[0].Transfers))
Expand Down Expand Up @@ -114,17 +115,8 @@ func newReceipt() *tx.Receipt {
}
}

func newLegacyTx(clause *tx.Clause) *tx.Transaction {
tx := tx.NewTxBuilder(tx.LegacyTxType).
Clause(clause).
MustBuild()
pk, _ := crypto.GenerateKey()
sig, _ := crypto.Sign(tx.SigningHash().Bytes(), pk)
return tx.WithSignature(sig)
}

func newDynFeeTx(clause *tx.Clause) *tx.Transaction {
tx := tx.NewTxBuilder(tx.DynamicFeeTxType).
func newTx(clause *tx.Clause, txType int) *tx.Transaction {
tx := tx.NewTxBuilder(txType).
Clause(clause).
MustBuild()
pk, _ := crypto.GenerateKey()
Expand Down
15 changes: 14 additions & 1 deletion consensus/fork/galactica.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ func GalacticaGasPrice(tr *tx.Transaction, baseGasPrice *big.Int, galacticaItems

feeItems := GalacticaTxGasPriceAdapter(tr, gasPrice)
// This gasPrice is the same that will be used when refunding the user
// it takes into account the priority fee that will be paid to the validator
// it takes into account the priority fee that will be paid to the validator and the base fee that will be implicitly burned
// tracked by Energy.TotalAddSub
return math.BigMin(new(big.Int).Add(feeItems.MaxPriorityFee, galacticaItems.BaseFee), feeItems.MaxFee)
}

Expand All @@ -144,3 +145,15 @@ func GalacticaPriorityPrice(tr *tx.Transaction, baseGasPrice, provedWork *big.In
*/
return math.BigMin(feeItems.MaxPriorityFee, new(big.Int).Sub(feeItems.MaxFee, galacticaItems.BaseFee))
}

func CalculateReward(gasUsed uint64, rewardGasPrice, rewardRatio *big.Int, isGalactica bool) *big.Int {
reward := new(big.Int).SetUint64(gasUsed)
reward.Mul(reward, rewardGasPrice)
if isGalactica {
return reward
}
// Calculating the 30% of the reward
reward.Mul(reward, rewardRatio)
reward.Div(reward, big.NewInt(1e18))
return reward
}
47 changes: 47 additions & 0 deletions consensus/fork/galactica_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,50 @@ func TestGalacticaPriorityPrice(t *testing.T) {
})
}
}

func TestCalculateReward(t *testing.T) {
rewardRatio := thor.InitialRewardRatio
tests := []struct {
name string
gasUsed uint64
rewardGasPrice *big.Int
isGalactica bool
expectedReward *big.Int
}{
{
name: "Galactica active, full reward",
gasUsed: 1000,
rewardGasPrice: big.NewInt(100),
isGalactica: true,
expectedReward: big.NewInt(100000),
},
{
name: "Galactica inactive, 30% reward",
gasUsed: 1000,
rewardGasPrice: big.NewInt(100),
isGalactica: false,
expectedReward: big.NewInt(30000),
},
{
name: "Galactica active, zero gas used",
gasUsed: 0,
rewardGasPrice: big.NewInt(100),
isGalactica: true,
expectedReward: big.NewInt(0),
},
{
name: "Galactica inactive, zero gas used",
gasUsed: 0,
rewardGasPrice: big.NewInt(100),
isGalactica: false,
expectedReward: big.NewInt(0),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reward := CalculateReward(tt.gasUsed, tt.rewardGasPrice, rewardRatio, tt.isGalactica)
assert.Equal(t, tt.expectedReward, reward)
})
}
}
6 changes: 2 additions & 4 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ func (rt *Runtime) PrepareTransaction(tx *tx.Transaction) (*TransactionExecutor,
finalized = true

receipt := &Tx.Receipt{
Type: tx.Type(),
Reverted: reverted,
Outputs: txOutputs,
GasUsed: tx.Gas() - leftOverGas,
Expand All @@ -523,11 +524,8 @@ func (rt *Runtime) PrepareTransaction(tx *tx.Transaction) (*TransactionExecutor,
return nil, err
}
rewardGasPrice := fork.GalacticaPriorityPrice(tx, baseGasPrice, provedWork, &fork.GalacticaItems{IsActive: galactica, BaseFee: rt.ctx.BaseFee})
reward := fork.CalculateReward(receipt.GasUsed, rewardGasPrice, rewardRatio, galactica)

reward := new(big.Int).SetUint64(receipt.GasUsed)
reward.Mul(reward, rewardGasPrice)
reward.Mul(reward, rewardRatio)
reward.Div(reward, big.NewInt(1e18))
if err := builtin.Energy.Native(rt.state, rt.ctx.Time).Add(rt.ctx.Beneficiary, reward); err != nil {
return nil, err
}
Expand Down
155 changes: 154 additions & 1 deletion tx/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,40 @@
package tx

import (
"bytes"
"errors"
"io"
"math/big"

"github.com/ethereum/go-ethereum/rlp"
"github.com/vechain/thor/v2/thor"
"github.com/vechain/thor/v2/trie"
)

var (
errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
errShortTypedReceipt = errors.New("typed receipt too short")
)

// Receipt represents the results of a transaction.
type Receipt struct {
// transaction type this receipt is associated with
Type byte
// gas used by this tx
GasUsed uint64
// the one who paid for gas
GasPayer thor.Address
// energy paid for used gas
Paid *big.Int
// energy reward given to block proposer
Reward *big.Int
// if the tx reverted
Reverted bool
// outputs of clauses in tx
Outputs []*Output
}

type receiptRLP struct {
// gas used by this tx
GasUsed uint64
// the one who paid for gas
Expand Down Expand Up @@ -56,9 +81,137 @@ func (rs derivableReceipts) Len() int {
return len(rs)
}
func (rs derivableReceipts) GetRlp(i int) []byte {
data, err := rlp.EncodeToBytes(rs[i])
data, err := rs[i].MarshalBinary()
if err != nil {
panic(err)
}
return data
}

// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream. If no post state is present, byzantium fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
data := &receiptRLP{
r.GasUsed, r.GasPayer, r.Paid, r.Reward, r.Reverted, r.Outputs,
}
if r.Type == LegacyTxType {
return rlp.Encode(w, data)
}

buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf)
buf.Reset()
buf.WriteByte(r.Type)
if err := rlp.Encode(buf, data); err != nil {
return err
}
return rlp.Encode(w, buf.Bytes())
}

// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
kind, _, err := s.Kind()
switch {
case err != nil:
return err
case kind == rlp.List:
// It's a legacy receipt.
var dec receiptRLP
if err := s.Decode(&dec); err != nil {
return err
}
r.Type = LegacyTxType
r.setFromRLP(dec)
case kind == rlp.String:
// It's an EIP-2718 typed tx receipt.
b, err := s.Bytes()
if err != nil {
return err
}
if len(b) == 0 {
return errEmptyTypedReceipt
}
r.Type = b[0]
switch r.Type {
case DynamicFeeTxType:
var dec receiptRLP
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
return err
}
r.setFromRLP(dec)
default:
return ErrTxTypeNotSupported
}
default:
return rlp.ErrExpectedList
}

return nil
}

func (r *Receipt) setFromRLP(dec receiptRLP) {
r.GasUsed = dec.GasUsed
r.GasPayer = dec.GasPayer
r.Paid = dec.Paid
r.Reward = dec.Reward
r.Reverted = dec.Reverted
r.Outputs = dec.Outputs
}

// MarshalBinary returns the consensus encoding of the receipt.
func (r *Receipt) MarshalBinary() ([]byte, error) {
if r.Type == LegacyTxType {
return rlp.EncodeToBytes(r)
}
data := &receiptRLP{
r.GasUsed, r.GasPayer, r.Paid, r.Reward, r.Reverted, r.Outputs,
}
var buf bytes.Buffer
err := r.encodeTyped(data, &buf)
return buf.Bytes(), err
}

// UnmarshalBinary decodes the consensus encoding of receipts.
// It supports legacy RLP receipts and EIP-2718 typed receipts.
func (r *Receipt) UnmarshalBinary(b []byte) error {
if len(b) > 0 && b[0] > 0x7f {
// It's a legacy receipt decode the RLP
var data receiptRLP
err := rlp.DecodeBytes(b, &data)
if err != nil {
return err
}
r.Type = LegacyTxType
r.setFromRLP(data)
return nil
}
// It's an EIP2718 typed transaction envelope.
return r.decodeTyped(b)
}

// encodeTyped writes the canonical encoding of a typed receipt to w.
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
w.WriteByte(r.Type)
return rlp.Encode(w, data)
}

// decodeTyped decodes a typed receipt from the canonical format.
func (r *Receipt) decodeTyped(b []byte) error {
if len(b) <= 1 {
return errShortTypedReceipt
}
switch b[0] {
case DynamicFeeTxType:
var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
return err
}
r.Type = b[0]
r.setFromRLP(data)
return nil
default:
return ErrTxTypeNotSupported
}
}
Loading

0 comments on commit 8daad08

Please sign in to comment.