github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/ver.go (about)

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/piotrnar/gocoin/client/common"
    13  	"github.com/piotrnar/gocoin/lib/btc"
    14  	"github.com/piotrnar/gocoin/lib/others/sys"
    15  	"github.com/piotrnar/gocoin/lib/secp256k1"
    16  )
    17  
    18  var IgnoreExternalIpFrom = []string{}
    19  
    20  func (c *OneConnection) SendVersion() {
    21  	b := bytes.NewBuffer([]byte{})
    22  
    23  	binary.Write(b, binary.LittleEndian, uint32(common.Version))
    24  	binary.Write(b, binary.LittleEndian, uint64(common.Services))
    25  	binary.Write(b, binary.LittleEndian, uint64(time.Now().Unix()))
    26  
    27  	b.Write(c.PeerAddr.NetAddr.Bytes())
    28  	if ExternalAddrLen() > 0 {
    29  		b.Write(BestExternalAddr())
    30  	} else {
    31  		b.Write(bytes.Repeat([]byte{0}, 26))
    32  	}
    33  
    34  	b.Write(nonce[:])
    35  
    36  	common.LockCfg()
    37  	btc.WriteVlen(b, uint64(len(common.UserAgent)))
    38  	b.Write([]byte(common.UserAgent))
    39  	common.UnlockCfg()
    40  
    41  	binary.Write(b, binary.LittleEndian, uint32(common.Last.BlockHeight()))
    42  	if !common.GetBool(&common.CFG.TXPool.Enabled) {
    43  		b.WriteByte(0) // don't notify me about txs
    44  	}
    45  
    46  	c.SendRawMsg("version", b.Bytes())
    47  }
    48  
    49  func (c *OneConnection) IsGocoin() bool {
    50  	return strings.HasPrefix(c.Node.Agent, "/Gocoin:")
    51  }
    52  
    53  func (c *OneConnection) HandleVersion(pl []byte) error {
    54  	if len(pl) < 80 /*Up to, includiong, the nonce */ {
    55  		return errors.New("MsgTooShort")
    56  	}
    57  
    58  	c.Mutex.Lock()
    59  	c.Node.Version = binary.LittleEndian.Uint32(pl[0:4])
    60  	c.Node.Services = binary.LittleEndian.Uint64(pl[4:12])
    61  	c.PeerAddr.Services = c.Node.Services
    62  	copy(c.Node.Nonce[:], pl[72:80])
    63  	c.Node.Timestamp = binary.LittleEndian.Uint64(pl[12:20])
    64  	c.Node.ReportedIp4 = binary.BigEndian.Uint32(pl[40:44])
    65  
    66  	use_this_ip := sys.ValidIp4(pl[40:44])
    67  
    68  	if len(pl) >= 82 {
    69  		le, of := btc.VLen(pl[80:])
    70  		if of == 0 || len(pl) < 80+le {
    71  			c.Mutex.Unlock()
    72  			return errors.New("MsgCorrupt")
    73  		}
    74  		of += 80
    75  		c.Node.Agent = string(pl[of : of+le])
    76  		of += le
    77  		if len(pl) >= of+4 {
    78  			c.Node.Height = binary.LittleEndian.Uint32(pl[of : of+4])
    79  			c.X.GetBlocksDataNow = true
    80  			of += 4
    81  			if len(pl) > of && pl[of] == 0 {
    82  				c.Node.DoNotRelayTxs = true
    83  			}
    84  		}
    85  		c.X.IsGocoin = c.IsGocoin()
    86  	}
    87  	c.PeerAddr.NodeAgent = c.Node.Agent
    88  	c.X.VersionReceived = true
    89  	c.Mutex.Unlock()
    90  
    91  	if !c.X.IsSpecial {
    92  		FriendsAccess.Lock()
    93  		for _, ua := range SpecialAgents {
    94  			if strings.HasPrefix(c.Node.Agent, ua) {
    95  				c.X.IsSpecial = true
    96  				goto special
    97  			}
    98  		}
    99  		for _, ip := range SpecialIPs {
   100  			if c.PeerAddr.Ip4 == ip {
   101  				c.X.IsSpecial = true
   102  				goto special
   103  			}
   104  		}
   105  	special:
   106  		FriendsAccess.Unlock()
   107  	}
   108  
   109  	if !c.X.IsSpecial {
   110  		if c.Node.Version < MIN_PROTO_VERSION {
   111  			return errors.New("TooLow")
   112  		}
   113  		if (c.Node.Services & btc.SERVICE_SEGWIT) == 0 {
   114  			return errors.New("NoSegwit")
   115  		}
   116  		if !c.HasNetworkService() {
   117  			return errors.New("NoService")
   118  		}
   119  		if c.Node.Nonce == [8]byte{0, 0, 0, 0, 0, 0, 0, 0} {
   120  			return errors.New("NullNonce")
   121  		}
   122  		if c.Node.Nonce == nonce {
   123  			return errors.New("OurNonce")
   124  		}
   125  	}
   126  
   127  	// check if we don't have this nonce yet
   128  	Mutex_net.Lock()
   129  	for _, v := range OpenCons {
   130  		if v != c {
   131  			v.Mutex.Lock()
   132  			yes := v.X.VersionReceived && v.Node.Nonce == c.Node.Nonce
   133  			v.Mutex.Unlock()
   134  			if yes {
   135  				Mutex_net.Unlock()
   136  				return errors.New("SameNonce")
   137  			}
   138  		}
   139  	}
   140  	Mutex_net.Unlock()
   141  
   142  	if use_this_ip {
   143  		if bytes.Equal(pl[40:44], c.PeerAddr.Ip4[:]) {
   144  			if common.FLAG.Log {
   145  				ExternalIpMutex.Lock()
   146  				f, _ := os.OpenFile("badip_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
   147  				if f != nil {
   148  					fmt.Fprintf(f, "%s: OWN IP from %s @ %s - %d\n",
   149  						time.Now().Format("2006-01-02 15:04:05"),
   150  						c.Node.Agent, c.PeerAddr.Ip(), c.ConnID)
   151  					f.Close()
   152  				}
   153  				ExternalIpMutex.Unlock()
   154  			}
   155  			common.CountSafe("IgnoreExtIP-O")
   156  			use_this_ip = false
   157  		} else if len(pl) >= 86 && binary.BigEndian.Uint32(pl[66:70]) != 0 &&
   158  			!bytes.Equal(pl[66:70], c.PeerAddr.Ip4[:]) {
   159  			if common.FLAG.Log {
   160  				ExternalIpMutex.Lock()
   161  				f, _ := os.OpenFile("badip_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
   162  				if f != nil {
   163  					fmt.Fprintf(f, "%s: BAD IP=%d.%d.%d.%d from %s @ %s - %d\n",
   164  						time.Now().Format("2006-01-02 15:04:05"),
   165  						pl[66], pl[67], pl[68], pl[69], c.Node.Agent, c.PeerAddr.Ip(), c.ConnID)
   166  					f.Close()
   167  				}
   168  				ExternalIpMutex.Unlock()
   169  			}
   170  			common.CountSafe("IgnoreExtIP-B")
   171  			use_this_ip = false
   172  		}
   173  	}
   174  
   175  	if use_this_ip {
   176  		ExternalIpMutex.Lock()
   177  		if _, known := ExternalIp4[c.Node.ReportedIp4]; !known { // New IP
   178  			use_this_ip = true
   179  			for x, v := range IgnoreExternalIpFrom {
   180  				if c.Node.Agent == v {
   181  					use_this_ip = false
   182  					common.CountSafe(fmt.Sprint("IgnoreExtIP", x))
   183  					break
   184  				}
   185  			}
   186  			if use_this_ip && common.IsListenTCP() && common.GetExternalIp() == "" {
   187  				fmt.Printf("New external IP %d.%d.%d.%d from ConnID=%d (%s)\n> ",
   188  					pl[40], pl[41], pl[42], pl[43], c.ConnID, c.Node.Agent)
   189  			}
   190  		}
   191  		if use_this_ip {
   192  			ExternalIp4[c.Node.ReportedIp4] = [2]uint{ExternalIp4[c.Node.ReportedIp4][0] + 1,
   193  				uint(time.Now().Unix())}
   194  		}
   195  		ExternalIpMutex.Unlock()
   196  	}
   197  
   198  	c.SendRawMsg("verack", []byte{})
   199  	return nil
   200  }
   201  
   202  // SendAuth sends auth messages (only used by other gocoin nodes).
   203  func (c *OneConnection) SendAuth() {
   204  	rnd := make([]byte, 32)
   205  	copy(rnd, c.Node.Nonce[:])
   206  	r, s, er := btc.EcdsaSign(common.SecretKey, rnd)
   207  	if er != nil {
   208  		println(er.Error())
   209  		return
   210  	}
   211  	var sig secp256k1.Signature
   212  	sig.R.Set(r)
   213  	sig.S.Set(s)
   214  
   215  	msg := bytes.NewBuffer(sig.Bytes())
   216  	// add last block hash and last block height
   217  	common.Last.Mutex.Lock()
   218  	msg.Write(common.Last.Block.BlockHash.Hash[:])
   219  	binary.Write(msg, binary.LittleEndian, uint32(common.Last.Block.Height))
   220  	common.Last.Mutex.Unlock()
   221  	c.SendRawMsg("auth", msg.Bytes())
   222  }
   223  
   224  // AuthRvcd processes auth messages (from other gocoin nodes).
   225  func (c *OneConnection) AuthRvcd(pl []byte) {
   226  	if c.X.AuthMsgGot > 0 {
   227  		c.DoS("AuthMsgCnt") // Only allow one auth message per connection (DoS prevention)
   228  		return
   229  	}
   230  	c.X.AuthMsgGot++
   231  
   232  	c.X.Authorized = false
   233  
   234  	var sig secp256k1.Signature
   235  	var pkey secp256k1.XY
   236  	var m secp256k1.Number
   237  	var b32 [32]byte
   238  
   239  	sig_len := sig.ParseBytes(pl)
   240  	if sig_len < 0 {
   241  		return
   242  	}
   243  
   244  	copy(b32[:8], nonce[:]) // the remaining bytes shall be zero'ed
   245  	m.SetBytes(b32[:])
   246  
   247  	FriendsAccess.Lock()
   248  	for _, pub := range AuthPubkeys {
   249  		if pkey.ParsePubkey(pub) && sig.Verify(&pkey, &m) {
   250  			c.X.Authorized = true
   251  			break
   252  		}
   253  	}
   254  	FriendsAccess.Unlock()
   255  	if !c.X.Authorized {
   256  		return
   257  	}
   258  
   259  	// Authorized node - check for last block data fields
   260  	if len(pl) >= sig_len+32+4 {
   261  		bl_height := binary.LittleEndian.Uint32(pl[sig_len+32 : sig_len+36])
   262  		common.Last.Mutex.Lock()
   263  		c.X.ChainSynchronized = bl_height >= uint32(common.Last.Block.Height)
   264  		common.Last.Mutex.Unlock()
   265  
   266  		if c.X.ChainSynchronized {
   267  			copy(b32[:], pl[sig_len:sig_len+32])
   268  			common.LockCfg()
   269  			common.ApplyLTB(btc.NewUint256(b32[:]), bl_height)
   270  			common.UnlockCfg()
   271  		}
   272  	}
   273  	var repl [1]byte // return whether (we think that) we are synchronized
   274  	if common.GetBool(&common.BlockChainSynchronized) {
   275  		repl[0] = 1
   276  	}
   277  	c.SendRawMsg("authack", repl[:])
   278  }
   279  
   280  func (c *OneConnection) HasNetworkService() bool {
   281  	return (c.Node.Services & (btc.SERVICE_NETWORK | btc.SERVICE_NETWORK_LIMITED)) != 0
   282  }