Commit 215abc31 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-dispute-mon: Add function to calculate required collateral (#9460)

* op-challenger: Add method to get credits for multiple addresses in a batch.

* op-dispute-mon: Add logic to calculate the required collateral

* fix(op-dispute-mon): fix no bond uint128

---------
Co-authored-by: default avatarrefcell <abigger87@gmail.com>
parent 3102d4d7
...@@ -116,12 +116,28 @@ func (c *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep ...@@ -116,12 +116,28 @@ func (c *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep
return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil
} }
func (c *FaultDisputeGameContract) GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error) { func (c *FaultDisputeGameContract) GetCredit(ctx context.Context, recipient common.Address) (*big.Int, error) {
credit, err := c.multiCaller.SingleCall(ctx, batching.BlockLatest, c.contract.Call(methodCredit, receipient)) if credits, err := c.GetCredits(ctx, batching.BlockLatest, recipient); err != nil {
return nil, err
} else {
return credits[0], nil
}
}
func (c *FaultDisputeGameContract) GetCredits(ctx context.Context, block batching.Block, recipients ...common.Address) ([]*big.Int, error) {
calls := make([]*batching.ContractCall, 0, len(recipients))
for _, recipient := range recipients {
calls = append(calls, c.contract.Call(methodCredit, recipient))
}
results, err := c.multiCaller.Call(ctx, block, calls...)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve credit: %w", err) return nil, fmt.Errorf("failed to retrieve credit: %w", err)
} }
return credit.GetBigInt(0), nil credits := make([]*big.Int, 0, len(recipients))
for _, result := range results {
credits = append(credits, result.GetBigInt(0))
}
return credits, nil
} }
func (f *FaultDisputeGameContract) ClaimCredit(recipient common.Address) (txmgr.TxCandidate, error) { func (f *FaultDisputeGameContract) ClaimCredit(recipient common.Address) (txmgr.TxCandidate, error) {
......
...@@ -333,6 +333,37 @@ func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) { ...@@ -333,6 +333,37 @@ func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
}) })
} }
func TestFaultDisputeGame_GetCredit(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
addr := common.Address{0x01}
expected := big.NewInt(4284)
stubRpc.SetResponse(fdgAddr, methodCredit, batching.BlockLatest, []interface{}{addr}, []interface{}{expected})
actual, err := game.GetCredit(context.Background(), addr)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestFaultDisputeGame_GetCredits(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
block := batching.BlockByNumber(482)
addrs := []common.Address{{0x01}, {0x02}, {0x03}}
expected := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(0)}
for i, addr := range addrs {
stubRpc.SetResponse(fdgAddr, methodCredit, block, []interface{}{addr}, []interface{}{expected[i]})
}
actual, err := game.GetCredits(context.Background(), block, addrs...)
require.NoError(t, err)
require.Equal(t, len(expected), len(actual))
for i := range expected {
require.Zerof(t, expected[i].Cmp(actual[i]), "expectd: %v actual: %v", expected[i], actual[i])
}
}
func setupFaultDisputeGameTest(t *testing.T) (*batchingTest.AbiBasedRpc, *FaultDisputeGameContract) { func setupFaultDisputeGameTest(t *testing.T) (*batchingTest.AbiBasedRpc, *FaultDisputeGameContract) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi() fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err) require.NoError(t, err)
......
package bonds
import (
"context"
"fmt"
"math/big"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common"
"golang.org/x/exp/maps"
)
var noBond = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1))
type BondContract interface {
GetCredits(ctx context.Context, block batching.Block, recipients ...common.Address) ([]*big.Int, error)
}
// CalculateRequiredCollateral determines the minimum balance required for a fault dispute game contract in order
// to pay the outstanding bonds and credits.
// It returns the sum of unpaid bonds from claims, plus the sum of allocated but unclaimed credits.
func CalculateRequiredCollateral(ctx context.Context, contract BondContract, blockHash common.Hash, claims []faultTypes.Claim) (*big.Int, error) {
unpaidBonds := big.NewInt(0)
recipients := make(map[common.Address]bool)
for _, claim := range claims {
if noBond.Cmp(claim.Bond) != 0 {
unpaidBonds = new(big.Int).Add(unpaidBonds, claim.Bond)
}
recipients[claim.Claimant] = true
if claim.CounteredBy != (common.Address{}) {
recipients[claim.CounteredBy] = true
}
}
credits, err := contract.GetCredits(ctx, batching.BlockByHash(blockHash), maps.Keys(recipients)...)
if err != nil {
return nil, fmt.Errorf("failed to load credits: %w", err)
}
for _, credit := range credits {
unpaidBonds = new(big.Int).Add(unpaidBonds, credit)
}
return unpaidBonds, nil
}
package bonds
import (
"context"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestMaxValue(t *testing.T) {
require.Equal(t, noBond.String(), "340282366920938463463374607431768211455")
}
func TestCalculateRequiredCollateral(t *testing.T) {
claims := []types.Claim{
{
ClaimData: types.ClaimData{
Bond: noBond,
},
Claimant: common.Address{0x01},
CounteredBy: common.Address{0x02},
},
{
ClaimData: types.ClaimData{
Bond: big.NewInt(5),
},
Claimant: common.Address{0x03},
CounteredBy: common.Address{},
},
{
ClaimData: types.ClaimData{
Bond: big.NewInt(7),
},
Claimant: common.Address{0x03},
CounteredBy: common.Address{},
},
}
contract := &stubBondContract{
credits: map[common.Address]*big.Int{
{0x01}: big.NewInt(3),
{0x03}: big.NewInt(8),
},
}
collateral, err := CalculateRequiredCollateral(context.Background(), contract, common.Hash{0xab}, claims)
require.NoError(t, err)
require.Equal(t, collateral.Int64(), int64(5+7+3+8))
}
type stubBondContract struct {
credits map[common.Address]*big.Int
}
func (s *stubBondContract) GetCredits(_ context.Context, _ batching.Block, recipients ...common.Address) ([]*big.Int, error) {
results := make([]*big.Int, len(recipients))
for i, recipient := range recipients {
credit, ok := s.credits[recipient]
if !ok {
credit = big.NewInt(0)
}
results[i] = credit
}
return results, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment