nested test suite (#231)

This commit is contained in:
Anton Evangelatov 2020-08-14 19:34:02 +02:00 committed by GitHub
parent acbb80920b
commit b2c2a7b5d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 415 additions and 20 deletions

View File

@ -6,6 +6,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -46,6 +47,13 @@ func (a *Asserter) NonceEq(addr address.Address, expected uint64) {
a.Equal(expected, actor.Nonce, "expected actor %s nonce: %d, got: %d", addr, expected, actor.Nonce) a.Equal(expected, actor.Nonce, "expected actor %s nonce: %d, got: %d", addr, expected, actor.Nonce)
} }
// HeadEq verifies that the head of the actor equals the expected one.
func (a *Asserter) HeadEq(addr address.Address, expected cid.Cid) {
actor, err := a.b.StateTree.GetActor(addr)
a.NoError(err, "failed to fetch actor %s from state", addr)
a.Equal(expected, actor.Head, "expected actor %s head: %v, got: %v", addr, expected, actor.Head)
}
// ActorExists verifies that the actor exists in the state tree. // ActorExists verifies that the actor exists in the state tree.
func (a *Asserter) ActorExists(addr address.Address) { func (a *Asserter) ActorExists(addr address.Address) {
_, err := a.b.StateTree.GetActor(addr) _, err := a.b.StateTree.GetActor(addr)

View File

@ -5,6 +5,7 @@ import (
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/puppet"
"github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
@ -360,3 +361,19 @@ func InitExec(params *init_.ExecParams) TypedCall {
return builtin.MethodsInit.Exec, MustSerialize(params) return builtin.MethodsInit.Exec, MustSerialize(params)
} }
} }
// ----------------------------------------------------------------------------
// | PUPPET
// ----------------------------------------------------------------------------
func PuppetConstructor(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return puppet.MethodsPuppet.Constructor, MustSerialize(params)
}
}
func PuppetSend(params *puppet.SendParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return puppet.MethodsPuppet.Send, MustSerialize(params)
}
}

View File

