github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/multisig/testing.go (about) 1 package multisig 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "github.com/filecoin-project/go-address" 7 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 8 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 9 ) 10 11 type StateSummary struct { 12 PendingTxnCount uint64 13 NumApprovalsThreshold uint64 14 SignerCount int 15 } 16 17 // Checks internal invariants of multisig state. 18 func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) { 19 acc := &builtin.MessageAccumulator{} 20 21 // assert invariants involving signers 22 acc.Require(len(st.Signers) <= SignersMax, "multisig has too many signers: %d", len(st.Signers)) 23 acc.Require(uint64(len(st.Signers)) >= st.NumApprovalsThreshold, 24 "multisig has insufficient signers to meet threshold (%d < %d)", len(st.Signers), st.NumApprovalsThreshold) 25 26 if st.UnlockDuration == 0 { // See https://github.com/filecoin-project/specs-actors/issues/1185 27 acc.Require(st.StartEpoch == 0, "non-zero start epoch %d with zero unlock duration", st.StartEpoch) 28 acc.Require(st.InitialBalance.IsZero(), "non-zero locked balance %v with zero unlock duration", st.InitialBalance) 29 } 30 31 // create lookup to test transaction approvals are multisig signers. 32 signers := make(map[address.Address]struct{}) 33 for _, a := range st.Signers { 34 signers[a] = struct{}{} 35 } 36 37 // test pending transactions 38 maxTxnID := TxnID(-1) 39 numPending := uint64(0) 40 if transactions, err := adt.AsMap(store, st.PendingTxns, builtin.DefaultHamtBitwidth); err != nil { 41 acc.Addf("error loading transactions: %v", err) 42 } else { 43 var txn Transaction 44 err = transactions.ForEach(&txn, func(txnIDStr string) error { 45 txnID, err := ParseTxnIDKey(txnIDStr) 46 if err != nil { 47 return err 48 } 49 if txnID > maxTxnID { 50 maxTxnID = txnID 51 } 52 53 seenApprovals := make(map[address.Address]struct{}) 54 for _, approval := range txn.Approved { 55 _, found := signers[approval] 56 acc.Require(found, "approval %v for transaction %d is not in signers list", approval, txnID) 57 58 _, seen := seenApprovals[approval] 59 acc.Require(!seen, "duplicate approval %v for transaction %d", approval, txnID) 60 61 seenApprovals[approval] = struct{}{} 62 } 63 64 numPending++ 65 return nil 66 }) 67 acc.RequireNoError(err, "error iterating transactions") 68 } 69 70 acc.Require(st.NextTxnID > maxTxnID, "next transaction id %d is not greater than pending ids", st.NextTxnID) 71 return &StateSummary{ 72 PendingTxnCount: numPending, 73 NumApprovalsThreshold: st.NumApprovalsThreshold, 74 SignerCount: len(st.Signers), 75 }, acc 76 } 77 78 func ParseTxnIDKey(key string) (TxnID, error) { 79 id, err := binary.ReadVarint(bytes.NewReader([]byte(key))) 80 return TxnID(id), err 81 }