github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/multisig/multisig_test.go (about)

     1  package multisig_test
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  	"testing"
     7  
     8  	addr "github.com/filecoin-project/go-address"
     9  	"github.com/filecoin-project/go-state-types/abi"
    10  	"github.com/filecoin-project/go-state-types/big"
    11  	"github.com/filecoin-project/go-state-types/cbor"
    12  	"github.com/filecoin-project/go-state-types/exitcode"
    13  	"github.com/minio/blake2b-simd"
    14  	assert "github.com/stretchr/testify/assert"
    15  	require "github.com/stretchr/testify/require"
    16  
    17  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    18  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/miner"
    19  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig"
    20  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    21  	"github.com/filecoin-project/specs-actors/v4/support/mock"
    22  	tutil "github.com/filecoin-project/specs-actors/v4/support/testing"
    23  )
    24  
    25  func TestExports(t *testing.T) {
    26  	mock.CheckActorExports(t, multisig.Actor{})
    27  }
    28  
    29  func TestConstruction(t *testing.T) {
    30  	actor := multisig.Actor{}
    31  
    32  	receiver := tutil.NewIDAddr(t, 100)
    33  	anne := tutil.NewIDAddr(t, 101)
    34  	anneNonId := tutil.NewBLSAddr(t, 1)
    35  
    36  	bob := tutil.NewIDAddr(t, 102)
    37  	bobNonId := tutil.NewBLSAddr(t, 2)
    38  
    39  	charlie := tutil.NewIDAddr(t, 103)
    40  
    41  	builder := mock.NewBuilder(receiver).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
    42  
    43  	t.Run("simple construction", func(t *testing.T) {
    44  		rt := builder.Build(t)
    45  		startEpoch := abi.ChainEpoch(100)
    46  		unlockDuration := abi.ChainEpoch(200)
    47  
    48  		params := multisig.ConstructorParams{
    49  			Signers:               []addr.Address{anne, bob, charlie},
    50  			NumApprovalsThreshold: 2,
    51  			UnlockDuration:        unlockDuration,
    52  			StartEpoch:            startEpoch,
    53  		}
    54  
    55  		rt.SetReceived(abi.NewTokenAmount(100))
    56  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
    57  		ret := rt.Call(actor.Constructor, &params)
    58  		assert.Nil(t, ret)
    59  		rt.Verify()
    60  
    61  		var st multisig.State
    62  		rt.GetState(&st)
    63  		assert.Equal(t, params.Signers, st.Signers)
    64  		assert.Equal(t, params.NumApprovalsThreshold, st.NumApprovalsThreshold)
    65  		assert.Equal(t, abi.NewTokenAmount(100), st.InitialBalance)
    66  		assert.Equal(t, unlockDuration, st.UnlockDuration)
    67  		assert.Equal(t, startEpoch, st.StartEpoch)
    68  		txns, err := adt.AsMap(adt.AsStore(rt), st.PendingTxns, builtin.DefaultHamtBitwidth)
    69  		assert.NoError(t, err)
    70  		keys, err := txns.CollectKeys()
    71  		require.NoError(t, err)
    72  		assert.Empty(t, keys)
    73  
    74  		assertStateInvariants(t, rt, &st)
    75  	})
    76  
    77  	t.Run("construction by resolving signers to ID addresses", func(t *testing.T) {
    78  		rt := builder.Build(t)
    79  		params := multisig.ConstructorParams{
    80  			Signers:               []addr.Address{anneNonId, bobNonId, charlie},
    81  			NumApprovalsThreshold: 2,
    82  			UnlockDuration:        0,
    83  		}
    84  		rt.AddIDAddress(anneNonId, anne)
    85  		rt.AddIDAddress(bobNonId, bob)
    86  
    87  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
    88  		ret := rt.Call(actor.Constructor, &params)
    89  		assert.Nil(t, ret)
    90  		rt.Verify()
    91  
    92  		var st multisig.State
    93  		rt.GetState(&st)
    94  		require.Equal(t, []addr.Address{anne, bob, charlie}, st.Signers)
    95  
    96  		assertStateInvariants(t, rt, &st)
    97  	})
    98  
    99  	t.Run("construction with vesting", func(t *testing.T) {
   100  		rt := builder.WithEpoch(1234).Build(t)
   101  		params := multisig.ConstructorParams{
   102  			Signers:               []addr.Address{anne, bob, charlie},
   103  			NumApprovalsThreshold: 3,
   104  			UnlockDuration:        100,
   105  			StartEpoch:            1234,
   106  		}
   107  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   108  		ret := rt.Call(actor.Constructor, &params)
   109  		assert.Nil(t, ret)
   110  		rt.Verify()
   111  
   112  		var st multisig.State
   113  		rt.GetState(&st)
   114  		assert.Equal(t, params.Signers, st.Signers)
   115  		assert.Equal(t, params.NumApprovalsThreshold, st.NumApprovalsThreshold)
   116  		assert.Equal(t, abi.NewTokenAmount(0), st.InitialBalance)
   117  		assert.Equal(t, abi.ChainEpoch(100), st.UnlockDuration)
   118  		assert.Equal(t, abi.ChainEpoch(1234), st.StartEpoch)
   119  
   120  		// assert no transactions
   121  		empty, err := adt.StoreEmptyMap(rt.AdtStore(), builtin.DefaultHamtBitwidth)
   122  		require.NoError(t, err)
   123  		assert.Equal(t, empty, st.PendingTxns)
   124  
   125  		assertStateInvariants(t, rt, &st)
   126  	})
   127  
   128  	t.Run("fail to construct multisig actor with 0 signers", func(t *testing.T) {
   129  		rt := builder.Build(t)
   130  		params := multisig.ConstructorParams{
   131  			Signers:               []addr.Address{},
   132  			NumApprovalsThreshold: 1,
   133  			UnlockDuration:        1,
   134  		}
   135  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   136  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   137  			rt.Call(actor.Constructor, &params)
   138  		})
   139  		rt.Verify()
   140  
   141  	})
   142  
   143  	t.Run("fail to construct multisig actor with more than max signers", func(t *testing.T) {
   144  		rt := builder.Build(t)
   145  		params := multisig.ConstructorParams{
   146  			Signers:               []addr.Address{},
   147  			NumApprovalsThreshold: 1,
   148  			UnlockDuration:        1,
   149  		}
   150  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   151  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   152  			rt.Call(actor.Constructor, &params)
   153  		})
   154  		rt.Verify()
   155  	})
   156  
   157  	t.Run("fail to construct multisig with more approvals than signers", func(t *testing.T) {
   158  		rt := builder.Build(t)
   159  		signers := make([]addr.Address, multisig.SignersMax+1)
   160  		for i := range signers {
   161  			signers[i] = tutil.NewIDAddr(t, uint64(101+i))
   162  		}
   163  		params := multisig.ConstructorParams{
   164  			Signers:               signers,
   165  			NumApprovalsThreshold: 4,
   166  			UnlockDuration:        1,
   167  		}
   168  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   169  		rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "cannot add more than 256 signers", func() {
   170  			rt.Call(actor.Constructor, &params)
   171  		})
   172  		rt.Verify()
   173  	})
   174  
   175  	t.Run("fail to construct multisig if a signer is not resolvable to an ID address", func(t *testing.T) {
   176  		builder := mock.NewBuilder(receiver).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
   177  		rt := builder.Build(t)
   178  		params := multisig.ConstructorParams{
   179  			Signers:               []addr.Address{anneNonId, bob, charlie},
   180  			NumApprovalsThreshold: 2,
   181  			UnlockDuration:        1,
   182  		}
   183  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   184  		rt.ExpectSend(anneNonId, builtin.MethodSend, nil, abi.NewTokenAmount(0), nil, exitcode.Ok)
   185  		rt.ExpectAbort(exitcode.ErrIllegalState, func() {
   186  			rt.Call(actor.Constructor, &params)
   187  		})
   188  		rt.Verify()
   189  	})
   190  
   191  	t.Run("fail to construct multisig with duplicate signers(all ID addresses)", func(t *testing.T) {
   192  		rt := builder.Build(t)
   193  		params := multisig.ConstructorParams{
   194  			Signers:               []addr.Address{anne, bob, bob},
   195  			NumApprovalsThreshold: 2,
   196  			UnlockDuration:        0,
   197  		}
   198  
   199  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   200  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   201  			rt.Call(actor.Constructor, &params)
   202  		})
   203  		rt.Verify()
   204  	})
   205  
   206  	t.Run("fail to construct multisig with duplicate signers(ID & non-ID addresses)", func(t *testing.T) {
   207  		rt := builder.Build(t)
   208  		params := multisig.ConstructorParams{
   209  			Signers:               []addr.Address{anne, bobNonId, bob},
   210  			NumApprovalsThreshold: 2,
   211  			UnlockDuration:        0,
   212  		}
   213  
   214  		rt.AddIDAddress(bobNonId, bob)
   215  		rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
   216  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   217  			rt.Call(actor.Constructor, &params)
   218  		})
   219  		rt.Verify()
   220  	})
   221  }
   222  
   223  func TestVesting(t *testing.T) {
   224  	actor := msActorHarness{multisig.Actor{}, t}
   225  	startEpoch := abi.ChainEpoch(0)
   226  
   227  	receiver := tutil.NewIDAddr(t, 100)
   228  	anne := tutil.NewIDAddr(t, 101)
   229  	bob := tutil.NewIDAddr(t, 102)
   230  	charlie := tutil.NewIDAddr(t, 103)
   231  	darlene := tutil.NewIDAddr(t, 103)
   232  
   233  	const unlockDuration = abi.ChainEpoch(10)
   234  	var multisigInitialBalance = abi.NewTokenAmount(100)
   235  
   236  	builder := mock.NewBuilder(receiver).
   237  		WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID).
   238  		WithEpoch(0).
   239  		WithBalance(multisigInitialBalance, multisigInitialBalance).
   240  		WithHasher(blake2b.Sum256)
   241  
   242  	t.Run("happy path full vesting", func(t *testing.T) {
   243  		rt := builder.Build(t)
   244  
   245  		actor.constructAndVerify(rt, 2, unlockDuration, startEpoch, anne, bob, charlie)
   246  		rt.SetReceived(big.Zero())
   247  
   248  		// anne proposes that darlene receives `multisgiInitialBalance` FIL.
   249  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   250  		proposalHashData := actor.proposeOK(rt, darlene, multisigInitialBalance, builtin.MethodSend, nil, nil)
   251  
   252  		// bob approves anne's transaction too soon
   253  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   254  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   255  			actor.approveOK(rt, 0, proposalHashData, nil)
   256  		})
   257  		rt.Reset()
   258  
   259  		// Advance the epoch s.t. all funds are unlocked.
   260  		rt.SetEpoch(0 + unlockDuration)
   261  		// expect darlene to receive the transaction proposed by anne.
   262  		rt.ExpectSend(darlene, builtin.MethodSend, nil, multisigInitialBalance, nil, exitcode.Ok)
   263  		actor.approveOK(rt, 0, proposalHashData, nil)
   264  		actor.checkState(rt)
   265  	})
   266  
   267  	t.Run("partial vesting propose to send half the actor balance when the epoch is half the unlock duration", func(t *testing.T) {
   268  		rt := builder.Build(t)
   269  
   270  		actor.constructAndVerify(rt, 2, 10, startEpoch, anne, bob, charlie)
   271  		rt.SetReceived(big.Zero())
   272  
   273  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   274  		proposalHashData := actor.proposeOK(rt, darlene, big.Div(multisigInitialBalance, big.NewInt(2)), builtin.MethodSend, nil, nil)
   275  
   276  		// set the current balance of the multisig actor to its InitialBalance amount
   277  		rt.SetEpoch(0 + unlockDuration/2)
   278  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   279  		rt.ExpectSend(darlene, builtin.MethodSend, nil, big.Div(multisigInitialBalance, big.NewInt(2)), nil, exitcode.Ok)
   280  		actor.approveOK(rt, 0, proposalHashData, nil)
   281  		actor.checkState(rt)
   282  	})
   283  
   284  	t.Run("propose and autoapprove transaction above locked amount fails", func(t *testing.T) {
   285  		rt := builder.Build(t)
   286  
   287  		actor.constructAndVerify(rt, 1, unlockDuration, startEpoch, anne, bob, charlie)
   288  		rt.SetReceived(big.Zero())
   289  
   290  		// this propose will fail since it would send more than the required locked balance and num approvals == 1
   291  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   292  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   293  			_ = actor.propose(rt, darlene, abi.NewTokenAmount(100), builtin.MethodSend, nil, nil)
   294  		})
   295  		rt.Reset()
   296  
   297  		// this will pass since sending below the locked amount is permitted
   298  		rt.SetEpoch(1)
   299  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   300  		rt.ExpectSend(darlene, builtin.MethodSend, nil, abi.NewTokenAmount(10), nil, 0)
   301  		actor.proposeOK(rt, darlene, abi.NewTokenAmount(10), builtin.MethodSend, nil, nil)
   302  		actor.checkState(rt)
   303  	})
   304  
   305  	t.Run("fail to vest more than locked amount", func(t *testing.T) {
   306  		rt := builder.Build(t)
   307  
   308  		actor.constructAndVerify(rt, 2, unlockDuration, startEpoch, anne, bob, charlie)
   309  		rt.SetReceived(big.Zero())
   310  
   311  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   312  		proposalHashData := actor.proposeOK(rt, darlene, big.Div(multisigInitialBalance, big.NewInt(2)), builtin.MethodSend, nil, nil)
   313  
   314  		// this propose will fail since it would send more than the required locked balance.
   315  		rt.SetEpoch(1)
   316  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   317  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   318  			_ = actor.approve(rt, 0, proposalHashData, nil)
   319  		})
   320  	})
   321  
   322  	t.Run("avoid truncating division", func(t *testing.T) {
   323  		rt := builder.Build(t)
   324  
   325  		lockedBalance := big.NewInt(int64(unlockDuration) - 1) // Balance < duration
   326  		rt.SetReceived(lockedBalance)
   327  		rt.SetBalance(lockedBalance)
   328  		actor.constructAndVerify(rt, 1, unlockDuration, startEpoch, anne)
   329  		rt.SetReceived(big.Zero())
   330  
   331  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   332  		// Expect nothing vested yet
   333  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   334  			actor.proposeOK(rt, anne, big.NewInt(1), builtin.MethodSend, nil, nil)
   335  		})
   336  		rt.Reset()
   337  
   338  		// Expect nothing (<1 unit) vested after 1 epoch
   339  		rt.SetEpoch(1)
   340  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   341  			actor.proposeOK(rt, anne, big.NewInt(1), builtin.MethodSend, nil, nil)
   342  		})
   343  		rt.Reset()
   344  
   345  		// Expect 1 unit available after 2 epochs
   346  		rt.SetEpoch(2)
   347  		rt.ExpectSend(anne, builtin.MethodSend, nil, big.NewInt(1), nil, exitcode.Ok)
   348  		actor.proposeOK(rt, anne, big.NewInt(1), builtin.MethodSend, nil, nil)
   349  		rt.SetBalance(lockedBalance)
   350  
   351  		// Do not expect full vesting before full duration has elapsed
   352  		rt.SetEpoch(unlockDuration - 1)
   353  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   354  			actor.proposeOK(rt, anne, lockedBalance, builtin.MethodSend, nil, nil)
   355  		})
   356  		rt.Reset()
   357  
   358  		// Expect all but one unit available after all but one epochs
   359  		rt.ExpectSend(anne, builtin.MethodSend, nil, big.Sub(lockedBalance, big.NewInt(1)), nil, exitcode.Ok)
   360  		actor.proposeOK(rt, anne, big.Sub(lockedBalance, big.NewInt(1)), builtin.MethodSend, nil, nil)
   361  		rt.SetBalance(lockedBalance)
   362  
   363  		// Expect everything after exactly the right epochs
   364  		rt.SetBalance(lockedBalance)
   365  		rt.SetEpoch(unlockDuration)
   366  		rt.ExpectSend(anne, builtin.MethodSend, nil, lockedBalance, nil, exitcode.Ok)
   367  		actor.proposeOK(rt, anne, lockedBalance, builtin.MethodSend, nil, nil)
   368  		actor.checkState(rt)
   369  	})
   370  
   371  	t.Run("sending zero ok when nothing vested", func(t *testing.T) {
   372  		rt := builder.Build(t)
   373  		actor.constructAndVerify(rt, 1, unlockDuration, startEpoch, anne)
   374  		rt.SetReceived(big.Zero())
   375  
   376  		sendAmount := abi.NewTokenAmount(0)
   377  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   378  		rt.ExpectSend(bob, builtin.MethodSend, nil, sendAmount, nil, 0)
   379  		actor.proposeOK(rt, bob, sendAmount, builtin.MethodSend, nil, nil)
   380  		actor.checkState(rt)
   381  	})
   382  
   383  	t.Run("sending zero ok when lockup exceeds balance", func(t *testing.T) {
   384  		rt := builder.Build(t)
   385  		rt.SetReceived(big.Zero())
   386  		rt.SetBalance(big.Zero())
   387  		actor.constructAndVerify(rt, 1, 0, startEpoch, anne)
   388  
   389  		// Lock up funds the actor doesn't have yet.
   390  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
   391  		actor.lockBalance(rt, startEpoch, unlockDuration, abi.NewTokenAmount(10))
   392  
   393  		// Make a transaction that transfers no value.
   394  		sendAmount := abi.NewTokenAmount(0)
   395  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   396  		rt.ExpectSend(bob, builtin.MethodSend, nil, sendAmount, nil, 0)
   397  		actor.proposeOK(rt, bob, sendAmount, builtin.MethodSend, nil, nil)
   398  
   399  		// Verify that sending any value is prevented
   400  		sendAmount = abi.NewTokenAmount(1)
   401  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   402  			_ = actor.propose(rt, bob, sendAmount, builtin.MethodSend, nil, nil)
   403  		})
   404  	})
   405  }
   406  
   407  func TestPropose(t *testing.T) {
   408  	actor := msActorHarness{multisig.Actor{}, t}
   409  	startEpoch := abi.ChainEpoch(0)
   410  
   411  	receiver := tutil.NewIDAddr(t, 100)
   412  	anne := tutil.NewIDAddr(t, 101)
   413  	bob := tutil.NewIDAddr(t, 102)
   414  	chuck := tutil.NewIDAddr(t, 103)
   415  
   416  	const noUnlockDuration = abi.ChainEpoch(0)
   417  	var sendValue = abi.NewTokenAmount(10)
   418  	var fakeParams = builtin.CBORBytes([]byte{1, 2, 3, 4})
   419  	var signers = []addr.Address{anne, bob}
   420  
   421  	builder := mock.NewBuilder(receiver).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
   422  
   423  	t.Run("simple propose", func(t *testing.T) {
   424  		const numApprovals = uint64(2)
   425  		rt := builder.Build(t)
   426  
   427  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   428  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   429  		actor.proposeOK(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   430  
   431  		// the transaction remains awaiting second approval
   432  		actor.assertTransactions(rt, multisig.Transaction{
   433  			To:       chuck,
   434  			Value:    sendValue,
   435  			Method:   builtin.MethodSend,
   436  			Params:   fakeParams,
   437  			Approved: []addr.Address{anne},
   438  		})
   439  		actor.checkState(rt)
   440  	})
   441  
   442  	t.Run("propose with threshold met", func(t *testing.T) {
   443  		const numApprovals = uint64(1)
   444  
   445  		rt := builder.WithBalance(abi.NewTokenAmount(20), abi.NewTokenAmount(0)).Build(t)
   446  
   447  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   448  
   449  		rt.ExpectSend(chuck, builtin.MethodSend, fakeParams, sendValue, nil, 0)
   450  
   451  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   452  		actor.proposeOK(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   453  
   454  		// the transaction has been sent and cleaned up
   455  		actor.assertTransactions(rt)
   456  		actor.checkState(rt)
   457  	})
   458  
   459  	t.Run("propose with threshold and non-empty return value", func(t *testing.T) {
   460  		const numApprovals = uint64(1)
   461  
   462  		rt := builder.WithBalance(abi.NewTokenAmount(20), abi.NewTokenAmount(0)).Build(t)
   463  
   464  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   465  
   466  		proposeRet := miner.GetControlAddressesReturn{
   467  			Owner:  tutil.NewIDAddr(t, 1),
   468  			Worker: tutil.NewIDAddr(t, 2),
   469  		}
   470  		rt.ExpectSend(chuck, builtin.MethodsMiner.ControlAddresses, fakeParams, sendValue, &proposeRet, 0)
   471  
   472  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   473  
   474  		var out miner.GetControlAddressesReturn
   475  		actor.proposeOK(rt, chuck, sendValue, builtin.MethodsMiner.ControlAddresses, fakeParams, &out)
   476  		// assert ProposeReturn.Ret can be marshaled into the expected structure.
   477  		assert.Equal(t, proposeRet, out)
   478  
   479  		// the transaction has been sent and cleaned up
   480  		actor.assertTransactions(rt)
   481  		actor.checkState(rt)
   482  	})
   483  
   484  	t.Run("fail propose with threshold met and insufficient balance", func(t *testing.T) {
   485  		const numApprovals = uint64(1)
   486  		rt := builder.WithBalance(abi.NewTokenAmount(0), abi.NewTokenAmount(0)).Build(t)
   487  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   488  
   489  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   490  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
   491  			_ = actor.propose(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   492  		})
   493  		rt.Reset()
   494  
   495  		// proposal failed since it should have but failed to immediately execute.
   496  		actor.assertTransactions(rt)
   497  		actor.checkState(rt)
   498  	})
   499  
   500  	t.Run("fail propose from non-signer", func(t *testing.T) {
   501  		// non-signer address
   502  		richard := tutil.NewIDAddr(t, 105)
   503  		const numApprovals = uint64(2)
   504  
   505  		rt := builder.Build(t)
   506  
   507  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   508  
   509  		rt.SetCaller(richard, builtin.AccountActorCodeID)
   510  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   511  			_ = actor.propose(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   512  		})
   513  		rt.Reset()
   514  
   515  		// the transaction is not persisted
   516  		actor.assertTransactions(rt)
   517  		actor.checkState(rt)
   518  	})
   519  }
   520  
   521  func TestApprove(t *testing.T) {
   522  	actor := msActorHarness{multisig.Actor{}, t}
   523  	startEpoch := abi.ChainEpoch(0)
   524  
   525  	receiver := tutil.NewIDAddr(t, 100)
   526  	anne := tutil.NewIDAddr(t, 101)
   527  	bob := tutil.NewIDAddr(t, 102)
   528  	chuck := tutil.NewIDAddr(t, 103)
   529  
   530  	const noUnlockDuration = abi.ChainEpoch(0)
   531  	const numApprovals = uint64(2)
   532  	const txnID = int64(0)
   533  	const fakeMethod = abi.MethodNum(42)
   534  	var sendValue = abi.NewTokenAmount(10)
   535  	var fakeParams = builtin.CBORBytes([]byte{1, 2, 3, 4})
   536  	var signers = []addr.Address{anne, bob}
   537  
   538  	builder := mock.NewBuilder(receiver).
   539  		WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID).
   540  		WithHasher(blake2b.Sum256)
   541  
   542  	t.Run("simple propose and approval", func(t *testing.T) {
   543  		rt := builder.Build(t)
   544  
   545  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   546  
   547  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   548  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   549  
   550  		actor.assertTransactions(rt, multisig.Transaction{
   551  			To:       chuck,
   552  			Value:    sendValue,
   553  			Method:   fakeMethod,
   554  			Params:   fakeParams,
   555  			Approved: []addr.Address{anne},
   556  		})
   557  
   558  		rt.SetBalance(sendValue)
   559  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   560  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   561  		actor.approveOK(rt, txnID, proposalHashData, nil)
   562  
   563  		// Transaction should be removed from actor state after send
   564  		actor.assertTransactions(rt)
   565  		actor.checkState(rt)
   566  	})
   567  
   568  	t.Run("approve with non-empty return value", func(t *testing.T) {
   569  		const numApprovals = uint64(2)
   570  
   571  		rt := builder.WithBalance(abi.NewTokenAmount(20), abi.NewTokenAmount(0)).Build(t)
   572  
   573  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   574  
   575  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   576  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, builtin.MethodsMiner.ControlAddresses, fakeParams, nil)
   577  
   578  		approveRet := miner.GetControlAddressesReturn{
   579  			Owner:  tutil.NewIDAddr(t, 1),
   580  			Worker: tutil.NewIDAddr(t, 2),
   581  		}
   582  
   583  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   584  		rt.ExpectSend(chuck, builtin.MethodsMiner.ControlAddresses, fakeParams, sendValue, &approveRet, 0)
   585  		var out miner.GetControlAddressesReturn
   586  		actor.approveOK(rt, txnID, proposalHashData, &out)
   587  		// assert approveRet.Ret can be marshaled into the expected structure.
   588  		assert.Equal(t, approveRet, out)
   589  
   590  		// the transaction has been sent and cleaned up
   591  		actor.assertTransactions(rt)
   592  		actor.checkState(rt)
   593  	})
   594  
   595  	t.Run("approval works if enough funds have been unlocked for the transaction", func(t *testing.T) {
   596  		rt := builder.Build(t)
   597  		unlockDuration := abi.ChainEpoch(20)
   598  		startEpoch := abi.ChainEpoch(10)
   599  		sendValue := abi.NewTokenAmount(20)
   600  
   601  		rt.SetReceived(sendValue)
   602  		actor.constructAndVerify(rt, numApprovals, unlockDuration, startEpoch, signers...)
   603  
   604  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   605  		proposalHash := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   606  
   607  		actor.assertTransactions(rt, multisig.Transaction{
   608  			To:       chuck,
   609  			Value:    sendValue,
   610  			Method:   fakeMethod,
   611  			Params:   fakeParams,
   612  			Approved: []addr.Address{anne},
   613  		})
   614  
   615  		rt.SetEpoch(startEpoch + 20)
   616  		rt.SetBalance(sendValue)
   617  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   618  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   619  
   620  		// as the (current epoch - startepoch) = 20 is  equal to unlock duration, all initial funds must have been vested and available to spend
   621  		actor.approveOK(rt, txnID, proposalHash, nil)
   622  		actor.checkState(rt)
   623  	})
   624  
   625  	t.Run("fail approval if current balance is less than the transaction value", func(t *testing.T) {
   626  		rt := builder.Build(t)
   627  		numApprovals := uint64(1)
   628  
   629  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   630  
   631  		rt.SetBalance(big.Sub(sendValue, big.NewInt(1)))
   632  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   633  		rt.ExpectAbortContainsMessage(exitcode.ErrInsufficientFunds, "insufficient funds unlocked: current balance 9 less than amount to spend 10", func() {
   634  			_ = actor.propose(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   635  		})
   636  	})
   637  
   638  	t.Run("fail approval if enough unlocked balance not available", func(t *testing.T) {
   639  		rt := builder.Build(t)
   640  		unlockDuration := abi.ChainEpoch(20)
   641  		startEpoch := abi.ChainEpoch(10)
   642  		sendValue := abi.NewTokenAmount(20)
   643  
   644  		rt.SetReceived(sendValue)
   645  		actor.constructAndVerify(rt, numApprovals, unlockDuration, startEpoch, signers...)
   646  
   647  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   648  		proposalHash := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   649  
   650  		actor.assertTransactions(rt, multisig.Transaction{
   651  			To:       chuck,
   652  			Value:    sendValue,
   653  			Method:   fakeMethod,
   654  			Params:   fakeParams,
   655  			Approved: []addr.Address{anne},
   656  		})
   657  
   658  		rt.SetEpoch(startEpoch + 5)
   659  		rt.SetBalance(sendValue)
   660  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   661  		// expected locked amount at epoch=startEpoch + 5 would be 15.
   662  		// however, remaining funds if this transactions is approved would be 0.
   663  		rt.ExpectAbortContainsMessage(exitcode.ErrInsufficientFunds, "insufficient funds unlocked: balance 0 if spent 20 would be less than locked amount 15",
   664  			func() {
   665  				actor.approveOK(rt, txnID, proposalHash, nil)
   666  			})
   667  	})
   668  
   669  	t.Run("fail approval with bad proposal hash", func(t *testing.T) {
   670  		rt := builder.Build(t)
   671  
   672  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   673  
   674  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   675  		actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   676  
   677  		actor.assertTransactions(rt, multisig.Transaction{
   678  			To:       chuck,
   679  			Value:    sendValue,
   680  			Method:   fakeMethod,
   681  			Params:   fakeParams,
   682  			Approved: []addr.Address{anne},
   683  		})
   684  
   685  		rt.SetBalance(sendValue)
   686  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   687  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   688  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   689  			proposalHashData := makeProposalHash(t, &multisig.Transaction{
   690  				To:       chuck,
   691  				Value:    sendValue,
   692  				Method:   fakeMethod,
   693  				Params:   fakeParams,
   694  				Approved: []addr.Address{bob}, // mismatch
   695  			})
   696  			_ = actor.approve(rt, txnID, proposalHashData, nil)
   697  		})
   698  	})
   699  
   700  	t.Run("accept approval with no proposal hash", func(t *testing.T) {
   701  		rt := builder.Build(t)
   702  
   703  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, 0, signers...)
   704  
   705  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   706  		actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   707  
   708  		actor.assertTransactions(rt, multisig.Transaction{
   709  			To:       chuck,
   710  			Value:    sendValue,
   711  			Method:   fakeMethod,
   712  			Params:   fakeParams,
   713  			Approved: []addr.Address{anne},
   714  		})
   715  
   716  		rt.SetBalance(sendValue)
   717  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   718  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   719  		actor.approveOK(rt, txnID, nil, nil)
   720  
   721  		// Transaction should be removed from actor state after send
   722  		actor.assertTransactions(rt)
   723  		actor.checkState(rt)
   724  	})
   725  	t.Run("fail approve transaction more than once", func(t *testing.T) {
   726  		const numApprovals = uint64(2)
   727  		rt := builder.Build(t)
   728  
   729  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   730  
   731  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   732  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   733  
   734  		// anne is going to approve it twice and fail, poor anne.
   735  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   736  			_ = actor.approve(rt, txnID, proposalHashData, nil)
   737  		})
   738  		rt.Reset()
   739  
   740  		// Transaction still exists
   741  		actor.assertTransactions(rt, multisig.Transaction{
   742  			To:       chuck,
   743  			Value:    sendValue,
   744  			Method:   builtin.MethodSend,
   745  			Params:   fakeParams,
   746  			Approved: []addr.Address{anne},
   747  		})
   748  		actor.checkState(rt)
   749  	})
   750  
   751  	t.Run("fail approve transaction that does not exist", func(t *testing.T) {
   752  		const dneTxnID = int64(1)
   753  		rt := builder.Build(t)
   754  
   755  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   756  
   757  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   758  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   759  
   760  		// bob is going to approve a transaction that doesn't exist.
   761  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   762  		rt.ExpectAbort(exitcode.ErrNotFound, func() {
   763  			_ = actor.approve(rt, dneTxnID, proposalHashData, nil)
   764  		})
   765  		rt.Reset()
   766  
   767  		// Transaction was not removed from store.
   768  		actor.assertTransactions(rt, multisig.Transaction{
   769  			To:       chuck,
   770  			Value:    sendValue,
   771  			Method:   builtin.MethodSend,
   772  			Params:   fakeParams,
   773  			Approved: []addr.Address{anne},
   774  		})
   775  		actor.checkState(rt)
   776  	})
   777  
   778  	t.Run("fail to approve transaction by non-signer", func(t *testing.T) {
   779  		// non-signer address
   780  		richard := tutil.NewIDAddr(t, 105)
   781  		rt := builder.Build(t)
   782  
   783  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   784  
   785  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   786  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, builtin.MethodSend, fakeParams, nil)
   787  
   788  		// richard is going to approve a transaction they are not a signer for.
   789  		rt.SetCaller(richard, builtin.AccountActorCodeID)
   790  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   791  			_ = actor.approve(rt, txnID, proposalHashData, nil)
   792  		})
   793  		rt.Reset()
   794  
   795  		// Transaction was not removed from store.
   796  		actor.assertTransactions(rt, multisig.Transaction{
   797  			To:       chuck,
   798  			Value:    sendValue,
   799  			Method:   builtin.MethodSend,
   800  			Params:   fakeParams,
   801  			Approved: []addr.Address{anne},
   802  		})
   803  		actor.checkState(rt)
   804  	})
   805  
   806  	t.Run("proposed transaction is approved by proposer if number of approvers has already crossed threshold", func(t *testing.T) {
   807  		rt := builder.Build(t)
   808  		const newThreshold = 1
   809  		signers := []addr.Address{anne, bob}
   810  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   811  
   812  		// anne proposes a transaction
   813  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   814  		proposalHash := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   815  
   816  		// reduce the threshold so the transaction is already approved
   817  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
   818  		actor.changeNumApprovalsThreshold(rt, newThreshold)
   819  
   820  		// even if anne calls for an approval again(duplicate approval), transaction is executed because the threshold has been met.
   821  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   822  		rt.SetBalance(sendValue)
   823  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   824  		actor.approveOK(rt, txnID, proposalHash, nil)
   825  
   826  		// Transaction should be removed from actor state after send
   827  		actor.assertTransactions(rt)
   828  		actor.checkState(rt)
   829  	})
   830  
   831  	t.Run("approve transaction if number of approvers has already crossed threshold even if we attempt a duplicate approval", func(t *testing.T) {
   832  		rt := builder.Build(t)
   833  		const numApprovals = 3
   834  		const newThreshold = 2
   835  		signers := []addr.Address{anne, bob, chuck}
   836  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   837  
   838  		// anne proposes a transaction
   839  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   840  		proposalHash := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   841  
   842  		// bob approves the transaction (number of approvals is now two but threshold is three)
   843  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   844  		actor.approveOK(rt, txnID, proposalHash, nil)
   845  
   846  		// reduce the threshold so the transaction is already approved
   847  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
   848  		actor.changeNumApprovalsThreshold(rt, newThreshold)
   849  
   850  		// even if bob calls for an approval again(duplicate approval), transaction is executed because the threshold has been met.
   851  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   852  		rt.SetBalance(sendValue)
   853  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   854  		actor.approveOK(rt, txnID, proposalHash, nil)
   855  
   856  		// Transaction should be removed from actor state after send
   857  		actor.assertTransactions(rt)
   858  		actor.checkState(rt)
   859  	})
   860  
   861  	t.Run("approve transaction if number of approvers has already crossed threshold and ensure non-signatory cannot approve a transaction", func(t *testing.T) {
   862  		rt := builder.Build(t)
   863  		const newThreshold = 1
   864  		signers := []addr.Address{anne, bob}
   865  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   866  
   867  		// anne proposes a transaction
   868  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   869  		proposalHash := actor.proposeOK(rt, chuck, sendValue, fakeMethod, fakeParams, nil)
   870  
   871  		// reduce the threshold so the transaction is already approved
   872  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
   873  		actor.changeNumApprovalsThreshold(rt, newThreshold)
   874  
   875  		// alice cannot approve the transaction as alice is not a signatory
   876  		alice := tutil.NewIDAddr(t, 104)
   877  		rt.SetCaller(alice, builtin.AccountActorCodeID)
   878  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   879  			_ = actor.approve(rt, txnID, proposalHash, nil)
   880  		})
   881  		rt.Reset()
   882  
   883  		// bob attempts to approve the transaction but it gets approved without
   884  		// processing his approval as it the threshold has been met.
   885  		rt.ExpectSend(chuck, fakeMethod, fakeParams, sendValue, nil, 0)
   886  		rt.SetBalance(sendValue)
   887  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   888  		actor.approveOK(rt, txnID, proposalHash, nil)
   889  
   890  		// Transaction should be removed from actor state after send
   891  		actor.assertTransactions(rt)
   892  		actor.checkState(rt)
   893  	})
   894  }
   895  
   896  func TestCancel(t *testing.T) {
   897  	actor := msActorHarness{multisig.Actor{}, t}
   898  	startEpoch := abi.ChainEpoch(0)
   899  
   900  	richard := tutil.NewIDAddr(t, 104)
   901  	receiver := tutil.NewIDAddr(t, 100)
   902  	anne := tutil.NewIDAddr(t, 101)
   903  	bob := tutil.NewIDAddr(t, 102)
   904  	chuck := tutil.NewIDAddr(t, 103)
   905  
   906  	const noUnlockDuration = abi.ChainEpoch(0)
   907  	const numApprovals = uint64(2)
   908  	const txnID = int64(0)
   909  	const fakeMethod = abi.MethodNum(42)
   910  	var sendValue = abi.NewTokenAmount(10)
   911  	var signers = []addr.Address{anne, bob}
   912  
   913  	builder := mock.NewBuilder(receiver).
   914  		WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID).
   915  		WithHasher(blake2b.Sum256)
   916  
   917  	t.Run("simple propose and cancel", func(t *testing.T) {
   918  		rt := builder.Build(t)
   919  
   920  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   921  
   922  		// anne proposes a transaction
   923  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   924  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
   925  
   926  		// anne cancels their transaction
   927  		rt.SetBalance(sendValue)
   928  		actor.cancel(rt, txnID, proposalHashData)
   929  
   930  		// Transaction should be removed from actor state after cancel
   931  		actor.assertTransactions(rt)
   932  		actor.checkState(rt)
   933  	})
   934  
   935  	t.Run("fail cancel with bad proposal hash", func(t *testing.T) {
   936  		rt := builder.Build(t)
   937  
   938  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   939  
   940  		// anne proposes a transaction
   941  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   942  		actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
   943  
   944  		// anne cancels their transaction
   945  		rt.SetBalance(sendValue)
   946  		rt.ExpectAbort(exitcode.ErrIllegalState, func() {
   947  			proposalHashData := makeProposalHash(t, &multisig.Transaction{
   948  				To:       bob, // mismatched To
   949  				Value:    sendValue,
   950  				Method:   fakeMethod,
   951  				Params:   nil,
   952  				Approved: []addr.Address{chuck},
   953  			})
   954  			actor.cancel(rt, txnID, proposalHashData)
   955  		})
   956  	})
   957  
   958  	t.Run("signer fails to cancel transaction from another signer", func(t *testing.T) {
   959  		rt := builder.Build(t)
   960  
   961  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
   962  
   963  		// anne proposes a transaction
   964  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   965  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
   966  
   967  		// bob (a signer) fails to cancel anne's transaction because bob didn't create it, nice try bob.
   968  		rt.SetCaller(bob, builtin.AccountActorCodeID)
   969  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   970  			actor.cancel(rt, txnID, proposalHashData)
   971  		})
   972  		rt.Reset()
   973  
   974  		// Transaction should remain after invalid cancel
   975  		actor.assertTransactions(rt, multisig.Transaction{
   976  			To:       chuck,
   977  			Value:    sendValue,
   978  			Method:   fakeMethod,
   979  			Params:   nil,
   980  			Approved: []addr.Address{anne},
   981  		})
   982  		actor.checkState(rt)
   983  	})
   984  
   985  	t.Run("fail to cancel transaction when not signer", func(t *testing.T) {
   986  		rt := builder.Build(t)
   987  
   988  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, 0, signers...)
   989  
   990  		// anne proposes a transaction
   991  		rt.SetCaller(anne, builtin.AccountActorCodeID)
   992  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
   993  
   994  		// richard (not a signer) fails to cancel anne's transaction because richard isn't a signer, go away richard.
   995  		rt.SetCaller(richard, builtin.AccountActorCodeID)
   996  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
   997  			actor.cancel(rt, txnID, proposalHashData)
   998  		})
   999  		rt.Reset()
  1000  
  1001  		// Transaction should remain after invalid cancel
  1002  		actor.assertTransactions(rt, multisig.Transaction{
  1003  			To:       chuck,
  1004  			Value:    sendValue,
  1005  			Method:   fakeMethod,
  1006  			Params:   nil,
  1007  			Approved: []addr.Address{anne},
  1008  		})
  1009  		actor.checkState(rt)
  1010  	})
  1011  
  1012  	t.Run("fail to cancel a transaction that does not exist", func(t *testing.T) {
  1013  		rt := builder.Build(t)
  1014  		const dneTxnID = int64(1)
  1015  
  1016  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
  1017  
  1018  		// anne proposes a transaction ID: 0
  1019  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1020  		proposalHashData := actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
  1021  
  1022  		// anne fails to cancel a transaction that does not exists ID: 1 (dneTxnID)
  1023  		rt.ExpectAbort(exitcode.ErrNotFound, func() {
  1024  			actor.cancel(rt, dneTxnID, proposalHashData)
  1025  		})
  1026  		rt.Reset()
  1027  
  1028  		// Transaction should remain after invalid cancel
  1029  		actor.assertTransactions(rt, multisig.Transaction{
  1030  			To:       chuck,
  1031  			Value:    sendValue,
  1032  			Method:   fakeMethod,
  1033  			Params:   nil,
  1034  			Approved: []addr.Address{anne},
  1035  		})
  1036  		actor.checkState(rt)
  1037  	})
  1038  
  1039  	t.Run("subsequent approver replaces removed proposer as owner", func(t *testing.T) {
  1040  		rt := builder.Build(t)
  1041  		const numApprovals = 3
  1042  		signers := []addr.Address{anne, bob, chuck}
  1043  
  1044  		txnId := int64(0)
  1045  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
  1046  
  1047  		// anne proposes a transaction ID: 0
  1048  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1049  		actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
  1050  
  1051  		// bob approves the transaction -> he is the second approver
  1052  		rt.SetCaller(bob, builtin.AccountActorCodeID)
  1053  		actor.approveOK(rt, txnId, nil, nil)
  1054  
  1055  		// remove anne as a signer, now bob is the "proposer"
  1056  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1057  		actor.removeSigner(rt, anne, true)
  1058  
  1059  		// anne fails to cancel a transaction - she is not a signer
  1060  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1061  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
  1062  			actor.cancel(rt, txnID, nil)
  1063  		})
  1064  
  1065  		// even after anne is restored as a signer, she's not the proposer any more
  1066  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1067  		actor.addSigner(rt, anne, true)
  1068  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1069  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
  1070  			actor.cancel(rt, txnID, nil)
  1071  		})
  1072  
  1073  		// Transaction should remain after invalid cancel
  1074  		actor.assertTransactions(rt, multisig.Transaction{
  1075  			To:       chuck,
  1076  			Value:    sendValue,
  1077  			Method:   fakeMethod,
  1078  			Params:   nil,
  1079  			Approved: []addr.Address{bob}, // Anne's approval is gone
  1080  		})
  1081  
  1082  		// bob can cancel the transaction
  1083  		rt.SetCaller(bob, builtin.AccountActorCodeID)
  1084  		actor.cancel(rt, txnID, nil)
  1085  		actor.checkState(rt)
  1086  	})
  1087  }
  1088  
  1089  type addSignerTestCase struct {
  1090  	desc string
  1091  
  1092  	idAddrsMapping   map[addr.Address]addr.Address
  1093  	initialSigners   []addr.Address
  1094  	initialApprovals uint64
  1095  
  1096  	addSigner addr.Address
  1097  	increase  bool
  1098  
  1099  	expectSigners   []addr.Address
  1100  	expectApprovals uint64
  1101  	code            exitcode.ExitCode
  1102  }
  1103  
  1104  func TestAddSigner(t *testing.T) {
  1105  	actor := msActorHarness{multisig.Actor{}, t}
  1106  	startEpoch := abi.ChainEpoch(0)
  1107  
  1108  	multisigWalletAdd := tutil.NewIDAddr(t, 100)
  1109  	anne := tutil.NewIDAddr(t, 101)
  1110  	bob := tutil.NewIDAddr(t, 102)
  1111  	chuck := tutil.NewIDAddr(t, 103)
  1112  	chuckNonId := tutil.NewBLSAddr(t, 1)
  1113  
  1114  	const noUnlockDuration = abi.ChainEpoch(0)
  1115  
  1116  	testCases := []addSignerTestCase{
  1117  		{
  1118  			desc: "happy path add signer",
  1119  
  1120  			initialSigners:   []addr.Address{anne, bob},
  1121  			initialApprovals: uint64(2),
  1122  
  1123  			addSigner: chuck,
  1124  			increase:  false,
  1125  
  1126  			expectSigners:   []addr.Address{anne, bob, chuck},
  1127  			expectApprovals: uint64(2),
  1128  			code:            exitcode.Ok,
  1129  		},
  1130  		{
  1131  			desc: "add signer and increase threshold",
  1132  
  1133  			initialSigners:   []addr.Address{anne, bob},
  1134  			initialApprovals: uint64(2),
  1135  
  1136  			addSigner: chuck,
  1137  			increase:  true,
  1138  
  1139  			expectSigners:   []addr.Address{anne, bob, chuck},
  1140  			expectApprovals: uint64(3),
  1141  			code:            exitcode.Ok,
  1142  		},
  1143  		{
  1144  			desc: "fail to add signer than already exists",
  1145  
  1146  			initialSigners:   []addr.Address{anne, bob, chuck},
  1147  			initialApprovals: uint64(3),
  1148  
  1149  			addSigner: chuck,
  1150  			increase:  false,
  1151  
  1152  			expectSigners:   []addr.Address{anne, bob, chuck},
  1153  			expectApprovals: uint64(3),
  1154  			code:            exitcode.ErrForbidden,
  1155  		},
  1156  		{
  1157  			desc: "fail to add signer with ID address that already exists(even though we ONLY have the non ID address as an approver)",
  1158  
  1159  			idAddrsMapping:   map[addr.Address]addr.Address{chuckNonId: chuck},
  1160  			initialSigners:   []addr.Address{anne, bob, chuckNonId},
  1161  			initialApprovals: uint64(3),
  1162  
  1163  			addSigner: chuck,
  1164  			increase:  false,
  1165  
  1166  			expectSigners:   []addr.Address{anne, bob, chuck},
  1167  			expectApprovals: uint64(3),
  1168  			code:            exitcode.ErrForbidden,
  1169  		},
  1170  		{
  1171  			desc:             "fail to add signer with non-ID address that already exists(even though we ONLY have the ID address as an approver)",
  1172  			idAddrsMapping:   map[addr.Address]addr.Address{chuckNonId: chuck},
  1173  			initialSigners:   []addr.Address{anne, bob, chuck},
  1174  			initialApprovals: uint64(3),
  1175  
  1176  			addSigner: chuckNonId,
  1177  			increase:  false,
  1178  
  1179  			expectSigners:   []addr.Address{anne, bob, chuck},
  1180  			expectApprovals: uint64(3),
  1181  			code:            exitcode.ErrForbidden,
  1182  		},
  1183  	}
  1184  
  1185  	for _, tc := range testCases {
  1186  		t.Run(tc.desc, func(t *testing.T) {
  1187  			builder := mock.NewBuilder(multisigWalletAdd).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
  1188  			rt := builder.Build(t)
  1189  			for src, target := range tc.idAddrsMapping {
  1190  				rt.AddIDAddress(src, target)
  1191  			}
  1192  
  1193  			actor.constructAndVerify(rt, tc.initialApprovals, noUnlockDuration, startEpoch, tc.initialSigners...)
  1194  
  1195  			rt.SetCaller(multisigWalletAdd, builtin.MultisigActorCodeID)
  1196  			if tc.code != exitcode.Ok {
  1197  				rt.ExpectAbort(tc.code, func() {
  1198  					actor.addSigner(rt, tc.addSigner, tc.increase)
  1199  				})
  1200  			} else {
  1201  				actor.addSigner(rt, tc.addSigner, tc.increase)
  1202  				var st multisig.State
  1203  				rt.GetState(&st)
  1204  				assert.Equal(t, tc.expectSigners, st.Signers)
  1205  				assert.Equal(t, tc.expectApprovals, st.NumApprovalsThreshold)
  1206  				actor.checkState(rt)
  1207  			}
  1208  			rt.Verify()
  1209  		})
  1210  	}
  1211  }
  1212  
  1213  type removeSignerTestCase struct {
  1214  	desc string
  1215  
  1216  	initialSigners   []addr.Address
  1217  	initialApprovals uint64
  1218  
  1219  	removeSigner addr.Address
  1220  	decrease     bool
  1221  
  1222  	expectSigners   []addr.Address
  1223  	expectApprovals uint64
  1224  	code            exitcode.ExitCode
  1225  }
  1226  
  1227  func TestRemoveSigner(t *testing.T) {
  1228  	startEpoch := abi.ChainEpoch(0)
  1229  	receiver := tutil.NewIDAddr(t, 100)
  1230  	anne := tutil.NewIDAddr(t, 101)
  1231  	anneNonID := tutil.NewBLSAddr(t, 1)
  1232  
  1233  	bob := tutil.NewIDAddr(t, 102)
  1234  	chuck := tutil.NewIDAddr(t, 103)
  1235  	richard := tutil.NewIDAddr(t, 104)
  1236  
  1237  	const noUnlockDuration = abi.ChainEpoch(0)
  1238  
  1239  	actor := msActorHarness{multisig.Actor{}, t}
  1240  	builder := mock.NewBuilder(receiver).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
  1241  
  1242  	testCases := []removeSignerTestCase{
  1243  		{
  1244  			desc: "happy path remove signer",
  1245  
  1246  			initialSigners:   []addr.Address{anne, bob, chuck},
  1247  			initialApprovals: uint64(2),
  1248  
  1249  			removeSigner: chuck,
  1250  			decrease:     false,
  1251  
  1252  			expectSigners:   []addr.Address{anne, bob},
  1253  			expectApprovals: uint64(2),
  1254  			code:            exitcode.Ok,
  1255  		},
  1256  		{
  1257  			desc: "remove signer and decrease threshold",
  1258  
  1259  			initialSigners:   []addr.Address{anne, bob, chuck},
  1260  			initialApprovals: uint64(2),
  1261  
  1262  			removeSigner: chuck,
  1263  			decrease:     true,
  1264  
  1265  			expectSigners:   []addr.Address{anne, bob},
  1266  			expectApprovals: uint64(1),
  1267  			code:            exitcode.Ok,
  1268  		},
  1269  		{
  1270  			desc:             "remove signer when multi-sig is created with an ID address and then removed using it's non-ID address",
  1271  			initialSigners:   []addr.Address{anne, bob, chuck},
  1272  			initialApprovals: uint64(2),
  1273  
  1274  			removeSigner: anneNonID,
  1275  			decrease:     true,
  1276  
  1277  			expectSigners:   []addr.Address{bob, chuck},
  1278  			expectApprovals: uint64(1),
  1279  			code:            exitcode.Ok,
  1280  		},
  1281  		{
  1282  			desc:             "remove signer when multi-sig is created with a non-ID address and then removed using it's ID address",
  1283  			initialSigners:   []addr.Address{anneNonID, bob, chuck},
  1284  			initialApprovals: uint64(2),
  1285  
  1286  			removeSigner: anne,
  1287  			decrease:     true,
  1288  
  1289  			expectSigners:   []addr.Address{bob, chuck},
  1290  			expectApprovals: uint64(1),
  1291  			code:            exitcode.Ok,
  1292  		},
  1293  		{
  1294  			desc:             "remove signer when multi-sig is created with a non-ID address and then removed using it's non-ID address",
  1295  			initialSigners:   []addr.Address{anneNonID, bob, chuck},
  1296  			initialApprovals: uint64(2),
  1297  
  1298  			removeSigner: anneNonID,
  1299  			decrease:     true,
  1300  
  1301  			expectSigners:   []addr.Address{bob, chuck},
  1302  			expectApprovals: uint64(1),
  1303  			code:            exitcode.Ok,
  1304  		},
  1305  		{
  1306  			desc:             "remove signer when multi-sig is created with a ID address and then removed using it's ID address",
  1307  			initialSigners:   []addr.Address{anne, bob, chuck},
  1308  			initialApprovals: uint64(2),
  1309  
  1310  			removeSigner: anne,
  1311  			decrease:     true,
  1312  
  1313  			expectSigners:   []addr.Address{bob, chuck},
  1314  			expectApprovals: uint64(1),
  1315  			code:            exitcode.Ok,
  1316  		},
  1317  		{
  1318  			desc: "fail remove signer if decrease set to false and number of signers below threshold",
  1319  
  1320  			initialSigners:   []addr.Address{anne, bob, chuck},
  1321  			initialApprovals: uint64(3),
  1322  
  1323  			removeSigner: chuck,
  1324  			decrease:     false,
  1325  
  1326  			expectSigners:   []addr.Address{anne, bob},
  1327  			expectApprovals: uint64(2),
  1328  			code:            exitcode.ErrIllegalArgument,
  1329  		},
  1330  		{
  1331  			desc: "remove signer from single singer list",
  1332  
  1333  			initialSigners:   []addr.Address{anne},
  1334  			initialApprovals: uint64(1),
  1335  
  1336  			removeSigner: anne,
  1337  			decrease:     false,
  1338  
  1339  			expectSigners:   nil,
  1340  			expectApprovals: uint64(1),
  1341  			code:            exitcode.ErrForbidden,
  1342  		},
  1343  		{
  1344  			desc: "fail to remove non-signer",
  1345  
  1346  			initialSigners:   []addr.Address{anne, bob, chuck},
  1347  			initialApprovals: uint64(2),
  1348  
  1349  			removeSigner: richard,
  1350  			decrease:     false,
  1351  
  1352  			expectSigners:   []addr.Address{anne, bob, chuck},
  1353  			expectApprovals: uint64(2),
  1354  			code:            exitcode.ErrForbidden,
  1355  		},
  1356  		{
  1357  			desc: "fail to remove a signer and decrease approvals below 1",
  1358  
  1359  			initialSigners:   []addr.Address{anne, bob, chuck},
  1360  			initialApprovals: uint64(1),
  1361  
  1362  			removeSigner: anne,
  1363  			decrease:     true,
  1364  
  1365  			expectSigners:   []addr.Address{anne, bob, chuck},
  1366  			expectApprovals: uint64(1),
  1367  			code:            exitcode.ErrIllegalArgument,
  1368  		},
  1369  	}
  1370  
  1371  	for _, tc := range testCases {
  1372  		t.Run(tc.desc, func(t *testing.T) {
  1373  			rt := builder.Build(t)
  1374  			rt.AddIDAddress(anneNonID, anne)
  1375  
  1376  			actor.constructAndVerify(rt, tc.initialApprovals, noUnlockDuration, startEpoch, tc.initialSigners...)
  1377  
  1378  			rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1379  			if tc.code != exitcode.Ok {
  1380  				rt.ExpectAbort(tc.code, func() {
  1381  					actor.removeSigner(rt, tc.removeSigner, tc.decrease)
  1382  				})
  1383  			} else {
  1384  				actor.removeSigner(rt, tc.removeSigner, tc.decrease)
  1385  				var st multisig.State
  1386  				rt.GetState(&st)
  1387  				assert.Equal(t, tc.expectSigners, st.Signers)
  1388  				assert.Equal(t, tc.expectApprovals, st.NumApprovalsThreshold)
  1389  				actor.checkState(rt)
  1390  			}
  1391  			rt.Verify()
  1392  		})
  1393  	}
  1394  
  1395  	t.Run("remove signer removes approvals", func(t *testing.T) {
  1396  		rt := builder.Build(t)
  1397  		signers := []addr.Address{anne, bob, chuck}
  1398  
  1399  		actor.constructAndVerify(rt, 3, noUnlockDuration, startEpoch, signers...)
  1400  
  1401  		// Anne proposes a tx
  1402  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1403  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1404  		// Bob approves
  1405  		rt.SetCaller(bob, builtin.AccountActorCodeID)
  1406  		actor.approveOK(rt, 0, nil, nil)
  1407  
  1408  		// Bob proposes a tx
  1409  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1410  		// Anne approves
  1411  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1412  		actor.approveOK(rt, 1, nil, nil)
  1413  
  1414  		// Anne is removed, threshold dropped to 2 of 2
  1415  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1416  		actor.removeSigner(rt, anne, true)
  1417  
  1418  		// Anne's approval is removed from each transaction.
  1419  		actor.assertTransactions(rt, multisig.Transaction{
  1420  			To:       chuck,
  1421  			Value:    big.Zero(),
  1422  			Method:   builtin.MethodSend,
  1423  			Params:   nil,
  1424  			Approved: []addr.Address{bob},
  1425  		}, multisig.Transaction{
  1426  			To:       chuck,
  1427  			Value:    big.Zero(),
  1428  			Method:   builtin.MethodSend,
  1429  			Params:   nil,
  1430  			Approved: []addr.Address{bob},
  1431  		})
  1432  		actor.checkState(rt)
  1433  	})
  1434  
  1435  	t.Run("remove signer deletes solo proposals", func(t *testing.T) {
  1436  		rt := builder.Build(t)
  1437  		rt.AddIDAddress(anneNonID, anne)
  1438  		signers := []addr.Address{anne, bob, chuck}
  1439  
  1440  		actor.constructAndVerify(rt, 2, noUnlockDuration, startEpoch, signers...)
  1441  
  1442  		// Anne proposes a tx
  1443  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1444  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1445  
  1446  		// Anne is removed
  1447  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1448  		actor.removeSigner(rt, anneNonID, false) // Remove via non-ID address.
  1449  
  1450  		// Transaction is gone.
  1451  		actor.assertTransactions(rt)
  1452  		actor.checkState(rt)
  1453  	})
  1454  }
  1455  
  1456  type swapTestCase struct {
  1457  	initialSigner []addr.Address
  1458  	desc          string
  1459  	to            addr.Address
  1460  	from          addr.Address
  1461  	expect        []addr.Address
  1462  	code          exitcode.ExitCode
  1463  }
  1464  
  1465  func TestSwapSigners(t *testing.T) {
  1466  	startEpoch := abi.ChainEpoch(0)
  1467  
  1468  	receiver := tutil.NewIDAddr(t, 100)
  1469  	anne := tutil.NewIDAddr(t, 101)
  1470  
  1471  	bob := tutil.NewIDAddr(t, 102)
  1472  	bobNonId := tutil.NewBLSAddr(t, 1)
  1473  
  1474  	chuck := tutil.NewIDAddr(t, 103)
  1475  	darlene := tutil.NewIDAddr(t, 104)
  1476  
  1477  	const noUnlockDuration = abi.ChainEpoch(0)
  1478  	const numApprovals = uint64(1)
  1479  
  1480  	actor := msActorHarness{multisig.Actor{}, t}
  1481  	builder := mock.NewBuilder(receiver).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
  1482  
  1483  	testCases := []swapTestCase{
  1484  		{
  1485  			desc:          "happy path signer swap",
  1486  			initialSigner: []addr.Address{anne, bob},
  1487  			to:            chuck,
  1488  			from:          bob,
  1489  			expect:        []addr.Address{anne, chuck},
  1490  			code:          exitcode.Ok,
  1491  		},
  1492  		{
  1493  			desc:          "swap signer when multi-sig is created with it's ID address but we ask for a swap with it's non-ID address",
  1494  			initialSigner: []addr.Address{anne, bob},
  1495  			to:            chuck,
  1496  			from:          bobNonId,
  1497  			expect:        []addr.Address{anne, chuck},
  1498  			code:          exitcode.Ok,
  1499  		},
  1500  		{
  1501  			desc:          "swap signer when multi-sig is created with it's non-ID address but we ask for a swap with it's ID address",
  1502  			initialSigner: []addr.Address{anne, bobNonId},
  1503  			to:            chuck,
  1504  			from:          bob,
  1505  			expect:        []addr.Address{anne, chuck},
  1506  			code:          exitcode.Ok,
  1507  		},
  1508  		{
  1509  			desc:          "swap signer when multi-sig is created with it's non-ID address and we ask for a swap with it's non-ID address",
  1510  			initialSigner: []addr.Address{anne, bobNonId},
  1511  			to:            chuck,
  1512  			from:          bobNonId,
  1513  			expect:        []addr.Address{anne, chuck},
  1514  			code:          exitcode.Ok,
  1515  		},
  1516  		{
  1517  			desc:          "swap signer when multi-sig is created with it's ID address and we ask for a swap with it's ID address",
  1518  			initialSigner: []addr.Address{anne, bob},
  1519  			to:            chuck,
  1520  			from:          bob,
  1521  			expect:        []addr.Address{anne, chuck},
  1522  			code:          exitcode.Ok,
  1523  		},
  1524  		{
  1525  			desc:          "fail to swap when from signer not found",
  1526  			initialSigner: []addr.Address{anne, bob},
  1527  			to:            chuck,
  1528  			from:          darlene,
  1529  			expect:        []addr.Address{anne, chuck},
  1530  			code:          exitcode.ErrForbidden,
  1531  		},
  1532  		{
  1533  			desc:          "fail to swap when to signer already present",
  1534  			initialSigner: []addr.Address{anne, bob},
  1535  			to:            bob,
  1536  			from:          anne,
  1537  			expect:        []addr.Address{anne, chuck},
  1538  			code:          exitcode.ErrIllegalArgument,
  1539  		},
  1540  		{
  1541  			desc:          "fail to swap when to signer ID address already present(even though we have the non-ID address)",
  1542  			initialSigner: []addr.Address{anne, bobNonId},
  1543  			to:            bob,
  1544  			from:          anne,
  1545  			expect:        []addr.Address{anne, chuck},
  1546  			code:          exitcode.ErrIllegalArgument,
  1547  		},
  1548  		{
  1549  			desc:          "fail to swap when to signer non-ID address already present(even though we have the ID address)",
  1550  			initialSigner: []addr.Address{anne, bob},
  1551  			to:            bobNonId,
  1552  			from:          anne,
  1553  			expect:        []addr.Address{anne, chuck},
  1554  			code:          exitcode.ErrIllegalArgument,
  1555  		},
  1556  	}
  1557  
  1558  	for _, tc := range testCases {
  1559  		t.Run(tc.desc, func(t *testing.T) {
  1560  			rt := builder.Build(t)
  1561  			rt.AddIDAddress(bobNonId, bob)
  1562  
  1563  			actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, tc.initialSigner...)
  1564  
  1565  			rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1566  			if tc.code != exitcode.Ok {
  1567  				rt.ExpectAbort(tc.code, func() {
  1568  					actor.swapSigners(rt, tc.from, tc.to)
  1569  				})
  1570  			} else {
  1571  				actor.swapSigners(rt, tc.from, tc.to)
  1572  				var st multisig.State
  1573  				rt.GetState(&st)
  1574  				assert.Equal(t, tc.expect, st.Signers)
  1575  				actor.checkState(rt)
  1576  			}
  1577  			rt.Verify()
  1578  		})
  1579  	}
  1580  
  1581  	t.Run("swap signer removes approvals", func(t *testing.T) {
  1582  		rt := builder.Build(t)
  1583  		signers := []addr.Address{anne, bob, chuck}
  1584  
  1585  		actor.constructAndVerify(rt, 3, noUnlockDuration, startEpoch, signers...)
  1586  
  1587  		// Anne proposes a tx
  1588  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1589  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1590  		// Bob approves
  1591  		rt.SetCaller(bob, builtin.AccountActorCodeID)
  1592  		actor.approveOK(rt, 0, nil, nil)
  1593  
  1594  		// Bob proposes a tx
  1595  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1596  		// Anne approves
  1597  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1598  		actor.approveOK(rt, 1, nil, nil)
  1599  
  1600  		// Anne is swapped for darlene
  1601  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1602  		actor.swapSigners(rt, anne, darlene)
  1603  
  1604  		// Anne's approval is removed from each transaction.
  1605  		actor.assertTransactions(rt, multisig.Transaction{
  1606  			To:       chuck,
  1607  			Value:    big.Zero(),
  1608  			Method:   builtin.MethodSend,
  1609  			Params:   nil,
  1610  			Approved: []addr.Address{bob},
  1611  		}, multisig.Transaction{
  1612  			To:       chuck,
  1613  			Value:    big.Zero(),
  1614  			Method:   builtin.MethodSend,
  1615  			Params:   nil,
  1616  			Approved: []addr.Address{bob},
  1617  		})
  1618  		actor.checkState(rt)
  1619  	})
  1620  
  1621  	t.Run("swap signer deletes solo proposals", func(t *testing.T) {
  1622  		rt := builder.Build(t)
  1623  		signers := []addr.Address{anne, bob}
  1624  
  1625  		actor.constructAndVerify(rt, 2, noUnlockDuration, startEpoch, signers...)
  1626  
  1627  		// Anne proposes a tx
  1628  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1629  		actor.proposeOK(rt, chuck, big.Zero(), builtin.MethodSend, nil, nil)
  1630  
  1631  		// Anne is swapped
  1632  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1633  		actor.swapSigners(rt, anne, chuck)
  1634  
  1635  		// Transaction is gone.
  1636  		actor.assertTransactions(rt)
  1637  		actor.checkState(rt)
  1638  	})
  1639  }
  1640  
  1641  type thresholdTestCase struct {
  1642  	desc             string
  1643  	initialThreshold uint64
  1644  	setThreshold     uint64
  1645  	code             exitcode.ExitCode
  1646  }
  1647  
  1648  func TestChangeThreshold(t *testing.T) {
  1649  	actor := msActorHarness{multisig.Actor{}, t}
  1650  	startEpoch := abi.ChainEpoch(0)
  1651  
  1652  	multisigWalletAdd := tutil.NewIDAddr(t, 100)
  1653  	anne := tutil.NewIDAddr(t, 101)
  1654  	bob := tutil.NewIDAddr(t, 102)
  1655  	chuck := tutil.NewIDAddr(t, 103)
  1656  
  1657  	const noUnlockDuration = abi.ChainEpoch(0)
  1658  	var initialSigner = []addr.Address{anne, bob, chuck}
  1659  
  1660  	testCases := []thresholdTestCase{
  1661  		{
  1662  			desc:             "happy path decrease threshold",
  1663  			initialThreshold: 2,
  1664  			setThreshold:     1,
  1665  			code:             exitcode.Ok,
  1666  		},
  1667  		{
  1668  			desc:             "happy path simple increase threshold",
  1669  			initialThreshold: 2,
  1670  			setThreshold:     3,
  1671  			code:             exitcode.Ok,
  1672  		},
  1673  		{
  1674  			desc:             "fail to set threshold to zero",
  1675  			initialThreshold: 2,
  1676  			setThreshold:     0,
  1677  			code:             exitcode.ErrIllegalArgument,
  1678  		},
  1679  		{
  1680  			desc:             "fail to set threshold above number of signers",
  1681  			initialThreshold: 2,
  1682  			setThreshold:     uint64(len(initialSigner) + 1),
  1683  			code:             exitcode.ErrIllegalArgument,
  1684  		},
  1685  	}
  1686  
  1687  	builder := mock.NewBuilder(multisigWalletAdd).WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID)
  1688  	for _, tc := range testCases {
  1689  		t.Run(tc.desc, func(t *testing.T) {
  1690  			rt := builder.Build(t)
  1691  
  1692  			actor.constructAndVerify(rt, tc.initialThreshold, noUnlockDuration, startEpoch, initialSigner...)
  1693  
  1694  			rt.SetCaller(multisigWalletAdd, builtin.MultisigActorCodeID)
  1695  			if tc.code != exitcode.Ok {
  1696  				rt.ExpectAbort(tc.code, func() {
  1697  					actor.changeNumApprovalsThreshold(rt, tc.setThreshold)
  1698  				})
  1699  			} else {
  1700  				actor.changeNumApprovalsThreshold(rt, tc.setThreshold)
  1701  				var st multisig.State
  1702  				rt.GetState(&st)
  1703  				assert.Equal(t, tc.setThreshold, st.NumApprovalsThreshold)
  1704  				actor.checkState(rt)
  1705  			}
  1706  			rt.Verify()
  1707  		})
  1708  	}
  1709  
  1710  	t.Run("transaction can be re-approved and executed after threshold lowered", func(t *testing.T) {
  1711  		fakeMethod := abi.MethodNum(42)
  1712  		numApprovals := uint64(2)
  1713  
  1714  		var sendValue = abi.NewTokenAmount(10)
  1715  		receiver := tutil.NewIDAddr(t, 100)
  1716  		rt := builder.Build(t)
  1717  		signers := []addr.Address{anne, bob, chuck}
  1718  
  1719  		actor.constructAndVerify(rt, numApprovals, noUnlockDuration, startEpoch, signers...)
  1720  
  1721  		// anne proposes a transaction ID: 0
  1722  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1723  		actor.proposeOK(rt, chuck, sendValue, fakeMethod, nil, nil)
  1724  
  1725  		// lower approver threshold. transaction is technically approved, but will not be executed yet.
  1726  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1727  		actor.changeNumApprovalsThreshold(rt, 1)
  1728  
  1729  		// anne may re-approve causing transaction to be executed
  1730  		rt.ExpectSend(chuck, fakeMethod, nil, sendValue, nil, 0)
  1731  		rt.SetBalance(sendValue)
  1732  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1733  		actor.approveOK(rt, 0, nil, nil)
  1734  		actor.checkState(rt)
  1735  	})
  1736  }
  1737  
  1738  func TestLockBalance(t *testing.T) {
  1739  	actor := msActorHarness{multisig.Actor{}, t}
  1740  	receiver := tutil.NewIDAddr(t, 100)
  1741  	anne := tutil.NewIDAddr(t, 101)
  1742  	bob := tutil.NewIDAddr(t, 102)
  1743  
  1744  	builder := mock.NewBuilder(receiver).
  1745  		WithCaller(builtin.InitActorAddr, builtin.InitActorCodeID).
  1746  		WithEpoch(0).
  1747  		WithHasher(blake2b.Sum256)
  1748  
  1749  	t.Run("retroactive vesting", func(t *testing.T) {
  1750  		rt := builder.Build(t)
  1751  
  1752  		// Create empty multisig
  1753  		rt.SetEpoch(100)
  1754  		actor.constructAndVerify(rt, 1, 0, 0, anne)
  1755  
  1756  		// Some time later, initialize vesting
  1757  		rt.SetEpoch(200)
  1758  		vestStart := abi.ChainEpoch(0)
  1759  		lockAmount := abi.NewTokenAmount(100_000)
  1760  		vestDuration := abi.ChainEpoch(1000)
  1761  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1762  		actor.lockBalance(rt, vestStart, vestDuration, lockAmount)
  1763  
  1764  		rt.SetEpoch(300)
  1765  		vested := abi.NewTokenAmount(30_000) // Since vestStart
  1766  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1767  
  1768  		// Fail to spend balance the multisig doesn't have
  1769  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
  1770  			_ = actor.propose(rt, bob, vested, builtin.MethodSend, nil, nil)
  1771  		})
  1772  		rt.Reset()
  1773  
  1774  		// Fail to spend more than the vested amount
  1775  		rt.SetBalance(lockAmount)
  1776  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
  1777  			_ = actor.propose(rt, bob, big.Add(vested, big.NewInt(1)), builtin.MethodSend, nil, nil)
  1778  		})
  1779  		rt.Reset()
  1780  
  1781  		// Can fully spend the vested amount
  1782  		rt.ExpectSend(bob, builtin.MethodSend, nil, vested, nil, exitcode.Ok)
  1783  		actor.proposeOK(rt, bob, vested, builtin.MethodSend, nil, nil)
  1784  
  1785  		// Can't spend more
  1786  		rt.SetBalance(big.Sub(lockAmount, vested))
  1787  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
  1788  			_ = actor.propose(rt, bob, abi.NewTokenAmount(1), builtin.MethodSend, nil, nil)
  1789  		})
  1790  		rt.Reset()
  1791  
  1792  		// Later, can spend the rest
  1793  		rt.SetEpoch(vestStart + vestDuration)
  1794  		rested := big.NewInt(70_000)
  1795  		rt.ExpectSend(bob, builtin.MethodSend, nil, rested, nil, exitcode.Ok)
  1796  		actor.proposeOK(rt, bob, rested, builtin.MethodSend, nil, nil)
  1797  		actor.checkState(rt)
  1798  	})
  1799  
  1800  	t.Run("prospective vesting", func(t *testing.T) {
  1801  		rt := builder.Build(t)
  1802  
  1803  		// Create empty multisig
  1804  		rt.SetEpoch(100)
  1805  		actor.constructAndVerify(rt, 1, 0, 0, anne)
  1806  
  1807  		// Some time later, initialize vesting
  1808  		rt.SetEpoch(200)
  1809  		vestStart := abi.ChainEpoch(1000)
  1810  		lockAmount := abi.NewTokenAmount(100_000)
  1811  		vestDuration := abi.ChainEpoch(1000)
  1812  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1813  		actor.lockBalance(rt, vestStart, vestDuration, lockAmount)
  1814  
  1815  		rt.SetEpoch(300)
  1816  		rt.SetCaller(anne, builtin.AccountActorCodeID)
  1817  
  1818  		// Oversupply the wallet, allow spending the oversupply.
  1819  		rt.SetBalance(big.Add(lockAmount, abi.NewTokenAmount(1)))
  1820  		rt.ExpectSend(bob, builtin.MethodSend, nil, abi.NewTokenAmount(1), nil, exitcode.Ok)
  1821  		actor.proposeOK(rt, bob, abi.NewTokenAmount(1), builtin.MethodSend, nil, nil)
  1822  
  1823  		// Fail to spend locked funds before vesting starts
  1824  		rt.SetBalance(lockAmount)
  1825  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
  1826  			_ = actor.propose(rt, bob, abi.NewTokenAmount(1), builtin.MethodSend, nil, nil)
  1827  		})
  1828  		rt.Reset()
  1829  
  1830  		// Can spend partially vested amount
  1831  		rt.SetEpoch(vestStart + 200)
  1832  		vested := abi.NewTokenAmount(20_000)
  1833  		rt.ExpectSend(bob, builtin.MethodSend, nil, vested, nil, exitcode.Ok)
  1834  		actor.proposeOK(rt, bob, vested, builtin.MethodSend, nil, nil)
  1835  
  1836  		// Can't spend more
  1837  		rt.SetBalance(big.Sub(lockAmount, vested))
  1838  		rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() {
  1839  			_ = actor.propose(rt, bob, abi.NewTokenAmount(1), builtin.MethodSend, nil, nil)
  1840  		})
  1841  		rt.Reset()
  1842  
  1843  		// Later, can spend the rest
  1844  		rt.SetEpoch(vestStart + vestDuration)
  1845  		rested := big.NewInt(80_000)
  1846  		rt.ExpectSend(bob, builtin.MethodSend, nil, rested, nil, exitcode.Ok)
  1847  		actor.proposeOK(rt, bob, rested, builtin.MethodSend, nil, nil)
  1848  		actor.checkState(rt)
  1849  	})
  1850  
  1851  	t.Run("can't alter vesting", func(t *testing.T) {
  1852  		rt := builder.Build(t)
  1853  
  1854  		// Create empty multisig
  1855  		rt.SetEpoch(100)
  1856  		actor.constructAndVerify(rt, 1, 0, 0, anne)
  1857  
  1858  		// Initialize vesting from zero
  1859  		vestStart := abi.ChainEpoch(0)
  1860  		lockAmount := abi.NewTokenAmount(100_000)
  1861  		vestDuration := abi.ChainEpoch(1000)
  1862  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1863  		actor.lockBalance(rt, vestStart, vestDuration, lockAmount)
  1864  
  1865  		// Can't change vest start
  1866  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
  1867  			actor.lockBalance(rt, vestStart-1, vestDuration, lockAmount)
  1868  		})
  1869  		rt.Reset()
  1870  
  1871  		// Can't change lock duration
  1872  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
  1873  			actor.lockBalance(rt, vestStart, vestDuration-1, lockAmount)
  1874  		})
  1875  		rt.Reset()
  1876  
  1877  		// Can't change locked amount
  1878  		rt.ExpectAbort(exitcode.ErrForbidden, func() {
  1879  			actor.lockBalance(rt, vestStart, vestDuration, big.Sub(lockAmount, big.NewInt(1)))
  1880  		})
  1881  		rt.Reset()
  1882  	})
  1883  
  1884  	t.Run("can't alter vesting from construction", func(t *testing.T) {
  1885  		rt := builder.Build(t)
  1886  
  1887  		// Create empty multisig with vesting
  1888  		startEpoch := abi.ChainEpoch(100)
  1889  		unlockDuration := abi.ChainEpoch(1000)
  1890  		actor.constructAndVerify(rt, 1, unlockDuration, startEpoch, anne)
  1891  
  1892  		// Can't change vest start
  1893  		rt.ExpectAbort(exitcode.SysErrForbidden, func() {
  1894  			actor.lockBalance(rt, startEpoch-1, abi.ChainEpoch(unlockDuration), big.Zero())
  1895  		})
  1896  		rt.Reset()
  1897  	})
  1898  
  1899  	t.Run("checks preconditions", func(t *testing.T) {
  1900  		rt := builder.Build(t)
  1901  
  1902  		actor.constructAndVerify(rt, 1, 0, 0, anne)
  1903  		vestStart := abi.ChainEpoch(0)
  1904  		lockAmount := abi.NewTokenAmount(100_000)
  1905  		vestDuration := abi.ChainEpoch(1000)
  1906  		rt.SetCaller(receiver, builtin.MultisigActorCodeID)
  1907  
  1908  		// Disallow negative duration (though negative start epoch is allowed).
  1909  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
  1910  			actor.lockBalance(rt, vestStart, abi.ChainEpoch(-1), lockAmount)
  1911  		})
  1912  
  1913  		// After version 7, disallow negative amount.
  1914  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
  1915  			actor.lockBalance(rt, vestStart, vestDuration, abi.NewTokenAmount(-1))
  1916  		})
  1917  	})
  1918  }
  1919  
  1920  //
  1921  // Helper methods for calling multisig actor methods
  1922  //
  1923  
  1924  type msActorHarness struct {
  1925  	a multisig.Actor
  1926  	t testing.TB
  1927  }
  1928  
  1929  func (h *msActorHarness) constructAndVerify(rt *mock.Runtime, numApprovalsThresh uint64, unlockDuration abi.ChainEpoch, startEpoch abi.ChainEpoch, signers ...addr.Address) {
  1930  	constructParams := multisig.ConstructorParams{
  1931  		Signers:               signers,
  1932  		NumApprovalsThreshold: numApprovalsThresh,
  1933  		UnlockDuration:        unlockDuration,
  1934  		StartEpoch:            startEpoch,
  1935  	}
  1936  
  1937  	rt.ExpectValidateCallerAddr(builtin.InitActorAddr)
  1938  	ret := rt.Call(h.a.Constructor, &constructParams)
  1939  	assert.Nil(h.t, ret)
  1940  	rt.Verify()
  1941  }
  1942  
  1943  func (h *msActorHarness) propose(rt *mock.Runtime, to addr.Address, value abi.TokenAmount, method abi.MethodNum, params []byte, out cbor.Unmarshaler) exitcode.ExitCode {
  1944  	rt.ExpectValidateCallerType(builtin.AccountActorCodeID, builtin.MultisigActorCodeID)
  1945  	proposeParams := &multisig.ProposeParams{
  1946  		To:     to,
  1947  		Value:  value,
  1948  		Method: method,
  1949  		Params: params,
  1950  	}
  1951  	ret := rt.Call(h.a.Propose, proposeParams)
  1952  	rt.Verify()
  1953  
  1954  	proposeReturn, ok := ret.(*multisig.ProposeReturn)
  1955  	if !ok {
  1956  		h.t.Fatalf("unexpected type returned from call to Propose")
  1957  	}
  1958  	// if the transaction was applied and a return value is expected deserialize it to the out parameter
  1959  	if proposeReturn.Applied {
  1960  		if out != nil {
  1961  			require.NoError(h.t, out.UnmarshalCBOR(bytes.NewReader(proposeReturn.Ret)))
  1962  		}
  1963  	}
  1964  	return proposeReturn.Code
  1965  }
  1966  
  1967  // returns the proposal hash
  1968  func (h *msActorHarness) proposeOK(rt *mock.Runtime, to addr.Address, value abi.TokenAmount, method abi.MethodNum, params []byte, out cbor.Unmarshaler) []byte {
  1969  	code := h.propose(rt, to, value, method, params, out)
  1970  	if code != exitcode.Ok {
  1971  		h.t.Fatalf("unexpected exitcode %d from propose", code)
  1972  	}
  1973  
  1974  	proposalHashData, err := multisig.ComputeProposalHash(&multisig.Transaction{
  1975  		To:       to,
  1976  		Value:    value,
  1977  		Method:   method,
  1978  		Params:   params,
  1979  		Approved: []addr.Address{rt.Caller()},
  1980  	}, blake2b.Sum256)
  1981  	require.NoError(h.t, err)
  1982  
  1983  	return proposalHashData
  1984  }
  1985  
  1986  func (h *msActorHarness) approve(rt *mock.Runtime, txnID int64, proposalParams []byte, out cbor.Unmarshaler) exitcode.ExitCode {
  1987  	rt.ExpectValidateCallerType(builtin.AccountActorCodeID, builtin.MultisigActorCodeID)
  1988  	ret := rt.Call(h.a.Approve, &multisig.TxnIDParams{
  1989  		ID:           multisig.TxnID(txnID),
  1990  		ProposalHash: proposalParams,
  1991  	})
  1992  	rt.Verify()
  1993  	approveReturn, ok := ret.(*multisig.ApproveReturn)
  1994  	if !ok {
  1995  		h.t.Fatalf("unexpected type returned from call to Approve")
  1996  	}
  1997  	// if the transaction was applied and a return value is expected deserialize it to the out parameter
  1998  	if approveReturn.Applied {
  1999  		if out != nil {
  2000  			require.NoError(h.t, out.UnmarshalCBOR(bytes.NewReader(approveReturn.Ret)))
  2001  		}
  2002  	}
  2003  	return approveReturn.Code
  2004  }
  2005  
  2006  func (h *msActorHarness) approveOK(rt *mock.Runtime, txnID int64, proposalParams []byte, out cbor.Unmarshaler) {
  2007  	code := h.approve(rt, txnID, proposalParams, out)
  2008  	if code != exitcode.Ok {
  2009  		h.t.Fatalf("unexpected exitcode %d from approve", code)
  2010  	}
  2011  }
  2012  
  2013  func (h *msActorHarness) cancel(rt *mock.Runtime, txnID int64, proposalParams []byte) {
  2014  	rt.ExpectValidateCallerType(builtin.AccountActorCodeID, builtin.MultisigActorCodeID)
  2015  	rt.Call(h.a.Cancel, &multisig.TxnIDParams{
  2016  		ID:           multisig.TxnID(txnID),
  2017  		ProposalHash: proposalParams,
  2018  	})
  2019  	rt.Verify()
  2020  }
  2021  
  2022  func (h *msActorHarness) addSigner(rt *mock.Runtime, signer addr.Address, increase bool) {
  2023  	rt.ExpectValidateCallerAddr(rt.Receiver())
  2024  	rt.Call(h.a.AddSigner, &multisig.AddSignerParams{
  2025  		Signer:   signer,
  2026  		Increase: increase,
  2027  	})
  2028  	rt.Verify()
  2029  }
  2030  
  2031  func (h *msActorHarness) removeSigner(rt *mock.Runtime, signer addr.Address, decrease bool) {
  2032  	rt.ExpectValidateCallerAddr(rt.Receiver())
  2033  	rt.Call(h.a.RemoveSigner, &multisig.RemoveSignerParams{
  2034  		Signer:   signer,
  2035  		Decrease: decrease,
  2036  	})
  2037  	rt.Verify()
  2038  }
  2039  
  2040  func (h *msActorHarness) swapSigners(rt *mock.Runtime, oldSigner, newSigner addr.Address) {
  2041  	rt.ExpectValidateCallerAddr(rt.Receiver())
  2042  	rt.Call(h.a.SwapSigner, &multisig.SwapSignerParams{
  2043  		From: oldSigner,
  2044  		To:   newSigner,
  2045  	})
  2046  	rt.Verify()
  2047  }
  2048  
  2049  func (h *msActorHarness) changeNumApprovalsThreshold(rt *mock.Runtime, newThreshold uint64) {
  2050  	rt.ExpectValidateCallerAddr(rt.Receiver())
  2051  	rt.Call(h.a.ChangeNumApprovalsThreshold, &multisig.ChangeNumApprovalsThresholdParams{
  2052  		NewThreshold: newThreshold,
  2053  	})
  2054  	rt.Verify()
  2055  }
  2056  
  2057  func (h *msActorHarness) lockBalance(rt *mock.Runtime, start, duration abi.ChainEpoch, amount abi.TokenAmount) {
  2058  	rt.ExpectValidateCallerAddr(rt.Receiver())
  2059  	rt.Call(h.a.LockBalance, &multisig.LockBalanceParams{
  2060  		StartEpoch:     start,
  2061  		UnlockDuration: duration,
  2062  		Amount:         amount,
  2063  	})
  2064  	rt.Verify()
  2065  }
  2066  
  2067  func (h *msActorHarness) assertTransactions(rt *mock.Runtime, expected ...multisig.Transaction) {
  2068  	var st multisig.State
  2069  	rt.GetState(&st)
  2070  
  2071  	txns, err := adt.AsMap(adt.AsStore(rt), st.PendingTxns, builtin.DefaultHamtBitwidth)
  2072  	assert.NoError(h.t, err)
  2073  	keys, err := txns.CollectKeys()
  2074  	assert.NoError(h.t, err)
  2075  
  2076  	require.Equal(h.t, len(expected), len(keys))
  2077  	for i, k := range keys {
  2078  		var actual multisig.Transaction
  2079  		found, err_ := txns.Get(asKey(k), &actual)
  2080  		require.NoError(h.t, err_)
  2081  		assert.True(h.t, found)
  2082  		assert.Equal(h.t, expected[i], actual)
  2083  	}
  2084  }
  2085  
  2086  func (h *msActorHarness) checkState(rt *mock.Runtime) {
  2087  	var st multisig.State
  2088  	rt.GetState(&st)
  2089  	assertStateInvariants(h.t, rt, &st)
  2090  }
  2091  
  2092  func assertStateInvariants(t testing.TB, rt *mock.Runtime, st *multisig.State) {
  2093  	_, msgs := multisig.CheckStateInvariants(st, rt.AdtStore())
  2094  	assert.True(t, msgs.IsEmpty(), strings.Join(msgs.Messages(), "\n"))
  2095  }
  2096  
  2097  func makeProposalHash(t *testing.T, txn *multisig.Transaction) []byte {
  2098  	proposalHashData, err := multisig.ComputeProposalHash(txn, blake2b.Sum256)
  2099  	require.NoError(t, err)
  2100  	return proposalHashData
  2101  }
  2102  
  2103  type key string
  2104  
  2105  func (s key) Key() string {
  2106  	return string(s)
  2107  }
  2108  
  2109  func asKey(in string) abi.Keyer {
  2110  	return key(in)
  2111  }