test: port chain validation multisig test suite (#216)
This commit is contained in:
parent
be5bd41640
commit
47f0ef0eae
@ -28,6 +28,7 @@ func suiteMessages(c *cli.Context) error {
|
||||
err = multierror.Append(MessageTest_AccountActorCreation())
|
||||
err = multierror.Append(MessageTest_InitActorSequentialIDAddressCreate())
|
||||
err = multierror.Append(MessageTest_MessageApplicationEdgecases())
|
||||
err = multierror.Append(MessageTest_MultiSigActor())
|
||||
err = multierror.Append(MessageTest_Paych())
|
||||
err = multierror.Append(MessageTest_ValueTransferSimple())
|
||||
err = multierror.Append(MessageTest_ValueTransferAdvance())
|
||||
|
362
tvx/suite_messages_multisig.go
Normal file
362
tvx/suite_messages_multisig.go
Normal file
@ -0,0 +1,362 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/oni/tvx/chain"
|
||||
"github.com/filecoin-project/oni/tvx/drivers"
|
||||
abi_spec "github.com/filecoin-project/specs-actors/actors/abi"
|
||||
big_spec "github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
builtin_spec "github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
multisig_spec "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
|
||||
exitcode_spec "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"github.com/minio/blake2b-simd"
|
||||
)
|
||||
|
||||
func MessageTest_MultiSigActor() error {
|
||||
err := func(testname string) error {
|
||||
const numApprovals = 1
|
||||
const unlockDuration = 10
|
||||
var valueSend = abi_spec.NewTokenAmount(10)
|
||||
var initialBal = abi_spec.NewTokenAmount(200000000000)
|
||||
|
||||
td := drivers.NewTestDriver()
|
||||
td.Vector.Meta.Desc = testname
|
||||
|
||||
// creator of the multisig actor
|
||||
alice, aliceID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
|
||||
// expected address of the actor
|
||||
multisigAddr := chain.MustNewIDAddr(1 + chain.MustIDFromAddress(aliceID))
|
||||
|
||||
preroot := td.GetStateRoot()
|
||||
|
||||
createRet := td.ComputeInitActorExecReturn(alice, 0, 0, multisigAddr)
|
||||
td.MustCreateAndVerifyMultisigActor(0, valueSend, multisigAddr, alice,
|
||||
&multisig_spec.ConstructorParams{
|
||||
Signers: []address.Address{aliceID},
|
||||
NumApprovalsThreshold: numApprovals,
|
||||
UnlockDuration: unlockDuration,
|
||||
},
|
||||
exitcode_spec.Ok, chain.MustSerialize(&createRet))
|
||||
|
||||
postroot := td.GetStateRoot()
|
||||
|
||||
td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
|
||||
td.Vector.Pre.StateTree.RootCID = preroot
|
||||
td.Vector.Post.StateTree.RootCID = postroot
|
||||
|
||||
// encode and output
|
||||
fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON()))
|
||||
|
||||
return nil
|
||||
}("constructor test")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = func(testname string) error {
|
||||
const numApprovals = 2
|
||||
const unlockDuration = 10
|
||||
var valueSend = abi_spec.NewTokenAmount(10)
|
||||
var initialBal = abi_spec.NewTokenAmount(200000000000)
|
||||
|
||||
td := drivers.NewTestDriver()
|
||||
td.Vector.Meta.Desc = testname
|
||||
|
||||
alice, aliceID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
|
||||
bob, bobID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
outsider, outsiderID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
|
||||
multisigAddr := chain.MustNewIDAddr(1 + chain.MustIDFromAddress(outsiderID))
|
||||
|
||||
preroot := td.GetStateRoot()
|
||||
|
||||
createRet := td.ComputeInitActorExecReturn(alice, 0, 0, multisigAddr)
|
||||
// create the multisig actor
|
||||
td.MustCreateAndVerifyMultisigActor(0, valueSend, multisigAddr, alice,
|
||||
&multisig_spec.ConstructorParams{
|
||||
Signers: []address.Address{aliceID, bobID},
|
||||
NumApprovalsThreshold: numApprovals,
|
||||
UnlockDuration: unlockDuration,
|
||||
},
|
||||
exitcode_spec.Ok, chain.MustSerialize(&createRet))
|
||||
td.AssertBalance(multisigAddr, valueSend)
|
||||
|
||||
// alice proposes that outsider should receive 'valueSend' FIL.
|
||||
pparams := multisig_spec.ProposeParams{
|
||||
To: outsider,
|
||||
Value: valueSend,
|
||||
Method: builtin_spec.MethodSend,
|
||||
Params: nil,
|
||||
}
|
||||
|
||||
// propose the transaction and assert it exists in the actor state
|
||||
txID0 := multisig_spec.TxnID(0)
|
||||
expected := multisig_spec.ProposeReturn{
|
||||
TxnID: 0,
|
||||
Applied: false,
|
||||
Code: 0,
|
||||
Ret: nil,
|
||||
}
|
||||
td.ApplyExpect(
|
||||
td.MessageProducer.MultisigPropose(alice, multisigAddr, &pparams, chain.Nonce(1)),
|
||||
chain.MustSerialize(&expected))
|
||||
|
||||
txn0 := multisig_spec.Transaction{
|
||||
To: pparams.To,
|
||||
Value: pparams.Value,
|
||||
Method: pparams.Method,
|
||||
Params: pparams.Params,
|
||||
Approved: []address.Address{aliceID},
|
||||
}
|
||||
ph := mustMakeProposalHash(&txn0)
|
||||
td.AssertMultisigTransaction(multisigAddr, txID0, txn0)
|
||||
|
||||
// bob cancels alice's transaction. This fails as bob did not create alice's transaction.
|
||||
td.ApplyFailure(
|
||||
td.MessageProducer.MultisigCancel(bob, multisigAddr, &multisig_spec.TxnIDParams{ID: txID0, ProposalHash: ph}, chain.Nonce(0)),
|
||||
exitcode_spec.ErrForbidden)
|
||||
|
||||
// alice cancels their transaction. The outsider doesn't receive any FIL, the multisig actor's balance is empty, and the
|
||||
// transaction is canceled.
|
||||
td.ApplyOk(
|
||||
td.MessageProducer.MultisigCancel(alice, multisigAddr, &multisig_spec.TxnIDParams{ID: txID0, ProposalHash: ph}, chain.Nonce(2)),
|
||||
)
|
||||
td.AssertMultisigState(multisigAddr, multisig_spec.State{
|
||||
Signers: []address.Address{aliceID, bobID},
|
||||
NumApprovalsThreshold: numApprovals,
|
||||
NextTxnID: 1,
|
||||
InitialBalance: valueSend,
|
||||
StartEpoch: 1,
|
||||
UnlockDuration: unlockDuration,
|
||||
})
|
||||
td.AssertBalance(multisigAddr, valueSend)
|
||||
|
||||
postroot := td.GetStateRoot()
|
||||
|
||||
td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
|
||||
td.Vector.Pre.StateTree.RootCID = preroot
|
||||
td.Vector.Post.StateTree.RootCID = postroot
|
||||
|
||||
// encode and output
|
||||
fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON()))
|
||||
|
||||
return nil
|
||||
}("propose and cancel")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = func(testname string) error {
|
||||
td := drivers.NewTestDriver()
|
||||
td.Vector.Meta.Desc = testname
|
||||
|
||||
var initialBal = abi_spec.NewTokenAmount(200000000000)
|
||||
const numApprovals = 2
|
||||
const unlockDuration = 1
|
||||
var valueSend = abi_spec.NewTokenAmount(10)
|
||||
|
||||
// Signers
|
||||
alice, aliceID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
bob, bobID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
|
||||
// Not Signer
|
||||
outsider, outsiderID := td.NewAccountActor(drivers.SECP, initialBal)
|
||||
|
||||
preroot := td.GetStateRoot()
|
||||
|
||||
// Multisig actor address
|
||||
multisigAddr := chain.MustNewIDAddr(1 + chain.MustIDFromAddress(outsiderID))
|
||||
createRet := td.ComputeInitActorExecReturn(alice, 0, 0, multisigAddr)
|
||||
|
||||
// create the multisig actor
|
||||
td.MustCreateAndVerifyMultisigActor(0, valueSend, multisigAddr, alice,
|
||||
&multisig_spec.ConstructorParams{
|
||||
Signers: []address.Address{aliceID, bobID},
|
||||
NumApprovalsThreshold: numApprovals,
|
||||
UnlockDuration: unlockDuration,
|
||||
},
|
||||
exitcode_spec.Ok, chain.MustSerialize(&createRet))
|
||||
|
||||
// setup propose expected values and params
|
||||
pparams := multisig_spec.ProposeParams{
|
||||
To: outsider,
|
||||
Value: valueSend,
|
||||
Method: builtin_spec.MethodSend,
|
||||
Params: nil,
|
||||
}
|
||||
|
||||
// propose the transaction and assert it exists in the actor state
|
||||
txID0 := multisig_spec.TxnID(0)
|
||||
expectedPropose := multisig_spec.ProposeReturn{
|
||||
TxnID: 0,
|
||||
Applied: false,
|
||||
Code: 0,
|
||||
Ret: nil,
|
||||
}
|
||||
td.ApplyExpect(
|
||||
td.MessageProducer.MultisigPropose(alice, multisigAddr, &pparams, chain.Nonce(1)),
|
||||
chain.MustSerialize(&expectedPropose))
|
||||
|
||||
txn0 := multisig_spec.Transaction{
|
||||
To: pparams.To,
|
||||
Value: pparams.Value,
|
||||
Method: pparams.Method,
|
||||
Params: pparams.Params,
|
||||
Approved: []address.Address{aliceID},
|
||||
}
|
||||
ph := mustMakeProposalHash(&txn0)
|
||||
td.AssertMultisigTransaction(multisigAddr, txID0, txn0)
|
||||
|
||||
// outsider proposes themselves to receive 'valueSend' FIL. This fails as they are not a signer.
|
||||
td.ApplyFailure(
|
||||
td.MessageProducer.MultisigPropose(outsider, multisigAddr, &pparams, chain.Nonce(0)),
|
||||
exitcode_spec.ErrForbidden)
|
||||
|
||||
// outsider approves the value transfer alice sent. This fails as they are not a signer.
|
||||
td.ApplyFailure(
|
||||
td.MessageProducer.MultisigApprove(outsider, multisigAddr, &multisig_spec.TxnIDParams{ID: txID0, ProposalHash: ph}, chain.Nonce(1)),
|
||||
exitcode_spec.ErrForbidden)
|
||||
|
||||
// increment the epoch to unlock the funds
|
||||
td.ExeCtx.Epoch += unlockDuration
|
||||
balanceBefore := td.GetBalance(outsider)
|
||||
|
||||
// bob approves transfer of 'valueSend' FIL to outsider.
|
||||
expectedApprove := multisig_spec.ApproveReturn{
|
||||
Applied: true,
|
||||
Code: 0,
|
||||
Ret: nil,
|
||||
}
|
||||
td.ApplyExpect(
|
||||
td.MessageProducer.MultisigApprove(bob, multisigAddr, &multisig_spec.TxnIDParams{ID: txID0, ProposalHash: ph}, chain.Nonce(0)),
|
||||
chain.MustSerialize(&expectedApprove))
|
||||
|
||||
txID1 := multisig_spec.TxnID(1)
|
||||
td.AssertMultisigState(multisigAddr, multisig_spec.State{
|
||||
Signers: []address.Address{aliceID, bobID},
|
||||
NumApprovalsThreshold: numApprovals,
|
||||
NextTxnID: txID1,
|
||||
InitialBalance: valueSend,
|
||||
StartEpoch: 1,
|
||||
UnlockDuration: unlockDuration,
|
||||
})
|
||||
td.AssertMultisigContainsTransaction(multisigAddr, txID0, false)
|
||||
// Multisig balance has been transferred to outsider.
|
||||
td.AssertBalance(multisigAddr, big_spec.Zero())
|
||||
td.AssertBalance(outsider, big_spec.Add(balanceBefore, valueSend))
|
||||
|
||||
postroot := td.GetStateRoot()
|
||||
|
||||
td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
|
||||
td.Vector.Pre.StateTree.RootCID = preroot
|
||||
td.Vector.Post.StateTree.RootCID = postroot
|
||||
|
||||
// encode and output
|
||||
fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON()))
|
||||
|
||||
return nil
|
||||
}("propose and approve")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = func(testname string) error {
|
||||
const initialNumApprovals = 1
|
||||
var msValue = abi_spec.NewTokenAmount(100000000000)
|
||||
var initialBal = abi_spec.NewTokenAmount(200000000000)
|
||||
|
||||
td := drivers.NewTestDriver()
|
||||
td.Vector.Meta.Desc = testname
|
||||
|
||||
alice, aliceID := td.NewAccountActor(drivers.SECP, initialBal) // 101
|
||||
_, bobID := td.NewAccountActor(drivers.SECP, initialBal) // 102
|
||||
var initialSigners = []address.Address{aliceID}
|
||||
|
||||
multisigAddr := chain.MustNewIDAddr(1 + chain.MustIDFromAddress(bobID))
|
||||
|
||||
preroot := td.GetStateRoot()
|
||||
|
||||
createRet := td.ComputeInitActorExecReturn(alice, 0, 0, multisigAddr)
|
||||
|
||||
td.MustCreateAndVerifyMultisigActor(0, msValue, multisigAddr, alice,
|
||||
&multisig_spec.ConstructorParams{
|
||||
Signers: initialSigners,
|
||||
NumApprovalsThreshold: initialNumApprovals,
|
||||
UnlockDuration: 0,
|
||||
},
|
||||
exitcode_spec.Ok,
|
||||
chain.MustSerialize(&createRet),
|
||||
)
|
||||
|
||||
addSignerParams := multisig_spec.AddSignerParams{
|
||||
Signer: bobID,
|
||||
Increase: false,
|
||||
}
|
||||
|
||||
// alice fails to call directly since AddSigner
|
||||
td.ApplyFailure(
|
||||
td.MessageProducer.MultisigAddSigner(alice, multisigAddr, &addSignerParams, chain.Nonce(1)),
|
||||
exitcode_spec.SysErrForbidden,
|
||||
)
|
||||
|
||||
// AddSigner must be staged through the multisig itself
|
||||
// Alice proposes the AddSigner.
|
||||
// Since approvals = 1 this auto-approves the transaction.
|
||||
expected := multisig_spec.ProposeReturn{
|
||||
TxnID: 0,
|
||||
Applied: true,
|
||||
Code: 0,
|
||||
Ret: nil,
|
||||
}
|
||||
td.ApplyExpect(
|
||||
td.MessageProducer.MultisigPropose(alice, multisigAddr, &multisig_spec.ProposeParams{
|
||||
To: multisigAddr,
|
||||
Value: big_spec.Zero(),
|
||||
Method: builtin_spec.MethodsMultisig.AddSigner,
|
||||
Params: chain.MustSerialize(&addSignerParams),
|
||||
}, chain.Nonce(2)),
|
||||
chain.MustSerialize(&expected),
|
||||
)
|
||||
|
||||
// TODO also exercise the approvals = 2 case with explicit approval.
|
||||
|
||||
// Check that bob is now a signer
|
||||
td.AssertMultisigState(multisigAddr, multisig_spec.State{
|
||||
Signers: append(initialSigners, bobID),
|
||||
NumApprovalsThreshold: initialNumApprovals,
|
||||
NextTxnID: multisig_spec.TxnID(1),
|
||||
InitialBalance: big_spec.Zero(),
|
||||
StartEpoch: 0,
|
||||
UnlockDuration: 0,
|
||||
})
|
||||
|
||||
postroot := td.GetStateRoot()
|
||||
|
||||
td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
|
||||
td.Vector.Pre.StateTree.RootCID = preroot
|
||||
td.Vector.Post.StateTree.RootCID = postroot
|
||||
|
||||
// encode and output
|
||||
fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON()))
|
||||
|
||||
return nil
|
||||
}("add signer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustMakeProposalHash(txn *multisig_spec.Transaction) []byte {
|
||||
txnHash, err := multisig_spec.ComputeProposalHash(txn, blake2b.Sum256)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return txnHash
|
||||
}
|
Loading…
Reference in New Issue
Block a user