diff --git a/backend/stakepoold/rpc/api.proto b/backend/stakepoold/rpc/api.proto index 63402863..07f98c1e 100644 --- a/backend/stakepoold/rpc/api.proto +++ b/backend/stakepoold/rpc/api.proto @@ -14,6 +14,7 @@ service StakepooldService { rpc ValidateAddress (ValidateAddressRequest) returns (ValidateAddressResponse); rpc AddMissingTicket (AddMissingTicketRequest) returns (AddMissingTicketResponse); rpc GetTickets (GetTicketsRequest) returns (GetTicketsResponse); + rpc GetTicketInfo (GetTicketInfoRequest) returns (GetTicketInfoResponse); rpc ListScripts (ListScriptsRequest) returns (ListScriptsResponse); rpc AccountSyncAddressIndex (AccountSyncAddressIndexRequest) returns (AccountSyncAddressIndexResponse); rpc CreateMultisig (CreateMultisigRequest) returns (CreateMultisigResponse); @@ -39,6 +40,15 @@ message GetLiveTicketsResponse { repeated Ticket tickets = 1; } +message GetTicketInfoRequest { + string Hash = 1; +} +message GetTicketInfoResponse { + string MultiSigAddress = 1; + string VspFeeAddress = 2; + string OwnerFeeAddress = 3; +} + message SetAddedLowFeeTicketsRequest { repeated Ticket tickets = 1; } diff --git a/backend/stakepoold/rpc/rpcserver/context.go b/backend/stakepoold/rpc/rpcserver/context.go index 5433ceac..6d8284d7 100644 --- a/backend/stakepoold/rpc/rpcserver/context.go +++ b/backend/stakepoold/rpc/rpcserver/context.go @@ -16,6 +16,7 @@ import ( wallettypes "github.com/decred/dcrwallet/rpc/jsonrpc/types" "github.com/decred/dcrd/rpcclient/v3" + "github.com/decred/dcrd/txscript" "github.com/decred/dcrd/wire" "github.com/decred/dcrstakepool/backend/stakepoold/userdata" "github.com/decred/dcrwallet/wallet/v2/txrules" @@ -99,6 +100,12 @@ type ticketMetadata struct { err error // log errors along the way } +type TicketInfo struct { + MultiSigAddress string + VspFeeAddress string + OwnerFeeAddress string +} + // EvaluateStakePoolTicket evaluates a voting service ticket to see if it's // acceptable to the voting service. The ticket must pay out to the voting // service cold wallet, and must have a sufficient fee. @@ -369,6 +376,49 @@ func (ctx *AppContext) GetTickets(includeImmature bool) ([]*chainhash.Hash, erro return tickets, nil } +func (ctx *AppContext) GetTicketInfo(ticketHash string) (ticketInfo *TicketInfo, err error) { + hash, err := chainhash.NewHashFromStr(ticketHash) + if err != nil { + log.Errorf("GetTicketInfo: Failed to parse ticket hash: %v", err) + return + } + + res, err := ctx.WalletConnection.GetTransaction(hash) + if err != nil { + log.Errorf("GetTicketInfo: GetTransaction rpc failed: %v", err) + return + } + + // get txout addresses using tx hes + msgTx, err := MsgTxFromHex(res.Hex) + if err != nil { + return nil, fmt.Errorf("GetTicketInfo: MsgTxFromHex failed for %v: %v", res.Hex, err) + } + + p2shOut := msgTx.TxOut[0] + _, addrs, _, _ := txscript.ExtractPkScriptAddrs(p2shOut.Version, p2shOut.PkScript, ctx.Params) + multiSigAddress := addrs[0] + + vspCommitmentOut := msgTx.TxOut[1] + vspCommitAddr, err := stake.AddrFromSStxPkScrCommitment(vspCommitmentOut.PkScript, ctx.Params) + if err != nil { + return nil, fmt.Errorf("GetTicketInfo: Failed to parse commit out addr: %s", err.Error()) + } + + ownerCommitmentOut := msgTx.TxOut[3] + ownerCommitAddr, err := stake.AddrFromSStxPkScrCommitment(ownerCommitmentOut.PkScript, ctx.Params) + if err != nil { + return nil, fmt.Errorf("GetTicketInfo: Failed to parse commit out addr: %s", err.Error()) + } + + ticketInfo = &TicketInfo{ + MultiSigAddress: multiSigAddress.EncodeAddress(), + VspFeeAddress: vspCommitAddr.EncodeAddress(), + OwnerFeeAddress: ownerCommitAddr.EncodeAddress(), + } + return +} + func (ctx *AppContext) StakePoolUserInfo(multisigAddress string) (*wallettypes.StakePoolUserInfoResult, error) { decodedMultisig, err := dcrutil.DecodeAddress(multisigAddress) if err != nil { diff --git a/backend/stakepoold/rpc/rpcserver/server.go b/backend/stakepoold/rpc/rpcserver/server.go index f9d3b87a..031efa97 100644 --- a/backend/stakepoold/rpc/rpcserver/server.go +++ b/backend/stakepoold/rpc/rpcserver/server.go @@ -186,6 +186,19 @@ func (s *stakepooldServer) GetTickets(ctx context.Context, req *pb.GetTicketsReq return &pb.GetTicketsResponse{Tickets: ticketBytes}, nil } +func (s *stakepooldServer) GetTicketInfo(ctx context.Context, req *pb.GetTicketInfoRequest) (*pb.GetTicketInfoResponse, error) { + ticketInfo, err := s.appContext.GetTicketInfo(req.Hash) + if err != nil { + return nil, err + } + + return &pb.GetTicketInfoResponse{ + OwnerFeeAddress: ticketInfo.OwnerFeeAddress, + VspFeeAddress: ticketInfo.VspFeeAddress, + MultiSigAddress: ticketInfo.MultiSigAddress, + }, nil +} + func (s *stakepooldServer) AddMissingTicket(ctx context.Context, req *pb.AddMissingTicketRequest) (*pb.AddMissingTicketResponse, error) { err := s.appContext.AddMissingTicket(req.Hash) if err != nil { diff --git a/backend/stakepoold/rpc/stakepoolrpc/api.pb.go b/backend/stakepoold/rpc/stakepoolrpc/api.pb.go index db70391f..6685a6b5 100644 --- a/backend/stakepoold/rpc/stakepoolrpc/api.pb.go +++ b/backend/stakepoold/rpc/stakepoolrpc/api.pb.go @@ -234,6 +234,100 @@ func (m *GetLiveTicketsResponse) GetTickets() []*Ticket { return nil } +type GetTicketInfoRequest struct { + Hash string `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTicketInfoRequest) Reset() { *m = GetTicketInfoRequest{} } +func (m *GetTicketInfoRequest) String() string { return proto.CompactTextString(m) } +func (*GetTicketInfoRequest) ProtoMessage() {} +func (*GetTicketInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{6} +} + +func (m *GetTicketInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTicketInfoRequest.Unmarshal(m, b) +} +func (m *GetTicketInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTicketInfoRequest.Marshal(b, m, deterministic) +} +func (m *GetTicketInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTicketInfoRequest.Merge(m, src) +} +func (m *GetTicketInfoRequest) XXX_Size() int { + return xxx_messageInfo_GetTicketInfoRequest.Size(m) +} +func (m *GetTicketInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTicketInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTicketInfoRequest proto.InternalMessageInfo + +func (m *GetTicketInfoRequest) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +type GetTicketInfoResponse struct { + MultiSigAddress string `protobuf:"bytes,1,opt,name=MultiSigAddress,proto3" json:"MultiSigAddress,omitempty"` + VspFeeAddress string `protobuf:"bytes,2,opt,name=VspFeeAddress,proto3" json:"VspFeeAddress,omitempty"` + OwnerFeeAddress string `protobuf:"bytes,3,opt,name=OwnerFeeAddress,proto3" json:"OwnerFeeAddress,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTicketInfoResponse) Reset() { *m = GetTicketInfoResponse{} } +func (m *GetTicketInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetTicketInfoResponse) ProtoMessage() {} +func (*GetTicketInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{7} +} + +func (m *GetTicketInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTicketInfoResponse.Unmarshal(m, b) +} +func (m *GetTicketInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTicketInfoResponse.Marshal(b, m, deterministic) +} +func (m *GetTicketInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTicketInfoResponse.Merge(m, src) +} +func (m *GetTicketInfoResponse) XXX_Size() int { + return xxx_messageInfo_GetTicketInfoResponse.Size(m) +} +func (m *GetTicketInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTicketInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTicketInfoResponse proto.InternalMessageInfo + +func (m *GetTicketInfoResponse) GetMultiSigAddress() string { + if m != nil { + return m.MultiSigAddress + } + return "" +} + +func (m *GetTicketInfoResponse) GetVspFeeAddress() string { + if m != nil { + return m.VspFeeAddress + } + return "" +} + +func (m *GetTicketInfoResponse) GetOwnerFeeAddress() string { + if m != nil { + return m.OwnerFeeAddress + } + return "" +} + type SetAddedLowFeeTicketsRequest struct { Tickets []*Ticket `protobuf:"bytes,1,rep,name=tickets,proto3" json:"tickets,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -245,7 +339,7 @@ func (m *SetAddedLowFeeTicketsRequest) Reset() { *m = SetAddedLowFeeTick func (m *SetAddedLowFeeTicketsRequest) String() string { return proto.CompactTextString(m) } func (*SetAddedLowFeeTicketsRequest) ProtoMessage() {} func (*SetAddedLowFeeTicketsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{6} + return fileDescriptor_00212fb1f9d3bf1c, []int{8} } func (m *SetAddedLowFeeTicketsRequest) XXX_Unmarshal(b []byte) error { @@ -283,7 +377,7 @@ func (m *SetAddedLowFeeTicketsResponse) Reset() { *m = SetAddedLowFeeTic func (m *SetAddedLowFeeTicketsResponse) String() string { return proto.CompactTextString(m) } func (*SetAddedLowFeeTicketsResponse) ProtoMessage() {} func (*SetAddedLowFeeTicketsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{7} + return fileDescriptor_00212fb1f9d3bf1c, []int{9} } func (m *SetAddedLowFeeTicketsResponse) XXX_Unmarshal(b []byte) error { @@ -314,7 +408,7 @@ func (m *SetUserVotingPrefsResponse) Reset() { *m = SetUserVotingPrefsRe func (m *SetUserVotingPrefsResponse) String() string { return proto.CompactTextString(m) } func (*SetUserVotingPrefsResponse) ProtoMessage() {} func (*SetUserVotingPrefsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{8} + return fileDescriptor_00212fb1f9d3bf1c, []int{10} } func (m *SetUserVotingPrefsResponse) XXX_Unmarshal(b []byte) error { @@ -346,7 +440,7 @@ func (m *SetUserVotingPrefsRequest) Reset() { *m = SetUserVotingPrefsReq func (m *SetUserVotingPrefsRequest) String() string { return proto.CompactTextString(m) } func (*SetUserVotingPrefsRequest) ProtoMessage() {} func (*SetUserVotingPrefsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{9} + return fileDescriptor_00212fb1f9d3bf1c, []int{11} } func (m *SetUserVotingPrefsRequest) XXX_Unmarshal(b []byte) error { @@ -385,7 +479,7 @@ func (m *AddMissingTicketRequest) Reset() { *m = AddMissingTicketRequest func (m *AddMissingTicketRequest) String() string { return proto.CompactTextString(m) } func (*AddMissingTicketRequest) ProtoMessage() {} func (*AddMissingTicketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{10} + return fileDescriptor_00212fb1f9d3bf1c, []int{12} } func (m *AddMissingTicketRequest) XXX_Unmarshal(b []byte) error { @@ -423,7 +517,7 @@ func (m *AddMissingTicketResponse) Reset() { *m = AddMissingTicketRespon func (m *AddMissingTicketResponse) String() string { return proto.CompactTextString(m) } func (*AddMissingTicketResponse) ProtoMessage() {} func (*AddMissingTicketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{11} + return fileDescriptor_00212fb1f9d3bf1c, []int{13} } func (m *AddMissingTicketResponse) XXX_Unmarshal(b []byte) error { @@ -455,7 +549,7 @@ func (m *GetTicketsRequest) Reset() { *m = GetTicketsRequest{} } func (m *GetTicketsRequest) String() string { return proto.CompactTextString(m) } func (*GetTicketsRequest) ProtoMessage() {} func (*GetTicketsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{12} + return fileDescriptor_00212fb1f9d3bf1c, []int{14} } func (m *GetTicketsRequest) XXX_Unmarshal(b []byte) error { @@ -494,7 +588,7 @@ func (m *GetTicketsResponse) Reset() { *m = GetTicketsResponse{} } func (m *GetTicketsResponse) String() string { return proto.CompactTextString(m) } func (*GetTicketsResponse) ProtoMessage() {} func (*GetTicketsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{13} + return fileDescriptor_00212fb1f9d3bf1c, []int{15} } func (m *GetTicketsResponse) XXX_Unmarshal(b []byte) error { @@ -532,7 +626,7 @@ func (m *ListScriptsRequest) Reset() { *m = ListScriptsRequest{} } func (m *ListScriptsRequest) String() string { return proto.CompactTextString(m) } func (*ListScriptsRequest) ProtoMessage() {} func (*ListScriptsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{14} + return fileDescriptor_00212fb1f9d3bf1c, []int{16} } func (m *ListScriptsRequest) XXX_Unmarshal(b []byte) error { @@ -564,7 +658,7 @@ func (m *ListScriptsResponse) Reset() { *m = ListScriptsResponse{} } func (m *ListScriptsResponse) String() string { return proto.CompactTextString(m) } func (*ListScriptsResponse) ProtoMessage() {} func (*ListScriptsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{15} + return fileDescriptor_00212fb1f9d3bf1c, []int{17} } func (m *ListScriptsResponse) XXX_Unmarshal(b []byte) error { @@ -605,7 +699,7 @@ func (m *AccountSyncAddressIndexRequest) Reset() { *m = AccountSyncAddre func (m *AccountSyncAddressIndexRequest) String() string { return proto.CompactTextString(m) } func (*AccountSyncAddressIndexRequest) ProtoMessage() {} func (*AccountSyncAddressIndexRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{16} + return fileDescriptor_00212fb1f9d3bf1c, []int{18} } func (m *AccountSyncAddressIndexRequest) XXX_Unmarshal(b []byte) error { @@ -657,7 +751,7 @@ func (m *AccountSyncAddressIndexResponse) Reset() { *m = AccountSyncAddr func (m *AccountSyncAddressIndexResponse) String() string { return proto.CompactTextString(m) } func (*AccountSyncAddressIndexResponse) ProtoMessage() {} func (*AccountSyncAddressIndexResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{17} + return fileDescriptor_00212fb1f9d3bf1c, []int{19} } func (m *AccountSyncAddressIndexResponse) XXX_Unmarshal(b []byte) error { @@ -691,7 +785,7 @@ func (m *ImportScriptRequest) Reset() { *m = ImportScriptRequest{} } func (m *ImportScriptRequest) String() string { return proto.CompactTextString(m) } func (*ImportScriptRequest) ProtoMessage() {} func (*ImportScriptRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{18} + return fileDescriptor_00212fb1f9d3bf1c, []int{20} } func (m *ImportScriptRequest) XXX_Unmarshal(b []byte) error { @@ -744,7 +838,7 @@ func (m *ImportScriptResponse) Reset() { *m = ImportScriptResponse{} } func (m *ImportScriptResponse) String() string { return proto.CompactTextString(m) } func (*ImportScriptResponse) ProtoMessage() {} func (*ImportScriptResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{19} + return fileDescriptor_00212fb1f9d3bf1c, []int{21} } func (m *ImportScriptResponse) XXX_Unmarshal(b []byte) error { @@ -783,7 +877,7 @@ func (m *StakePoolUserInfoRequest) Reset() { *m = StakePoolUserInfoReque func (m *StakePoolUserInfoRequest) String() string { return proto.CompactTextString(m) } func (*StakePoolUserInfoRequest) ProtoMessage() {} func (*StakePoolUserInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{20} + return fileDescriptor_00212fb1f9d3bf1c, []int{22} } func (m *StakePoolUserInfoRequest) XXX_Unmarshal(b []byte) error { @@ -823,7 +917,7 @@ func (m *StakePoolUserInfoResponse) Reset() { *m = StakePoolUserInfoResp func (m *StakePoolUserInfoResponse) String() string { return proto.CompactTextString(m) } func (*StakePoolUserInfoResponse) ProtoMessage() {} func (*StakePoolUserInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{21} + return fileDescriptor_00212fb1f9d3bf1c, []int{23} } func (m *StakePoolUserInfoResponse) XXX_Unmarshal(b []byte) error { @@ -868,7 +962,7 @@ func (m *WalletInfoRequest) Reset() { *m = WalletInfoRequest{} } func (m *WalletInfoRequest) String() string { return proto.CompactTextString(m) } func (*WalletInfoRequest) ProtoMessage() {} func (*WalletInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{22} + return fileDescriptor_00212fb1f9d3bf1c, []int{24} } func (m *WalletInfoRequest) XXX_Unmarshal(b []byte) error { @@ -903,7 +997,7 @@ func (m *WalletInfoResponse) Reset() { *m = WalletInfoResponse{} } func (m *WalletInfoResponse) String() string { return proto.CompactTextString(m) } func (*WalletInfoResponse) ProtoMessage() {} func (*WalletInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{23} + return fileDescriptor_00212fb1f9d3bf1c, []int{25} } func (m *WalletInfoResponse) XXX_Unmarshal(b []byte) error { @@ -963,7 +1057,7 @@ func (m *ValidateAddressRequest) Reset() { *m = ValidateAddressRequest{} func (m *ValidateAddressRequest) String() string { return proto.CompactTextString(m) } func (*ValidateAddressRequest) ProtoMessage() {} func (*ValidateAddressRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{24} + return fileDescriptor_00212fb1f9d3bf1c, []int{26} } func (m *ValidateAddressRequest) XXX_Unmarshal(b []byte) error { @@ -1003,7 +1097,7 @@ func (m *ValidateAddressResponse) Reset() { *m = ValidateAddressResponse func (m *ValidateAddressResponse) String() string { return proto.CompactTextString(m) } func (*ValidateAddressResponse) ProtoMessage() {} func (*ValidateAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{25} + return fileDescriptor_00212fb1f9d3bf1c, []int{27} } func (m *ValidateAddressResponse) XXX_Unmarshal(b []byte) error { @@ -1049,7 +1143,7 @@ func (m *CreateMultisigRequest) Reset() { *m = CreateMultisigRequest{} } func (m *CreateMultisigRequest) String() string { return proto.CompactTextString(m) } func (*CreateMultisigRequest) ProtoMessage() {} func (*CreateMultisigRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{26} + return fileDescriptor_00212fb1f9d3bf1c, []int{28} } func (m *CreateMultisigRequest) XXX_Unmarshal(b []byte) error { @@ -1089,7 +1183,7 @@ func (m *CreateMultisigResponse) Reset() { *m = CreateMultisigResponse{} func (m *CreateMultisigResponse) String() string { return proto.CompactTextString(m) } func (*CreateMultisigResponse) ProtoMessage() {} func (*CreateMultisigResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{27} + return fileDescriptor_00212fb1f9d3bf1c, []int{29} } func (m *CreateMultisigResponse) XXX_Unmarshal(b []byte) error { @@ -1139,7 +1233,7 @@ func (m *StakePoolUserTicket) Reset() { *m = StakePoolUserTicket{} } func (m *StakePoolUserTicket) String() string { return proto.CompactTextString(m) } func (*StakePoolUserTicket) ProtoMessage() {} func (*StakePoolUserTicket) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{28} + return fileDescriptor_00212fb1f9d3bf1c, []int{30} } func (m *StakePoolUserTicket) XXX_Unmarshal(b []byte) error { @@ -1207,7 +1301,7 @@ func (m *Ticket) Reset() { *m = Ticket{} } func (m *Ticket) String() string { return proto.CompactTextString(m) } func (*Ticket) ProtoMessage() {} func (*Ticket) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{29} + return fileDescriptor_00212fb1f9d3bf1c, []int{31} } func (m *Ticket) XXX_Unmarshal(b []byte) error { @@ -1256,7 +1350,7 @@ func (m *UserVotingConfigEntry) Reset() { *m = UserVotingConfigEntry{} } func (m *UserVotingConfigEntry) String() string { return proto.CompactTextString(m) } func (*UserVotingConfigEntry) ProtoMessage() {} func (*UserVotingConfigEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{30} + return fileDescriptor_00212fb1f9d3bf1c, []int{32} } func (m *UserVotingConfigEntry) XXX_Unmarshal(b []byte) error { @@ -1315,7 +1409,7 @@ func (m *VersionRequest) Reset() { *m = VersionRequest{} } func (m *VersionRequest) String() string { return proto.CompactTextString(m) } func (*VersionRequest) ProtoMessage() {} func (*VersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{31} + return fileDescriptor_00212fb1f9d3bf1c, []int{33} } func (m *VersionRequest) XXX_Unmarshal(b []byte) error { @@ -1352,7 +1446,7 @@ func (m *VersionResponse) Reset() { *m = VersionResponse{} } func (m *VersionResponse) String() string { return proto.CompactTextString(m) } func (*VersionResponse) ProtoMessage() {} func (*VersionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{32} + return fileDescriptor_00212fb1f9d3bf1c, []int{34} } func (m *VersionResponse) XXX_Unmarshal(b []byte) error { @@ -1425,7 +1519,7 @@ func (m *GetStakeInfoRequest) Reset() { *m = GetStakeInfoRequest{} } func (m *GetStakeInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetStakeInfoRequest) ProtoMessage() {} func (*GetStakeInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{33} + return fileDescriptor_00212fb1f9d3bf1c, []int{35} } func (m *GetStakeInfoRequest) XXX_Unmarshal(b []byte) error { @@ -1472,7 +1566,7 @@ func (m *GetStakeInfoResponse) Reset() { *m = GetStakeInfoResponse{} } func (m *GetStakeInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetStakeInfoResponse) ProtoMessage() {} func (*GetStakeInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{34} + return fileDescriptor_00212fb1f9d3bf1c, []int{36} } func (m *GetStakeInfoResponse) XXX_Unmarshal(b []byte) error { @@ -1612,6 +1706,8 @@ func init() { proto.RegisterType((*GetIgnoredLowFeeTicketsResponse)(nil), "stakepoolrpc.GetIgnoredLowFeeTicketsResponse") proto.RegisterType((*GetLiveTicketsRequest)(nil), "stakepoolrpc.GetLiveTicketsRequest") proto.RegisterType((*GetLiveTicketsResponse)(nil), "stakepoolrpc.GetLiveTicketsResponse") + proto.RegisterType((*GetTicketInfoRequest)(nil), "stakepoolrpc.GetTicketInfoRequest") + proto.RegisterType((*GetTicketInfoResponse)(nil), "stakepoolrpc.GetTicketInfoResponse") proto.RegisterType((*SetAddedLowFeeTicketsRequest)(nil), "stakepoolrpc.SetAddedLowFeeTicketsRequest") proto.RegisterType((*SetAddedLowFeeTicketsResponse)(nil), "stakepoolrpc.SetAddedLowFeeTicketsResponse") proto.RegisterType((*SetUserVotingPrefsResponse)(nil), "stakepoolrpc.SetUserVotingPrefsResponse") @@ -1646,97 +1742,101 @@ func init() { func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) } var fileDescriptor_00212fb1f9d3bf1c = []byte{ - // 1435 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x4e, 0xdc, 0x46, - 0x14, 0xd6, 0x02, 0x01, 0xf6, 0xc0, 0x02, 0x19, 0xfe, 0x1c, 0x8b, 0xc0, 0xc6, 0xf9, 0x5b, 0xa5, - 0x0d, 0x55, 0xa9, 0xd4, 0x9b, 0xaa, 0x95, 0x20, 0x3f, 0x64, 0xd5, 0xd0, 0x10, 0x3b, 0xd0, 0x4a, - 0x95, 0x8a, 0x8c, 0x3d, 0x2c, 0xd3, 0x78, 0xc7, 0xae, 0x3d, 0xbb, 0x09, 0xbd, 0xea, 0x03, 0xf4, - 0xb2, 0xf7, 0xbd, 0xee, 0x63, 0xf4, 0x29, 0xfa, 0x3a, 0xd5, 0xcc, 0x1c, 0xef, 0xda, 0xb3, 0xde, - 0x25, 0xc9, 0x9d, 0xcf, 0x37, 0x67, 0xce, 0x9c, 0xff, 0x39, 0x63, 0xa8, 0xfb, 0x09, 0xdb, 0x4d, - 0xd2, 0x58, 0xc4, 0x64, 0x31, 0x13, 0xfe, 0x5b, 0x9a, 0xc4, 0x71, 0x94, 0x26, 0x81, 0xb3, 0x0d, - 0x5b, 0x87, 0x54, 0xec, 0x87, 0x21, 0x0d, 0x5f, 0xc6, 0xef, 0x9e, 0x53, 0xfa, 0x86, 0x05, 0x6f, - 0xa9, 0xc8, 0x5c, 0xfa, 0x5b, 0x8f, 0x66, 0xc2, 0x79, 0x05, 0xb7, 0xc7, 0xac, 0x67, 0x49, 0xcc, - 0x33, 0x4a, 0x76, 0x61, 0x4e, 0x68, 0xc8, 0xaa, 0x35, 0xa7, 0x5b, 0x0b, 0x7b, 0x6b, 0xbb, 0xc5, - 0x03, 0x76, 0x35, 0xbf, 0x9b, 0x33, 0x39, 0x4d, 0xd8, 0x3e, 0xa4, 0xa2, 0xdd, 0xe1, 0x71, 0x3a, - 0xe6, 0xc8, 0xd7, 0xb0, 0x33, 0x96, 0xe3, 0x13, 0x0f, 0xdd, 0x84, 0xf5, 0x43, 0x2a, 0x5e, 0xb2, - 0xbe, 0x79, 0xd6, 0x0b, 0xd8, 0x30, 0x17, 0x3e, 0xf1, 0x88, 0x1f, 0x60, 0xcb, 0x9b, 0xe0, 0xc8, - 0x8f, 0x96, 0xb7, 0x03, 0xb7, 0xbd, 0x49, 0x8e, 0x77, 0xb6, 0xc0, 0xf6, 0xa8, 0x38, 0xc9, 0x68, - 0x7a, 0x1a, 0x0b, 0xc6, 0x3b, 0xc7, 0x29, 0xbd, 0x18, 0xae, 0x72, 0xb8, 0x55, 0xb5, 0xaa, 0x75, - 0x79, 0x0d, 0xa4, 0x97, 0xd1, 0xf4, 0xac, 0xaf, 0x96, 0xce, 0x82, 0x98, 0x5f, 0xb0, 0x0e, 0xaa, - 0x75, 0xb7, 0xac, 0xd6, 0x50, 0xc2, 0x13, 0xc5, 0xf5, 0x8c, 0x8b, 0xf4, 0xca, 0x5d, 0xe9, 0x19, - 0xb0, 0xf3, 0x18, 0x36, 0xf7, 0xc3, 0xf0, 0x88, 0x65, 0x19, 0xe3, 0x1d, 0xb4, 0x05, 0x4f, 0x23, - 0x30, 0xf3, 0xc2, 0xcf, 0x2e, 0xad, 0x5a, 0xb3, 0xd6, 0x5a, 0x74, 0xd5, 0xb7, 0x63, 0x83, 0x35, - 0xca, 0x8e, 0xaa, 0x7f, 0x0b, 0x37, 0x0f, 0xa9, 0x30, 0xdc, 0xd7, 0x82, 0xe5, 0x36, 0x0f, 0xa2, - 0x5e, 0x48, 0xdb, 0xdd, 0xae, 0x2f, 0x7a, 0x29, 0x55, 0xf2, 0xe6, 0x5d, 0x13, 0x76, 0x76, 0x81, - 0x14, 0xb7, 0x63, 0x38, 0x2d, 0x98, 0x7b, 0x53, 0x70, 0xff, 0xa2, 0x9b, 0x93, 0xce, 0x1a, 0x90, - 0x97, 0x2c, 0x13, 0x5e, 0x90, 0xb2, 0x64, 0x98, 0x18, 0x5f, 0xc0, 0x6a, 0x09, 0x1d, 0x8a, 0x41, - 0x28, 0x17, 0x83, 0xa4, 0x73, 0x09, 0xdb, 0xfb, 0x41, 0x10, 0xf7, 0xb8, 0xf0, 0xae, 0x78, 0xb0, - 0x1f, 0x86, 0x29, 0xcd, 0xb2, 0x36, 0x0f, 0xe9, 0xfb, 0xdc, 0x04, 0x0b, 0xe6, 0x90, 0x43, 0xa9, - 0x5e, 0x77, 0x73, 0x92, 0x6c, 0xc0, 0xec, 0x41, 0xea, 0xf3, 0xe0, 0xd2, 0x9a, 0x6a, 0xd6, 0x5a, - 0x0d, 0x17, 0x29, 0xb2, 0x06, 0x37, 0x94, 0x04, 0x6b, 0xba, 0x59, 0x6b, 0x4d, 0xbb, 0x9a, 0x70, - 0xee, 0xc0, 0xce, 0xd8, 0x93, 0xd0, 0x85, 0x0c, 0x56, 0xdb, 0xdd, 0x24, 0x4e, 0x51, 0xff, 0x5c, - 0x83, 0x0d, 0x98, 0xd5, 0x00, 0xc6, 0x02, 0x29, 0x89, 0xbb, 0x34, 0x0b, 0x7c, 0xae, 0xce, 0x9f, - 0x77, 0x91, 0x22, 0x0e, 0x2c, 0xea, 0xaf, 0x17, 0x94, 0x75, 0x2e, 0x05, 0xaa, 0x51, 0xc2, 0x9c, - 0xef, 0x60, 0xad, 0x7c, 0x14, 0x7a, 0xea, 0x01, 0x2c, 0x69, 0x0e, 0xbd, 0x4a, 0x43, 0x75, 0xe6, - 0xb4, 0x6b, 0xa0, 0xce, 0x53, 0xb0, 0x3c, 0x99, 0x70, 0xc7, 0x71, 0x1c, 0xc9, 0x64, 0x6b, 0xf3, - 0x8b, 0xb8, 0x10, 0xf4, 0xa3, 0x5e, 0x24, 0x98, 0xc7, 0x3a, 0x68, 0x26, 0x7a, 0xce, 0x84, 0x9d, - 0x3f, 0x6a, 0x70, 0xab, 0x42, 0x0c, 0xea, 0xf2, 0x4d, 0x39, 0xf8, 0x0b, 0x7b, 0x77, 0xca, 0x49, - 0x5e, 0xda, 0x99, 0x17, 0x22, 0xee, 0x90, 0x86, 0xb4, 0x79, 0xdf, 0x8f, 0x58, 0x98, 0xcb, 0x98, - 0x6a, 0x4e, 0xb7, 0xea, 0xae, 0x81, 0x3a, 0xab, 0x70, 0xf3, 0x47, 0x3f, 0x8a, 0xa8, 0x28, 0x58, - 0xe0, 0xfc, 0x55, 0x03, 0x52, 0x44, 0x51, 0xa1, 0x26, 0x2c, 0x9c, 0xc6, 0x82, 0x9e, 0xd2, 0x34, - 0x63, 0x31, 0x57, 0x46, 0x35, 0xdc, 0x22, 0x24, 0x4d, 0x7f, 0xea, 0xd3, 0x6e, 0xcc, 0x9f, 0xc4, - 0x9c, 0xd3, 0x40, 0xfa, 0x4f, 0xc7, 0xc6, 0x84, 0x89, 0x0d, 0xf3, 0x27, 0x3c, 0x8a, 0x83, 0xb7, - 0x34, 0x54, 0x01, 0x9a, 0x77, 0x07, 0xb4, 0x0c, 0xac, 0xae, 0x52, 0x6b, 0x46, 0x07, 0x56, 0x53, - 0xce, 0x1e, 0x6c, 0x9c, 0x4a, 0xdd, 0x7d, 0x41, 0xd1, 0x83, 0xc5, 0x24, 0x2d, 0xb9, 0x3a, 0x27, - 0x9d, 0xd7, 0xb0, 0x39, 0xb2, 0x07, 0xcd, 0xd9, 0x80, 0xd9, 0x76, 0x76, 0xc4, 0x78, 0x5e, 0x93, - 0x48, 0x91, 0x6d, 0x80, 0xe3, 0xde, 0xf9, 0xf7, 0xf4, 0x4a, 0x6e, 0x50, 0xfa, 0xd7, 0xdd, 0x02, - 0xe2, 0x7c, 0x09, 0xeb, 0x4f, 0x52, 0xea, 0x0b, 0xaa, 0xc2, 0x99, 0xb1, 0x4e, 0xa5, 0x16, 0xd3, - 0x45, 0x2d, 0x4e, 0x61, 0xc3, 0xdc, 0x82, 0x4a, 0xa8, 0x64, 0x0d, 0x29, 0xed, 0x16, 0x52, 0xbc, - 0xee, 0x96, 0xb0, 0xa2, 0xdc, 0xa9, 0xb2, 0x75, 0xff, 0xd4, 0x60, 0xb5, 0x22, 0x0d, 0x54, 0xc9, - 0x08, 0x5f, 0xf4, 0x72, 0x77, 0x20, 0x25, 0x71, 0xcd, 0x81, 0x82, 0x90, 0x92, 0x5a, 0xe8, 0xaf, - 0x42, 0xc9, 0x34, 0xdc, 0x12, 0xa6, 0x9a, 0x48, 0x42, 0xb9, 0x38, 0xb8, 0x52, 0x61, 0xa9, 0xbb, - 0x39, 0x49, 0xee, 0x41, 0x03, 0x3f, 0x71, 0xfb, 0x0d, 0xb5, 0xbd, 0x0c, 0x3a, 0x5f, 0xe7, 0x67, - 0x8f, 0x8f, 0xd6, 0xa0, 0xe9, 0x4e, 0x15, 0x9a, 0xee, 0xdf, 0x35, 0x58, 0xaf, 0xec, 0xe7, 0xd2, - 0x1a, 0x55, 0x34, 0x79, 0x91, 0x22, 0x55, 0x55, 0x80, 0x53, 0x95, 0x05, 0x28, 0xb3, 0x50, 0xa6, - 0xef, 0x01, 0x13, 0x19, 0xb6, 0x89, 0x01, 0x2d, 0xa5, 0xe4, 0xdf, 0x79, 0xc6, 0xcf, 0x28, 0x16, - 0x13, 0x76, 0x56, 0x60, 0x09, 0x3f, 0xf3, 0x02, 0xfa, 0xb7, 0x06, 0xcb, 0x03, 0x08, 0x23, 0x7d, - 0x1f, 0x96, 0xfa, 0x1a, 0x3a, 0xcb, 0x44, 0x2a, 0xb3, 0x5b, 0x1b, 0xdf, 0x40, 0xd4, 0x53, 0xa0, - 0xec, 0x9e, 0x5d, 0xff, 0xd7, 0x38, 0xc5, 0xa6, 0xaa, 0x09, 0x85, 0x32, 0x1e, 0xa7, 0x18, 0x19, - 0x4d, 0x48, 0x34, 0xf1, 0x45, 0x70, 0xa9, 0x14, 0x6b, 0xb8, 0x9a, 0x90, 0xf9, 0x9b, 0xa4, 0x34, - 0xa5, 0x11, 0xf5, 0x33, 0xaa, 0x62, 0x51, 0x77, 0x0b, 0x88, 0x54, 0xe4, 0xbc, 0xc7, 0xa2, 0xf0, - 0xac, 0x4b, 0x85, 0x1f, 0xfa, 0xc2, 0xb7, 0x66, 0xb5, 0x22, 0x0a, 0x3d, 0x42, 0xd0, 0x59, 0x87, - 0xd5, 0x43, 0x2a, 0x54, 0x76, 0x15, 0x7b, 0xc3, 0x9f, 0x33, 0xb0, 0x56, 0xc6, 0x87, 0xdd, 0xe1, - 0x40, 0x16, 0x30, 0xe6, 0x80, 0x0e, 0x49, 0x11, 0x92, 0x8a, 0x3d, 0x65, 0x17, 0x17, 0x2c, 0xe8, - 0x45, 0xe2, 0x4a, 0xd9, 0x57, 0x73, 0x0b, 0x88, 0xca, 0xc2, 0x58, 0xf8, 0x91, 0xd7, 0x3b, 0xcf, - 0x58, 0x78, 0xa5, 0x6c, 0xad, 0xb9, 0x25, 0x4c, 0xe6, 0xda, 0xab, 0x77, 0xfc, 0x88, 0x76, 0x65, - 0x17, 0x7c, 0xc3, 0xde, 0xa3, 0xe9, 0x65, 0x50, 0xc6, 0x75, 0x70, 0xe1, 0xea, 0x64, 0x1c, 0xd0, - 0x32, 0xfb, 0x4e, 0x78, 0x26, 0x53, 0x53, 0xd9, 0xdd, 0x70, 0x73, 0x52, 0xba, 0x53, 0x86, 0x36, - 0xb4, 0xe6, 0xb4, 0x3b, 0x15, 0x21, 0xf9, 0x5d, 0xda, 0x8f, 0x65, 0xa3, 0x9a, 0xd7, 0xfc, 0x48, - 0xca, 0x1e, 0x8b, 0x5b, 0x9f, 0xbd, 0x4f, 0x58, 0x4a, 0x43, 0xab, 0xae, 0x18, 0x0c, 0x54, 0x6a, - 0x23, 0xeb, 0xd3, 0x63, 0xbf, 0x53, 0x0b, 0xb4, 0x36, 0x39, 0x2d, 0xed, 0xd9, 0x8f, 0xa2, 0x82, - 0x3d, 0x0b, 0xda, 0x9e, 0x12, 0x28, 0xeb, 0x42, 0x4e, 0x7b, 0xd6, 0xa2, 0x5a, 0x54, 0xdf, 0xf2, - 0xf4, 0xe3, 0x34, 0x96, 0xf7, 0x11, 0x8b, 0xb9, 0x5a, 0x6d, 0x28, 0x7f, 0x19, 0xa8, 0xac, 0x12, - 0x39, 0xb1, 0xd0, 0xd0, 0x5a, 0xd2, 0xd7, 0xb4, 0xa6, 0xc8, 0x23, 0x58, 0x19, 0x72, 0x22, 0xc7, - 0xb2, 0x92, 0x30, 0x82, 0x4b, 0x1f, 0xe4, 0x26, 0xae, 0x68, 0x1f, 0x20, 0xb9, 0xf7, 0xdf, 0x02, - 0xdc, 0xf4, 0xf2, 0x5b, 0x29, 0xf4, 0x68, 0xda, 0x67, 0x01, 0x25, 0x89, 0x9a, 0x5c, 0x47, 0xc7, - 0x40, 0xf2, 0xa8, 0x7c, 0x85, 0x4d, 0x1a, 0xe2, 0xed, 0xcf, 0x3e, 0x88, 0x17, 0xb3, 0xaf, 0x0f, - 0x9b, 0x63, 0xc6, 0x6f, 0xf2, 0xf9, 0x88, 0x9c, 0x09, 0x73, 0xbc, 0xfd, 0xf8, 0x03, 0xb9, 0xf1, - 0xdc, 0x9f, 0x61, 0xa9, 0x3c, 0x8a, 0x93, 0xbb, 0x23, 0x02, 0x46, 0x27, 0x78, 0xfb, 0xde, 0x64, - 0x26, 0x14, 0x9e, 0xc0, 0xba, 0xf7, 0x21, 0x6e, 0xf4, 0x3e, 0xc2, 0x8d, 0x13, 0xc7, 0x73, 0xd2, - 0x01, 0x32, 0x3a, 0x80, 0x93, 0x87, 0x23, 0x22, 0xaa, 0x47, 0x74, 0xbb, 0x75, 0x3d, 0x23, 0x1e, - 0x74, 0x02, 0x8b, 0xc5, 0x01, 0x8c, 0x18, 0xb3, 0x4d, 0xc5, 0x1c, 0x68, 0x3b, 0x93, 0x58, 0x50, - 0x6c, 0x88, 0xd9, 0x58, 0x1c, 0xa8, 0xc8, 0x83, 0x09, 0x73, 0x53, 0xa1, 0xb5, 0xd9, 0x0f, 0xaf, - 0xe5, 0xc3, 0x53, 0x5e, 0x01, 0x0c, 0xc7, 0x23, 0xb2, 0x53, 0xde, 0x36, 0x32, 0x4e, 0xd9, 0xcd, - 0xf1, 0x0c, 0x28, 0xf0, 0x17, 0x58, 0x36, 0xa6, 0x14, 0x62, 0x64, 0x48, 0xf5, 0xe0, 0x63, 0xdf, - 0xbf, 0x86, 0x0b, 0xe5, 0xfb, 0xb0, 0x62, 0x3e, 0x5c, 0x88, 0xb1, 0x75, 0xcc, 0x3b, 0xc8, 0x7e, - 0x70, 0x1d, 0xdb, 0xd0, 0x27, 0xc3, 0x07, 0x8c, 0xe9, 0x93, 0x91, 0x97, 0x91, 0xe9, 0x93, 0x8a, - 0xb7, 0x8f, 0x0b, 0x0b, 0x85, 0xb7, 0x0c, 0x31, 0x36, 0x8c, 0x3e, 0x7e, 0xec, 0x3b, 0x13, 0x38, - 0x86, 0x5d, 0x62, 0xcc, 0x23, 0xc4, 0xec, 0x12, 0x93, 0x5f, 0x45, 0x66, 0x97, 0xb8, 0xe6, 0x65, - 0x23, 0xbb, 0x44, 0x79, 0xfe, 0x33, 0xbb, 0x44, 0xe5, 0x40, 0x69, 0x76, 0x89, 0x31, 0x23, 0xe4, - 0x09, 0x2c, 0x16, 0x2f, 0x64, 0xb3, 0x94, 0x2a, 0x2e, 0x71, 0xb3, 0x94, 0xaa, 0xee, 0xf3, 0xbd, - 0x9f, 0x06, 0x53, 0x4d, 0xde, 0xd5, 0x9f, 0xc3, 0x5c, 0x3e, 0xe8, 0x6f, 0x19, 0x79, 0x57, 0x1a, - 0x7f, 0xec, 0xdb, 0x63, 0x56, 0xb5, 0xe4, 0xf3, 0x59, 0xf5, 0x4b, 0xe7, 0xab, 0xff, 0x03, 0x00, - 0x00, 0xff, 0xff, 0xcb, 0x80, 0xac, 0x80, 0xdf, 0x11, 0x00, 0x00, + // 1500 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdb, 0x4f, 0xdc, 0x46, + 0x17, 0xd7, 0x02, 0xe1, 0x72, 0xd8, 0x05, 0x32, 0xdc, 0x36, 0x16, 0x01, 0xe2, 0xdc, 0x50, 0xbe, + 0x2f, 0x7c, 0xfa, 0xa8, 0xd4, 0x97, 0xaa, 0x95, 0x20, 0x17, 0xb2, 0x6a, 0x28, 0xc4, 0x0e, 0x34, + 0x52, 0xa5, 0x22, 0x63, 0x0f, 0xcb, 0x34, 0xde, 0xb1, 0x6b, 0xcf, 0x92, 0xd0, 0xa7, 0xfe, 0x01, + 0x7d, 0xa9, 0xd4, 0xf7, 0x3e, 0xf7, 0xb1, 0x7f, 0x42, 0xff, 0xb3, 0x6a, 0x66, 0x8e, 0x77, 0xc7, + 0xb3, 0xde, 0x85, 0xe4, 0xcd, 0xe7, 0x37, 0x67, 0xce, 0x9c, 0xdb, 0x9c, 0x39, 0xc7, 0x30, 0x13, + 0xa4, 0x6c, 0x3b, 0xcd, 0x12, 0x91, 0x90, 0x7a, 0x2e, 0x82, 0xf7, 0x34, 0x4d, 0x92, 0x38, 0x4b, + 0x43, 0x77, 0x1d, 0xd6, 0xf6, 0xa9, 0xd8, 0x8d, 0x22, 0x1a, 0xbd, 0x4e, 0x3e, 0xbc, 0xa4, 0xf4, + 0x2d, 0x0b, 0xdf, 0x53, 0x91, 0x7b, 0xf4, 0xe7, 0x2e, 0xcd, 0x85, 0x7b, 0x08, 0x77, 0x87, 0xac, + 0xe7, 0x69, 0xc2, 0x73, 0x4a, 0xb6, 0x61, 0x4a, 0x68, 0xa8, 0x59, 0xdb, 0x1c, 0xdf, 0x9a, 0xdd, + 0x59, 0xda, 0x36, 0x0f, 0xd8, 0xd6, 0xfc, 0x5e, 0xc1, 0xe4, 0x6e, 0xc2, 0xfa, 0x3e, 0x15, 0xad, + 0x36, 0x4f, 0xb2, 0x21, 0x47, 0xbe, 0x81, 0x8d, 0xa1, 0x1c, 0x9f, 0x79, 0xe8, 0x2a, 0x2c, 0xef, + 0x53, 0xf1, 0x9a, 0x5d, 0xda, 0x67, 0xbd, 0x82, 0x15, 0x7b, 0xe1, 0x33, 0x8f, 0x78, 0x02, 0x4b, + 0xfb, 0x54, 0x68, 0xb4, 0xc5, 0xcf, 0x13, 0x3c, 0x81, 0x10, 0x98, 0x78, 0x15, 0xe4, 0x17, 0xcd, + 0xda, 0x66, 0x6d, 0x6b, 0xc6, 0x53, 0xdf, 0xee, 0xef, 0x35, 0xa5, 0x8f, 0xc9, 0x8c, 0xa7, 0x6e, + 0xc1, 0xfc, 0x41, 0x37, 0x16, 0xcc, 0x67, 0xed, 0xdd, 0x28, 0xca, 0x68, 0x9e, 0xe3, 0x46, 0x1b, + 0x26, 0x0f, 0xa0, 0x71, 0x92, 0xa7, 0x2f, 0x29, 0x2d, 0xf8, 0xc6, 0x14, 0x5f, 0x19, 0x94, 0xf2, + 0x0e, 0x3f, 0x70, 0x9a, 0x19, 0x7c, 0xe3, 0x5a, 0x9e, 0x05, 0xbb, 0xdf, 0xc1, 0x9a, 0x3f, 0x22, + 0x11, 0x3e, 0xd9, 0x1f, 0x1b, 0x70, 0xd7, 0x1f, 0x95, 0x38, 0xee, 0x1a, 0x38, 0x3e, 0x15, 0xc7, + 0x39, 0xcd, 0x4e, 0x12, 0xc1, 0x78, 0xfb, 0x28, 0xa3, 0xe7, 0xfd, 0x55, 0x0e, 0x77, 0xaa, 0x56, + 0xb5, 0x2e, 0x6f, 0x80, 0x74, 0x73, 0x9a, 0x9d, 0x5e, 0xaa, 0xa5, 0xd3, 0x30, 0xe1, 0xe7, 0xac, + 0x8d, 0x6a, 0xdd, 0x2f, 0xab, 0xd5, 0x97, 0xf0, 0x4c, 0x71, 0xbd, 0xe0, 0x22, 0xbb, 0xf2, 0x16, + 0xba, 0x16, 0xec, 0x3e, 0x85, 0xd5, 0xdd, 0x28, 0x3a, 0x60, 0x79, 0xce, 0x78, 0x1b, 0x6d, 0xa9, + 0x88, 0x60, 0x1d, 0x23, 0xe8, 0x40, 0x73, 0x90, 0x1d, 0x55, 0xff, 0x1a, 0x6e, 0xf7, 0x82, 0xdb, + 0x53, 0x79, 0x0b, 0xe6, 0x5b, 0x3c, 0x8c, 0xbb, 0x11, 0x6d, 0x75, 0x3a, 0x81, 0xe8, 0x66, 0x54, + 0xc9, 0x9b, 0xf6, 0x6c, 0xd8, 0xdd, 0x06, 0x62, 0x6e, 0xc7, 0xc4, 0x68, 0xc2, 0xd4, 0x5b, 0xc3, + 0xfd, 0x75, 0xaf, 0x20, 0xdd, 0x25, 0x20, 0xaf, 0x59, 0x2e, 0xfc, 0x30, 0x63, 0x69, 0x3f, 0xb1, + 0xff, 0x07, 0x8b, 0x25, 0xb4, 0x2f, 0x06, 0xa1, 0x42, 0x0c, 0x92, 0xee, 0x05, 0xac, 0xef, 0x86, + 0x61, 0xd2, 0xe5, 0xc2, 0xbf, 0xe2, 0x21, 0x66, 0x45, 0x8b, 0x47, 0xf4, 0x63, 0x61, 0x42, 0x13, + 0xa6, 0x90, 0x03, 0x73, 0xb2, 0x20, 0xc9, 0x0a, 0x4c, 0xee, 0x65, 0x01, 0x0f, 0x2f, 0x54, 0x12, + 0x36, 0x3c, 0xa4, 0xc8, 0x12, 0xdc, 0x52, 0x12, 0x54, 0xce, 0x8d, 0x7b, 0x9a, 0x70, 0xef, 0xc1, + 0xc6, 0xd0, 0x93, 0xd0, 0x85, 0x0c, 0x16, 0x5b, 0x9d, 0x34, 0xc9, 0x50, 0xff, 0x42, 0x83, 0x15, + 0x98, 0xd4, 0x00, 0xc6, 0x02, 0x29, 0x89, 0x7b, 0x34, 0x0f, 0x03, 0xae, 0xce, 0x9f, 0xf6, 0x90, + 0x22, 0x2e, 0xd4, 0xf5, 0xd7, 0x2b, 0xca, 0xda, 0x17, 0x02, 0xd5, 0x28, 0x61, 0xee, 0x37, 0xb0, + 0x54, 0x3e, 0x0a, 0x3d, 0xf5, 0x08, 0xe6, 0x34, 0x87, 0x5e, 0xa5, 0x91, 0x3a, 0x73, 0xdc, 0xb3, + 0x50, 0xf7, 0x39, 0x34, 0x7d, 0x99, 0x70, 0x47, 0x49, 0x12, 0xcb, 0x64, 0x33, 0xef, 0xfe, 0x8d, + 0x6f, 0xb3, 0xfb, 0x6b, 0x0d, 0xee, 0x54, 0x88, 0x41, 0x5d, 0xbe, 0x2a, 0x07, 0x7f, 0x76, 0xe7, + 0x5e, 0x39, 0xc9, 0x4b, 0x3b, 0x8b, 0x8b, 0x88, 0x3b, 0xa4, 0x21, 0x2d, 0x7e, 0x19, 0xc4, 0x2c, + 0x2a, 0x64, 0x8c, 0x6d, 0x8e, 0x6f, 0xcd, 0x78, 0x16, 0xea, 0x2e, 0xc2, 0xed, 0xef, 0x83, 0x38, + 0x2e, 0x55, 0x2f, 0xf7, 0x8f, 0x1a, 0x10, 0x13, 0x45, 0x85, 0x36, 0x61, 0xf6, 0x24, 0x11, 0xf4, + 0x84, 0x66, 0x39, 0x4b, 0xb8, 0x32, 0xaa, 0xe1, 0x99, 0x90, 0x34, 0xfd, 0x79, 0x40, 0x3b, 0x09, + 0x7f, 0x96, 0x70, 0x4e, 0x43, 0xe9, 0x3f, 0x1d, 0x1b, 0x1b, 0x26, 0x0e, 0x4c, 0x1f, 0xf3, 0x38, + 0x09, 0xdf, 0xd3, 0x48, 0x05, 0x68, 0xda, 0xeb, 0xd1, 0x32, 0xb0, 0xfa, 0x96, 0x36, 0x27, 0x74, + 0x60, 0x35, 0xe5, 0xee, 0xc0, 0xca, 0x89, 0xd4, 0x3d, 0x10, 0x45, 0xfd, 0x32, 0x93, 0xb4, 0xe4, + 0xea, 0x82, 0x74, 0xdf, 0xc0, 0xea, 0xc0, 0x1e, 0x34, 0x67, 0x05, 0x26, 0x5b, 0xf9, 0x01, 0xe3, + 0xc5, 0x9d, 0x44, 0x8a, 0xac, 0x03, 0x1c, 0x75, 0xcf, 0xbe, 0xa5, 0x57, 0x72, 0x03, 0x16, 0x58, + 0x03, 0x71, 0xff, 0x0f, 0xcb, 0xcf, 0x32, 0x1a, 0x08, 0xaa, 0xc2, 0x99, 0xb3, 0x76, 0xa5, 0x16, + 0xe3, 0xa6, 0x16, 0x27, 0xb0, 0x62, 0x6f, 0x41, 0x25, 0x54, 0xb2, 0x46, 0x94, 0x76, 0x8c, 0x14, + 0x9f, 0xf1, 0x4a, 0x98, 0x29, 0x77, 0xac, 0x6c, 0xdd, 0x5f, 0x35, 0x58, 0xac, 0x48, 0x03, 0x75, + 0x65, 0x44, 0x20, 0xba, 0x85, 0x3b, 0x90, 0x92, 0xb8, 0xe6, 0x40, 0x41, 0x48, 0x49, 0x2d, 0xf4, + 0x97, 0x71, 0x65, 0x1a, 0x5e, 0x09, 0x53, 0x45, 0x24, 0xa5, 0x5c, 0xec, 0x5d, 0xa9, 0xb0, 0xcc, + 0x78, 0x05, 0x29, 0x1f, 0x25, 0xfc, 0xc4, 0xed, 0xb7, 0xd4, 0xf6, 0x32, 0xe8, 0x7e, 0x59, 0x9c, + 0x3d, 0x3c, 0x5a, 0xbd, 0xa2, 0x3b, 0x66, 0x14, 0xdd, 0x3f, 0x6b, 0xb0, 0x5c, 0x59, 0xcf, 0xa5, + 0x35, 0xea, 0xd2, 0x14, 0x97, 0x14, 0xa9, 0xaa, 0x0b, 0x38, 0x56, 0xfd, 0x9c, 0x3a, 0x30, 0x2d, + 0xd3, 0x77, 0x8f, 0x89, 0x1c, 0xcb, 0x44, 0x8f, 0x96, 0x52, 0x8a, 0xef, 0x22, 0xe3, 0x27, 0x14, + 0x8b, 0x0d, 0xbb, 0x0b, 0x30, 0x87, 0x9f, 0xc5, 0x05, 0xfa, 0xa7, 0x06, 0xf3, 0x3d, 0x08, 0x23, + 0xfd, 0x10, 0xe6, 0x2e, 0x35, 0x74, 0x9a, 0x8b, 0x4c, 0x66, 0xb7, 0x36, 0xbe, 0x81, 0xa8, 0xaf, + 0x40, 0x59, 0x3d, 0x3b, 0xc1, 0x4f, 0x49, 0x86, 0x45, 0x55, 0x13, 0x0a, 0x65, 0x3c, 0xc9, 0x30, + 0x32, 0x9a, 0x90, 0x68, 0x1a, 0x88, 0xf0, 0x42, 0x29, 0xd6, 0xf0, 0x34, 0x21, 0xf3, 0x37, 0xcd, + 0x68, 0x46, 0x63, 0x1a, 0xe4, 0x54, 0xc5, 0x62, 0xc6, 0x33, 0x10, 0xa9, 0xc8, 0x59, 0x97, 0xc5, + 0xd1, 0x69, 0x87, 0x8a, 0x20, 0x0a, 0x44, 0xd0, 0x9c, 0xd4, 0x8a, 0x28, 0xf4, 0x00, 0x41, 0x77, + 0x19, 0x16, 0xf7, 0xa9, 0x50, 0xd9, 0x65, 0xd6, 0x86, 0xdf, 0x26, 0x54, 0xcb, 0x63, 0xe0, 0xfd, + 0xea, 0xb0, 0x27, 0x2f, 0x30, 0xe6, 0x80, 0x0e, 0x89, 0x09, 0x49, 0xc5, 0x9e, 0xb3, 0xf3, 0x73, + 0x16, 0x76, 0x63, 0x71, 0xa5, 0xec, 0xab, 0x79, 0x06, 0xa2, 0xb2, 0x30, 0x11, 0x41, 0xec, 0x77, + 0xcf, 0x72, 0x16, 0x5d, 0x29, 0x5b, 0x6b, 0x5e, 0x09, 0x93, 0xb9, 0x76, 0xf8, 0x81, 0x1f, 0xd0, + 0x8e, 0xac, 0x82, 0x6f, 0xd9, 0x47, 0x34, 0xbd, 0x0c, 0xca, 0xb8, 0xf6, 0x1e, 0x5c, 0x9d, 0x8c, + 0x3d, 0x5a, 0x66, 0xdf, 0x31, 0xcf, 0x65, 0x6a, 0x2a, 0xbb, 0x1b, 0x5e, 0x41, 0x4a, 0x77, 0xca, + 0xd0, 0x46, 0xcd, 0x29, 0xed, 0x4e, 0x45, 0x48, 0x7e, 0x8f, 0x5e, 0x26, 0xb2, 0x50, 0x4d, 0x6b, + 0x7e, 0x24, 0x65, 0x8d, 0xc5, 0xad, 0x2f, 0x3e, 0xa6, 0x2c, 0xa3, 0x51, 0x73, 0x46, 0x31, 0x58, + 0xa8, 0xd4, 0x46, 0xde, 0x4f, 0x9f, 0xfd, 0x42, 0x9b, 0xa0, 0xb5, 0x29, 0x68, 0x69, 0xcf, 0x6e, + 0x1c, 0x1b, 0xf6, 0xcc, 0x6a, 0x7b, 0x4a, 0xa0, 0xbc, 0x17, 0xb2, 0x5b, 0x6d, 0xd6, 0xd5, 0xa2, + 0xfa, 0x96, 0xa7, 0x1f, 0x65, 0x89, 0x7c, 0x8f, 0x58, 0xc2, 0xd5, 0x6a, 0x43, 0xf9, 0xcb, 0x42, + 0xe5, 0x2d, 0x91, 0x1d, 0x0b, 0x8d, 0x9a, 0x73, 0xfa, 0x99, 0xd6, 0x14, 0x79, 0x02, 0x0b, 0x7d, + 0x4e, 0xe4, 0x98, 0x57, 0x12, 0x06, 0x70, 0xe9, 0x83, 0xc2, 0xc4, 0x05, 0xed, 0x03, 0x24, 0x77, + 0xfe, 0xae, 0xc3, 0x6d, 0xbf, 0x78, 0x95, 0x22, 0x9f, 0x66, 0x97, 0x2c, 0xa4, 0x24, 0x55, 0x9d, + 0xee, 0x60, 0x1b, 0x48, 0x9e, 0x94, 0x9f, 0xb0, 0x51, 0x43, 0x88, 0xf3, 0x9f, 0x1b, 0xf1, 0x62, + 0xf6, 0x5d, 0xc2, 0xea, 0x90, 0xf1, 0x81, 0xfc, 0x77, 0x40, 0xce, 0x88, 0x39, 0xc4, 0x79, 0x7a, + 0x43, 0x6e, 0x3c, 0xf7, 0x07, 0x98, 0x2b, 0x8f, 0x12, 0xe4, 0xfe, 0x80, 0x80, 0xc1, 0x09, 0xc4, + 0x79, 0x30, 0x9a, 0x09, 0x85, 0xa7, 0xb0, 0xec, 0xdf, 0xc4, 0x8d, 0xfe, 0x27, 0xb8, 0x71, 0x64, + 0x7b, 0x4e, 0xda, 0x40, 0x06, 0x1b, 0x70, 0xf2, 0x78, 0x40, 0x44, 0x75, 0x8b, 0xee, 0x6c, 0x5d, + 0xcf, 0x88, 0x07, 0x1d, 0x43, 0xdd, 0x6c, 0xc0, 0x88, 0xd5, 0xdb, 0x54, 0xf4, 0x81, 0x8e, 0x3b, + 0x8a, 0x05, 0xc5, 0x46, 0x98, 0x8d, 0x66, 0x43, 0x45, 0x1e, 0x8d, 0xe8, 0x9b, 0x8c, 0xd2, 0xe6, + 0x3c, 0xbe, 0x96, 0x0f, 0x4f, 0x39, 0x04, 0xe8, 0xb7, 0x47, 0x64, 0xa3, 0xbc, 0x6d, 0xa0, 0x9d, + 0x72, 0x36, 0x87, 0x33, 0xa0, 0xc0, 0x1f, 0x61, 0xde, 0xea, 0x52, 0x88, 0x95, 0x21, 0xd5, 0x8d, + 0x8f, 0xf3, 0xf0, 0x1a, 0x2e, 0x94, 0x1f, 0xc0, 0x82, 0x3d, 0xb8, 0x10, 0x6b, 0xeb, 0x90, 0x39, + 0xc8, 0x79, 0x74, 0x1d, 0x5b, 0xdf, 0x27, 0xfd, 0x01, 0xc6, 0xf6, 0xc9, 0xc0, 0x64, 0x64, 0xfb, + 0xa4, 0x62, 0xf6, 0x79, 0x07, 0x8d, 0xd2, 0xb4, 0x4c, 0xdc, 0x21, 0x5b, 0x4c, 0x57, 0xdf, 0x1f, + 0xc9, 0x83, 0x92, 0x3d, 0x98, 0x35, 0xa6, 0x24, 0x62, 0xa9, 0x32, 0x38, 0x56, 0x39, 0xf7, 0x46, + 0x70, 0xf4, 0xeb, 0xcf, 0x90, 0xf1, 0xc6, 0xae, 0x3f, 0xa3, 0xe7, 0x2d, 0xbb, 0xfe, 0x5c, 0x33, + 0x33, 0xc9, 0xfa, 0x53, 0xee, 0x2c, 0xed, 0xfa, 0x53, 0xd9, 0xaa, 0xda, 0xf5, 0x67, 0x48, 0x73, + 0x7a, 0x0c, 0x75, 0xf3, 0xa9, 0xb7, 0x2f, 0x69, 0x45, 0x7b, 0xe0, 0xb8, 0xa3, 0x58, 0xb4, 0xd8, + 0x9d, 0x77, 0xbd, 0x7e, 0xa9, 0x78, 0x2f, 0x5e, 0xc2, 0x54, 0x31, 0x42, 0xac, 0x59, 0x19, 0x5d, + 0x6a, 0xac, 0x9c, 0xbb, 0x43, 0x56, 0xb5, 0xe4, 0xb3, 0x49, 0xf5, 0xb3, 0xeb, 0x8b, 0x7f, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x72, 0x58, 0xed, 0x5d, 0xf9, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1762,6 +1862,7 @@ type StakepooldServiceClient interface { ValidateAddress(ctx context.Context, in *ValidateAddressRequest, opts ...grpc.CallOption) (*ValidateAddressResponse, error) AddMissingTicket(ctx context.Context, in *AddMissingTicketRequest, opts ...grpc.CallOption) (*AddMissingTicketResponse, error) GetTickets(ctx context.Context, in *GetTicketsRequest, opts ...grpc.CallOption) (*GetTicketsResponse, error) + GetTicketInfo(ctx context.Context, in *GetTicketInfoRequest, opts ...grpc.CallOption) (*GetTicketInfoResponse, error) ListScripts(ctx context.Context, in *ListScriptsRequest, opts ...grpc.CallOption) (*ListScriptsResponse, error) AccountSyncAddressIndex(ctx context.Context, in *AccountSyncAddressIndexRequest, opts ...grpc.CallOption) (*AccountSyncAddressIndexResponse, error) CreateMultisig(ctx context.Context, in *CreateMultisigRequest, opts ...grpc.CallOption) (*CreateMultisigResponse, error) @@ -1875,6 +1976,15 @@ func (c *stakepooldServiceClient) GetTickets(ctx context.Context, in *GetTickets return out, nil } +func (c *stakepooldServiceClient) GetTicketInfo(ctx context.Context, in *GetTicketInfoRequest, opts ...grpc.CallOption) (*GetTicketInfoResponse, error) { + out := new(GetTicketInfoResponse) + err := c.cc.Invoke(ctx, "/stakepoolrpc.StakepooldService/GetTicketInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *stakepooldServiceClient) ListScripts(ctx context.Context, in *ListScriptsRequest, opts ...grpc.CallOption) (*ListScriptsResponse, error) { out := new(ListScriptsResponse) err := c.cc.Invoke(ctx, "/stakepoolrpc.StakepooldService/ListScripts", in, out, opts...) @@ -1924,6 +2034,7 @@ type StakepooldServiceServer interface { ValidateAddress(context.Context, *ValidateAddressRequest) (*ValidateAddressResponse, error) AddMissingTicket(context.Context, *AddMissingTicketRequest) (*AddMissingTicketResponse, error) GetTickets(context.Context, *GetTicketsRequest) (*GetTicketsResponse, error) + GetTicketInfo(context.Context, *GetTicketInfoRequest) (*GetTicketInfoResponse, error) ListScripts(context.Context, *ListScriptsRequest) (*ListScriptsResponse, error) AccountSyncAddressIndex(context.Context, *AccountSyncAddressIndexRequest) (*AccountSyncAddressIndexResponse, error) CreateMultisig(context.Context, *CreateMultisigRequest) (*CreateMultisigResponse, error) @@ -1967,6 +2078,9 @@ func (*UnimplementedStakepooldServiceServer) AddMissingTicket(ctx context.Contex func (*UnimplementedStakepooldServiceServer) GetTickets(ctx context.Context, req *GetTicketsRequest) (*GetTicketsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTickets not implemented") } +func (*UnimplementedStakepooldServiceServer) GetTicketInfo(ctx context.Context, req *GetTicketInfoRequest) (*GetTicketInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTicketInfo not implemented") +} func (*UnimplementedStakepooldServiceServer) ListScripts(ctx context.Context, req *ListScriptsRequest) (*ListScriptsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListScripts not implemented") } @@ -2182,6 +2296,24 @@ func _StakepooldService_GetTickets_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _StakepooldService_GetTicketInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTicketInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StakepooldServiceServer).GetTicketInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stakepoolrpc.StakepooldService/GetTicketInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StakepooldServiceServer).GetTicketInfo(ctx, req.(*GetTicketInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _StakepooldService_ListScripts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListScriptsRequest) if err := dec(in); err != nil { @@ -2302,6 +2434,10 @@ var _StakepooldService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetTickets", Handler: _StakepooldService_GetTickets_Handler, }, + { + MethodName: "GetTicketInfo", + Handler: _StakepooldService_GetTicketInfo_Handler, + }, { MethodName: "ListScripts", Handler: _StakepooldService_ListScripts_Handler, diff --git a/config.go b/config.go index 24734793..999e9048 100644 --- a/config.go +++ b/config.go @@ -26,28 +26,32 @@ import ( ) const ( - defaultBaseURL = "http://127.0.0.1:8000" - defaultClosePoolMsg = "The voting service is temporarily closed to new signups." - defaultConfigFilename = "dcrstakepool.conf" - defaultDataDirname = "data" - defaultLogLevel = "info" - defaultLogDirname = "logs" - defaultLogFilename = "dcrstakepool.log" - defaultCookieSecure = false - defaultDBHost = "localhost" - defaultDBName = "stakepool" - defaultDBPort = "3306" - defaultDBUser = "stakepool" - defaultListen = ":8000" - defaultPoolEmail = "admin@example.com" - defaultPoolFees = 7.5 - defaultPoolLink = "https://forum.decred.org/threads/rfp-6-setup-and-operate-10-stake-pools.1361/" - defaultPublicPath = "public" - defaultTemplatePath = "views" - defaultSMTPHost = "" - defaultMaxVotedTickets = 1000 - defaultDescription = "" - defaultDesignation = "" + defaultBaseURL = "http://127.0.0.1:8000" + defaultClosePoolMsg = "The voting service is temporarily closed to new signups." + defaultConfigFilename = "dcrstakepool.conf" + defaultDataDirname = "data" + defaultLogLevel = "info" + defaultLogDirname = "logs" + defaultLogFilename = "dcrstakepool.log" + defaultTicketChallengeMaxAge = 600 // 10 minutes + defaultCookieSecure = false + defaultDBHost = "localhost" + defaultDBName = "stakepool" + defaultDBPort = "3306" + defaultDBUser = "stakepool" + defaultListen = ":8000" + defaultPoolEmail = "admin@example.com" + defaultPoolFees = 7.5 + defaultPoolLink = "https://forum.decred.org/threads/rfp-6-setup-and-operate-10-stake-pools.1361/" + defaultPublicPath = "public" + defaultTemplatePath = "views" + defaultSMTPHost = "" + defaultMinServers = 2 + defaultMaxVotedTickets = 1000 + defaultDescription = "" + defaultDesignation = "" + + MaxTicketChallengeAge = 60 * 30 // 30 minutes ) var ( @@ -67,50 +71,51 @@ var runServiceCommand func(string) error // // See loadConfig for details on the configuration load process. type config struct { - ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` - ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` - DataDir string `short:"b" long:"datadir" description:"Directory to store data"` - LogDir string `long:"logdir" description:"Directory to log output."` - Listen string `long:"listen" description:"Listen for connections on the specified interface/port (default all interfaces port: 9113, testnet: 19113)"` - TestNet bool `long:"testnet" description:"Use the test network"` - SimNet bool `long:"simnet" description:"Use the simulation test network"` - Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` - CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` - MemProfile string `long:"memprofile" description:"Write mem profile to the specified file"` - DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` - APISecret string `long:"apisecret" description:"Secret string used to encrypt API tokens."` - BaseURL string `long:"baseurl" description:"BaseURL to use when sending links via email"` + ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` + ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` + DataDir string `short:"b" long:"datadir" description:"Directory to store data"` + LogDir string `long:"logdir" description:"Directory to log output."` + Listen string `long:"listen" description:"Listen for connections on the specified interface/port (default all interfaces port: 9113, testnet: 19113)"` + TestNet bool `long:"testnet" description:"Use the test network"` + SimNet bool `long:"simnet" description:"Use the simulation test network"` + Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` + CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` + MemProfile string `long:"memprofile" description:"Write mem profile to the specified file"` + DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` + APISecret string `long:"apisecret" description:"Secret string used to encrypt API tokens."` + TicketChallengeMaxAge int64 `long:"ticketchallengemaxage" description:"Max age (in seconds) for API v3 ticket authentication timestamps. Max allowed value is 1800 (30 minutes)."` + BaseURL string `long:"baseurl" description:"BaseURL to use when sending links via email"` // todo: can `ColdWalletExtPub` and `PoolFees` be read from stakepoold via rpc? - ColdWalletExtPub string `long:"coldwalletextpub" description:"The extended public key for addresses to which voting service user fees are sent."` - ClosePool bool `long:"closepool" description:"Disable user registration actions (sign-ups and submitting addresses)"` - ClosePoolMsg string `long:"closepoolmsg" description:"Message to display when closepool is set."` - CookieSecret string `long:"cookiesecret" description:"Secret string used to encrypt session data."` - CookieSecure bool `long:"cookiesecure" description:"Set whether cookies can be sent in clear text or not."` - DBHost string `long:"dbhost" description:"Hostname for database connection"` - DBUser string `long:"dbuser" description:"Username for database connection"` - DBPassword string `long:"dbpassword" description:"Password for database connection"` - DBPort string `long:"dbport" description:"Port for database connection"` - DBName string `long:"dbname" description:"Name of database"` - PublicPath string `long:"publicpath" description:"Path to the public folder which contains css/fonts/images/javascript."` - TemplatePath string `long:"templatepath" description:"Path to the views folder which contains html files."` - PoolEmail string `long:"poolemail" description:"Email address to for support inquiries"` - PoolFees float64 `long:"poolfees" description:"The per-ticket fees the user must send to the pool with their tickets"` - PoolLink string `long:"poollink" description:"URL for support inquiries such as forum, IRC, etc"` - RealIPHeader string `long:"realipheader" description:"The name of an HTTP request header containing the actual remote client IP address, typically set by a reverse proxy. An empty string (default) indicates to use net/Request.RemodeAddr."` - SMTPFrom string `long:"smtpfrom" description:"From address to use on outbound mail"` - SMTPHost string `long:"smtphost" description:"SMTP hostname/ip and port, e.g. mail.example.com:25"` - SMTPUsername string `long:"smtpusername" description:"SMTP username for authentication if required"` - SMTPPassword string `long:"smtppassword" description:"SMTP password for authentication if required"` - UseSMTPS bool `long:"usesmtps" description:"Connect to the SMTP server using smtps."` - SMTPSkipVerify bool `long:"smtpskipverify" description:"Skip SMTP TLS cert verification. Will only skip if SMTPCert is empty"` - SMTPCert string `long:"smtpcert" description:"Path for the smtp certificate file"` - SystemCerts *x509.CertPool - StakepooldHosts []string `long:"stakepooldhosts" description:"Hostnames for stakepoold servers"` - StakepooldCerts []string `long:"stakepooldcerts" description:"Certificate paths for stakepoold servers"` - WalletHosts []string `long:"wallethosts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletUsers []string `long:"walletusers" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletPasswords []string `long:"walletpasswords" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletCerts []string `long:"walletcerts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + ColdWalletExtPub string `long:"coldwalletextpub" description:"The extended public key for addresses to which voting service user fees are sent."` + ClosePool bool `long:"closepool" description:"Disable user registration actions (sign-ups and submitting addresses)"` + ClosePoolMsg string `long:"closepoolmsg" description:"Message to display when closepool is set."` + CookieSecret string `long:"cookiesecret" description:"Secret string used to encrypt session data."` + CookieSecure bool `long:"cookiesecure" description:"Set whether cookies can be sent in clear text or not."` + DBHost string `long:"dbhost" description:"Hostname for database connection"` + DBUser string `long:"dbuser" description:"Username for database connection"` + DBPassword string `long:"dbpassword" description:"Password for database connection"` + DBPort string `long:"dbport" description:"Port for database connection"` + DBName string `long:"dbname" description:"Name of database"` + PublicPath string `long:"publicpath" description:"Path to the public folder which contains css/fonts/images/javascript."` + TemplatePath string `long:"templatepath" description:"Path to the views folder which contains html files."` + PoolEmail string `long:"poolemail" description:"Email address to for support inquiries"` + PoolFees float64 `long:"poolfees" description:"The per-ticket fees the user must send to the pool with their tickets"` + PoolLink string `long:"poollink" description:"URL for support inquiries such as forum, IRC, etc"` + RealIPHeader string `long:"realipheader" description:"The name of an HTTP request header containing the actual remote client IP address, typically set by a reverse proxy. An empty string (default) indicates to use net/Request.RemodeAddr."` + SMTPFrom string `long:"smtpfrom" description:"From address to use on outbound mail"` + SMTPHost string `long:"smtphost" description:"SMTP hostname/ip and port, e.g. mail.example.com:25"` + SMTPUsername string `long:"smtpusername" description:"SMTP username for authentication if required"` + SMTPPassword string `long:"smtppassword" description:"SMTP password for authentication if required"` + UseSMTPS bool `long:"usesmtps" description:"Connect to the SMTP server using smtps."` + SMTPSkipVerify bool `long:"smtpskipverify" description:"Skip SMTP TLS cert verification. Will only skip if SMTPCert is empty"` + SMTPCert string `long:"smtpcert" description:"Path for the smtp certificate file"` + SystemCerts *x509.CertPool + StakepooldHosts []string `long:"stakepooldhosts" description:"Hostnames for stakepoold servers"` + StakepooldCerts []string `long:"stakepooldcerts" description:"Certificate paths for stakepoold servers"` + WalletHosts []string `long:"wallethosts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletUsers []string `long:"walletusers" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletPasswords []string `long:"walletpasswords" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletCerts []string `long:"walletcerts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` // todo: `VotingWalletExtPub` can be read from the vsp backend dcrwallet via stakepoold rpc instead! VotingWalletExtPub string `long:"votingwalletextpub" description:"The extended public key of the default account of the voting wallet"` AdminIPs []string `long:"adminips" description:"Expected admin host"` @@ -317,28 +322,30 @@ func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *fl func loadConfig() (*config, []string, error) { // Default config. cfg := config{ - BaseURL: defaultBaseURL, - ClosePool: false, - ClosePoolMsg: defaultClosePoolMsg, - ConfigFile: defaultConfigFile, - DebugLevel: defaultLogLevel, - DataDir: defaultDataDir, - LogDir: defaultLogDir, - CookieSecure: defaultCookieSecure, - DBHost: defaultDBHost, - DBName: defaultDBName, - DBPort: defaultDBPort, - DBUser: defaultDBUser, - Listen: defaultListen, - PoolEmail: defaultPoolEmail, - PoolFees: defaultPoolFees, - PoolLink: defaultPoolLink, - PublicPath: defaultPublicPath, - TemplatePath: defaultTemplatePath, - SMTPHost: defaultSMTPHost, - MaxVotedTickets: defaultMaxVotedTickets, - Description: defaultDescription, - Designation: defaultDesignation, + BaseURL: defaultBaseURL, + ClosePool: false, + ClosePoolMsg: defaultClosePoolMsg, + ConfigFile: defaultConfigFile, + DebugLevel: defaultLogLevel, + DataDir: defaultDataDir, + LogDir: defaultLogDir, + TicketChallengeMaxAge: defaultTicketChallengeMaxAge, + CookieSecure: defaultCookieSecure, + DBHost: defaultDBHost, + DBName: defaultDBName, + DBPort: defaultDBPort, + DBUser: defaultDBUser, + Listen: defaultListen, + PoolEmail: defaultPoolEmail, + PoolFees: defaultPoolFees, + PoolLink: defaultPoolLink, + PublicPath: defaultPublicPath, + TemplatePath: defaultTemplatePath, + SMTPHost: defaultSMTPHost, + MinServers: defaultMinServers, + MaxVotedTickets: defaultMaxVotedTickets, + Description: defaultDescription, + Designation: defaultDesignation, } // Service options which are only added on Windows. @@ -479,6 +486,12 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } + // Ensure ticket challenge max age is not greater than permitted maximum. + if cfg.TicketChallengeMaxAge > MaxTicketChallengeAge { + return nil, nil, fmt.Errorf("%s: Ticket challenge max age cannot be higher than %v", + funcName, MaxTicketChallengeAge) + } + // Validate profile port number if cfg.Profile != "" { profilePort, err := strconv.Atoi(cfg.Profile) diff --git a/controllers/main.go b/controllers/main.go index 53f44fec..82a72b00 100644 --- a/controllers/main.go +++ b/controllers/main.go @@ -287,6 +287,10 @@ func (controller *MainController) API(c web.C, r *http.Request) *system.APIRespo } } + if invalidAuthMessage := c.Env["AuthErrorMessage"]; invalidAuthMessage != "" && code == codes.Unauthenticated { + err = fmt.Errorf("invalid api authorization: %s", invalidAuthMessage) + } + if err != nil { status = "error" response = response + " - " + err.Error() diff --git a/go.mod b/go.mod index 2f76edbd..dd370828 100644 --- a/go.mod +++ b/go.mod @@ -12,10 +12,11 @@ require ( github.com/decred/dcrd/dcrutil v1.4.0 github.com/decred/dcrd/hdkeychain v1.1.1 github.com/decred/dcrd/rpcclient/v3 v3.0.0 - github.com/decred/dcrd/txscript v1.0.3-0.20190613214542-d0a6bf024dfc // indirect + github.com/decred/dcrd/txscript v1.0.3-0.20190613214542-d0a6bf024dfc github.com/decred/dcrd/wire v1.2.0 github.com/decred/dcrdata/api/types/v4 v4.0.2 github.com/decred/dcrwallet/rpc/jsonrpc/types v1.1.0 + github.com/decred/dcrwallet/wallet v1.3.0 github.com/decred/dcrwallet/wallet/v2 v2.0.0 github.com/decred/slog v1.0.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible diff --git a/go.sum b/go.sum index 824ed3bc..242888bf 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,7 @@ github.com/decred/dcrd/dcrec v0.0.0-20180721005212-59fe2b293f69/go.mod h1:cRAH1S github.com/decred/dcrd/dcrec v0.0.0-20180721005914-d26200ec716b/go.mod h1:cRAH1SNk8Mi9hKBc/DHbeiWz/fyO8KWZR3H7okrIuOA= github.com/decred/dcrd/dcrec v0.0.0-20180721031028-5369a485acf6/go.mod h1:cRAH1SNk8Mi9hKBc/DHbeiWz/fyO8KWZR3H7okrIuOA= github.com/decred/dcrd/dcrec v0.0.0-20180801202239-0761de129164/go.mod h1:cRAH1SNk8Mi9hKBc/DHbeiWz/fyO8KWZR3H7okrIuOA= +github.com/decred/dcrd/dcrec v0.0.0-20181212181811-1a370d38d671/go.mod h1:cRAH1SNk8Mi9hKBc/DHbeiWz/fyO8KWZR3H7okrIuOA= github.com/decred/dcrd/dcrec v0.0.0-20190130161649-59ed4247a1d5 h1:rz0HmQLuABmKj2fFaeYMpJSG66JrDhU3mQ1tDBeGfQk= github.com/decred/dcrd/dcrec v0.0.0-20190130161649-59ed4247a1d5/go.mod h1:cRAH1SNk8Mi9hKBc/DHbeiWz/fyO8KWZR3H7okrIuOA= github.com/decred/dcrd/dcrec v0.0.0-20190214012338-9265b4051009 h1:+VNyY6U5yMZCPE2UqHgJSOmKDjD6WYB+NlDCEj7ZYJg= @@ -83,6 +84,7 @@ github.com/decred/dcrd/dcrec/secp256k1 v1.0.1/go.mod h1:lhu4eZFSfTJWUnR3CFRcpD+V github.com/decred/dcrd/dcrec/secp256k1 v1.0.2 h1:awk7sYJ4pGWmtkiGHFfctztJjHMKGLV8jctGQhAbKe0= github.com/decred/dcrd/dcrec/secp256k1 v1.0.2/go.mod h1:CHTUIVfmDDd0KFVFpNX1pFVCBUegxW387nN0IGwNKR0= github.com/decred/dcrd/dcrjson v1.0.0/go.mod h1:ozddIaeF+EAvZZvFuB3zpfxhyxBGfvbt22crQh+PYuI= +github.com/decred/dcrd/dcrjson v1.1.0/go.mod h1:ozddIaeF+EAvZZvFuB3zpfxhyxBGfvbt22crQh+PYuI= github.com/decred/dcrd/dcrjson v1.2.0 h1:3BFFQHq3/YO/zae9WLxQkXsX6AXKx3+M8H3yk4oXZi0= github.com/decred/dcrd/dcrjson v1.2.0/go.mod h1:ozddIaeF+EAvZZvFuB3zpfxhyxBGfvbt22crQh+PYuI= github.com/decred/dcrd/dcrjson/v2 v2.0.0 h1:W0q4Alh36c5N318eUpfmU8kXoCNgImMLI87NIXni9Us= @@ -107,11 +109,14 @@ github.com/decred/dcrd/hdkeychain/v2 v2.0.0 h1:b6GklXT+LeDumc0bDqMHkss+p2Bu+mgiU github.com/decred/dcrd/hdkeychain/v2 v2.0.0/go.mod h1:tG+VpXfloIkNGHGd6NeoTElHWA68Wf1aP87zegXDGEw= github.com/decred/dcrd/hdkeychain/v2 v2.0.1 h1:LnMLuPDx6j1/7ywGdfX5onPOsa98yObzBIJrp+nK4Qo= github.com/decred/dcrd/hdkeychain/v2 v2.0.1/go.mod h1:qPv+vTla19liVHFuXVnQ70dMI4ERPCniDXbV5RzwQiM= +github.com/decred/dcrd/mempool v1.1.1 h1:ysFIS3HzEIJ88B1Y4OfL6wjzBurlChbKkzq54hPglGo= github.com/decred/dcrd/mempool v1.1.1/go.mod h1:u1I2KRv9UHhx2crlbZXYoLDabWyQ8VnnHDSG53UdhCA= +github.com/decred/dcrd/mining v1.1.0 h1:9Wtla+i+pEjfYsNCfixsipmyyoB26DgL4LSXWAin/zw= github.com/decred/dcrd/mining v1.1.0/go.mod h1:NQEtX604XgNwKcPFId1hVTTiBqmVQDlnqV1yNqGl4oU= github.com/decred/dcrd/rpc/jsonrpc/types v1.0.0 h1:d5ptnjuSADTQMa3i83VpeJNoMRTOJZZBqk7P+E41VXM= github.com/decred/dcrd/rpc/jsonrpc/types v1.0.0/go.mod h1:0dwmpIP21tJxjg/UuUHWIFMbfoLv2ifCBMokNKlOxpo= -github.com/decred/dcrd/rpcclient/v2 v2.0.0 h1:Zy9twdEaOGUdCj/89LAs/IrStm6FcabxzBve4UsA73A= +github.com/decred/dcrd/rpcclient v1.1.0 h1:nQZ1qOJaLYoOTM1oQ2dLaqocb5TWI7gNBK+BTY7UVXk= +github.com/decred/dcrd/rpcclient v1.1.0/go.mod h1:SCwBs4d+aqRV2ChnriIZ1y/LgNVHG/2ieEC1vIop82s= github.com/decred/dcrd/rpcclient/v2 v2.0.0/go.mod h1:9XjbRHBSNqN+DXz8I47gUZszvVjvugqLGK8TZQ4c/u0= github.com/decred/dcrd/rpcclient/v3 v3.0.0 h1:doabWkWWd8O6ccYqaLamE1mkQ5p8YoO5UJhkl5pAGpQ= github.com/decred/dcrd/rpcclient/v3 v3.0.0/go.mod h1:OQ361/iQMrJtWIg+S0XpZT4/AA6mU/46oOkBRWp/P/s= @@ -135,6 +140,7 @@ github.com/decred/dcrdata/semver v1.0.0 h1:DBqYU/x+4LqHq/3r4xKdF6xG5ewktG2KDC+g/ github.com/decred/dcrdata/semver v1.0.0/go.mod h1:z+nQqiAd9fYkHhBLbejysZ2FPHtgkrErWDgMf+JlZWE= github.com/decred/dcrdata/txhelpers/v3 v3.0.2 h1:ViaYNSyOR0FjNkUAEBXPyj6eBKWNFC7r2vCfTASaOeU= github.com/decred/dcrdata/txhelpers/v3 v3.0.2/go.mod h1:iukqEl5AR6BdippUWKsC8OzaV9TW/KMBYpIwKv8IkW0= +github.com/decred/dcrwallet/deployments v1.1.0 h1:83arg+7ct7PS1H2IYhuePnrBK2rkVpEYWKqrJpCwtHA= github.com/decred/dcrwallet/deployments v1.1.0/go.mod h1:8Sasryu8SX23Jvqr6maZ7MoS7wFIGXupWzbsVtcZsUg= github.com/decred/dcrwallet/errors v1.0.1 h1:8EF7IY6twRlo9sqWaSfm8abfi2/rHZ1wacOiGvBy+bM= github.com/decred/dcrwallet/errors v1.0.1/go.mod h1:XUm95dWmm9XmQGvneBXJkkIaFeRsQVBB6ni/KTy1hrY= @@ -146,7 +152,10 @@ github.com/decred/dcrwallet/rpc/jsonrpc/types v1.0.0 h1:FPVohgxZHHwi7qTOBki/Dw9j github.com/decred/dcrwallet/rpc/jsonrpc/types v1.0.0/go.mod h1:k+IOPnUY0YqlwhSDhczzaUN17NX/gMtztwl3UxKgVZY= github.com/decred/dcrwallet/rpc/jsonrpc/types v1.1.0 h1:ZOMpbSK/Cz8D8Yfrt7/yNfS+myBUWMNOdgAg31ND7bM= github.com/decred/dcrwallet/rpc/jsonrpc/types v1.1.0/go.mod h1:xUT7XXATLOzE0pwwmvgfRWtZdrB+PsWFilo+jkH5/Ig= +github.com/decred/dcrwallet/validate v1.0.2 h1:EXY48b3SOkTGXJAB+OUmilvyFNROZywYIWTGYfUnTDE= github.com/decred/dcrwallet/validate v1.0.2/go.mod h1:1ur2sRZkQ23ECalUKdwgx6rdIiP8rIiaSQAz1Y9LQsI= +github.com/decred/dcrwallet/wallet v1.3.0 h1:RQ4uHIfbDF0ppyeVDkTr7GapeABCddUUKOSexTN7X1M= +github.com/decred/dcrwallet/wallet v1.3.0/go.mod h1:ItOhnw3C4znuLQVWACSq8jCLy221v9X0Xo0b/j5WqgU= github.com/decred/dcrwallet/wallet/v2 v2.0.0 h1:ppSLjwu3DHmRWdnRbN53fu9AFjY0MqK40jzbZQAouG8= github.com/decred/dcrwallet/wallet/v2 v2.0.0/go.mod h1:uDSN0GRTH583eSnPyPiTXNcZ9DrQ2tBqdm/tjp5rjwY= github.com/decred/go-socks v1.0.0 h1:gd5+vE9LmL+FYiN5cxAMtaAVb791mG8E/+Jo411M/BE= @@ -177,12 +186,14 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU= github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -224,6 +235,7 @@ github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= golang.org/x/crypto v0.0.0-20180718160520-a2144134853f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= @@ -250,6 +262,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTm golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/models/user.go b/models/user.go index a693cd1f..31f3c8cb 100644 --- a/models/user.go +++ b/models/user.go @@ -168,6 +168,16 @@ func GetUserById(dbMap *gorp.DbMap, id int64) (user *User, err error) { return user, nil } +func GetUserByMSA(dbMap *gorp.DbMap, msa string) (user *User, err error) { + err = dbMap.SelectOne(&user, "SELECT * FROM Users WHERE MultiSigAddress = ?", msa) + + if err != nil { + return nil, err + } + + return user, nil +} + // GetUserCount gives a count of all users func GetUserCount(dbMap *gorp.DbMap) int64 { userCount, err := dbMap.SelectInt("SELECT COUNT(*) FROM Users") diff --git a/sample-dcrstakepool.conf b/sample-dcrstakepool.conf index 67765b00..ff2e95e6 100644 --- a/sample-dcrstakepool.conf +++ b/sample-dcrstakepool.conf @@ -9,6 +9,10 @@ designation=YourVSP ; Printed on the home screen under the VSP overview description=Your VSP description +; Max age (in seconds) for API v3 ticket authentication timestamps. +; Max allowed value is 1800 (30 minutes). +; ticketchallengemaxage=600 + ; Secret string used to encrypt API and to generate CSRF tokens. ; Can use openssl rand -hex 32 to generate one. ;apisecret= diff --git a/server.go b/server.go index 4b9a55f1..30f0f213 100644 --- a/server.go +++ b/server.go @@ -63,7 +63,7 @@ func runMain() error { var application = &system.Application{} - application.Init(cfg.APISecret, cfg.BaseURL, cfg.CookieSecret, + application.Init(cfg.APISecret, cfg.TicketChallengeMaxAge, cfg.BaseURL, cfg.CookieSecret, cfg.CookieSecure, cfg.DBHost, cfg.DBName, cfg.DBPassword, cfg.DBPort, cfg.DBUser) if application.DbMap == nil { @@ -89,6 +89,8 @@ func runMain() error { return fmt.Errorf("Failed to connect to stakepoold host: %v", err) } + application.StakepooldConnMan = stakepooldConnMan + var sender email.Sender if cfg.SMTPHost != "" { sender, err = email.NewSender(cfg.SMTPHost, cfg.SMTPUsername, cfg.SMTPPassword, diff --git a/stakepooldclient/stakepooldclient.go b/stakepooldclient/stakepooldclient.go index cedbbbe3..eea73b96 100644 --- a/stakepooldclient/stakepooldclient.go +++ b/stakepooldclient/stakepooldclient.go @@ -116,6 +116,25 @@ func (s *StakepooldManager) connected() error { return nil } +// GetAddedLowFeeTickets performs gRPC GetAddedLowFeeTickets +// requests against all stakepoold instances and returns the first result fetched +// without errors. Returns an error if all RPC requests fail. +func (s *StakepooldManager) GetTicketInfo(hash string) (*pb.GetTicketInfoResponse, error) { + for i, conn := range s.grpcConnections { + client := pb.NewStakepooldServiceClient(conn) + resp, err := client.GetTicketInfo(context.Background(), &pb.GetTicketInfoRequest{Hash: hash}) + if err != nil { + log.Warnf("GetTicketInfo RPC failed on stakepoold instance %d: %v", i, err) + continue + } + + return resp, err + } + + // All RPC requests failed + return nil, errors.New("GetTicketInfo RPC failed on all stakepoold instances") +} + // GetAddedLowFeeTickets performs gRPC GetAddedLowFeeTickets // requests against all stakepoold instances and returns the first result fetched // without errors. Returns an error if all RPC requests fail. diff --git a/system/api-auth.go b/system/api-auth.go new file mode 100644 index 00000000..b440b944 --- /dev/null +++ b/system/api-auth.go @@ -0,0 +1,128 @@ +package system + +import ( + "encoding/base64" + "fmt" + "strconv" + "strings" + "time" + + "github.com/decred/dcrd/dcrutil" + "github.com/decred/dcrwallet/wallet" + "github.com/dgrijalva/jwt-go" +) + +func (application *Application) validateToken(authHeader string) (int64, string) { + apitoken := strings.TrimPrefix(authHeader, "Bearer ") + + JWTtoken, err := jwt.Parse(apitoken, func(token *jwt.Token) (interface{}, error) { + // validate signing algorithm + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(application.APISecret), nil + }) + + if err != nil { + return -1, fmt.Sprintf("invalid token %v: %v", apitoken, err) + } else if claims, ok := JWTtoken.Claims.(jwt.MapClaims); ok && JWTtoken.Valid { + return int64(claims["loggedInAs"].(float64)), "" + } else { + return -1, fmt.Sprintf("invalid token %v", apitoken) + } +} + +func (application *Application) validateTicketOwnership(authHeader string) (multiSigAddress, authValidationFailureReason string) { + timestamp, timestampSignature, ticketHash := extractTicketAuthParams(strings.TrimPrefix(authHeader, "TicketAuth ")) + if timestamp == "" || timestampSignature == "" || ticketHash == "" { + authValidationFailureReason = fmt.Sprintf("invalid ticket auth header value %s", authHeader) + return + } + + // Ensure that this signature had not been used in a previous authentication attempt. + if application.ProcessedTicketChallenges.ContainsChallenge(timestampSignature) { + authValidationFailureReason = fmt.Sprintf("disallowed reuse of ticket auth signature %v", timestampSignature) + return + } + + authTimestamp, err := strconv.Atoi(timestamp) + if err != nil { + authValidationFailureReason = fmt.Sprintf("invalid ticket auth timestamp value %v", timestamp) + return + } + + // Ensure that the auth timestamp + // - is not more than 5 minutes into the future + // - is not more than 30 seconds into the past + timestampDelta := time.Now().Unix() - int64(authTimestamp) + if timestampDelta < -300 { + // more than 5 minutes into the future + authValidationFailureReason = "invalid (future) timestamp" + return + } else if timestampDelta > application.TicketChallengeMaxAge { + authValidationFailureReason = fmt.Sprintf("expired ticket auth timestamp value %v", timestamp) + return + } + + // confirm that the timestamp signature is a valid base64 string + decodedSignature, err := base64.StdEncoding.DecodeString(timestampSignature) + if err != nil { + authValidationFailureReason = fmt.Sprintf("invalid ticket auth signature %s", timestampSignature) + return + } + + // Mark this timestamp signature as used to prevent subsequent reuse. + challengeExpiresIn := application.TicketChallengeMaxAge - timestampDelta + application.ProcessedTicketChallenges.AddChallenge(timestampSignature, challengeExpiresIn) + + // todo check if ticket belongs to this vsp + + // get user wallet address using ticket hash + // todo: may be better to maintain a memory map of tickets-userWalletAddresses + ticketInfo, err := application.StakepooldConnMan.GetTicketInfo(ticketHash) + if err != nil { + authValidationFailureReason = fmt.Sprintf("ticket auth, get ticket info failed: %v", err) + return + } + + // check if timestamp signature checks out against address + addr, err := dcrutil.DecodeAddress(ticketInfo.OwnerFeeAddress) + if err != nil { + authValidationFailureReason = fmt.Sprintf("ticket auth, unexpected decode address error: %v", err) + return + } + + valid, err := wallet.VerifyMessage(timestamp, addr, decodedSignature) + if err != nil { + authValidationFailureReason = fmt.Sprintf("error validating timestamp signature for ticket auth %v", err) + return + } + + if valid { + multiSigAddress = ticketInfo.MultiSigAddress + } + return +} + +func extractTicketAuthParams(authHeader string) (timestampMessage, timestampSignature, ticketHash string) { + authParams := strings.Split(authHeader, ",") + for _, param := range authParams { + paramKeyValue := strings.TrimSpace(param) + if value := getAuthValueFromParam(paramKeyValue, "SignedTimestamp"); value != "" { + timestampMessage = strings.TrimSpace(value) + } else if value := getAuthValueFromParam(paramKeyValue, "Signature"); value != "" { + timestampSignature = strings.TrimSpace(value) + } else if value := getAuthValueFromParam(paramKeyValue, "TicketHash"); value != "" { + ticketHash = strings.TrimSpace(value) + } + } + return +} + +func getAuthValueFromParam(paramKeyValue, key string) string { + keyPrefix := key + "=" + if strings.HasPrefix(paramKeyValue, keyPrefix) { + return strings.TrimPrefix(paramKeyValue, keyPrefix) + } + return "" +} diff --git a/system/core.go b/system/core.go index 9245ebc8..a18da209 100644 --- a/system/core.go +++ b/system/core.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/decred/dcrstakepool/models" + "github.com/decred/dcrstakepool/stakepooldclient" "github.com/go-gorp/gorp" "github.com/gorilla/sessions" "github.com/zenazn/goji/web" @@ -21,11 +22,14 @@ import ( ) type Application struct { - APISecret string - Template *template.Template - TemplatesPath string - Store *SQLStore - DbMap *gorp.DbMap + APISecret string + TicketChallengeMaxAge int64 + ProcessedTicketChallenges *ticketChallengesCache + Template *template.Template + TemplatesPath string + Store *SQLStore + DbMap *gorp.DbMap + StakepooldConnMan *stakepooldclient.StakepooldManager } // GojiWebHandlerFunc is an adaptor that allows an http.HanderFunc where a @@ -36,10 +40,9 @@ func GojiWebHandlerFunc(h http.HandlerFunc) web.HandlerFunc { } } -func (application *Application) Init(APISecret string, baseURL string, - cookieSecret string, cookieSecure bool, DBHost string, DBName string, - DBPassword string, - DBPort string, DBUser string) { +func (application *Application) Init(APISecret string, ticketChallengeMaxAge int64, + baseURL string, cookieSecret string, cookieSecure bool, + DBHost string, DBName string, DBPassword string, DBPort string, DBUser string) { application.DbMap = models.GetDbMap( APISecret, @@ -62,6 +65,8 @@ func (application *Application) Init(APISecret string, baseURL string, } application.APISecret = APISecret + application.TicketChallengeMaxAge = ticketChallengeMaxAge + application.ProcessedTicketChallenges = newTicketChallengesCache() } func (application *Application) LoadTemplates(templatePath string) error { diff --git a/system/middleware.go b/system/middleware.go index 3037ce4f..fd2feb60 100644 --- a/system/middleware.go +++ b/system/middleware.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/decred/dcrstakepool/models" - "github.com/dgrijalva/jwt-go" "github.com/go-gorp/gorp" "github.com/gorilla/sessions" "github.com/zenazn/goji/web" @@ -45,33 +44,36 @@ func (application *Application) ApplyDbMap(c *web.C, h http.Handler) http.Handle func (application *Application) ApplyAPI(c *web.C, h http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/api") { + var user *models.User + var err error + dbMap := c.Env["DbMap"].(*gorp.DbMap) + authHeader := r.Header.Get("Authorization") if strings.HasPrefix(authHeader, "Bearer ") { - apitoken := strings.TrimPrefix(authHeader, "Bearer ") - - JWTtoken, err := jwt.Parse(apitoken, func(token *jwt.Token) (interface{}, error) { - // validate signing algorithm - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte(application.APISecret), nil - }) - - if err != nil { - log.Warnf("invalid token %v: %v", apitoken, err) - } else if claims, ok := JWTtoken.Claims.(jwt.MapClaims); ok && JWTtoken.Valid { - dbMap := c.Env["DbMap"].(*gorp.DbMap) - - user, err := models.GetUserById(dbMap, int64(claims["loggedInAs"].(float64))) - if err != nil { - log.Errorf("unable to map apitoken %v to user id %v", apitoken, claims["loggedInAs"]) - } else { - c.Env["APIUserID"] = user.Id - log.Infof("mapped apitoken %v to user id %v", apitoken, user.Id) - } + userId, authFailureReason := application.validateToken(authHeader) + if authFailureReason != "" { + err = fmt.Errorf(authFailureReason) + } else { + user, err = models.GetUserById(dbMap, userId) + } + } else if strings.HasPrefix(authHeader, "TicketAuth ") { + userMsa, authFailureReason := application.validateTicketOwnership(authHeader) + if authFailureReason != "" { + err = fmt.Errorf(authFailureReason) + } else { + user, err = models.GetUserByMSA(dbMap, userMsa) } } + + if err != nil { + log.Warnf("api authorization failure: %v", err) + c.Env["AuthErrorMessage"] = err.Error() + } else if user != nil { + c.Env["APIUserID"] = user.Id + log.Infof("mapped api auth header %v to user %v", authHeader, user.Id) + } } + h.ServeHTTP(w, r) } return http.HandlerFunc(fn) diff --git a/system/ticketchallengescache.go b/system/ticketchallengescache.go new file mode 100644 index 00000000..5c934a66 --- /dev/null +++ b/system/ticketchallengescache.go @@ -0,0 +1,46 @@ +package system + +import ( + "sync" + "time" +) + +type ticketChallengesCache struct { + sync.Mutex + usedSignatures map[string]int64 // [signature]expiry +} + +func newTicketChallengesCache() *ticketChallengesCache { + cache := &ticketChallengesCache{ + usedSignatures: make(map[string]int64, 0), + } + + go func() { + for now := range time.Tick(time.Second) { + cache.Lock() + for usedSignature, challengeExpiry := range cache.usedSignatures { + if now.Unix() > challengeExpiry { + delete(cache.usedSignatures, usedSignature) + } + } + cache.Unlock() + } + }() + + return cache +} + +func (cache *ticketChallengesCache) AddChallenge(signature string, expiresIn int64) { + cache.Lock() + if _, ok := cache.usedSignatures[signature]; !ok { + cache.usedSignatures[signature] = time.Now().Unix() + expiresIn + } + cache.Unlock() +} + +func (cache *ticketChallengesCache) ContainsChallenge(signatures string) (exists bool) { + cache.Lock() + _, exists = cache.usedSignatures[signatures] + cache.Unlock() + return +}