package paychmgr import ( "context" "fmt" "sync" "github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/specs-actors/actors/crypto" cbornode "github.com/ipfs/go-ipld-cbor" "github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/specs-actors/actors/builtin/account" "github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/ipfs/go-cid" ) type mockManagerAPI struct { *mockStateManager *mockPaychAPI } func newMockManagerAPI() *mockManagerAPI { return &mockManagerAPI{ mockStateManager: newMockStateManager(), mockPaychAPI: newMockPaychAPI(), } } type mockPchState struct { actor *types.Actor state paych.State } type mockStateManager struct { lk sync.Mutex accountState map[address.Address]account.State paychState map[address.Address]mockPchState store adt.Store response *api.InvocResult lastCall *types.Message } func newMockStateManager() *mockStateManager { return &mockStateManager{ accountState: make(map[address.Address]account.State), paychState: make(map[address.Address]mockPchState), store: adt.WrapStore(context.Background(), cbornode.NewMemCborStore()), } } func (sm *mockStateManager) AdtStore(ctx context.Context) adt.Store { return sm.store } func (sm *mockStateManager) setAccountState(a address.Address, state account.State) { sm.lk.Lock() defer sm.lk.Unlock() sm.accountState[a] = state } func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor, state paych.State) { sm.lk.Lock() defer sm.lk.Unlock() sm.paychState[a] = mockPchState{actor, state} } func (sm *mockStateManager) storeLaneStates(laneStates map[uint64]paych.LaneState) (cid.Cid, error) { arr := adt.MakeEmptyArray(sm.store) for i, ls := range laneStates { ls := ls if err := arr.Set(i, &ls); err != nil { return cid.Undef, err } } return arr.Root() } func (sm *mockStateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) { sm.lk.Lock() defer sm.lk.Unlock() if outState, ok := out.(*account.State); ok { *outState = sm.accountState[a] return nil, nil } if outState, ok := out.(*paych.State); ok { info := sm.paychState[a] *outState = info.state return info.actor, nil } panic(fmt.Sprintf("unexpected state type %v", out)) } func (sm *mockStateManager) setCallResponse(response *api.InvocResult) { sm.lk.Lock() defer sm.lk.Unlock() sm.response = response } func (sm *mockStateManager) getLastCall() *types.Message { sm.lk.Lock() defer sm.lk.Unlock() return sm.lastCall } func (sm *mockStateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) { sm.lk.Lock() defer sm.lk.Unlock() sm.lastCall = msg return sm.response, nil } type waitingCall struct { response chan types.MessageReceipt } type waitingResponse struct { receipt types.MessageReceipt done chan struct{} } type mockPaychAPI struct { lk sync.Mutex messages map[cid.Cid]*types.SignedMessage waitingCalls map[cid.Cid]*waitingCall waitingResponses map[cid.Cid]*waitingResponse wallet map[address.Address]struct{} signingKey []byte } func newMockPaychAPI() *mockPaychAPI { return &mockPaychAPI{ messages: make(map[cid.Cid]*types.SignedMessage), waitingCalls: make(map[cid.Cid]*waitingCall), waitingResponses: make(map[cid.Cid]*waitingResponse), wallet: make(map[address.Address]struct{}), } } func (pchapi *mockPaychAPI) StateWaitMsg(ctx context.Context, mcid cid.Cid, confidence uint64) (*api.MsgLookup, error) { pchapi.lk.Lock() response := make(chan types.MessageReceipt) if response, ok := pchapi.waitingResponses[mcid]; ok { defer pchapi.lk.Unlock() defer func() { go close(response.done) }() delete(pchapi.waitingResponses, mcid) return &api.MsgLookup{Receipt: response.receipt}, nil } pchapi.waitingCalls[mcid] = &waitingCall{response: response} pchapi.lk.Unlock() receipt := <-response return &api.MsgLookup{Receipt: receipt}, nil } func (pchapi *mockPaychAPI) receiveMsgResponse(mcid cid.Cid, receipt types.MessageReceipt) { pchapi.lk.Lock() if call, ok := pchapi.waitingCalls[mcid]; ok { defer pchapi.lk.Unlock() delete(pchapi.waitingCalls, mcid) call.response <- receipt return } done := make(chan struct{}) pchapi.waitingResponses[mcid] = &waitingResponse{receipt: receipt, done: done} pchapi.lk.Unlock() <-done } // Send success response for any waiting calls func (pchapi *mockPaychAPI) close() { pchapi.lk.Lock() defer pchapi.lk.Unlock() success := types.MessageReceipt{ ExitCode: 0, Return: []byte{}, } for mcid, call := range pchapi.waitingCalls { delete(pchapi.waitingCalls, mcid) call.response <- success } } func (pchapi *mockPaychAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) (*types.SignedMessage, error) { pchapi.lk.Lock() defer pchapi.lk.Unlock() smsg := &types.SignedMessage{Message: *msg} pchapi.messages[smsg.Cid()] = smsg return smsg, nil } func (pchapi *mockPaychAPI) pushedMessages(c cid.Cid) *types.SignedMessage { pchapi.lk.Lock() defer pchapi.lk.Unlock() return pchapi.messages[c] } func (pchapi *mockPaychAPI) pushedMessageCount() int { pchapi.lk.Lock() defer pchapi.lk.Unlock() return len(pchapi.messages) } func (pchapi *mockPaychAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { return addr, nil } func (pchapi *mockPaychAPI) WalletHas(ctx context.Context, addr address.Address) (bool, error) { pchapi.lk.Lock() defer pchapi.lk.Unlock() _, ok := pchapi.wallet[addr] return ok, nil } func (pchapi *mockPaychAPI) addWalletAddress(addr address.Address) { pchapi.lk.Lock() defer pchapi.lk.Unlock() pchapi.wallet[addr] = struct{}{} } func (pchapi *mockPaychAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) { pchapi.lk.Lock() defer pchapi.lk.Unlock() return sigs.Sign(crypto.SigTypeSecp256k1, pchapi.signingKey, msg) } func (pchapi *mockPaychAPI) addSigningKey(key []byte) { pchapi.lk.Lock() defer pchapi.lk.Unlock() pchapi.signingKey = key }