lotus/tvx/suite_messages_multisig.go

363 lines
11 KiB
Go

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
}