@ -1,35 +1,101 @@
package main package main
import ( import (
"os"
"bytes" "bytes"
"os"
//"fmt"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/abi/big"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
builtin "github.com/filecoin-project/specs-actors/actors/builtin" builtin "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/runtime" init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
//"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/builtin/multisig" "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/runtime"
typegen "github.com/whyrusleeping/cbor-gen"
//"github.com/filecoin-project/specs-actors/actors/crypto" //"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
"github.com/filecoin-project/specs-actors/actors/puppet"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode" "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/filecoin-project/specs-actors/actors/util/adt"
. "github.com/filecoin-project/oni/tvx/builders" . "github.com/filecoin-project/oni/tvx/builders"
"github.com/filecoin-project/oni/tvx/schema" "github.com/filecoin-project/oni/tvx/schema"
//"github.com/davecgh/go-spew/spew" //"github.com/davecgh/go-spew/spew"
) )
var (
acctDefaultBalance = abi.NewTokenAmount(1_000_000_000_000)
multisigBalance = abi.NewTokenAmount(1_000_000_000)
nonce = uint64(1)
PuppetAddress address.Address
)
func init() {
var err error
// the address before the burnt funds address
PuppetAddress, err = address.NewIDAddress(builtin.FirstNonSingletonActorId - 2)
if err != nil {
panic(err)
}
}
func main() { func main() {
nestedSends_OkBasic() nestedSends_OkBasic()
nestedSends_OkToNewActor()
nestedSends_OkToNewActorWithInvoke()
nestedSends_OkRecursive()
nestedSends_OKNonCBORParamsWithTransfer()
// TODO: Tests to exercise invalid "syntax" of the inner message.
// These would fail message syntax validation if the message were top-level.
//
// Some of these require handcrafting the proposal params serialization.
// - malformed address: zero-length, one-length, too-short pubkeys, invalid UVarints, ...
// - negative method num
//
// Unfortunately the multisig actor can't be used to trigger a negative-value internal transfer because
// it checks just before sending.
// We need a custom actor for staging whackier messages.
//
// The following tests exercise invalid semantics of the inner message
//
nestedSends_FailNonexistentIDAddress()
nestedSends_FailNonexistentActorAddress()
nestedSends_FailInvalidMethodNumNewActor()
nestedSends_FailInvalidMethodNumForActor()
// The multisig actor checks before attempting to transfer more than its balance, so we can't exercise that
// the VM also checks this. Need a custome actor to exercise this.
//t.Run("fail insufficient funds", func(t *testing.T) {
// td := builder.Build(t)
// defer td.Complete()
//
// stage := prepareStage(td, acctDefaultBalance, multisigBalance)
// balanceBefore := td.GetBalance(stage.creator)
//
// // Attempt to transfer from the multisig more than the balance it has.
// // The proposal to do should succeed, but the inner message fail.
// amtSent := big.Add(multisigBalance, abi.NewTokenAmount(1))
// result := stage.send(stage.creator, amtSent, builtin.MethodSend, nil, nonce)
// assert.Equal(t, exitcode_spec.Ok, result.Receipt.ExitCode)
//
// td.AssertBalance(stage.msAddr, multisigBalance) // No change.
// td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) // Pay gas, don't receive funds.
//})
nestedSends_FailMissingParams()
nestedSends_FailMismatchParams()
nestedSends_FailInnerAbort()
nestedSends_FailAbortedExec()
nestedSends_FailInsufficientFundsForTransferInInnerSend()
} }
func nestedSends_OkBasic() { func nestedSends_OkBasic() {
var acctDefaultBalance = abi.NewTokenAmount(1_000_000_000_000)
var multisigBalance = abi.NewTokenAmount(1_000_000_000)
nonce := uint64(1)
metadata := &schema.Metadata{ID: "nested-sends-ok-basic", Version: "v1", Desc: ""} metadata := &schema.Metadata{ID: "nested-sends-ok-basic", Version: "v1", Desc: ""}
v := MessageVector(metadata) v := MessageVector(metadata)
@ -49,6 +115,315 @@ func nestedSends_OkBasic() {
v.Finish(os.Stdout) v.Finish(os.Stdout)
} }
func nestedSends_OkToNewActor() {
metadata := &schema.Metadata{ID: "nested-sends-ok-to-new-actor", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to new address.
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed)))
v.Assert.BalanceEq(newAddr, amtSent)
v.Finish(os.Stdout)
}
func nestedSends_OkToNewActorWithInvoke() {
metadata := &schema.Metadata{ID: "nested-sends-ok-to-new-actor-with-invoke", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to new address and invokes pubkey method at the same time.
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(newAddr, amtSent, builtin.MethodsAccount.PubkeyAddress, nil, nonce)
// TODO: use an explicit Approve() and check the return value is the correct pubkey address
// when the multisig Approve() method plumbs through the inner exit code and value.
// https://github.com/filecoin-project/specs-actors/issues/113
//expected := bytes.Buffer{}
//require.NoError(t, newAddr.MarshalCBOR(&expected))
//assert.Equal(t, expected.Bytes(), result.Receipt.ReturnValue)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed)))
v.Assert.BalanceEq(newAddr, amtSent)
v.Finish(os.Stdout)
}
func nestedSends_OkRecursive() {
metadata := &schema.Metadata{ID: "nested-sends-ok-recursive", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
another := v.Actors.Account(address.SECP256K1, big.Zero())
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to itself.
params := multisig.AddSignerParams{
Signer: another.ID,
Increase: false,
}
result := stage.sendOk(stage.msAddr, big.Zero(), builtin.MethodsMultisig.AddSigner, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance)
v.Assert.Equal(big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed)), v.Actors.Balance(stage.creator))
var st multisig.State
v.Actors.ActorState(stage.msAddr, &st)
v.Assert.Equal([]address.Address{stage.creator, another.ID}, st.Signers)
v.Finish(os.Stdout)
}
func nestedSends_OKNonCBORParamsWithTransfer() {
metadata := &schema.Metadata{ID: "nested-sends-ok-non-cbor-params-with-transfer", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
// So long as the parameters are not actually used by the method, a message can carry arbitrary bytes.
params := typegen.Deferred{Raw: []byte{1, 2, 3, 4}}
stage.sendOk(newAddr, amtSent, builtin.MethodSend, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(newAddr, amtSent)
v.Finish(os.Stdout)
}
func nestedSends_FailNonexistentIDAddress() {
metadata := &schema.Metadata{ID: "nested-sends-fail-nonexistent-id-address", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := MustNewIDAddr(1234)
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
v.Finish(os.Stdout)
}
func nestedSends_FailNonexistentActorAddress() {
metadata := &schema.Metadata{ID: "nested-sends-fail-nonexistent-actor-address", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := MustNewActorAddr("1234")
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
v.Finish(os.Stdout)
}
func nestedSends_FailInvalidMethodNumNewActor() {
metadata := &schema.Metadata{ID: "nested-sends-fail-invalid-methodnum-new-actor", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, abi.MethodNum(99), nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
v.Finish(os.Stdout)
}
func nestedSends_FailInvalidMethodNumForActor() {
metadata := &schema.Metadata{ID: "nested-sends-fail-invalid-methodnum-for-actor", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(stage.creator, amtSent, abi.MethodNum(99), nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed))) // Pay gas, don't receive funds.
v.Finish(os.Stdout)
}
func nestedSends_FailMissingParams() {
metadata := &schema.Metadata{ID: "nested-sends-fail-missing-params", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
params := adt.Empty // Missing params required by AddSigner
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(stage.msAddr, amtSent, builtin.MethodsMultisig.AddSigner, params, nonce)
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed)))
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.Equal(1, len(stage.state().Signers)) // No new signers
v.Finish(os.Stdout)
}
func nestedSends_FailMismatchParams() {
metadata := &schema.Metadata{ID: "nested-sends-fail-mismatched-params", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Wrong params for AddSigner
params := multisig.ProposeParams{
To: stage.creator,
Value: big.Zero(),
Method: builtin.MethodSend,
Params: nil,
}
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(stage.msAddr, amtSent, builtin.MethodsMultisig.AddSigner, &params, nonce)
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, big.NewInt(result.MessageReceipt.GasUsed)))
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.Equal(1, len(stage.state().Signers)) // No new signers
v.Finish(os.Stdout)
}
func nestedSends_FailInnerAbort() {
metadata := &schema.Metadata{ID: "nested-sends-fail-inner-abort", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
prevHead := v.Actors.Head(builtin.RewardActorAddr)
// AwardBlockReward will abort unless invoked by the system actor
params := reward.AwardBlockRewardParams{
Miner: stage.creator,
Penalty: big.Zero(),
GasReward: big.Zero(),
}
amtSent := abi.NewTokenAmount(1)
stage.sendOk(builtin.RewardActorAddr, amtSent, builtin.MethodsReward.AwardBlockReward, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.HeadEq(builtin.RewardActorAddr, prevHead)
v.Finish(os.Stdout)
}
func nestedSends_FailAbortedExec() {
metadata := &schema.Metadata{ID: "nested-sends-fail-aborted-exec", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
prevHead := v.Actors.Head(builtin.InitActorAddr)
// Illegal paych constructor params (addresses are not accounts)
ctorParams := paych.ConstructorParams{
From: builtin.SystemActorAddr,
To: builtin.SystemActorAddr,
}
execParams := init_.ExecParams{
CodeCID: builtin.PaymentChannelActorCodeID,
ConstructorParams: MustSerialize(&ctorParams),
}
amtSent := abi.NewTokenAmount(1)
stage.sendOk(builtin.InitActorAddr, amtSent, builtin.MethodsInit.Exec, &execParams, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.HeadEq(builtin.InitActorAddr, prevHead) // Init state unchanged.
v.Finish(os.Stdout)
}
func nestedSends_FailInsufficientFundsForTransferInInnerSend() {
metadata := &schema.Metadata{ID: "nested-sends-fail-insufficient-funds-for-transfer-in-inner-send", Version: "v1", Desc: ""}
v := MessageVector(metadata)
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1))
// puppet actor has zero funds
puppetBalance := big.Zero()
_ = v.Actors.CreateActor(puppet.PuppetActorCodeID, PuppetAddress, puppetBalance, &puppet.State{})
alice := v.Actors.Account(address.SECP256K1, acctDefaultBalance)
bob := v.Actors.Account(address.SECP256K1, big.Zero())
v.CommitPreconditions()
// alice tells the puppet actor to send funds to bob, the puppet actor has 0 balance so the inner send will fail,
// and alice will pay the gas cost.
amtSent := abi.NewTokenAmount(1)
msg := v.Messages.Typed(alice.ID, PuppetAddress, PuppetSend(&puppet.SendParams{
To: bob.ID,
Value: amtSent,
Method: builtin.MethodSend,
Params: nil,
}), Nonce(0), Value(big.Zero()))
v.Messages.ApplyOne(msg)
v.CommitApplies()
// the outer message should be applied successfully
v.Assert.Equal(exitcode.Ok, msg.Result.ExitCode)
var puppetRet puppet.SendReturn
MustDeserialize(msg.Result.MessageReceipt.Return, &puppetRet)
// the inner message should fail
v.Assert.Equal(exitcode.SysErrInsufficientFunds, puppetRet.Code)
// alice should be charged for the gas cost and bob should have not received any funds.
v.Assert.BalanceEq(alice.ID, big.Sub(acctDefaultBalance, big.NewInt(msg.Result.GasUsed)))
v.Assert.BalanceEq(bob.ID, big.Zero())
v.Finish(os.Stdout)
}
type msStage struct { type msStage struct {
v *Builder v *Builder
@ -71,7 +446,6 @@ func prepareStage(v *Builder, creatorBalance, msBalance abi.TokenAmount) *msStag
var ret init_.ExecReturn var ret init_.ExecReturn
MustDeserialize(msg.Result.Return, &ret) MustDeserialize(msg.Result.Return, &ret)
return &msStage{ return &msStage{
v: v, v: v,
creator: creator.ID, creator: creator.ID,
@ -79,7 +453,6 @@ func prepareStage(v *Builder, creatorBalance, msBalance abi.TokenAmount) *msStag
} }
} }
//func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.MethodNum, params runtime.CBORMarshaler, approverNonce uint64) vtypes.ApplyMessageResult {
func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.MethodNum, params runtime.CBORMarshaler, approverNonce uint64) *vm.ApplyRet { func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.MethodNum, params runtime.CBORMarshaler, approverNonce uint64) *vm.ApplyRet {
buf := bytes.Buffer{} buf := bytes.Buffer{}
if params != nil { if params != nil {
@ -87,7 +460,6 @@ func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.M
if err != nil { if err != nil {
panic(err) panic(err)
} }
//require.NoError(drivers.T, err)
} }
pparams := multisig.ProposeParams{ pparams := multisig.ProposeParams{
To: to, To: to,
@ -96,9 +468,7 @@ func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.M
Params: buf.Bytes(), Params: buf.Bytes(),
} }
msg := s.v.Messages.Typed(s.creator, s.msAddr, MultisigPropose(&pparams), Nonce(approverNonce), Value(big.NewInt(0))) msg := s.v.Messages.Typed(s.creator, s.msAddr, MultisigPropose(&pparams), Nonce(approverNonce), Value(big.NewInt(0)))
//result := s.driver.ApplyMessage(msg)
s.v.CommitApplies() s.v.CommitApplies()
//s.v.Assert.Equal(exitcode_spec.Ok, result.Receipt.ExitCode)
// all messages succeeded. // all messages succeeded.
s.v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok)) s.v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok))
@ -106,8 +476,8 @@ func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.M
return msg.Result return msg.Result
} }
//func (s *msStage) state() *multisig.State { func (s *msStage) state() *multisig.State {
//var msState multisig.State var msState multisig.State
//s.driver.GetActorState(s.msAddr, &msState) s.v.Actors.ActorState(s.msAddr, &msState)
//return &msState return &msState
//} }