From e2d0047a2aef2f209bf1af0518d7201e7c5f37b1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 27 Mar 2021 16:35:46 +0200 Subject: [PATCH] introduce message prototypes This introduces message prototypes to applicable API endpoints, which allows us to invert control of message sending and give the user a chance to intervene with an interactive ui. Signed-off-by: Jakub Sztandera --- api/api_full.go | 31 ++- api/mocks/mock_full.go | 48 ++-- api/proxy_gen.go | 96 ++++---- api/types.go | 7 + api/v0api/v1_wrapper.go | 127 ++++++++++ build/openrpc/full.json.gz | Bin 22681 -> 23192 bytes build/openrpc/miner.json.gz | Bin 7849 -> 7847 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes cli/init_test.go | 9 + cli/multisig.go | 247 ++++++++++++++----- cli/send.go | 4 +- cli/sending_ui.go | 18 +- cli/services.go | 60 +++-- cli/services_send_test.go | 37 +-- cli/servicesmock_test.go | 22 +- cmd/lotus-gateway/endtoend_test.go | 35 ++- cmd/lotus-shed/verifreg.go | 19 +- documentation/en/api-v0-methods.md | 48 ---- documentation/en/api-v1-unstable-methods.md | 252 +++++++++++++++++++- node/impl/full/multisig.go | 115 ++++----- 20 files changed, 852 insertions(+), 323 deletions(-) create mode 100644 cli/init_test.go diff --git a/api/api_full.go b/api/api_full.go index 148f4ac92..e8e8dcb2e 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -586,15 +586,16 @@ type FullNode interface { // MsigCreate creates a multisig wallet // It takes the following params: , , //, , - MsigCreate(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) //perm:sign + MsigCreate(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (*MessagePrototype, error) //perm:sign + // MsigPropose proposes a multisig message // It takes the following params: , , , // , , - MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign // MsigApprove approves a previously-proposed multisig message by transaction ID // It takes the following params: , - MsigApprove(context.Context, address.Address, uint64, address.Address) (cid.Cid, error) //perm:sign + MsigApprove(context.Context, address.Address, uint64, address.Address) (*MessagePrototype, error) //perm:sign // MsigApproveTxnHash approves a previously-proposed multisig message, specified // using both transaction ID and a hash of the parameters used in the @@ -602,43 +603,49 @@ type FullNode interface { // exactly the transaction you think you are. // It takes the following params: , , , , , // , , - MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign // MsigCancel cancels a previously-proposed multisig message // It takes the following params: , , , , // , , - MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign + // MsigAddPropose proposes adding a signer in the multisig // It takes the following params: , , // , - MsigAddPropose(context.Context, address.Address, address.Address, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddPropose(context.Context, address.Address, address.Address, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigAddApprove approves a previously proposed AddSigner message // It takes the following params: , , , // , , - MsigAddApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigAddCancel cancels a previously proposed AddSigner message // It takes the following params: , , , // , - MsigAddCancel(context.Context, address.Address, address.Address, uint64, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddCancel(context.Context, address.Address, address.Address, uint64, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigSwapPropose proposes swapping 2 signers in the multisig // It takes the following params: , , // , - MsigSwapPropose(context.Context, address.Address, address.Address, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapPropose(context.Context, address.Address, address.Address, address.Address, address.Address) (*MessagePrototype, error) //perm:sign + // MsigSwapApprove approves a previously proposed SwapSigner // It takes the following params: , , , // , , - MsigSwapApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (*MessagePrototype, error) //perm:sign + // MsigSwapCancel cancels a previously proposed SwapSigner message // It takes the following params: , , , // , - MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (*MessagePrototype, error) //perm:sign // MsigRemoveSigner proposes the removal of a signer from the multisig. // It accepts the multisig to make the change on, the proposer address to // send the message from, the address to be removed, and a boolean // indicating whether or not the signing threshold should be lowered by one // along with the address removal. - MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) //perm:sign + MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (*MessagePrototype, error) //perm:sign // MarketAddBalance adds funds to the market actor MarketAddBalance(ctx context.Context, wallet, addr address.Address, amt types.BigInt) (cid.Cid, error) //perm:sign diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index ee89a1d23..a14336537 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1262,10 +1262,10 @@ func (mr *MockFullNodeMockRecorder) MpoolSub(arg0 interface{}) *gomock.Call { } // MsigAddApprove mocks base method -func (m *MockFullNode) MsigAddApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address, arg6 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address, arg6 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddApprove", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1277,10 +1277,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddApprove(arg0, arg1, arg2, arg3, arg4, } // MsigAddCancel mocks base method -func (m *MockFullNode) MsigAddCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4 address.Address, arg5 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4 address.Address, arg5 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddCancel", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1292,10 +1292,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddCancel(arg0, arg1, arg2, arg3, arg4, } // MsigAddPropose mocks base method -func (m *MockFullNode) MsigAddPropose(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddPropose(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddPropose", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1307,10 +1307,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddPropose(arg0, arg1, arg2, arg3, arg4 } // MsigApprove mocks base method -func (m *MockFullNode) MsigApprove(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigApprove(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigApprove", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1322,10 +1322,10 @@ func (mr *MockFullNodeMockRecorder) MsigApprove(arg0, arg1, arg2, arg3 interface } // MsigApproveTxnHash mocks base method -func (m *MockFullNode) MsigApproveTxnHash(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3, arg4 address.Address, arg5 big.Int, arg6 address.Address, arg7 uint64, arg8 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigApproveTxnHash(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3, arg4 address.Address, arg5 big.Int, arg6 address.Address, arg7 uint64, arg8 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigApproveTxnHash", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1337,10 +1337,10 @@ func (mr *MockFullNodeMockRecorder) MsigApproveTxnHash(arg0, arg1, arg2, arg3, a } // MsigCancel mocks base method -func (m *MockFullNode) MsigCancel(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address, arg4 big.Int, arg5 address.Address, arg6 uint64, arg7 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigCancel(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address, arg4 big.Int, arg5 address.Address, arg6 uint64, arg7 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigCancel", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1352,10 +1352,10 @@ func (mr *MockFullNodeMockRecorder) MsigCancel(arg0, arg1, arg2, arg3, arg4, arg } // MsigCreate mocks base method -func (m *MockFullNode) MsigCreate(arg0 context.Context, arg1 uint64, arg2 []address.Address, arg3 abi.ChainEpoch, arg4 big.Int, arg5 address.Address, arg6 big.Int) (cid.Cid, error) { +func (m *MockFullNode) MsigCreate(arg0 context.Context, arg1 uint64, arg2 []address.Address, arg3 abi.ChainEpoch, arg4 big.Int, arg5 address.Address, arg6 big.Int) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigCreate", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1427,10 +1427,10 @@ func (mr *MockFullNodeMockRecorder) MsigGetVestingSchedule(arg0, arg1, arg2 inte } // MsigPropose mocks base method -func (m *MockFullNode) MsigPropose(arg0 context.Context, arg1, arg2 address.Address, arg3 big.Int, arg4 address.Address, arg5 uint64, arg6 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigPropose(arg0 context.Context, arg1, arg2 address.Address, arg3 big.Int, arg4 address.Address, arg5 uint64, arg6 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigPropose", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1442,10 +1442,10 @@ func (mr *MockFullNodeMockRecorder) MsigPropose(arg0, arg1, arg2, arg3, arg4, ar } // MsigRemoveSigner mocks base method -func (m *MockFullNode) MsigRemoveSigner(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigRemoveSigner(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigRemoveSigner", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1457,10 +1457,10 @@ func (mr *MockFullNodeMockRecorder) MsigRemoveSigner(arg0, arg1, arg2, arg3, arg } // MsigSwapApprove mocks base method -func (m *MockFullNode) MsigSwapApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5, arg6 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5, arg6 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapApprove", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1472,10 +1472,10 @@ func (mr *MockFullNodeMockRecorder) MsigSwapApprove(arg0, arg1, arg2, arg3, arg4 } // MsigSwapCancel mocks base method -func (m *MockFullNode) MsigSwapCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapCancel", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1487,10 +1487,10 @@ func (mr *MockFullNodeMockRecorder) MsigSwapCancel(arg0, arg1, arg2, arg3, arg4, } // MsigSwapPropose mocks base method -func (m *MockFullNode) MsigSwapPropose(arg0 context.Context, arg1, arg2, arg3, arg4 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapPropose(arg0 context.Context, arg1, arg2, arg3, arg4 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapPropose", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index dfb9f3731..f285f1ce6 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -261,19 +261,19 @@ type FullNodeStruct struct { MpoolSub func(p0 context.Context) (<-chan MpoolUpdate, error) `perm:"read"` - MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) `perm:"sign"` + MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) `perm:"sign"` - MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) `perm:"sign"` + MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) `perm:"sign"` - MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) `perm:"sign"` + MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) `perm:"sign"` + MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) `perm:"sign"` + MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) `perm:"sign"` + MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) `perm:"sign"` MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` @@ -283,15 +283,15 @@ type FullNodeStruct struct { MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `perm:"read"` - MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) `perm:"sign"` + MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) `perm:"sign"` NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"` @@ -1619,60 +1619,60 @@ func (s *FullNodeStub) MpoolSub(p0 context.Context) (<-chan MpoolUpdate, error) return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) { return s.Internal.MsigAddApprove(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) { return s.Internal.MsigAddCancel(p0, p1, p2, p3, p4, p5) } -func (s *FullNodeStub) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { return s.Internal.MsigAddPropose(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) { return s.Internal.MsigApprove(p0, p1, p2, p3) } -func (s *FullNodeStub) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) { return s.Internal.MsigApproveTxnHash(p0, p1, p2, p3, p4, p5, p6, p7, p8) } -func (s *FullNodeStub) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) { return s.Internal.MsigCancel(p0, p1, p2, p3, p4, p5, p6, p7) } -func (s *FullNodeStub) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) { +func (s *FullNodeStruct) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) { return s.Internal.MsigCreate(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } func (s *FullNodeStruct) MsigGetAvailableBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) { @@ -1707,44 +1707,44 @@ func (s *FullNodeStub) MsigGetVestingSchedule(p0 context.Context, p1 address.Add return *new(MsigVesting), xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) { return s.Internal.MsigPropose(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { return s.Internal.MsigRemoveSigner(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapApprove(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapCancel(p0, p1, p2, p3, p4, p5) } -func (s *FullNodeStub) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapPropose(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } func (s *FullNodeStruct) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) { diff --git a/api/types.go b/api/types.go index ae8bbe958..83de131a2 100644 --- a/api/types.go +++ b/api/types.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/filecoin-project/lotus/chain/types" + datatransfer "github.com/filecoin-project/go-data-transfer" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -169,3 +171,8 @@ type MessageCheckStatus struct { Cid cid.Cid CheckStatus } + +type MessagePrototype struct { + Message types.Message + ValidNonce bool +} diff --git a/api/v0api/v1_wrapper.go b/api/v0api/v1_wrapper.go index e977c6b67..ff4474fe5 100644 --- a/api/v0api/v1_wrapper.go +++ b/api/v0api/v1_wrapper.go @@ -3,7 +3,9 @@ package v0api import ( "context" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/chain/types" + "golang.org/x/xerrors" "github.com/ipfs/go-cid" @@ -57,4 +59,129 @@ func (w *WrapperV1Full) Version(ctx context.Context) (api.APIVersion, error) { return ver, nil } +func (w *WrapperV1Full) executePrototype(ctx context.Context, p *api.MessagePrototype) (cid.Cid, error) { + sm, err := w.FullNode.MpoolPushMessage(ctx, &p.Message, nil) + if err != nil { + return cid.Undef, xerrors.Errorf("pushing message: %w", err) + } + + return sm.Cid(), nil +} +func (w *WrapperV1Full) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) { + + p, err := w.FullNode.MsigCreate(ctx, req, addrs, duration, val, src, gp) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + + p, err := w.FullNode.MsigPropose(ctx, msig, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} +func (w *WrapperV1Full) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigApprove(ctx, msig, txID, src) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + p, err := w.FullNode.MsigApproveTxnHash(ctx, msig, txID, proposer, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + p, err := w.FullNode.MsigCancel(ctx, msig, txID, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddPropose(ctx, msig, src, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddApprove(ctx, msig, src, txID, proposer, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddCancel(ctx, msig, src, txID, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapPropose(ctx, msig, src, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapApprove(ctx, msig, src, txID, proposer, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapCancel(ctx, msig, src, txID, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigRemoveSigner(ctx, msig, proposer, toRemove, decrease) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + var _ FullNode = &WrapperV1Full{} diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 7763e4af8ba241fea0cb4d20a1fc61790c2646f4..cb781bec9169de220365f7d8cfbac0850a948f70 100644 GIT binary patch literal 23192 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 Npp+;_KoSPZe*qz{wEF-6 literal 22681 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 BZlVAH diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 501b238282ef3d11fb1e356a867206a563054b2b..0d52dac870e8b5a1854f566261221e5616a6839f 100644 GIT binary patch delta 7234 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 Q2LJ&7|C@$z=;D0=07z*TkN^Mx delta 7236 zcmV-K9J}MEJ*hpg6?KWa>N@A@d%~kDS7}s4?gnW*}eZ3mYaW4Y9mNuCD+H2RW$uw zYwK@%7HT5>bYNIA=xo4}2@?Ot|Df_fvZ9fJA}!!{!5Z#!qd- zo4DQmS5=w%Ht`kHV_1K!7(!_Hkfk$4UW&|b;xWKJfFLRo8+L!te|>t=ANG%jx2wSg zC7-$M46|kDe1U(=yRk>_IS`hToCF1>+)Q!%8qG29AF{@D!p0t@vleiB;Pb67$p%w= zjrj9E;OP4jyl|lVc{TrX3U7VpVUnxJ*Sp5M!kgsew<~km!`&L=mS@-1D0-<@GbTn0 zJ-88Z+X{8q=Nu)LGzu;+aPZ%h};P+E2_=;OPWuG#1tC+zX@w5Pe7&gY{ zNGj;UQ`ApQ5tk9IIpNe}9SAL(@`$}bEK|MQTvo@d?e_Y{$S@)K9|oW)`U^Y+Enrjg z&fpz%A>&d%PB2INrIVIA+`G%F9=NW!v};~2u$TON-X37wm`slk;&1<0{pW9KKM4cS)eaOMwUS^p*~;> z$V3(KIgcDZL@tTmk`PSh;45Ye{{9QO`u*nP++b>L}y5BFnV`D&<$M`le@ zz$o={Rh($OJheu-;iw&Plp;!(oFcLnpvs_H7f)rAZkTE}Om(V=4`j?y%_x7=7@aS}81k=!FsSljioR5sn3*h)#fVXfV;R#(SR|6?nRl|{8Cwo-qPZkTE} zOx4?DrC&NO_1FbuusjQ`Xs7}+Cbrt=*b;GZtsqUB?oQ&~K|k}CDKq2DNm}RF0v=}w z9}(sQ2z+GEj%1lBCUg0r2n$mxkOgo=tqm?2z&@B!&p{TLJ^~2-kjaZJ0Kqx6kO^LV zU0`zoFtM;Hf>RJUOPgVnzde6)p?Qa_s1?rBFGW}gf)BjZ0{V+~auxNP@k)d*XOF$< zKhQ+5|JAn^$B%v3KW5Xue}^3RKlI_h?(&g3?tiAv3_Z^JgBk8GACH$yH0a$AjHj}p znvJ3HCUEp!pktFec7=Ub9yECEZ2j{2_5<|p5aY$P^h`i%EBYkfnT&rmi^R(hma*7-zD zkuasZzH`s*mEuDUYgYMO69zq2;DZH{HTJNH#>k6m7Hc)M%>)Z)Bz|Tkf=n1>s303& zL7#neEe>!+#wb(Xihe&C8~O|F^63#C{xaQ3e^W?A3LLiH*wnnUE1`UflnU}A3sD2_ zvxsg9{8t9yI`Ze5iaHDQ2V_R3jI0Bbn8A~v2Ood0HwBo|yiiYwX*1pWIe3kF!@q~a zzsH+mS#eINb%`*CaGe~SO}=5_17hJ#F_jc6)Q`YAYm(L2x53|oW!{rbeq)Rg>R$fL zC0!#ED;sDS`EVF{sOiqtyV#mTxJD*ckZ)M{a9H>>$Cs-yjF&}Cy&)pUeYjh-2MNB1_7G#hGW!hok%L|c| z;UeU<#ov7$ZL^{2{4M6H%D@zlIREmlg0QdAo)|F4_lT^XUtS1Crfh*cF@oB*sVF|x z_Rd|{%J%hOQuUH1y1k43uatgz|Ifoezy5#u?|by~|1tOd@tN;^{N;`L>EY)$?i*aNW3$r96??j+`^Up6MJz&!=zGM@{JRR+Bnf0M`TZ0et>B1~?FpWf z$XY$~@A4J`*BaQ$@?TCDQy(V3yv|O4UQ(ocV3T*EW2_eGpyGtXRh~b422dQ(%9BC2N)1UEMMYA5O9{<>NU6druMGh+s2XVyKMp zaY_oP_rf9pG8V!2HS*g5`iu^y(LKA6-kF3lN9)Za3*q}C@x^f$D?lH^M+XrmzQ8@S zIRK{~UJ}zo&_@EDi(zewyjDh>s(HP1y7;JzVqM|`mBgb15oXn~e^N$*EO~zf?b2EN zS%hj!8fH%-Dml}WRUlPnPWYo(_`{o5Ehy=;->9-${@54FD%-8>&Es!@xiK?-) zw-}tG{F8mxjs}Y=>75I_wgrDeoN!}tm#DZMauJ{9jPYF2dFhT_(Rn7#AW^Cvvju!YJCpw{w%>C#+ea_!-72pFo)P>f-1>80;A8r64w zr3p5BznZ;Y88w@|U(MdH>Vd!mgL>5wjVn~WBqrss#VX`GDXFR8Ke>N+B4`!U1?hIS zq!koBRUaRoQF6Qb;>@z%Es5oyW2-oFp4~%i!>NtlQ7?+VX1f6^1(Tv8Up=#BfR^+X zFePQ31!(Kqr|+Q8az3w^!)xRt?_L_Q#d@>JN&A2pv>=0H$QIcJqGRPVHLJ2-W2!W! z%6^zCAt#ms!SQlxV}E~9s_r$K73BUf2XgDYa}B?WJj((#C}qXF6*}J$6e}F+#Nu|b zL)nYkdNJEok84rUNJ9|xyUd5`tSdkA+sTU9#ktOR=~~22{k?wxJ6Cn;@ycSXVSk{C zn99msUqlWenO740sc-M@<~&i2E@v3NI(4>d0*As_^g5mIA&h@5<9z9Pg;(bT^Uy@N zlzFH-(knEF=Hu9-jG9zoscteg$MDgnu-r`@-yv(Eni+Q)+(28z*Ad`TatWb`$8m+o zoN4OH-)A!)u_BQgy?Vq~m!EcFr6?{(7d z0$`O_`UVc&7>+# zpnV7cX;~UcLD%~D894IIm{HGxOzs>*M+`e?gL!fwX{mpzyE+235q_t7bX%HQri#XM^uZBUqezt>Xp#Q3Os=9J1g2kG4%1&~3$_izWwaZi&z}M(+ z{Sc*QN9>{Rh;0z!s&}foKkEY~n9Ses*)IhPQFVXCu6b#4++cTtSZ=63PHa1S@?g;5W+gGza9n9<1AwNSIi`#bLO&P0kd-=y&(vy2vM$OV=<1|2NC2<*9{rpWsa zFsOf^DN%maXT9-qON9kd*JBuZXkpPgfiGkv)g?lhT- zm*iyi>|`L1urK zScYABylx--lq~mF3c|+4wN(50NnWc)K2h%ud6GnJIa%vt)s%)wv{ywT7ji?G(K(U- zKg#%5_ibz&PSlc$~*jYkV|S_jxGH*Pj- z#uYT8>YhZY3*I3nva`ynLs7qwE-bD2!lrg|Tyvw^8r7Dk+8U5;KsEqb)g7Jza$y2( z{7)6nI*ZVbV0XRE27ep;ZSc3j-+jVg)djQ?!amUZ@Fw^Q;2Wc|8{|zk*uZ~o1G^3E zHn6)tu-osbE*F;gb&X)ntU09p!Q z<8tdYvJv!=+%1OEnI}cmK!Vb4?C*4=jSb#tfxXZI4Foq396)e)i)iD)vSN=WIr0ayg}r>LF6>6S4*cYatKH2$c?i3s*Tm(s2mNI zqq@?69i-DzQby(UN~xT&N9n9CZPRPCO`~o0QQHVEZxl_VXbw}+^fxZ+PQ)2Rs2=xb zMX02@L3KJgiQY7tr_nrnsCiK1>NFbY&^6Ft;|g~Is;hcCD?+6-kVJo?b8PuFs-MR3 zY1GeNs-G3NYjR1n2wB_lE|Xg66=b|sUS-FLY=FyAA9Hu=`q(JiyHhOy*}(>ZMOu)jZdx1G1aJl^Uly`zu24yvDtQQ8> z(s6x&17g6J?*X4+YiT17_zW^s+i5!A?~<+97ftml`A!1>)sQ8-RL-8+Q@J=%EKo@x ze2>`M2N#(PS)92pmL3ckqKuGipqPEu8p951heh&5Wix%1%xr(C1?GtP;+8-Zyal{V z;-oeRd{GrJDg>+Yk!^{=rti#Jd%K+*kYcwpm%hzuPZ?cxZFs~0V&ahvAFFY9M5a!b zjuXw{WI1)$$f5UDSXvUOm6aoso6PoALGk_Wk;Lu5z?gZ|i55x0B?A^kKKUO8 z81fuUAQKo($&tB$n6!XALD+=k3z;HdQG(=c$FiN9uu^{;h$gv10Eq<%T@mEZYn!qHn)u1wYd1Wah#$O1y;NNWuf@YZqJBcLAm1Thnr zG09l3|AjCLn1|t)vEhC&!Pyulh}?tvwg}{F$qYh-43*Tepv|*NT)*6ZWu_Q`$ zW91qzzO$Epxf9E?2q`YS*1Wt)K~YPlL}7p(2U!?0WOLJwSbj`;5O8GHiwr=7`bh{B zvVse5B0+0^Au{XC2b>uIo~^1w5%BO4VU?rLKdG*f`soM1Gc(xU5(;FY8!z`&Bvsc9 zuCage7!j+Qg}6>ib*WmB>!XyEwZt3-B6$DDu&zB-O zBYKh)lFSw#{t`gj=8n=l3PSj`GW&1YW?6q`k$k5A-0k&S-QM7Ke>NrVDXXgP3ykr_ zC7B2Zxhl(sbW}di=iA7YXQ(PO#~M?+LV~elu>lu)44X?EdQnWECO;)Hwvto=W1zsDpovW|bt6*UwfO4s<_Drn=>93TNJ~{5RqO0q~??{dvGdu7XE*&=0DY`#4G)+D;zFPTZXhkM#k6^SuRG)IH!(t zVT`!ZA%9$lKjbk~Bjex1?)*7(Fp|~{CT`fi3it+c>*;Dy_Ru9!=%07`(8nxz4g5HEo^@+5FJ4*UnxTXaWxzzAik1(Q{@YWzFqsLqt03fQskO;YO{ zp@M1C`Rs8H(kZ{jG(|r^#Pxrvjg$f7m$OQK#l*^_souEB;lM#YgN~~=@*5AD8+U*z z?U9vO>9mu?mZIDlz{NL11ortKQ%s=u_?DPdL`g@+6#D3R5bNfub_7{^=3RtNTd)QL z6fVF70Y>Xk53qf^a+t*!fm+Mt#!xvO*pOqIFB*1ecsymRr(&pbXy$*u>Nd~Nf(}fn zD)zo4q4u?7k)T^ln67OfkyRV@!7qTxjB>NSu0U-@uQR>ybQvAfG_*oT-Cifhsjfk) z4vnEz*CPgO^U<}crK$uJ3i+>zIIc$G%Bqc>9HjEh9nzwaOQdv zIvh@q(5&A%8dxXjXxc~S(J4Icoy-PP_!wYx z);&54y3oYzQ3O}d?jg3}l=u9!WxSgte_taXdH1|8r{ZIZ|92Z>XVSLMf$Ep9{_=Znv`S$ZkQ!A^JZJDiK09Q4*)lI~VqlH~Mkt@Hgs z|Mlrff7m}B)-a6pp|xq2(lly=ZB%vIvss*+k(5)?bg6%m75d{D6^E5+)KlH@70kf} zGHvLg;K08Mke?uw<3|MN@g|eN6DXh33WFd|LdVBg?@AotIjOCKycnV$nrQhGrMDyK z!MOI3n2Liii&?Te%QLT0Lp&pWJ=mk1mLJZxR^4+I0+UDG8jv=kQZvkIqHDbQo!ZU~ ztTwQ^Q&@j(Fzu?}Q{9{tz`;9Atm>GyqLRP%7`rb?VJ!C3A%xI=j0FvXm9?UwgG5(% zYFakXqI0Xz^MIC(2>C1{Ec;}5s)c+*R3)S6bBnEvJuLg1Ri_E0RMUf-mtZrQtAGaN z^>gj{m*7R|kay6)POjV77fU@08#E%V%@Xm!M00Y2->Vt26UW` z-oje&OIDH9^^k3?shwRYyf1EfF37BXRo!C`7-Z7RttUgbWn>&HP!QZzTeQQzHYvar zQ-GU$L2JR-&B4teJ-#%y^7=Z#%|t%Frt5zb(Ej1$n(KQRnw<`Z1ak-Cx+-rjld^o{ zJy!Kz0}|VIU|&or)eVNOy43ixjW65yvMZexWC(nZw1GWE$iC`Mxlh!)L*6+hgbP3! zfJ{)RwC#MIlG^r4lmrafw!CKDZvA*SOcW*{r^M7FONpCYgWX z7dkC>#OA8Hdii9z#8&&`$7rojLSs}jPhlsT*xFi3PA4;$R`ToU=y)_6TBoP5cXT{F zetk6P4o{DUvl%=(ouOH0HtZZDgrY=-@EpmQUQaD<28jX2$#G}cox;x1ad&DR4PL|7 zM^m#iJc6^%>sigci@iZ_4IzvIG-p#AE;^NO>$IL7T2*W%Yp@(E&Qj5;ES z;xS`n^g6xHQMY^4?cH=vN4?&tbMmLrGJd8$3qJdT`pnqMvg@jmj3|%%j0LF43iTc= z=D>$bNw9m|G8{}|zt)p2R`atrMqfI>6I+XpxIzEiy!{+p#LVA}8TA~<+1h`C3>`7- zpseEwrI3_3L-D{|%D(F6Ly3(O23Zw#BkJqun{`kY74`H@*v1YNPG&E26ppG+eG2EB zA#&^zXeuFF#8v3hGXr5n&Uo?YV-woOsCP0Lo}8R^j!y@J7JoJQ3SDE=?G1;6(@u9d z9CpOxRb-~u9h?k?{o}!~Wt@MHKl;Y#c+fK5i@!b_@csBB==S?3#w{0;w**e@l|Xzz z&PN|vA21QBC;SN?#LxHcQigN_xi}ZUjZx<*N97br-V11I9Yz~=J}Z?k>TG77UoaDO zsldei6&bCV6`irZ4@&B%6eie6D+(CrBFzet8|zYZaemY6K*X-QTum6+TP-m(o}O-> S{(k@f0RR7}{E`;peE|UZq(4jm diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index dcb410ce0056baaef6a154a896b1246547580e6a..5f024f24c2b314b106ed981c82c1a08907ed8b3e 100644 GIT binary patch literal 2579 zcmV+u3hebCiwFP!00000|Li?&bJ{xAe?_C`OLNBo0whiO)+gE9%}ig|8pEY|QcC z2`j2xzyth|t3aa5;Td{AyucQ|2O%kEA;i&9t8?hUJ)T?G12yH2#Dniom=RBd{z+Z1 zIc2T)jx(fS1GcaO(iYTL2F2~|?Sx;malmZ!MxsByad8LYN{PT$2WSEI#_u4TGX;;T zb!Psxkbi`v;(|<|bp^KK;u6Ld^1JB!4H?naSLPGP0fXR*{sas8Jyi5fKgM-V6gfa| z^hY`-Hnh%&>t38&2!!C`06kr^Yb8Jhf+5+uUL0-=|2G&}UUq~Vf9U#FJYKgz6YW@S=i>ksx6 zJfim*9*O6>U@%^s>!weXfxv^KPN!qx&w)46pWe+aJQSRd|C;%L2dDEJ3lql)$s+Dh za36%C8qb)x5-jW|niiNV07OJA8Ic)6EUZET7N6%!64&>g$1QyH18jxUld6E=`vSww z+`4yXVJ50nT%Z-y_xCQ7%2lbc)1-Rfp`tAJxm?HLPP_r}6hLBtf98KlQDYlWyp z1g%W+`vT@h@9mn!|(k&z2dMPDI;%5fDA(3=tG?m7#;M38T=D_G_{Y1JfTrW7@tuc5T=@+xKxj(U5~HbfY4D~emNv+8a4M5&F}4S**|dyYPgFF za?AOOjZ^pvox&pa+@8m@0e30u^<@RA6xU%1#HDP46GTZR!j{eWa2YW!N0-O%p*B&C z8`rpT`{u^=P7AC!WyGaFA=-QqK}|H-9_htjSyx+iX+_2(r>9tb;PS@4F_nWQ#x@u1(ebi>sH@#7Dd zAVJ(~hS3dY!>`i(??d=Mt!d{KMSDrv(}J@j=YGCITm__turiYOA?g|HmJAK6x*)Yi zWrj#CTS)-3=#4t)^^g1c>fjT`CS!yoDyfKh=FK1sBYL$M66>w+O68*pejnw;rO$i}2fOzt(!^O)RyW zvD9;i;Th@JVj*O|(>vWxE#%87>1<-dCN|u+*zmOAeBu&ZAQHYZ38Z=<$t3 zX98D1+%xWZRAss6dHF~{R`d4^Qxf+w%uO-#xr@TPTPxdal`I)QU>kdQcFKbQYcIMT-(@ zOs3F<15D8|O>*+`Nbi3TA|zJsc#YE1inpZJic<`OC*0NwlZ`mgUs4AC(SKH6zGE{-w zq!-l0driFelAYS4f|LEFKbJM%Wu4k?_3BF98o7~Q0`*INXt&8>?wauIg72B-vX|$N zHHN4$M0;n53d(A3&Xl#6A z<6n`D?-o?fq%2~ccykF{FXUXB+ein}3?h&zeY6c+qUBm(nYd%@Q&^qDK9rbPcmC6b^NWs_^> zkr`6t4iG_Oct8?t&K!vpM_P%J2~nUwC?fBWW>hC|H&{pZd)}OF3bj7=j*pAZw2TM# z-f_aKzg1a24P6{)2+AhwI6s4{nEf{vEQHC7a*u!}UkKXdqnodf6$`T3e-U|iZWLBU zHcUVkvLFywYcUxPV;-n^mL1bLx6g_h`pUme+rIC#?J#_5cN^CH71lf{NXFgj;mcRSmv0M#vBjQLeSRf|%MZvr;(yO>{EZBupXTerv?TU?=3MkZhu1-; z(*tB)7Ww7yYYpLzZ>G>^^u3EB<_<^r8$?8Hv^(Yf~6|o?JLBh`mhp zVx1Qw5|DM@ugiNS;<=yc$~_D}MmBX?XVj^YF;CB-xi8S9&P%<8_Y&W4*xR4v+Oq4?CJ3cghMmpNIBOJtW1V{-EqA|7>x=?xn#$0&d zpwSEw^l$TnkQO57j5}bXHxE;4PZ4Df)l<}Cc!8^^t-n3=iG2r7JcmBvFr?su#uCh--Ht`#w>R$l`Rgr;WxLg)`HZzk1&F7rB2O7EDzLO|min>TmK>#8G p5Hn{j5{ZDZ@Py_UneDZuN=`Z_cs{?G|2F^t|NpL25vosm008}t0%8CF delta 2573 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 diff --git a/cli/init_test.go b/cli/init_test.go new file mode 100644 index 000000000..8c343bcfa --- /dev/null +++ b/cli/init_test.go @@ -0,0 +1,9 @@ +package cli + +import ( + logging "github.com/ipfs/go-log/v2" +) + +func init() { + logging.SetLogLevel("watchdog", "ERROR") +} diff --git a/cli/multisig.go b/cli/multisig.go index f6caa6ee0..6ec0dcd5d 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -95,11 +95,13 @@ var msigCreateCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("multisigs must have at least one signer")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) var addrs []address.Address @@ -146,13 +148,20 @@ var msigCreateCmd = &cli.Command{ gp := types.NewInt(1) - msgCid, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp) + proto, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -364,11 +373,13 @@ var msigProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must either pass three or five arguments")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -426,14 +437,21 @@ var msigProposeCmd = &cli.Command{ return fmt.Errorf("actor %s is not a multisig actor", msig) } - msgCid, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params) + proto, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("send proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -481,11 +499,13 @@ var msigApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("usage: msig approve [ ]")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -515,10 +535,17 @@ var msigApproveCmd = &cli.Command{ var msgCid cid.Cid if cctx.Args().Len() == 2 { - msgCid, err = api.MsigApprove(ctx, msig, txid, from) + proto, err := api.MsigApprove(ctx, msig, txid, from) if err != nil { return err } + + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid = sm.Cid() } else { proposer, err := address.NewFromString(cctx.Args().Get(2)) if err != nil { @@ -558,15 +585,22 @@ var msigApproveCmd = &cli.Command{ params = p } - msgCid, err = api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params) + proto, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params) if err != nil { return err } + + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid = sm.Cid() } fmt.Println("sent approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -598,11 +632,13 @@ var msigRemoveProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -630,14 +666,21 @@ var msigRemoveProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold")) + proto, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold")) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent remove proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -676,11 +719,13 @@ var msigAddProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -708,14 +753,21 @@ var msigAddProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold")) + proto, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold")) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -743,11 +795,13 @@ var msigAddApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, new signer address, whether to increase threshold")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -790,14 +844,21 @@ var msigAddApproveCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc) + proto, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent add approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -825,11 +886,13 @@ var msigAddCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, new signer address, whether to increase threshold")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -867,14 +930,21 @@ var msigAddCancelCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc) + proto, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent add cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -902,11 +972,13 @@ var msigSwapProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -939,14 +1011,21 @@ var msigSwapProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd) + proto, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -974,11 +1053,13 @@ var msigSwapApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1021,14 +1102,21 @@ var msigSwapApproveCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd) + proto, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1056,11 +1144,13 @@ var msigSwapCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1098,14 +1188,21 @@ var msigSwapCancelCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd) + proto, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1133,11 +1230,13 @@ var msigLockProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1185,14 +1284,21 @@ var msigLockProposeCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1220,11 +1326,13 @@ var msigLockApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, tx id, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1282,14 +1390,21 @@ var msigLockApproveCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1317,11 +1432,13 @@ var msigLockCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1374,14 +1491,21 @@ var msigLockCancelCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1471,11 +1595,13 @@ var msigProposeThresholdCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and new threshold value")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1511,14 +1637,21 @@ var msigProposeThresholdCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params) + proto, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params) if err != nil { return fmt.Errorf("failed to propose change of threshold: %w", err) } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent change threshold proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } diff --git a/cli/send.go b/cli/send.go index 4056a2d61..9efed458a 100644 --- a/cli/send.go +++ b/cli/send.go @@ -147,12 +147,12 @@ var sendCmd = &cli.Command{ } msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force")) if xerrors.Is(err, ErrCheckFailed) { - proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) + proto, err := resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) if err != nil { return xerrors.Errorf("from UI: %w", err) } - msg, _, err = srv.PublishMessage(ctx, proto, true) + msg, _, err = srv.PublishMessage(ctx, proto, true) //nolint } if err != nil { diff --git a/cli/sending_ui.go b/cli/sending_ui.go index c05f67a97..d58e93c74 100644 --- a/cli/sending_ui.go +++ b/cli/sending_ui.go @@ -41,17 +41,17 @@ func baseFeeFromHints(hint map[string]interface{}) big.Int { } func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, - proto *types.Message, checkGroups [][]api.MessageCheckStatus, - interactive bool) (*types.Message, error) { + proto *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus, + interactive bool) (*api.MessagePrototype, error) { fmt.Fprintf(printer, "Following checks have failed:\n") - printChecks(printer, checkGroups, proto.Cid()) + printChecks(printer, checkGroups, proto.Message.Cid()) if !interactive { return nil, ErrCheckFailed } if interactive { - if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Cid()); feeCapBad { + if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad { fmt.Fprintf(printer, "Fee of the message can be adjusted\n") if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { var err error @@ -65,7 +65,7 @@ func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, return nil, err } fmt.Fprintf(printer, "Following checks still failed:\n") - printChecks(printer, checks, proto.Cid()) + printChecks(printer, checks, proto.Message.Cid()) } if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { @@ -125,15 +125,15 @@ func isFeeCapProblem(checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) ( return yes, baseFee } -func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*types.Message, error) { +func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount) (*api.MessagePrototype, error) { t, err := imtui.NewTui() if err != nil { return nil, err } - maxFee := big.Mul(proto.GasFeeCap, big.NewInt(proto.GasLimit)) + maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit)) send := false - t.SetScene(ui(baseFee, proto.GasLimit, &maxFee, &send)) + t.SetScene(ui(baseFee, proto.Message.GasLimit, &maxFee, &send)) err = t.Run() if err != nil { @@ -143,7 +143,7 @@ func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*type return nil, fmt.Errorf("aborted by user") } - proto.GasFeeCap = big.Div(maxFee, big.NewInt(proto.GasLimit)) + proto.Message.GasFeeCap = big.Div(maxFee, big.NewInt(proto.Message.GasLimit)) return proto, nil } diff --git a/cli/services.go b/cli/services.go index 6fc4afe58..82d95397b 100644 --- a/cli/services.go +++ b/cli/services.go @@ -22,22 +22,24 @@ import ( //go:generate go run github.com/golang/mock/mockgen -destination=servicesmock_test.go -package=cli -self_package github.com/filecoin-project/lotus/cli . ServicesAPI type ServicesAPI interface { + FullNodeAPI() api.FullNode + GetBaseFee(ctx context.Context) (abi.TokenAmount, error) // MessageForSend creates a prototype of a message based on SendParams - MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) + MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error) // DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON // parameters to bytes of their CBOR encoding DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) - RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) + RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error) // PublishMessage takes in a message prototype and publishes it // before publishing the message, it runs checks on the node, message and mpool to verify that // message is valid and won't be stuck. // if `force` is true, it skips the checks - PublishMessage(ctx context.Context, prototype *types.Message, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) + PublishMessage(ctx context.Context, prototype *api.MessagePrototype, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) // Close ends the session of services and disconnects from RPC, using Services after Close is called // most likely will result in an error @@ -50,6 +52,10 @@ type ServicesImpl struct { closer jsonrpc.ClientCloser } +func (s *ServicesImpl) FullNodeAPI() api.FullNode { + return s.api +} + func (s *ServicesImpl) Close() error { if s.closer == nil { return xerrors.Errorf("Services already closed") @@ -102,15 +108,24 @@ type CheckInfo struct { var ErrCheckFailed = fmt.Errorf("check has failed") -func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) { +func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error) { + if !prototype.ValidNonce { + nonce, err := s.api.MpoolGetNonce(ctx, prototype.Message.From) + if err != nil { + return nil, xerrors.Errorf("mpool get nonce: %w", err) + } + prototype.Message.Nonce = nonce + prototype.ValidNonce = true + } + var outChecks [][]api.MessageCheckStatus - checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{prototype}) + checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{&prototype.Message}) if err != nil { return nil, xerrors.Errorf("message check: %w", err) } outChecks = append(outChecks, checks...) - checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.From) + checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.Message.From) if err != nil { return nil, xerrors.Errorf("pending mpool check: %w", err) } @@ -123,27 +138,30 @@ func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *typ // Errors with ErrCheckFailed if any of the checks fail // First group of checks is related to the message prototype func (s *ServicesImpl) PublishMessage(ctx context.Context, - prototype *types.Message, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { + prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { - gasedMsg, err := s.api.GasEstimateMessageGas(ctx, prototype, nil, types.EmptyTSK) + gasedMsg, err := s.api.GasEstimateMessageGas(ctx, &prototype.Message, nil, types.EmptyTSK) if err != nil { return nil, nil, xerrors.Errorf("estimating gas: %w", err) } - *prototype = *gasedMsg + prototype.Message = *gasedMsg if !force { checks, err := s.RunChecksForPrototype(ctx, prototype) if err != nil { return nil, nil, xerrors.Errorf("running checks: %w", err) } - if len(checks) != 0 { - return nil, checks, ErrCheckFailed + for _, chks := range checks { + for _, c := range chks { + if !c.OK { + return nil, checks, ErrCheckFailed + } + } } } - //TODO: message prototype needs to have "IsNonceSet" - if prototype.Nonce != 0 { - sm, err := s.api.WalletSignMessage(ctx, prototype.From, prototype) + if prototype.ValidNonce { + sm, err := s.api.WalletSignMessage(ctx, prototype.Message.From, &prototype.Message) if err != nil { return nil, nil, err } @@ -155,7 +173,7 @@ func (s *ServicesImpl) PublishMessage(ctx context.Context, return sm, nil, nil } - sm, err := s.api.MpoolPushMessage(ctx, prototype, nil) + sm, err := s.api.MpoolPushMessage(ctx, &prototype.Message, nil) if err != nil { return nil, nil, err } @@ -177,7 +195,7 @@ type SendParams struct { Params []byte } -func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) { +func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error) { if params.From == address.Undef { defaddr, err := s.api.WalletDefaultAddress(ctx) if err != nil { @@ -186,7 +204,7 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (* params.From = defaddr } - msg := &types.Message{ + msg := types.Message{ From: params.From, To: params.To, Value: params.Val, @@ -210,9 +228,15 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (* } else { msg.GasLimit = 0 } + validNonce := false if params.Nonce != nil { msg.Nonce = *params.Nonce + validNonce = true } - return msg, nil + prototype := &api.MessagePrototype{ + Message: msg, + ValidNonce: validNonce, + } + return prototype, nil } diff --git a/cli/services_send_test.go b/cli/services_send_test.go index faa052e0c..3437e90d9 100644 --- a/cli/services_send_test.go +++ b/cli/services_send_test.go @@ -7,12 +7,10 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks" types "github.com/filecoin-project/lotus/chain/types" gomock "github.com/golang/mock/gomock" - cid "github.com/ipfs/go-cid" "github.com/stretchr/testify/assert" ) @@ -61,22 +59,23 @@ func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) { return srvcs, mockApi } -func fakeSign(msg *types.Message) *types.SignedMessage { - return &types.SignedMessage{ - Message: *msg, - Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, - } -} +// linter doesn't like dead code, so these are commented out. +// func fakeSign(msg *types.Message) *types.SignedMessage { +// return &types.SignedMessage{ +// Message: *msg, +// Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, +// } +// } -func makeMessageSigner() (*cid.Cid, interface{}) { - smCid := cid.Undef - return &smCid, - func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { - sm := fakeSign(msg) - smCid = sm.Cid() - return sm, nil - } -} +// func makeMessageSigner() (*cid.Cid, interface{}) { +// smCid := cid.Undef +// return &smCid, +// func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { +// sm := fakeSign(msg) +// smCid = sm.Cid() +// return sm, nil +// } +// } type MessageMatcher SendParams @@ -84,11 +83,13 @@ var _ gomock.Matcher = MessageMatcher{} // Matches returns whether x is a match. func (mm MessageMatcher) Matches(x interface{}) bool { - m, ok := x.(*types.Message) + proto, ok := x.(*api.MessagePrototype) if !ok { return false } + m := &proto.Message + if mm.From != address.Undef && mm.From != m.From { return false } diff --git a/cli/servicesmock_test.go b/cli/servicesmock_test.go index 4d1f589cd..0a353c153 100644 --- a/cli/servicesmock_test.go +++ b/cli/servicesmock_test.go @@ -67,6 +67,20 @@ func (mr *MockServicesAPIMockRecorder) DecodeTypedParamsFromJSON(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeTypedParamsFromJSON", reflect.TypeOf((*MockServicesAPI)(nil).DecodeTypedParamsFromJSON), arg0, arg1, arg2, arg3) } +// FullNodeAPI mocks base method +func (m *MockServicesAPI) FullNodeAPI() api.FullNode { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullNodeAPI") + ret0, _ := ret[0].(api.FullNode) + return ret0 +} + +// FullNodeAPI indicates an expected call of FullNodeAPI +func (mr *MockServicesAPIMockRecorder) FullNodeAPI() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullNodeAPI", reflect.TypeOf((*MockServicesAPI)(nil).FullNodeAPI)) +} + // GetBaseFee mocks base method func (m *MockServicesAPI) GetBaseFee(arg0 context.Context) (big.Int, error) { m.ctrl.T.Helper() @@ -83,10 +97,10 @@ func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call } // MessageForSend mocks base method -func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*types.Message, error) { +func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MessageForSend", arg0, arg1) - ret0, _ := ret[0].(*types.Message) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -98,7 +112,7 @@ func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *g } // PublishMessage mocks base method -func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *types.Message, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { +func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PublishMessage", arg0, arg1, arg2) ret0, _ := ret[0].(*types.SignedMessage) @@ -114,7 +128,7 @@ func (mr *MockServicesAPIMockRecorder) PublishMessage(arg0, arg1, arg2 interface } // RunChecksForPrototype mocks base method -func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *types.Message) ([][]api.MessageCheckStatus, error) { +func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *api.MessagePrototype) ([][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RunChecksForPrototype", arg0, arg1) ret0, _ := ret[0].([][]api.MessageCheckStatus) diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index 084218b24..fa1004df3 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -18,6 +18,8 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/xerrors" + "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" @@ -102,7 +104,28 @@ func TestWalletMsig(t *testing.T) { // Create an msig with three of the addresses and threshold of two sigs msigAddrs := walletAddrs[:3] amt := types.NewInt(1000) - addProposal, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0)) + proto, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0)) + require.NoError(t, err) + + doSend := func(proto *api.MessagePrototype) (cid.Cid, error) { + if proto.ValidNonce { + sm, err := lite.WalletSignMessage(ctx, proto.Message.From, &proto.Message) + if err != nil { + return cid.Undef, err + } + + return lite.MpoolPush(ctx, sm) + } + + sm, err := lite.MpoolPushMessage(ctx, &proto.Message, nil) + if err != nil { + return cid.Undef, err + } + + return sm.Cid(), nil + } + + addProposal, err := doSend(proto) require.NoError(t, err) res, err := lite.StateWaitMsg(ctx, addProposal, 1, api.LookbackNoLimit, true) @@ -122,7 +145,10 @@ func TestWalletMsig(t *testing.T) { require.Less(t, msigBalance.Int64(), amt.Int64()) // Propose to add a new address to the msig - addProposal, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false) + proto, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + addProposal, err = doSend(proto) require.NoError(t, err) res, err = lite.StateWaitMsg(ctx, addProposal, 1, api.LookbackNoLimit, true) @@ -136,7 +162,10 @@ func TestWalletMsig(t *testing.T) { // Approve proposal (proposer is first (implicit) signer, approver is // second signer txnID := uint64(proposeReturn.TxnID) - approval1, err := lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false) + proto, err = lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + approval1, err := doSend(proto) require.NoError(t, err) res, err = lite.StateWaitMsg(ctx, approval1, 1, api.LookbackNoLimit, true) diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index 426827ad2..1b0b57ca0 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -67,11 +67,13 @@ var verifRegAddVerifierCmd = &cli.Command{ return err } - api, closer, err := lcli.GetFullNodeAPI(cctx) + srv, err := lcli.GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := lcli.ReqContext(cctx) vrk, err := api.StateVerifiedRegistryRootKey(ctx, types.EmptyTSK) @@ -79,14 +81,21 @@ var verifRegAddVerifierCmd = &cli.Command{ return err } - smsg, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.AddVerifier), params) + proto, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.AddVerifier), params) if err != nil { return err } - fmt.Printf("message sent, now waiting on cid: %s\n", smsg) + sm, _, err := srv.PublishMessage(ctx, proto, false) + if err != nil { + return err + } - mwait, err := api.StateWaitMsg(ctx, smsg, build.MessageConfidence) + msgCid := sm.Cid() + + fmt.Printf("message sent, now waiting on cid: %s\n", msgCid) + + mwait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence) if err != nil { return err } diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index e90ba7d6a..3c5356a56 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -82,9 +82,6 @@ * [MpoolBatchPush](#MpoolBatchPush) * [MpoolBatchPushMessage](#MpoolBatchPushMessage) * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) - * [MpoolCheckMessages](#MpoolCheckMessages) - * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) - * [MpoolCheckReplaceMessages](#MpoolCheckReplaceMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -1997,51 +1994,6 @@ Inputs: Response: `null` -### MpoolCheckMessages -MpoolCheckMessages performs logical checks on a batch of messages - - -Perms: read - -Inputs: -```json -[ - null -] -``` - -Response: `null` - -### MpoolCheckPendingMessages -MpoolCheckPendingMessages performs logical checks for all pending messages from a given address - - -Perms: read - -Inputs: -```json -[ - "f01234" -] -``` - -Response: `null` - -### MpoolCheckReplaceMessages -MpoolCheckMessages performs logical checks on pending messages with replacement - - -Perms: read - -Inputs: -```json -[ - null -] -``` - -Response: `null` - ### MpoolClear MpoolClear clears pending messages from the mpool diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index a0ee6fcf3..83c2d6d41 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -82,6 +82,9 @@ * [MpoolBatchPush](#MpoolBatchPush) * [MpoolBatchPushMessage](#MpoolBatchPushMessage) * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) + * [MpoolCheckMessages](#MpoolCheckMessages) + * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) + * [MpoolCheckReplaceMessages](#MpoolCheckReplaceMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -1993,6 +1996,51 @@ Inputs: Response: `null` +### MpoolCheckMessages +MpoolCheckMessages performs logical checks on a batch of messages + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + +### MpoolCheckPendingMessages +MpoolCheckPendingMessages performs logical checks for all pending messages from a given address + + +Perms: read + +Inputs: +```json +[ + "f01234" +] +``` + +Response: `null` + +### MpoolCheckReplaceMessages +MpoolCheckReplaceMessages performs logical checks on pending messages with replacement + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + ### MpoolClear MpoolClear clears pending messages from the mpool @@ -2326,7 +2374,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2352,7 +2415,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2377,7 +2455,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2400,7 +2493,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2432,7 +2540,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2460,7 +2583,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2487,7 +2625,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2624,7 +2777,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2651,7 +2819,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2678,7 +2861,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2704,7 +2902,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2729,7 +2942,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` diff --git a/node/impl/full/multisig.go b/node/impl/full/multisig.go index 9c5f683c4..e44509d7c 100644 --- a/node/impl/full/multisig.go +++ b/node/impl/full/multisig.go @@ -14,7 +14,6 @@ import ( multisig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig" - "github.com/ipfs/go-cid" "go.uber.org/fx" "golang.org/x/xerrors" ) @@ -37,134 +36,129 @@ func (a *MsigAPI) messageBuilder(ctx context.Context, from address.Address) (mul // TODO: remove gp (gasPrice) from arguments // TODO: Add "vesting start" to arguments. -func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) { +func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (*api.MessagePrototype, error) { mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } msg, err := mb.Create(addrs, req, 0, duration, val) if err != nil { - return cid.Undef, err + return nil, err } - // send the message out to the network - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } msg, err := mb.Propose(msig, to, amt, abi.MethodNum(method), params) if err != nil { - return cid.Undef, xerrors.Errorf("failed to create proposal: %w", err) + return nil, xerrors.Errorf("failed to create proposal: %w", err) } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to push message: %w", err) - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigApproveTxnHash(ctx, msig, txID, proposer, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigCancel(ctx, msig, txID, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigApproveTxnHash(ctx, msig, txID, proposer, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigCancel(ctx, msig, txID, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (*api.MessagePrototype, error) { return a.msigApproveOrCancelSimple(ctx, api.MsigApprove, msig, txID, src) } -func (a *MsigAPI) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { return a.msigApproveOrCancelTxnHash(ctx, api.MsigApprove, msig, txID, proposer, to, amt, src, method, params) } -func (a *MsigAPI) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { return a.msigApproveOrCancelTxnHash(ctx, api.MsigCancel, msig, txID, src, to, amt, src, method, params) } -func (a *MsigAPI) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) { +func (a *MsigAPI) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (*api.MessagePrototype, error) { enc, actErr := serializeRemoveParams(toRemove, decrease) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, types.NewInt(0), proposer, uint64(multisig.Methods.RemoveSigner), enc) } -func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { +func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, src address.Address) (*api.MessagePrototype, error) { if msig == address.Undef { - return cid.Undef, xerrors.Errorf("must provide multisig address") + return nil, xerrors.Errorf("must provide multisig address") } if src == address.Undef { - return cid.Undef, xerrors.Errorf("must provide source address") + return nil, xerrors.Errorf("must provide source address") } mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } var msg *types.Message @@ -174,34 +168,31 @@ func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.M case api.MsigCancel: msg, err = mb.Cancel(msig, txID, nil) default: - return cid.Undef, xerrors.Errorf("Invalid operation for msigApproveOrCancel") + return nil, xerrors.Errorf("Invalid operation for msigApproveOrCancel") } if err != nil { - return cid.Undef, err + return nil, err } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil - + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { if msig == address.Undef { - return cid.Undef, xerrors.Errorf("must provide multisig address") + return nil, xerrors.Errorf("must provide multisig address") } if src == address.Undef { - return cid.Undef, xerrors.Errorf("must provide source address") + return nil, xerrors.Errorf("must provide source address") } if proposer.Protocol() != address.ID { proposerID, err := a.StateAPI.StateLookupID(ctx, proposer, types.EmptyTSK) if err != nil { - return cid.Undef, err + return nil, err } proposer = proposerID } @@ -216,7 +207,7 @@ func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api. mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } var msg *types.Message @@ -226,18 +217,16 @@ func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api. case api.MsigCancel: msg, err = mb.Cancel(msig, txID, &p) default: - return cid.Undef, xerrors.Errorf("Invalid operation for msigApproveOrCancel") + return nil, xerrors.Errorf("Invalid operation for msigApproveOrCancel") } if err != nil { - return cid.Undef, err + return nil, err } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } func serializeAddParams(new address.Address, inc bool) ([]byte, error) {