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

Typed receipts #986

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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
58 changes: 27 additions & 31 deletions api/transactions/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ func TestErrorWhileRetrievingTxOriginInConvertReceipt(t *testing.T) {
tr := tx.NewTxBuilder(txType).MustBuild()
header := &block.Header{}
receipt := &tx.Receipt{
Reward: big.NewInt(100),
Paid: big.NewInt(10),
ReceiptBody: tx.ReceiptBody{
Reward: big.NewInt(100),
Paid: big.NewInt(10),
},
}

convRec, err := convertReceipt(receipt, header, tr)
Expand All @@ -42,8 +44,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 +66,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 +77,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 All @@ -95,36 +98,29 @@ func randAddress() (addr thor.Address) {

func newReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randomBytes32()},
Data: randomBytes32().Bytes(),
}},
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randomBytes32()},
Data: randomBytes32().Bytes(),
}},
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
},
},
Reward: big.NewInt(100),
Paid: big.NewInt(10),
},
Reward: big.NewInt(100),
Paid: big.NewInt(10),
}
}

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
16 changes: 9 additions & 7 deletions api/transfers/transfers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,15 @@ func createDb(t *testing.T) *logdb.LogDB {
// Utilities functions
func newReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Transfers: tx.Transfers{{
Sender: datagen.RandAddress(),
Recipient: datagen.RandAddress(),
Amount: new(big.Int).SetBytes(datagen.RandAddress().Bytes()),
}},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{
Transfers: tx.Transfers{{
Sender: datagen.RandAddress(),
Recipient: datagen.RandAddress(),
Amount: new(big.Int).SetBytes(datagen.RandAddress().Bytes()),
}},
},
},
},
}
Expand Down
6 changes: 4 additions & 2 deletions cmd/thor/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,10 @@ func initChainRepository(gene *genesis.Genesis, mainDB *muxdb.MuxDB, logDB *logd
}
w := logDB.NewWriter()
if err := w.Write(genesisBlock, tx.Receipts{{
Outputs: []*tx.Output{
{Events: genesisEvents, Transfers: genesisTransfers},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{Events: genesisEvents, Transfers: genesisTransfers},
},
},
}}); err != nil {
return nil, errors.Wrap(err, "write genesis logs")
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
}
// Returning 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)
})
}
}
58 changes: 32 additions & 26 deletions logdb/logdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,46 +40,52 @@ func randBytes32() (b thor.Bytes32) {

func newReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randBytes32()},
Data: randBytes32().Bytes(),
}},
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randBytes32()},
Data: randBytes32().Bytes(),
}},
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
},
},
},
}
}

func newEventOnlyReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randBytes32()},
Data: randBytes32().Bytes(),
}},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{
Events: tx.Events{{
Address: randAddress(),
Topics: []thor.Bytes32{randBytes32()},
Data: randBytes32().Bytes(),
}},
},
},
},
}
}

func newTransferOnlyReceipt() *tx.Receipt {
return &tx.Receipt{
Outputs: []*tx.Output{
{
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
ReceiptBody: tx.ReceiptBody{
Outputs: []*tx.Output{
{
Transfers: tx.Transfers{{
Sender: randAddress(),
Recipient: randAddress(),
Amount: new(big.Int).SetBytes(randAddress().Bytes()),
}},
},
},
},
}
Expand Down
16 changes: 8 additions & 8 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,13 @@ func (rt *Runtime) PrepareTransaction(tx *tx.Transaction) (*TransactionExecutor,
finalized = true

receipt := &Tx.Receipt{
Reverted: reverted,
Outputs: txOutputs,
GasUsed: tx.Gas() - leftOverGas,
GasPayer: payer,
Type: tx.Type(),
ReceiptBody: Tx.ReceiptBody{
Reverted: reverted,
Outputs: txOutputs,
GasUsed: tx.Gas() - leftOverGas,
GasPayer: payer,
},
}

receipt.Paid = new(big.Int).Mul(new(big.Int).SetUint64(receipt.GasUsed), gasPrice)
Expand All @@ -523,11 +526,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
Loading
Loading