-
Notifications
You must be signed in to change notification settings - Fork 324
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
[iip-15] Sharing gas-fee for DApps #3844
Changes from 3 commits
7ad87f0
ae8ccfa
5a40cc4
f3dc4e1
ec98dc3
bb5fef6
a00c4d2
16ab082
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,19 @@ var ( | |
ErrInconsistentNonce = errors.New("Nonce is not identical to executor nonce") | ||
) | ||
|
||
type ( | ||
// GetBlockHash gets block hash by height | ||
GetBlockHash func(uint64) (hash.Hash256, error) | ||
|
||
// DepositGasWithSGD deposits gas with Sharing of Gas-fee with DApps | ||
DepositGasWithSGD func(context.Context, protocol.StateManager, address.Address, *big.Int, *big.Int) (*action.TransactionLog, error) | ||
|
||
// SGDRegistry is the interface for handling Sharing of Gas-fee with DApps | ||
SGDRegistry interface { | ||
CheckContract(context.Context, string) (address.Address, uint64, bool, error) | ||
} | ||
) | ||
|
||
// CanTransfer checks whether the from account has enough balance | ||
func CanTransfer(db vm.StateDB, fromHash common.Address, balance *big.Int) bool { | ||
return db.GetBalance(fromHash).Cmp(balance) >= 0 | ||
|
@@ -191,7 +204,8 @@ func ExecuteContract( | |
sm protocol.StateManager, | ||
execution *action.Execution, | ||
getBlockHash GetBlockHash, | ||
depositGasFunc DepositGas, | ||
depositGasFunc DepositGasWithSGD, | ||
sgd SGDRegistry, | ||
) ([]byte, *action.Receipt, error) { | ||
ctx, span := tracer.NewSpan(ctx, "evm.ExecuteContract") | ||
defer span.End() | ||
|
@@ -219,7 +233,10 @@ func ExecuteContract( | |
} | ||
|
||
receipt.Status = uint64(statusCode) | ||
var burnLog *action.TransactionLog | ||
var ( | ||
depositLog, burnLog *action.TransactionLog | ||
consumedGas = depositGas - remainingGas | ||
) | ||
if featureCtx.FixDoubleChargeGas { | ||
// Refund all deposit and, actual gas fee will be subtracted when depositing gas fee to the rewarding protocol | ||
stateDB.AddBalance(ps.txCtx.Origin, big.NewInt(0).Mul(big.NewInt(0).SetUint64(depositGas), ps.txCtx.GasPrice)) | ||
|
@@ -228,19 +245,33 @@ func ExecuteContract( | |
remainingValue := new(big.Int).Mul(new(big.Int).SetUint64(remainingGas), ps.txCtx.GasPrice) | ||
stateDB.AddBalance(ps.txCtx.Origin, remainingValue) | ||
} | ||
if depositGas-remainingGas > 0 { | ||
if consumedGas > 0 { | ||
burnLog = &action.TransactionLog{ | ||
Type: iotextypes.TransactionLogType_GAS_FEE, | ||
Sender: actionCtx.Caller.String(), | ||
Recipient: "", // burned | ||
Amount: new(big.Int).Mul(new(big.Int).SetUint64(depositGas-remainingGas), ps.txCtx.GasPrice), | ||
Amount: new(big.Int).Mul(new(big.Int).SetUint64(consumedGas), ps.txCtx.GasPrice), | ||
} | ||
} | ||
} | ||
var depositLog *action.TransactionLog | ||
if depositGas-remainingGas > 0 { | ||
gasValue := new(big.Int).Mul(new(big.Int).SetUint64(depositGas-remainingGas), ps.txCtx.GasPrice) | ||
depositLog, err = depositGasFunc(ctx, sm, gasValue) | ||
if consumedGas > 0 { | ||
var ( | ||
receiver address.Address | ||
sharedGas uint64 | ||
sharedGasFee, totalGasFee *big.Int | ||
) | ||
if featureCtx.SharedGasWithDapp && sgd != nil { | ||
receiver, sharedGas, err = processSGD(ctx, sm, execution, consumedGas, sgd) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could be done inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if err != nil { | ||
return nil, nil, errors.Wrap(err, "failed to process Sharing of Gas-fee with DApps") | ||
} | ||
} | ||
if sharedGas > 0 { | ||
sharedGasFee = big.NewInt(int64(sharedGas)) | ||
sharedGasFee.Mul(sharedGasFee, ps.txCtx.GasPrice) | ||
} | ||
totalGasFee = new(big.Int).Mul(new(big.Int).SetUint64(consumedGas), ps.txCtx.GasPrice) | ||
depositLog, err = depositGasFunc(ctx, sm, receiver, totalGasFee, sharedGasFee) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
@@ -267,6 +298,24 @@ func ExecuteContract( | |
return retval, receipt, nil | ||
} | ||
|
||
func processSGD(ctx context.Context, sm protocol.StateManager, execution *action.Execution, consumedGas uint64, sgd SGDRegistry, | ||
) (address.Address, uint64, error) { | ||
if execution.Contract() == action.EmptyAddress { | ||
return nil, 0, nil | ||
} | ||
|
||
receiver, percentage, ok, err := sgd.CheckContract(ctx, execution.Contract()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pls provide the benchmark result for the change. Reading the contract directly for each tx in the block might cause performance regression. IMO, an indexer built for events emitted from the contract would be a better option There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, the actual implementation is PR3845, we can review and decide the implementation there |
||
if err != nil || !ok { | ||
return nil, 0, err | ||
} | ||
|
||
sharedGas := consumedGas * percentage / 100 | ||
if sharedGas > consumedGas { | ||
sharedGas = consumedGas | ||
} | ||
return receiver, sharedGas, nil | ||
} | ||
|
||
// ReadContractStorage reads contract's storage | ||
func ReadContractStorage( | ||
ctx context.Context, | ||
|
@@ -569,8 +618,9 @@ func SimulateExecution( | |
sm, | ||
ex, | ||
getBlockHash, | ||
func(context.Context, protocol.StateManager, *big.Int) (*action.TransactionLog, error) { | ||
func(context.Context, protocol.StateManager, address.Address, *big.Int, *big.Int) (*action.TransactionLog, error) { | ||
return nil, nil | ||
}, | ||
nil, | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ package evm | |
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/hex" | ||
"fmt" | ||
"math/big" | ||
|
@@ -40,12 +39,6 @@ type ( | |
// preimageMap records the preimage of hash reported by VM | ||
preimageMap map[common.Hash]protocol.SerializableBytes | ||
|
||
// GetBlockHash gets block hash by height | ||
GetBlockHash func(uint64) (hash.Hash256, error) | ||
|
||
// DepositGas deposits gas | ||
DepositGas func(context.Context, protocol.StateManager, *big.Int) (*action.TransactionLog, error) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these 2 are not used in here, move to |
||
// StateDBAdapter represents the state db adapter for evm to access iotx blockchain | ||
StateDBAdapter struct { | ||
sm protocol.StateManager | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,18 +29,19 @@ const ( | |
// Protocol defines the protocol of handling executions | ||
type Protocol struct { | ||
getBlockHash evm.GetBlockHash | ||
depositGas evm.DepositGas | ||
depositGas evm.DepositGasWithSGD | ||
addr address.Address | ||
sgd evm.SGDRegistry | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// NewProtocol instantiates the protocol of exeuction | ||
func NewProtocol(getBlockHash evm.GetBlockHash, depostGas evm.DepositGas) *Protocol { | ||
func NewProtocol(getBlockHash evm.GetBlockHash, depositGasWithSGD evm.DepositGasWithSGD, sgd evm.SGDRegistry) *Protocol { | ||
h := hash.Hash160b([]byte(_protocolID)) | ||
addr, err := address.FromBytes(h[:]) | ||
if err != nil { | ||
log.L().Panic("Error when constructing the address of vote protocol", zap.Error(err)) | ||
} | ||
return &Protocol{getBlockHash: getBlockHash, depositGas: depostGas, addr: addr} | ||
return &Protocol{getBlockHash: getBlockHash, depositGas: depositGasWithSGD, addr: addr, sgd: sgd} | ||
} | ||
|
||
// FindProtocol finds the registered protocol from registry | ||
|
@@ -65,7 +66,7 @@ func (p *Protocol) Handle(ctx context.Context, act action.Action, sm protocol.St | |
if !ok { | ||
return nil, nil | ||
} | ||
_, receipt, err := evm.ExecuteContract(ctx, sm, exec, p.getBlockHash, p.depositGas) | ||
_, receipt, err := evm.ExecuteContract(ctx, sm, exec, p.getBlockHash, p.depositGas, p.sgd) | ||
|
||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to execute contract") | ||
|
envestcc marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,17 @@ func (p *Protocol) Deposit( | |
sm protocol.StateManager, | ||
amount *big.Int, | ||
transactionLogType iotextypes.TransactionLogType, | ||
) (*action.TransactionLog, error) { | ||
// fallback to regular case by setting sgdAmount = nil | ||
return p.deposit(ctx, sm, nil, amount, nil, transactionLogType) | ||
} | ||
|
||
func (p *Protocol) deposit( | ||
ctx context.Context, | ||
sm protocol.StateManager, | ||
receiver address.Address, | ||
amount, sgdAmount *big.Int, | ||
transactionLogType iotextypes.TransactionLogType, | ||
) (*action.TransactionLog, error) { | ||
actionCtx := protocol.MustGetActionCtx(ctx) | ||
accountCreationOpts := []state.AccountCreationOption{} | ||
|
@@ -85,8 +96,18 @@ func (p *Protocol) Deposit( | |
if _, err := p.state(ctx, sm, _fundKey, &f); err != nil { | ||
return nil, err | ||
} | ||
f.totalBalance = big.NewInt(0).Add(f.totalBalance, amount) | ||
f.unclaimedBalance = big.NewInt(0).Add(f.unclaimedBalance, amount) | ||
f.totalBalance.Add(f.totalBalance, amount) | ||
f.unclaimedBalance.Add(f.unclaimedBalance, amount) | ||
if !isZero(sgdAmount) { | ||
envestcc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
f.unclaimedBalance.Sub(f.unclaimedBalance, sgdAmount) | ||
if f.unclaimedBalance.Sign() == -1 { | ||
return nil, errors.New("no enough available balance") | ||
} | ||
// grant sgd amount to receiver | ||
if err := p.grantToAccount(ctx, sm, receiver, sgdAmount); err != nil { | ||
envestcc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil, err | ||
} | ||
} | ||
if err := p.putState(ctx, sm, _fundKey, &f); err != nil { | ||
return nil, err | ||
} | ||
|
@@ -146,3 +167,35 @@ func DepositGas(ctx context.Context, sm protocol.StateManager, amount *big.Int) | |
} | ||
return rp.Deposit(ctx, sm, amount, iotextypes.TransactionLogType_GAS_FEE) | ||
Liuhaai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// DepositGasWithSGD deposits gas into the rewarding fund with Sharing of Gas-fee with DApps | ||
func DepositGasWithSGD(ctx context.Context, sm protocol.StateManager, sgdReceiver address.Address, totalAmount, sgdAmount *big.Int, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when will DepositGas() be used, and when will DepositGasWithSGD() be used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DepositGas() is not touched, and will be used by other protocols. |
||
) (*action.TransactionLog, error) { | ||
if isZero(sgdAmount) { | ||
Liuhaai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// fallback to regular case if SGD amount is zero | ||
return DepositGas(ctx, sm, totalAmount) | ||
} | ||
if sgdReceiver == nil { | ||
// a valid SGD amount but no valid receiver address | ||
return nil, errors.New("no valid receiver address to receive the Sharing of Gas-fee with DApps") | ||
} | ||
// TODO: we bypass the gas deposit for the actions in genesis block. Later we should remove this after we remove | ||
// genesis actions | ||
blkCtx := protocol.MustGetBlockCtx(ctx) | ||
if blkCtx.BlockHeight == 0 { | ||
return nil, nil | ||
} | ||
reg, ok := protocol.GetRegistry(ctx) | ||
if !ok { | ||
return nil, nil | ||
} | ||
rp := FindProtocol(reg) | ||
if rp == nil { | ||
return nil, nil | ||
} | ||
return rp.deposit(ctx, sm, sgdReceiver, totalAmount, sgdAmount, iotextypes.TransactionLogType_GAS_FEE) | ||
} | ||
|
||
func isZero(a *big.Int) bool { | ||
return a == nil || len(a.Bytes()) == 0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) 2023 IoTeX Foundation | ||
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability | ||
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. | ||
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file. | ||
|
||
package rewarding | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/iotexproject/iotex-address/address" | ||
|
||
"github.com/iotexproject/iotex-core/action/protocol/execution/evm" | ||
) | ||
|
||
type ( | ||
// SGDRegistry is the interface for Sharing of Gas-fee with DApps | ||
SGDRegistry interface { | ||
// CheckContract returns the contract's eligibility for SGD and percentage | ||
CheckContract(context.Context, string) (address.Address, uint64, bool, error) | ||
} | ||
|
||
sgdRegistry struct { | ||
getHash evm.GetBlockHash | ||
} | ||
) | ||
|
||
// NewSGDRegistry creates a new SGDIndexer | ||
func NewSGDRegistry() SGDRegistry { | ||
return &sgdRegistry{} | ||
} | ||
|
||
func (sgd *sgdRegistry) CheckContract(ctx context.Context, contract string) (address.Address, uint64, bool, error) { | ||
return nil, 0, false, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sGDRegistry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIP-15 doc name it
SGDRegistry
, to the evm module, it is a registry to query the SGD status for a contractinside chainservice, the var is named
sgdIndexer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why should it be public here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this means an interface/func that EVM relies on (but will be provided outside of the EVM module), similar to
GetBlockHash
andDepositGasWithSGD
being public