diff --git a/chain/actors/builtin/multisig/diff.go b/chain/actors/builtin/multisig/diff.go new file mode 100644 index 000000000..680d0870a --- /dev/null +++ b/chain/actors/builtin/multisig/diff.go @@ -0,0 +1,134 @@ +package multisig + +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 PendingTransactionChanges struct { + Added []TransactionChange + Modified []TransactionModification + Removed []TransactionChange +} + +type TransactionChange struct { + TxID int64 + Tx Transaction +} + +type TransactionModification struct { + TxID int64 + From Transaction + To Transaction +} + +func DiffPendingTransactions(pre, cur State) (*PendingTransactionChanges, error) { + results := new(PendingTransactionChanges) + if changed, err := pre.PendingTxnChanged(cur); err != nil { + return nil, err + } else if !changed { // if nothing has changed then return an empty result and bail. + return results, nil + } + + pret, err := pre.transactions() + if err != nil { + return nil, err + } + + curt, err := cur.transactions() + if err != nil { + return nil, err + } + + if err := adt.DiffAdtMap(pret, curt, &transactionDiffer{results, pre, cur}); err != nil { + return nil, err + } + return results, nil +} + +type transactionDiffer struct { + Results *PendingTransactionChanges + pre, after State +} + +func (t *transactionDiffer) AsKey(key string) (abi.Keyer, error) { + txID, err := abi.ParseIntKey(key) + if err != nil { + return nil, err + } + return abi.IntKey(txID), nil +} + +func (t *transactionDiffer) Add(key string, val *cbg.Deferred) error { + txID, err := abi.ParseIntKey(key) + if err != nil { + return err + } + tx, err := t.after.decodeTransaction(val) + if err != nil { + return err + } + t.Results.Added = append(t.Results.Added, TransactionChange{ + TxID: txID, + Tx: tx, + }) + return nil +} + +func (t *transactionDiffer) Modify(key string, from, to *cbg.Deferred) error { + txID, err := abi.ParseIntKey(key) + if err != nil { + return err + } + + txFrom, err := t.pre.decodeTransaction(from) + if err != nil { + return err + } + + txTo, err := t.after.decodeTransaction(to) + if err != nil { + return err + } + + if approvalsChanged(txFrom.Approved, txTo.Approved) { + t.Results.Modified = append(t.Results.Modified, TransactionModification{ + TxID: txID, + From: txFrom, + To: txTo, + }) + } + + return nil +} + +func approvalsChanged(from, to []address.Address) bool { + if len(from) != len(to) { + return true + } + for idx := range from { + if from[idx] != to[idx] { + return true + } + } + return false +} + +func (t *transactionDiffer) Remove(key string, val *cbg.Deferred) error { + txID, err := abi.ParseIntKey(key) + if err != nil { + return err + } + tx, err := t.pre.decodeTransaction(val) + if err != nil { + return err + } + t.Results.Removed = append(t.Results.Removed, TransactionChange{ + TxID: txID, + Tx: tx, + }) + return nil +} diff --git a/chain/actors/builtin/multisig/state.go b/chain/actors/builtin/multisig/state.go index 89a7eedad..fea42ba5f 100644 --- a/chain/actors/builtin/multisig/state.go +++ b/chain/actors/builtin/multisig/state.go @@ -1,6 +1,7 @@ package multisig import ( + cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -47,6 +48,10 @@ type State interface { Signers() ([]address.Address, error) ForEachPendingTxn(func(id int64, txn Transaction) error) error + PendingTxnChanged(State) (bool, error) + + transactions() (adt.Map, error) + decodeTransaction(val *cbg.Deferred) (Transaction, error) } type Transaction = msig0.Transaction diff --git a/chain/actors/builtin/multisig/state0.go b/chain/actors/builtin/multisig/state0.go index c934343e7..e6f9a9c36 100644 --- a/chain/actors/builtin/multisig/state0.go +++ b/chain/actors/builtin/multisig/state0.go @@ -1,17 +1,20 @@ package multisig import ( + "bytes" "encoding/binary" "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" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/chain/actors/adt" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" + multisig0 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig" ) var _ State = (*state0)(nil) @@ -68,3 +71,24 @@ func (s *state0) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err return cb(txid, (Transaction)(out)) }) } + +func (s *state0) PendingTxnChanged(other State) (bool, error) { + other0, ok := other.(*state0) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other0.PendingTxns), nil +} + +func (s *state0) transactions() (adt.Map, error) { + return adt0.AsMap(s.store, s.PendingTxns) +} + +func (s *state0) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx multisig0.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return tx, nil +} diff --git a/chain/actors/builtin/multisig/state2.go b/chain/actors/builtin/multisig/state2.go index a78b07d55..628da3f2c 100644 --- a/chain/actors/builtin/multisig/state2.go +++ b/chain/actors/builtin/multisig/state2.go @@ -1,11 +1,13 @@ package multisig import ( + "bytes" "encoding/binary" "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" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -68,3 +70,24 @@ func (s *state2) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err return cb(txid, (Transaction)(out)) }) } + +func (s *state2) PendingTxnChanged(other State) (bool, error) { + other2, ok := other.(*state2) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other2.PendingTxns), nil +} + +func (s *state2) transactions() (adt.Map, error) { + return adt2.AsMap(s.store, s.PendingTxns) +} + +func (s *state2) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig2.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return tx, nil +}