diff options
Diffstat (limited to 'whisper/whisperv5/api.go')
-rw-r--r-- | whisper/whisperv5/api.go | 490 |
1 files changed, 253 insertions, 237 deletions
diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 9b43f7b70..579efba9e 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -20,15 +20,14 @@ import ( "encoding/json" "errors" "fmt" - mathrand "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover" ) -var whisperOffLineErr = errors.New("whisper is offline") +var whisperOfflineErr = errors.New("whisper is offline") // PublicWhisperAPI provides the whisper RPC service. type PublicWhisperAPI struct { @@ -43,7 +42,7 @@ func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { // Start starts the Whisper worker threads. func (api *PublicWhisperAPI) Start() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Start(nil) } @@ -51,7 +50,7 @@ func (api *PublicWhisperAPI) Start() error { // Stop stops the Whisper worker threads. func (api *PublicWhisperAPI) Stop() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Stop() } @@ -59,179 +58,219 @@ func (api *PublicWhisperAPI) Stop() error { // Version returns the Whisper version this node offers. func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { if api.whisper == nil { - return 0, whisperOffLineErr + return 0, whisperOfflineErr } return hexutil.Uint(api.whisper.Version()), nil } -// Stats returns the Whisper statistics for diagnostics. -func (api *PublicWhisperAPI) Stats() (string, error) { +// Info returns the Whisper statistics for diagnostics. +func (api *PublicWhisperAPI) Info() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.Stats(), nil } -// MarkPeerTrusted marks specific peer trusted, which will allow it +// SetMaxMessageLength sets the maximal message length allowed by this node +func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error { + if api.whisper == nil { + return whisperOfflineErr + } + return api.whisper.SetMaxMessageLength(val) +} + +// SetMinimumPoW sets the minimal PoW required by this node +func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { + if api.whisper == nil { + return whisperOfflineErr + } + return api.whisper.SetMinimumPoW(val) +} + +// AllowP2PMessagesFromPeer marks specific peer trusted, which will allow it // to send historic (expired) messages. -func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { +func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } - return api.whisper.MarkPeerTrusted(peerID) + n, err := discover.ParseNode(enode) + if err != nil { + return errors.New("failed to parse enode of trusted peer: " + err.Error()) + } + return api.whisper.AllowP2PMessagesFromPeer(n.ID[:]) } -// RequestHistoricMessages requests the peer to deliver the old (expired) messages. -// data contains parameters (time frame, payment details, etc.), required -// by the remote email-like server. Whisper is not aware about the data format, -// it will just forward the raw data to the server. -//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { -// if api.whisper == nil { -// return whisperOffLineErr -// } -// return api.whisper.RequestHistoricMessages(peerID, data) -//} - -// HasIdentity checks if the whisper node is configured with the private key +// HasKeyPair checks if the whisper node is configured with the private key // of the specified public pair. -func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { +func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } - return api.whisper.HasIdentity(identity), nil + return api.whisper.HasKeyPair(id), nil } -// DeleteIdentity deletes the specifies key if it exists. -func (api *PublicWhisperAPI) DeleteIdentity(identity string) error { +// DeleteKeyPair deletes the specifies key if it exists. +func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) { if api.whisper == nil { - return whisperOffLineErr + return false, whisperOfflineErr } - api.whisper.DeleteIdentity(identity) - return nil + return api.whisper.DeleteKeyPair(id), nil } -// NewIdentity generates a new cryptographic identity for the client, and injects +// NewKeyPair generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. -func (api *PublicWhisperAPI) NewIdentity() (string, error) { +func (api *PublicWhisperAPI) NewKeyPair() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } - identity := api.whisper.NewIdentity() - return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil + return api.whisper.NewKeyPair() } -// GenerateSymKey generates a random symmetric key and stores it under -// the 'name' id. Will be used in the future for session key exchange. -func (api *PublicWhisperAPI) GenerateSymKey(name string) error { +// GetPublicKey returns the public key for identity id +func (api *PublicWhisperAPI) GetPublicKey(id string) (hexutil.Bytes, error) { if api.whisper == nil { - return whisperOffLineErr + return nil, whisperOfflineErr + } + key, err := api.whisper.GetPrivateKey(id) + if err != nil { + return nil, err } - return api.whisper.GenerateSymKey(name) + return crypto.FromECDSAPub(&key.PublicKey), nil } -// AddSymKey stores the key under the 'name' id. -func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error { +// GetPrivateKey returns the private key for identity id +func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) { if api.whisper == nil { - return whisperOffLineErr + return "", whisperOfflineErr } - return api.whisper.AddSymKey(name, key) + key, err := api.whisper.GetPrivateKey(id) + if err != nil { + return "", err + } + return common.ToHex(crypto.FromECDSA(key)), nil } -// HasSymKey returns true if there is a key associated with the name string. -// Otherwise returns false. -func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) { +// GenerateSymmetricKey generates a random symmetric key and stores it under id, +// which is then returned. Will be used in the future for session key exchange. +func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) { if api.whisper == nil { - return false, whisperOffLineErr + return "", whisperOfflineErr } - res := api.whisper.HasSymKey(name) - return res, nil + return api.whisper.GenerateSymKey() } -// DeleteSymKey deletes the key associated with the name string if it exists. -func (api *PublicWhisperAPI) DeleteSymKey(name string) error { +// AddSymmetricKeyDirect stores the key, and returns its id. +func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) { if api.whisper == nil { - return whisperOffLineErr + return "", whisperOfflineErr } - api.whisper.DeleteSymKey(name) - return nil + return api.whisper.AddSymKeyDirect(key) } -// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. -// Returns the ID of the newly created Filter. -func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { +// AddSymmetricKeyFromPassword generates the key from password, stores it, and returns its id. +func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } + return api.whisper.AddSymKeyFromPassword(password) +} - filter := Filter{ - Src: crypto.ToECDSAPub(common.FromHex(args.From)), - KeySym: api.whisper.GetSymKey(args.KeyName), - PoW: args.PoW, - Messages: make(map[common.Hash]*ReceivedMessage), - AcceptP2P: args.AcceptP2P, - } - if len(filter.KeySym) > 0 { - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) +// HasSymmetricKey returns true if there is a key associated with the given id. +// Otherwise returns false. +func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) { + if api.whisper == nil { + return false, whisperOfflineErr } - filter.Topics = append(filter.Topics, args.Topics...) + res := api.whisper.HasSymKey(id) + return res, nil +} - if len(args.Topics) == 0 && len(args.KeyName) != 0 { - info := "NewFilter: at least one topic must be specified" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// GetSymmetricKey returns the symmetric key associated with the given id. +func (api *PublicWhisperAPI) GetSymmetricKey(name string) (hexutil.Bytes, error) { + if api.whisper == nil { + return nil, whisperOfflineErr + } + b, err := api.whisper.GetSymKey(name) + if err != nil { + return nil, err } + return b, nil +} - if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { - info := "NewFilter: key was not found by name: " + args.KeyName - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// DeleteSymmetricKey deletes the key associated with the name string if it exists. +func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) { + if api.whisper == nil { + return false, whisperOfflineErr } + res := api.whisper.DeleteSymKey(name) + return res, nil +} - if len(args.To) == 0 && len(filter.KeySym) == 0 { - info := "NewFilter: filter must contain either symmetric or asymmetric key" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// Subscribe creates and registers a new filter to watch for inbound whisper messages. +// Returns the ID of the newly created filter. +func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) { + if api.whisper == nil { + return "", whisperOfflineErr } - if len(args.To) != 0 && len(filter.KeySym) != 0 { - info := "NewFilter: filter must not contain both symmetric and asymmetric key" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + filter := Filter{ + Src: crypto.ToECDSAPub(common.FromHex(args.SignedWith)), + PoW: args.MinPoW, + Messages: make(map[common.Hash]*ReceivedMessage), + AllowP2P: args.AllowP2P, } - if len(args.To) > 0 { - dst := crypto.ToECDSAPub(common.FromHex(args.To)) - if !ValidatePublicKey(dst) { - info := "NewFilter: Invalid 'To' address" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) - } - filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) - if filter.KeyAsym == nil { - info := "NewFilter: non-existent identity provided" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + var err error + for i, bt := range args.Topics { + if len(bt) == 0 || len(bt) > 4 { + return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt))) } + filter.Topics = append(filter.Topics, bt) } - if len(args.From) > 0 { + if err = ValidateKeyID(args.Key); err != nil { + return "", errors.New("subscribe: " + err.Error()) + } + + if len(args.SignedWith) > 0 { if !ValidatePublicKey(filter.Src) { - info := "NewFilter: Invalid 'From' address" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + return "", errors.New("subscribe: invalid 'SignedWith' field") + } + } + + if args.Symmetric { + if len(args.Topics) == 0 { + return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption") + } + symKey, err := api.whisper.GetSymKey(args.Key) + if err != nil { + return "", errors.New("subscribe: invalid key ID") + } + if !validateSymmetricKey(symKey) { + return "", errors.New("subscribe: retrieved key is invalid") + } + filter.KeySym = symKey + filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) + } else { + filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key) + if err != nil { + return "", errors.New("subscribe: invalid key ID") + } + if filter.KeyAsym == nil { + return "", errors.New("subscribe: non-existent identity provided") } } - return api.whisper.Watch(&filter) + return api.whisper.Subscribe(&filter) } -// UninstallFilter disables and removes an existing filter. -func (api *PublicWhisperAPI) UninstallFilter(filterId string) { - api.whisper.Unwatch(filterId) +// Unsubscribe disables and removes an existing filter. +func (api *PublicWhisperAPI) Unsubscribe(id string) { + api.whisper.Unsubscribe(id) } -// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. -func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage { +// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval. +func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage { f := api.whisper.GetFilter(filterId) if f != nil { newMail := f.Retrieve() @@ -240,7 +279,8 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage return toWhisperMessages(nil) } -// GetMessages retrieves all the known messages that match a specific filter. +// GetMessages retrieves all the floating messages that match a specific filter. +// It is likely to be called once per session, right after Subscribe call. func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage { all := api.whisper.Messages(filterId) return toWhisperMessages(all) @@ -258,139 +298,107 @@ func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage { // Post creates a whisper message and injects it into the network for distribution. func (api *PublicWhisperAPI) Post(args PostArgs) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } + var err error params := MessageParams{ TTL: args.TTL, - Dst: crypto.ToECDSAPub(common.FromHex(args.To)), - KeySym: api.whisper.GetSymKey(args.KeyName), - Topic: args.Topic, + WorkTime: args.PowTime, + PoW: args.PowTarget, Payload: args.Payload, Padding: args.Padding, - WorkTime: args.WorkTime, - PoW: args.PoW, } - if len(args.From) > 0 { - pub := crypto.ToECDSAPub(common.FromHex(args.From)) - if !ValidatePublicKey(pub) { - info := "Post: Invalid 'From' address" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if len(args.Key) == 0 { + return errors.New("post: key is missing") + } + + if len(args.SignWith) > 0 { + params.Src, err = api.whisper.GetPrivateKey(args.SignWith) + if err != nil { + return err } - params.Src = api.whisper.GetIdentity(string(args.From)) if params.Src == nil { - info := "Post: non-existent identity provided" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + return errors.New("post: empty identity") } } - filter := api.whisper.GetFilter(args.FilterID) - if filter == nil && len(args.FilterID) > 0 { - info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID) - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if len(args.Topic) == TopicLength { + params.Topic = BytesToTopic(args.Topic) + } else if len(args.Topic) != 0 { + return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic))) } - if filter != nil { - // get the missing fields from the filter - if params.KeySym == nil && filter.KeySym != nil { - params.KeySym = filter.KeySym + if args.Type == "sym" { + if err = ValidateKeyID(args.Key); err != nil { + return err } - if params.Src == nil && filter.Src != nil { - params.Src = filter.KeyAsym + params.KeySym, err = api.whisper.GetSymKey(args.Key) + if err != nil { + return err } - if (params.Topic == TopicType{}) { - sz := len(filter.Topics) - if sz < 1 { - info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID) - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } else if sz == 1 { - params.Topic = filter.Topics[0] - } else { - // choose randomly - rnd := mathrand.Intn(sz) - params.Topic = filter.Topics[rnd] - } + if !validateSymmetricKey(params.KeySym) { + return errors.New("post: key for symmetric encryption is invalid") } - } - - // validate - if len(args.KeyName) != 0 && len(params.KeySym) == 0 { - info := "Post: key was not found by name: " + args.KeyName - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) == 0 && len(params.KeySym) == 0 { - info := "Post: message must be encrypted either symmetrically or asymmetrically" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) != 0 && len(params.KeySym) != 0 { - info := "Post: ambigous encryption method requested" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) > 0 { + if len(params.Topic) == 0 { + return errors.New("post: topic is missing for symmetric encryption") + } + } else if args.Type == "asym" { + params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key)) if !ValidatePublicKey(params.Dst) { - info := "Post: Invalid 'To' address" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + return errors.New("post: public key for asymmetric encryption is invalid") } + } else { + return errors.New("post: wrong type (sym/asym)") } // encrypt and send message := NewSentMessage(¶ms) + if message == nil { + return errors.New("post: failed create new message, probably due to failed rand function (OS level)") + } envelope, err := message.Wrap(¶ms) if err != nil { - log.Error(fmt.Sprintf(err.Error())) return err } - if len(envelope.Data) > MaxMessageLength { - info := "Post: message is too big" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) { - info := "Post: topic is missing for symmetric encryption" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if envelope.size() > api.whisper.maxMsgLength { + return errors.New("post: message is too big") } - if args.PeerID != nil { - return api.whisper.SendP2PMessage(args.PeerID, envelope) + if len(args.TargetPeer) != 0 { + n, err := discover.ParseNode(args.TargetPeer) + if err != nil { + return errors.New("post: failed to parse enode of target peer: " + err.Error()) + } + return api.whisper.SendP2PMessage(n.ID[:], envelope) + } else if args.PowTarget < api.whisper.minPoW { + return errors.New("post: target PoW is less than minimum PoW, the message can not be sent") } return api.whisper.Send(envelope) } type PostArgs struct { - TTL uint32 `json:"ttl"` - From string `json:"from"` - To string `json:"to"` - KeyName string `json:"keyname"` - Topic TopicType `json:"topic"` - Padding hexutil.Bytes `json:"padding"` - Payload hexutil.Bytes `json:"payload"` - WorkTime uint32 `json:"worktime"` - PoW float64 `json:"pow"` - FilterID string `json:"filterID"` - PeerID hexutil.Bytes `json:"peerID"` + Type string `json:"type"` // "sym"/"asym" (symmetric or asymmetric) + TTL uint32 `json:"ttl"` // time-to-live in seconds + SignWith string `json:"signWith"` // id of the signing key + Key string `json:"key"` // id of encryption key + Topic hexutil.Bytes `json:"topic"` // topic (4 bytes) + Padding hexutil.Bytes `json:"padding"` // optional padding bytes + Payload hexutil.Bytes `json:"payload"` // payload to be encrypted + PowTime uint32 `json:"powTime"` // maximal time in seconds to be spent on PoW + PowTarget float64 `json:"powTarget"` // minimal PoW required for this message + TargetPeer string `json:"targetPeer"` // peer id (for p2p message only) } type WhisperFilterArgs struct { - To string `json:"to"` - From string `json:"from"` - KeyName string `json:"keyname"` - PoW float64 `json:"pow"` - Topics []TopicType `json:"topics"` - AcceptP2P bool `json:"p2p"` + Symmetric bool // encryption type + Key string // id of the key to be used for decryption + SignedWith string // public key of the sender to be verified + MinPoW float64 // minimal PoW requirement + Topics [][]byte // list of topics (up to 4 bytes each) to match + AllowP2P bool // indicates wheather direct p2p messages are allowed for this filter } // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a @@ -398,22 +406,30 @@ type WhisperFilterArgs struct { func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // Unmarshal the JSON message and sanity check var obj struct { - To string `json:"to"` - From string `json:"from"` - KeyName string `json:"keyname"` - PoW float64 `json:"pow"` - Topics []interface{} `json:"topics"` - AcceptP2P bool `json:"p2p"` + Type string `json:"type"` + Key string `json:"key"` + SignedWith string `json:"signedWith"` + MinPoW float64 `json:"minPoW"` + Topics []interface{} `json:"topics"` + AllowP2P bool `json:"allowP2P"` } if err := json.Unmarshal(b, &obj); err != nil { return err } - args.To = obj.To - args.From = obj.From - args.KeyName = obj.KeyName - args.PoW = obj.PoW - args.AcceptP2P = obj.AcceptP2P + switch obj.Type { + case "sym": + args.Symmetric = true + case "asym": + args.Symmetric = false + default: + return errors.New("wrong type (sym/asym)") + } + + args.Key = obj.Key + args.SignedWith = obj.SignedWith + args.MinPoW = obj.MinPoW + args.AllowP2P = obj.AllowP2P // Construct the topic array if obj.Topics != nil { @@ -428,13 +444,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { return fmt.Errorf("topic[%d] is not a string", i) } } - topicsDecoded := make([]TopicType, len(topics)) + topicsDecoded := make([][]byte, len(topics)) for j, s := range topics { x := common.FromHex(s) - if x == nil || len(x) != TopicLength { + if x == nil || len(x) > TopicLength { return fmt.Errorf("topic[%d] is invalid", j) } - topicsDecoded[j] = BytesToTopic(x) + topicsDecoded[j] = x } args.Topics = topicsDecoded } @@ -444,34 +460,34 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // WhisperMessage is the RPC representation of a whisper message. type WhisperMessage struct { - Topic string `json:"topic"` - Payload string `json:"payload"` - Padding string `json:"padding"` - From string `json:"from"` - To string `json:"to"` - Sent uint32 `json:"sent"` - TTL uint32 `json:"ttl"` - PoW float64 `json:"pow"` - Hash string `json:"hash"` + Topic string `json:"topic"` + Payload string `json:"payload"` + Padding string `json:"padding"` + Src string `json:"signedWith"` + Dst string `json:"recipientPublicKey"` + Timestamp uint32 `json:"timestamp"` + TTL uint32 `json:"ttl"` + PoW float64 `json:"pow"` + Hash string `json:"hash"` } // NewWhisperMessage converts an internal message into an API version. func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage { msg := WhisperMessage{ - Topic: common.ToHex(message.Topic[:]), - Payload: common.ToHex(message.Payload), - Padding: common.ToHex(message.Padding), - Sent: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: common.ToHex(message.EnvelopeHash.Bytes()), + Topic: common.ToHex(message.Topic[:]), + Payload: common.ToHex(message.Payload), + Padding: common.ToHex(message.Padding), + Timestamp: message.Sent, + TTL: message.TTL, + PoW: message.PoW, + Hash: common.ToHex(message.EnvelopeHash.Bytes()), } if message.Dst != nil { - msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst)) + msg.Dst = common.ToHex(crypto.FromECDSAPub(message.Dst)) } if isMessageSigned(message.Raw[0]) { - msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) + msg.Src = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) } return &msg } |