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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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, ¶ms) 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 }