{"id":476529,"date":"2026-04-19T10:15:26","date_gmt":"2026-04-19T10:15:26","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=476529"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=476529","title":{"rendered":"\u041f\u0440\u0438\u0432\u0430\u0442\u043d\u0430\u044f C\u0432\u044f\u0437\u044c \u043d\u0430 Go \u0438 Flutter"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"bordered full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/848\/c9a\/6f1\/848c9a6f16a15609d6e1d035e29e0468.png\" width=\"2752\" height=\"1536\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/848\/c9a\/6f1\/848c9a6f16a15609d6e1d035e29e0468.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/848\/c9a\/6f1\/848c9a6f16a15609d6e1d035e29e0468.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u041e\u0442 \u0430\u0432\u0442\u043e\u0440\u0430<\/h3>\n<p>\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c: <\/p>\n<ul>\n<li>\n<p>\u041d\u0435\u0442 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f end-to-end \u0438 \u043d\u0435 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u043d\u0438\u0433\u0434\u0435 <\/p>\n<\/li>\n<li>\n<p>\u041b\u044e\u0431\u043e\u0439 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043b\u0435\u0433\u043a\u043e \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0435\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u043e\u0431\u0449\u0435\u0439 \u0441\u0435\u0442\u0438  <\/p>\n<\/li>\n<li>\n<p>\u041e\u0434\u0438\u043d \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0441\u0442\u0435\u043a \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043e\u043e\u043f\u0430\u0440\u043a\u0430 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430 Go \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u00a0<strong>libp2p<\/strong>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043e\u0432, \u0438\u043c\u0435\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u0438\u0440\u043e\u0432 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0434\u0435\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 P2P-\u0441\u0435\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043a\u0440\u0430\u0439\u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u0434\u0435\u043b\u044e\u0441\u044c \u043d\u0438\u0436\u0435. <\/p>\n<h3>\u0421\u0442\u0435\u043a<\/h3>\n<p>Flutter \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 UI. \u0412\u0441\u044f \u0441\u0435\u0442\u0435\u0432\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0436\u0438\u0432\u0451\u0442 \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 <code>.dylib<\/code> (macOS), <code>.so<\/code> (Android\/Linux) \u0438\u043b\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 (iOS). Dart \u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f \u0441 Go \u0447\u0435\u0440\u0435\u0437 FFI (Foreign Function Interface) \u2014 \u043f\u0440\u044f\u043c\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b C-\u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043f\u0438\u0440\u0430\u043c\u0438 \u043c\u043e\u0436\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0432\u0443\u043c\u044f \u043f\u0443\u0442\u044f\u043c\u0438: \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u2014\u00a0<strong>Circuit Relay v2<\/strong>. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u0431\u0445\u043e\u0434\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 NAT \u0438 \u0431\u0440\u0430\u043d\u0434\u043c\u0430\u0443\u044d\u0440\u043e\u0432, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u044f\u043c\u043e\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442 \u043c\u0435\u0436\u0434\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u0435\u043d.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/228\/195\/2d8\/2281952d82ad716675f53d6f71331bc4.png\" alt=\"\" title=\"\" width=\"2880\" height=\"1440\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/228\/195\/2d8\/2281952d82ad716675f53d6f71331bc4.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/228\/195\/2d8\/2281952d82ad716675f53d6f71331bc4.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0438 \u0441\u0430\u043c\u044b\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0442\u0430\u043b \u0443 \u043c\u0435\u043d\u044f \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438: \u043a\u0430\u043a \u0438\u0437 Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c Go-\u043a\u043e\u0434? \u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0447\u0435\u0440\u0435\u0437 CGO. Go \u0443\u043c\u0435\u0435\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 C-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u0443\u044e shared library \u0441 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.  <\/p>\n<h3>\u0421\u0431\u043e\u0440\u043a\u0430 Go \u2192 C-shared library<\/h3>\n<h4>Android (so, \u043d\u0443\u0436\u0435\u043d NDK):<\/h4>\n<pre><code class=\"bash\">CGO_ENABLED=1 GOOS=android GOARCH=arm64 \\CC=$ANDROID_NDK\/toolchains\/llvm\/prebuilt\/darwin-x86_64\/bin\/aarch64-linux-android21-clang \\go build -buildmode=c-shared \\-o libp2p_network.so \\.\/main.go<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>iOS (\u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 .a \u0447\u0435\u0440\u0435\u0437 CGO):<\/strong><\/p>\n<pre><code class=\"bash\">CGO_ENABLED=1 GOOS=ios GOARCH=arm64 \\CC=$(xcrun --sdk iphoneos --find clang) \\CGO_CFLAGS=\"-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -miphoneos-version-min=12.0\" \\CGO_LDFLAGS=\"-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -miphoneos-version-min=12.0\" \\go build -buildmode=c-archive \\-o libp2p_network.a \\.\/main.go<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u0441 C-\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 Dart \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 <code>dart:ffi<\/code>.<br \/>\u041d\u0430 Android \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c <code>.so<\/code> \u0432 <code>jniLibs\/arm64-v8a\/<\/code>, \u0438 Flutter \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0433\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u041d\u0430 iOS \u2014 <code>c-archive<\/code> \u0432\u044b\u0434\u0430\u0451\u0442 <code>.a<\/code> + <code>.h<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0438\u043d\u043a\u0443\u044e\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432 Xcode-\u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u0414\u043b\u044f \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 (device + simulator) \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0434\u0432\u0435 <code>.a<\/code> \u043f\u043e\u0434 arm64 \u0438 x86_64, \u0437\u0430\u0442\u0435\u043c \u0441\u043a\u043b\u0435\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>lipo -create<\/code>.<\/p>\n<h3>FFI \u043c\u043e\u0441\u0442: Go \u2192 Dart<\/h3>\n<h4>Go-\u0441\u0442\u043e\u0440\u043e\u043d\u0430: \u044d\u043a\u0441\u043f\u043e\u0440\u0442 C-\u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/h4>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0437 Dart, \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c <code>\/\/export<\/code>:<\/p>\n<pre><code class=\"go\">package main\/*#include &lt;stdlib.h&gt;*\/import \"C\"import (    \"sync\"    \"github.com\/myapp\/p2p\")var (    nodeInstance *p2p.Node    nodeMu       sync.Mutex)\/\/export StartNodefunc StartNode(storagePath *C.char) *C.char {    nodeMu.Lock()    defer nodeMu.Unlock()    if nodeInstance != nil {        return C.CString(nodeInstance.GetPeerID())    }    path := C.GoString(storagePath)    node, err := p2p.NewNode(path)    if err != nil {        return C.CString(\"\")    }    node.SetMessageHandler(func(msg *p2p.Message) {        \/\/ \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0432 \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f polling    })    if err := node.Start(); err != nil {        return C.CString(\"\")    }    nodeInstance = node    return C.CString(node.GetPeerID())}\/\/export SendMessagefunc SendMessage(peerID, content, msgType, id *C.char) C.int {    nodeMu.Lock()    node := nodeInstance    nodeMu.Unlock()    if node == nil {        return -1    }    err := node.SendMessage(        C.GoString(peerID),        C.GoString(content),        C.GoString(msgType),        C.GoString(id),    )    if err != nil {        return -1    }    return 0}\/\/export FreeStringfunc FreeString(s *C.char) {    C.free(unsafe.Pointer(s))}func main() {}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0430\u0436\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b:<\/p>\n<p>&#8212; <code>C.GoString()<\/code> \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438\u0437 C-\u043f\u0430\u043c\u044f\u0442\u0438 \u0432 Go \u2014 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e Dart \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u0441\u0432\u043e\u044e \u043a\u043e\u043f\u0438\u044e<\/p>\n<p>&#8212; <code>C.CString()<\/code> \u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u0432 C-\u0445\u0438\u043f\u0435 \u2014 Dart \u043e\u0431\u044f\u0437\u0430\u043d \u0432\u044b\u0437\u0432\u0430\u0442\u044c <code>FreeString<\/code> \u043f\u043e\u0441\u043b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0438\u043d\u0430\u0447\u0435 \u0443\u0442\u0435\u0447\u043a\u0430<\/p>\n<p>&#8212; \u0412\u0441\u0435 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>main<\/code><\/p>\n<p>&#8212; <code>func main() {}<\/code> \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043f\u0443\u0441\u0442\u0430\u044f<\/p>\n<h4>Dart-\u0441\u0442\u043e\u0440\u043e\u043d\u0430: \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438 \u0432\u044b\u0437\u043e\u0432<\/h4>\n<pre><code class=\"dart\">class P2PNode {  static DynamicLibrary? _lib;  void _loadLibrary() {    if (Platform.isAndroid) {      _lib = DynamicLibrary.open('libp2p_network.so');    } else if (Platform.isIOS) {      _lib = DynamicLibrary.process(); \/\/ \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043b\u0438\u043d\u043a\u043e\u0432\u0430\u043d\u043e    } else if (Platform.isMacOS) {      \/\/ \u0438\u0449\u0435\u043c dylib \u0432 Frameworks \u0431\u0430\u043d\u0434\u043b\u0430      final appDir = Platform.resolvedExecutable;      final frameworksDir = '${File(appDir).parent.path}\/Frameworks';      _lib = DynamicLibrary.open('$frameworksDir\/libp2p_network.dylib');    }    _startNode = _lib!.lookupFunction&lt;        Pointer&lt;Utf8&gt; Function(Pointer&lt;Utf8&gt;),  \/\/ C-\u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430        Pointer&lt;Utf8&gt; Function(Pointer&lt;Utf8&gt;)   \/\/ Dart-\u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430    &gt;('StartNode');    _sendMessage = _lib!.lookupFunction&lt;        Int32 Function(Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;),        int Function(Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;)    &gt;('SendMessage');    _freeString = _lib!.lookupFunction&lt;        Void Function(Pointer&lt;Utf8&gt;),        void Function(Pointer&lt;Utf8&gt;)    &gt;('FreeString');  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u044b\u0437\u043e\u0432 Go-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0437 Dart \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"dart\">Future&lt;String&gt; start() async {  final dir = await getApplicationDocumentsDirectory();  final pathPtr = dir.path.toNativeUtf8();  final resultPtr = _startNode(pathPtr);  final peerId = resultPtr.toDartString();  _freeString(resultPtr);  \/\/ \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c C-\u043f\u0430\u043c\u044f\u0442\u044c  calloc.free(pathPtr);    \/\/ \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c Dart-\u043f\u0430\u043c\u044f\u0442\u044c  return peerId;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 libp2p-\u043d\u043e\u0434\u0430<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0434\u044b \u2014 \u044d\u0442\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 libp2p \u0445\u043e\u0441\u0442\u0430 \u0441 \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430\u043c\u0438 \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430\u043c\u0438:<\/p>\n<pre><code class=\"go\">func NewNode(storagePath string) (*Node, error) {    ctx, cancel := context.WithCancel(context.Background())    \/\/ \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0438\u043b\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c Ed25519-\u043a\u043b\u044e\u0447 (\u044d\u0442\u043e \u043d\u0430\u0448 PeerID)    keyPath := filepath.Join(storagePath, \"identity.key\")    priv, _ := loadOrCreateKey(keyPath)    h, err := libp2p.New(        libp2p.Identity(priv),        libp2p.ListenAddrStrings(            \"\/ip4\/0.0.0.0\/tcp\/0\",            \"\/ip4\/0.0.0.0\/udp\/0\/quic-v1\",        ),        libp2p.EnableNATService(),        libp2p.EnableRelay(),        libp2p.NATPortMap(),        libp2p.EnableAutoRelayWithStaticRelays(relayAddrs),    )    \/\/ Kademlia DHT \u0434\u043b\u044f discovery    kadDHT, _ := dht.New(ctx, h, dht.Mode(dht.ModeAutoServer))    node := &amp;Node{host: h, dht: kadDHT, ctx: ctx}    \/\/ \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439    h.SetStreamHandler(\"\/messaging\/1.0.0\", node.handleStream)    \/\/ \u0421\u043b\u0443\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\/\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u0438\u0440\u043e\u0432    h.Network().Notify(&amp;network.NotifyBundle{        ConnectedF:    func(n network.Network, c network.Conn) { ... },        DisconnectedF: func(n network.Network, c network.Conn) { ... },    })    return node, nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 <strong>PeerID<\/strong> \u2014 \u044d\u0442\u043e \u0445\u0435\u0448 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0433\u043e Ed25519-\u043a\u043b\u044e\u0447\u0430. \u041a\u043b\u044e\u0447 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435. PeerID \u2014 \u044d\u0442\u043e \u0432\u0430\u0448 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0432 \u0441\u0435\u0442\u0438, \u0430\u043d\u0430\u043b\u043e\u0433 \u043d\u043e\u043c\u0435\u0440\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430, \u043d\u043e \u0431\u0435\u0437 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0447\u0435\u043c\u0443-\u043b\u0438\u0431\u043e.<\/p>\n<h4>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/h4>\n<pre><code class=\"go\">func (n *Node) SendMessage(peerIDStr, content, msgType, id string) error {    msg := Message{        ID:      id,        From:    n.host.ID().String(),        To:      peerIDStr,        Content: content,        Type:    msgType,    }    data, _ := json.Marshal(msg)    peerID, _ := peer.Decode(peerIDStr)    \/\/ \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c stream \u043a \u043f\u0438\u0440\u0443 (\u0447\u0435\u0440\u0435\u0437 relay \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e)    s, err := n.host.NewStream(ctx, peerID, \"\/messaging\/1.0.0\")    s.Write(data)    s.Close()    return nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0438\u0440 \u043e\u043d\u043b\u0430\u0439\u043d \u2014 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0415\u0441\u043b\u0438 \u043e\u0444\u0444\u043b\u0430\u0439\u043d \u2014 \u0448\u0438\u0444\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u043a\u043b\u0430\u0434\u0451\u0442\u0441\u044f \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u043d\u043e\u0434\u044b \u0434\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438(\u0434\u0430\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c). \u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0438\u0440 \u0437\u0430\u0431\u0435\u0440\u0451\u0442 \u0432\u0441\u0435 \u043d\u0430\u043a\u043e\u043f\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<h2>\u0417\u0432\u043e\u043d\u043a\u0438<\/h2>\n<p>\u0414\u043b\u044f P2P-\u0437\u0432\u043e\u043d\u043a\u043e\u0432 1 \u043d\u0430 1 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u0440\u0435\u0445\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443, \u043d\u043e \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0432\u044f\u0437\u044c \u0434\u0430\u0436\u0435 \u0437\u0430 \u0436\u0435\u0441\u0442\u043a\u0438\u043c\u0438 NAT:<\/p>\n<p>1. <strong>\u041f\u0440\u044f\u043c\u043e\u0439 UDP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 (Pion ICE):<\/strong> \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0438 \u0441\u0430\u043c\u044b\u0439 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u043a\u0430\u043d\u0430\u043b. \u041f\u0440\u0438 \u043e\u0442\u0432\u0435\u0442\u0435 \u043d\u0430 \u0437\u0432\u043e\u043d\u043e\u043a \u043d\u043e\u0434\u044b \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 libp2p-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u0431\u0435\u0437 \u0433\u0440\u043e\u043c\u043e\u0437\u0434\u043a\u043e\u0433\u043e SDP). \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u044f\u043c\u043e\u0439 UDP-\u043a\u0430\u043d\u0430\u043b, \u0430\u0443\u0434\u0438\u043e-\u0444\u0440\u0435\u0439\u043c\u044b (Opus) \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c (\u043d\u0430 \u0431\u0430\u0437\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 libp2p) \u0438 \u043b\u0435\u0442\u044f\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e.<\/p>\n<p>2. <strong>libp2p DCUtR (Hole Punching):<\/strong> \u0415\u0441\u043b\u0438 \u0447\u0438\u0441\u0442\u044b\u0439 UDP \u043d\u0435 \u043f\u0440\u043e\u0431\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c DCUtR (Direct Connection Upgrade through Relay). \u041f\u0438\u0440\u044b \u0443\u0437\u043d\u0430\u044e\u0442 \u0441\u0432\u043e\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 IP \u0447\u0435\u0440\u0435\u0437 Relay \u0438 \u043f\u0440\u043e\u0431\u0438\u0432\u0430\u044e\u0442 \u043f\u0440\u044f\u043c\u043e\u0435 TCP\/QUIC \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 libp2p.<\/p>\n<p>3. <strong>libp2p stream \u0447\u0435\u0440\u0435\u0437 Relay (\u0424\u043e\u043b\u043b\u0431\u0435\u043a):<\/strong> \u0415\u0441\u043b\u0438 \u043e\u0431\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0437\u0430 \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u043c\u0438 NAT \u0438 \u043f\u0440\u044f\u043c\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0442\u0440\u0430\u0444\u0438\u043a \u0431\u0435\u0441\u0448\u043e\u0432\u043d\u043e \u0438\u0434\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0443\u044e \u043d\u043e\u0434\u0443 \u043f\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u043c\u0443 libp2p-\u0441\u0442\u0440\u0438\u043c\u0443 (<code>\/call\/1.0.0<\/code>).<\/p>\n<h4>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0430\u0443\u0434\u0438\u043e<\/h4>\n<pre><code class=\"go\">func (n *Node) SendAudio(data []byte) error {    call := n.activeCall    if call == nil || call.State != CallStateActive {        return fmt.Errorf(\"no active call\")    }    \/\/ \u0424\u0440\u0435\u0439\u043c: [0xFE][Len 2 bytes][Opus data]    packet := make([]byte, 1+2+len(data))    packet[0] = 0xFE    binary.BigEndian.PutUint16(packet[1:], uint16(len(data)))    copy(packet[3:], data)    call.Stream.Write(packet)    return nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u041f\u0440\u0438\u0435\u043c \u0430\u0443\u0434\u0438\u043e<\/h4>\n<pre><code class=\"go\">func (n *Node) audioReadLoop() {    call := n.activeCall    for {        \/\/ \u0416\u0434\u0451\u043c sync byte        syncBuf := make([]byte, 1)        call.Stream.Read(syncBuf)        switch syncBuf[0] {        case 0xFE: \/\/ \u0430\u0443\u0434\u0438\u043e            lenBuf := make([]byte, 2)            io.ReadFull(call.Stream, lenBuf)            frameLen := binary.BigEndian.Uint16(lenBuf)            opusData := make([]byte, frameLen)            io.ReadFull(call.Stream, opusData)            \/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0451\u043c Opus-\u0444\u0440\u0435\u0439\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443            n.audioHandler(opusData)        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043e\u0444\u0444\u043b\u0430\u0439\u043d.<\/h3>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0435\u0442 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0440\u0435\u0437\u043e\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u0433\u0440\u0443\u0436\u0435\u043d\u043e \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438\u043b\u0438 \u0442\u0435\u043b\u0435\u0444\u043e\u043d \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d?<\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 Push-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f (APNs \u0434\u043b\u044f iOS, FCM \u0434\u043b\u044f Android), \u043d\u043e \u0441 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0433\u043e\u0432\u043e\u0440\u043a\u043e\u0439 \u0440\u0430\u0434\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f E2EE \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438:\u00a0<strong>\u0432 \u0441\u0430\u043c\u043e\u043c \u043f\u0443\u0448\u0435 \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0438\u0447\u0435\u0433\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e<\/strong>. \u0412 \u043d\u0451\u043c \u043d\u0435\u0442 \u043d\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043d\u0438 \u043a\u043b\u044e\u0447\u0435\u0439, \u043d\u0438 \u0434\u0430\u0436\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f. \u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e &#171;\u0441\u043b\u0435\u043f\u043e\u0439&#187; \u0442\u0440\u0438\u0433\u0433\u0435\u0440 (silent\/data push), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043b\u0443\u0436\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0439 \u0446\u0435\u043b\u0438 \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e.<\/p>\n<p>\u041c\u0435\u0445\u0430\u043d\u0438\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<ol>\n<li>\n<p>\u041d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u0435\u0442 \u043f\u0443\u0448-\u0441\u0438\u0433\u043d\u0430\u043b.<\/p>\n<\/li>\n<li>\n<p>\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0430 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0431\u0443\u0434\u0438\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0444\u043e\u043d\u0435 \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u043d\u0430\u0448\u0430 Go-\u043d\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041d\u043e\u0434\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a \u0441\u0435\u0442\u0438 \u0438 \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u0430\u043a\u043e\u043f\u0438\u0432\u0448\u0438\u0435\u0441\u044f \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u0442\u0440\u043e\u0433\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0430\u043c\u043e \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435 \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u043e\u0432, \u043d\u0435 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0435\u0442\u0438\u0440\u0443\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0428\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435: E2EE \u043a\u0430\u043a \u0432 Signal \u0438 WhatsApp<\/h3>\n<p>\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442 \u043b\u044e\u0431\u043e\u0433\u043e \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430. \u041d\u0435 \u0441\u0442\u0430\u043b(\u0434\u0430 \u0438 \u043d\u0435 \u0441\u043c\u043e\u0433 \u0431\u044b \u0431\u044b\u0441\u0442\u0440\u043e) \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434 (\u0441\u0432\u043e\u044e \u043a\u0440\u0438\u043f\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044e) \u0438\u043b\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u043c \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c. \u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 End-to-End \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (E2EE) \u0441 <strong>Perfect Forward Secrecy (PFS)<\/strong> \u0438 <strong>Post-Compromise Security (PCS)<\/strong>.<\/p>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u043d\u0430 \u0434\u0432\u0430 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430: \u043e\u0434\u0438\u043d \u0434\u043b\u044f \u043b\u0438\u0447\u043d\u044b\u0445 \u0447\u0430\u0442\u043e\u0432, \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u0445.<\/p>\n<h4>1. \u041b\u0438\u0447\u043d\u044b\u0435 \u0447\u0430\u0442\u044b (1 \u043d\u0430 1): Double Ratchet<\/h4>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u043e\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c <strong>Double Ratchet<\/strong> (\u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439, \u0447\u0442\u043e \u043b\u0435\u0436\u0438\u0442 \u0432 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Signal). \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <a href=\"https:\/\/github.com\/status-im\/doubleratchet\" rel=\"noopener noreferrer nofollow\"><code>status-im\/doubleratchet<\/code><\/a>.<\/p>\n<p><strong>\u041a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/strong><\/p>\n<p>1. <strong>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f (X3DH):<\/strong> \u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u043a\u043e\u043d\u0442\u0430\u043a\u0442\u0435 \u043f\u0438\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0441\u0432\u043e\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043a\u043b\u044e\u0447\u0438 libp2p (Ed25519 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 X25519) \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Diffie-Hellman \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0433\u043e <code>Root Key<\/code>.<\/p>\n<p>2. <strong>\u0421\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u0439 \u0445\u0440\u0430\u043f\u043e\u0432\u0438\u043a (Symmetric Ratchet):<\/strong> \u041a\u0430\u0436\u0434\u043e\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0435\u0442 \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u043a\u043b\u044e\u0447\u0435\u0439 (KDF) \u0447\u0435\u0440\u0435\u0437 \u0445\u0435\u0448-\u0444\u0443\u043d\u043a\u0446\u0438\u044e. \u041a\u043b\u044e\u0447 \u043e\u0442 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0443\u043d\u0438\u043a\u0430\u043b\u0435\u043d. \u0415\u0441\u043b\u0438 \u0445\u0430\u043a\u0435\u0440 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442 \u043a\u043b\u044e\u0447 \u043e\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u21165, \u043e\u043d \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u21161\u20134 (Forward Secrecy).<\/p>\n<p>3. <strong>\u0410\u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u0439 \u0445\u0440\u0430\u043f\u043e\u0432\u0438\u043a (DH Ratchet):<\/strong> \u041f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043a \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435 \u044d\u0444\u0435\u043c\u0435\u0440\u043d\u044b\u0435 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 (Diffie-Hellman). \u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 <code>Root Key<\/code>. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u044b\u043b\u043e \u0441\u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0435\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e (\u043a\u043b\u044e\u0447\u0438 \u0443\u0442\u0435\u043a\u043b\u0438), \u043d\u043e \u043f\u043e\u0442\u043e\u043c \u0445\u0430\u043a\u0435\u0440 \u043f\u043e\u0442\u0435\u0440\u044f\u043b \u043a \u043d\u0435\u043c\u0443 \u0434\u043e\u0441\u0442\u0443\u043f \u2014 \u043f\u043e\u0441\u043b\u0435 \u043f\u0430\u0440\u044b \u043d\u043e\u0432\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 \u043e\u0431\u043d\u043e\u0432\u044f\u0442\u0441\u044f, \u0438 \u0445\u0430\u043a\u0435\u0440 \u0441\u043d\u043e\u0432\u0430 \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u043a\u0443 (Post-Compromise Security).<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0444\u0444\u043b\u0430\u0439\u043d\u0435 (\u0447\u0435\u0440\u0435\u0437 Relay-\u0441\u0435\u0440\u0432\u0435\u0440), \u043e\u043d\u043e \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c \u0441\u0435\u0441\u0441\u0438\u0438. Relay \u0432\u0438\u0434\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0439 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u043c\u0443\u0441\u043e\u0440.<\/p>\n<h4>2. \u0413\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u0435 \u0447\u0430\u0442\u044b: Messaging Layer Security (MLS)<\/h4>\n<p>Double Ratchet \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043b\u044f \u0434\u0432\u0443\u0445 \u0447\u0435\u043b\u043e\u0432\u0435\u043a, \u043d\u043e \u0432 \u0433\u0440\u0443\u043f\u043f\u0430\u0445 \u043e\u043d \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u0448\u043c\u0430\u0440: \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u0433\u0440\u0443\u043f\u043f\u0443 \u0438\u0437 50 \u0447\u0435\u043b\u043e\u0432\u0435\u043a, \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e 50 \u0440\u0430\u0437 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 (Sender Keys). \u042d\u0442\u043e \u0443\u0431\u0438\u0432\u0430\u0435\u0442 \u0431\u0430\u0442\u0430\u0440\u0435\u044e \u0438 \u0441\u0435\u0442\u044c.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f \u0432\u043d\u0435\u0434\u0440\u0438\u043b <strong>MLS (Messaging Layer Security, RFC 9420)<\/strong> \u2014 \u043d\u043e\u0432\u0435\u0439\u0448\u0438\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 IETF \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u043e\u0432\u043e\u0433\u043e E2EE. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/thomas-vilte\/mls-go\" rel=\"noopener noreferrer nofollow\"><code>mls-go<\/code><\/a>.<\/p>\n<p><strong>\u0412 \u0447\u0435\u043c \u043c\u0430\u0433\u0438\u044f MLS:<\/strong><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, MLS \u0441\u0442\u0440\u043e\u0438\u0442 \u0431\u0438\u043d\u0430\u0440\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u043a\u043b\u044e\u0447\u0435\u0439 (Ratchet Tree).<\/p>\n<p>* \u0413\u0440\u0443\u043f\u043f\u0430 \u0438\u043c\u0435\u0435\u0442 \u043e\u0434\u0438\u043d \u043e\u0431\u0449\u0438\u0439 \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439.<\/p>\n<p>* \u041f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0434\u0435\u0440\u0435\u0432\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f (\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>Commit<\/code> \u0438 <code>Welcome<\/code> \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f), \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u044d\u043f\u043e\u0445\u0430 (Epoch) \u0441 \u043d\u043e\u0432\u044b\u043c \u043e\u0431\u0449\u0438\u043c \u043a\u043b\u044e\u0447\u043e\u043c.<\/p>\n<p>* \u0412\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f\/\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0435\u0439 \u043b\u043e\u0433\u0430\u0440\u0438\u0444\u043c\u0438\u0447\u0435\u0441\u043a\u0430\u044f <code>O(log N)<\/code>, \u0430 \u043d\u0435 \u043b\u0438\u043d\u0435\u0439\u043d\u0430\u044f <code>O(N)<\/code>.<\/p>\n<p><strong>\u0418\u0442\u043e\u0433:<\/strong> \u0413\u0440\u0443\u043f\u043f\u044b \u043d\u0430 \u0441\u043e\u0442\u043d\u0438 \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f \u0442\u0430\u043a \u0436\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0441 \u0442\u0430\u043a\u0438\u043c\u0438 \u0436\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f\u043c\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 (PFS \u0438 PCS), \u043a\u0430\u043a \u0438 \u043b\u0438\u0447\u043d\u044b\u0435 \u0447\u0430\u0442\u044b. Relay-\u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 (fan-out) \u043e\u0434\u0438\u043d \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 \u0432\u0441\u0435\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0433\u0440\u0443\u043f\u043f\u044b, \u043d\u0435 \u0438\u043c\u0435\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u043b\u044e\u0447\u0430\u043c \u0434\u0435\u0440\u0435\u0432\u0430.<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0432\u0438\u0436\u0443 \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0430, \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442. \u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c: <\/p>\n<ul>\n<li>\n<p><strong>\u0424\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0445 \u043d\u043e\u0434<\/strong> \u2014 \u043b\u044e\u0431\u043e\u0439 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0441\u0432\u043e\u044e, DHT \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043a\u0430\u043a \u0432 Matrix. <\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043b\u043d\u044b\u0439 P2P \u0440\u0435\u0436\u0438\u043c<\/strong>. \u041a\u0430\u043a \u0443 Jami \u0438 \u0435\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u0435 \u0437\u0432\u043e\u043d\u043a\u0438. <\/strong><\/p>\n<\/li>\n<li>\n<p><strong>Open-source<\/strong> \u2014 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 \u043f\u0430\u043a\u0435\u0442 (Go + Dart) <\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430 &#8212; <a href=\"https:\/\/apps.apple.com\/ru\/app\/%D1%81%D0%B2%D1%8F%D0%B7%D1%8C\/id6760964559\" rel=\"noopener noreferrer nofollow\">App Store<\/a>. \u0427\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u0432\u044b\u043b\u043e\u0436\u0443 \u0432 \u0433\u0443\u0433\u043b \u043c\u0430\u0440\u043a\u0435\u0442. \u0412\u0441\u0435\u043c \u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1025202\/\">https:\/\/habr.com\/ru\/articles\/1025202\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041e\u0442 \u0430\u0432\u0442\u043e\u0440\u0430\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c: \u041d\u0435\u0442 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f end-to-end \u0438 \u043d\u0435 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u043d\u0438\u0433\u0434\u0435 \u041b\u044e\u0431\u043e\u0439 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043b\u0435\u0433\u043a\u043e \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0435\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u043e\u0431\u0449\u0435\u0439 \u0441\u0435\u0442\u0438  \u041e\u0434\u0438\u043d \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0441\u0442\u0435\u043a \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043e\u043e\u043f\u0430\u0440\u043a\u0430 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432\u041d\u0430 Go \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u00a0libp2p, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043e\u0432, \u0438\u043c\u0435\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u0438\u0440\u043e\u0432 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0434\u0435\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 P2P-\u0441\u0435\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043a\u0440\u0430\u0439\u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u0434\u0435\u043b\u044e\u0441\u044c \u043d\u0438\u0436\u0435. \u0421\u0442\u0435\u043aFlutter \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 UI. \u0412\u0441\u044f \u0441\u0435\u0442\u0435\u0432\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0436\u0438\u0432\u0451\u0442 \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 .dylib (macOS), .so (Android\/Linux) \u0438\u043b\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 (iOS). Dart \u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f \u0441 Go \u0447\u0435\u0440\u0435\u0437 FFI (Foreign Function Interface) \u2014 \u043f\u0440\u044f\u043c\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b C-\u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043f\u0438\u0440\u0430\u043c\u0438 \u043c\u043e\u0436\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0432\u0443\u043c\u044f \u043f\u0443\u0442\u044f\u043c\u0438: \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u2014\u00a0Circuit Relay v2. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u0431\u0445\u043e\u0434\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 NAT \u0438 \u0431\u0440\u0430\u043d\u0434\u043c\u0430\u0443\u044d\u0440\u043e\u0432, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u044f\u043c\u043e\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442 \u043c\u0435\u0436\u0434\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u0435\u043d.\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0438 \u0441\u0430\u043c\u044b\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0442\u0430\u043b \u0443 \u043c\u0435\u043d\u044f \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438: \u043a\u0430\u043a \u0438\u0437 Flutter-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c Go-\u043a\u043e\u0434? \u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0447\u0435\u0440\u0435\u0437 CGO. Go \u0443\u043c\u0435\u0435\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 C-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u0443\u044e shared library \u0441 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.  \u0421\u0431\u043e\u0440\u043a\u0430 Go \u2192 C-shared libraryAndroid (so, \u043d\u0443\u0436\u0435\u043d NDK):CGO_ENABLED=1 GOOS=android GOARCH=arm64 \\CC=$ANDROID_NDK\/toolchains\/llvm\/prebuilt\/darwin-x86_64\/bin\/aarch64-linux-android21-clang \\go build -buildmode=c-shared \\-o libp2p_network.so \\.\/main.goiOS (\u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 .a \u0447\u0435\u0440\u0435\u0437 CGO):CGO_ENABLED=1 GOOS=ios GOARCH=arm64 \\CC=$(xcrun &#8212;sdk iphoneos &#8212;find clang) \\CGO_CFLAGS=&#187;-isysroot $(xcrun &#8212;sdk iphoneos &#8212;show-sdk-path) -arch arm64 -miphoneos-version-min=12.0&#8243; \\CGO_LDFLAGS=&#187;-isysroot $(xcrun &#8212;sdk iphoneos &#8212;show-sdk-path) -arch arm64 -miphoneos-version-min=12.0&#8243; \\go build -buildmode=c-archive \\-o libp2p_network.a \\.\/main.go\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u0441 C-\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 Dart \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 dart:ffi.\u041d\u0430 Android \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c .so \u0432 jniLibs\/arm64-v8a\/, \u0438 Flutter \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0433\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u041d\u0430 iOS \u2014 c-archive \u0432\u044b\u0434\u0430\u0451\u0442 .a + .h, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0438\u043d\u043a\u0443\u044e\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432 Xcode-\u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u0414\u043b\u044f \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 (device + simulator) \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0434\u0432\u0435 .a \u043f\u043e\u0434 arm64 \u0438 x86_64, \u0437\u0430\u0442\u0435\u043c \u0441\u043a\u043b\u0435\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 lipo -create.FFI \u043c\u043e\u0441\u0442: Go \u2192 DartGo-\u0441\u0442\u043e\u0440\u043e\u043d\u0430: \u044d\u043a\u0441\u043f\u043e\u0440\u0442 C-\u0444\u0443\u043d\u043a\u0446\u0438\u0439\u041a\u0430\u0436\u0434\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0437 Dart, \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c \/\/export:package main\/*#include &lt;stdlib.h&gt;*\/import &#171;C&#187;import (    &#171;sync&#187;    &#171;github.com\/myapp\/p2p&#187;)var (    nodeInstance *p2p.Node    nodeMu       sync.Mutex)\/\/export StartNodefunc StartNode(storagePath *C.char) *C.char {    nodeMu.Lock()    defer nodeMu.Unlock()    if nodeInstance != nil {        return C.CString(nodeInstance.GetPeerID())    }    path := C.GoString(storagePath)    node, err := p2p.NewNode(path)    if err != nil {        return C.CString(&#171;&#187;)    }    node.SetMessageHandler(func(msg *p2p.Message) {        \/\/ \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0432 \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f polling    })    if err := node.Start(); err != nil {        return C.CString(&#171;&#187;)    }    nodeInstance = node    return C.CString(node.GetPeerID())}\/\/export SendMessagefunc SendMessage(peerID, content, msgType, id *C.char) C.int {    nodeMu.Lock()    node := nodeInstance    nodeMu.Unlock()    if node == nil {        return -1    }    err := node.SendMessage(        C.GoString(peerID),        C.GoString(content),        C.GoString(msgType),        C.GoString(id),    )    if err != nil {        return -1    }    return 0}\/\/export FreeStringfunc FreeString(s *C.char) {    C.free(unsafe.Pointer(s))}func main() {}\u0412\u0430\u0436\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b:- C.GoString() \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438\u0437 C-\u043f\u0430\u043c\u044f\u0442\u0438 \u0432 Go \u2014 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e Dart \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u0441\u0432\u043e\u044e \u043a\u043e\u043f\u0438\u044e- C.CString() \u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u0432 C-\u0445\u0438\u043f\u0435 \u2014 Dart \u043e\u0431\u044f\u0437\u0430\u043d \u0432\u044b\u0437\u0432\u0430\u0442\u044c FreeString \u043f\u043e\u0441\u043b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0438\u043d\u0430\u0447\u0435 \u0443\u0442\u0435\u0447\u043a\u0430- \u0412\u0441\u0435 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 main- func main() {} \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043f\u0443\u0441\u0442\u0430\u044fDart-\u0441\u0442\u043e\u0440\u043e\u043d\u0430: \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438 \u0432\u044b\u0437\u043e\u0432class P2PNode {  static DynamicLibrary? _lib;  void _loadLibrary() {    if (Platform.isAndroid) {      _lib = DynamicLibrary.open(&#8216;libp2p_network.so&#8217;);    } else if (Platform.isIOS) {      _lib = DynamicLibrary.process(); \/\/ \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043b\u0438\u043d\u043a\u043e\u0432\u0430\u043d\u043e    } else if (Platform.isMacOS) {      \/\/ \u0438\u0449\u0435\u043c dylib \u0432 Frameworks \u0431\u0430\u043d\u0434\u043b\u0430      final appDir = Platform.resolvedExecutable;      final frameworksDir = &#8216;${File(appDir).parent.path}\/Frameworks&#8217;;      _lib = DynamicLibrary.open(&#8216;$frameworksDir\/libp2p_network.dylib&#8217;);    }    _startNode = _lib!.lookupFunction&lt;        Pointer&lt;Utf8&gt; Function(Pointer&lt;Utf8&gt;),  \/\/ C-\u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430        Pointer&lt;Utf8&gt; Function(Pointer&lt;Utf8&gt;)   \/\/ Dart-\u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430    &gt;(&#8216;StartNode&#8217;);    _sendMessage = _lib!.lookupFunction&lt;        Int32 Function(Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;),        int Function(Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;, Pointer&lt;Utf8&gt;)    &gt;(&#8216;SendMessage&#8217;);    _freeString = _lib!.lookupFunction&lt;        Void Function(Pointer&lt;Utf8&gt;),        void Function(Pointer&lt;Utf8&gt;)    &gt;(&#8216;FreeString&#8217;);  }}\u0412\u044b\u0437\u043e\u0432 Go-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0437 Dart \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:Future&lt;String&gt; start() async {  final dir = await getApplicationDocumentsDirectory();  final pathPtr = dir.path.toNativeUtf8();  final resultPtr = _startNode(pathPtr);  final peerId = resultPtr.toDartString();  _freeString(resultPtr);  \/\/ \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c C-\u043f\u0430\u043c\u044f\u0442\u044c  calloc.free(pathPtr);    \/\/ \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c Dart-\u043f\u0430\u043c\u044f\u0442\u044c  return peerId;}\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 libp2p-\u043d\u043e\u0434\u0430\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0434\u044b \u2014 \u044d\u0442\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 libp2p \u0445\u043e\u0441\u0442\u0430 \u0441 \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430\u043c\u0438 \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430\u043c\u0438:func NewNode(storagePath string) (*Node, error) {    ctx, cancel := context.WithCancel(context.Background())    \/\/ \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0438\u043b\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c Ed25519-\u043a\u043b\u044e\u0447 (\u044d\u0442\u043e \u043d\u0430\u0448 PeerID)    keyPath := filepath.Join(storagePath, &#171;identity.key&#187;)    priv, _ := loadOrCreateKey(keyPath)    h, err := libp2p.New(        libp2p.Identity(priv),        libp2p.ListenAddrStrings(            &#171;\/ip4\/0.0.0.0\/tcp\/0&#187;,            &#171;\/ip4\/0.0.0.0\/udp\/0\/quic-v1&#187;,        ),        libp2p.EnableNATService(),        libp2p.EnableRelay(),        libp2p.NATPortMap(),        libp2p.EnableAutoRelayWithStaticRelays(relayAddrs),    )    \/\/ Kademlia DHT \u0434\u043b\u044f discovery    kadDHT, _ := dht.New(ctx, h, dht.Mode(dht.ModeAutoServer))    node := &amp;Node{host: h, dht: kadDHT, ctx: ctx}    \/\/ \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439    h.SetStreamHandler(&#171;\/messaging\/1.0.0&#187;, node.handleStream)    \/\/ \u0421\u043b\u0443\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\/\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u0438\u0440\u043e\u0432    h.Network().Notify(&amp;network.NotifyBundle{        ConnectedF:    func(n network.Network, c network.Conn) { &#8230; },        DisconnectedF: func(n network.Network, c network.Conn) { &#8230; },    })    return node, nil}\u041a\u0430\u0436\u0434\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 PeerID \u2014 \u044d\u0442\u043e \u0445\u0435\u0448 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0433\u043e Ed25519-\u043a\u043b\u044e\u0447\u0430. \u041a\u043b\u044e\u0447 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435. PeerID \u2014 \u044d\u0442\u043e \u0432\u0430\u0448 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0432 \u0441\u0435\u0442\u0438, \u0430\u043d\u0430\u043b\u043e\u0433 \u043d\u043e\u043c\u0435\u0440\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430, \u043d\u043e \u0431\u0435\u0437 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0447\u0435\u043c\u0443-\u043b\u0438\u0431\u043e.\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439func (n *Node) SendMessage(peerIDStr, content, msgType, id string) error {    msg := Message{        ID:      id,        From:    n.host.ID().String(),        To:      peerIDStr,        Content: content,        Type:    msgType,    }    data, _ := json.Marshal(msg)    peerID, _ := peer.Decode(peerIDStr)    \/\/ \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c stream \u043a \u043f\u0438\u0440\u0443 (\u0447\u0435\u0440\u0435\u0437 relay \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e)    s, err := n.host.NewStream(ctx, peerID, &#171;\/messaging\/1.0.0&#187;)    s.Write(data)    s.Close()    return nil}\u0415\u0441\u043b\u0438 \u043f\u0438\u0440 \u043e\u043d\u043b\u0430\u0439\u043d \u2014 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0415\u0441\u043b\u0438 \u043e\u0444\u0444\u043b\u0430\u0439\u043d \u2014 \u0448\u0438\u0444\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u043a\u043b\u0430\u0434\u0451\u0442\u0441\u044f \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u043d\u043e\u0434\u044b \u0434\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438(\u0434\u0430\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c). \u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0438\u0440 \u0437\u0430\u0431\u0435\u0440\u0451\u0442 \u0432\u0441\u0435 \u043d\u0430\u043a\u043e\u043f\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.\u0417\u0432\u043e\u043d\u043a\u0438\u0414\u043b\u044f P2P-\u0437\u0432\u043e\u043d\u043a\u043e\u0432 1 \u043d\u0430 1 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u0440\u0435\u0445\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443, \u043d\u043e \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0432\u044f\u0437\u044c \u0434\u0430\u0436\u0435 \u0437\u0430 \u0436\u0435\u0441\u0442\u043a\u0438\u043c\u0438 NAT:1. \u041f\u0440\u044f\u043c\u043e\u0439 UDP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 (Pion ICE): \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0438 \u0441\u0430\u043c\u044b\u0439 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u043a\u0430\u043d\u0430\u043b. \u041f\u0440\u0438 \u043e\u0442\u0432\u0435\u0442\u0435 \u043d\u0430 \u0437\u0432\u043e\u043d\u043e\u043a \u043d\u043e\u0434\u044b \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 libp2p-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u0431\u0435\u0437 \u0433\u0440\u043e\u043c\u043e\u0437\u0434\u043a\u043e\u0433\u043e SDP). \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u044f\u043c\u043e\u0439 UDP-\u043a\u0430\u043d\u0430\u043b, \u0430\u0443\u0434\u0438\u043e-\u0444\u0440\u0435\u0439\u043c\u044b (Opus) \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c (\u043d\u0430 \u0431\u0430\u0437\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 libp2p) \u0438 \u043b\u0435\u0442\u044f\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e.2. libp2p DCUtR (Hole Punching): \u0415\u0441\u043b\u0438 \u0447\u0438\u0441\u0442\u044b\u0439 UDP \u043d\u0435 \u043f\u0440\u043e\u0431\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c DCUtR (Direct Connection Upgrade through Relay). \u041f\u0438\u0440\u044b \u0443\u0437\u043d\u0430\u044e\u0442 \u0441\u0432\u043e\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 IP \u0447\u0435\u0440\u0435\u0437 Relay \u0438 \u043f\u0440\u043e\u0431\u0438\u0432\u0430\u044e\u0442 \u043f\u0440\u044f\u043c\u043e\u0435 TCP\/QUIC \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 libp2p.3. libp2p stream \u0447\u0435\u0440\u0435\u0437 Relay (\u0424\u043e\u043b\u043b\u0431\u0435\u043a): \u0415\u0441\u043b\u0438 \u043e\u0431\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0437\u0430 \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u043c\u0438 NAT \u0438 \u043f\u0440\u044f\u043c\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0442\u0440\u0430\u0444\u0438\u043a \u0431\u0435\u0441\u0448\u043e\u0432\u043d\u043e \u0438\u0434\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0443\u044e \u043d\u043e\u0434\u0443 \u043f\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u043c\u0443 libp2p-\u0441\u0442\u0440\u0438\u043c\u0443 (\/call\/1.0.0).\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0430\u0443\u0434\u0438\u043efunc (n *Node) SendAudio(data []byte) error {    call := n.activeCall    if call == nil || call.State != CallStateActive {        return fmt.Errorf(&#171;no active call&#187;)    }    \/\/ \u0424\u0440\u0435\u0439\u043c: [0xFE][Len 2 bytes][Opus data]    packet := make([]byte, 1+2+len(data))    packet[0] = 0xFE    binary.BigEndian.PutUint16(packet[1:], uint16(len(data)))    copy(packet[3:], data)    call.Stream.Write(packet)    return nil}\u041f\u0440\u0438\u0435\u043c \u0430\u0443\u0434\u0438\u043efunc (n *Node) audioReadLoop() {    call := n.activeCall    for {        \/\/ \u0416\u0434\u0451\u043c sync byte        syncBuf := make([]byte, 1)        call.Stream.Read(syncBuf)        switch syncBuf[0] {        case 0xFE: \/\/ \u0430\u0443\u0434\u0438\u043e            lenBuf := make([]byte, 2)            io.ReadFull(call.Stream, lenBuf)            frameLen := binary.BigEndian.Uint16(lenBuf)            opusData := make([]byte, frameLen)            io.ReadFull(call.Stream, opusData)            \/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0451\u043c Opus-\u0444\u0440\u0435\u0439\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443            n.audioHandler(opusData)        }    }}\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043e\u0444\u0444\u043b\u0430\u0439\u043d.\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0435\u0442 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0440\u0435\u0437\u043e\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u0433\u0440\u0443\u0436\u0435\u043d\u043e \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438\u043b\u0438 \u0442\u0435\u043b\u0435\u0444\u043e\u043d \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d?\u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 Push-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f (APNs \u0434\u043b\u044f iOS, FCM \u0434\u043b\u044f Android), \u043d\u043e \u0441 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0433\u043e\u0432\u043e\u0440\u043a\u043e\u0439 \u0440\u0430\u0434\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f E2EE \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438:\u00a0\u0432 \u0441\u0430\u043c\u043e\u043c \u043f\u0443\u0448\u0435 \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0438\u0447\u0435\u0433\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e. \u0412 \u043d\u0451\u043c \u043d\u0435\u0442 \u043d\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043d\u0438 \u043a\u043b\u044e\u0447\u0435\u0439, \u043d\u0438 \u0434\u0430\u0436\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-476529","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/476529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=476529"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/476529\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=476529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=476529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=476529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}