Major refactoring of the code base into Backend structure. Increase version number to Alpha 6 to prepare for the upcoming release.

This commit is contained in:
Kleissner
2021-12-29 05:29:34 +01:00
parent ade13d6422
commit f2c344dc96
40 changed files with 642 additions and 649 deletions

View File

@@ -24,35 +24,35 @@ type BlockchainCache struct {
store *blockchain.MultiStore
peerLock *locker.Locker
backend *Backend
}
func initBlockchainCache(BlockchainDirectory string, MaxBlockSize, MaxBlockCount, LimitTotalRecords uint64) (cache *BlockchainCache) {
if BlockchainDirectory == "" {
return nil
func (backend *Backend) initBlockchainCache() {
if backend.Config.BlockchainGlobal == "" {
return
}
backend.GlobalBlockchainCache = &BlockchainCache{
backend: backend,
BlockchainDirectory: backend.Config.BlockchainGlobal,
MaxBlockSize: backend.Config.CacheMaxBlockSize,
MaxBlockCount: backend.Config.CacheMaxBlockCount,
LimitTotalRecords: backend.Config.LimitTotalRecords,
}
var err error
cache = &BlockchainCache{
BlockchainDirectory: BlockchainDirectory,
MaxBlockSize: MaxBlockSize,
MaxBlockCount: MaxBlockCount,
LimitTotalRecords: LimitTotalRecords,
}
cache.store, err = blockchain.InitMultiStore(BlockchainDirectory)
backend.GlobalBlockchainCache.store, err = blockchain.InitMultiStore(backend.Config.BlockchainGlobal)
if err != nil {
Filters.LogError("initBlockchainCache", "initializing database '%s': %s", BlockchainDirectory, err.Error())
return nil
backend.Filters.LogError("initBlockchainCache", "initializing database '%s': %s", backend.Config.BlockchainGlobal, err.Error())
return
}
cache.peerLock = locker.Initialize()
backend.GlobalBlockchainCache.peerLock = locker.Initialize()
if LimitTotalRecords > 0 && cache.store.Database.Count() >= LimitTotalRecords {
cache.ReadOnly = true
if backend.Config.LimitTotalRecords > 0 && backend.GlobalBlockchainCache.store.Database.Count() >= backend.Config.LimitTotalRecords {
backend.GlobalBlockchainCache.ReadOnly = true
}
return cache
}
// SeenBlockchainVersion shall be called with information about another peer's blockchain.
@@ -76,7 +76,7 @@ func (cache *BlockchainCache) SeenBlockchainVersion(peer *PeerInfo) {
cache.store.WriteBlock(peer.PublicKey, peer.BlockchainVersion, targetBlock.Offset, data)
header.ListBlocks = append(header.ListBlocks, targetBlock.Offset)
currentBackend.SearchIndex.IndexNewBlock(peer.PublicKey, peer.BlockchainVersion, targetBlock.Offset, data)
cache.backend.SearchIndex.IndexNewBlock(peer.PublicKey, peer.BlockchainVersion, targetBlock.Offset, data)
})
// only update the blockchain header if it changed
@@ -98,7 +98,7 @@ func (cache *BlockchainCache) SeenBlockchainVersion(peer *PeerInfo) {
case blockchain.MultiStatusInvalidRemote:
cache.store.DeleteBlockchain(peer.PublicKey, header)
currentBackend.SearchIndex.UnindexBlockchain(peer.PublicKey)
cache.backend.SearchIndex.UnindexBlockchain(peer.PublicKey)
case blockchain.MultiStatusHeaderNA:
if header, err = cache.store.NewBlockchainHeader(peer.PublicKey, peer.BlockchainVersion, peer.BlockchainHeight); err != nil {
@@ -111,7 +111,7 @@ func (cache *BlockchainCache) SeenBlockchainVersion(peer *PeerInfo) {
// delete existing data first, then create it new
cache.store.DeleteBlockchain(peer.PublicKey, header)
currentBackend.SearchIndex.UnindexBlockchain(peer.PublicKey)
cache.backend.SearchIndex.UnindexBlockchain(peer.PublicKey)
if header, err = cache.store.NewBlockchainHeader(peer.PublicKey, peer.BlockchainVersion, peer.BlockchainHeight); err != nil {
return
@@ -138,12 +138,12 @@ func (cache *BlockchainCache) SeenBlockchainVersion(peer *PeerInfo) {
// It will use the blockchain version and height to update the data lake as appropriate.
// This function is called in the Go routine of the packet worker and therefore must not stall.
func (peer *PeerInfo) remoteBlockchainUpdate() {
if currentBackend.GlobalBlockchainCache == nil || currentBackend.GlobalBlockchainCache.ReadOnly || peer.BlockchainVersion == 0 && peer.BlockchainHeight == 0 {
if peer.Backend.GlobalBlockchainCache == nil || peer.Backend.GlobalBlockchainCache.ReadOnly || peer.BlockchainVersion == 0 && peer.BlockchainHeight == 0 {
return
}
// TODO: This entire function should be instead a non-blocking message via a buffer channel.
go currentBackend.GlobalBlockchainCache.SeenBlockchainVersion(peer)
go peer.Backend.GlobalBlockchainCache.SeenBlockchainVersion(peer)
}
func (cache *BlockchainCache) ReadFile(PublicKey *btcec.PublicKey, Version, BlockNumber uint64, FileID uuid.UUID) (file blockchain.BlockRecordFile, raw []byte, found bool, err error) {
@@ -164,14 +164,14 @@ func (cache *BlockchainCache) ReadFile(PublicKey *btcec.PublicKey, Version, Bloc
// ReadBlock reads a block and decodes the records.
func (cache *BlockchainCache) ReadBlock(PublicKey *btcec.PublicKey, Version, BlockNumber uint64) (decoded *blockchain.BlockDecoded, raw []byte, found bool, err error) {
// requesting a block from the user's blockchain?
if PublicKey.IsEqual(peerPublicKey) {
_, _, version := UserBlockchain.Header()
if PublicKey.IsEqual(cache.backend.peerPublicKey) {
_, _, version := cache.backend.UserBlockchain.Header()
if Version != version {
return nil, nil, false, nil
}
var status int
raw, status, err = UserBlockchain.GetBlockRaw(BlockNumber)
raw, status, err = cache.backend.UserBlockchain.GetBlockRaw(BlockNumber)
if err != nil || status != blockchain.StatusOK {
return nil, raw, false, err
}

View File

@@ -12,28 +12,25 @@ import (
"github.com/PeernetOfficial/core/blockchain"
)
// UserBlockchain is the user's blockchain and exports functions to directly read and write it
var UserBlockchain *blockchain.Blockchain
// initUserBlockchain initializes the users blockchain. It creates the blockchain file if it does not exist already.
// If it is corrupted, it will log the error and exit the process.
func initUserBlockchain() {
func (backend *Backend) initUserBlockchain() {
var err error
UserBlockchain, err = blockchain.Init(peerPrivateKey, config.BlockchainMain)
backend.UserBlockchain, err = blockchain.Init(backend.peerPrivateKey, backend.Config.BlockchainMain)
if err != nil {
Filters.LogError("initUserBlockchain", "error: %s\n", err.Error())
backend.Filters.LogError("initUserBlockchain", "error: %s\n", err.Error())
os.Exit(ExitBlockchainCorrupt)
}
}
// Index the user's blockchain each time there is an update.
func (backend *Backend) userBlockchainUpdateSearchIndex() {
UserBlockchain.BlockchainUpdate = func(blockchainU *blockchain.Blockchain, oldHeight, oldVersion, newHeight, newVersion uint64) {
backend.UserBlockchain.BlockchainUpdate = func(blockchainU *blockchain.Blockchain, oldHeight, oldVersion, newHeight, newVersion uint64) {
if newVersion != oldVersion || newHeight < oldHeight {
// invalidate search index data for the user's blockchain
backend.SearchIndex.UnindexBlockchain(peerPublicKey)
backend.SearchIndex.UnindexBlockchain(backend.peerPublicKey)
// reindex everything
for blockN := uint64(0); blockN < newHeight; blockN++ {
@@ -42,7 +39,7 @@ func (backend *Backend) userBlockchainUpdateSearchIndex() {
continue
}
backend.SearchIndex.IndexNewBlock(peerPublicKey, newVersion, blockN, raw)
backend.SearchIndex.IndexNewBlock(backend.peerPublicKey, newVersion, blockN, raw)
}
return
@@ -56,7 +53,7 @@ func (backend *Backend) userBlockchainUpdateSearchIndex() {
continue
}
backend.SearchIndex.IndexNewBlock(peerPublicKey, newVersion, blockN, raw)
backend.SearchIndex.IndexNewBlock(backend.peerPublicKey, newVersion, blockN, raw)
}
}
}

View File

@@ -29,33 +29,34 @@ type rootPeer struct {
peer *PeerInfo // loaded PeerInfo
publicKey *btcec.PublicKey // Public key
addresses []*net.UDPAddr // IP:Port addresses
backend *Backend
}
var rootPeers map[[btcec.PubKeyBytesLenCompressed]byte]*rootPeer
// initSeedList loads the seed list from the config
// Note: This should be called before any network listening function so that incoming root peers are properly recognized.
func initSeedList() {
func (backend *Backend) initSeedList() {
rootPeers = make(map[[btcec.PubKeyBytesLenCompressed]byte]*rootPeer)
recentContacts = make(map[[btcec.PubKeyBytesLenCompressed]byte]*recentContactInfo)
loopSeedList:
for _, seed := range config.SeedList {
peer := &rootPeer{}
for _, seed := range backend.Config.SeedList {
peer := &rootPeer{backend: backend}
// parse the Public Key
publicKeyB, err := hex.DecodeString(seed.PublicKey)
if err != nil {
Filters.LogError("initSeedList", "public key '%s': %v\n", seed.PublicKey, err.Error())
backend.Filters.LogError("initSeedList", "public key '%s': %v\n", seed.PublicKey, err.Error())
continue
}
if peer.publicKey, err = btcec.ParsePubKey(publicKeyB, btcec.S256()); err != nil {
Filters.LogError("initSeedList", "public key '%s': %v\n", seed.PublicKey, err.Error())
backend.Filters.LogError("initSeedList", "public key '%s': %v\n", seed.PublicKey, err.Error())
continue
}
if peer.publicKey.IsEqual(peerPublicKey) { // skip if self
if peer.publicKey.IsEqual(backend.peerPublicKey) { // skip if self
continue
}
@@ -63,7 +64,7 @@ loopSeedList:
for _, addressA := range seed.Address {
address, err := parseAddress(addressA)
if err != nil {
Filters.LogError("initSeedList", "public key '%s' address '%s': %v\n", seed.PublicKey, addressA, err.Error())
backend.Filters.LogError("initSeedList", "public key '%s' address '%s': %v\n", seed.PublicKey, addressA, err.Error())
continue loopSeedList
}
@@ -99,22 +100,22 @@ func parseAddress(Address string) (remote *net.UDPAddr, err error) {
// contact tries to contact the root peer on all networks
func (peer *rootPeer) contact() {
// If already in peer list, no need to contact.
if PeerlistLookup(peer.publicKey) != nil {
if peer.backend.PeerlistLookup(peer.publicKey) != nil {
return
}
for _, address := range peer.addresses {
// Port internal is always set to 0 for root peers. It disables NAT detection and will not send out a Traverse message.
contactArbitraryPeer(peer.publicKey, address, 0, false)
peer.backend.contactArbitraryPeer(peer.publicKey, address, 0, false)
}
}
// bootstrap connects to the initial set of peers.
func bootstrap() {
func (backend *Backend) bootstrap() {
go resetRecentContacts()
if len(rootPeers) == 0 {
Filters.LogError("bootstrap", "warning: Empty list of root peers. Connectivity relies on local peer discovery and incoming connections.\n")
backend.Filters.LogError("bootstrap", "warning: Empty list of root peers. Connectivity relies on local peer discovery and incoming connections.\n")
return
}
@@ -130,7 +131,7 @@ func bootstrap() {
for _, peer := range rootPeers {
if peer.peer != nil {
connectedCount++
} else if peer.peer = PeerlistLookup(peer.publicKey); peer.peer != nil {
} else if peer.peer = peer.backend.PeerlistLookup(peer.publicKey); peer.peer != nil {
connectedCount++
}
}
@@ -162,7 +163,7 @@ func bootstrap() {
}
}
Filters.LogError("bootstrap", "unable to connect to at least 2 root peers, aborting\n")
backend.Filters.LogError("bootstrap", "unable to connect to at least 2 root peers, aborting\n")
}
func (nets *Networks) autoMulticastBroadcast() {
@@ -172,13 +173,13 @@ func (nets *Networks) autoMulticastBroadcast() {
for _, network := range nets.networks6 {
if err := network.MulticastIPv6Send(); err != nil {
Filters.LogError("autoMulticastBroadcast", "multicast from network address '%s': %v\n", network.address.IP.String(), err.Error())
nets.backend.Filters.LogError("autoMulticastBroadcast", "multicast from network address '%s': %v\n", network.address.IP.String(), err.Error())
}
}
for _, network := range nets.networks4 {
if err := network.BroadcastIPv4Send(); err != nil {
Filters.LogError("autoMulticastBroadcast", "broadcast from network address '%s': %v\n", network.address.IP.String(), err.Error())
nets.backend.Filters.LogError("autoMulticastBroadcast", "broadcast from network address '%s': %v\n", network.address.IP.String(), err.Error())
}
}
}
@@ -190,7 +191,7 @@ func (nets *Networks) autoMulticastBroadcast() {
for {
time.Sleep(time.Second * 10)
if PeerlistCount() >= 1 {
if nets.backend.PeerlistCount() >= 1 {
break
}
@@ -205,18 +206,18 @@ func (nets *Networks) autoMulticastBroadcast() {
}
// contactArbitraryPeer contacts a new arbitrary peer for the first time.
func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, receiverPortInternal uint16, receiverFirewall bool) (contacted bool) {
func (backend *Backend) contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, receiverPortInternal uint16, receiverFirewall bool) (contacted bool) {
findSelf := ShouldSendFindSelf()
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, findSelf, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, findSelf, nil, nil, nil, backend.FeatureSupport(), blockchainHeight, blockchainVersion, backend.userAgent)
if len(packets) == 0 {
return false
}
raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packets[0]}
Filters.MessageOutAnnouncement(publicKey, nil, raw, findSelf, nil, nil, nil)
backend.Filters.MessageOutAnnouncement(publicKey, nil, raw, findSelf, nil, nil, nil)
networks.sendAllNetworks(publicKey, raw, address, receiverPortInternal, receiverFirewall, nil, &bootstrapFindSelf{})
backend.networks.sendAllNetworks(publicKey, raw, address, receiverPortInternal, receiverFirewall, nil, &bootstrapFindSelf{})
return true
}
@@ -236,12 +237,12 @@ func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *protocol.MessageResponse
}
for _, closePeer := range closest {
if isReturnedPeerBadQuality(&closePeer) {
if peer.Backend.isReturnedPeerBadQuality(&closePeer) {
continue
}
// If the peer is already in the peer list, no need to contact it again.
if PeerlistLookup(closePeer.PublicKey) != nil {
if peer.Backend.PeerlistLookup(closePeer.PublicKey) != nil {
continue
}
@@ -258,7 +259,7 @@ func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *protocol.MessageResponse
}
// Initiate contact. Once a response comes back, the peer will be actually added to the peer list.
contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, closePeer.Features&(1<<protocol.FeatureFirewall) > 0)
peer.Backend.contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, closePeer.Features&(1<<protocol.FeatureFirewall) > 0)
}
}
}
@@ -270,7 +271,7 @@ func ShouldSendFindSelf() bool {
}
// isReturnedPeerBadQuality checks if the returned peer record is bad quality and should be discarded
func isReturnedPeerBadQuality(record *protocol.PeerRecord) bool {
func (backend *Backend) isReturnedPeerBadQuality(record *protocol.PeerRecord) bool {
isIPv4 := record.IPv4 != nil && !record.IPv4.IsUnspecified()
isIPv6 := record.IPv6 != nil && !record.IPv6.IsUnspecified()
@@ -286,7 +287,7 @@ func isReturnedPeerBadQuality(record *protocol.PeerRecord) bool {
}
// Must not be self. There is no point that a remote peer would return self
if record.PublicKey.IsEqual(peerPublicKey) {
if record.PublicKey.IsEqual(backend.peerPublicKey) {
//fmt.Printf("IsReturnedPeerBadQuality received self peer\n")
return true
}

View File

@@ -29,7 +29,7 @@ func (peer *PeerInfo) cmdTraverseForward(msg *protocol.MessageTraverse) {
// Check if the target peer is known in the peer list. If not, nothing will be done.
// The original sender should only send the Traverse message as answer to a Response that contains a reported peer that is behind a NAT.
// In that case the target peer should be still in this peers' peer list.
peerTarget := PeerlistLookup(msg.TargetPeer)
peerTarget := peer.Backend.PeerlistLookup(msg.TargetPeer)
if peerTarget == nil {
return
}
@@ -72,7 +72,7 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *protocol.MessageTraverse) {
// Already an active connection established? The relayed message should not be needed in this case.
// This could be changed in the future if it turns out that there are 1-way connection issues.
if peerTarget := PeerlistLookup(msg.SignerPublicKey); peerTarget != nil {
if peerTarget := peer.Backend.PeerlistLookup(msg.SignerPublicKey); peerTarget != nil {
return
}
@@ -99,13 +99,13 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *protocol.MessageTraverse) {
// ---- fork packetWorker to decode and validate embedded packet ---
// Due to missing connection and other embedded details in the message (such as ports), the packet is not just simply queued to rawPacketsIncoming.
decoded, senderPublicKey, err := protocol.PacketDecrypt(msg.EmbeddedPacketRaw, peerPublicKey)
decoded, senderPublicKey, err := protocol.PacketDecrypt(msg.EmbeddedPacketRaw, peer.Backend.peerPublicKey)
if err != nil {
return
}
if !senderPublicKey.IsEqual(msg.SignerPublicKey) {
return
} else if senderPublicKey.IsEqual(peerPublicKey) {
} else if senderPublicKey.IsEqual(peer.Backend.peerPublicKey) {
return
} else if decoded.Protocol != 0 {
return
@@ -115,7 +115,7 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *protocol.MessageTraverse) {
// process the packet and create a virtual peer
raw := &protocol.MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded}
peerV := &PeerInfo{PublicKey: senderPublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(senderPublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses}
peerV := &PeerInfo{Backend: peer.Backend, PublicKey: senderPublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(senderPublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses}
// process it!
switch decoded.Command {

View File

@@ -38,12 +38,12 @@ func (peer *PeerInfo) cmdAnouncement(msg *protocol.MessageAnnouncement, connecti
// FIND_SELF: Requesting peers close to the sender?
if msg.Actions&(1<<protocol.ActionFindSelf) > 0 {
Filters.IncomingRequest(peer, protocol.ActionFindSelf, peer.NodeID, nil)
peer.Backend.Filters.IncomingRequest(peer, protocol.ActionFindSelf, peer.NodeID, nil)
selfD := protocol.Hash2Peer{ID: protocol.KeyHash{Hash: peer.NodeID}}
// do not respond the caller's own peer (add to ignore list)
for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, peer.NodeID, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) {
for _, node := range peer.Backend.nodesDHT.GetClosestContacts(respondClosesContactsCount, peer.NodeID, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) {
if info := node.Info.(*PeerInfo).peer2Record(connection.IsLocal(), allowIPv4, allowIPv6); info != nil {
selfD.Closest = append(selfD.Closest, *info)
}
@@ -59,12 +59,12 @@ func (peer *PeerInfo) cmdAnouncement(msg *protocol.MessageAnnouncement, connecti
// FIND_PEER: Find a different peer?
if msg.Actions&(1<<protocol.ActionFindPeer) > 0 && len(msg.FindPeerKeys) > 0 {
for _, findPeer := range msg.FindPeerKeys {
Filters.IncomingRequest(peer, protocol.ActionFindPeer, findPeer.Hash, nil)
peer.Backend.Filters.IncomingRequest(peer, protocol.ActionFindPeer, findPeer.Hash, nil)
details := protocol.Hash2Peer{ID: findPeer}
// Same as before, put self as ignoredNodes.
for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, findPeer.Hash, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) {
for _, node := range peer.Backend.nodesDHT.GetClosestContacts(respondClosesContactsCount, findPeer.Hash, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) {
if info := node.Info.(*PeerInfo).peer2Record(connection.IsLocal(), allowIPv4, allowIPv6); info != nil {
details.Closest = append(details.Closest, *info)
}
@@ -81,13 +81,13 @@ func (peer *PeerInfo) cmdAnouncement(msg *protocol.MessageAnnouncement, connecti
// Find a value?
if msg.Actions&(1<<protocol.ActionFindValue) > 0 {
for _, findHash := range msg.FindDataKeys {
Filters.IncomingRequest(peer, protocol.ActionFindValue, findHash.Hash, nil)
peer.Backend.Filters.IncomingRequest(peer, protocol.ActionFindValue, findHash.Hash, nil)
stored, data := announcementGetData(findHash.Hash)
stored, data := peer.announcementGetData(findHash.Hash)
if stored && len(data) > 0 {
filesEmbed = append(filesEmbed, protocol.EmbeddedFileData{ID: findHash, Data: data})
} else if stored {
selfRecord := selfPeerRecord()
selfRecord := peer.Backend.selfPeerRecord()
hash2Peers = append(hash2Peers, protocol.Hash2Peer{ID: findHash, Storing: []protocol.PeerRecord{selfRecord}})
} else {
hashesNotFound = append(hashesNotFound, findHash.Hash)
@@ -98,7 +98,7 @@ func (peer *PeerInfo) cmdAnouncement(msg *protocol.MessageAnnouncement, connecti
// Information about files stored by the sender?
if msg.Actions&(1<<protocol.ActionInfoStore) > 0 && len(msg.InfoStoreFiles) > 0 {
for n := range msg.InfoStoreFiles {
Filters.IncomingRequest(peer, protocol.ActionInfoStore, msg.InfoStoreFiles[n].ID.Hash, &msg.InfoStoreFiles[n])
peer.Backend.Filters.IncomingRequest(peer, protocol.ActionInfoStore, msg.InfoStoreFiles[n].ID.Hash, &msg.InfoStoreFiles[n])
}
peer.announcementStore(msg.InfoStoreFiles)
@@ -144,7 +144,7 @@ func (peer *PeerInfo) cmdResponse(msg *protocol.MessageResponse, connection *Con
if msg.SequenceInfo == nil || msg.SequenceInfo.Data == nil {
// If there is no sequence data but there were results returned, it means we received unsolicited response data. It will be rejected.
if len(msg.HashesNotFound) > 0 || len(msg.Hash2Peers) > 0 || len(msg.FilesEmbed) > 0 {
Filters.LogError("cmdResponse", "unsolicited response data received from %s\n", connection.Address.String())
peer.Backend.Filters.LogError("cmdResponse", "unsolicited response data received from %s\n", connection.Address.String())
}
return
@@ -154,8 +154,8 @@ func (peer *PeerInfo) cmdResponse(msg *protocol.MessageResponse, connection *Con
if _, ok := msg.SequenceInfo.Data.(*bootstrapFindSelf); ok {
for _, hash2Peer := range msg.Hash2Peers {
// Make sure no garbage is returned. The key must be self and only Closest is expected.
if !bytes.Equal(hash2Peer.ID.Hash, nodeID) || len(hash2Peer.Closest) == 0 {
Filters.LogError("cmdResponse", "incoming response to bootstrap FIND_SELF contains invalid data from %s\n", connection.Address.String())
if !bytes.Equal(hash2Peer.ID.Hash, peer.Backend.nodeID) || len(hash2Peer.Closest) == 0 {
peer.Backend.Filters.LogError("cmdResponse", "incoming response to bootstrap FIND_SELF contains invalid data from %s\n", connection.Address.String())
return
}
@@ -176,7 +176,7 @@ func (peer *PeerInfo) cmdResponse(msg *protocol.MessageResponse, connection *Con
}
for _, hash2Peer := range msg.Hash2Peers {
info.QueueResult(&dht.NodeMessage{SenderID: peer.NodeID, Closest: records2Nodes(hash2Peer.Closest, peer), Storing: records2Nodes(hash2Peer.Storing, peer)})
info.QueueResult(&dht.NodeMessage{SenderID: peer.NodeID, Closest: peer.records2Nodes(hash2Peer.Closest), Storing: peer.records2Nodes(hash2Peer.Storing)})
if hash2Peer.IsLast {
info.Done()
@@ -203,7 +203,7 @@ func (peer *PeerInfo) cmdPing(msg *protocol.MessageRaw, connection *Connection)
raw := &protocol.PacketRaw{Command: protocol.CommandPong, Sequence: msg.Sequence}
Filters.MessageOutPong(peer, raw)
peer.Backend.Filters.MessageOutPong(peer, raw)
peer.send(raw)
}
@@ -231,8 +231,8 @@ func (peer *PeerInfo) cmdLocalDiscovery(msg *protocol.MessageAnnouncement, conne
}
// SendChatAll sends a text message to all peers
func SendChatAll(text string) {
for _, peer := range PeerlistGet() {
func (backend *Backend) SendChatAll(text string) {
for _, peer := range backend.PeerlistGet() {
peer.Chat(text)
}
}
@@ -247,7 +247,7 @@ func (peer *PeerInfo) cmdTransfer(msg *protocol.MessageTransfer, connection *Con
switch msg.Control {
case protocol.TransferControlRequestStart:
// First check if the file available in the warehouse.
_, fileInfo, status, _ := UserWarehouse.FileExists(msg.Hash)
_, fileInfo, status, _ := peer.Backend.UserWarehouse.FileExists(msg.Hash)
if status != warehouse.StatusOK {
// File not available.
peer.sendTransfer(nil, protocol.TransferControlNotAvailable, msg.TransferProtocol, msg.Hash, 0, 0, msg.Sequence)
@@ -286,10 +286,10 @@ func (peer *PeerInfo) cmdGetBlock(msg *protocol.MessageGetBlock, connection *Con
switch msg.Control {
case protocol.GetBlockControlRequestStart:
// Currently only support the local blockchain.
if !msg.BlockchainPublicKey.IsEqual(peerPublicKey) {
if !msg.BlockchainPublicKey.IsEqual(peer.Backend.peerPublicKey) {
peer.sendGetBlock(nil, protocol.GetBlockControlNotAvailable, msg.BlockchainPublicKey, 0, 0, nil, msg.Sequence)
return
} else if _, height, _ := UserBlockchain.Header(); height == 0 {
} else if _, height, _ := peer.Backend.UserBlockchain.Header(); height == 0 {
peer.sendGetBlock(nil, protocol.GetBlockControlEmpty, msg.BlockchainPublicKey, 0, 0, nil, msg.Sequence)
return
} else if msg.LimitBlockCount == 0 {

105
Config.go
View File

@@ -17,9 +17,10 @@ import (
)
// Version is the current core library version
const Version = "Alpha 5/14.12.2021"
const Version = "Alpha 6/28.12.2021"
var config struct {
// Config defines the minimum required config for a Peernet client.
type Config struct {
// Locations of important files and folders
LogFile string `yaml:"LogFile"` // Log file. It contains informational and error messages.
BlockchainMain string `yaml:"BlockchainMain"` // Blockchain main stores the end-users blockchain data. It contains meta data of shared files, profile data, and social interactions.
@@ -60,84 +61,74 @@ type peerSeed struct {
Address []string `yaml:"Address"` // IP:Port
}
var configFile string
//go:embed "Config Default.yaml"
var defaultConfig []byte
// LoadConfig reads the YAML configuration file. If an error is returned, the application shall exit.
// Status: 0 = Unknown error checking config file, 1 = Error reading config file, 2 = Error parsing config file, 3 = Success
func LoadConfig(filename string) (status int, err error) {
// LoadConfig reads the YAML configuration file and unmarshals it into the provided structure.
// If the config file does not exist or is empty, it will fall back to the default config which is hardcoded.
// Status is of type ExitX.
func LoadConfig(Filename string, ConfigOut interface{}) (status int, err error) {
var configData []byte
configFile = filename
// check if the file is non existent or empty
stats, err := os.Stat(filename)
if err != nil && os.IsNotExist(err) || err == nil && stats.Size() == 0 {
configData = defaultConfig
} else if err != nil {
return 0, err
} else if configData, err = ioutil.ReadFile(filename); err != nil {
return 1, err
}
// parse the config
err = yaml.Unmarshal(configData, &config)
if err != nil {
return 2, err
}
return 3, nil
}
// LoadConfigOut is similar to LoadConfig but unmarshals the config into a caller provided structure.
func LoadConfigOut(Filename string, ConfigOut interface{}) (status int, err error) {
var configData []byte
configFile = Filename
// check if the file is non existent or empty
stats, err := os.Stat(Filename)
if err != nil && os.IsNotExist(err) || err == nil && stats.Size() == 0 {
configData = defaultConfig
} else if err != nil {
return 0, err
return ExitErrorConfigAccess, err
} else if configData, err = ioutil.ReadFile(Filename); err != nil {
return 1, err
return ExitErrorConfigRead, err
}
// parse the config
if err = yaml.Unmarshal(configData, &config); err != nil {
return 2, err
}
if err = yaml.Unmarshal(configData, ConfigOut); err != nil {
return 2, err
err = yaml.Unmarshal(configData, ConfigOut)
if err != nil {
return ExitErrorConfigParse, err
}
return 3, nil
return ExitSuccess, nil
}
func saveConfig() {
data, err := yaml.Marshal(config)
// SaveConfig stores the config.
func SaveConfig(Filename string, Config interface{}) (err error) {
data, err := yaml.Marshal(Config)
if err != nil {
Filters.LogError("saveConfig", "marshalling config: %v\n", err.Error())
return err
}
return ioutil.WriteFile(Filename, data, 0666)
}
// SaveConfig stores the current runtime config to file. Any foreign settings not present in the Config structure will be deleted.
func (backend *Backend) SaveConfig() {
if err := SaveConfig(backend.ConfigFilename, *backend.Config); err != nil {
backend.Filters.LogError("SaveConfig", "writing config '%s': %v\n", backend.ConfigFilename, err.Error())
}
}
func (backend *Backend) configUpdateSeedList() {
// parse the embedded config
var configD Config
if err := yaml.Unmarshal(defaultConfig, &configD); err != nil {
return
}
err = ioutil.WriteFile(configFile, data, 0666)
if err != nil {
Filters.LogError("saveConfig", "writing config '%s': %v\n", configFile, err.Error())
return
// check if the seed list needs an update
if backend.Config.SeedListVersion < configD.SeedListVersion {
backend.Config.SeedList = configD.SeedList
backend.Config.SeedListVersion = configD.SeedListVersion
backend.SaveConfig()
}
}
// InitLog redirects subsequent log messages into the default log file specified in the configuration
func InitLog() (err error) {
func (backend *Backend) initLog() (err error) {
// create the directory to the log file if specified
if directory, _ := path.Split(config.LogFile); directory != "" {
if directory, _ := path.Split(backend.Config.LogFile); directory != "" {
os.MkdirAll(directory, os.ModePerm)
}
logFile, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) // 666 : All uses can read/write
logFile, err := os.OpenFile(backend.Config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) // 666 : All uses can read/write
if err != nil {
return err
}
@@ -148,17 +139,3 @@ func InitLog() (err error) {
return nil
}
func configUpdateSeedList() {
// parse the embedded config
configD := config
if err := yaml.Unmarshal(defaultConfig, &configD); err != nil {
return
}
if config.SeedListVersion < configD.SeedListVersion {
config.SeedList = configD.SeedList
config.SeedListVersion = configD.SeedListVersion
saveConfig()
}
}

View File

@@ -31,6 +31,7 @@ type Connection struct {
RoundTripTime time.Duration // Full round-trip time of last reply.
Firewall bool // Whether the remote peer indicates a potential firewall. This means a Traverse message shall be sent to establish a connection.
traversePeer *PeerInfo // Temporary peer that may act as proxy for a Traverse message used for the first packet. This is used to establish this Connection to a peer that is behind a NAT or firewall.
backend *Backend
}
// Connection status
@@ -190,7 +191,7 @@ func (peer *PeerInfo) registerConnection(incoming *Connection) (result *Connecti
peer.connectionActive = append(peer.connectionActive, incoming)
peer.setConnectionLatest(incoming)
Filters.NewPeerConnection(peer, incoming)
peer.Backend.Filters.NewPeerConnection(peer, incoming)
return incoming
}
@@ -343,9 +344,9 @@ func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.P
packet.Protocol = protocol.ProtocolVersion
packet.SetSelfReportedPorts(c.Network.SelfReportedPorts())
Filters.PacketOut(packet, receiverPublicKey, c)
c.backend.Filters.PacketOut(packet, receiverPublicKey, c)
raw, err := protocol.PacketEncrypt(peerPrivateKey, receiverPublicKey, packet)
raw, err := protocol.PacketEncrypt(c.backend.peerPrivateKey, receiverPublicKey, packet)
if err != nil {
return err
}
@@ -366,7 +367,7 @@ func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.P
func (peer *PeerInfo) send(packet *protocol.PacketRaw) (err error) {
if peer.isVirtual { // special case for peers that were not contacted before
for _, address := range peer.targetAddresses {
networks.sendAllNetworks(peer.PublicKey, packet, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, peer.Features&(1<<protocol.FeatureFirewall) > 0, peer.traversePeer, nil)
peer.Backend.networks.sendAllNetworks(peer.PublicKey, packet, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, peer.Features&(1<<protocol.FeatureFirewall) > 0, peer.traversePeer, nil)
}
return
}
@@ -443,7 +444,7 @@ func (nets *Networks) sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet
if sequenceData != nil {
packet.Sequence = nets.Sequences.ArbitrarySequence(receiverPublicKey, sequenceData).SequenceNumber
}
err = (&Connection{Network: network, Address: remote, PortInternal: receiverPortInternal, traversePeer: traversePeer, Firewall: receiverFirewall}).send(packet, receiverPublicKey, isFirstPacket)
err = (&Connection{backend: nets.backend, Network: network, Address: remote, PortInternal: receiverPortInternal, traversePeer: traversePeer, Firewall: receiverFirewall}).send(packet, receiverPublicKey, isFirstPacket)
isFirstPacket = false
if err == nil {

View File

@@ -11,19 +11,16 @@ import (
"github.com/PeernetOfficial/core/store"
)
// dhtStore contains all key-value data served via DHT
var dhtStore store.Store
// TODO: Via descriptors, files stored by other peers
func initStore() {
dhtStore = store.NewMemoryStore()
func (backend *Backend) initStore() {
backend.dhtStore = store.NewMemoryStore()
}
// announcementGetData returns data for an announcement
func announcementGetData(hash []byte) (stored bool, data []byte) {
func (peer *PeerInfo) announcementGetData(hash []byte) (stored bool, data []byte) {
// TODO: Create RetrieveIfSize to prevent files larger than EmbeddedFileSizeMax from being loaded
data, found := dhtStore.Get(hash)
data, found := peer.Backend.dhtStore.Get(hash)
if !found {
return false, nil
}

View File

@@ -18,7 +18,7 @@ import (
// Filters contains all functions to install the hook. Use nil for unused.
// The functions are called sequentially and block execution; if the filter takes a long time it should start a Go routine.
var Filters struct {
type Filters struct {
// NewPeer is called every time a new peer, that is one that is not currently in the peer list.
// Note that peers might be removed from peer lists and reappear quickly, i.e. this function may be called multiple times for the same peers.
// The filter must maintain its own map of unique peer IDs if actual uniqueness of new peers is desired.
@@ -64,51 +64,51 @@ var Filters struct {
MessageOutPong func(peer *PeerInfo, packet *protocol.PacketRaw)
}
func initFilters() {
func (backend *Backend) initFilters() {
// Set default filters to blank functions so they can be safely called without constant nil checks.
// Only if not already set before init.
if Filters.NewPeer == nil {
Filters.NewPeer = func(peer *PeerInfo, connection *Connection) {}
if backend.Filters.NewPeer == nil {
backend.Filters.NewPeer = func(peer *PeerInfo, connection *Connection) {}
}
if Filters.NewPeerConnection == nil {
Filters.NewPeerConnection = func(peer *PeerInfo, connection *Connection) {}
if backend.Filters.NewPeerConnection == nil {
backend.Filters.NewPeerConnection = func(peer *PeerInfo, connection *Connection) {}
}
if Filters.DHTSearchStatus == nil {
Filters.DHTSearchStatus = func(client *dht.SearchClient, function, format string, v ...interface{}) {}
if backend.Filters.DHTSearchStatus == nil {
backend.Filters.DHTSearchStatus = func(client *dht.SearchClient, function, format string, v ...interface{}) {}
}
if Filters.LogError == nil {
Filters.LogError = DefaultLogError
if backend.Filters.LogError == nil {
backend.Filters.LogError = DefaultLogError
}
if Filters.IncomingRequest == nil {
Filters.IncomingRequest = func(peer *PeerInfo, Action int, Key []byte, Info interface{}) {}
if backend.Filters.IncomingRequest == nil {
backend.Filters.IncomingRequest = func(peer *PeerInfo, Action int, Key []byte, Info interface{}) {}
}
if Filters.PacketIn == nil {
Filters.PacketIn = func(packet *protocol.PacketRaw, senderPublicKey *btcec.PublicKey, c *Connection) {}
if backend.Filters.PacketIn == nil {
backend.Filters.PacketIn = func(packet *protocol.PacketRaw, senderPublicKey *btcec.PublicKey, c *Connection) {}
}
if Filters.PacketOut == nil {
Filters.PacketOut = func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, c *Connection) {}
if backend.Filters.PacketOut == nil {
backend.Filters.PacketOut = func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, c *Connection) {}
}
if Filters.MessageIn == nil {
Filters.MessageIn = func(peer *PeerInfo, raw *protocol.MessageRaw, message interface{}) {}
if backend.Filters.MessageIn == nil {
backend.Filters.MessageIn = func(peer *PeerInfo, raw *protocol.MessageRaw, message interface{}) {}
}
if Filters.MessageOutAnnouncement == nil {
Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore) {
if backend.Filters.MessageOutAnnouncement == nil {
backend.Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore) {
}
}
if Filters.MessageOutResponse == nil {
Filters.MessageOutResponse = func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) {
if backend.Filters.MessageOutResponse == nil {
backend.Filters.MessageOutResponse = func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) {
}
}
if Filters.MessageOutTraverse == nil {
Filters.MessageOutTraverse = func(peer *PeerInfo, packet *protocol.PacketRaw, embeddedPacket *protocol.PacketRaw, receiverEnd *btcec.PublicKey) {
if backend.Filters.MessageOutTraverse == nil {
backend.Filters.MessageOutTraverse = func(peer *PeerInfo, packet *protocol.PacketRaw, embeddedPacket *protocol.PacketRaw, receiverEnd *btcec.PublicKey) {
}
}
if Filters.MessageOutPing == nil {
Filters.MessageOutPing = func(peer *PeerInfo, packet *protocol.PacketRaw, connection *Connection) {}
if backend.Filters.MessageOutPing == nil {
backend.Filters.MessageOutPing = func(peer *PeerInfo, packet *protocol.PacketRaw, connection *Connection) {}
}
if Filters.MessageOutPong == nil {
Filters.MessageOutPong = func(peer *PeerInfo, packet *protocol.PacketRaw) {}
if backend.Filters.MessageOutPong == nil {
backend.Filters.MessageOutPong = func(peer *PeerInfo, packet *protocol.PacketRaw) {}
}
}

View File

@@ -17,13 +17,11 @@ import (
const alpha = 5 // Count of nodes to be contacted in parallel for finding a key
const bucketSize = 20 // Count of nodes per bucket
var nodesDHT *dht.DHT
func initKademlia() {
nodesDHT = dht.NewDHT(&dht.Node{ID: nodeID}, 256, bucketSize, alpha)
func (backend *Backend) initKademlia() {
backend.nodesDHT = dht.NewDHT(&dht.Node{ID: backend.nodeID}, 256, bucketSize, alpha)
// ShouldEvict determines whether node 1 shall be evicted in favor of node 2
nodesDHT.ShouldEvict = func(node1, node2 *dht.Node) bool {
backend.nodesDHT.ShouldEvict = func(node1, node2 *dht.Node) bool {
rttOld := node1.Info.(*PeerInfo).GetRTT()
rttNew := node2.Info.(*PeerInfo).GetRTT()
@@ -36,33 +34,33 @@ func initKademlia() {
}
// If here, none has a RTT. Keep the closer (by distance) one.
return nodesDHT.IsNodeCloser(node1.ID, node2.ID)
return backend.nodesDHT.IsNodeCloser(node1.ID, node2.ID)
}
// SendRequestStore sends a store message to the remote node. I.e. asking it to store the given key-value
nodesDHT.SendRequestStore = func(node *dht.Node, key []byte, dataSize uint64) {
backend.nodesDHT.SendRequestStore = func(node *dht.Node, key []byte, dataSize uint64) {
node.Info.(*PeerInfo).sendAnnouncementStore(key, dataSize)
}
// SendRequestFindNode sends an information request to find a particular node. nodes are the nodes to send the request to.
nodesDHT.SendRequestFindNode = func(request *dht.InformationRequest) {
backend.nodesDHT.SendRequestFindNode = func(request *dht.InformationRequest) {
for _, node := range request.Nodes {
node.Info.(*PeerInfo).sendAnnouncementFindNode(request)
}
}
// SendRequestFindValue sends an information request to find data. nodes are the nodes to send the request to.
nodesDHT.SendRequestFindValue = func(request *dht.InformationRequest) {
backend.nodesDHT.SendRequestFindValue = func(request *dht.InformationRequest) {
for _, node := range request.Nodes {
node.Info.(*PeerInfo).sendAnnouncementFindValue(request)
}
}
nodesDHT.FilterSearchStatus = Filters.DHTSearchStatus
backend.nodesDHT.FilterSearchStatus = backend.Filters.DHTSearchStatus
}
// autoBucketRefresh refreshes buckets every 5 minutes to meet the alpha nodes per bucket target. Force full refresh every hour.
func autoBucketRefresh() {
func (backend *Backend) autoBucketRefresh() {
for minute := 5; ; minute += 5 {
time.Sleep(time.Minute * 5)
@@ -71,28 +69,28 @@ func autoBucketRefresh() {
target = 0
}
nodesDHT.RefreshBuckets(target)
backend.nodesDHT.RefreshBuckets(target)
}
}
// bootstrapKademlia bootstraps the Kademlia bucket list
func bootstrapKademlia() {
func (backend *Backend) bootstrapKademlia() {
monitor := make(chan *PeerInfo)
registerPeerMonitor(monitor)
backend.registerPeerMonitor(monitor)
// Wait until there are at least 2 peers connected.
for {
<-monitor
if nodesDHT.NumNodes() >= 2 {
if backend.nodesDHT.NumNodes() >= 2 {
break
}
}
unregisterPeerMonitor(monitor)
backend.unregisterPeerMonitor(monitor)
// Refresh every 10 seconds 3 times
for n := 0; n < 3; n++ {
nodesDHT.RefreshBuckets(alpha)
backend.nodesDHT.RefreshBuckets(alpha)
time.Sleep(time.Second)
}
@@ -102,7 +100,7 @@ func bootstrapKademlia() {
func (peer *PeerInfo) sendAnnouncementFindNode(request *dht.InformationRequest) {
// If the key is self, send it as FIND_SELF
if bytes.Equal(request.Key, nodeID) {
if bytes.Equal(request.Key, peer.Backend.nodeID) {
peer.sendAnnouncement(false, true, nil, nil, nil, request)
} else {
peer.sendAnnouncement(false, false, []protocol.KeyHash{{Hash: request.Key}}, nil, nil, request)
@@ -132,46 +130,46 @@ func Data2Hash(data []byte) (hash []byte) {
}
// GetData returns the requested data. It checks first the local store and then tries via DHT.
func GetData(hash []byte) (data []byte, senderNodeID []byte, found bool) {
if data, found = GetDataLocal(hash); found {
return data, nodeID, found
func (backend *Backend) GetData(hash []byte) (data []byte, senderNodeID []byte, found bool) {
if data, found = backend.GetDataLocal(hash); found {
return data, backend.nodeID, found
}
return GetDataDHT(hash)
return backend.GetDataDHT(hash)
}
// GetDataLocal returns data from the local warehouse.
func GetDataLocal(hash []byte) (data []byte, found bool) {
return dhtStore.Get(hash)
func (backend *Backend) GetDataLocal(hash []byte) (data []byte, found bool) {
return backend.dhtStore.Get(hash)
}
// GetDataDHT requests data via DHT
func GetDataDHT(hash []byte) (data []byte, senderNodeID []byte, found bool) {
data, senderNodeID, found, _ = nodesDHT.Get(hash)
func (backend *Backend) GetDataDHT(hash []byte) (data []byte, senderNodeID []byte, found bool) {
data, senderNodeID, found, _ = backend.nodesDHT.Get(hash)
return data, senderNodeID, found
}
// StoreDataLocal stores data into the local warehouse.
func StoreDataLocal(data []byte) error {
func (backend *Backend) StoreDataLocal(data []byte) error {
key := protocol.HashData(data)
return dhtStore.Set(key, data)
return backend.dhtStore.Set(key, data)
}
// StoreDataDHT stores data locally and informs closestCount peers in the DHT about it.
// Remote peers may choose to keep a record (in case another peers asks) or mirror the full data.
func StoreDataDHT(data []byte, closestCount int) error {
func (backend *Backend) StoreDataDHT(data []byte, closestCount int) error {
key := protocol.HashData(data)
if err := dhtStore.Set(key, data); err != nil {
if err := backend.dhtStore.Set(key, data); err != nil {
return err
}
return nodesDHT.Store(key, uint64(len(data)), closestCount)
return backend.nodesDHT.Store(key, uint64(len(data)), closestCount)
}
// ---- NODE FUNCTIONS ----
// IsNodeContact checks if the node is a contact in the local DHT routing table
func IsNodeContact(nodeID []byte) (node *dht.Node, peer *PeerInfo) {
node = nodesDHT.IsNodeContact(nodeID)
func (backend *Backend) IsNodeContact(nodeID []byte) (node *dht.Node, peer *PeerInfo) {
node = backend.nodesDHT.IsNodeContact(nodeID)
if node == nil {
return nil, nil
}
@@ -180,14 +178,14 @@ func IsNodeContact(nodeID []byte) (node *dht.Node, peer *PeerInfo) {
}
// FindNode finds a node via the DHT
func FindNode(nodeID []byte, Timeout time.Duration) (node *dht.Node, peer *PeerInfo, err error) {
func (backend *Backend) FindNode(nodeID []byte, Timeout time.Duration) (node *dht.Node, peer *PeerInfo, err error) {
// first check if in mirrored node list
if peer = NodelistLookup(nodeID); peer != nil {
if peer = backend.NodelistLookup(nodeID); peer != nil {
return nil, peer, nil
}
// Search the node via DHT.
node, err = nodesDHT.FindNode(nodeID)
node, err = backend.nodesDHT.FindNode(nodeID)
if node == nil {
return nil, nil, err
}
@@ -200,6 +198,6 @@ func FindNode(nodeID []byte, Timeout time.Duration) (node *dht.Node, peer *PeerI
// AsyncSearch creates an async search for the given key in the DHT.
// Timeout is the total time the search may take, covering all information requests. TimeoutIR is the time an information request may take.
// Alpha is the number of concurrent requests that will be performed.
func AsyncSearch(Action int, Key []byte, Timeout, TimeoutIR time.Duration, Alpha int) (client *dht.SearchClient) {
return nodesDHT.NewSearch(Action, Key, Timeout, TimeoutIR, Alpha)
func (backend *Backend) AsyncSearch(Action int, Key []byte, Timeout, TimeoutIR time.Duration, Alpha int) (client *dht.SearchClient) {
return backend.nodesDHT.NewSearch(Action, Key, Timeout, TimeoutIR, Alpha)
}

View File

@@ -15,8 +15,8 @@ import (
// pingConnection sends a ping to the target peer via the specified connection
func (peer *PeerInfo) pingConnection(connection *Connection) {
raw := &protocol.PacketRaw{Command: protocol.CommandPing, Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber}
Filters.MessageOutPing(peer, raw, connection)
raw := &protocol.PacketRaw{Command: protocol.CommandPing, Sequence: peer.Backend.networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber}
peer.Backend.Filters.MessageOutPing(peer, raw, connection)
err := peer.sendConnection(raw, connection)
connection.LastPingOut = time.Now()
@@ -29,14 +29,14 @@ func (peer *PeerInfo) pingConnection(connection *Connection) {
// pingConnectionAnnouncement sends an empty announcement via a particular connection.
// It has the same effect as ping, but returns the blockchain version and height of the other peer in the Response message, which may be useful for keeping the global blockchain cache up to date.
func (peer *PeerInfo) pingConnectionAnnouncement(connection *Connection) {
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(false, false, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := peer.Backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(false, false, nil, nil, nil, peer.Backend.FeatureSupport(), blockchainHeight, blockchainVersion, peer.Backend.userAgent)
if len(packets) != 1 {
return
}
raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packets[0], Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber}
Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, false, nil, nil, nil)
raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packets[0], Sequence: peer.Backend.networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber}
peer.Backend.Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, false, nil, nil, nil)
err := peer.sendConnection(raw, connection)
connection.LastPingOut = time.Now()
@@ -49,7 +49,7 @@ func (peer *PeerInfo) pingConnectionAnnouncement(connection *Connection) {
// Ping sends a ping. This function exists only for debugging purposes, it should not be used normally.
// This ping is not used for uptime detection and the LastPingOut time in connections is not set.
func (peer *PeerInfo) Ping() {
peer.send(&protocol.PacketRaw{Command: protocol.CommandPing, Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber})
peer.send(&protocol.PacketRaw{Command: protocol.CommandPing, Sequence: peer.Backend.networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber})
}
// Chat sends a text message
@@ -59,24 +59,24 @@ func (peer *PeerInfo) Chat(text string) {
// sendAnnouncement sends the announcement message. It acquires a new sequence for each message.
func (peer *PeerInfo) sendAnnouncement(sendUA, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore, sequenceData interface{}) {
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(sendUA, findSelf, findPeer, findValue, files, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := peer.Backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(sendUA, findSelf, findPeer, findValue, files, peer.Backend.FeatureSupport(), blockchainHeight, blockchainVersion, peer.Backend.userAgent)
for _, packet := range packets {
raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packet, Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, sequenceData).SequenceNumber}
Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, findSelf, findPeer, findValue, files)
raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packet, Sequence: peer.Backend.networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, sequenceData).SequenceNumber}
peer.Backend.Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, findSelf, findPeer, findValue, files)
peer.send(raw)
}
}
// sendResponse sends the response message
func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) (err error) {
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets, err := protocol.EncodeResponse(sendUA, hash2Peers, filesEmbed, hashesNotFound, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := peer.Backend.UserBlockchain.Header()
packets, err := protocol.EncodeResponse(sendUA, hash2Peers, filesEmbed, hashesNotFound, peer.Backend.FeatureSupport(), blockchainHeight, blockchainVersion, peer.Backend.userAgent)
for _, packet := range packets {
raw := &protocol.PacketRaw{Command: protocol.CommandResponse, Payload: packet, Sequence: sequence}
Filters.MessageOutResponse(peer, raw, hash2Peers, filesEmbed, hashesNotFound)
peer.Backend.Filters.MessageOutResponse(peer, raw, hash2Peers, filesEmbed, hashesNotFound)
peer.send(raw)
}
@@ -89,26 +89,26 @@ func (peer *PeerInfo) sendTraverse(packet *protocol.PacketRaw, receiverEnd *btce
// self-reported ports are not set, as this isn't sent via a specific network but a relay
//packet.SetSelfReportedPorts(c.Network.SelfReportedPorts())
embeddedPacketRaw, err := protocol.PacketEncrypt(peerPrivateKey, receiverEnd, packet)
embeddedPacketRaw, err := protocol.PacketEncrypt(peer.Backend.peerPrivateKey, receiverEnd, packet)
if err != nil {
return err
}
packetRaw, err := protocol.EncodeTraverse(peerPrivateKey, embeddedPacketRaw, receiverEnd, peer.PublicKey)
packetRaw, err := protocol.EncodeTraverse(peer.Backend.peerPrivateKey, embeddedPacketRaw, receiverEnd, peer.PublicKey)
if err != nil {
return err
}
raw := &protocol.PacketRaw{Command: protocol.CommandTraverse, Payload: packetRaw}
Filters.MessageOutTraverse(peer, raw, packet, receiverEnd)
peer.Backend.Filters.MessageOutTraverse(peer, raw, packet, receiverEnd)
return peer.send(raw)
}
// sendTransfer sends a transfer message
func (peer *PeerInfo) sendTransfer(data []byte, control, transferProtocol uint8, hash []byte, offset, limit uint64, sequenceNumber uint32) (err error) {
packetRaw, err := protocol.EncodeTransfer(peerPrivateKey, data, control, transferProtocol, hash, offset, limit)
packetRaw, err := protocol.EncodeTransfer(peer.Backend.peerPrivateKey, data, control, transferProtocol, hash, offset, limit)
if err != nil {
return err
}
@@ -122,7 +122,7 @@ func (peer *PeerInfo) sendTransfer(data []byte, control, transferProtocol uint8,
// sendGetBlock sends a get block message
func (peer *PeerInfo) sendGetBlock(data []byte, control uint8, blockchainPublicKey *btcec.PublicKey, limitBlockCount, maxBlockSize uint64, targetBlocks []protocol.BlockRange, sequenceNumber uint32) (err error) {
packetRaw, err := protocol.EncodeGetBlock(peerPrivateKey, data, control, blockchainPublicKey, limitBlockCount, maxBlockSize, targetBlocks)
packetRaw, err := protocol.EncodeGetBlock(peer.Backend.peerPrivateKey, data, control, blockchainPublicKey, limitBlockCount, maxBlockSize, targetBlocks)
if err != nil {
return err
}

View File

@@ -95,7 +95,7 @@ const changeMonitorFrequency = 10
// networkChangeMonitor() monitors for network changes to act accordingly
func (nets *Networks) networkChangeMonitor() {
// If manual IPs are entered, no need for monitoring for any network changes.
if len(config.Listen) > 0 {
if len(nets.backend.Config.Listen) > 0 {
return
}
@@ -104,7 +104,7 @@ func (nets *Networks) networkChangeMonitor() {
interfaceList, err := net.Interfaces()
if err != nil {
Filters.LogError("networkChangeMonitor", "enumerating network adapters failed: %s\n", err.Error())
nets.backend.Filters.LogError("networkChangeMonitor", "enumerating network adapters failed: %s\n", err.Error())
continue
}
@@ -113,7 +113,7 @@ func (nets *Networks) networkChangeMonitor() {
for _, iface := range interfaceList {
addressesNew, err := iface.Addrs()
if err != nil {
Filters.LogError("networkChangeMonitor", "enumerating IPs for network adapter '%s': %s\n", iface.Name, err.Error())
nets.backend.Filters.LogError("networkChangeMonitor", "enumerating IPs for network adapter '%s': %s\n", iface.Name, err.Error())
continue
}
ifacesNew[iface.Name] = addressesNew
@@ -168,7 +168,7 @@ func (nets *Networks) networkChangeMonitor() {
// networkChangeInterfaceNew is called when a new interface is detected
func (nets *Networks) networkChangeInterfaceNew(iface net.Interface, addresses []net.Addr) {
Filters.LogError("networkChangeInterfaceNew", "new interface '%s' (%d IPs)\n", iface.Name, len(addresses))
nets.backend.Filters.LogError("networkChangeInterfaceNew", "new interface '%s' (%d IPs)\n", iface.Name, len(addresses))
networksNew := nets.InterfaceStart(iface, addresses)
@@ -176,7 +176,7 @@ func (nets *Networks) networkChangeInterfaceNew(iface net.Interface, addresses [
go network.upnpAuto()
}
go nodesDHT.RefreshBuckets(0)
go nets.backend.nodesDHT.RefreshBuckets(0)
}
// networkChangeInterfaceRemove is called when an existing interface is removed
@@ -184,7 +184,7 @@ func (nets *Networks) networkChangeInterfaceRemove(iface string, addresses []net
nets.RLock()
defer nets.RUnlock()
Filters.LogError("networkChangeInterfaceRemove", "removing interface '%s' (%d IPs)\n", iface, len(addresses))
nets.backend.Filters.LogError("networkChangeInterfaceRemove", "removing interface '%s' (%d IPs)\n", iface, len(addresses))
for n, network := range nets.networks6 {
if network.iface != nil && network.iface.Name == iface {
@@ -215,7 +215,7 @@ func (nets *Networks) networkChangeInterfaceRemove(iface string, addresses []net
// networkChangeIPNew is called when an existing interface lists a new IP
func (nets *Networks) networkChangeIPNew(iface net.Interface, address net.Addr) {
Filters.LogError("networkChangeIPNew", "new interface '%s' IP %s\n", iface.Name, address.String())
nets.backend.Filters.LogError("networkChangeIPNew", "new interface '%s' IP %s\n", iface.Name, address.String())
networksNew := nets.InterfaceStart(iface, []net.Addr{address})
@@ -223,7 +223,7 @@ func (nets *Networks) networkChangeIPNew(iface net.Interface, address net.Addr)
go network.upnpAuto()
}
go nodesDHT.RefreshBuckets(0)
go nets.backend.nodesDHT.RefreshBuckets(0)
}
// networkChangeIPRemove is called when an existing interface removes an IP
@@ -231,7 +231,7 @@ func (nets *Networks) networkChangeIPRemove(iface net.Interface, address net.Add
nets.RLock()
defer nets.RUnlock()
Filters.LogError("networkChangeIPRemove", "remove interface '%s' IP %s\n", iface.Name, address.String())
nets.backend.Filters.LogError("networkChangeIPRemove", "remove interface '%s' IP %s\n", iface.Name, address.String())
for n, network := range nets.networks6 {
if network.address.IP.Equal(address.(*net.IPNet).IP) {

View File

@@ -43,7 +43,7 @@ func (network *Network) BroadcastIPv4() (err error) {
// listen on a special socket
network.broadcastSocket, err = reuseport.ListenPacket("udp4", net.JoinHostPort(network.address.IP.String(), strconv.Itoa(ipv4BroadcastPort)))
if err != nil {
Filters.LogError("BroadcastIPv4", "broadcast socket listen on IP '%s' port '%d': %v\n", network.address.IP.String(), ipv4BroadcastPort, err)
network.backend.Filters.LogError("BroadcastIPv4", "broadcast socket listen on IP '%s' port '%d': %v\n", network.address.IP.String(), ipv4BroadcastPort, err)
return err
}
@@ -64,8 +64,8 @@ func (network *Network) BroadcastIPv4Listen() {
length, sender, err := network.broadcastSocket.ReadFrom(buffer)
if err != nil {
Filters.LogError("BroadcastIPv4Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
network.backend.Filters.LogError("BroadcastIPv4Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
continue
}
@@ -92,13 +92,13 @@ func (network *Network) BroadcastIPv4Listen() {
// BroadcastIPv4Send sends out a single broadcast messages to discover peers
func (network *Network) BroadcastIPv4Send() (err error) {
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := network.backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, network.backend.FeatureSupport(), blockchainHeight, blockchainVersion, network.backend.userAgent)
if len(packets) == 0 {
return errors.New("error encoding broadcast announcement")
}
raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]})
raw, err := protocol.PacketEncrypt(network.backend.peerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]})
if err != nil {
return err
}
@@ -107,7 +107,7 @@ func (network *Network) BroadcastIPv4Send() (err error) {
for _, ip := range network.broadcastIPv4 {
err = network.send(ip, ipv4BroadcastPort, raw)
if err != nil {
Filters.LogError("BroadcastIPv4Send", "sending UDP packet: %v\n", err)
network.backend.Filters.LogError("BroadcastIPv4Send", "sending UDP packet: %v\n", err)
}
}

View File

@@ -56,7 +56,7 @@ func (network *Network) MulticastIPv6Join() (err error) {
// listen on a special socket
network.multicastSocket, err = reuseport.ListenPacket("udp6", net.JoinHostPort(network.address.IP.String(), strconv.Itoa(ipv6MulticastPort)))
if err != nil {
Filters.LogError("MulticastIPv6Join", "multicast socket listen on IP '%s' port '%d': %v\n", network.address.IP.String(), ipv6MulticastPort, err)
network.backend.Filters.LogError("MulticastIPv6Join", "multicast socket listen on IP '%s' port '%d': %v\n", network.address.IP.String(), ipv6MulticastPort, err)
return err
}
@@ -70,7 +70,7 @@ func (network *Network) MulticastIPv6Join() (err error) {
// receive messages from self or other processes running on the same computer
if loop, err := pc.MulticastLoopback(); err == nil && !loop {
if err := pc.SetMulticastLoopback(true); err != nil {
Filters.LogError("MulticastIPv6Join", "setting multicast loopback status: %v\n", err)
network.backend.Filters.LogError("MulticastIPv6Join", "setting multicast loopback status: %v\n", err)
}
}
@@ -108,8 +108,8 @@ func (network *Network) MulticastIPv6Listen() {
length, sender, err := network.multicastSocket.ReadFrom(buffer)
if err != nil {
Filters.LogError("MulticastIPv6Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
network.backend.Filters.LogError("MulticastIPv6Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
continue
}
@@ -137,13 +137,13 @@ func (network *Network) MulticastIPv6Listen() {
// MulticastIPv6Send sends out a single multicast messages to discover peers at the same site
func (network *Network) MulticastIPv6Send() (err error) {
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
_, blockchainHeight, blockchainVersion := network.backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, network.backend.FeatureSupport(), blockchainHeight, blockchainVersion, network.backend.userAgent)
if len(packets) == 0 {
return errors.New("error encoding multicast announcement")
}
raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv6MulticastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]})
raw, err := protocol.PacketEncrypt(network.backend.peerPrivateKey, ipv6MulticastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]})
if err != nil {
return err
}

View File

@@ -31,33 +31,33 @@ type networkWire struct {
}
// initNetwork sets up the network configuration and starts listening.
func initNetwork() {
func (backend *Backend) initNetwork() {
rand.Seed(time.Now().UnixNano()) // we are not using "crypto/rand" for speed tradeoff
// start listen workers
if config.ListenWorkers == 0 {
config.ListenWorkers = 2
if backend.Config.ListenWorkers == 0 {
backend.Config.ListenWorkers = 2
}
for n := 0; n < config.ListenWorkers; n++ {
go networks.packetWorker()
for n := 0; n < backend.Config.ListenWorkers; n++ {
go backend.networks.packetWorker()
}
// check if user specified where to listen
if len(config.Listen) > 0 {
for _, listenA := range config.Listen {
if len(backend.Config.Listen) > 0 {
for _, listenA := range backend.Config.Listen {
host, portA, err := net.SplitHostPort(listenA)
if err != nil && strings.Contains(err.Error(), "missing port in address") { // port is optional
host = listenA
portA = "0"
} else if err != nil {
Filters.LogError("initNetwork", "invalid input listen address '%s': %s\n", listenA, err.Error())
backend.Filters.LogError("initNetwork", "invalid input listen address '%s': %s\n", listenA, err.Error())
continue
}
portI, _ := strconv.Atoi(portA)
if _, err := networks.PrepareListen(host, portI); err != nil {
Filters.LogError("initNetwork", "listen on '%s': %s\n", listenA, err.Error())
if _, err := backend.networks.PrepareListen(host, portI); err != nil {
backend.Filters.LogError("initNetwork", "listen on '%s': %s\n", listenA, err.Error())
continue
}
}
@@ -79,20 +79,20 @@ func initNetwork() {
// * Network adapters and IPs might change. Simplest case is if someone changes Wifi network.
interfaceList, err := net.Interfaces()
if err != nil {
Filters.LogError("initNetwork", "enumerating network adapters failed: %s\n", err.Error())
backend.Filters.LogError("initNetwork", "enumerating network adapters failed: %s\n", err.Error())
return
}
for _, iface := range interfaceList {
addresses, err := iface.Addrs()
if err != nil {
Filters.LogError("initNetwork", "enumerating IPs for network adapter '%s': %s\n", iface.Name, err.Error())
backend.Filters.LogError("initNetwork", "enumerating IPs for network adapter '%s': %s\n", iface.Name, err.Error())
continue
}
networks.ipListen.ifacesExist[iface.Name] = addresses
backend.networks.ipListen.ifacesExist[iface.Name] = addresses
networks.InterfaceStart(iface, addresses)
backend.networks.InterfaceStart(iface, addresses)
}
}
@@ -116,13 +116,13 @@ func (nets *Networks) InterfaceStart(iface net.Interface, addresses []net.Addr)
continue
}
Filters.LogError("networks.InterfaceStart", "listening on network adapter '%s' IPv4 '%s': %s\n", iface.Name, net1.IP.String(), err.Error())
nets.backend.Filters.LogError("networks.InterfaceStart", "listening on network adapter '%s' IPv4 '%s': %s\n", iface.Name, net1.IP.String(), err.Error())
continue
}
nets.ipListen.Add(networkNew.address)
Filters.LogError("networks.InterfaceStart", "listen on network '%s' UDP %s\n", iface.Name, networkNew.address.String())
nets.backend.Filters.LogError("networks.InterfaceStart", "listen on network '%s' UDP %s\n", iface.Name, networkNew.address.String())
networksNew = append(networksNew, networkNew)
}
@@ -137,7 +137,7 @@ func (nets *Networks) PrepareListen(ipA string, port int) (network *Network, err
return nil, errors.New("invalid input IP")
}
network = &Network{networkGroup: nets}
network = &Network{backend: nets.backend, networkGroup: nets}
network.terminateSignal = make(chan interface{})
// get the network interface that belongs to the IP

View File

@@ -33,10 +33,10 @@ func (nets *Networks) startUPnP() {
}
}
if config.PortForward > 0 {
config.EnableUPnP = false
if nets.backend.Config.PortForward > 0 {
nets.backend.Config.EnableUPnP = false
}
if !config.EnableUPnP {
if !nets.backend.Config.EnableUPnP {
return
}
@@ -84,7 +84,7 @@ func isPrivateIP(ip net.IP) bool {
// upnpAuto runs a UPnP daemon to forward the port, refresh the forwarding and continuously monitor if the forwarding remains valid.
func (network *Network) upnpAuto() {
if !config.EnableUPnP || !network.upnpIsEligible() {
if !network.backend.Config.EnableUPnP || !network.upnpIsEligible() {
return
}
@@ -147,7 +147,7 @@ monitorLoop:
}
// invalid :(
Filters.LogError("upnpMonitorPortForward", "port forwarding invalidated for local IP %s (adapter %s) external IP %s port %d\n", network.address.String(), network.iface.Name, network.ipExternal.String(), network.portExternal)
network.backend.Filters.LogError("upnpMonitorPortForward", "port forwarding invalidated for local IP %s (adapter %s) external IP %s port %d\n", network.address.String(), network.iface.Name, network.ipExternal.String(), network.portExternal)
network.portExternal = 0
network.ipExternal = net.IP{}

View File

@@ -35,6 +35,7 @@ type Network struct {
terminateSignal chan interface{} // gets closed on termination signal, can be used in select via "case _ = <- network.terminateSignal:"
sync.RWMutex // for sychronized closing
networkGroup *Networks // Pointer to the pool of networks that this is part of
backend *Backend
}
// Default ports to use. This may be randomized in the future to prevent fingerprinting (and subsequent blocking) by corporate and ISP firewalls.
@@ -115,8 +116,8 @@ func (network *Network) Listen() {
return
}
Filters.LogError("Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
network.backend.Filters.LogError("Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
continue
}
@@ -126,7 +127,7 @@ func (network *Network) Listen() {
}
// send the packet to a channel which is processed by multiple workers.
network.networkGroup.rawPacketsIncoming <- networkWire{network: network, sender: sender, raw: buffer[:length], receiverPublicKey: peerPublicKey, unicast: true}
network.networkGroup.rawPacketsIncoming <- networkWire{network: network, sender: sender, raw: buffer[:length], receiverPublicKey: network.backend.peerPublicKey, unicast: true}
}
}
@@ -140,7 +141,7 @@ func (nets *Networks) packetWorker() {
}
// immediately discard message if sender = self
if senderPublicKey.IsEqual(peerPublicKey) {
if senderPublicKey.IsEqual(nets.backend.peerPublicKey) {
continue
}
@@ -149,12 +150,12 @@ func (nets *Networks) packetWorker() {
continue
}
connection := &Connection{Network: packet.network, Address: packet.sender, Status: ConnectionActive}
connection := &Connection{backend: nets.backend, Network: packet.network, Address: packet.sender, Status: ConnectionActive}
Filters.PacketIn(decoded, senderPublicKey, connection)
nets.backend.Filters.PacketIn(decoded, senderPublicKey, connection)
// A peer structure will always be returned, even if the peer won't be added to the peer list.
peer, added := PeerlistAdd(senderPublicKey, connection)
peer, added := nets.backend.PeerlistAdd(senderPublicKey, connection)
if !added {
connection = peer.registerConnection(connection)
}
@@ -182,7 +183,7 @@ func (nets *Networks) packetWorker() {
peer.BlockchainVersion = announce.BlockchainVersion
peer.blockchainLastRefresh = time.Now()
Filters.MessageIn(peer, raw, announce)
nets.backend.Filters.MessageIn(peer, raw, announce)
peer.cmdAnouncement(announce, connection)
@@ -218,7 +219,7 @@ func (nets *Networks) packetWorker() {
peer.BlockchainVersion = response.BlockchainVersion
peer.blockchainLastRefresh = time.Now()
Filters.MessageIn(peer, raw, response)
nets.backend.Filters.MessageIn(peer, raw, response)
peer.cmdResponse(response, connection)
@@ -239,7 +240,7 @@ func (nets *Networks) packetWorker() {
peer.BlockchainVersion = announce.BlockchainVersion
peer.blockchainLastRefresh = time.Now()
Filters.MessageIn(peer, raw, announce)
nets.backend.Filters.MessageIn(peer, raw, announce)
peer.cmdLocalDiscovery(announce, connection)
@@ -249,7 +250,7 @@ func (nets *Networks) packetWorker() {
}
case protocol.CommandPing: // Ping
Filters.MessageIn(peer, raw, nil)
nets.backend.Filters.MessageIn(peer, raw, nil)
peer.cmdPing(raw, connection)
case protocol.CommandPong: // Ping
@@ -263,20 +264,20 @@ func (nets *Networks) packetWorker() {
}
raw.SequenceInfo = sequenceInfo
Filters.MessageIn(peer, raw, nil)
nets.backend.Filters.MessageIn(peer, raw, nil)
peer.cmdPong(raw, connection)
case protocol.CommandChat: // Chat [debug]
Filters.MessageIn(peer, raw, nil)
nets.backend.Filters.MessageIn(peer, raw, nil)
peer.cmdChat(raw, connection)
case protocol.CommandTraverse:
if traverse, _ := protocol.DecodeTraverse(raw); traverse != nil {
Filters.MessageIn(peer, raw, traverse)
if traverse.TargetPeer.IsEqual(peerPublicKey) && traverse.AuthorizedRelayPeer.IsEqual(peer.PublicKey) {
nets.backend.Filters.MessageIn(peer, raw, traverse)
if traverse.TargetPeer.IsEqual(nets.backend.peerPublicKey) && traverse.AuthorizedRelayPeer.IsEqual(peer.PublicKey) {
peer.cmdTraverseReceive(traverse)
} else if traverse.AuthorizedRelayPeer.IsEqual(peerPublicKey) {
} else if traverse.AuthorizedRelayPeer.IsEqual(nets.backend.peerPublicKey) {
peer.cmdTraverseForward(traverse)
}
}
@@ -314,7 +315,7 @@ func (nets *Networks) packetWorker() {
}
default: // Unknown command
Filters.MessageIn(peer, raw, nil)
nets.backend.Filters.MessageIn(peer, raw, nil)
}
@@ -322,12 +323,12 @@ func (nets *Networks) packetWorker() {
}
// GetNetworks returns the list of connected networks
func GetNetworks(networkType int) (networksConnected []*Network) {
func (backend *Backend) GetNetworks(networkType int) (networksConnected []*Network) {
switch networkType {
case 4:
return networks.networks4
return backend.networks.networks4
case 6:
return networks.networks6
return backend.networks.networks6
}
return nil
}
@@ -383,22 +384,22 @@ func (network *Network) SelfReportedPorts() (portI, portE uint16) {
// This external port will be then passed onto other peers who will use it to connect.
portE = network.portExternal
if config.PortForward > 0 {
portE = config.PortForward
if network.backend.Config.PortForward > 0 {
portE = network.backend.Config.PortForward
}
return portI, portE
}
// FeatureSupport returns supported features by this peer
func FeatureSupport() (feature byte) {
if networks.countListen4 > 0 {
func (backend *Backend) FeatureSupport() (feature byte) {
if backend.networks.countListen4 > 0 {
feature |= 1 << protocol.FeatureIPv4Listen
}
if networks.countListen6 > 0 {
if backend.networks.countListen6 > 0 {
feature |= 1 << protocol.FeatureIPv6Listen
}
if networks.localFirewall {
if backend.networks.localFirewall {
feature |= 1 << protocol.FeatureFirewall
}
return feature

View File

@@ -35,25 +35,26 @@ type Networks struct {
// localFirewall indicates if a local firewall may drop unsolicited incoming packets
localFirewall bool
// backend
backend *Backend
}
// ReplyTimeout is the round-trip timeout for message sequences.
const ReplyTimeout = 20
var networks *Networks
func (backend *Backend) initMessageSequence() {
backend.networks = &Networks{backend: backend}
func initMessageSequence() {
networks = &Networks{}
backend.networks.rawPacketsIncoming = make(chan networkWire, 1000) // buffer up to 1000 UDP packets before they get buffered by the OS network stack and eventually dropped
networks.rawPacketsIncoming = make(chan networkWire, 1000) // buffer up to 1000 UDP packets before they get buffered by the OS network stack and eventually dropped
backend.networks.Sequences = protocol.NewSequenceManager(ReplyTimeout)
networks.Sequences = protocol.NewSequenceManager(ReplyTimeout)
networks.ipListen = NewIPList()
backend.networks.ipListen = NewIPList()
// There is currently no suitable live firewall detection code. Instead, there is the config flag.
// Windows: If the user runs as non-admin, it can be assumed that the Windows Firewall creates a rule to drop unsolicited incoming packets.
// Changing the Windows Firewall (via netsh or otherwise) requires elevated admin rights.
// This flag will be passed on to other peers to indicate that uncontacted peers shall use the Traverse message for establishing connections.
networks.localFirewall = config.LocalFirewall
backend.networks.localFirewall = backend.Config.LocalFirewall
}

View File

@@ -20,46 +20,40 @@ import (
"github.com/PeernetOfficial/core/protocol"
)
// peerID is the current peers ID. It is a ECDSA (secp256k1) 257-bit public key.
// The node ID is the blake3 hash of the public key compressed form.
var peerPrivateKey *btcec.PrivateKey
var peerPublicKey *btcec.PublicKey
var nodeID []byte
func initPeerID() {
peerList = make(map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo)
nodeList = make(map[[protocol.HashSize]byte]*PeerInfo)
func (backend *Backend) initPeerID() {
backend.peerList = make(map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo)
backend.nodeList = make(map[[protocol.HashSize]byte]*PeerInfo)
// load existing key from config, if available
if len(config.PrivateKey) > 0 {
configPK, err := hex.DecodeString(config.PrivateKey)
if len(backend.Config.PrivateKey) > 0 {
configPK, err := hex.DecodeString(backend.Config.PrivateKey)
if err == nil {
peerPrivateKey, peerPublicKey = btcec.PrivKeyFromBytes(btcec.S256(), configPK)
nodeID = protocol.PublicKey2NodeID(peerPublicKey)
backend.peerPrivateKey, backend.peerPublicKey = btcec.PrivKeyFromBytes(btcec.S256(), configPK)
backend.nodeID = protocol.PublicKey2NodeID(backend.peerPublicKey)
if config.AutoUpdateSeedList {
configUpdateSeedList()
if backend.Config.AutoUpdateSeedList {
backend.configUpdateSeedList()
}
return
}
Filters.LogError("initPeerID", "private key in config is corrupted! Error: %s\n", err.Error())
backend.Filters.LogError("initPeerID", "private key in config is corrupted! Error: %s\n", err.Error())
os.Exit(ExitPrivateKeyCorrupt)
}
// if the peer ID is empty, create a new user public-private key pair
var err error
peerPrivateKey, peerPublicKey, err = Secp256k1NewPrivateKey()
backend.peerPrivateKey, backend.peerPublicKey, err = Secp256k1NewPrivateKey()
if err != nil {
Filters.LogError("initPeerID", "generating public-private key pairs: %s\n", err.Error())
backend.Filters.LogError("initPeerID", "generating public-private key pairs: %s\n", err.Error())
os.Exit(ExitPrivateKeyCreate)
}
nodeID = protocol.PublicKey2NodeID(peerPublicKey)
backend.nodeID = protocol.PublicKey2NodeID(backend.peerPublicKey)
// save the newly generated private key into the config
config.PrivateKey = hex.EncodeToString(peerPrivateKey.Serialize())
backend.Config.PrivateKey = hex.EncodeToString(backend.peerPrivateKey.Serialize())
saveConfig()
backend.SaveConfig()
}
// Secp256k1NewPrivateKey creates a new public-private key pair
@@ -73,18 +67,18 @@ func Secp256k1NewPrivateKey() (privateKey *btcec.PrivateKey, publicKey *btcec.Pu
}
// ExportPrivateKey returns the peers public and private key
func ExportPrivateKey() (privateKey *btcec.PrivateKey, publicKey *btcec.PublicKey) {
return peerPrivateKey, peerPublicKey
func (backend *Backend) ExportPrivateKey() (privateKey *btcec.PrivateKey, publicKey *btcec.PublicKey) {
return backend.peerPrivateKey, backend.peerPublicKey
}
// SelfNodeID returns the node ID used for DHT
func SelfNodeID() []byte {
return nodeID
func (backend *Backend) SelfNodeID() []byte {
return backend.nodeID
}
// SelfUserAgent returns the User Agent
func SelfUserAgent() string {
return userAgent
func (backend *Backend) SelfUserAgent() string {
return backend.userAgent
}
// PeerInfo stores information about a single remote peer
@@ -109,6 +103,8 @@ type PeerInfo struct {
// statistics
StatsPacketSent uint64 // Count of packets sent
StatsPacketReceived uint64 // Count of packets received
Backend *Backend
}
type peerAddress struct {
@@ -117,79 +113,72 @@ type peerAddress struct {
PortInternal uint16
}
// peerList keeps track of all peers
var peerList map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo
var peerlistMutex sync.RWMutex
// nodeList is a mirror of peerList but using the node ID
var nodeList map[[protocol.HashSize]byte]*PeerInfo
// PeerlistAdd adds a new peer to the peer list. It does not validate the peer info. If the peer is already added, it does nothing. Connections must be live.
func PeerlistAdd(PublicKey *btcec.PublicKey, connections ...*Connection) (peer *PeerInfo, added bool) {
func (backend *Backend) PeerlistAdd(PublicKey *btcec.PublicKey, connections ...*Connection) (peer *PeerInfo, added bool) {
if len(connections) == 0 {
return nil, false
}
publicKeyCompressed := publicKey2Compressed(PublicKey)
peerlistMutex.Lock()
defer peerlistMutex.Unlock()
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
peer, ok := peerList[publicKeyCompressed]
peer, ok := backend.peerList[publicKeyCompressed]
if ok {
return peer, false
}
peer = &PeerInfo{PublicKey: PublicKey, connectionActive: connections, connectionLatest: connections[0], NodeID: protocol.PublicKey2NodeID(PublicKey), messageSequence: rand.Uint32()}
peer = &PeerInfo{Backend: backend, PublicKey: PublicKey, connectionActive: connections, connectionLatest: connections[0], NodeID: protocol.PublicKey2NodeID(PublicKey), messageSequence: rand.Uint32()}
_, peer.IsRootPeer = rootPeers[publicKeyCompressed]
peerList[publicKeyCompressed] = peer
backend.peerList[publicKeyCompressed] = peer
// also add to mirrored nodeList
var nodeID [protocol.HashSize]byte
copy(nodeID[:], peer.NodeID)
nodeList[nodeID] = peer
backend.nodeList[nodeID] = peer
// add to Kademlia
nodesDHT.AddNode(&dht.Node{ID: peer.NodeID, Info: peer})
backend.nodesDHT.AddNode(&dht.Node{ID: peer.NodeID, Info: peer})
// TODO: If the node isn't added to Kademlia, it should be either added temporarily to the peerList with an expiration, or to a temp list, or not at all.
// send to all channels non-blocking
for _, monitor := range peerMonitor {
for _, monitor := range backend.peerMonitor {
select {
case monitor <- peer:
default:
}
}
Filters.NewPeer(peer, connections[0])
Filters.NewPeerConnection(peer, connections[0])
backend.Filters.NewPeer(peer, connections[0])
backend.Filters.NewPeerConnection(peer, connections[0])
return peer, true
}
// PeerlistRemove removes a peer from the peer list.
func PeerlistRemove(peer *PeerInfo) {
peerlistMutex.Lock()
defer peerlistMutex.Unlock()
func (backend *Backend) PeerlistRemove(peer *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
// remove from Kademlia
nodesDHT.RemoveNode(peer.NodeID)
backend.nodesDHT.RemoveNode(peer.NodeID)
delete(peerList, publicKey2Compressed(peer.PublicKey))
delete(backend.peerList, publicKey2Compressed(peer.PublicKey))
var nodeID [protocol.HashSize]byte
copy(nodeID[:], peer.NodeID)
delete(nodeList, nodeID)
delete(backend.nodeList, nodeID)
}
// PeerlistGet returns the full peer list
func PeerlistGet() (peers []*PeerInfo) {
peerlistMutex.RLock()
defer peerlistMutex.RUnlock()
func (backend *Backend) PeerlistGet() (peers []*PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
for _, peer := range peerList {
for _, peer := range backend.peerList {
peers = append(peers, peer)
}
@@ -197,30 +186,30 @@ func PeerlistGet() (peers []*PeerInfo) {
}
// PeerlistLookup returns the peer from the list with the public key
func PeerlistLookup(publicKey *btcec.PublicKey) (peer *PeerInfo) {
peerlistMutex.RLock()
defer peerlistMutex.RUnlock()
func (backend *Backend) PeerlistLookup(publicKey *btcec.PublicKey) (peer *PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
return peerList[publicKey2Compressed(publicKey)]
return backend.peerList[publicKey2Compressed(publicKey)]
}
// NodelistLookup returns the peer from the list with the node ID
func NodelistLookup(nodeID []byte) (peer *PeerInfo) {
peerlistMutex.RLock()
defer peerlistMutex.RUnlock()
func (backend *Backend) NodelistLookup(nodeID []byte) (peer *PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
var nodeID2 [protocol.HashSize]byte
copy(nodeID2[:], nodeID)
return nodeList[nodeID2]
return backend.nodeList[nodeID2]
}
// PeerlistCount returns the current count of peers in the peer list
func PeerlistCount() (count int) {
peerlistMutex.RLock()
defer peerlistMutex.RUnlock()
func (backend *Backend) PeerlistCount() (count int) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
return len(peerList)
return len(backend.peerList)
}
func publicKey2Compressed(publicKey *btcec.PublicKey) [btcec.PubKeyBytesLenCompressed]byte {
@@ -229,11 +218,11 @@ func publicKey2Compressed(publicKey *btcec.PublicKey) [btcec.PubKeyBytesLenCompr
return key
}
// records2Nodes translates infoPeer structures to nodes. If the reported nodes are not in the peer table, it will create temporary PeerInfo structures.
// records2Nodes translates infoPeer structures to nodes reported by the peer. If the reported nodes are not in the peer table, it will create temporary PeerInfo structures.
// LastContact is passed on in the Node.LastSeen field.
func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes []*dht.Node) {
func (peerSource *PeerInfo) records2Nodes(records []protocol.PeerRecord) (nodes []*dht.Node) {
for _, record := range records {
if isReturnedPeerBadQuality(&record) {
if peerSource.Backend.isReturnedPeerBadQuality(&record) {
continue
}
@@ -241,7 +230,7 @@ func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes [
if record.PublicKey.IsEqual(peerSource.PublicKey) {
// Special case if peer that stores info = sender. In that case IP:Port in the record would be empty anyway.
peer = peerSource
} else if peer = PeerlistLookup(record.PublicKey); peer == nil {
} else if peer = peerSource.Backend.PeerlistLookup(record.PublicKey); peer == nil {
// Create temporary peer which is not added to the global list and not added to Kademlia.
// traversePeer is set to the peer who provided the node information.
addresses := peerRecordToAddresses(&record)
@@ -249,7 +238,7 @@ func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes [
continue
}
peer = &PeerInfo{PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource, Features: record.Features}
peer = &PeerInfo{Backend: peerSource.Backend, PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource, Features: record.Features}
}
nodes = append(nodes, &dht.Node{ID: peer.NodeID, LastSeen: record.LastContactT, Info: peer})
@@ -259,55 +248,53 @@ func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes [
}
// selfPeerRecord returns self as peer record
func selfPeerRecord() (result protocol.PeerRecord) {
func (backend *Backend) selfPeerRecord() (result protocol.PeerRecord) {
return protocol.PeerRecord{
PublicKey: peerPublicKey,
NodeID: nodeID,
PublicKey: backend.peerPublicKey,
NodeID: backend.nodeID,
//IP: network.address.IP,
//Port: uint16(network.address.Port),
LastContact: 0,
Features: FeatureSupport(),
Features: backend.FeatureSupport(),
}
}
var peerMonitor []chan<- *PeerInfo
// registerPeerMonitor registers a channel to receive all new peers
func registerPeerMonitor(channel chan<- *PeerInfo) {
peerlistMutex.Lock()
defer peerlistMutex.Unlock()
func (backend *Backend) registerPeerMonitor(channel chan<- *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
peerMonitor = append(peerMonitor, channel)
backend.peerMonitor = append(backend.peerMonitor, channel)
}
// unregisterPeerMonitor unregisters a channel
func unregisterPeerMonitor(channel chan<- *PeerInfo) {
peerlistMutex.Lock()
defer peerlistMutex.Unlock()
func (backend *Backend) unregisterPeerMonitor(channel chan<- *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
for n, channel2 := range peerMonitor {
for n, channel2 := range backend.peerMonitor {
if channel == channel2 {
peerMonitorNew := peerMonitor[:n]
if n < len(peerMonitor)-1 {
peerMonitorNew = append(peerMonitorNew, peerMonitor[n+1:]...)
peerMonitorNew := backend.peerMonitor[:n]
if n < len(backend.peerMonitor)-1 {
peerMonitorNew = append(peerMonitorNew, backend.peerMonitor[n+1:]...)
}
peerMonitor = peerMonitorNew
backend.peerMonitor = peerMonitorNew
break
}
}
}
// DeleteAccount deletes the account
func DeleteAccount() {
func (backend *Backend) DeleteAccount() {
// delete the blockchain
UserBlockchain.DeleteBlockchain()
backend.UserBlockchain.DeleteBlockchain()
// delete the warehouse
UserWarehouse.DeleteWarehouse()
backend.UserWarehouse.DeleteWarehouse()
// delete the private key
config.PrivateKey = ""
saveConfig()
backend.Config.PrivateKey = ""
backend.SaveConfig()
}
// PublicKeyFromPeerID decodes the peer ID (hex encoded) into a public key.

View File

@@ -7,63 +7,105 @@ Author: Peter Kleissner
package core
import (
"sync"
"github.com/PeernetOfficial/core/blockchain"
"github.com/PeernetOfficial/core/btcec"
"github.com/PeernetOfficial/core/dht"
"github.com/PeernetOfficial/core/protocol"
"github.com/PeernetOfficial/core/search"
"github.com/PeernetOfficial/core/store"
"github.com/PeernetOfficial/core/warehouse"
)
var userAgent = "Peernet Core/0.1" // must be overwritten by the caller
// Init initializes the client. The config must be loaded first!
// Init initializes the client. If the config file does not exist or is empty, a default one will be created.
// The User Agent must be provided in the form "Application Name/1.0".
func Init(UserAgent string) (backend *Backend) {
if userAgent = UserAgent; userAgent == "" {
// The returned status is of type ExitX. Anything other than ExitSuccess indicates a fatal failure.
func Init(UserAgent string, ConfigFilename string, Filters *Filters) (backend *Backend, status int, err error) {
if UserAgent == "" {
return
}
backend = &Backend{}
currentBackend = backend
backend = &Backend{
ConfigFilename: ConfigFilename,
userAgent: UserAgent,
}
initFilters()
initPeerID()
initUserBlockchain()
initUserWarehouse()
initKademlia()
initMessageSequence()
initSeedList()
if Filters != nil {
backend.Filters = *Filters
}
// The configuration and log init are fatal events if they fail.
if status, err = LoadConfig(ConfigFilename, &backend.Config); status != ExitSuccess {
return nil, status, err
}
if err = backend.initLog(); err != nil {
return nil, ExitErrorLogInit, err
}
backend.initFilters()
backend.initPeerID()
backend.initUserBlockchain()
backend.initUserWarehouse()
backend.initKademlia()
backend.initMessageSequence()
backend.initSeedList()
initMulticastIPv6()
initBroadcastIPv4()
initStore()
initNetwork()
backend.initStore()
backend.initNetwork()
backend.initBlockchainCache()
var err error
backend.GlobalBlockchainCache = initBlockchainCache(config.BlockchainGlobal, config.CacheMaxBlockSize, config.CacheMaxBlockCount, config.LimitTotalRecords)
if backend.SearchIndex, err = search.InitSearchIndexStore(config.SearchIndex); err != nil {
Filters.LogError("Init", "search index '%s' init: %s", config.SearchIndex, err.Error())
if backend.SearchIndex, err = search.InitSearchIndexStore(backend.Config.SearchIndex); err != nil {
backend.Filters.LogError("Init", "search index '%s' init: %s", backend.Config.SearchIndex, err.Error())
} else {
backend.userBlockchainUpdateSearchIndex()
}
return backend
return backend, ExitSuccess, nil
}
// Connect starts bootstrapping and local peer discovery.
func Connect() {
go bootstrapKademlia()
go bootstrap()
go networks.autoMulticastBroadcast()
go autoPingAll()
go networks.networkChangeMonitor()
go networks.startUPnP()
go autoBucketRefresh()
func (backend *Backend) Connect() {
go backend.bootstrapKademlia()
go backend.bootstrap()
go backend.networks.autoMulticastBroadcast()
go backend.autoPingAll()
go backend.networks.networkChangeMonitor()
go backend.networks.startUPnP()
go backend.autoBucketRefresh()
}
// The Backend represents an instance of a Peernet client to be used by a frontend.
// Global variables and init functions are to be merged.
type Backend struct {
ConfigFilename string // Filename of the configuration file.
Config *Config // Config
Filters Filters // Filters allow to install hooks.
userAgent string // User Agent
GlobalBlockchainCache *BlockchainCache // Caches blockchains of other peers.
SearchIndex *search.SearchIndexStore // Search index of blockchain records.
}
networks *Networks // All connected networks.
dhtStore store.Store // dhtStore contains all key-value data served via DHT
UserBlockchain *blockchain.Blockchain // UserBlockchain is the user's blockchain and exports functions to directly read and write it
UserWarehouse *warehouse.Warehouse // UserWarehouse is the user's warehouse for storing files that are shared
nodesDHT *dht.DHT // Nodes connected in the DHT.
// This variable is to be replaced later by pointers in structures.
var currentBackend *Backend
// peerID is the current peer's ID. It is a ECDSA (secp256k1) 257-bit public key.
peerPrivateKey *btcec.PrivateKey
peerPublicKey *btcec.PublicKey
// The node ID is the blake3 hash of the public key compressed form.
nodeID []byte
// peerList keeps track of all peers
peerList map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo
peerlistMutex sync.RWMutex
// nodeList is a mirror of peerList but using the node ID
nodeList map[[protocol.HashSize]byte]*PeerInfo
// peerMonitor is a list of channels receiving information about new peers
peerMonitor []chan<- *PeerInfo
}

View File

@@ -24,7 +24,7 @@ const connectionInvalidate = 22
const connectionRemove = 2 * 60
// autoPingAll sends out regular ping messages to all connections of all peers. This allows to detect invalid connections and eventually drop them.
func autoPingAll() {
func (backend *Backend) autoPingAll() {
for {
time.Sleep(time.Second)
thresholdInvalidate1 := time.Now().Add(-connectionInvalidate * time.Second)
@@ -33,7 +33,7 @@ func autoPingAll() {
thresholdPingOut2 := time.Now().Add(-pingTime * time.Second * 4)
thresholdBlockchainRefresh := time.Now().Add(-thresholdBlockchainRefresh)
for _, peer := range PeerlistGet() {
for _, peer := range backend.PeerlistGet() {
// first handle active connections
for _, connection := range peer.GetConnections(true) {
thresholdPing := thresholdPingOut1

View File

@@ -2,7 +2,7 @@
The core library which is needed for any Peernet application. It provides connectivity to the network and all basic functions. For details about Peernet see https://peernet.org/.
Current version: Alpha 5
Current version: Alpha 6
## Use
@@ -11,25 +11,21 @@ package main
import (
"fmt"
"os"
"github.com/PeernetOfficial/core"
)
func init() {
if status, err := core.LoadConfig("Config.yaml"); err != nil {
fmt.Printf("Error loading config file: %s", err.Error())
os.Exit(core.ExitErrorConfigAccess)
func main() {
backend, status, err := core.Init("Your application/1.0", "Config.yaml", nil)
if status != core.ExitSuccess {
fmt.Printf("Error %d initializing backend: %s\n", status, err.Error())
return
}
core.InitLog()
core.Init("Your application/1.0")
}
backend.Connect()
func main() {
core.Connect()
// use functions from core package, for example to find and download files
// Use the backend functions for example to search for files or download them.
// The webapi package exports some high-level functions that can be used directly (without calling the HTTP API).
}
```

View File

@@ -28,7 +28,7 @@ func (peer *PeerInfo) startBlockTransfer(BlockchainPublicKey *btcec.PublicKey, L
// register the sequence since packets are sent bi-directional
virtualConn.sequenceNumber = sequenceNumber
networks.Sequences.RegisterSequenceBi(peer.PublicKey, sequenceNumber, virtualConn, blockSequenceTimeout, virtualConn.sequenceTerminate)
peer.Backend.networks.Sequences.RegisterSequenceBi(peer.PublicKey, sequenceNumber, virtualConn, blockSequenceTimeout, virtualConn.sequenceTerminate)
udtConfig := udt.DefaultConfig()
udtConfig.MaxPacketSize = protocol.TransferMaxEmbedSize
@@ -48,7 +48,7 @@ func (peer *PeerInfo) startBlockTransfer(BlockchainPublicKey *btcec.PublicKey, L
for _, target := range TargetBlocks {
for blockN := target.Offset; blockN < target.Offset+target.Limit; blockN++ {
blockData, status, err := UserBlockchain.GetBlockRaw(blockN)
blockData, status, err := peer.Backend.UserBlockchain.GetBlockRaw(blockN)
if err != nil {
protocol.BlockTransferWriteHeader(udtConn, protocol.GetBlockStatusNotAvailable, protocol.BlockRange{Offset: blockN, Limit: 1}, 0)
continue
@@ -84,7 +84,7 @@ func (peer *PeerInfo) BlockTransferRequest(BlockchainPublicKey *btcec.PublicKey,
})
// new sequence
sequence := networks.Sequences.NewSequenceBi(peer.PublicKey, &peer.messageSequence, virtualConn, blockSequenceTimeout, virtualConn.sequenceTerminate)
sequence := peer.Backend.networks.Sequences.NewSequenceBi(peer.PublicKey, &peer.messageSequence, virtualConn, blockSequenceTimeout, virtualConn.sequenceTerminate)
if sequence == nil {
return nil, nil, errors.New("cannot acquire sequence")
}

View File

@@ -42,7 +42,7 @@ func (peer *PeerInfo) startFileTransferUDT(hash []byte, fileSize uint64, offset,
// register the sequence since packets are sent bi-directional
virtualConnection.sequenceNumber = sequenceNumber
networks.Sequences.RegisterSequenceBi(peer.PublicKey, sequenceNumber, virtualConnection, transferSequenceTimeout, virtualConnection.sequenceTerminate)
peer.Backend.networks.Sequences.RegisterSequenceBi(peer.PublicKey, sequenceNumber, virtualConnection, transferSequenceTimeout, virtualConnection.sequenceTerminate)
udtConfig := udt.DefaultConfig()
udtConfig.MaxPacketSize = protocol.TransferMaxEmbedSize
@@ -60,7 +60,7 @@ func (peer *PeerInfo) startFileTransferUDT(hash []byte, fileSize uint64, offset,
// First send the header (Total File Size, Transfer Size) and then the file data.
protocol.FileTransferWriteHeader(udtConn, fileSize, limit)
_, _, err = UserWarehouse.ReadFile(hash, int64(offset), int64(limit), udtConn)
_, _, err = peer.Backend.UserWarehouse.ReadFile(hash, int64(offset), int64(limit), udtConn)
return err
}
@@ -74,7 +74,7 @@ func (peer *PeerInfo) FileTransferRequestUDT(hash []byte, offset, limit uint64)
})
// new sequence
sequence := networks.Sequences.NewSequenceBi(peer.PublicKey, &peer.messageSequence, virtualConn, transferSequenceTimeout, virtualConn.sequenceTerminate)
sequence := peer.Backend.networks.Sequences.NewSequenceBi(peer.PublicKey, &peer.messageSequence, virtualConn, transferSequenceTimeout, virtualConn.sequenceTerminate)
if sequence == nil {
return nil, nil, errors.New("cannot acquire sequence")
}

View File

@@ -107,7 +107,7 @@ func (v *virtualPacketConn) sequenceTerminate() {
// Do not call the function manually; otherwise the underlying transfer protocol may not have time to send a termination message (and the remote peer would subsequently try to reconnect).
// Rather, use the underlying transfer protocol's close function.
func (v *virtualPacketConn) Close(reason int) (err error) {
networks.Sequences.InvalidateSequence(v.peer.PublicKey, v.sequenceNumber, true)
v.peer.Backend.networks.Sequences.InvalidateSequence(v.peer.PublicKey, v.sequenceNumber, true)
return v.Terminate(reason)
}

View File

@@ -10,14 +10,11 @@ import (
"github.com/PeernetOfficial/core/warehouse"
)
// UserWarehouse is the user's warehouse for storing files that are shared
var UserWarehouse *warehouse.Warehouse
func initUserWarehouse() {
func (backend *Backend) initUserWarehouse() {
var err error
UserWarehouse, err = warehouse.Init(config.WarehouseMain)
backend.UserWarehouse, err = warehouse.Init(backend.Config.WarehouseMain)
if err != nil {
Filters.LogError("initUserWarehouse", "error: %s\n", err.Error())
backend.Filters.LogError("initUserWarehouse", "error: %s\n", err.Error())
}
}

View File

@@ -58,40 +58,40 @@ func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, Certifi
}
api.Router.HandleFunc("/test", apiTest).Methods("GET")
api.Router.HandleFunc("/status", apiStatus).Methods("GET")
api.Router.HandleFunc("/account/info", apiAccountInfo).Methods("GET")
api.Router.HandleFunc("/account/delete", apiAccountDelete).Methods("GET")
api.Router.HandleFunc("/blockchain/header", apiBlockchainHeaderFunc).Methods("GET")
api.Router.HandleFunc("/blockchain/append", apiBlockchainAppend).Methods("POST")
api.Router.HandleFunc("/blockchain/read", apiBlockchainRead).Methods("GET")
api.Router.HandleFunc("/blockchain/file/add", apiBlockchainFileAdd).Methods("POST")
api.Router.HandleFunc("/blockchain/file/list", apiBlockchainFileList).Methods("GET")
api.Router.HandleFunc("/blockchain/file/delete", apiBlockchainFileDelete).Methods("POST")
api.Router.HandleFunc("/blockchain/file/update", apiBlockchainFileUpdate).Methods("POST")
api.Router.HandleFunc("/profile/list", apiProfileList).Methods("GET")
api.Router.HandleFunc("/profile/read", apiProfileRead).Methods("GET")
api.Router.HandleFunc("/profile/write", apiProfileWrite).Methods("POST")
api.Router.HandleFunc("/profile/delete", apiProfileDelete).Methods("POST")
api.Router.HandleFunc("/status", api.apiStatus).Methods("GET")
api.Router.HandleFunc("/account/info", api.apiAccountInfo).Methods("GET")
api.Router.HandleFunc("/account/delete", api.apiAccountDelete).Methods("GET")
api.Router.HandleFunc("/blockchain/header", api.apiBlockchainHeaderFunc).Methods("GET")
api.Router.HandleFunc("/blockchain/append", api.apiBlockchainAppend).Methods("POST")
api.Router.HandleFunc("/blockchain/read", api.apiBlockchainRead).Methods("GET")
api.Router.HandleFunc("/blockchain/file/add", api.apiBlockchainFileAdd).Methods("POST")
api.Router.HandleFunc("/blockchain/file/list", api.apiBlockchainFileList).Methods("GET")
api.Router.HandleFunc("/blockchain/file/delete", api.apiBlockchainFileDelete).Methods("POST")
api.Router.HandleFunc("/blockchain/file/update", api.apiBlockchainFileUpdate).Methods("POST")
api.Router.HandleFunc("/profile/list", api.apiProfileList).Methods("GET")
api.Router.HandleFunc("/profile/read", api.apiProfileRead).Methods("GET")
api.Router.HandleFunc("/profile/write", api.apiProfileWrite).Methods("POST")
api.Router.HandleFunc("/profile/delete", api.apiProfileDelete).Methods("POST")
api.Router.HandleFunc("/search", api.apiSearch).Methods("POST")
api.Router.HandleFunc("/search/result", apiSearchResult).Methods("GET")
api.Router.HandleFunc("/search/result/ws", apiSearchResultStream).Methods("GET")
api.Router.HandleFunc("/search/statistic", apiSearchStatistic).Methods("GET")
api.Router.HandleFunc("/search/terminate", apiSearchTerminate).Methods("GET")
api.Router.HandleFunc("/search/result", api.apiSearchResult).Methods("GET")
api.Router.HandleFunc("/search/result/ws", api.apiSearchResultStream).Methods("GET")
api.Router.HandleFunc("/search/statistic", api.apiSearchStatistic).Methods("GET")
api.Router.HandleFunc("/search/terminate", api.apiSearchTerminate).Methods("GET")
api.Router.HandleFunc("/explore", api.apiExplore).Methods("GET")
api.Router.HandleFunc("/file/format", apiFileFormat).Methods("GET")
api.Router.HandleFunc("/download/start", apiDownloadStart).Methods("GET")
api.Router.HandleFunc("/download/status", apiDownloadStatus).Methods("GET")
api.Router.HandleFunc("/download/action", apiDownloadAction).Methods("GET")
api.Router.HandleFunc("/warehouse/create", apiWarehouseCreateFile).Methods("POST")
api.Router.HandleFunc("/warehouse/create/path", apiWarehouseCreateFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/read", apiWarehouseReadFile).Methods("GET")
api.Router.HandleFunc("/warehouse/read/path", apiWarehouseReadFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/delete", apiWarehouseDeleteFile).Methods("GET")
api.Router.HandleFunc("/file/read", apiFileRead).Methods("GET")
api.Router.HandleFunc("/file/view", apiFileView).Methods("GET")
api.Router.HandleFunc("/file/format", api.apiFileFormat).Methods("GET")
api.Router.HandleFunc("/download/start", api.apiDownloadStart).Methods("GET")
api.Router.HandleFunc("/download/status", api.apiDownloadStatus).Methods("GET")
api.Router.HandleFunc("/download/action", api.apiDownloadAction).Methods("GET")
api.Router.HandleFunc("/warehouse/create", api.apiWarehouseCreateFile).Methods("POST")
api.Router.HandleFunc("/warehouse/create/path", api.apiWarehouseCreateFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/read", api.apiWarehouseReadFile).Methods("GET")
api.Router.HandleFunc("/warehouse/read/path", api.apiWarehouseReadFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/delete", api.apiWarehouseDeleteFile).Methods("GET")
api.Router.HandleFunc("/file/read", api.apiFileRead).Methods("GET")
api.Router.HandleFunc("/file/view", api.apiFileView).Methods("GET")
for _, listen := range ListenAddresses {
go startWebAPI(listen, UseSSL, CertificateFile, CertificateKey, api.Router, "API", TimeoutRead, TimeoutWrite)
go startWebAPI(Backend, listen, UseSSL, CertificateFile, CertificateKey, api.Router, "API", TimeoutRead, TimeoutWrite)
}
return api
@@ -99,8 +99,8 @@ func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, Certifi
// startWebAPI starts a web-server with given parameters and logs the status. If may block forever and only returns if there is an error.
// The certificate file and key are only used if SSL is enabled. The read and write timeout may be 0 for no timeout.
func startWebAPI(WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) {
core.Filters.LogError("startWebAPI", "Start API at '%s'\n", WebListen)
func startWebAPI(Backend *core.Backend, WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) {
Backend.Filters.LogError("startWebAPI", "Start API at '%s'\n", WebListen)
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1
@@ -116,23 +116,23 @@ func startWebAPI(WebListen string, UseSSL bool, CertificateFile, CertificateKey
if UseSSL {
// HTTPS
if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil {
core.Filters.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
Backend.Filters.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
}
} else {
// HTTP
if err := server.ListenAndServe(); err != nil {
core.Filters.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
Backend.Filters.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
}
}
}
// EncodeJSON encodes the data as JSON
func EncodeJSON(w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
func EncodeJSON(Backend *core.Backend, w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(data)
if err != nil {
core.Filters.LogError("EncodeJSON", "Error writing data for route '%s': %v\n", r.URL.Path, err)
Backend.Filters.LogError("EncodeJSON", "Error writing data for route '%s': %v\n", r.URL.Path, err)
}
return err

View File

@@ -11,7 +11,6 @@ import (
"net/http"
"strconv"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/blockchain"
)
@@ -27,10 +26,10 @@ apiBlockchainHeaderFunc returns the current blockchain header information
Request: GET /blockchain/header
Result: 200 with JSON structure apiResponsePeerSelf
*/
func apiBlockchainHeaderFunc(w http.ResponseWriter, r *http.Request) {
publicKey, height, version := core.UserBlockchain.Header()
func (api *WebapiInstance) apiBlockchainHeaderFunc(w http.ResponseWriter, r *http.Request) {
publicKey, height, version := api.backend.UserBlockchain.Header()
EncodeJSON(w, r, apiBlockchainHeader{Version: version, Height: height, PeerID: hex.EncodeToString(publicKey.SerializeCompressed())})
EncodeJSON(api.backend, w, r, apiBlockchainHeader{Version: version, Height: height, PeerID: hex.EncodeToString(publicKey.SerializeCompressed())})
}
type apiBlockRecordRaw struct {
@@ -56,7 +55,7 @@ Do not use this function. Adding invalid data to the blockchain may corrupt it w
Request: POST /blockchain/append with JSON structure apiBlockchainBlockRaw
Response: 200 with JSON structure apiBlockchainBlockStatus
*/
func apiBlockchainAppend(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiBlockchainAppend(w http.ResponseWriter, r *http.Request) {
var input apiBlockchainBlockRaw
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -68,9 +67,9 @@ func apiBlockchainAppend(w http.ResponseWriter, r *http.Request) {
records = append(records, blockchain.BlockRecordRaw{Type: record.Type, Data: record.Data})
}
newHeight, newVersion, status := core.UserBlockchain.Append(records)
newHeight, newVersion, status := api.backend.UserBlockchain.Append(records)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
type apiBlockchainBlock struct {
@@ -89,7 +88,7 @@ apiBlockchainRead reads a block and returns the decoded information.
Request: GET /blockchain/read?block=[number]
Result: 200 with JSON structure apiBlockchainBlock
*/
func apiBlockchainRead(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiBlockchainRead(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
blockN, err := strconv.Atoi(r.Form.Get("block"))
if err != nil || blockN < 0 {
@@ -97,7 +96,7 @@ func apiBlockchainRead(w http.ResponseWriter, r *http.Request) {
return
}
block, status, _ := core.UserBlockchain.Read(uint64(blockN))
block, status, _ := api.backend.UserBlockchain.Read(uint64(blockN))
result := apiBlockchainBlock{Status: status}
if status == 0 {
@@ -119,5 +118,5 @@ func apiBlockchainRead(w http.ResponseWriter, r *http.Request) {
}
}
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}

View File

@@ -13,20 +13,19 @@ import (
"os"
"time"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/warehouse"
)
// Starts the download.
func (info *downloadInfo) Start() {
// current user?
if bytes.Equal(info.nodeID, core.SelfNodeID()) {
if bytes.Equal(info.nodeID, info.backend.SelfNodeID()) {
info.DownloadSelf()
return
}
for n := 0; n < 3 && info.peer == nil; n++ {
_, info.peer, _ = core.FindNode(info.nodeID, time.Second*5)
_, info.peer, _ = info.backend.FindNode(info.nodeID, time.Second*5)
if info.status == DownloadCanceled {
return
@@ -179,7 +178,7 @@ func (info *downloadInfo) storeDownloadData(data []byte, offset uint64) (status
func (info *downloadInfo) DownloadSelf() {
// Check if the file is available in the local warehouse.
_, fileInfo, status, _ := core.UserWarehouse.FileExists(info.hash)
_, fileInfo, status, _ := info.backend.UserWarehouse.FileExists(info.hash)
if status != warehouse.StatusOK {
info.status = DownloadCanceled
return
@@ -189,7 +188,7 @@ func (info *downloadInfo) DownloadSelf() {
info.status = DownloadActive
// read the file
status, bytesRead, _ := core.UserWarehouse.ReadFile(info.hash, 0, int64(fileInfo.Size()), info.DiskFile.Handle)
status, bytesRead, _ := info.backend.UserWarehouse.ReadFile(info.hash, 0, int64(fileInfo.Size()), info.DiskFile.Handle)
info.DiskFile.StoredSize = uint64(bytesRead)

View File

@@ -59,7 +59,7 @@ The hash parameter identifies the file to download. The node ID identifies the b
Request: GET /download/start?path=[target path on disk]&hash=[file hash to download]&node=[node ID]
Result: 200 with JSON structure apiResponseDownloadStatus
*/
func apiDownloadStart(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiDownloadStart(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
// validate hashes, must be blake3
@@ -76,11 +76,11 @@ func apiDownloadStart(w http.ResponseWriter, r *http.Request) {
return
}
info := &downloadInfo{id: uuid.New(), created: time.Now(), hash: hash, nodeID: nodeID}
info := &downloadInfo{backend: api.backend, id: uuid.New(), created: time.Now(), hash: hash, nodeID: nodeID}
// create the file immediately
if info.initDiskFile(filePath) != nil {
EncodeJSON(w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseFileInvalid})
EncodeJSON(api.backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseFileInvalid})
return
}
@@ -90,7 +90,7 @@ func apiDownloadStart(w http.ResponseWriter, r *http.Request) {
// start the download!
go info.Start()
EncodeJSON(w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseSuccess, ID: info.id, DownloadStatus: DownloadWaitMetadata})
EncodeJSON(api.backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseSuccess, ID: info.id, DownloadStatus: DownloadWaitMetadata})
}
/*
@@ -99,7 +99,7 @@ apiDownloadStatus returns the status of an active download.
Request: GET /download/status?id=[download ID]
Result: 200 with JSON structure apiResponseDownloadStatus
*/
func apiDownloadStatus(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiDownloadStatus(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id, err := uuid.Parse(r.Form.Get("id"))
if err != nil {
@@ -109,7 +109,7 @@ func apiDownloadStatus(w http.ResponseWriter, r *http.Request) {
info := downloadLookup(id)
if info == nil {
EncodeJSON(w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseIDNotFound})
EncodeJSON(api.backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseIDNotFound})
return
}
@@ -132,7 +132,7 @@ func apiDownloadStatus(w http.ResponseWriter, r *http.Request) {
info.RUnlock()
EncodeJSON(w, r, response)
EncodeJSON(api.backend, w, r, response)
}
/*
@@ -143,7 +143,7 @@ Action: 0 = Pause, 1 = Resume, 2 = Cancel.
Request: GET /download/action?id=[download ID]&action=[action]
Result: 200 with JSON structure apiResponseDownloadStatus (using APIStatus and DownloadStatus)
*/
func apiDownloadAction(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiDownloadAction(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id, err := uuid.Parse(r.Form.Get("id"))
action, err2 := strconv.Atoi(r.Form.Get("action"))
@@ -154,7 +154,7 @@ func apiDownloadAction(w http.ResponseWriter, r *http.Request) {
info := downloadLookup(id)
if info == nil {
EncodeJSON(w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseIDNotFound})
EncodeJSON(api.backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseIDNotFound})
return
}
@@ -171,7 +171,7 @@ func apiDownloadAction(w http.ResponseWriter, r *http.Request) {
apiStatus = info.Cancel()
}
EncodeJSON(w, r, apiResponseDownloadStatus{APIStatus: apiStatus, ID: info.id, DownloadStatus: info.status})
EncodeJSON(api.backend, w, r, apiResponseDownloadStatus{APIStatus: apiStatus, ID: info.id, DownloadStatus: info.status})
}
// ---- download tracking ----
@@ -203,6 +203,8 @@ type downloadInfo struct {
// live connections, to be changed
peer *core.PeerInfo
backend *core.Backend
}
var (

View File

@@ -203,7 +203,7 @@ It will primarily use the file extension for detection. If unavailable, it uses
Request: GET /file/format?path=[file path on disk]
Result: 200 with JSON structure apiResponseFileFormat
*/
func apiFileFormat(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiFileFormat(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
filePath := r.Form.Get("path")
if filePath == "" {
@@ -213,9 +213,9 @@ func apiFileFormat(w http.ResponseWriter, r *http.Request) {
fileType, fileFormat, err := FileDetectType(filePath)
if err != nil {
EncodeJSON(w, r, apiResponseFileFormat{Status: 1})
EncodeJSON(api.backend, w, r, apiResponseFileFormat{Status: 1})
return
}
EncodeJSON(w, r, apiResponseFileFormat{Status: 0, FileType: fileType, FileFormat: fileFormat})
EncodeJSON(api.backend, w, r, apiResponseFileFormat{Status: 0, FileType: fileType, FileFormat: fileFormat})
}

View File

@@ -34,7 +34,7 @@ Response: 200 with the content
404 if the file was not found or other error on transfer initiate
502 if unable to find or connect to the remote peer in time
*/
func apiFileRead(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiFileRead(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var err error
@@ -69,7 +69,7 @@ func apiFileRead(w http.ResponseWriter, r *http.Request) {
}
// Is the file available in the local warehouse? In that case requesting it from the remote is unnecessary.
if serveFileFromWarehouse(w, fileHash, uint64(offset), uint64(limit), ranges) {
if serveFileFromWarehouse(api.backend, w, fileHash, uint64(offset), uint64(limit), ranges) {
return
}
@@ -77,9 +77,9 @@ func apiFileRead(w http.ResponseWriter, r *http.Request) {
var peer *core.PeerInfo
if valid2 {
peer, err = PeerConnectNode(nodeID, timeout)
peer, err = PeerConnectNode(api.backend, nodeID, timeout)
} else if err3 == nil {
peer, err = PeerConnectPublicKey(publicKey, timeout)
peer, err = PeerConnectPublicKey(api.backend, publicKey, timeout)
}
if err != nil {
w.WriteHeader(http.StatusBadGateway)
@@ -105,9 +105,9 @@ func apiFileRead(w http.ResponseWriter, r *http.Request) {
// serveFileFromWarehouse serves the file from the warehouse. If it is not available, it returns false and does not use the writer.
// Limit is optional, 0 means the entire file.
func serveFileFromWarehouse(w http.ResponseWriter, fileHash []byte, offset, limit uint64, ranges []HTTPRange) (valid bool) {
func serveFileFromWarehouse(backend *core.Backend, w http.ResponseWriter, fileHash []byte, offset, limit uint64, ranges []HTTPRange) (valid bool) {
// Check if the file is available in the local warehouse.
_, fileInfo, status, _ := core.UserWarehouse.FileExists(fileHash)
_, fileInfo, status, _ := backend.UserWarehouse.FileExists(fileHash)
if status != warehouse.StatusOK {
return false
}
@@ -126,7 +126,7 @@ func serveFileFromWarehouse(w http.ResponseWriter, fileHash []byte, offset, limi
setContentLengthRangeHeader(w, offset, limit, uint64(fileInfo.Size()), ranges)
status, _, _ = core.UserWarehouse.ReadFile(fileHash, int64(offset), int64(limit), w)
status, _, _ = backend.UserWarehouse.ReadFile(fileHash, int64(offset), int64(limit), w)
// StatusErrorReadFile must be considered success, since parts of the file may have been transferred already and recovery is not possible.
return status == warehouse.StatusErrorReadFile || status == warehouse.StatusOK
@@ -148,7 +148,7 @@ Response: 200 with the content
404 if the file was not found or other error on transfer initiate
502 if unable to find or connect to the remote peer in time
*/
func apiFileView(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiFileView(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var err error
@@ -194,7 +194,7 @@ func apiFileView(w http.ResponseWriter, r *http.Request) {
// Is the file available in the local warehouse? In that case requesting it from the remote is unnecessary.
if !localCacheDisable {
if serveFileFromWarehouse(w, fileHash, uint64(offset), uint64(limit), ranges) {
if serveFileFromWarehouse(api.backend, w, fileHash, uint64(offset), uint64(limit), ranges) {
return
}
}
@@ -203,9 +203,9 @@ func apiFileView(w http.ResponseWriter, r *http.Request) {
var peer *core.PeerInfo
if valid2 {
peer, err = PeerConnectNode(nodeID, timeout)
peer, err = PeerConnectNode(api.backend, nodeID, timeout)
} else if err3 == nil {
peer, err = PeerConnectPublicKey(publicKey, timeout)
peer, err = PeerConnectPublicKey(api.backend, publicKey, timeout)
}
if err != nil {
w.WriteHeader(http.StatusBadGateway)
@@ -230,19 +230,19 @@ func apiFileView(w http.ResponseWriter, r *http.Request) {
}
// PeerConnectPublicKey attempts to connect to the peer specified by its public key (= peer ID).
func PeerConnectPublicKey(publicKey *btcec.PublicKey, timeout time.Duration) (peer *core.PeerInfo, err error) {
func PeerConnectPublicKey(backend *core.Backend, publicKey *btcec.PublicKey, timeout time.Duration) (peer *core.PeerInfo, err error) {
if publicKey == nil {
return nil, errors.New("invalid public key")
}
// First look up in the peer list.
if peer = core.PeerlistLookup(publicKey); peer != nil {
if peer = backend.PeerlistLookup(publicKey); peer != nil {
return peer, nil
}
// Try to connect via DHT.
nodeID := protocol.PublicKey2NodeID(publicKey)
if _, peer, _ = core.FindNode(nodeID, timeout); peer != nil {
if _, peer, _ = backend.FindNode(nodeID, timeout); peer != nil {
return peer, nil
}
@@ -251,13 +251,13 @@ func PeerConnectPublicKey(publicKey *btcec.PublicKey, timeout time.Duration) (pe
}
// PeerConnectNode tries to connect via the node ID
func PeerConnectNode(nodeID []byte, timeout time.Duration) (peer *core.PeerInfo, err error) {
func PeerConnectNode(backend *core.Backend, nodeID []byte, timeout time.Duration) (peer *core.PeerInfo, err error) {
if len(nodeID) == 256/8 {
return nil, errors.New("invalid node ID")
}
// Try to connect via DHT.
if _, peer, _ = core.FindNode(nodeID, timeout); peer != nil {
if _, peer, _ = backend.FindNode(nodeID, timeout); peer != nil {
return peer, nil
}

View File

@@ -132,7 +132,7 @@ Request: POST /blockchain/file/add with JSON structure apiBlockAddFiles
Response: 200 with JSON structure apiBlockchainBlockStatus
400 if invalid input
*/
func apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) {
var input apiBlockAddFiles
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -154,8 +154,8 @@ func apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) {
if _, err := warehouse.ValidateHash(file.Hash); err != nil {
http.Error(w, "", http.StatusBadRequest)
return
} else if _, fileInfo, status, _ := core.UserWarehouse.FileExists(file.Hash); status != warehouse.StatusOK {
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
} else if _, fileInfo, status, _ := api.backend.UserWarehouse.FileExists(file.Hash); status != warehouse.StatusOK {
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
return
} else {
file.Size = uint64(fileInfo.Size())
@@ -168,17 +168,17 @@ func apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) {
blockRecord := blockRecordFileFromAPI(file)
// Set the merkle tree info as appropriate.
if !setFileMerkleInfo(&blockRecord) {
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
if !setFileMerkleInfo(api.backend, &blockRecord) {
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
return
}
filesAdd = append(filesAdd, blockRecord)
}
newHeight, newVersion, status := core.UserBlockchain.AddFiles(filesAdd)
newHeight, newVersion, status := api.backend.UserBlockchain.AddFiles(filesAdd)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
/*
@@ -187,8 +187,8 @@ apiBlockchainFileList lists all files stored on the blockchain.
Request: GET /blockchain/file/list
Response: 200 with JSON structure apiBlockAddFiles
*/
func apiBlockchainFileList(w http.ResponseWriter, r *http.Request) {
files, status := core.UserBlockchain.ListFiles()
func (api *WebapiInstance) apiBlockchainFileList(w http.ResponseWriter, r *http.Request) {
files, status := api.backend.UserBlockchain.ListFiles()
var result apiBlockAddFiles
@@ -198,7 +198,7 @@ func apiBlockchainFileList(w http.ResponseWriter, r *http.Request) {
result.Status = status
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}
/*
@@ -208,7 +208,7 @@ It will automatically delete the file in the Warehouse if there are no other ref
Request: POST /blockchain/file/delete with JSON structure apiBlockAddFiles
Response: 200 with JSON structure apiBlockchainBlockStatus
*/
func apiBlockchainFileDelete(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiBlockchainFileDelete(w http.ResponseWriter, r *http.Request) {
var input apiBlockAddFiles
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -220,18 +220,18 @@ func apiBlockchainFileDelete(w http.ResponseWriter, r *http.Request) {
deleteIDs = append(deleteIDs, input.Files[n].ID)
}
newHeight, newVersion, deletedFiles, status := core.UserBlockchain.DeleteFiles(deleteIDs)
newHeight, newVersion, deletedFiles, status := api.backend.UserBlockchain.DeleteFiles(deleteIDs)
// If successfully deleted from the blockchain, delete from the Warehouse in case there are no other references.
if status == blockchain.StatusOK {
for n := range deletedFiles {
if files, status := core.UserBlockchain.FileExists(deletedFiles[n].Hash); status == blockchain.StatusOK && len(files) == 0 {
core.UserWarehouse.DeleteFile(deletedFiles[n].Hash)
if files, status := api.backend.UserBlockchain.FileExists(deletedFiles[n].Hash); status == blockchain.StatusOK && len(files) == 0 {
api.backend.UserWarehouse.DeleteFile(deletedFiles[n].Hash)
}
}
}
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
/*
@@ -241,7 +241,7 @@ Request: POST /blockchain/file/update with JSON structure apiBlockAddFiles
Response: 200 with JSON structure apiBlockchainBlockStatus
400 if invalid input
*/
func apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) {
var input apiBlockAddFiles
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -263,8 +263,8 @@ func apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) {
if _, err := warehouse.ValidateHash(file.Hash); err != nil {
http.Error(w, "", http.StatusBadRequest)
return
} else if _, fileInfo, status, _ := core.UserWarehouse.FileExists(file.Hash); status != warehouse.StatusOK {
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
} else if _, fileInfo, status, _ := api.backend.UserWarehouse.FileExists(file.Hash); status != warehouse.StatusOK {
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
return
} else {
file.Size = uint64(fileInfo.Size())
@@ -277,17 +277,17 @@ func apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) {
blockRecord := blockRecordFileFromAPI(file)
// Set the merkle tree info as appropriate.
if !setFileMerkleInfo(&blockRecord) {
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
if !setFileMerkleInfo(api.backend, &blockRecord) {
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse})
return
}
filesAdd = append(filesAdd, blockRecord)
}
newHeight, newVersion, status := core.UserBlockchain.ReplaceFiles(filesAdd)
newHeight, newVersion, status := api.backend.UserBlockchain.ReplaceFiles(filesAdd)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
// ---- metadata functions ----
@@ -318,14 +318,14 @@ func (file *apiFile) IsVirtualFolder() bool {
}
// setFileMerkleInfo sets the merkle fields in the BlockRecordFile
func setFileMerkleInfo(file *blockchain.BlockRecordFile) (valid bool) {
func setFileMerkleInfo(backend *core.Backend, file *blockchain.BlockRecordFile) (valid bool) {
if file.Size <= merkle.MinimumFragmentSize {
// If smaller or equal than the minimum fragment size, the merkle tree is not used.
file.MerkleRootHash = file.Hash
file.FragmentSize = merkle.MinimumFragmentSize
} else {
// Get the information from the Warehouse .merkle companion file.
tree, status, _ := core.UserWarehouse.ReadMerkleTree(file.Hash, true)
tree, status, _ := backend.UserWarehouse.ReadMerkleTree(file.Hash, true)
if status != warehouse.StatusOK {
return false
}

View File

@@ -10,7 +10,6 @@ import (
"net/http"
"strconv"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/blockchain"
)
@@ -35,15 +34,15 @@ apiProfileList lists all users profile fields.
Request: GET /profile/list
Response: 200 with JSON structure apiProfileData
*/
func apiProfileList(w http.ResponseWriter, r *http.Request) {
fields, status := core.UserBlockchain.ProfileList()
func (api *WebapiInstance) apiProfileList(w http.ResponseWriter, r *http.Request) {
fields, status := api.backend.UserBlockchain.ProfileList()
result := apiProfileData{Status: status}
for n := range fields {
result.Fields = append(result.Fields, blockRecordProfileToAPI(fields[n]))
}
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}
/*
@@ -52,7 +51,7 @@ apiProfileRead reads a specific users profile field. See core.ProfileX for recog
Request: GET /profile/read?field=[index]
Response: 200 with JSON structure apiProfileData
*/
func apiProfileRead(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiProfileRead(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fieldN, err1 := strconv.Atoi(r.Form.Get("field"))
@@ -64,11 +63,11 @@ func apiProfileRead(w http.ResponseWriter, r *http.Request) {
var result apiProfileData
var data []byte
if data, result.Status = core.UserBlockchain.ProfileReadField(uint16(fieldN)); result.Status == blockchain.StatusOK {
if data, result.Status = api.backend.UserBlockchain.ProfileReadField(uint16(fieldN)); result.Status == blockchain.StatusOK {
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: uint16(fieldN), Data: data}))
}
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}
/*
@@ -77,7 +76,7 @@ apiProfileWrite writes profile fields. See core.ProfileX for recognized fields.
Request: POST /profile/write with JSON structure apiProfileData
Response: 200 with JSON structure apiBlockchainBlockStatus
*/
func apiProfileWrite(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiProfileWrite(w http.ResponseWriter, r *http.Request) {
var input apiProfileData
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -89,9 +88,9 @@ func apiProfileWrite(w http.ResponseWriter, r *http.Request) {
fields = append(fields, blockRecordProfileFromAPI(input.Fields[n]))
}
newHeight, newVersion, status := core.UserBlockchain.ProfileWrite(fields)
newHeight, newVersion, status := api.backend.UserBlockchain.ProfileWrite(fields)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
/*
@@ -100,7 +99,7 @@ apiProfileDelete deletes profile fields identified by the types. See core.Profil
Request: POST /profile/delete with JSON structure apiProfileData
Response: 200 with JSON structure apiBlockchainBlockStatus
*/
func apiProfileDelete(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiProfileDelete(w http.ResponseWriter, r *http.Request) {
var input apiProfileData
if err := DecodeJSON(w, r, &input); err != nil {
return
@@ -112,9 +111,9 @@ func apiProfileDelete(w http.ResponseWriter, r *http.Request) {
fields = append(fields, input.Fields[n].Type)
}
newHeight, newVersion, status := core.UserBlockchain.ProfileDelete(fields)
newHeight, newVersion, status := api.backend.UserBlockchain.ProfileDelete(fields)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
EncodeJSON(api.backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
// --- conversion from core to API data ---

View File

@@ -11,7 +11,6 @@ import (
"fmt"
"time"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/blockchain"
)
@@ -20,7 +19,7 @@ func (api *WebapiInstance) dispatchSearch(input SearchRequest) (job *SearchJob)
Filter := input.ToSearchFilter()
// create the search job
job = CreateSearchJob(Timeout, input.MaxResults, Filter)
job = CreateSearchJob(api.backend, Timeout, input.MaxResults, Filter)
// todo: create actual search clients!
job.Status = SearchStatusLive
@@ -56,10 +55,10 @@ resultLoop:
}
}
if bytes.Equal(file.NodeID, core.SelfNodeID()) {
if bytes.Equal(file.NodeID, job.backend.SelfNodeID()) {
// Indicates data from the current user.
file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1))
} else if peer := core.NodelistLookup(file.NodeID); peer != nil {
} else if peer := job.backend.NodelistLookup(file.NodeID); peer != nil {
// add the tags 'Shared By Count' and 'Shared By GeoIP'
file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1))
if latitude, longitude, valid := api.Peer2GeoIP(peer); valid {

View File

@@ -11,6 +11,7 @@ import (
"sync"
"time"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/blockchain"
"github.com/google/uuid"
)
@@ -29,6 +30,8 @@ type SearchFilter struct {
// SearchJob is a collection of search jobs
type SearchJob struct {
backend *core.Backend
// input settings
id uuid.UUID // The job id
timeout time.Duration // timeout set for all searches
@@ -79,8 +82,8 @@ const (
// CreateSearchJob creates a new search job and adds it to the lookup list.
// Timeout and MaxResults must be set and must not be 0.
func CreateSearchJob(Timeout time.Duration, MaxResults int, Filter SearchFilter) (job *SearchJob) {
job = &SearchJob{}
func CreateSearchJob(Backend *core.Backend, Timeout time.Duration, MaxResults int, Filter SearchFilter) (job *SearchJob) {
job = &SearchJob{backend: Backend}
job.Status = SearchStatusNotStarted
job.id = uuid.New()
job.timeout = Timeout

View File

@@ -106,7 +106,7 @@ func (api *WebapiInstance) apiSearch(w http.ResponseWriter, r *http.Request) {
job := api.dispatchSearch(input)
EncodeJSON(w, r, SearchRequestResponse{Status: 0, ID: job.id})
EncodeJSON(api.backend, w, r, SearchRequestResponse{Status: 0, ID: job.id})
}
/*
@@ -124,7 +124,7 @@ Optional parameters:
&sort=[sort order]
Result: 200 with JSON structure SearchResult. Check the field status.
*/
func apiSearchResult(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiSearchResult(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil {
@@ -139,7 +139,7 @@ func apiSearchResult(w http.ResponseWriter, r *http.Request) {
// find the job ID
job := JobLookup(jobID)
if job == nil {
EncodeJSON(w, r, SearchResult{Status: 2})
EncodeJSON(api.backend, w, r, SearchResult{Status: 2})
return
}
@@ -194,7 +194,7 @@ func apiSearchResult(w http.ResponseWriter, r *http.Request) {
result.Statistic = job.Statistics()
}
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}
/*
@@ -204,7 +204,7 @@ Request: GET /search/result/ws?id=[UUID]&limit=[optional max records]
Result: If successful, upgrades to a websocket and sends JSON structure SearchResult messages.
Limit is optional. Not used if ommitted or 0.
*/
func apiSearchResultStream(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiSearchResultStream(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil {
@@ -217,7 +217,7 @@ func apiSearchResultStream(w http.ResponseWriter, r *http.Request) {
// look up the job
job := JobLookup(jobID)
if job == nil {
EncodeJSON(w, r, SearchResult{Status: 2})
EncodeJSON(api.backend, w, r, SearchResult{Status: 2})
return
}
@@ -289,7 +289,7 @@ Response: 204 Empty
400 Invalid input
404 ID not found
*/
func apiSearchTerminate(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiSearchTerminate(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id"))
@@ -318,7 +318,7 @@ apiSearchStatistic returns search result statistics. Statistics are always calcu
Request: GET /search/result?id=[UUID]
Result: 200 with JSON structure SearchStatistic. Check the field status (0 = Success, 2 = ID not found).
*/
func apiSearchStatistic(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiSearchStatistic(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil {
@@ -329,13 +329,13 @@ func apiSearchStatistic(w http.ResponseWriter, r *http.Request) {
// find the job ID
job := JobLookup(jobID)
if job == nil {
EncodeJSON(w, r, SearchStatistic{Status: 2})
EncodeJSON(api.backend, w, r, SearchStatistic{Status: 2})
return
}
stats := job.Statistics()
EncodeJSON(w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()})
EncodeJSON(api.backend, w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()})
}
/*
@@ -372,7 +372,7 @@ func (api *WebapiInstance) apiExplore(w http.ResponseWriter, r *http.Request) {
result.Status = 1 // No more results to expect
EncodeJSON(w, r, result)
EncodeJSON(api.backend, w, r, result)
}
func (input *SearchRequest) Parse() (Timeout time.Duration) {

View File

@@ -19,7 +19,7 @@ func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int
}
// Use the peer list to know about active peers. Random order!
peerList := core.PeerlistGet()
peerList := api.backend.PeerlistGet()
// Files from peers exceeding the limit. It is used if from all peers the total limit is not reached.
var filesSeconday []blockchain.BlockRecordFile

View File

@@ -10,8 +10,6 @@ import (
"encoding/hex"
"net/http"
"strconv"
"github.com/PeernetOfficial/core"
)
func apiTest(w http.ResponseWriter, r *http.Request) {
@@ -33,8 +31,8 @@ apiStatus returns the current connectivity status to the network
Request: GET /status
Result: 200 with JSON structure Status
*/
func apiStatus(w http.ResponseWriter, r *http.Request) {
status := apiResponseStatus{Status: 0, CountPeerList: core.PeerlistCount()}
func (api *WebapiInstance) apiStatus(w http.ResponseWriter, r *http.Request) {
status := apiResponseStatus{Status: 0, CountPeerList: api.backend.PeerlistCount()}
status.CountNetwork = status.CountPeerList // For now always same as CountPeerList, until native Statistics message to root peers is available.
// Connected: If at leat 2 peers.
@@ -42,7 +40,7 @@ func apiStatus(w http.ResponseWriter, r *http.Request) {
// Instead, the core should keep a count of "active peers".
status.IsConnected = status.CountPeerList >= 2
EncodeJSON(w, r, status)
EncodeJSON(api.backend, w, r, status)
}
type apiResponsePeerSelf struct {
@@ -55,14 +53,14 @@ apiAccountInfo provides information about the current account.
Request: GET /account/info
Result: 200 with JSON structure apiResponsePeerSelf
*/
func apiAccountInfo(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiAccountInfo(w http.ResponseWriter, r *http.Request) {
response := apiResponsePeerSelf{}
response.NodeID = hex.EncodeToString(core.SelfNodeID())
response.NodeID = hex.EncodeToString(api.backend.SelfNodeID())
_, publicKey := core.ExportPrivateKey()
_, publicKey := api.backend.ExportPrivateKey()
response.PeerID = hex.EncodeToString(publicKey.SerializeCompressed())
EncodeJSON(w, r, response)
EncodeJSON(api.backend, w, r, response)
}
/*
@@ -71,14 +69,14 @@ Request: GET /account/delete?confirm=[0 or 1]
Result: 204 if the user choses not to delete the account
200 if successfully deleted
*/
func apiAccountDelete(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiAccountDelete(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
if confirm, _ := strconv.ParseBool(r.Form.Get("confirm")); !confirm {
w.WriteHeader(http.StatusNoContent)
return
}
core.DeleteAccount()
api.backend.DeleteAccount()
w.WriteHeader(http.StatusOK)
}

View File

@@ -10,7 +10,6 @@ import (
"net/http"
"strconv"
"github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/warehouse"
)
@@ -26,14 +25,14 @@ apiWarehouseCreateFile creates a file in the warehouse.
Request: POST /warehouse/create with raw data to create as new file
Response: 200 with JSON structure WarehouseResult
*/
func apiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) {
hash, status, err := core.UserWarehouse.CreateFile(r.Body, 0)
func (api *WebapiInstance) apiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) {
hash, status, err := api.backend.UserWarehouse.CreateFile(r.Body, 0)
if err != nil {
core.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
api.backend.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
}
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
EncodeJSON(api.backend, w, r, WarehouseResult{Status: status, Hash: hash})
}
/*
@@ -44,7 +43,7 @@ In the future the API should be secured using a random API key and setting the C
Request: GET /warehouse/create/path?path=[target path on disk]
Response: 200 with JSON structure WarehouseResult
*/
func apiWarehouseCreateFilePath(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiWarehouseCreateFilePath(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
filePath := r.Form.Get("path")
if filePath == "" {
@@ -52,13 +51,13 @@ func apiWarehouseCreateFilePath(w http.ResponseWriter, r *http.Request) {
return
}
hash, status, err := core.UserWarehouse.CreateFileFromPath(filePath)
hash, status, err := api.backend.UserWarehouse.CreateFileFromPath(filePath)
if err != nil {
core.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
api.backend.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
}
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
EncodeJSON(api.backend, w, r, WarehouseResult{Status: status, Hash: hash})
}
/*
@@ -70,7 +69,7 @@ Response: 200 with the raw file data
404 if file was not found
500 in case of internal error opening the file
*/
func apiWarehouseReadFile(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiWarehouseReadFile(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
hash, valid1 := DecodeBlake3Hash(r.Form.Get("hash"))
if !valid1 {
@@ -81,7 +80,7 @@ func apiWarehouseReadFile(w http.ResponseWriter, r *http.Request) {
offset, _ := strconv.Atoi(r.Form.Get("offset"))
limit, _ := strconv.Atoi(r.Form.Get("limit"))
status, bytesRead, err := core.UserWarehouse.ReadFile(hash, int64(offset), int64(limit), w)
status, bytesRead, err := api.backend.UserWarehouse.ReadFile(hash, int64(offset), int64(limit), w)
switch status {
case warehouse.StatusFileNotFound:
@@ -95,7 +94,7 @@ func apiWarehouseReadFile(w http.ResponseWriter, r *http.Request) {
}
if err != nil {
core.Filters.LogError("warehouse.ReadFile", "status %d read %d error: %v", status, bytesRead, err)
api.backend.Filters.LogError("warehouse.ReadFile", "status %d read %d error: %v", status, bytesRead, err)
}
}
@@ -105,7 +104,7 @@ apiWarehouseDeleteFile deletes a file in the warehouse.
Request: GET /warehouse/delete?hash=[hash]
Response: 200 with JSON structure WarehouseResult
*/
func apiWarehouseDeleteFile(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiWarehouseDeleteFile(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
hash, valid1 := DecodeBlake3Hash(r.Form.Get("hash"))
if !valid1 {
@@ -113,13 +112,13 @@ func apiWarehouseDeleteFile(w http.ResponseWriter, r *http.Request) {
return
}
status, err := core.UserWarehouse.DeleteFile(hash)
status, err := api.backend.UserWarehouse.DeleteFile(hash)
if err != nil {
core.Filters.LogError("warehouse.DeleteFile", "status %d error: %v", status, err)
api.backend.Filters.LogError("warehouse.DeleteFile", "status %d error: %v", status, err)
}
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
EncodeJSON(api.backend, w, r, WarehouseResult{Status: status, Hash: hash})
}
/*
@@ -130,7 +129,7 @@ Request: GET /warehouse/read/path?hash=[hash]&path=[target path on disk]
Optional parameters &offset=[file offset]&limit=[read limit in bytes]
Response: 200 with JSON structure WarehouseResult
*/
func apiWarehouseReadFilePath(w http.ResponseWriter, r *http.Request) {
func (api *WebapiInstance) apiWarehouseReadFilePath(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
hash, valid1 := DecodeBlake3Hash(r.Form.Get("hash"))
if !valid1 {
@@ -142,11 +141,11 @@ func apiWarehouseReadFilePath(w http.ResponseWriter, r *http.Request) {
offset, _ := strconv.Atoi(r.Form.Get("offset"))
limit, _ := strconv.Atoi(r.Form.Get("limit"))
status, bytesRead, err := core.UserWarehouse.ReadFileToDisk(hash, int64(offset), int64(limit), targetFile)
status, bytesRead, err := api.backend.UserWarehouse.ReadFileToDisk(hash, int64(offset), int64(limit), targetFile)
if err != nil {
core.Filters.LogError("warehouse.ReadFileToDisk", "status %d read %d error: %v", status, bytesRead, err)
api.backend.Filters.LogError("warehouse.ReadFileToDisk", "status %d read %d error: %v", status, bytesRead, err)
}
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
EncodeJSON(api.backend, w, r, WarehouseResult{Status: status, Hash: hash})
}