// TestPongCrossProtocol tests the MsgPong API when encoding with the latest // protocol version and decoding with BIP0031Version. func TestPongCrossProtocol(t *testing.T) { nonce, err := wire.RandomUint64() if err != nil { t.Errorf("Error generating nonce: %v", err) } msg := wire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("Should get same nonce back out.") } // Encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, wire.ProtocolVersion) if err != nil { t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) } // Decode with old protocol version. readmsg := wire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, wire.BIP0031Version) if err == nil { t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", msg) } // Since one of the protocol versions doesn't support the pong message, // make sure the nonce didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Error("Should not get same nonce for cross protocol") } }
// TestRandomUint64 exercises the randomness of the random number generator on // the system by ensuring the probability of the generated numbers. If the RNG // is evenly distributed as a proper cryptographic RNG should be, there really // should only be 1 number < 2^56 in 2^8 tries for a 64-bit number. However, // use a higher number of 5 to really ensure the test doesn't fail unless the // RNG is just horrendous. func TestRandomUint64(t *testing.T) { tries := 1 << 8 // 2^8 watermark := uint64(1 << 56) // 2^56 maxHits := 5 badRNG := "The random number generator on this system is clearly " + "terrible since we got %d values less than %d in %d runs " + "when only %d was expected" numHits := 0 for i := 0; i < tries; i++ { nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64 iteration %d failed - err %v", i, err) return } if nonce < watermark { numHits++ } if numHits > maxHits { str := fmt.Sprintf(badRNG, numHits, watermark, tries, maxHits) t.Errorf("Random Uint64 iteration %d failed - %v %v", i, str, numHits) return } } }
// TestBlockHeader tests the BlockHeader API. func TestBlockHeader(t *testing.T) { nonce64, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } nonce := uint32(nonce64) hash := mainNetGenesisHash merkleHash := mainNetGenesisMerkleRoot bits := uint32(0x1d00ffff) bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce) // Ensure we get the same data back out. if !bh.PrevBlock.IsEqual(&hash) { t.Errorf("NewBlockHeader: wrong prev hash - got %v, want %v", spew.Sprint(bh.PrevBlock), spew.Sprint(hash)) } if !bh.MerkleRoot.IsEqual(&merkleHash) { t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v", spew.Sprint(bh.MerkleRoot), spew.Sprint(merkleHash)) } if bh.Bits != bits { t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", bh.Bits, bits) } if bh.Nonce != nonce { t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v", bh.Nonce, nonce) } }
// TestPing tests the MsgPing API against the latest protocol version. func TestPing(t *testing.T) { pver := wire.ProtocolVersion // Ensure we get the same nonce back out. nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := wire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure the command is expected value. wantCmd := "ping" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgPing: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. wantPayload := uint32(8) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } return }
// TestPingCrossProtocol tests the MsgPing API when encoding with the latest // protocol version and decoding with BIP0031Version. func TestPingCrossProtocol(t *testing.T) { nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := wire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, wire.ProtocolVersion) if err != nil { t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) } // Decode with old protocol version. readmsg := wire.NewMsgPing(0) err = readmsg.BtcDecode(&buf, wire.BIP0031Version) if err != nil { t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) } // Since one of the protocol versions doesn't support the nonce, make // sure it didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Error("Should not get same nonce for cross protocol") } }
// TestPongLatest tests the MsgPong API against the latest protocol version. func TestPongLatest(t *testing.T) { pver := wire.ProtocolVersion nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } msg := wire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPong: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure the command is expected value. wantCmd := "pong" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgPong: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. wantPayload := uint32(8) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Test encode with latest protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err != nil { t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) } // Test decode with latest protocol version. readmsg := wire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, pver) if err != nil { t.Errorf("decode of MsgPong failed [%v] err <%v>", buf, err) } // Ensure nonce is the same. if msg.Nonce != readmsg.Nonce { t.Errorf("Should get same nonce for protocol version %d", pver) } return }
// TestPingBIP0031 tests the MsgPing API against the protocol version // BIP0031Version. func TestPingBIP0031(t *testing.T) { // Use the protocol version just prior to BIP0031Version changes. pver := wire.BIP0031Version nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: Error generating nonce: %v", err) } msg := wire.NewMsgPing(nonce) if msg.Nonce != nonce { t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", msg.Nonce, nonce) } // Ensure max payload is expected value for old protocol version. wantPayload := uint32(0) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Test encode with old protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err != nil { t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) } // Test decode with old protocol version. readmsg := wire.NewMsgPing(0) err = readmsg.BtcDecode(&buf, pver) if err != nil { t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) } // Since this protocol version doesn't support the nonce, make sure // it didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Errorf("Should not get same nonce for protocol version %d", pver) } return }
// TestPongBIP0031 tests the MsgPong API against the protocol version // BIP0031Version. func TestPongBIP0031(t *testing.T) { // Use the protocol version just prior to BIP0031Version changes. pver := wire.BIP0031Version nonce, err := wire.RandomUint64() if err != nil { t.Errorf("Error generating nonce: %v", err) } msg := wire.NewMsgPong(nonce) if msg.Nonce != nonce { t.Errorf("Should get same nonce back out.") } // Ensure max payload is expected value for old protocol version. size := msg.MaxPayloadLength(pver) if size != 0 { t.Errorf("Max length should be 0 for pong protocol version %d.", pver) } // Test encode with old protocol version. var buf bytes.Buffer err = msg.BtcEncode(&buf, pver) if err == nil { t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", msg) } // Test decode with old protocol version. readmsg := wire.NewMsgPong(0) err = readmsg.BtcDecode(&buf, pver) if err == nil { t.Errorf("decode of MsgPong succeeded when it shouldn't have %v", spew.Sdump(buf)) } // Since this protocol version doesn't support pong, make sure the // nonce didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Errorf("Should not get same nonce for protocol version %d", pver) } return }
// TestVersion tests the MsgVersion API. func TestVersion(t *testing.T) { pver := wire.ProtocolVersion // Create version message data. lastBlock := int32(234234) tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 6682} me, err := wire.NewNetAddress(tcpAddrMe, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 6682} you, err := wire.NewNetAddress(tcpAddrYou, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } nonce, err := wire.RandomUint64() if err != nil { t.Errorf("RandomUint64: error generating nonce: %v", err) } // Ensure we get the correct data back out. msg := wire.NewMsgVersion(me, you, nonce, lastBlock) if msg.ProtocolVersion != int32(pver) { t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v", msg.ProtocolVersion, pver) } if !reflect.DeepEqual(&msg.AddrMe, me) { t.Errorf("NewMsgVersion: wrong me address - got %v, want %v", spew.Sdump(&msg.AddrMe), spew.Sdump(me)) } if !reflect.DeepEqual(&msg.AddrYou, you) { t.Errorf("NewMsgVersion: wrong you address - got %v, want %v", spew.Sdump(&msg.AddrYou), spew.Sdump(you)) } if msg.Nonce != nonce { t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v", msg.Nonce, nonce) } if msg.UserAgent != wire.DefaultUserAgent { t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", msg.UserAgent, wire.DefaultUserAgent) } if msg.LastBlock != lastBlock { t.Errorf("NewMsgVersion: wrong last block - got %v, want %v", msg.LastBlock, lastBlock) } if msg.DisableRelayTx != false { t.Errorf("NewMsgVersion: disable relay tx is not false by "+ "default - got %v, want %v", msg.DisableRelayTx, false) } msg.AddUserAgent("myclient", "1.2.3", "optional", "comments") customUserAgent := wire.DefaultUserAgent + "myclient:1.2.3(optional; comments)/" if msg.UserAgent != customUserAgent { t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", msg.UserAgent, customUserAgent) } msg.AddUserAgent("mygui", "3.4.5") customUserAgent += "mygui:3.4.5/" if msg.UserAgent != customUserAgent { t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", msg.UserAgent, customUserAgent) } // accounting for ":", "/" err = msg.AddUserAgent(strings.Repeat("t", wire.MaxUserAgentLen-len(customUserAgent)-2+1), "") if _, ok := err.(*wire.MessageError); !ok { t.Errorf("AddUserAgent: expected error not received "+ "- got %v, want %T", err, wire.MessageError{}) } // Version message should not have any services set by default. if msg.Services != 0 { t.Errorf("NewMsgVersion: wrong default services - got %v, want %v", msg.Services, 0) } if msg.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service is set") } // Ensure the command is expected value. wantCmd := "version" if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgVersion: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value. // Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes + // remote and local net addresses + nonce 8 bytes + length of user agent // (varInt) + max allowed user agent length + last block 4 bytes + // relay transactions flag 1 byte. wantPayload := uint32(2102) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Ensure adding the full service node flag works. msg.AddService(wire.SFNodeNetwork) if msg.Services != wire.SFNodeNetwork { t.Errorf("AddService: wrong services - got %v, want %v", msg.Services, wire.SFNodeNetwork) } if !msg.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service not set") } // Use a fake connection. conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != nil { t.Errorf("NewMsgVersionFromConn: %v", err) } // Ensure we get the correct connection data back out. if !msg.AddrMe.IP.Equal(tcpAddrMe.IP) { t.Errorf("NewMsgVersionFromConn: wrong me ip - got %v, want %v", msg.AddrMe.IP, tcpAddrMe.IP) } if !msg.AddrYou.IP.Equal(tcpAddrYou.IP) { t.Errorf("NewMsgVersionFromConn: wrong you ip - got %v, want %v", msg.AddrYou.IP, tcpAddrYou.IP) } // Use a fake connection with local UDP addresses to force a failure. conn = &fakeConn{ localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 6682}, remoteAddr: tcpAddrYou, } msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != wire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, wire.ErrInvalidNetAddr) } // Use a fake connection with remote UDP addresses to force a failure. conn = &fakeConn{ localAddr: tcpAddrMe, remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 6682}, } msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) if err != wire.ErrInvalidNetAddr { t.Errorf("NewMsgVersionFromConn: expected error not received "+ "- got %v, want %v", err, wire.ErrInvalidNetAddr) } return }
// solveBlock attempts to find some combination of a nonce, extra nonce, and // current timestamp which makes the passed block hash to a value less than the // target difficulty. The timestamp is updated periodically and the passed // block is modified with all tweaks during this process. This means that // when the function returns true, the block is ready for submission. // // This function will return early with false when conditions that trigger a // stale block such as a new block showing up or periodically when there are // new transactions and enough time has elapsed without finding a solution. func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, ticker *time.Ticker, quit chan struct{}) bool { // Choose a random extra nonce offset for this block template and // worker. enOffset, err := wire.RandomUint64() if err != nil { minrLog.Errorf("Unexpected error while generating random "+ "extra nonce offset: %v", err) enOffset = 0 } // Create a couple of convenience variables. header := &msgBlock.Header targetDifficulty := blockchain.CompactToBig(header.Bits) // Initial state. lastGenerated := time.Now() lastTxUpdate := m.txSource.LastUpdated() hashesCompleted := uint64(0) // Note that the entire extra nonce range is iterated and the offset is // added relying on the fact that overflow will wrap around 0 as // provided by the Go spec. for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { // Update the extra nonce in the block template with the // new value by regenerating the coinbase script and // setting the merkle root to the new value. The UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) // Search through the entire nonce range for a solution while // periodically checking for early quit and stale block // conditions along with updates to the speed monitor. for i := uint32(0); i <= maxNonce; i++ { select { case <-quit: return false case <-ticker.C: m.updateHashes <- hashesCompleted hashesCompleted = 0 // The current block is stale if the best block // has changed. bestHash, _ := m.server.blockManager.chainState.Best() if !header.PrevBlock.IsEqual(bestHash) { return false } // The current block is stale if the memory pool // has been updated since the block template was // generated and it has been at least one // minute. if lastTxUpdate != m.txSource.LastUpdated() && time.Now().After(lastGenerated.Add(time.Minute)) { return false } UpdateBlockTime(msgBlock, m.server.blockManager) default: // Non-blocking select to fall through } // Update the nonce and hash the block header. Each // hash is actually a double sha256 (two hashes), so // increment the number of hashes completed for each // attempt accordingly. header.Nonce = i hash := header.BlockSha() hashesCompleted += 2 // The block is solved when the new block hash is less // than the target difficulty. Yay! if blockchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 { m.updateHashes <- hashesCompleted return true } } } return false }