174 lines
4.4 KiB
Go
174 lines
4.4 KiB
Go
package wakuext
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
"github.com/status-im/status-go/services/ext"
|
|
waku "github.com/status-im/status-go/waku/common"
|
|
)
|
|
|
|
const (
|
|
// defaultWorkTime is a work time reported in messages sent to MailServer nodes.
|
|
defaultWorkTime = 5
|
|
)
|
|
|
|
// PublicAPI extends waku public API.
|
|
type PublicAPI struct {
|
|
*ext.PublicAPI
|
|
service *Service
|
|
publicAPI types.PublicWakuAPI
|
|
log log.Logger
|
|
}
|
|
|
|
// NewPublicAPI returns instance of the public API.
|
|
func NewPublicAPI(s *Service) *PublicAPI {
|
|
return &PublicAPI{
|
|
PublicAPI: ext.NewPublicAPI(s.Service, s.w),
|
|
service: s,
|
|
publicAPI: s.w.PublicWakuAPI(),
|
|
log: log.New("package", "status-go/services/wakuext.PublicAPI"),
|
|
}
|
|
}
|
|
|
|
// makeEnvelop makes an envelop for a historic messages request.
|
|
// Symmetric key is used to authenticate to MailServer.
|
|
// PK is the current node ID.
|
|
// DEPRECATED
|
|
func makeEnvelop(
|
|
payload []byte,
|
|
symKey []byte,
|
|
publicKey *ecdsa.PublicKey,
|
|
nodeID *ecdsa.PrivateKey,
|
|
pow float64,
|
|
now time.Time,
|
|
) (types.Envelope, error) {
|
|
params := waku.MessageParams{
|
|
PoW: pow,
|
|
Payload: payload,
|
|
WorkTime: defaultWorkTime,
|
|
Src: nodeID,
|
|
}
|
|
// Either symKey or public key is required.
|
|
// This condition is verified in `message.Wrap()` method.
|
|
if len(symKey) > 0 {
|
|
params.KeySym = symKey
|
|
} else if publicKey != nil {
|
|
params.Dst = publicKey
|
|
}
|
|
message, err := waku.NewSentMessage(¶ms)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
envelope, err := message.Wrap(¶ms, now)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return gethbridge.NewWakuEnvelope(envelope), nil
|
|
}
|
|
|
|
// RequestMessages sends a request for historic messages to a MailServer.
|
|
func (api *PublicAPI) RequestMessages(_ context.Context, r ext.MessagesRequest) (types.HexBytes, error) {
|
|
api.log.Info("RequestMessages", "request", r)
|
|
|
|
now := api.service.w.GetCurrentTime()
|
|
r.SetDefaults(now)
|
|
|
|
if r.From > r.To {
|
|
return nil, fmt.Errorf("Query range is invalid: from > to (%d > %d)", r.From, r.To)
|
|
}
|
|
|
|
mailServerNode, err := api.service.GetPeer(r.MailServerPeer)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%v: %v", ext.ErrInvalidMailServerPeer, err)
|
|
}
|
|
|
|
var (
|
|
symKey []byte
|
|
publicKey *ecdsa.PublicKey
|
|
)
|
|
|
|
if r.SymKeyID != "" {
|
|
symKey, err = api.service.w.GetSymKey(r.SymKeyID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%v: %v", ext.ErrInvalidSymKeyID, err)
|
|
}
|
|
} else {
|
|
publicKey = mailServerNode.Pubkey()
|
|
}
|
|
|
|
payload, err := ext.MakeMessagesRequestPayload(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
envelope, err := makeEnvelop(
|
|
payload,
|
|
symKey,
|
|
publicKey,
|
|
api.service.NodeID(),
|
|
api.service.w.MinPow(),
|
|
now,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hash := envelope.Hash()
|
|
|
|
if !r.Force {
|
|
err = api.service.RequestsRegistry().Register(hash, r.Topics)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := api.service.w.RequestHistoricMessagesWithTimeout(mailServerNode.ID().Bytes(), envelope, r.Timeout*time.Second); err != nil {
|
|
if !r.Force {
|
|
api.service.RequestsRegistry().Unregister(hash)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return hash[:], nil
|
|
}
|
|
|
|
// RequestMessagesSync repeats MessagesRequest using configuration in retry conf.
|
|
func (api *PublicAPI) RequestMessagesSync(conf ext.RetryConfig, r ext.MessagesRequest) (ext.MessagesResponse, error) {
|
|
var resp ext.MessagesResponse
|
|
|
|
events := make(chan types.EnvelopeEvent, 10)
|
|
var (
|
|
requestID types.HexBytes
|
|
err error
|
|
retries int
|
|
)
|
|
for retries <= conf.MaxRetries {
|
|
sub := api.service.w.SubscribeEnvelopeEvents(events)
|
|
r.Timeout = conf.BaseTimeout + conf.StepTimeout*time.Duration(retries)
|
|
timeout := r.Timeout
|
|
// FIXME this weird conversion is required because MessagesRequest expects seconds but defines time.Duration
|
|
r.Timeout = time.Duration(int(r.Timeout.Seconds()))
|
|
requestID, err = api.RequestMessages(context.Background(), r)
|
|
if err != nil {
|
|
sub.Unsubscribe()
|
|
return resp, err
|
|
}
|
|
mailServerResp, err := ext.WaitForExpiredOrCompleted(types.BytesToHash(requestID), events, timeout)
|
|
sub.Unsubscribe()
|
|
if err == nil {
|
|
resp.Cursor = hex.EncodeToString(mailServerResp.Cursor)
|
|
resp.Error = mailServerResp.Error
|
|
return resp, nil
|
|
}
|
|
retries++
|
|
api.log.Error("[RequestMessagesSync] failed", "err", err, "retries", retries)
|
|
}
|
|
return resp, fmt.Errorf("failed to request messages after %d retries", retries)
|
|
}
|