-
Notifications
You must be signed in to change notification settings - Fork 11
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
Executor & data provider payouts #464
base: main
Are you sure you want to change the base?
Changes from all commits
d5e85f2
f480d4d
c09883c
023fd17
ad59ae6
099eed5
eefd206
dc86eb7
21f4455
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 |
---|---|---|
|
@@ -9,6 +9,7 @@ import ( | |
"strings" | ||
|
||
"cosmossdk.io/math" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/sedaprotocol/seda-wasm-vm/tallyvm/v2" | ||
|
@@ -81,7 +82,7 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err | |
|
||
// Loop through the list to apply filter, execute tally, and post | ||
// execution result. | ||
processedReqs := make(map[string]types.DistributionMessages) | ||
processedReqs := make(map[string][]types.Distribution) | ||
tallyResults := make([]TallyResult, len(tallyList)) | ||
dataResults := make([]batchingtypes.DataResult, len(tallyList)) | ||
for i, req := range tallyList { | ||
|
@@ -97,23 +98,31 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err | |
SedaPayload: req.SedaPayload, | ||
} | ||
|
||
gasPriceInt, ok := math.NewIntFromString(req.GasPrice) | ||
gasPrice, ok := math.NewIntFromString(req.GasPrice) | ||
if !ok { | ||
return fmt.Errorf("invalid gas price: %s", req.GasPrice) // TODO improve error handling | ||
return fmt.Errorf("invalid gas price: %s", req.GasPrice) | ||
} | ||
|
||
var distMsgs types.DistributionMessages | ||
if len(req.Commits) < int(req.ReplicationFactor) { | ||
var gasCalc GasCalculation | ||
switch { | ||
case len(req.Commits) < int(req.ReplicationFactor): | ||
dataResults[i].Result = []byte(fmt.Sprintf("need %d commits; received %d", req.ReplicationFactor, len(req.Commits))) | ||
dataResults[i].ExitCode = TallyExitCodeNotEnoughCommits | ||
k.Logger(ctx).Info("data request's number of commits did not meet replication factor", "request_id", req.ID) | ||
|
||
distMsgs, err = k.CalculateCommitterPayouts(ctx, req, gasPriceInt) | ||
if err != nil { | ||
return err | ||
} | ||
} else { | ||
_, tallyResults[i], distMsgs = k.FilterAndTally(ctx, req, params) | ||
gasCalc.Executors, dataResults[i].GasUsed = CommitGasUsed(req, params.GasCostCommit, req.ExecGasLimit) | ||
dataResults[i].GasUsed += params.GasCostBase | ||
gasCalc.Burn += params.GasCostBase | ||
case len(req.Reveals) == 0: | ||
dataResults[i].Result = []byte("no reveals") | ||
dataResults[i].ExitCode = TallyExitCodeNoReveals | ||
k.Logger(ctx).Info("data request has no reveals", "request_id", req.ID) | ||
|
||
gasCalc.Executors, dataResults[i].GasUsed = CommitGasUsed(req, params.GasCostCommit, req.ExecGasLimit) | ||
dataResults[i].GasUsed += params.GasCostBase | ||
gasCalc.Burn += params.GasCostBase | ||
default: | ||
_, tallyResults[i], gasCalc = k.FilterAndTally(ctx, req, params, gasPrice) | ||
dataResults[i].Result = tallyResults[i].Result | ||
//nolint:gosec // G115: We shouldn't get negative exit code anyway. | ||
dataResults[i].ExitCode = uint32(tallyResults[i].ExitInfo.ExitCode) | ||
|
@@ -124,7 +133,10 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err | |
k.Logger(ctx).Debug("tally result", "request_id", req.ID, "tally_result", tallyResults[i]) | ||
} | ||
|
||
processedReqs[req.ID] = distMsgs | ||
processedReqs[req.ID] = k.DistributionsFromGasCalculation(ctx, req.ID, gasCalc, gasPrice, params.BurnRatio) | ||
|
||
// Total gas used should not exceed the gas limit specified by the request. | ||
dataResults[i].GasUsed = min(req.TallyGasLimit+req.ExecGasLimit, dataResults[i].GasUsed) | ||
dataResults[i].Id, err = dataResults[i].TryHash() | ||
if err != nil { | ||
return err | ||
|
@@ -181,10 +193,8 @@ type TallyResult struct { | |
|
||
// FilterAndTally builds and applies filter, executes tally program, | ||
// and calculates payouts. | ||
func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request, params types.Params) (FilterResult, TallyResult, types.DistributionMessages) { | ||
var result TallyResult | ||
|
||
// Sort the reveals by their keys. | ||
func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request, params types.Params, gasPrice math.Int) (FilterResult, TallyResult, GasCalculation) { | ||
// Sort the reveals by their keys (executors). | ||
keys := make([]string, len(req.Reveals)) | ||
i := 0 | ||
for k := range req.Reveals { | ||
|
@@ -199,17 +209,26 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request, params types. | |
sort.Strings(reveals[i].ProxyPubKeys) | ||
} | ||
|
||
// Phase I: Filtering | ||
// Phase 1: Filtering | ||
filterResult, filterErr := ExecuteFilter(reveals, req.ConsensusFilter, req.ReplicationFactor, params) | ||
result.Consensus = filterResult.Consensus | ||
result.ProxyPubKeys = filterResult.ProxyPubKeys | ||
result.TallyGasUsed += filterResult.GasUsed | ||
filterResult.GasUsed += params.GasCostBase | ||
|
||
// Begin tracking tally and gas calculation results. | ||
result := TallyResult{ | ||
Consensus: filterResult.Consensus, | ||
ProxyPubKeys: filterResult.ProxyPubKeys, | ||
TallyGasUsed: filterResult.GasUsed, | ||
} | ||
var gasCalc GasCalculation | ||
gasCalc.Burn += filterResult.GasUsed | ||
|
||
// Phase II: Tally Program Execution | ||
// Phase 2: Tally Program Execution | ||
var vmRes tallyvm.VmResult | ||
var tallyErr error | ||
if filterErr == nil { | ||
vmRes, err := k.ExecuteTallyProgram(ctx, req, filterResult, reveals) | ||
if err != nil { | ||
result.Result = []byte(err.Error()) | ||
vmRes, tallyErr = k.ExecuteTallyProgram(ctx, req, filterResult, reveals) | ||
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. The execution needs to be aware of the base gas fee that was already consumed. 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. Shouldn't the base gas be taken from the execution gas limit while the tally program uses the tally gas limit? 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. No, the base gas is charged from the tally gas limit. If we were to take it from the execution gas limit all the overlay nodes would also have to consider the base gas cost when calculating their individual limits. And we said it is to cover some of the computational load a DR causes for a validator that are not charged directly like the filter and tally VM; think basic consensus check, median gas price calculation, etc. 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 added. Perhaps |
||
if tallyErr != nil { | ||
result.Result = []byte(tallyErr.Error()) | ||
result.ExitInfo.ExitCode = TallyExitCodeExecError | ||
} else { | ||
result.Result = vmRes.Result | ||
|
@@ -218,6 +237,7 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request, params types. | |
result.StdErr = vmRes.Stderr | ||
} | ||
result.TallyGasUsed += vmRes.GasUsed | ||
gasCalc.Burn += vmRes.GasUsed | ||
} else { | ||
result.Result = []byte(filterErr.Error()) | ||
if errors.Is(filterErr, types.ErrInvalidFilterInput) { | ||
|
@@ -227,14 +247,49 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request, params types. | |
} | ||
} | ||
|
||
// Phase III: Calculate Payouts | ||
// TODO: Calculate gas used & payouts | ||
result.ExecGasUsed = calculateExecGasUsed(reveals) | ||
distMsgs := types.DistributionMessages{ | ||
Messages: []types.DistributionMessage{}, | ||
RefundType: types.DistributionTypeNoConsensus, | ||
// Phase 3: Calculate data proxy and executor gas consumption. | ||
remainingGasLimit := req.ExecGasLimit // track execution gas limit | ||
|
||
// Calculate data proxy gas consumption if basic consensus was reached. | ||
var proxyGasUsedPerExec uint64 | ||
if filterErr == nil || !errors.Is(filterErr, types.ErrNoBasicConsensus) { | ||
var proxyGasUsed uint64 | ||
gasCalc.Proxies, proxyGasUsed = k.ProxyGasUsed(ctx, result.ProxyPubKeys, gasPrice, req.ExecGasLimit, req.ReplicationFactor) | ||
|
||
remainingGasLimit -= proxyGasUsed | ||
proxyGasUsedPerExec = proxyGasUsed / uint64(req.ReplicationFactor) | ||
result.ExecGasUsed += proxyGasUsed | ||
} | ||
return filterResult, result, distMsgs | ||
|
||
// Calculate executor gas consumption. | ||
switch { | ||
case errors.Is(filterErr, types.ErrNoBasicConsensus): | ||
var commitGasUsed uint64 | ||
gasCalc.Executors, commitGasUsed = CommitGasUsed(req, params.GasCostCommit, remainingGasLimit) | ||
result.ExecGasUsed += commitGasUsed | ||
case errors.Is(filterErr, types.ErrInvalidFilterInput) || errors.Is(filterErr, types.ErrNoConsensus) || tallyErr != nil: | ||
gasCalc.ReducedPayout = true | ||
fallthrough | ||
default: // filterErr == ErrConsensusInError || filterErr == nil | ||
gasReports := make([]uint64, len(reveals)) | ||
for i, reveal := range reveals { | ||
if proxyGasUsedPerExec > reveal.GasUsed { | ||
gasReports[i] = 0 | ||
} else { | ||
gasReports[i] = reveal.GasUsed - proxyGasUsedPerExec | ||
} | ||
} | ||
|
||
var execGasUsed uint64 | ||
if areGasReportsUniform(gasReports) { | ||
gasCalc.Executors, execGasUsed = ExecutorGasUsedUniform(keys, gasReports[0], remainingGasLimit, req.ReplicationFactor) | ||
} else { | ||
gasCalc.Executors, execGasUsed = ExecutorGasUsedDivergent(keys, gasReports, remainingGasLimit, req.ReplicationFactor) | ||
} | ||
result.ExecGasUsed += execGasUsed | ||
} | ||
|
||
return filterResult, result, gasCalc | ||
} | ||
|
||
// logErrAndRet logs the base error along with the request ID for | ||
|
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.
where is this used?
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.
We charge the base gas cost for every data request.
When the request times out or when there is no basic consensus, we charge the commit cost for each executor that did commit (
CalculateCommitterPayouts()
)