Merge pull request #216 from filecoin-project/feat/consensus-slashing
Feat/consensus slashing
This commit is contained in:
commit
4202da62cd
@ -23,4 +23,11 @@ const RandomnessLookback = 20
|
|||||||
const ProvingPeriodDuration = 10
|
const ProvingPeriodDuration = 10
|
||||||
const PoSTChallangeTime = 5
|
const PoSTChallangeTime = 5
|
||||||
|
|
||||||
|
const PowerCollateralProportion = 20
|
||||||
|
const PerCapitaCollateralProportion = 5
|
||||||
|
const CollateralPrecision = 100
|
||||||
|
|
||||||
|
const TotalFilecoin = 2000000000
|
||||||
|
const FilecoinPrecision = 1000000000000000000
|
||||||
|
|
||||||
// TODO: Move other important consts here
|
// TODO: Move other important consts here
|
||||||
|
@ -119,9 +119,10 @@ type maMethods struct {
|
|||||||
PaymentVerifyInclusion uint64
|
PaymentVerifyInclusion uint64
|
||||||
PaymentVerifySector uint64
|
PaymentVerifySector uint64
|
||||||
AddFaults uint64
|
AddFaults uint64
|
||||||
|
SlashConsensusFault uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
|
||||||
|
|
||||||
func (sma StorageMinerActor) Exports() []interface{} {
|
func (sma StorageMinerActor) Exports() []interface{} {
|
||||||
return []interface{}{
|
return []interface{}{
|
||||||
@ -144,6 +145,7 @@ func (sma StorageMinerActor) Exports() []interface{} {
|
|||||||
17: sma.PaymentVerifyInclusion,
|
17: sma.PaymentVerifyInclusion,
|
||||||
18: sma.PaymentVerifySector,
|
18: sma.PaymentVerifySector,
|
||||||
19: nil,
|
19: nil,
|
||||||
|
20: sma.SlashConsensusFault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,3 +706,70 @@ func (sma StorageMinerActor) PaymentVerifySector(act *types.Actor, vmctx types.V
|
|||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MinerSlashConsensusFault struct {
|
||||||
|
Slasher address.Address
|
||||||
|
AtHeight uint64
|
||||||
|
SlashedCollateral types.BigInt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sma StorageMinerActor) SlashConsensusFault(act *types.Actor, vmctx types.VMContext, params *MinerSlashConsensusFault) ([]byte, ActorError) {
|
||||||
|
if vmctx.Message().From != StorageMarketAddress {
|
||||||
|
return nil, aerrors.New(1, "SlashConsensusFault may only be called by the storage market actor")
|
||||||
|
}
|
||||||
|
|
||||||
|
slashedCollateral := params.SlashedCollateral
|
||||||
|
if types.BigCmp(slashedCollateral, act.Balance) < 0 {
|
||||||
|
slashedCollateral = act.Balance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some of the slashed collateral should be paid to the slasher
|
||||||
|
// GROWTH_RATE determines how fast the slasher share of slashed collateral will increase as block elapses
|
||||||
|
// current GROWTH_RATE results in SLASHER_SHARE reaches 1 after 30 blocks
|
||||||
|
// TODO: define arithmetic precision and rounding for this operation
|
||||||
|
blockElapsed := vmctx.BlockHeight() - params.AtHeight
|
||||||
|
|
||||||
|
slasherShare := slasherShare(params.SlashedCollateral, blockElapsed)
|
||||||
|
|
||||||
|
burnPortion := types.BigSub(slashedCollateral, slasherShare)
|
||||||
|
|
||||||
|
_, err := vmctx.Send(vmctx.Message().From, 0, slasherShare, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, aerrors.Wrap(err, "failed to pay slasher")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vmctx.Send(BurntFundsAddress, 0, burnPortion, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, aerrors.Wrap(err, "failed to burn funds")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this still allows the miner to commit sectors and submit posts,
|
||||||
|
// their users could potentially be unaffected, but the miner will never be
|
||||||
|
// able to mine a block again
|
||||||
|
// One potential issue: the miner will have to pay back the slashed
|
||||||
|
// collateral to continue submitting PoSts, which includes pledge
|
||||||
|
// collateral that they no longer really 'need'
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func slasherShare(total types.BigInt, elapsed uint64) types.BigInt {
|
||||||
|
// [int(pow(1.26, n) * 10) for n in range(30)]
|
||||||
|
fracs := []uint64{10, 12, 15, 20, 25, 31, 40, 50, 63, 80, 100, 127, 160, 201, 254, 320, 403, 508, 640, 807, 1017, 1281, 1614, 2034, 2563, 3230, 4070, 5128, 6462, 8142}
|
||||||
|
const precision = 10000
|
||||||
|
|
||||||
|
var frac uint64
|
||||||
|
if elapsed >= uint64(len(fracs)) {
|
||||||
|
return total
|
||||||
|
} else {
|
||||||
|
frac = fracs[elapsed]
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.BigDiv(
|
||||||
|
types.BigMul(
|
||||||
|
types.NewInt(frac),
|
||||||
|
total,
|
||||||
|
),
|
||||||
|
types.NewInt(precision),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package actors
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-lotus/build"
|
"github.com/filecoin-project/go-lotus/build"
|
||||||
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
||||||
@ -12,37 +13,41 @@ import (
|
|||||||
hamt "github.com/ipfs/go-hamt-ipld"
|
hamt "github.com/ipfs/go-hamt-ipld"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
xerrors "golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageMarketActor struct{}
|
type StorageMarketActor struct{}
|
||||||
|
|
||||||
type smaMethods struct {
|
type smaMethods struct {
|
||||||
Constructor uint64
|
Constructor uint64
|
||||||
CreateStorageMiner uint64
|
CreateStorageMiner uint64
|
||||||
SlashConsensusFault uint64
|
ArbitrateConsensusFault uint64
|
||||||
UpdateStorage uint64
|
UpdateStorage uint64
|
||||||
GetTotalStorage uint64
|
GetTotalStorage uint64
|
||||||
PowerLookup uint64
|
PowerLookup uint64
|
||||||
IsMiner uint64
|
IsMiner uint64
|
||||||
|
PledgeCollateralForSize uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7}
|
var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
||||||
func (sma StorageMarketActor) Exports() []interface{} {
|
func (sma StorageMarketActor) Exports() []interface{} {
|
||||||
return []interface{}{
|
return []interface{}{
|
||||||
//1: sma.StorageMarketConstructor,
|
//1: sma.StorageMarketConstructor,
|
||||||
2: sma.CreateStorageMiner,
|
2: sma.CreateStorageMiner,
|
||||||
//3: sma.SlashConsensusFault,
|
3: sma.ArbitrateConsensusFault,
|
||||||
4: sma.UpdateStorage,
|
4: sma.UpdateStorage,
|
||||||
5: sma.GetTotalStorage,
|
5: sma.GetTotalStorage,
|
||||||
6: sma.PowerLookup,
|
6: sma.PowerLookup,
|
||||||
7: sma.IsMiner,
|
7: sma.IsMiner,
|
||||||
//8: sma.StorageCollateralForSize,
|
8: sma.PledgeCollateralForSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageMarketState struct {
|
type StorageMarketState struct {
|
||||||
Miners cid.Cid
|
Miners cid.Cid
|
||||||
|
MinerCount uint64
|
||||||
|
|
||||||
TotalStorage types.BigInt
|
TotalStorage types.BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +63,21 @@ func (sma StorageMarketActor) CreateStorageMiner(act *types.Actor, vmctx types.V
|
|||||||
return nil, aerrors.New(1, "Unsupported sector size")
|
return nil, aerrors.New(1, "Unsupported sector size")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var self StorageMarketState
|
||||||
|
old := vmctx.Storage().GetHead()
|
||||||
|
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqColl, err := pledgeCollateralForSize(vmctx, types.NewInt(0), self.TotalStorage, self.MinerCount+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if types.BigCmp(vmctx.Message().Value, reqColl) < 0 {
|
||||||
|
return nil, aerrors.Newf(1, "not enough funds passed to cover required miner collateral (needed %s, got %s)", reqColl, vmctx.Message().Value)
|
||||||
|
}
|
||||||
|
|
||||||
encoded, err := CreateExecParams(StorageMinerCodeCid, &StorageMinerConstructorParams{
|
encoded, err := CreateExecParams(StorageMinerCodeCid, &StorageMinerConstructorParams{
|
||||||
Owner: params.Owner,
|
Owner: params.Owner,
|
||||||
Worker: params.Worker,
|
Worker: params.Worker,
|
||||||
@ -75,13 +95,7 @@ func (sma StorageMarketActor) CreateStorageMiner(act *types.Actor, vmctx types.V
|
|||||||
|
|
||||||
naddr, nerr := address.NewFromBytes(ret)
|
naddr, nerr := address.NewFromBytes(ret)
|
||||||
if nerr != nil {
|
if nerr != nil {
|
||||||
return nil, aerrors.Absorb(nerr, 1, "could not read address of new actor")
|
return nil, aerrors.Absorb(nerr, 2, "could not read address of new actor")
|
||||||
}
|
|
||||||
|
|
||||||
var self StorageMarketState
|
|
||||||
old := vmctx.Storage().GetHead()
|
|
||||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ncid, err := MinerSetAdd(context.TODO(), vmctx, self.Miners, naddr)
|
ncid, err := MinerSetAdd(context.TODO(), vmctx, self.Miners, naddr)
|
||||||
@ -89,6 +103,7 @@ func (sma StorageMarketActor) CreateStorageMiner(act *types.Actor, vmctx types.V
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
self.Miners = ncid
|
self.Miners = ncid
|
||||||
|
self.MinerCount++
|
||||||
|
|
||||||
nroot, err := vmctx.Storage().Put(&self)
|
nroot, err := vmctx.Storage().Put(&self)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -109,6 +124,148 @@ func SupportedSectorSize(ssize types.BigInt) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ArbitrateConsensusFaultParams struct {
|
||||||
|
Block1 *types.BlockHeader
|
||||||
|
Block2 *types.BlockHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sma StorageMarketActor) ArbitrateConsensusFault(act *types.Actor, vmctx types.VMContext, params *ArbitrateConsensusFaultParams) ([]byte, ActorError) {
|
||||||
|
if params.Block1.Miner != params.Block2.Miner {
|
||||||
|
return nil, aerrors.New(2, "blocks must be from the same miner")
|
||||||
|
}
|
||||||
|
|
||||||
|
rval, err := vmctx.Send(params.Block1.Miner, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, aerrors.Wrap(err, "failed to get miner worker")
|
||||||
|
}
|
||||||
|
|
||||||
|
worker, oerr := address.NewFromBytes(rval)
|
||||||
|
if oerr != nil {
|
||||||
|
// REVIEW: should this be fatal? i can't think of a real situation that would get us here
|
||||||
|
return nil, aerrors.Absorb(oerr, 3, "response from 'GetWorkerAddr' was not a valid address")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := params.Block1.CheckBlockSignature(worker); err != nil {
|
||||||
|
return nil, aerrors.Absorb(err, 4, "block1 did not have valid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := params.Block2.CheckBlockSignature(worker); err != nil {
|
||||||
|
return nil, aerrors.Absorb(err, 5, "block2 did not have valid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
// see the "Consensus Faults" section of the faults spec (faults.md)
|
||||||
|
// for details on these slashing conditions.
|
||||||
|
if !shouldSlash(params.Block1, params.Block2) {
|
||||||
|
return nil, aerrors.New(6, "blocks do not prove a slashable offense")
|
||||||
|
}
|
||||||
|
|
||||||
|
var self StorageMarketState
|
||||||
|
old := vmctx.Storage().GetHead()
|
||||||
|
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if types.BigCmp(self.TotalStorage, types.NewInt(0)) == 0 {
|
||||||
|
return nil, aerrors.Fatal("invalid state, storage market actor has zero total storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
miner := params.Block1.Miner
|
||||||
|
if has, err := MinerSetHas(context.TODO(), vmctx, self.Miners, miner); err != nil {
|
||||||
|
return nil, aerrors.Wrapf(err, "failed to check miner in set")
|
||||||
|
} else if !has {
|
||||||
|
return nil, aerrors.New(7, "either already slashed or not a miner")
|
||||||
|
}
|
||||||
|
|
||||||
|
minerPower, err := powerLookup(context.TODO(), vmctx, &self, miner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
slashedCollateral, err := pledgeCollateralForSize(vmctx, minerPower, self.TotalStorage, self.MinerCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enc, err := SerializeParams(&MinerSlashConsensusFault{
|
||||||
|
Slasher: vmctx.Message().From,
|
||||||
|
AtHeight: params.Block1.Height,
|
||||||
|
SlashedCollateral: slashedCollateral,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vmctx.Send(miner, MAMethods.SlashConsensusFault, types.NewInt(0), enc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the miner from the list of network miners
|
||||||
|
ncid, err := MinerSetRemove(context.TODO(), vmctx, self.Miners, miner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
self.Miners = ncid
|
||||||
|
self.MinerCount--
|
||||||
|
|
||||||
|
self.TotalStorage = types.BigSub(self.TotalStorage, minerPower)
|
||||||
|
|
||||||
|
nroot, err := vmctx.Storage().Put(&self)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cidArrContains(a []cid.Cid, b cid.Cid) bool {
|
||||||
|
for _, c := range a {
|
||||||
|
if b == c {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldSlash(block1, block2 *types.BlockHeader) bool {
|
||||||
|
// First slashing condition, blocks have the same ticket round
|
||||||
|
if block1.Height == block2.Height {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second slashing condition requires having access to the parent tipset blocks
|
||||||
|
// This might not always be available, needs some thought on the best way to deal with this
|
||||||
|
|
||||||
|
|
||||||
|
// Second slashing condition, miner ignored own block when mining
|
||||||
|
// Case A: block2 could have been in block1's parent set but is not
|
||||||
|
b1ParentHeight := block1.Height - len(block1.Tickets)
|
||||||
|
|
||||||
|
block1ParentTipSet := block1.Parents
|
||||||
|
if !cidArrContains(block1.Parents, block2.Cid()) &&
|
||||||
|
b1ParentHeight == block2.Height &&
|
||||||
|
block1ParentTipSet.ParentCids == block2.ParentCids {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case B: block1 could have been in block2's parent set but is not
|
||||||
|
block2ParentTipSet := parentOf(block2)
|
||||||
|
if !block2Parent.contains(block1) &&
|
||||||
|
block2ParentTipSet.Height == block1.Height &&
|
||||||
|
block2ParentTipSet.ParentCids == block1.ParentCids {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateStorageParams struct {
|
type UpdateStorageParams struct {
|
||||||
Delta types.BigInt
|
Delta types.BigInt
|
||||||
}
|
}
|
||||||
@ -162,21 +319,30 @@ func (sma StorageMarketActor) PowerLookup(act *types.Actor, vmctx types.VMContex
|
|||||||
return nil, aerrors.Wrap(err, "getting head")
|
return nil, aerrors.Wrap(err, "getting head")
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err := MinerSetHas(context.TODO(), vmctx, self.Miners, params.Miner)
|
pow, err := powerLookup(context.TODO(), vmctx, &self, params.Miner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has {
|
return pow.Bytes(), nil
|
||||||
return nil, aerrors.New(1, "miner not registered with storage market")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ret, err := vmctx.Send(params.Miner, MAMethods.GetPower, types.NewInt(0), nil)
|
func powerLookup(ctx context.Context, vmctx types.VMContext, self *StorageMarketState, miner address.Address) (types.BigInt, ActorError) {
|
||||||
|
has, err := MinerSetHas(context.TODO(), vmctx, self.Miners, miner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, aerrors.Wrap(err, "invoke Miner.GetPower")
|
return types.EmptyInt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
if !has {
|
||||||
|
return types.EmptyInt, aerrors.New(1, "miner not registered with storage market")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := vmctx.Send(miner, MAMethods.GetPower, types.NewInt(0), nil)
|
||||||
|
if err != nil {
|
||||||
|
return types.EmptyInt, aerrors.Wrap(err, "invoke Miner.GetPower")
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.BigFromBytes(ret), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsMinerParam struct {
|
type IsMinerParam struct {
|
||||||
@ -197,6 +363,76 @@ func (sma StorageMarketActor) IsMiner(act *types.Actor, vmctx types.VMContext, p
|
|||||||
return cbg.EncodeBool(has), nil
|
return cbg.EncodeBool(has), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PledgeCollateralParams struct {
|
||||||
|
Size types.BigInt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sma StorageMarketActor) PledgeCollateralForSize(act *types.Actor, vmctx types.VMContext, param *PledgeCollateralParams) ([]byte, ActorError) {
|
||||||
|
var self StorageMarketState
|
||||||
|
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCollateral, err := pledgeCollateralForSize(vmctx, param.Size, self.TotalStorage, self.MinerCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalCollateral.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pledgeCollateralForSize(vmctx types.VMContext, size, totalStorage types.BigInt, minerCount uint64) (types.BigInt, aerrors.ActorError) {
|
||||||
|
netBalance, err := vmctx.GetBalance(NetworkAddress)
|
||||||
|
if err != nil {
|
||||||
|
return types.EmptyInt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: the spec says to also grab 'total vested filecoin' and include it as available
|
||||||
|
// If we don't factor that in, we effectively assume all of the locked up filecoin is 'available'
|
||||||
|
// the blocker on that right now is that its hard to tell how much filecoin is unlocked
|
||||||
|
|
||||||
|
availableFilecoin := types.BigSub(
|
||||||
|
types.BigMul(types.NewInt(build.TotalFilecoin), types.NewInt(build.FilecoinPrecision)),
|
||||||
|
netBalance,
|
||||||
|
)
|
||||||
|
|
||||||
|
totalPowerCollateral := types.BigDiv(
|
||||||
|
types.BigMul(
|
||||||
|
availableFilecoin,
|
||||||
|
types.NewInt(build.PowerCollateralProportion),
|
||||||
|
),
|
||||||
|
types.NewInt(build.CollateralPrecision),
|
||||||
|
)
|
||||||
|
|
||||||
|
totalPerCapitaCollateral := types.BigDiv(
|
||||||
|
types.BigMul(
|
||||||
|
availableFilecoin,
|
||||||
|
types.NewInt(build.PerCapitaCollateralProportion),
|
||||||
|
),
|
||||||
|
types.NewInt(build.CollateralPrecision),
|
||||||
|
)
|
||||||
|
|
||||||
|
// REVIEW: for bootstrapping purposes, we skip the power portion of the
|
||||||
|
// collateral if there is no collateral in the network yet
|
||||||
|
powerCollateral := types.NewInt(0)
|
||||||
|
if types.BigCmp(totalStorage, types.NewInt(0)) != 0 {
|
||||||
|
powerCollateral = types.BigDiv(
|
||||||
|
types.BigMul(
|
||||||
|
totalPowerCollateral,
|
||||||
|
size,
|
||||||
|
),
|
||||||
|
totalStorage,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
perCapCollateral := types.BigDiv(
|
||||||
|
totalPerCapitaCollateral,
|
||||||
|
types.NewInt(minerCount),
|
||||||
|
)
|
||||||
|
|
||||||
|
return types.BigAdd(powerCollateral, perCapCollateral), nil
|
||||||
|
}
|
||||||
|
|
||||||
func MinerSetHas(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (bool, aerrors.ActorError) {
|
func MinerSetHas(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (bool, aerrors.ActorError) {
|
||||||
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -220,7 +456,17 @@ func MinerSetAdd(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr
|
|||||||
return cid.Undef, aerrors.Escalate(err, "failed to load miner set")
|
return cid.Undef, aerrors.Escalate(err, "failed to load miner set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := nd.Set(ctx, string(maddr.Bytes()), uint64(1)); err != nil {
|
mkey := string(maddr.Bytes())
|
||||||
|
err = nd.Find(ctx, mkey, nil)
|
||||||
|
if err == nil {
|
||||||
|
return cid.Undef, aerrors.Escalate(fmt.Errorf("miner already found"), "miner set add failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !xerrors.Is(err, hamt.ErrNotFound) {
|
||||||
|
return cid.Undef, aerrors.Escalate(err, "failed to do miner set check")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := nd.Set(ctx, mkey, uint64(1)); err != nil {
|
||||||
return cid.Undef, aerrors.Escalate(err, "adding miner address to set failed")
|
return cid.Undef, aerrors.Escalate(err, "adding miner address to set failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,3 +481,30 @@ func MinerSetAdd(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr
|
|||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MinerSetRemove(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (cid.Cid, aerrors.ActorError) {
|
||||||
|
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, aerrors.Escalate(err, "failed to load miner set")
|
||||||
|
}
|
||||||
|
|
||||||
|
mkey := string(maddr.Bytes())
|
||||||
|
switch nd.Delete(ctx, mkey) {
|
||||||
|
case hamt.ErrNotFound:
|
||||||
|
return cid.Undef, aerrors.New(1, "miner not found in set on delete")
|
||||||
|
default:
|
||||||
|
return cid.Undef, aerrors.Escalate(err, "failed to delete miner from set")
|
||||||
|
case nil:
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := nd.Flush(ctx); err != nil {
|
||||||
|
return cid.Undef, aerrors.Escalate(err, "failed to flush miner set")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := vmctx.Ipld().Put(ctx, nd)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, aerrors.Escalate(err, "failed to persist miner set to storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
@ -1,23 +1,32 @@
|
|||||||
package actors_test
|
package actors_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-lotus/build"
|
"github.com/filecoin-project/go-lotus/build"
|
||||||
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
|
||||||
. "github.com/filecoin-project/go-lotus/chain/actors"
|
. "github.com/filecoin-project/go-lotus/chain/actors"
|
||||||
"github.com/filecoin-project/go-lotus/chain/address"
|
"github.com/filecoin-project/go-lotus/chain/address"
|
||||||
"github.com/filecoin-project/go-lotus/chain/types"
|
"github.com/filecoin-project/go-lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/go-lotus/chain/vm"
|
||||||
|
"github.com/filecoin-project/go-lotus/chain/wallet"
|
||||||
|
|
||||||
|
cid "github.com/ipfs/go-cid"
|
||||||
|
hamt "github.com/ipfs/go-hamt-ipld"
|
||||||
|
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||||
cbor "github.com/ipfs/go-ipld-cbor"
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
|
mh "github.com/multiformats/go-multihash"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStorageMarketCreateMiner(t *testing.T) {
|
func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||||
var ownerAddr, workerAddr address.Address
|
var ownerAddr, workerAddr address.Address
|
||||||
|
|
||||||
opts := []HarnessOpt{
|
opts := []HarnessOpt{
|
||||||
HarnessAddr(&ownerAddr, 100000),
|
HarnessAddr(&ownerAddr, 1000000),
|
||||||
HarnessAddr(&workerAddr, 100000),
|
HarnessAddr(&workerAddr, 100000),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +34,11 @@ func TestStorageMarketCreateMiner(t *testing.T) {
|
|||||||
|
|
||||||
var minerAddr address.Address
|
var minerAddr address.Address
|
||||||
{
|
{
|
||||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SMAMethods.CreateStorageMiner,
|
// cheating the bootstrapping problem
|
||||||
|
cheatStorageMarketTotal(t, h.vm, h.cs.Blockstore())
|
||||||
|
|
||||||
|
ret, _ := h.InvokeWithValue(t, ownerAddr, StorageMarketAddress, SMAMethods.CreateStorageMiner,
|
||||||
|
types.NewInt(500000),
|
||||||
&CreateStorageMinerParams{
|
&CreateStorageMinerParams{
|
||||||
Owner: ownerAddr,
|
Owner: ownerAddr,
|
||||||
Worker: workerAddr,
|
Worker: workerAddr,
|
||||||
@ -72,4 +85,90 @@ func TestStorageMarketCreateMiner(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, ownerAddr, oA, "return from GetOwner should be equal to the owner")
|
assert.Equal(t, ownerAddr, oA, "return from GetOwner should be equal to the owner")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
b1 := fakeBlock(t, minerAddr, 100)
|
||||||
|
b2 := fakeBlock(t, minerAddr, 101)
|
||||||
|
|
||||||
|
signBlock(t, h.w, workerAddr, b1)
|
||||||
|
signBlock(t, h.w, workerAddr, b2)
|
||||||
|
|
||||||
|
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SMAMethods.ArbitrateConsensusFault,
|
||||||
|
&ArbitrateConsensusFaultParams{
|
||||||
|
Block1: b1,
|
||||||
|
Block2: b2,
|
||||||
|
})
|
||||||
|
ApplyOK(t, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SMAMethods.PowerLookup,
|
||||||
|
&PowerLookupParams{Miner: minerAddr})
|
||||||
|
assert.Equal(t, ret.ExitCode, byte(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SMAMethods.IsMiner, &IsMinerParam{minerAddr})
|
||||||
|
ApplyOK(t, ret)
|
||||||
|
assert.Equal(t, ret.Return, cbg.CborBoolFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
sma, err := vm.StateTree().GetActor(StorageMarketAddress)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cst := hamt.CSTFromBstore(bs)
|
||||||
|
|
||||||
|
var smastate StorageMarketState
|
||||||
|
if err := cst.Get(context.TODO(), sma.Head, &smastate); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
smastate.TotalStorage = types.NewInt(10000)
|
||||||
|
|
||||||
|
c, err := cst.Put(context.TODO(), &smastate)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sma.Head = c
|
||||||
|
|
||||||
|
if err := vm.StateTree().SetActor(StorageMarketAddress, sma); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader {
|
||||||
|
c := fakeCid(t, 1)
|
||||||
|
return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, StateRoot: c, Messages: c, MessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fakeCid(t *testing.T, s int) cid.Cid {
|
||||||
|
t.Helper()
|
||||||
|
c, err := cid.NewPrefixV1(cid.Raw, mh.IDENTITY).Sum([]byte(fmt.Sprintf("%d", s)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func signBlock(t *testing.T, w *wallet.Wallet, worker address.Address, blk *types.BlockHeader) {
|
||||||
|
t.Helper()
|
||||||
|
sb, err := blk.SigningBytes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := w.Sign(context.TODO(), worker, sb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blk.BlockSig = *sig
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ var PaymentChannelActorCodeCid cid.Cid
|
|||||||
var InitActorAddress = mustIDAddress(0)
|
var InitActorAddress = mustIDAddress(0)
|
||||||
var NetworkAddress = mustIDAddress(1)
|
var NetworkAddress = mustIDAddress(1)
|
||||||
var StorageMarketAddress = mustIDAddress(2)
|
var StorageMarketAddress = mustIDAddress(2)
|
||||||
|
var BurntFundsAddress = mustIDAddress(99)
|
||||||
|
|
||||||
func mustIDAddress(i uint64) address.Address {
|
func mustIDAddress(i uint64) address.Address {
|
||||||
a, err := address.NewIDAddress(i)
|
a, err := address.NewIDAddress(i)
|
||||||
|
@ -3,6 +3,7 @@ package actors_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-lotus/build"
|
"github.com/filecoin-project/go-lotus/build"
|
||||||
@ -29,7 +30,7 @@ func blsaddr(n uint64) address.Address {
|
|||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address) {
|
func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore) {
|
||||||
bs := bstore.NewBlockstore(dstore.NewMapDatastore())
|
bs := bstore.NewBlockstore(dstore.NewMapDatastore())
|
||||||
|
|
||||||
from := blsaddr(0)
|
from := blsaddr(0)
|
||||||
@ -56,11 +57,11 @@ func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return vm, []address.Address{from, maddr}
|
return vm, []address.Address{from, maddr}, bs
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVMInvokeMethod(t *testing.T) {
|
func TestVMInvokeMethod(t *testing.T) {
|
||||||
vm, addrs := setupVMTestEnv(t)
|
vm, addrs, _ := setupVMTestEnv(t)
|
||||||
from := addrs[0]
|
from := addrs[0]
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -108,10 +109,12 @@ func TestVMInvokeMethod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStorageMarketActorCreateMiner(t *testing.T) {
|
func TestStorageMarketActorCreateMiner(t *testing.T) {
|
||||||
vm, addrs := setupVMTestEnv(t)
|
vm, addrs, bs := setupVMTestEnv(t)
|
||||||
from := addrs[0]
|
from := addrs[0]
|
||||||
maddr := addrs[1]
|
maddr := addrs[1]
|
||||||
|
|
||||||
|
cheatStorageMarketTotal(t, vm, bs)
|
||||||
|
|
||||||
params := &StorageMinerConstructorParams{
|
params := &StorageMinerConstructorParams{
|
||||||
Worker: maddr,
|
Worker: maddr,
|
||||||
SectorSize: types.NewInt(build.SectorSize),
|
SectorSize: types.NewInt(build.SectorSize),
|
||||||
@ -130,7 +133,7 @@ func TestStorageMarketActorCreateMiner(t *testing.T) {
|
|||||||
Params: enc,
|
Params: enc,
|
||||||
GasPrice: types.NewInt(1),
|
GasPrice: types.NewInt(1),
|
||||||
GasLimit: types.NewInt(10000),
|
GasLimit: types.NewInt(10000),
|
||||||
Value: types.NewInt(0),
|
Value: types.NewInt(50000),
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
||||||
@ -139,6 +142,7 @@ func TestStorageMarketActorCreateMiner(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ret.ExitCode != 0 {
|
if ret.ExitCode != 0 {
|
||||||
|
fmt.Println(ret.ActorErr)
|
||||||
t.Fatal("invocation failed: ", ret.ExitCode)
|
t.Fatal("invocation failed: ", ret.ExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,22 @@ func Newf(retCode uint8, format string, args ...interface{}) ActorError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Fatal(message string, args ...interface{}) ActorError {
|
||||||
|
return &actorError{
|
||||||
|
fatal: true,
|
||||||
|
msg: message,
|
||||||
|
frame: xerrors.Caller(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatalf(format string, args ...interface{}) ActorError {
|
||||||
|
return &actorError{
|
||||||
|
fatal: true,
|
||||||
|
msg: fmt.Sprintf(format, args...),
|
||||||
|
frame: xerrors.Caller(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap extens chain of errors with a message
|
// Wrap extens chain of errors with a message
|
||||||
func Wrap(err ActorError, message string) ActorError {
|
func Wrap(err ActorError, message string) ActorError {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -2384,7 +2384,7 @@ func (t *StorageMarketState) MarshalCBOR(w io.Writer) error {
|
|||||||
_, err := w.Write(cbg.CborNull)
|
_, err := w.Write(cbg.CborNull)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := w.Write([]byte{130}); err != nil {
|
if _, err := w.Write([]byte{131}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2394,6 +2394,11 @@ func (t *StorageMarketState) MarshalCBOR(w io.Writer) error {
|
|||||||
return xerrors.Errorf("failed to write cid field t.Miners: %w", err)
|
return xerrors.Errorf("failed to write cid field t.Miners: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// t.t.MinerCount (uint64)
|
||||||
|
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.MinerCount)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// t.t.TotalStorage (types.BigInt)
|
// t.t.TotalStorage (types.BigInt)
|
||||||
if err := t.TotalStorage.MarshalCBOR(w); err != nil {
|
if err := t.TotalStorage.MarshalCBOR(w); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -2412,7 +2417,7 @@ func (t *StorageMarketState) UnmarshalCBOR(r io.Reader) error {
|
|||||||
return fmt.Errorf("cbor input should be of type array")
|
return fmt.Errorf("cbor input should be of type array")
|
||||||
}
|
}
|
||||||
|
|
||||||
if extra != 2 {
|
if extra != 3 {
|
||||||
return fmt.Errorf("cbor input had wrong number of fields")
|
return fmt.Errorf("cbor input had wrong number of fields")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2428,6 +2433,16 @@ func (t *StorageMarketState) UnmarshalCBOR(r io.Reader) error {
|
|||||||
t.Miners = c
|
t.Miners = c
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// t.t.MinerCount (uint64)
|
||||||
|
|
||||||
|
maj, extra, err = cbg.CborReadHeader(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maj != cbg.MajUnsignedInt {
|
||||||
|
return fmt.Errorf("wrong type for uint64 field")
|
||||||
|
}
|
||||||
|
t.MinerCount = extra
|
||||||
// t.t.TotalStorage (types.BigInt)
|
// t.t.TotalStorage (types.BigInt)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -2657,3 +2672,199 @@ func (t *UpdateStorageParams) UnmarshalCBOR(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *ArbitrateConsensusFaultParams) MarshalCBOR(w io.Writer) error {
|
||||||
|
if t == nil {
|
||||||
|
_, err := w.Write(cbg.CborNull)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write([]byte{130}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Block1 (types.BlockHeader)
|
||||||
|
if err := t.Block1.MarshalCBOR(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Block2 (types.BlockHeader)
|
||||||
|
if err := t.Block2.MarshalCBOR(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ArbitrateConsensusFaultParams) UnmarshalCBOR(r io.Reader) error {
|
||||||
|
br := cbg.GetPeeker(r)
|
||||||
|
|
||||||
|
maj, extra, err := cbg.CborReadHeader(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maj != cbg.MajArray {
|
||||||
|
return fmt.Errorf("cbor input should be of type array")
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra != 2 {
|
||||||
|
return fmt.Errorf("cbor input had wrong number of fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Block1 (types.BlockHeader)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
pb, err := br.PeekByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pb == cbg.CborNull[0] {
|
||||||
|
var nbuf [1]byte
|
||||||
|
if _, err := br.Read(nbuf[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Block1 = new(types.BlockHeader)
|
||||||
|
if err := t.Block1.UnmarshalCBOR(br); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// t.t.Block2 (types.BlockHeader)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
pb, err := br.PeekByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pb == cbg.CborNull[0] {
|
||||||
|
var nbuf [1]byte
|
||||||
|
if _, err := br.Read(nbuf[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Block2 = new(types.BlockHeader)
|
||||||
|
if err := t.Block2.UnmarshalCBOR(br); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PledgeCollateralParams) MarshalCBOR(w io.Writer) error {
|
||||||
|
if t == nil {
|
||||||
|
_, err := w.Write(cbg.CborNull)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write([]byte{129}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Size (types.BigInt)
|
||||||
|
if err := t.Size.MarshalCBOR(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PledgeCollateralParams) UnmarshalCBOR(r io.Reader) error {
|
||||||
|
br := cbg.GetPeeker(r)
|
||||||
|
|
||||||
|
maj, extra, err := cbg.CborReadHeader(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maj != cbg.MajArray {
|
||||||
|
return fmt.Errorf("cbor input should be of type array")
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra != 1 {
|
||||||
|
return fmt.Errorf("cbor input had wrong number of fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Size (types.BigInt)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
if err := t.Size.UnmarshalCBOR(br); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MinerSlashConsensusFault) MarshalCBOR(w io.Writer) error {
|
||||||
|
if t == nil {
|
||||||
|
_, err := w.Write(cbg.CborNull)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write([]byte{131}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Slasher (address.Address)
|
||||||
|
if err := t.Slasher.MarshalCBOR(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.AtHeight (uint64)
|
||||||
|
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.AtHeight)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.SlashedCollateral (types.BigInt)
|
||||||
|
if err := t.SlashedCollateral.MarshalCBOR(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MinerSlashConsensusFault) UnmarshalCBOR(r io.Reader) error {
|
||||||
|
br := cbg.GetPeeker(r)
|
||||||
|
|
||||||
|
maj, extra, err := cbg.CborReadHeader(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maj != cbg.MajArray {
|
||||||
|
return fmt.Errorf("cbor input should be of type array")
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra != 3 {
|
||||||
|
return fmt.Errorf("cbor input had wrong number of fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.t.Slasher (address.Address)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
if err := t.Slasher.UnmarshalCBOR(br); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// t.t.AtHeight (uint64)
|
||||||
|
|
||||||
|
maj, extra, err = cbg.CborReadHeader(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maj != cbg.MajUnsignedInt {
|
||||||
|
return fmt.Errorf("wrong type for uint64 field")
|
||||||
|
}
|
||||||
|
t.AtHeight = extra
|
||||||
|
// t.t.SlashedCollateral (types.BigInt)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
if err := t.SlashedCollateral.UnmarshalCBOR(br); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -240,11 +240,17 @@ func (h *Harness) SendFunds(t testing.TB, from address.Address, to address.Addre
|
|||||||
func (h *Harness) Invoke(t testing.TB, from address.Address, to address.Address,
|
func (h *Harness) Invoke(t testing.TB, from address.Address, to address.Address,
|
||||||
method uint64, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
method uint64, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
return h.InvokeWithValue(t, from, to, method, types.NewInt(0), params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Harness) InvokeWithValue(t testing.TB, from address.Address, to address.Address,
|
||||||
|
method uint64, value types.BigInt, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||||
|
t.Helper()
|
||||||
return h.Apply(t, types.Message{
|
return h.Apply(t, types.Message{
|
||||||
To: to,
|
To: to,
|
||||||
From: from,
|
From: from,
|
||||||
Method: method,
|
Method: method,
|
||||||
Value: types.NewInt(0),
|
Value: value,
|
||||||
Params: DumpObject(t, params),
|
Params: DumpObject(t, params),
|
||||||
GasPrice: types.NewInt(1),
|
GasPrice: types.NewInt(1),
|
||||||
GasLimit: types.NewInt(testGasLimit),
|
GasLimit: types.NewInt(testGasLimit),
|
||||||
|
@ -129,7 +129,7 @@ func NewGenerator() (*ChainGen, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
|
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
|
||||||
worker: types.NewInt(50000),
|
worker: types.NewInt(50000000),
|
||||||
banker: types.NewInt(90000000),
|
banker: types.NewInt(90000000),
|
||||||
}, minercfg, 100000)
|
}, minercfg, 100000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,15 +98,29 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
|||||||
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
netAmt := types.FromFil(build.TotalFilecoin)
|
||||||
|
for _, amt := range actmap {
|
||||||
|
netAmt = types.BigSub(netAmt, amt)
|
||||||
|
}
|
||||||
|
|
||||||
err = state.SetActor(actors.NetworkAddress, &types.Actor{
|
err = state.SetActor(actors.NetworkAddress, &types.Actor{
|
||||||
Code: actors.AccountActorCodeCid,
|
Code: actors.AccountActorCodeCid,
|
||||||
Balance: types.NewInt(100000000000),
|
Balance: netAmt,
|
||||||
Head: emptyobject,
|
Head: emptyobject,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("set network account actor: %w", err)
|
return nil, xerrors.Errorf("set network account actor: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = state.SetActor(actors.BurntFundsAddress, &types.Actor{
|
||||||
|
Code: actors.AccountActorCodeCid,
|
||||||
|
Balance: types.NewInt(0),
|
||||||
|
Head: emptyobject,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("set burnt funds account actor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
for a, v := range actmap {
|
for a, v := range actmap {
|
||||||
err = state.SetActor(a, &types.Actor{
|
err = state.SetActor(a, &types.Actor{
|
||||||
Code: actors.AccountActorCodeCid,
|
Code: actors.AccountActorCodeCid,
|
||||||
@ -186,7 +200,9 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
|||||||
PeerID: pid,
|
PeerID: pid,
|
||||||
})
|
})
|
||||||
|
|
||||||
rval, err := doExec(ctx, vm, actors.StorageMarketAddress, owner, actors.SMAMethods.CreateStorageMiner, params)
|
// TODO: hardcoding 7000000 here is a little fragile, it changes any
|
||||||
|
// time anyone changes the initial account allocations
|
||||||
|
rval, err := doExecValue(ctx, vm, actors.StorageMarketAddress, owner, types.NewInt(7000000), actors.SMAMethods.CreateStorageMiner, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||||
}
|
}
|
||||||
@ -241,6 +257,10 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) {
|
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) {
|
||||||
|
return doExecValue(ctx, vm, to, from, types.NewInt(0), method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value types.BigInt, method uint64, params []byte) ([]byte, error) {
|
||||||
act, err := vm.StateTree().GetActor(from)
|
act, err := vm.StateTree().GetActor(from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("doExec failed to get from actor: %w", err)
|
return nil, xerrors.Errorf("doExec failed to get from actor: %w", err)
|
||||||
@ -253,7 +273,7 @@ func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uin
|
|||||||
Params: params,
|
Params: params,
|
||||||
GasLimit: types.NewInt(1000000),
|
GasLimit: types.NewInt(1000000),
|
||||||
GasPrice: types.NewInt(0),
|
GasPrice: types.NewInt(0),
|
||||||
Value: types.NewInt(0),
|
Value: value,
|
||||||
Nonce: act.Nonce,
|
Nonce: act.Nonce,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -374,15 +374,6 @@ func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Addre
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkBlockSignature(blk *types.BlockHeader, worker address.Address) error {
|
|
||||||
sigb, err := blk.SigningBytes()
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("failed to get block signing bytes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return blk.BlockSig.Verify(worker, sigb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should match up with 'Semantical Validation' in validation.md in the spec
|
// Should match up with 'Semantical Validation' in validation.md in the spec
|
||||||
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
|
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
|
||||||
h := b.Header
|
h := b.Header
|
||||||
@ -414,7 +405,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
|||||||
return xerrors.Errorf("GetMinerWorker failed: %w", err)
|
return xerrors.Errorf("GetMinerWorker failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkBlockSignature(h, waddr); err != nil {
|
if err := h.CheckBlockSignature(waddr); err != nil {
|
||||||
return xerrors.Errorf("check block signature failed: %w", err)
|
return xerrors.Errorf("check block signature failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/build"
|
||||||
|
|
||||||
cbor "github.com/ipfs/go-ipld-cbor"
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
"github.com/polydawn/refmt/obj/atlas"
|
"github.com/polydawn/refmt/obj/atlas"
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
@ -41,6 +43,10 @@ func NewInt(i uint64) BigInt {
|
|||||||
return BigInt{big.NewInt(0).SetUint64(i)}
|
return BigInt{big.NewInt(0).SetUint64(i)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromFil(i uint64) BigInt {
|
||||||
|
return BigMul(NewInt(i), NewInt(build.FilecoinPrecision))
|
||||||
|
}
|
||||||
|
|
||||||
func BigFromBytes(b []byte) BigInt {
|
func BigFromBytes(b []byte) BigInt {
|
||||||
i := big.NewInt(0).SetBytes(b)
|
i := big.NewInt(0).SetBytes(b)
|
||||||
return BigInt{i}
|
return BigInt{i}
|
||||||
|
@ -100,6 +100,15 @@ func (blk *BlockHeader) SigningBytes() ([]byte, error) {
|
|||||||
return blkcopy.Serialize()
|
return blkcopy.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (blk *BlockHeader) CheckBlockSignature(worker address.Address) error {
|
||||||
|
sigb, err := blk.SigningBytes()
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to get block signing bytes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blk.BlockSig.Verify(worker, sigb)
|
||||||
|
}
|
||||||
|
|
||||||
type MsgMeta struct {
|
type MsgMeta struct {
|
||||||
BlsMessages cid.Cid
|
BlsMessages cid.Cid
|
||||||
SecpkMessages cid.Cid
|
SecpkMessages cid.Cid
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/filecoin-project/go-amt-ipld"
|
amt "github.com/filecoin-project/go-amt-ipld"
|
||||||
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
||||||
"github.com/filecoin-project/go-lotus/chain/address"
|
"github.com/filecoin-project/go-lotus/chain/address"
|
||||||
"github.com/ipfs/go-cid"
|
cid "github.com/ipfs/go-cid"
|
||||||
"github.com/ipfs/go-hamt-ipld"
|
hamt "github.com/ipfs/go-hamt-ipld"
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ type VMContext interface {
|
|||||||
VerifySignature(sig *Signature, from address.Address, data []byte) aerrors.ActorError
|
VerifySignature(sig *Signature, from address.Address, data []byte) aerrors.ActorError
|
||||||
ChargeGas(uint64) aerrors.ActorError
|
ChargeGas(uint64) aerrors.ActorError
|
||||||
GetRandomness(height uint64) ([]byte, aerrors.ActorError)
|
GetRandomness(height uint64) ([]byte, aerrors.ActorError)
|
||||||
|
GetBalance(address.Address) (BigInt, aerrors.ActorError)
|
||||||
}
|
}
|
||||||
|
|
||||||
type storageWrapper struct {
|
type storageWrapper struct {
|
||||||
|
@ -225,6 +225,18 @@ func ResolveToKeyAddr(state types.StateTree, cst *hamt.CborIpldStore, addr addre
|
|||||||
return aast.Address, nil
|
return aast.Address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vmctx *VMContext) GetBalance(a address.Address) (types.BigInt, aerrors.ActorError) {
|
||||||
|
act, err := vmctx.state.GetActor(a)
|
||||||
|
switch err {
|
||||||
|
default:
|
||||||
|
return types.EmptyInt, aerrors.Escalate(err, "failed to look up actor balance")
|
||||||
|
case hamt.ErrNotFound:
|
||||||
|
return types.NewInt(0), nil
|
||||||
|
case nil:
|
||||||
|
return act.Balance, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type hBlocks interface {
|
type hBlocks interface {
|
||||||
GetBlock(context.Context, cid.Cid) (block.Block, error)
|
GetBlock(context.Context, cid.Cid) (block.Block, error)
|
||||||
AddBlock(block.Block) error
|
AddBlock(block.Block) error
|
||||||
|
@ -72,6 +72,9 @@ func main() {
|
|||||||
actors.IsMinerParam{},
|
actors.IsMinerParam{},
|
||||||
actors.PowerLookupParams{},
|
actors.PowerLookupParams{},
|
||||||
actors.UpdateStorageParams{},
|
actors.UpdateStorageParams{},
|
||||||
|
actors.ArbitrateConsensusFaultParams{},
|
||||||
|
actors.PledgeCollateralParams{},
|
||||||
|
actors.MinerSlashConsensusFault{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -40,7 +40,7 @@ func MakeGenesisMem(out io.Writer) func(bs dtypes.ChainBlockstore, w *wallet.Wal
|
|||||||
PeerIDs: []peer.ID{"peerID 1"},
|
PeerIDs: []peer.ID{"peerID 1"},
|
||||||
}
|
}
|
||||||
alloc := map[address.Address]types.BigInt{
|
alloc := map[address.Address]types.BigInt{
|
||||||
w: types.NewInt(100000),
|
w: types.NewInt(10000000),
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := gen.MakeGenesisBlock(bs, alloc, gmc, 100000)
|
b, err := gen.MakeGenesisBlock(bs, alloc, gmc, 100000)
|
||||||
|
Loading…
Reference in New Issue
Block a user