diff --git a/chain/actors/builtin/power/diff.go b/chain/actors/builtin/power/diff.go new file mode 100644 index 00000000000..3daa7056956 --- /dev/null +++ b/chain/actors/builtin/power/diff.go @@ -0,0 +1,117 @@ +package power + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +type ClaimChanges struct { + Added []ClaimInfo + Modified []ClaimModification + Removed []ClaimInfo +} + +type ClaimModification struct { + Miner address.Address + From Claim + To Claim +} + +type ClaimInfo struct { + Miner address.Address + Claim Claim +} + +func DiffClaims(pre, cur State) (*ClaimChanges, error) { + results := new(ClaimChanges) + + prec, err := pre.claims() + if err != nil { + return nil, err + } + + curc, err := cur.claims() + if err != nil { + return nil, err + } + + if err := adt.DiffAdtMap(prec, curc, &claimDiffer{results, pre, cur}); err != nil { + return nil, err + } + + return results, nil +} + +type claimDiffer struct { + Results *ClaimChanges + pre, after State +} + +func (c *claimDiffer) AsKey(key string) (abi.Keyer, error) { + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return nil, err + } + return abi.AddrKey(addr), nil +} + +func (c *claimDiffer) Add(key string, val *cbg.Deferred) error { + ci, err := c.after.decodeClaim(val) + if err != nil { + return err + } + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + c.Results.Added = append(c.Results.Added, ClaimInfo{ + Miner: addr, + Claim: ci, + }) + return nil +} + +func (c *claimDiffer) Modify(key string, from, to *cbg.Deferred) error { + ciFrom, err := c.pre.decodeClaim(from) + if err != nil { + return err + } + + ciTo, err := c.after.decodeClaim(to) + if err != nil { + return err + } + + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + + if ciFrom != ciTo { + c.Results.Modified = append(c.Results.Modified, ClaimModification{ + Miner: addr, + From: ciFrom, + To: ciTo, + }) + } + return nil +} + +func (c *claimDiffer) Remove(key string, val *cbg.Deferred) error { + ci, err := c.after.decodeClaim(val) + if err != nil { + return err + } + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + c.Results.Removed = append(c.Results.Removed, ClaimInfo{ + Miner: addr, + Claim: ci, + }) + return nil +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index f941ce93e17..e0cf0d700c9 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -4,6 +4,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" @@ -56,6 +57,11 @@ type State interface { MinerNominalPowerMeetsConsensusMinimum(address.Address) (bool, error) ListAllMiners() ([]address.Address, error) ForEachClaim(func(miner address.Address, claim Claim) error) error + ClaimsChanged(State) (bool, error) + + // Diff helpers. Used by Diff* functions internally. + claims() (adt.Map, error) + decodeClaim(*cbg.Deferred) (Claim, error) } type Claim struct { diff --git a/chain/actors/builtin/power/v0.go b/chain/actors/builtin/power/v0.go index 3f9a657777f..7636b612b51 100644 --- a/chain/actors/builtin/power/v0.go +++ b/chain/actors/builtin/power/v0.go @@ -1,9 +1,12 @@ package power import ( + "bytes" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -115,3 +118,28 @@ func (s *state0) ForEachClaim(cb func(miner address.Address, claim Claim) error) }) }) } + +func (s *state0) ClaimsChanged(other State) (bool, error) { + other0, ok := other.(*state0) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other0.State.Claims), nil +} + +func (s *state0) claims() (adt.Map, error) { + return adt0.AsMap(s.store, s.Claims) +} + +func (s *state0) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power0.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV0Claim(ci), nil +} + +func fromV0Claim(v0 power0.Claim) Claim { + return (Claim)(v0) +} diff --git a/chain/actors/builtin/power/v2.go b/chain/actors/builtin/power/v2.go index 0c15f066928..012dc2a4f01 100644 --- a/chain/actors/builtin/power/v2.go +++ b/chain/actors/builtin/power/v2.go @@ -1,9 +1,12 @@ package power import ( + "bytes" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -115,3 +118,31 @@ func (s *state2) ForEachClaim(cb func(miner address.Address, claim Claim) error) }) }) } + +func (s *state2) ClaimsChanged(other State) (bool, error) { + other2, ok := other.(*state2) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other2.State.Claims), nil +} + +func (s *state2) claims() (adt.Map, error) { + return adt2.AsMap(s.store, s.Claims) +} + +func (s *state2) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power2.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV2Claim(ci), nil +} + +func fromV2Claim(v2 power2.Claim) Claim { + return Claim{ + RawBytePower: v2.RawBytePower, + QualityAdjPower: v2.QualityAdjPower, + } +}