diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp index 9a11ca56c..ed198211e 100644 --- a/src/crypto/sha256_avx2.cpp +++ b/src/crypto/sha256_avx2.cpp @@ -1,454 +1,454 @@ #ifdef ENABLE_AVX2 +#include #include -#include #include "crypto/common.h" #include "crypto/sha256.h" namespace sha256d64_avx2 { namespace { __m256i inline K(uint32_t x) { return _mm256_set1_epi32(x); } __m256i inline Add(__m256i x, __m256i y) { return _mm256_add_epi32(x, y); } __m256i inline Add(__m256i x, __m256i y, __m256i z) { return Add(Add(x, y), z); } __m256i inline Add(__m256i x, __m256i y, __m256i z, __m256i w) { return Add(Add(x, y), Add(z, w)); } __m256i inline Add(__m256i x, __m256i y, __m256i z, __m256i w, __m256i v) { return Add(Add(x, y, z), Add(w, v)); } __m256i inline Inc(__m256i &x, __m256i y) { x = Add(x, y); return x; } __m256i inline Inc(__m256i &x, __m256i y, __m256i z) { x = Add(x, y, z); return x; } __m256i inline Inc(__m256i &x, __m256i y, __m256i z, __m256i w) { x = Add(x, y, z, w); return x; } __m256i inline Xor(__m256i x, __m256i y) { return _mm256_xor_si256(x, y); } __m256i inline Xor(__m256i x, __m256i y, __m256i z) { return Xor(Xor(x, y), z); } __m256i inline Or(__m256i x, __m256i y) { return _mm256_or_si256(x, y); } __m256i inline And(__m256i x, __m256i y) { return _mm256_and_si256(x, y); } __m256i inline ShR(__m256i x, int n) { return _mm256_srli_epi32(x, n); } __m256i inline ShL(__m256i x, int n) { return _mm256_slli_epi32(x, n); } __m256i inline Ch(__m256i x, __m256i y, __m256i z) { return Xor(z, And(x, Xor(y, z))); } __m256i inline Maj(__m256i x, __m256i y, __m256i z) { return Or(And(x, y), And(z, Or(x, y))); } __m256i inline Sigma0(__m256i x) { return Xor(Or(ShR(x, 2), ShL(x, 30)), Or(ShR(x, 13), ShL(x, 19)), Or(ShR(x, 22), ShL(x, 10))); } __m256i inline Sigma1(__m256i x) { return Xor(Or(ShR(x, 6), ShL(x, 26)), Or(ShR(x, 11), ShL(x, 21)), Or(ShR(x, 25), ShL(x, 7))); } __m256i inline sigma0(__m256i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR(x, 18), ShL(x, 14)), ShR(x, 3)); } __m256i inline sigma1(__m256i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ inline void __attribute__((always_inline)) Round(__m256i a, __m256i b, __m256i c, __m256i &d, __m256i e, __m256i f, __m256i g, __m256i &h, __m256i k) { __m256i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m256i t2 = Add(Sigma0(a), Maj(a, b, c)); d = Add(d, t1); h = Add(t1, t2); } __m256i inline Read8(const uint8_t *chunk, int offset) { __m256i ret = _mm256_set_epi32( ReadLE32(chunk + 0 + offset), ReadLE32(chunk + 64 + offset), ReadLE32(chunk + 128 + offset), ReadLE32(chunk + 192 + offset), ReadLE32(chunk + 256 + offset), ReadLE32(chunk + 320 + offset), ReadLE32(chunk + 384 + offset), ReadLE32(chunk + 448 + offset)); return _mm256_shuffle_epi8( ret, _mm256_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL, 0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); } inline void Write8(uint8_t *out, int offset, __m256i v) { v = _mm256_shuffle_epi8( v, _mm256_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL, 0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); WriteLE32(out + 0 + offset, _mm256_extract_epi32(v, 7)); WriteLE32(out + 32 + offset, _mm256_extract_epi32(v, 6)); WriteLE32(out + 64 + offset, _mm256_extract_epi32(v, 5)); WriteLE32(out + 96 + offset, _mm256_extract_epi32(v, 4)); WriteLE32(out + 128 + offset, _mm256_extract_epi32(v, 3)); WriteLE32(out + 160 + offset, _mm256_extract_epi32(v, 2)); WriteLE32(out + 192 + offset, _mm256_extract_epi32(v, 1)); WriteLE32(out + 224 + offset, _mm256_extract_epi32(v, 0)); } } void Transform_8way(uint8_t *out, const uint8_t *in) { // Transform 1 __m256i a = K(0x6a09e667ul); __m256i b = K(0xbb67ae85ul); __m256i c = K(0x3c6ef372ul); __m256i d = K(0xa54ff53aul); __m256i e = K(0x510e527ful); __m256i f = K(0x9b05688cul); __m256i g = K(0x1f83d9abul); __m256i h = K(0x5be0cd19ul); __m256i w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0 = Read8(in, 0))); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1 = Read8(in, 4))); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2 = Read8(in, 8))); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3 = Read8(in, 12))); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4 = Read8(in, 16))); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5 = Read8(in, 20))); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6 = Read8(in, 24))); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7 = Read8(in, 28))); Round(a, b, c, d, e, f, g, h, Add(K(0xd807aa98ul), w8 = Read8(in, 32))); Round(h, a, b, c, d, e, f, g, Add(K(0x12835b01ul), w9 = Read8(in, 36))); Round(g, h, a, b, c, d, e, f, Add(K(0x243185beul), w10 = Read8(in, 40))); Round(f, g, h, a, b, c, d, e, Add(K(0x550c7dc3ul), w11 = Read8(in, 44))); Round(e, f, g, h, a, b, c, d, Add(K(0x72be5d74ul), w12 = Read8(in, 48))); Round(d, e, f, g, h, a, b, c, Add(K(0x80deb1feul), w13 = Read8(in, 52))); Round(c, d, e, f, g, h, a, b, Add(K(0x9bdc06a7ul), w14 = Read8(in, 56))); Round(b, c, d, e, f, g, h, a, Add(K(0xc19bf174ul), w15 = Read8(in, 60))); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); a = Add(a, K(0x6a09e667ul)); b = Add(b, K(0xbb67ae85ul)); c = Add(c, K(0x3c6ef372ul)); d = Add(d, K(0xa54ff53aul)); e = Add(e, K(0x510e527ful)); f = Add(f, K(0x9b05688cul)); g = Add(g, K(0x1f83d9abul)); h = Add(h, K(0x5be0cd19ul)); __m256i t0 = a, t1 = b, t2 = c, t3 = d, t4 = e, t5 = f, t6 = g, t7 = h; // Transform 2 Round(a, b, c, d, e, f, g, h, K(0xc28a2f98ul)); Round(h, a, b, c, d, e, f, g, K(0x71374491ul)); Round(g, h, a, b, c, d, e, f, K(0xb5c0fbcful)); Round(f, g, h, a, b, c, d, e, K(0xe9b5dba5ul)); Round(e, f, g, h, a, b, c, d, K(0x3956c25bul)); Round(d, e, f, g, h, a, b, c, K(0x59f111f1ul)); Round(c, d, e, f, g, h, a, b, K(0x923f82a4ul)); Round(b, c, d, e, f, g, h, a, K(0xab1c5ed5ul)); Round(a, b, c, d, e, f, g, h, K(0xd807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf374ul)); Round(a, b, c, d, e, f, g, h, K(0x649b69c1ul)); Round(h, a, b, c, d, e, f, g, K(0xf0fe4786ul)); Round(g, h, a, b, c, d, e, f, K(0x0fe1edc6ul)); Round(f, g, h, a, b, c, d, e, K(0x240cf254ul)); Round(e, f, g, h, a, b, c, d, K(0x4fe9346ful)); Round(d, e, f, g, h, a, b, c, K(0x6cc984beul)); Round(c, d, e, f, g, h, a, b, K(0x61b9411eul)); Round(b, c, d, e, f, g, h, a, K(0x16f988faul)); Round(a, b, c, d, e, f, g, h, K(0xf2c65152ul)); Round(h, a, b, c, d, e, f, g, K(0xa88e5a6dul)); Round(g, h, a, b, c, d, e, f, K(0xb019fc65ul)); Round(f, g, h, a, b, c, d, e, K(0xb9d99ec7ul)); Round(e, f, g, h, a, b, c, d, K(0x9a1231c3ul)); Round(d, e, f, g, h, a, b, c, K(0xe70eeaa0ul)); Round(c, d, e, f, g, h, a, b, K(0xfdb1232bul)); Round(b, c, d, e, f, g, h, a, K(0xc7353eb0ul)); Round(a, b, c, d, e, f, g, h, K(0x3069bad5ul)); Round(h, a, b, c, d, e, f, g, K(0xcb976d5ful)); Round(g, h, a, b, c, d, e, f, K(0x5a0f118ful)); Round(f, g, h, a, b, c, d, e, K(0xdc1eeefdul)); Round(e, f, g, h, a, b, c, d, K(0x0a35b689ul)); Round(d, e, f, g, h, a, b, c, K(0xde0b7a04ul)); Round(c, d, e, f, g, h, a, b, K(0x58f4ca9dul)); Round(b, c, d, e, f, g, h, a, K(0xe15d5b16ul)); Round(a, b, c, d, e, f, g, h, K(0x007f3e86ul)); Round(h, a, b, c, d, e, f, g, K(0x37088980ul)); Round(g, h, a, b, c, d, e, f, K(0xa507ea32ul)); Round(f, g, h, a, b, c, d, e, K(0x6fab9537ul)); Round(e, f, g, h, a, b, c, d, K(0x17406110ul)); Round(d, e, f, g, h, a, b, c, K(0x0d8cd6f1ul)); Round(c, d, e, f, g, h, a, b, K(0xcdaa3b6dul)); Round(b, c, d, e, f, g, h, a, K(0xc0bbbe37ul)); Round(a, b, c, d, e, f, g, h, K(0x83613bdaul)); Round(h, a, b, c, d, e, f, g, K(0xdb48a363ul)); Round(g, h, a, b, c, d, e, f, K(0x0b02e931ul)); Round(f, g, h, a, b, c, d, e, K(0x6fd15ca7ul)); Round(e, f, g, h, a, b, c, d, K(0x521afacaul)); Round(d, e, f, g, h, a, b, c, K(0x31338431ul)); Round(c, d, e, f, g, h, a, b, K(0x6ed41a95ul)); Round(b, c, d, e, f, g, h, a, K(0x6d437890ul)); Round(a, b, c, d, e, f, g, h, K(0xc39c91f2ul)); Round(h, a, b, c, d, e, f, g, K(0x9eccabbdul)); Round(g, h, a, b, c, d, e, f, K(0xb5c9a0e6ul)); Round(f, g, h, a, b, c, d, e, K(0x532fb63cul)); Round(e, f, g, h, a, b, c, d, K(0xd2c741c6ul)); Round(d, e, f, g, h, a, b, c, K(0x07237ea3ul)); Round(c, d, e, f, g, h, a, b, K(0xa4954b68ul)); Round(b, c, d, e, f, g, h, a, K(0x4c191d76ul)); w0 = Add(t0, a); w1 = Add(t1, b); w2 = Add(t2, c); w3 = Add(t3, d); w4 = Add(t4, e); w5 = Add(t5, f); w6 = Add(t6, g); w7 = Add(t7, h); // Transform 3 a = K(0x6a09e667ul); b = K(0xbb67ae85ul); c = K(0x3c6ef372ul); d = K(0xa54ff53aul); e = K(0x510e527ful); f = K(0x9b05688cul); g = K(0x1f83d9abul); h = K(0x5be0cd19ul); Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0)); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1)); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2)); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3)); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4)); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5)); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6)); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7)); Round(a, b, c, d, e, f, g, h, K(0x5807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf274ul)); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, K(0xa00000ul), sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), K(0x100ul), sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, K(0x11002000ul)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), w8 = Add(K(0x80000000ul), sigma1(w6), w1))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), w9 = Add(sigma1(w7), w2))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), w10 = Add(sigma1(w8), w3))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), w11 = Add(sigma1(w9), w4))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), w12 = Add(sigma1(w10), w5))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), w13 = Add(sigma1(w11), w6))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), w14 = Add(sigma1(w12), w7, K(0x400022ul)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), w15 = Add(K(0x100ul), sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), w14, sigma1(w12), w7, sigma0(w15))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), w15, sigma1(w13), w8, sigma0(w0))); // Output Write8(out, 0, Add(a, K(0x6a09e667ul))); Write8(out, 4, Add(b, K(0xbb67ae85ul))); Write8(out, 8, Add(c, K(0x3c6ef372ul))); Write8(out, 12, Add(d, K(0xa54ff53aul))); Write8(out, 16, Add(e, K(0x510e527ful))); Write8(out, 20, Add(f, K(0x9b05688cul))); Write8(out, 24, Add(g, K(0x1f83d9abul))); Write8(out, 28, Add(h, K(0x5be0cd19ul))); } } #endif diff --git a/src/crypto/sha256_shani.cpp b/src/crypto/sha256_shani.cpp index d3a0b6563..aaa9c5f6d 100644 --- a/src/crypto/sha256_shani.cpp +++ b/src/crypto/sha256_shani.cpp @@ -1,358 +1,358 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // Based on https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c, // Written and placed in public domain by Jeffrey Walton. // Based on code from Intel, and by Sean Gulley for the miTLS project. #ifdef ENABLE_SHANI +#include #include -#include #include namespace { const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); const __m128i INIT0 = _mm_set_epi64x(0x6a09e667bb67ae85ull, 0x510e527f9b05688cull); const __m128i INIT1 = _mm_set_epi64x(0x3c6ef372a54ff53aull, 0x1f83d9ab5be0cd19ull); inline void __attribute__((always_inline)) QuadRound(__m128i &state0, __m128i &state1, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_set_epi64x(k1, k0); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } inline void __attribute__((always_inline)) QuadRound(__m128i &state0, __m128i &state1, __m128i m, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_add_epi32(m, _mm_set_epi64x(k1, k0)); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } inline void __attribute__((always_inline)) ShiftMessageA(__m128i &m0, __m128i m1) { m0 = _mm_sha256msg1_epu32(m0, m1); } inline void __attribute__((always_inline)) ShiftMessageC(__m128i &m0, __m128i m1, __m128i &m2) { m2 = _mm_sha256msg2_epu32(_mm_add_epi32(m2, _mm_alignr_epi8(m1, m0, 4)), m1); } inline void __attribute__((always_inline)) ShiftMessageB(__m128i &m0, __m128i m1, __m128i &m2) { ShiftMessageC(m0, m1, m2); ShiftMessageA(m0, m1); } inline void __attribute__((always_inline)) Shuffle(__m128i &s0, __m128i &s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0xB1); const __m128i t2 = _mm_shuffle_epi32(s1, 0x1B); s0 = _mm_alignr_epi8(t1, t2, 0x08); s1 = _mm_blend_epi16(t2, t1, 0xF0); } inline void __attribute__((always_inline)) Unshuffle(__m128i &s0, __m128i &s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0x1B); const __m128i t2 = _mm_shuffle_epi32(s1, 0xB1); s0 = _mm_blend_epi16(t1, t2, 0xF0); s1 = _mm_alignr_epi8(t2, t1, 0x08); } __m128i inline __attribute__((always_inline)) Load(const unsigned char *in) { return _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)in), MASK); } inline void __attribute__((always_inline)) Save(unsigned char *out, __m128i s) { _mm_storeu_si128((__m128i *)out, _mm_shuffle_epi8(s, MASK)); } } namespace sha256_shani { void Transform(uint32_t *s, const unsigned char *chunk, size_t blocks) { __m128i m0, m1, m2, m3, s0, s1, so0, so1; /* Load state */ s0 = _mm_loadu_si128((const __m128i *)s); s1 = _mm_loadu_si128((const __m128i *)(s + 4)); Shuffle(s0, s1); while (blocks--) { /* Remember old state */ so0 = s0; so1 = s1; /* Load data and transform */ m0 = Load(chunk); QuadRound(s0, s1, m0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); m1 = Load(chunk + 16); QuadRound(s0, s1, m1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(m0, m1); m2 = Load(chunk + 32); QuadRound(s0, s1, m2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); ShiftMessageA(m1, m2); m3 = Load(chunk + 48); QuadRound(s0, s1, m3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(m0, m1, m2); QuadRound(s0, s1, m2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(m1, m2, m3); QuadRound(s0, s1, m3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(m0, m1, m2); QuadRound(s0, s1, m2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); ShiftMessageB(m1, m2, m3); QuadRound(s0, s1, m3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(m0, m1, m2); QuadRound(s0, s1, m2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(m1, m2, m3); QuadRound(s0, s1, m3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); /* Combine with old state */ s0 = _mm_add_epi32(s0, so0); s1 = _mm_add_epi32(s1, so1); /* Advance */ chunk += 64; } Unshuffle(s0, s1); _mm_storeu_si128((__m128i *)s, s0); _mm_storeu_si128((__m128i *)(s + 4), s1); } } namespace sha256d64_shani { void Transform_2way(unsigned char *out, const unsigned char *in) { __m128i am0, am1, am2, am3, as0, as1, aso0, aso1; __m128i bm0, bm1, bm2, bm3, bs0, bs1, bso0, bso1; /* Transform 1 */ bs0 = as0 = INIT0; bs1 = as1 = INIT1; am0 = Load(in); bm0 = Load(in + 64); QuadRound(as0, as1, am0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); QuadRound(bs0, bs1, bm0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); am1 = Load(in + 16); bm1 = Load(in + 80); QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(am0, am1); ShiftMessageA(bm0, bm1); am2 = Load(in + 32); bm2 = Load(in + 96); QuadRound(as0, as1, am2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(bs0, bs1, bm2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); ShiftMessageA(am1, am2); ShiftMessageA(bm1, bm2); am3 = Load(in + 48); bm3 = Load(in + 112); QuadRound(as0, as1, am3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, bm3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); QuadRound(bs0, bs1, bm2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(am0, am1, am2); ShiftMessageC(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(am1, am2, am3); ShiftMessageC(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); QuadRound(bs0, bs1, bm3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); as0 = _mm_add_epi32(as0, INIT0); bs0 = _mm_add_epi32(bs0, INIT0); as1 = _mm_add_epi32(as1, INIT1); bs1 = _mm_add_epi32(bs1, INIT1); /* Transform 2 */ aso0 = as0; bso0 = bs0; aso1 = as1; bso1 = bs1; QuadRound(as0, as1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); QuadRound(bs0, bs1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); QuadRound(as0, as1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(as0, as1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(as0, as1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); QuadRound(bs0, bs1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); QuadRound(as0, as1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); QuadRound(bs0, bs1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); QuadRound(as0, as1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); QuadRound(bs0, bs1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); QuadRound(as0, as1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); QuadRound(bs0, bs1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); QuadRound(as0, as1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); QuadRound(bs0, bs1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); QuadRound(as0, as1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); QuadRound(bs0, bs1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); QuadRound(as0, as1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); QuadRound(bs0, bs1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); QuadRound(as0, as1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); QuadRound(bs0, bs1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); QuadRound(as0, as1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); QuadRound(bs0, bs1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); QuadRound(as0, as1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); QuadRound(bs0, bs1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); QuadRound(as0, as1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); QuadRound(bs0, bs1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); QuadRound(as0, as1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); QuadRound(bs0, bs1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); as0 = _mm_add_epi32(as0, aso0); bs0 = _mm_add_epi32(bs0, bso0); as1 = _mm_add_epi32(as1, aso1); bs1 = _mm_add_epi32(bs1, bso1); /* Extract hash */ Unshuffle(as0, as1); Unshuffle(bs0, bs1); am0 = as0; bm0 = bs0; am1 = as1; bm1 = bs1; /* Transform 3 */ bs0 = as0 = INIT0; bs1 = as1 = INIT1; QuadRound(as0, as1, am0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); QuadRound(bs0, bs1, bm0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(am0, am1); ShiftMessageA(bm0, bm1); bm2 = am2 = _mm_set_epi64x(0x0ull, 0x80000000ull); QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); ShiftMessageA(am1, am2); ShiftMessageA(bm1, bm2); bm3 = am3 = _mm_set_epi64x(0x10000000000ull, 0x0ull); QuadRound(as0, as1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); QuadRound(bs0, bs1, bm2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(am0, am1, am2); ShiftMessageC(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(am1, am2, am3); ShiftMessageC(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); QuadRound(bs0, bs1, bm3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); as0 = _mm_add_epi32(as0, INIT0); bs0 = _mm_add_epi32(bs0, INIT0); as1 = _mm_add_epi32(as1, INIT1); bs1 = _mm_add_epi32(bs1, INIT1); /* Extract hash into out */ Unshuffle(as0, as1); Unshuffle(bs0, bs1); Save(out, as0); Save(out + 16, as1); Save(out + 32, bs0); Save(out + 48, bs1); } } #endif diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp index f76e36ef9..1e869836f 100644 --- a/src/crypto/sha256_sse4.cpp +++ b/src/crypto/sha256_sse4.cpp @@ -1,1506 +1,1506 @@ // Copyright (c) 2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // This is a translation to GCC extended asm syntax from YASM code by Intel // (available at the bottom of this file). -#include -#include +#include +#include #if defined(__x86_64__) || defined(__amd64__) namespace sha256_sse4 { void Transform(uint32_t *s, const uint8_t *chunk, size_t blocks) { static const uint32_t K256 alignas(16)[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; static const uint32_t FLIP_MASK alignas(16)[] = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f}; static const uint32_t SHUF_00BA alignas(16)[] = {0x03020100, 0x0b0a0908, 0xffffffff, 0xffffffff}; static const uint32_t SHUF_DC00 alignas(16)[] = {0xffffffff, 0xffffffff, 0x03020100, 0x0b0a0908}; uint32_t a, b, c, d, f, g, h, y0, y1, y2; uint64_t tbl; uint64_t inp_end, inp; uint32_t xfer alignas(16)[4]; __asm__ __volatile__( "shl $0x6,%2;" "je Ldone_hash_%=;" "add %1,%2;" "mov %2,%14;" "mov (%0),%3;" "mov 0x4(%0),%4;" "mov 0x8(%0),%5;" "mov 0xc(%0),%6;" "mov 0x10(%0),%k2;" "mov 0x14(%0),%7;" "mov 0x18(%0),%8;" "mov 0x1c(%0),%9;" "movdqa %18,%%xmm12;" "movdqa %19,%%xmm10;" "movdqa %20,%%xmm11;" "Lloop0_%=:" "lea %17,%13;" "movdqu (%1),%%xmm4;" "pshufb %%xmm12,%%xmm4;" "movdqu 0x10(%1),%%xmm5;" "pshufb %%xmm12,%%xmm5;" "movdqu 0x20(%1),%%xmm6;" "pshufb %%xmm12,%%xmm6;" "movdqu 0x30(%1),%%xmm7;" "pshufb %%xmm12,%%xmm7;" "mov %1,%15;" "mov $3,%1;" "Lloop1_%=:" "movdqa 0x0(%13),%%xmm9;" "paddd %%xmm4,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm7,%%xmm0;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "palignr $0x4,%%xmm6,%%xmm0;" "ror $0x9,%11;" "xor %k2,%10;" "mov %7,%12;" "ror $0x5,%10;" "movdqa %%xmm5,%%xmm1;" "xor %3,%11;" "xor %8,%12;" "paddd %%xmm4,%%xmm0;" "xor %k2,%10;" "and %k2,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm4,%%xmm1;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "pslld $0x19,%%xmm1;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "psrld $0x7,%%xmm2;" "and %4,%10;" "add %11,%9;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%9;" "movdqa %%xmm3,%%xmm2;" "mov %6,%10;" "mov %9,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %6,%10;" "mov %k2,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %6,%10;" "and %6,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %9,%11;" "xor %7,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "pxor %%xmm8,%%xmm1;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "pshufd $0xfa,%%xmm7,%%xmm2;" "and %3,%10;" "add %11,%8;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%8;" "movdqa %%xmm2,%%xmm3;" "mov %5,%10;" "mov %8,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %k2,%12;" "psrlq $0x13,%%xmm3;" "xor %5,%10;" "and %5,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %8,%11;" "xor %k2,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "pshufb %%xmm10,%%xmm8;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "paddd %%xmm8,%%xmm0;" "and %9,%10;" "add %11,%7;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%7;" "movdqa %%xmm2,%%xmm3;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "movdqa %%xmm2,%%xmm4;" "ror $0x9,%11;" "xor %4,%10;" "mov %5,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %7,%11;" "xor %6,%12;" "psrlq $0x13,%%xmm3;" "xor %4,%10;" "and %4,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm4;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm4;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "pshufb %%xmm11,%%xmm4;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "paddd %%xmm0,%%xmm4;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "movdqa 0x10(%13),%%xmm9;" "paddd %%xmm5,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm4,%%xmm0;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "palignr $0x4,%%xmm7,%%xmm0;" "ror $0x9,%11;" "xor %3,%10;" "mov %4,%12;" "ror $0x5,%10;" "movdqa %%xmm6,%%xmm1;" "xor %k2,%11;" "xor %5,%12;" "paddd %%xmm5,%%xmm0;" "xor %3,%10;" "and %3,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm5,%%xmm1;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "pslld $0x19,%%xmm1;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "psrld $0x7,%%xmm2;" "and %7,%10;" "add %11,%6;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%6;" "movdqa %%xmm3,%%xmm2;" "mov %9,%10;" "mov %6,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %9,%10;" "mov %3,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %9,%10;" "and %9,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %6,%11;" "xor %4,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "pxor %%xmm8,%%xmm1;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "pshufd $0xfa,%%xmm4,%%xmm2;" "and %k2,%10;" "add %11,%5;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%5;" "movdqa %%xmm2,%%xmm3;" "mov %8,%10;" "mov %5,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %3,%12;" "psrlq $0x13,%%xmm3;" "xor %8,%10;" "and %8,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %5,%11;" "xor %3,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "pshufb %%xmm10,%%xmm8;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "paddd %%xmm8,%%xmm0;" "and %6,%10;" "add %11,%4;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%4;" "movdqa %%xmm2,%%xmm3;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "movdqa %%xmm2,%%xmm5;" "ror $0x9,%11;" "xor %7,%10;" "mov %8,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %4,%11;" "xor %9,%12;" "psrlq $0x13,%%xmm3;" "xor %7,%10;" "and %7,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm5;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm5;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "pshufb %%xmm11,%%xmm5;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "paddd %%xmm0,%%xmm5;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "movdqa 0x20(%13),%%xmm9;" "paddd %%xmm6,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm5,%%xmm0;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "palignr $0x4,%%xmm4,%%xmm0;" "ror $0x9,%11;" "xor %k2,%10;" "mov %7,%12;" "ror $0x5,%10;" "movdqa %%xmm7,%%xmm1;" "xor %3,%11;" "xor %8,%12;" "paddd %%xmm6,%%xmm0;" "xor %k2,%10;" "and %k2,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm6,%%xmm1;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "pslld $0x19,%%xmm1;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "psrld $0x7,%%xmm2;" "and %4,%10;" "add %11,%9;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%9;" "movdqa %%xmm3,%%xmm2;" "mov %6,%10;" "mov %9,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %6,%10;" "mov %k2,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %6,%10;" "and %6,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %9,%11;" "xor %7,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "pxor %%xmm8,%%xmm1;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "pshufd $0xfa,%%xmm5,%%xmm2;" "and %3,%10;" "add %11,%8;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%8;" "movdqa %%xmm2,%%xmm3;" "mov %5,%10;" "mov %8,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %k2,%12;" "psrlq $0x13,%%xmm3;" "xor %5,%10;" "and %5,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %8,%11;" "xor %k2,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "pshufb %%xmm10,%%xmm8;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "paddd %%xmm8,%%xmm0;" "and %9,%10;" "add %11,%7;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%7;" "movdqa %%xmm2,%%xmm3;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "movdqa %%xmm2,%%xmm6;" "ror $0x9,%11;" "xor %4,%10;" "mov %5,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %7,%11;" "xor %6,%12;" "psrlq $0x13,%%xmm3;" "xor %4,%10;" "and %4,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm6;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm6;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "pshufb %%xmm11,%%xmm6;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "paddd %%xmm0,%%xmm6;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "movdqa 0x30(%13),%%xmm9;" "paddd %%xmm7,%%xmm9;" "movdqa %%xmm9,%16;" "add $0x40,%13;" "movdqa %%xmm6,%%xmm0;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "palignr $0x4,%%xmm5,%%xmm0;" "ror $0x9,%11;" "xor %3,%10;" "mov %4,%12;" "ror $0x5,%10;" "movdqa %%xmm4,%%xmm1;" "xor %k2,%11;" "xor %5,%12;" "paddd %%xmm7,%%xmm0;" "xor %3,%10;" "and %3,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm7,%%xmm1;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "pslld $0x19,%%xmm1;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "psrld $0x7,%%xmm2;" "and %7,%10;" "add %11,%6;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%6;" "movdqa %%xmm3,%%xmm2;" "mov %9,%10;" "mov %6,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %9,%10;" "mov %3,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %9,%10;" "and %9,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %6,%11;" "xor %4,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "pxor %%xmm8,%%xmm1;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "pshufd $0xfa,%%xmm6,%%xmm2;" "and %k2,%10;" "add %11,%5;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%5;" "movdqa %%xmm2,%%xmm3;" "mov %8,%10;" "mov %5,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %3,%12;" "psrlq $0x13,%%xmm3;" "xor %8,%10;" "and %8,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %5,%11;" "xor %3,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "pshufb %%xmm10,%%xmm8;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "paddd %%xmm8,%%xmm0;" "and %6,%10;" "add %11,%4;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%4;" "movdqa %%xmm2,%%xmm3;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "movdqa %%xmm2,%%xmm7;" "ror $0x9,%11;" "xor %7,%10;" "mov %8,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %4,%11;" "xor %9,%12;" "psrlq $0x13,%%xmm3;" "xor %7,%10;" "and %7,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm7;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm7;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "pshufb %%xmm11,%%xmm7;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "paddd %%xmm0,%%xmm7;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "sub $0x1,%1;" "jne Lloop1_%=;" "mov $0x2,%1;" "Lloop2_%=:" "paddd 0x0(%13),%%xmm4;" "movdqa %%xmm4,%16;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "xor %k2,%10;" "ror $0x9,%11;" "mov %7,%12;" "xor %3,%11;" "ror $0x5,%10;" "xor %8,%12;" "xor %k2,%10;" "ror $0xb,%11;" "and %k2,%12;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "add %10,%12;" "ror $0x2,%11;" "add %16,%12;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "and %4,%10;" "add %11,%9;" "or %12,%10;" "add %10,%9;" "mov %6,%10;" "ror $0xe,%10;" "mov %9,%11;" "xor %6,%10;" "ror $0x9,%11;" "mov %k2,%12;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "xor %6,%10;" "ror $0xb,%11;" "and %6,%12;" "xor %9,%11;" "ror $0x6,%10;" "xor %7,%12;" "add %10,%12;" "ror $0x2,%11;" "add 4+%16,%12;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "and %3,%10;" "add %11,%8;" "or %12,%10;" "add %10,%8;" "mov %5,%10;" "ror $0xe,%10;" "mov %8,%11;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "xor %k2,%12;" "xor %5,%10;" "ror $0xb,%11;" "and %5,%12;" "xor %8,%11;" "ror $0x6,%10;" "xor %k2,%12;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "and %9,%10;" "add %11,%7;" "or %12,%10;" "add %10,%7;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "xor %4,%10;" "ror $0x9,%11;" "mov %5,%12;" "xor %7,%11;" "ror $0x5,%10;" "xor %6,%12;" "xor %4,%10;" "ror $0xb,%11;" "and %4,%12;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "add %10,%12;" "ror $0x2,%11;" "add 12+%16,%12;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "paddd 0x10(%13),%%xmm5;" "movdqa %%xmm5,%16;" "add $0x20,%13;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "xor %3,%10;" "ror $0x9,%11;" "mov %4,%12;" "xor %k2,%11;" "ror $0x5,%10;" "xor %5,%12;" "xor %3,%10;" "ror $0xb,%11;" "and %3,%12;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "add %10,%12;" "ror $0x2,%11;" "add %16,%12;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "and %7,%10;" "add %11,%6;" "or %12,%10;" "add %10,%6;" "mov %9,%10;" "ror $0xe,%10;" "mov %6,%11;" "xor %9,%10;" "ror $0x9,%11;" "mov %3,%12;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "xor %9,%10;" "ror $0xb,%11;" "and %9,%12;" "xor %6,%11;" "ror $0x6,%10;" "xor %4,%12;" "add %10,%12;" "ror $0x2,%11;" "add 4+%16,%12;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "and %k2,%10;" "add %11,%5;" "or %12,%10;" "add %10,%5;" "mov %8,%10;" "ror $0xe,%10;" "mov %5,%11;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "xor %3,%12;" "xor %8,%10;" "ror $0xb,%11;" "and %8,%12;" "xor %5,%11;" "ror $0x6,%10;" "xor %3,%12;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "and %6,%10;" "add %11,%4;" "or %12,%10;" "add %10,%4;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "xor %7,%10;" "ror $0x9,%11;" "mov %8,%12;" "xor %4,%11;" "ror $0x5,%10;" "xor %9,%12;" "xor %7,%10;" "ror $0xb,%11;" "and %7,%12;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "add %10,%12;" "ror $0x2,%11;" "add 12+%16,%12;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "movdqa %%xmm6,%%xmm4;" "movdqa %%xmm7,%%xmm5;" "sub $0x1,%1;" "jne Lloop2_%=;" "add (%0),%3;" "mov %3,(%0);" "add 0x4(%0),%4;" "mov %4,0x4(%0);" "add 0x8(%0),%5;" "mov %5,0x8(%0);" "add 0xc(%0),%6;" "mov %6,0xc(%0);" "add 0x10(%0),%k2;" "mov %k2,0x10(%0);" "add 0x14(%0),%7;" "mov %7,0x14(%0);" "add 0x18(%0),%8;" "mov %8,0x18(%0);" "add 0x1c(%0),%9;" "mov %9,0x1c(%0);" "mov %15,%1;" "add $0x40,%1;" "cmp %14,%1;" "jne Lloop0_%=;" "Ldone_hash_%=:" : "+r"(s), "+r"(chunk), "+r"(blocks), "=r"(a), "=r"(b), "=r"(c), "=r"(d), /* e = chunk */ "=r"(f), "=r"(g), "=r"(h), "=r"(y0), "=r"(y1), "=r"(y2), "=r"(tbl), "+m"(inp_end), "+m"(inp), "+m"(xfer) : "m"(K256), "m"(FLIP_MASK), "m"(SHUF_00BA), "m"(SHUF_DC00) : "cc", "memory", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12"); } } /* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Copyright (c) 2012, Intel Corporation ; ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions are ; met: ; ; * Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; notice, this list of conditions and the following disclaimer in the ; documentation and/or other materials provided with the ; distribution. ; ; * Neither the name of the Intel Corporation nor the names of its ; contributors may be used to endorse or promote products derived from ; this software without specific prior written permission. ; ; ; THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR ; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Example YASM command lines: ; Windows: yasm -Xvc -f x64 -rnasm -pnasm -o sha256_sse4.obj -g cv8 sha256_sse4.asm ; Linux: yasm -f x64 -f elf64 -X gnu -g dwarf2 -D LINUX -o sha256_sse4.o sha256_sse4.asm ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This code is described in an Intel White-Paper: ; "Fast SHA-256 Implementations on Intel Architecture Processors" ; ; To find it, surf to http://www.intel.com/p/en_US/embedded ; and search for that title. ; The paper is expected to be released roughly at the end of April, 2012 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This code schedules 1 blocks at a time, with 4 lanes per block ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %define MOVDQ movdqu ;; assume buffers not aligned ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Define Macros ; addm [mem], reg ; Add reg to mem using reg-mem add and store %macro addm 2 add %2, %1 mov %1, %2 %endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; COPY_XMM_AND_BSWAP xmm, [mem], byte_flip_mask ; Load xmm with mem and byte swap each dword %macro COPY_XMM_AND_BSWAP 3 MOVDQ %1, %2 pshufb %1, %3 %endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %define X0 xmm4 %define X1 xmm5 %define X2 xmm6 %define X3 xmm7 %define XTMP0 xmm0 %define XTMP1 xmm1 %define XTMP2 xmm2 %define XTMP3 xmm3 %define XTMP4 xmm8 %define XFER xmm9 %define SHUF_00BA xmm10 ; shuffle xBxA -> 00BA %define SHUF_DC00 xmm11 ; shuffle xDxC -> DC00 %define BYTE_FLIP_MASK xmm12 %ifdef LINUX %define NUM_BLKS rdx ; 3rd arg %define CTX rsi ; 2nd arg %define INP rdi ; 1st arg %define SRND rdi ; clobbers INP %define c ecx %define d r8d %define e edx %else %define NUM_BLKS r8 ; 3rd arg %define CTX rdx ; 2nd arg %define INP rcx ; 1st arg %define SRND rcx ; clobbers INP %define c edi %define d esi %define e r8d %endif %define TBL rbp %define a eax %define b ebx %define f r9d %define g r10d %define h r11d %define y0 r13d %define y1 r14d %define y2 r15d _INP_END_SIZE equ 8 _INP_SIZE equ 8 _XFER_SIZE equ 8 %ifdef LINUX _XMM_SAVE_SIZE equ 0 %else _XMM_SAVE_SIZE equ 7*16 %endif ; STACK_SIZE plus pushes must be an odd multiple of 8 _ALIGN_SIZE equ 8 _INP_END equ 0 _INP equ _INP_END + _INP_END_SIZE _XFER equ _INP + _INP_SIZE _XMM_SAVE equ _XFER + _XFER_SIZE + _ALIGN_SIZE STACK_SIZE equ _XMM_SAVE + _XMM_SAVE_SIZE ; rotate_Xs ; Rotate values of symbols X0...X3 %macro rotate_Xs 0 %xdefine X_ X0 %xdefine X0 X1 %xdefine X1 X2 %xdefine X2 X3 %xdefine X3 X_ %endm ; ROTATE_ARGS ; Rotate values of symbols a...h %macro ROTATE_ARGS 0 %xdefine TMP_ h %xdefine h g %xdefine g f %xdefine f e %xdefine e d %xdefine d c %xdefine c b %xdefine b a %xdefine a TMP_ %endm %macro FOUR_ROUNDS_AND_SCHED 0 ;; compute s0 four at a time and s1 two at a time ;; compute W[-16] + W[-7] 4 at a time movdqa XTMP0, X3 mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a palignr XTMP0, X2, 4 ; XTMP0 = W[-7] ror y1, (22-13) ; y1 = a >> (22-13) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) movdqa XTMP1, X1 xor y1, a ; y1 = a ^ (a >> (22-13) xor y2, g ; y2 = f^g paddd XTMP0, X0 ; XTMP0 = W[-7] + W[-16] xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) ;; compute s0 palignr XTMP1, X0, 4 ; XTMP1 = W[-15] xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g movdqa XTMP2, XTMP1 ; XTMP2 = W[-15] ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 0*4] ; y2 = k + w + S1 + CH movdqa XTMP3, XTMP1 ; XTMP3 = W[-15] mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pslld XTMP1, (32-7) or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c psrld XTMP2, 7 and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 por XTMP1, XTMP2 ; XTMP1 = W[-15] ror 7 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP2, XTMP3 ; XTMP2 = W[-15] mov y0, e ; y0 = e mov y1, a ; y1 = a movdqa XTMP4, XTMP3 ; XTMP4 = W[-15] ror y0, (25-11) ; y0 = e >> (25-11) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y1, (22-13) ; y1 = a >> (22-13) pslld XTMP3, (32-18) xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) xor y2, g ; y2 = f^g psrld XTMP2, 18 ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) pxor XTMP1, XTMP3 xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) xor y2, g ; y2 = CH = ((f^g)&e)^g psrld XTMP4, 3 ; XTMP4 = W[-15] >> 3 add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 1*4] ; y2 = k + w + S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) pxor XTMP1, XTMP2 ; XTMP1 = W[-15] ror 7 ^ W[-15] ror 18 mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pxor XTMP1, XTMP4 ; XTMP1 = s0 or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c ;; compute low s1 pshufd XTMP2, X3, 11111010b ; XTMP2 = W[-2] {BBAA} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 paddd XTMP0, XTMP1 ; XTMP0 = W[-16] + W[-7] + s0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP3, XTMP2 ; XTMP3 = W[-2] {BBAA} mov y0, e ; y0 = e mov y1, a ; y1 = a ror y0, (25-11) ; y0 = e >> (25-11) movdqa XTMP4, XTMP2 ; XTMP4 = W[-2] {BBAA} xor y0, e ; y0 = e ^ (e >> (25-11)) ror y1, (22-13) ; y1 = a >> (22-13) mov y2, f ; y2 = f xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) psrlq XTMP2, 17 ; XTMP2 = W[-2] ror 17 {xBxA} xor y2, g ; y2 = f^g psrlq XTMP3, 19 ; XTMP3 = W[-2] ror 19 {xBxA} xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e psrld XTMP4, 10 ; XTMP4 = W[-2] >> 10 {BBAA} ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) xor y2, g ; y2 = CH = ((f^g)&e)^g ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) pxor XTMP2, XTMP3 add y2, y0 ; y2 = S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, [rsp + _XFER + 2*4] ; y2 = k + w + S1 + CH pxor XTMP4, XTMP2 ; XTMP4 = s1 {xBxA} mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pshufb XTMP4, SHUF_00BA ; XTMP4 = s1 {00BA} or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c paddd XTMP0, XTMP4 ; XTMP0 = {..., ..., W[1], W[0]} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 ;; compute high s1 pshufd XTMP2, XTMP0, 01010000b ; XTMP2 = W[-2] {DDCC} or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP3, XTMP2 ; XTMP3 = W[-2] {DDCC} mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a movdqa X0, XTMP2 ; X0 = W[-2] {DDCC} ror y1, (22-13) ; y1 = a >> (22-13) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) psrlq XTMP2, 17 ; XTMP2 = W[-2] ror 17 {xDxC} xor y1, a ; y1 = a ^ (a >> (22-13) xor y2, g ; y2 = f^g psrlq XTMP3, 19 ; XTMP3 = W[-2] ror 19 {xDxC} xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) psrld X0, 10 ; X0 = W[-2] >> 10 {DDCC} xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g pxor XTMP2, XTMP3 ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 3*4] ; y2 = k + w + S1 + CH pxor X0, XTMP2 ; X0 = s1 {xDxC} mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pshufb X0, SHUF_DC00 ; X0 = s1 {DC00} or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c paddd X0, XTMP0 ; X0 = {W[3], W[2], W[1], W[0]} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS rotate_Xs %endm ;; input is [rsp + _XFER + %1 * 4] %macro DO_ROUND 1 mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a xor y0, e ; y0 = e ^ (e >> (25-11)) ror y1, (22-13) ; y1 = a >> (22-13) mov y2, f ; y2 = f xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) xor y2, g ; y2 = f^g xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) and y2, e ; y2 = (f^g)&e xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g add y2, y0 ; y2 = S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, [rsp + _XFER + %1 * 4] ; y2 = k + w + S1 + CH mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS %endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; void sha256_sse4(void *input_data, UINT32 digest[8], UINT64 num_blks) ;; arg 1 : pointer to input data ;; arg 2 : pointer to digest ;; arg 3 : Num blocks section .text global sha256_sse4 align 32 sha256_sse4: push rbx %ifndef LINUX push rsi push rdi %endif push rbp push r13 push r14 push r15 sub rsp,STACK_SIZE %ifndef LINUX movdqa [rsp + _XMM_SAVE + 0*16],xmm6 movdqa [rsp + _XMM_SAVE + 1*16],xmm7 movdqa [rsp + _XMM_SAVE + 2*16],xmm8 movdqa [rsp + _XMM_SAVE + 3*16],xmm9 movdqa [rsp + _XMM_SAVE + 4*16],xmm10 movdqa [rsp + _XMM_SAVE + 5*16],xmm11 movdqa [rsp + _XMM_SAVE + 6*16],xmm12 %endif shl NUM_BLKS, 6 ; convert to bytes jz done_hash add NUM_BLKS, INP ; pointer to end of data mov [rsp + _INP_END], NUM_BLKS ;; load initial digest mov a,[4*0 + CTX] mov b,[4*1 + CTX] mov c,[4*2 + CTX] mov d,[4*3 + CTX] mov e,[4*4 + CTX] mov f,[4*5 + CTX] mov g,[4*6 + CTX] mov h,[4*7 + CTX] movdqa BYTE_FLIP_MASK, [PSHUFFLE_BYTE_FLIP_MASK wrt rip] movdqa SHUF_00BA, [_SHUF_00BA wrt rip] movdqa SHUF_DC00, [_SHUF_DC00 wrt rip] loop0: lea TBL,[K256 wrt rip] ;; byte swap first 16 dwords COPY_XMM_AND_BSWAP X0, [INP + 0*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X1, [INP + 1*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X2, [INP + 2*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X3, [INP + 3*16], BYTE_FLIP_MASK mov [rsp + _INP], INP ;; schedule 48 input dwords, by doing 3 rounds of 16 each mov SRND, 3 align 16 loop1: movdqa XFER, [TBL + 0*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 1*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 2*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 3*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER add TBL, 4*16 FOUR_ROUNDS_AND_SCHED sub SRND, 1 jne loop1 mov SRND, 2 loop2: paddd X0, [TBL + 0*16] movdqa [rsp + _XFER], X0 DO_ROUND 0 DO_ROUND 1 DO_ROUND 2 DO_ROUND 3 paddd X1, [TBL + 1*16] movdqa [rsp + _XFER], X1 add TBL, 2*16 DO_ROUND 0 DO_ROUND 1 DO_ROUND 2 DO_ROUND 3 movdqa X0, X2 movdqa X1, X3 sub SRND, 1 jne loop2 addm [4*0 + CTX],a addm [4*1 + CTX],b addm [4*2 + CTX],c addm [4*3 + CTX],d addm [4*4 + CTX],e addm [4*5 + CTX],f addm [4*6 + CTX],g addm [4*7 + CTX],h mov INP, [rsp + _INP] add INP, 64 cmp INP, [rsp + _INP_END] jne loop0 done_hash: %ifndef LINUX movdqa xmm6,[rsp + _XMM_SAVE + 0*16] movdqa xmm7,[rsp + _XMM_SAVE + 1*16] movdqa xmm8,[rsp + _XMM_SAVE + 2*16] movdqa xmm9,[rsp + _XMM_SAVE + 3*16] movdqa xmm10,[rsp + _XMM_SAVE + 4*16] movdqa xmm11,[rsp + _XMM_SAVE + 5*16] movdqa xmm12,[rsp + _XMM_SAVE + 6*16] %endif add rsp, STACK_SIZE pop r15 pop r14 pop r13 pop rbp %ifndef LINUX pop rdi pop rsi %endif pop rbx ret section .data align 64 K256: dd 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 dd 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 dd 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 dd 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 dd 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc dd 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da dd 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 dd 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 dd 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 dd 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 dd 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 dd 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 dd 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 dd 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 dd 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 dd 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 PSHUFFLE_BYTE_FLIP_MASK: ddq 0x0c0d0e0f08090a0b0405060700010203 ; shuffle xBxA -> 00BA _SHUF_00BA: ddq 0xFFFFFFFFFFFFFFFF0b0a090803020100 ; shuffle xDxC -> DC00 _SHUF_DC00: ddq 0x0b0a090803020100FFFFFFFFFFFFFFFF */ #endif diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp index 610509423..15b498ec0 100644 --- a/src/crypto/sha256_sse41.cpp +++ b/src/crypto/sha256_sse41.cpp @@ -1,444 +1,444 @@ #ifdef ENABLE_SSE41 +#include #include -#include #include "crypto/common.h" #include "crypto/sha256.h" namespace sha256d64_sse41 { namespace { __m128i inline K(uint32_t x) { return _mm_set1_epi32(x); } __m128i inline Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); } __m128i inline Add(__m128i x, __m128i y, __m128i z) { return Add(Add(x, y), z); } __m128i inline Add(__m128i x, __m128i y, __m128i z, __m128i w) { return Add(Add(x, y), Add(z, w)); } __m128i inline Add(__m128i x, __m128i y, __m128i z, __m128i w, __m128i v) { return Add(Add(x, y, z), Add(w, v)); } __m128i inline Inc(__m128i &x, __m128i y) { x = Add(x, y); return x; } __m128i inline Inc(__m128i &x, __m128i y, __m128i z) { x = Add(x, y, z); return x; } __m128i inline Inc(__m128i &x, __m128i y, __m128i z, __m128i w) { x = Add(x, y, z, w); return x; } __m128i inline Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); } __m128i inline Xor(__m128i x, __m128i y, __m128i z) { return Xor(Xor(x, y), z); } __m128i inline Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); } __m128i inline And(__m128i x, __m128i y) { return _mm_and_si128(x, y); } __m128i inline ShR(__m128i x, int n) { return _mm_srli_epi32(x, n); } __m128i inline ShL(__m128i x, int n) { return _mm_slli_epi32(x, n); } __m128i inline Ch(__m128i x, __m128i y, __m128i z) { return Xor(z, And(x, Xor(y, z))); } __m128i inline Maj(__m128i x, __m128i y, __m128i z) { return Or(And(x, y), And(z, Or(x, y))); } __m128i inline Sigma0(__m128i x) { return Xor(Or(ShR(x, 2), ShL(x, 30)), Or(ShR(x, 13), ShL(x, 19)), Or(ShR(x, 22), ShL(x, 10))); } __m128i inline Sigma1(__m128i x) { return Xor(Or(ShR(x, 6), ShL(x, 26)), Or(ShR(x, 11), ShL(x, 21)), Or(ShR(x, 25), ShL(x, 7))); } __m128i inline sigma0(__m128i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR(x, 18), ShL(x, 14)), ShR(x, 3)); } __m128i inline sigma1(__m128i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ inline void __attribute__((always_inline)) Round(__m128i a, __m128i b, __m128i c, __m128i &d, __m128i e, __m128i f, __m128i g, __m128i &h, __m128i k) { __m128i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m128i t2 = Add(Sigma0(a), Maj(a, b, c)); d = Add(d, t1); h = Add(t1, t2); } __m128i inline Read4(const uint8_t *chunk, int offset) { __m128i ret = _mm_set_epi32( ReadLE32(chunk + 0 + offset), ReadLE32(chunk + 64 + offset), ReadLE32(chunk + 128 + offset), ReadLE32(chunk + 192 + offset)); return _mm_shuffle_epi8(ret, _mm_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); } inline void Write4(uint8_t *out, int offset, __m128i v) { v = _mm_shuffle_epi8(v, _mm_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); WriteLE32(out + 0 + offset, _mm_extract_epi32(v, 3)); WriteLE32(out + 32 + offset, _mm_extract_epi32(v, 2)); WriteLE32(out + 64 + offset, _mm_extract_epi32(v, 1)); WriteLE32(out + 96 + offset, _mm_extract_epi32(v, 0)); } } void Transform_4way(uint8_t *out, const uint8_t *in) { // Transform 1 __m128i a = K(0x6a09e667ul); __m128i b = K(0xbb67ae85ul); __m128i c = K(0x3c6ef372ul); __m128i d = K(0xa54ff53aul); __m128i e = K(0x510e527ful); __m128i f = K(0x9b05688cul); __m128i g = K(0x1f83d9abul); __m128i h = K(0x5be0cd19ul); __m128i w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0 = Read4(in, 0))); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1 = Read4(in, 4))); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2 = Read4(in, 8))); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3 = Read4(in, 12))); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4 = Read4(in, 16))); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5 = Read4(in, 20))); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6 = Read4(in, 24))); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7 = Read4(in, 28))); Round(a, b, c, d, e, f, g, h, Add(K(0xd807aa98ul), w8 = Read4(in, 32))); Round(h, a, b, c, d, e, f, g, Add(K(0x12835b01ul), w9 = Read4(in, 36))); Round(g, h, a, b, c, d, e, f, Add(K(0x243185beul), w10 = Read4(in, 40))); Round(f, g, h, a, b, c, d, e, Add(K(0x550c7dc3ul), w11 = Read4(in, 44))); Round(e, f, g, h, a, b, c, d, Add(K(0x72be5d74ul), w12 = Read4(in, 48))); Round(d, e, f, g, h, a, b, c, Add(K(0x80deb1feul), w13 = Read4(in, 52))); Round(c, d, e, f, g, h, a, b, Add(K(0x9bdc06a7ul), w14 = Read4(in, 56))); Round(b, c, d, e, f, g, h, a, Add(K(0xc19bf174ul), w15 = Read4(in, 60))); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); a = Add(a, K(0x6a09e667ul)); b = Add(b, K(0xbb67ae85ul)); c = Add(c, K(0x3c6ef372ul)); d = Add(d, K(0xa54ff53aul)); e = Add(e, K(0x510e527ful)); f = Add(f, K(0x9b05688cul)); g = Add(g, K(0x1f83d9abul)); h = Add(h, K(0x5be0cd19ul)); __m128i t0 = a, t1 = b, t2 = c, t3 = d, t4 = e, t5 = f, t6 = g, t7 = h; // Transform 2 Round(a, b, c, d, e, f, g, h, K(0xc28a2f98ul)); Round(h, a, b, c, d, e, f, g, K(0x71374491ul)); Round(g, h, a, b, c, d, e, f, K(0xb5c0fbcful)); Round(f, g, h, a, b, c, d, e, K(0xe9b5dba5ul)); Round(e, f, g, h, a, b, c, d, K(0x3956c25bul)); Round(d, e, f, g, h, a, b, c, K(0x59f111f1ul)); Round(c, d, e, f, g, h, a, b, K(0x923f82a4ul)); Round(b, c, d, e, f, g, h, a, K(0xab1c5ed5ul)); Round(a, b, c, d, e, f, g, h, K(0xd807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf374ul)); Round(a, b, c, d, e, f, g, h, K(0x649b69c1ul)); Round(h, a, b, c, d, e, f, g, K(0xf0fe4786ul)); Round(g, h, a, b, c, d, e, f, K(0x0fe1edc6ul)); Round(f, g, h, a, b, c, d, e, K(0x240cf254ul)); Round(e, f, g, h, a, b, c, d, K(0x4fe9346ful)); Round(d, e, f, g, h, a, b, c, K(0x6cc984beul)); Round(c, d, e, f, g, h, a, b, K(0x61b9411eul)); Round(b, c, d, e, f, g, h, a, K(0x16f988faul)); Round(a, b, c, d, e, f, g, h, K(0xf2c65152ul)); Round(h, a, b, c, d, e, f, g, K(0xa88e5a6dul)); Round(g, h, a, b, c, d, e, f, K(0xb019fc65ul)); Round(f, g, h, a, b, c, d, e, K(0xb9d99ec7ul)); Round(e, f, g, h, a, b, c, d, K(0x9a1231c3ul)); Round(d, e, f, g, h, a, b, c, K(0xe70eeaa0ul)); Round(c, d, e, f, g, h, a, b, K(0xfdb1232bul)); Round(b, c, d, e, f, g, h, a, K(0xc7353eb0ul)); Round(a, b, c, d, e, f, g, h, K(0x3069bad5ul)); Round(h, a, b, c, d, e, f, g, K(0xcb976d5ful)); Round(g, h, a, b, c, d, e, f, K(0x5a0f118ful)); Round(f, g, h, a, b, c, d, e, K(0xdc1eeefdul)); Round(e, f, g, h, a, b, c, d, K(0x0a35b689ul)); Round(d, e, f, g, h, a, b, c, K(0xde0b7a04ul)); Round(c, d, e, f, g, h, a, b, K(0x58f4ca9dul)); Round(b, c, d, e, f, g, h, a, K(0xe15d5b16ul)); Round(a, b, c, d, e, f, g, h, K(0x007f3e86ul)); Round(h, a, b, c, d, e, f, g, K(0x37088980ul)); Round(g, h, a, b, c, d, e, f, K(0xa507ea32ul)); Round(f, g, h, a, b, c, d, e, K(0x6fab9537ul)); Round(e, f, g, h, a, b, c, d, K(0x17406110ul)); Round(d, e, f, g, h, a, b, c, K(0x0d8cd6f1ul)); Round(c, d, e, f, g, h, a, b, K(0xcdaa3b6dul)); Round(b, c, d, e, f, g, h, a, K(0xc0bbbe37ul)); Round(a, b, c, d, e, f, g, h, K(0x83613bdaul)); Round(h, a, b, c, d, e, f, g, K(0xdb48a363ul)); Round(g, h, a, b, c, d, e, f, K(0x0b02e931ul)); Round(f, g, h, a, b, c, d, e, K(0x6fd15ca7ul)); Round(e, f, g, h, a, b, c, d, K(0x521afacaul)); Round(d, e, f, g, h, a, b, c, K(0x31338431ul)); Round(c, d, e, f, g, h, a, b, K(0x6ed41a95ul)); Round(b, c, d, e, f, g, h, a, K(0x6d437890ul)); Round(a, b, c, d, e, f, g, h, K(0xc39c91f2ul)); Round(h, a, b, c, d, e, f, g, K(0x9eccabbdul)); Round(g, h, a, b, c, d, e, f, K(0xb5c9a0e6ul)); Round(f, g, h, a, b, c, d, e, K(0x532fb63cul)); Round(e, f, g, h, a, b, c, d, K(0xd2c741c6ul)); Round(d, e, f, g, h, a, b, c, K(0x07237ea3ul)); Round(c, d, e, f, g, h, a, b, K(0xa4954b68ul)); Round(b, c, d, e, f, g, h, a, K(0x4c191d76ul)); w0 = Add(t0, a); w1 = Add(t1, b); w2 = Add(t2, c); w3 = Add(t3, d); w4 = Add(t4, e); w5 = Add(t5, f); w6 = Add(t6, g); w7 = Add(t7, h); // Transform 3 a = K(0x6a09e667ul); b = K(0xbb67ae85ul); c = K(0x3c6ef372ul); d = K(0xa54ff53aul); e = K(0x510e527ful); f = K(0x9b05688cul); g = K(0x1f83d9abul); h = K(0x5be0cd19ul); Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0)); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1)); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2)); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3)); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4)); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5)); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6)); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7)); Round(a, b, c, d, e, f, g, h, K(0x5807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf274ul)); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, K(0xa00000ul), sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), K(0x100ul), sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, K(0x11002000ul)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), w8 = Add(K(0x80000000ul), sigma1(w6), w1))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), w9 = Add(sigma1(w7), w2))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), w10 = Add(sigma1(w8), w3))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), w11 = Add(sigma1(w9), w4))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), w12 = Add(sigma1(w10), w5))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), w13 = Add(sigma1(w11), w6))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), w14 = Add(sigma1(w12), w7, K(0x400022ul)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), w15 = Add(K(0x100ul), sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), w14, sigma1(w12), w7, sigma0(w15))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), w15, sigma1(w13), w8, sigma0(w0))); // Output Write4(out, 0, Add(a, K(0x6a09e667ul))); Write4(out, 4, Add(b, K(0xbb67ae85ul))); Write4(out, 8, Add(c, K(0x3c6ef372ul))); Write4(out, 12, Add(d, K(0xa54ff53aul))); Write4(out, 16, Add(e, K(0x510e527ful))); Write4(out, 20, Add(f, K(0x9b05688cul))); Write4(out, 24, Add(g, K(0x1f83d9abul))); Write4(out, 28, Add(h, K(0x5be0cd19ul))); } } #endif diff --git a/src/fs.h b/src/fs.h index 219a70e39..e0a9b0c32 100644 --- a/src/fs.h +++ b/src/fs.h @@ -1,24 +1,24 @@ // Copyright (c) 2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_FS_H #define BITCOIN_FS_H -#include +#include #include #include #include #include /** Filesystem operations and types */ namespace fs = boost::filesystem; /** Bridge operations to C stdio */ namespace fsbridge { FILE *fopen(const fs::path &p, const char *mode); FILE *freopen(const fs::path &p, const char *mode, FILE *stream); }; #endif diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 71d20ef99..1af317c37 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -1,674 +1,674 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "httpserver.h" #include "chainparamsbase.h" #include "compat.h" #include "config.h" #include "netbase.h" #include "rpc/protocol.h" // For HTTP status codes #include "sync.h" #include "ui_interface.h" #include "util.h" #include "utilstrencodings.h" -#include #include #include #include #include #include #include #include #include #include #ifdef EVENT__HAVE_NETINET_IN_H #include #ifdef _XOPEN_SOURCE_EXTENDED #include #endif #endif +#include #include #include #include #include /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; /** * Maximum HTTP post body size. Twice the maximum block size is added to this * value in practice. */ static const size_t MIN_SUPPORTED_BODY_SIZE = 0x02000000; /** HTTP request work item */ class HTTPWorkItem final : public HTTPClosure { public: HTTPWorkItem(Config &_config, std::unique_ptr _req, const std::string &_path, const HTTPRequestHandler &_func) : req(std::move(_req)), path(_path), func(_func), config(&_config) {} void operator()() override { func(*config, req.get(), path); } std::unique_ptr req; private: std::string path; HTTPRequestHandler func; Config *config; }; /** * Simple work queue for distributing work over multiple threads. * Work items are simply callable objects. */ template class WorkQueue { private: /** Mutex protects entire object */ CWaitableCriticalSection cs; std::condition_variable cond; std::deque> queue; bool running; size_t maxDepth; public: explicit WorkQueue(size_t _maxDepth) : running(true), maxDepth(_maxDepth) {} /** * Precondition: worker threads have all stopped (they have all been joined) */ ~WorkQueue() {} /** Enqueue a work item */ bool Enqueue(WorkItem *item) { LOCK(cs); if (queue.size() >= maxDepth) { return false; } queue.emplace_back(std::unique_ptr(item)); cond.notify_one(); return true; } /** Thread function */ void Run() { while (true) { std::unique_ptr i; { WAIT_LOCK(cs, lock); while (running && queue.empty()) cond.wait(lock); if (!running) break; i = std::move(queue.front()); queue.pop_front(); } (*i)(); } } /** Interrupt and exit loops */ void Interrupt() { LOCK(cs); running = false; cond.notify_all(); } }; struct HTTPPathHandler { HTTPPathHandler() {} HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler) : prefix(_prefix), exactMatch(_exactMatch), handler(_handler) {} std::string prefix; bool exactMatch; HTTPRequestHandler handler; }; /** HTTP module state */ //! libevent event loop static struct event_base *eventBase = nullptr; //! HTTP server struct evhttp *eventHTTP = nullptr; //! List of subnets to allow RPC connections from static std::vector rpc_allow_subnets; //! Work queue for handling longer requests off the event loop thread static WorkQueue *workQueue = nullptr; //! Handlers for (sub)paths std::vector pathHandlers; //! Bound listening sockets std::vector boundSockets; /** Check if a network address is allowed to access the HTTP server */ static bool ClientAllowed(const CNetAddr &netaddr) { if (!netaddr.IsValid()) return false; for (const CSubNet &subnet : rpc_allow_subnets) if (subnet.Match(netaddr)) return true; return false; } /** Initialize ACL list for HTTP server */ static bool InitHTTPAllowList() { rpc_allow_subnets.clear(); CNetAddr localv4; CNetAddr localv6; LookupHost("127.0.0.1", localv4, false); LookupHost("::1", localv6, false); // always allow IPv4 local subnet. rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv6 localhost. rpc_allow_subnets.push_back(CSubNet(localv6)); for (const std::string &strAllow : gArgs.GetArgs("-rpcallowip")) { CSubNet subnet; LookupSubNet(strAllow.c_str(), subnet); if (!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( strprintf("Invalid -rpcallowip subnet specification: %s. " "Valid are a single IP (e.g. 1.2.3.4), a " "network/netmask (e.g. 1.2.3.4/255.255.255.0) or a " "network/CIDR (e.g. 1.2.3.4/24).", strAllow), "", CClientUIInterface::MSG_ERROR); return false; } rpc_allow_subnets.push_back(subnet); } std::string strAllowed; for (const CSubNet &subnet : rpc_allow_subnets) { strAllowed += subnet.ToString() + " "; } LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed); return true; } /** HTTP request method as string - use for logging only */ static std::string RequestMethodString(HTTPRequest::RequestMethod m) { switch (m) { case HTTPRequest::GET: return "GET"; case HTTPRequest::POST: return "POST"; case HTTPRequest::HEAD: return "HEAD"; case HTTPRequest::PUT: return "PUT"; case HTTPRequest::OPTIONS: return "OPTIONS"; default: return "unknown"; } } /** HTTP request callback */ static void http_request_cb(struct evhttp_request *req, void *arg) { Config &config = *reinterpret_cast(arg); // Disable reading to work around a libevent bug, fixed in 2.2.0. if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { evhttp_connection *conn = evhttp_request_get_connection(req); if (conn) { bufferevent *bev = evhttp_connection_get_bufferevent(conn); if (bev) { bufferevent_disable(bev, EV_READ); } } } std::unique_ptr hreq(new HTTPRequest(req)); LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); // Early address-based allow check if (!ClientAllowed(hreq->GetPeer())) { hreq->WriteReply(HTTP_FORBIDDEN); return; } // Early reject unknown HTTP methods if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { hreq->WriteReply(HTTP_BADMETHOD); return; } // Find registered handler for prefix std::string strURI = hreq->GetURI(); std::string path; std::vector::const_iterator i = pathHandlers.begin(); std::vector::const_iterator iend = pathHandlers.end(); for (; i != iend; ++i) { bool match = false; if (i->exactMatch) { match = (strURI == i->prefix); } else { match = (strURI.substr(0, i->prefix.size()) == i->prefix); } if (match) { path = strURI.substr(i->prefix.size()); break; } } // Dispatch to worker thread. if (i != iend) { std::unique_ptr item( new HTTPWorkItem(config, std::move(hreq), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) { /* if true, queue took ownership */ item.release(); } else { LogPrintf("WARNING: request rejected because http work queue depth " "exceeded, it can be increased with the -rpcworkqueue= " "setting\n"); item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); } } else { hreq->WriteReply(HTTP_NOTFOUND); } } /** Callback to reject HTTP requests after shutdown. */ static void http_reject_request_cb(struct evhttp_request *req, void *) { LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr); } /** Event dispatcher thread */ static bool ThreadHTTP(struct event_base *base) { RenameThread("bitcoin-http"); LogPrint(BCLog::HTTP, "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() LogPrint(BCLog::HTTP, "Exited http event loop\n"); return event_base_got_break(base) == 0; } /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp *http) { int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); std::vector> endpoints; // Determine what addresses to bind to if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs. endpoints.push_back(std::make_pair("::1", defaultPort)); endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); if (gArgs.IsArgSet("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because " "-rpcallowip was not specified, refusing to allow " "everyone to connect\n"); } } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address. for (const std::string &strRPCBind : gArgs.GetArgs("-rpcbind")) { int port = defaultPort; std::string host; SplitHostPort(strRPCBind, port, host); endpoints.push_back(std::make_pair(host, port)); } } else { // No specific bind address specified, bind to any. endpoints.push_back(std::make_pair("::", defaultPort)); endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); } // Bind addresses for (std::vector>::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle( http, i->first.empty() ? nullptr : i->first.c_str(), i->second); if (bind_handle) { boundSockets.push_back(bind_handle); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); } } return !boundSockets.empty(); } /** Simple wrapper to set thread name and run work queue */ static void HTTPWorkQueueRun(WorkQueue *queue) { RenameThread("bitcoin-httpworker"); queue->Run(); } /** libevent event log callback */ static void libevent_log_cb(int severity, const char *msg) { #ifndef EVENT_LOG_WARN // EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed. #define EVENT_LOG_WARN _EVENT_LOG_WARN #endif // Log warn messages and higher without debug category. if (severity >= EVENT_LOG_WARN) { LogPrintf("libevent: %s\n", msg); } else { LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg); } } bool InitHTTPServer(Config &config) { struct evhttp *http = 0; struct event_base *base = 0; if (!InitHTTPAllowList()) return false; if (gArgs.GetBoolArg("-rpcssl", false)) { uiInterface.ThreadSafeMessageBox( "SSL mode for RPC (-rpcssl) is no longer supported.", "", CClientUIInterface::MSG_ERROR); return false; } // Redirect libevent's logging to our own log event_set_log_callback(&libevent_log_cb); #if LIBEVENT_VERSION_NUMBER >= 0x02010100 // If -debug=libevent, set full libevent debugging. // Otherwise, disable all libevent debugging. if (LogAcceptCategory(BCLog::LIBEVENT)) { event_enable_debug_logging(EVENT_DBG_ALL); } else { event_enable_debug_logging(EVENT_DBG_NONE); } #endif #ifdef WIN32 evthread_use_windows_threads(); #else evthread_use_pthreads(); #endif // XXX RAII: Create a new event_base for Libevent use base = event_base_new(); if (!base) { LogPrintf("Couldn't create an event_base: exiting\n"); return false; } // XXX RAII: Create a new evhttp object to handle requests http = evhttp_new(base); if (!http) { LogPrintf("couldn't create evhttp. Exiting.\n"); event_base_free(base); return false; } evhttp_set_timeout( http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE); evhttp_set_max_body_size(http, MIN_SUPPORTED_BODY_SIZE + 2 * config.GetMaxBlockSize()); evhttp_set_gencb(http, http_request_cb, &config); // Only POST and OPTIONS are supported, but we return HTTP 405 for the // others evhttp_set_allowed_methods( http, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_HEAD | EVHTTP_REQ_PUT | EVHTTP_REQ_DELETE | EVHTTP_REQ_OPTIONS); if (!HTTPBindAddresses(http)) { LogPrintf("Unable to bind any endpoint for RPC server\n"); evhttp_free(http); event_base_free(base); return false; } LogPrint(BCLog::HTTP, "Initialized HTTP server\n"); int workQueueDepth = std::max( (long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); workQueue = new WorkQueue(workQueueDepth); eventBase = base; eventHTTP = http; return true; } std::thread threadHTTP; std::future threadResult; static std::vector g_thread_http_workers; bool StartHTTPServer() { LogPrint(BCLog::HTTP, "Starting HTTP server\n"); int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); std::packaged_task task(ThreadHTTP); threadResult = task.get_future(); threadHTTP = std::thread(std::move(task), eventBase); for (int i = 0; i < rpcThreads; i++) { g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue); } return true; } void InterruptHTTPServer() { LogPrint(BCLog::HTTP, "Interrupting HTTP server\n"); if (eventHTTP) { // Unlisten sockets for (evhttp_bound_socket *socket : boundSockets) { evhttp_del_accept_socket(eventHTTP, socket); } // Reject requests on current connections evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr); } if (workQueue) workQueue->Interrupt(); } void StopHTTPServer() { LogPrint(BCLog::HTTP, "Stopping HTTP server\n"); if (workQueue) { LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n"); for (auto &thread : g_thread_http_workers) { thread.join(); } g_thread_http_workers.clear(); delete workQueue; } if (eventBase) { LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); // Exit the event loop as soon as there are no active events. event_base_loopexit(eventBase, nullptr); // Give event loop a few seconds to exit (to send back last RPC // responses), then break it. Before this was solved with // event_base_loopexit, but that didn't work as expected in at least // libevent 2.0.21 and always introduced a delay. In libevent master // that appears to be solved, so in the future that solution could be // used again (if desirable). // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { LogPrintf("HTTP event loop did not exit within allotted time, " "sending loopbreak\n"); event_base_loopbreak(eventBase); } threadHTTP.join(); } if (eventHTTP) { evhttp_free(eventHTTP); eventHTTP = nullptr; } if (eventBase) { event_base_free(eventBase); eventBase = nullptr; } LogPrint(BCLog::HTTP, "Stopped HTTP server\n"); } struct event_base *EventBase() { return eventBase; } static void httpevent_callback_fn(evutil_socket_t, short, void *data) { // Static handler: simply call inner handler HTTPEvent *self = static_cast(data); self->handler(); if (self->deleteWhenTriggered) delete self; } HTTPEvent::HTTPEvent(struct event_base *base, bool _deleteWhenTriggered, const std::function &_handler) : deleteWhenTriggered(_deleteWhenTriggered), handler(_handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); assert(ev); } HTTPEvent::~HTTPEvent() { event_free(ev); } void HTTPEvent::trigger(struct timeval *tv) { if (tv == nullptr) { // Immediately trigger event in main thread. event_active(ev, 0, 0); } else { // Trigger after timeval passed. evtimer_add(ev, tv); } } HTTPRequest::HTTPRequest(struct evhttp_request *_req) : req(_req), replySent(false) {} HTTPRequest::~HTTPRequest() { if (!replySent) { // Keep track of whether reply was sent to avoid request leaks LogPrintf("%s: Unhandled request\n", __func__); WriteReply(HTTP_INTERNAL, "Unhandled request"); } // evhttpd cleans up the request, as long as a reply was sent. } std::pair HTTPRequest::GetHeader(const std::string &hdr) { const struct evkeyvalq *headers = evhttp_request_get_input_headers(req); assert(headers); const char *val = evhttp_find_header(headers, hdr.c_str()); if (val) return std::make_pair(true, val); else return std::make_pair(false, ""); } std::string HTTPRequest::ReadBody() { struct evbuffer *buf = evhttp_request_get_input_buffer(req); if (!buf) return ""; size_t size = evbuffer_get_length(buf); /** * Trivial implementation: if this is ever a performance bottleneck, * internal copying can be avoided in multi-segment buffers by using * evbuffer_peek and an awkward loop. Though in that case, it'd be even * better to not copy into an intermediate string but use a stream * abstraction to consume the evbuffer on the fly in the parsing algorithm. */ const char *data = (const char *)evbuffer_pullup(buf, size); // returns nullptr in case of empty buffer. if (!data) { return ""; } std::string rv(data, size); evbuffer_drain(buf, size); return rv; } void HTTPRequest::WriteHeader(const std::string &hdr, const std::string &value) { struct evkeyvalq *headers = evhttp_request_get_output_headers(req); assert(headers); evhttp_add_header(headers, hdr.c_str(), value.c_str()); } /** * Closure sent to main thread to request a reply to be sent to a HTTP request. * Replies must be sent in the main loop in the main http thread, this cannot be * done from worker threads. */ void HTTPRequest::WriteReply(int nStatus, const std::string &strReply) { assert(!replySent && req); // Send event to main http thread to send reply message struct evbuffer *evb = evhttp_request_get_output_buffer(req); assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); auto req_copy = req; HTTPEvent *ev = new HTTPEvent(eventBase, true, [req_copy, nStatus] { evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); // Re-enable reading from the socket. This is the second part of the // libevent workaround above. if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { evhttp_connection *conn = evhttp_request_get_connection(req_copy); if (conn) { bufferevent *bev = evhttp_connection_get_bufferevent(conn); if (bev) { bufferevent_enable(bev, EV_READ | EV_WRITE); } } } }); ev->trigger(nullptr); replySent = true; // transferred back to main thread. req = nullptr; } CService HTTPRequest::GetPeer() { evhttp_connection *con = evhttp_request_get_connection(req); CService peer; if (con) { // evhttp retains ownership over returned address string const char *address = ""; uint16_t port = 0; evhttp_connection_get_peer(con, (char **)&address, &port); peer = LookupNumeric(address, port); } return peer; } std::string HTTPRequest::GetURI() { return evhttp_request_get_uri(req); } HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() { switch (evhttp_request_get_command(req)) { case EVHTTP_REQ_GET: return GET; case EVHTTP_REQ_POST: return POST; case EVHTTP_REQ_HEAD: return HEAD; case EVHTTP_REQ_PUT: return PUT; case EVHTTP_REQ_OPTIONS: return OPTIONS; default: return UNKNOWN; } } void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) { LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler)); } void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) { std::vector::iterator i = pathHandlers.begin(); std::vector::iterator iend = pathHandlers.end(); for (; i != iend; ++i) if (i->prefix == prefix && i->exactMatch == exactMatch) break; if (i != iend) { LogPrint(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.erase(i); } } diff --git a/src/init.cpp b/src/init.cpp index b8186b903..facdcb94f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,2399 +1,2399 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "init.h" #include "addrman.h" #include "amount.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" #include "compat/sanity.h" #include "config.h" #include "consensus/validation.h" #include "diskblockpos.h" #include "fs.h" #include "httprpc.h" #include "httpserver.h" #include "key.h" #include "miner.h" #include "net.h" #include "net_processing.h" #include "netbase.h" #include "policy/policy.h" #include "rpc/register.h" #include "rpc/safemode.h" #include "rpc/server.h" #include "scheduler.h" #include "script/scriptcache.h" #include "script/sigcache.h" #include "script/standard.h" #include "timedata.h" #include "torcontrol.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" #include "util.h" #include "utilmoneystr.h" #include "validation.h" #include "validationinterface.h" #ifdef ENABLE_WALLET #include "wallet/rpcdump.h" #endif #include "walletinitinterface.h" #include "warnings.h" #include #include #include #ifndef WIN32 -#include +#include #endif #include #include #include #include #include #include #if ENABLE_ZMQ #include "zmq/zmqnotificationinterface.h" #endif static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr g_connman; std::unique_ptr peerLogic; #if !(ENABLE_WALLET) class DummyWalletInit : public WalletInitInterface { public: std::string GetHelpString(bool showDebug) override { return std::string{}; } bool ParameterInteraction() override { return true; } void RegisterRPC(CRPCTable &) override {} bool Verify(const CChainParams &chainParams) override { return true; } bool Open(const CChainParams &chainParams) override { return true; } void Start(CScheduler &scheduler) override {} void Flush() override {} void Stop() override {} void Close() override {} }; static DummyWalletInit g_dummy_wallet_init; WalletInitInterface *const g_wallet_init_interface = &g_dummy_wallet_init; #endif #if ENABLE_ZMQ static CZMQNotificationInterface *pzmqNotificationInterface = nullptr; #endif #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing // block files don't count towards the fd_set size limit anyway. #define MIN_CORE_FILEDESCRIPTORS 0 #else #define MIN_CORE_FILEDESCRIPTORS 150 #endif ////////////////////////////////////////////////////////////////////////////// // // Shutdown // // // Thread management and startup/shutdown: // // The network-processing threads are all part of a thread group created by // AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM signal handler sets // fRequestShutdown, which triggers the DetectShutdownThread(), which interrupts // the main thread group. DetectShutdownThread() then exits, which causes // AppInit() to continue (it .joins the shutdown thread). Shutdown() is then // called to clean up database connections, and stop other threads that should // only be stopped after the main network-processing threads have exited. // // Note that if running -daemon the parent process returns from AppInit2 before // adding any threads to the threadGroup, so .join_all() returns immediately and // the parent exits from main(). // // Shutdown for Qt is very similar, only it uses a QTimer to detect // fRequestShutdown getting set, and then does the normal Qt shutdown thing. // std::atomic fRequestShutdown(false); std::atomic fDumpMempoolLater(false); void StartShutdown() { fRequestShutdown = true; } bool ShutdownRequested() { return fRequestShutdown; } /** * This is a minimally invasive approach to shutdown on LevelDB read errors from * the chainstate, while keeping user interface out of the common library, which * is shared between bitcoind, and bitcoin-qt and non-server tools. */ class CCoinsViewErrorCatcher final : public CCoinsViewBacked { public: explicit CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {} bool GetCoin(const COutPoint &outpoint, Coin &coin) const override { try { return CCoinsViewBacked::GetCoin(outpoint, coin); } catch (const std::runtime_error &e) { uiInterface.ThreadSafeMessageBox( _("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); LogPrintf("Error reading from database: %s\n", e.what()); // Starting the shutdown sequence and returning false to the caller // would be interpreted as 'entry not found' (as opposed to unable // to read data), and could lead to invalid interpretation. Just // exit immediately, as we can't continue anyway, and all writes // should be atomic. abort(); } } // Writes do not need similar protection, as failure to write is handled by // the caller. }; static std::unique_ptr pcoinscatcher; static std::unique_ptr globalVerifyHandle; static boost::thread_group threadGroup; static CScheduler scheduler; void Interrupt() { InterruptHTTPServer(); InterruptHTTPRPC(); InterruptRPC(); InterruptREST(); InterruptTorControl(); InterruptMapPort(); if (g_connman) { g_connman->Interrupt(); } } void Shutdown() { LogPrintf("%s: In progress...\n", __func__); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) { return; } /// Note: Shutdown() must be able to handle cases in which AppInit2() failed /// part of the way, for example if the data directory was found to be /// locked. Be sure that anything that writes files or flushes caches only /// does this if the respective module was initialized. RenameThread("bitcoin-shutoff"); g_mempool.AddTransactionsUpdated(1); StopHTTPRPC(); StopREST(); StopRPC(); StopHTTPServer(); g_wallet_init_interface->Flush(); StopMapPort(); // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. if (peerLogic) { UnregisterValidationInterface(peerLogic.get()); } if (g_connman) { g_connman->Stop(); } peerLogic.reset(); g_connman.reset(); StopTorControl(); // After everything has been shut down, but before things get flushed, stop // the CScheduler/checkqueue threadGroup threadGroup.interrupt_all(); threadGroup.join_all(); if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(); } // FlushStateToDisk generates a SetBestChain callback, which we should avoid // missing if (pcoinsTip != nullptr) { FlushStateToDisk(); } // After there are no more peers/RPC left to give us new data which may // generate CValidationInterface callbacks, flush them... GetMainSignals().FlushBackgroundCallbacks(); // Any future callbacks will be dropped. This should absolutely be safe - if // missing a callback results in an unrecoverable situation, unclean // shutdown would too. The only reason to do the above flushes is to let the // wallet catch up with our current chain to avoid any strange pruning edge // cases and make next startup faster by avoiding rescan. { LOCK(cs_main); if (pcoinsTip != nullptr) { FlushStateToDisk(); } pcoinsTip.reset(); pcoinscatcher.reset(); pcoinsdbview.reset(); pblocktree.reset(); } g_wallet_init_interface->Stop(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { UnregisterValidationInterface(pzmqNotificationInterface); delete pzmqNotificationInterface; pzmqNotificationInterface = nullptr; } #endif #ifndef WIN32 try { fs::remove(GetPidFile()); } catch (const fs::filesystem_error &e) { LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); } #endif UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterWithMempoolSignals(g_mempool); g_wallet_init_interface->Close(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); } /** * Signal handlers are very limited in what they are allowed to do, so: */ void HandleSIGTERM(int) { fRequestShutdown = true; } void HandleSIGHUP(int) { GetLogger().m_reopen_file = true; } void OnRPCStarted() { uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange); } void OnRPCStopped() { uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); RPCNotifyBlockChange(false, nullptr); g_best_block_cv.notify_all(); LogPrint(BCLog::RPC, "RPC stopped.\n"); } std::string HelpMessage(HelpMessageMode mode) { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); const bool showDebug = gArgs.GetBoolArg("-help-debug", false); // When adding new options to the categories, please keep and ensure // alphabetical ordering. Do not translate _(...) -help-debug options, Many // technical terms, and only a very small audience, so is unnecessary stress // to translators. std::string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt( "-alertnotify=", _("Execute command when a relevant alert is received or we see a " "really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes " "(%s in cmd is replaced by block hash)")); if (showDebug) { strUsage += HelpMessageOpt( "-blocksonly", strprintf( _("Whether to operate in a blocks only mode (default: %d)"), DEFAULT_BLOCKSONLY)); } strUsage += HelpMessageOpt( "-assumevalid=", strprintf( _("If this block is in the chain assume that it and its ancestors " "are valid and potentially skip their script verification (0 to " "verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex())); strUsage += HelpMessageOpt( "-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); if (mode == HelpMessageMode::BITCOIND) { #if HAVE_DECL_DAEMON strUsage += HelpMessageOpt( "-daemon", _("Run in the background as a daemon and accept commands")); #endif } strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); if (showDebug) { strUsage += HelpMessageOpt( "-dbbatchsize", strprintf( "Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize)); } strUsage += HelpMessageOpt( "-dbcache=", strprintf( _("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); if (showDebug) { strUsage += HelpMessageOpt( "-feefilter", strprintf("Tell other nodes to filter invs to us by " "our mempool min fee (default: %d)", DEFAULT_FEEFILTER)); } strUsage += HelpMessageOpt( "-finalizationdelay=", strprintf("Set the minimum amount of time to wait between a " "block header reception and the block finalization. " "Unit is seconds (default: %d)", DEFAULT_MIN_FINALIZATION_DELAY)); strUsage += HelpMessageOpt( "-maxreorgdepth=", strprintf("Configure at what depth blocks are considered final " "(default: %d). Use -1 to disable.", DEFAULT_MAX_REORG_DEPTH)); strUsage += HelpMessageOpt( "-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt( "-debuglogfile=", strprintf( _("Specify location of debug log file: this can be an absolute " "path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt( "-maxorphantx=", strprintf(_("Keep at most unconnectable " "transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool " "below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool " "longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); if (showDebug) { strUsage += HelpMessageOpt( "-minimumchainwork=", strprintf( "Minimum work assumed to exist on a valid chain in hex " "(default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex())); } strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown " "and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); strUsage += HelpMessageOpt( "-blockreconstructionextratxn=", strprintf(_("Extra transactions to keep in memory for compact block " "reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); strUsage += HelpMessageOpt( "-par=", strprintf(_("Set the number of script verification threads (%u to %d, " "0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 strUsage += HelpMessageOpt( "-pid=", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); #endif strUsage += HelpMessageOpt( "-prune=", strprintf( _("Reduce storage requirements by enabling pruning (deleting) of " "old blocks. This allows the pruneblockchain RPC to be called to " "delete specific blocks, and enables automatic pruning of old " "blocks if a target size in MiB is provided. This mode is " "incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the " "entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning " "via RPC, >%u = automatically prune block files to stay under " "the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); strUsage += HelpMessageOpt( "-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from " "the blk*.dat files on disk")); #ifndef WIN32 strUsage += HelpMessageOpt( "-sysperms", _("Create new files with system default permissions, instead of umask " "077 (only effective with disabled wallet functionality)")); #endif strUsage += HelpMessageOpt( "-txindex", strprintf(_("Maintain a full transaction index, used by " "the getrawtransaction rpc call (default: %d)"), DEFAULT_TXINDEX)); strUsage += HelpMessageOpt( "-usecashaddr", _("Use Cash Address for destination encoding instead " "of base58 (activate by default on Jan, 14)")); strUsage += HelpMessageGroup(_("Connection options:")); strUsage += HelpMessageOpt( "-addnode=", _("Add a node to connect to and attempt to keep the connection open " "(see the `addnode` RPC command help for more info)")); strUsage += HelpMessageOpt( "-banscore=", strprintf( _("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); strUsage += HelpMessageOpt( "-bantime=", strprintf(_("Number of seconds to keep misbehaving " "peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); strUsage += HelpMessageOpt("-bind=", _("Bind to given address and always listen on " "it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt( "-connect=", _("Connect only to the specified node(s); -connect=0 " "disables automatic connections (the rules for this " "peer are the same as for -addnode)")); strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when " "listening and no -externalip or -proxy)")); strUsage += HelpMessageOpt( "-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %d)"), DEFAULT_NAME_LOOKUP)); strUsage += HelpMessageOpt( "-dnsseed", _("Query for peer addresses via DNS lookup, if low on " "addresses (default: 1 unless -connect/-noconnect)")); strUsage += HelpMessageOpt("-externalip=", _("Specify your own public address")); strUsage += HelpMessageOpt( "-forcednsseed", strprintf( _("Always query for peer addresses via DNS lookup (default: %d)"), DEFAULT_FORCEDNSSEED)); strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: " "1 if no -proxy or -connect/-noconnect)")); strUsage += HelpMessageOpt( "-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION)); strUsage += HelpMessageOpt( "-maxconnections=", strprintf(_("Maintain at most connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS)); strUsage += HelpMessageOpt("-maxreceivebuffer=", strprintf(_("Maximum per-connection receive buffer, " "*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER)); strUsage += HelpMessageOpt( "-maxsendbuffer=", strprintf(_("Maximum per-connection send buffer, " "*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER)); strUsage += HelpMessageOpt( "-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local " "perspective of time may be influenced by peers forward or " "backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT)); strUsage += HelpMessageOpt("-onion=", strprintf(_("Use separate SOCKS5 proxy to reach peers " "via Tor hidden services (default: %s)"), "-proxy")); strUsage += HelpMessageOpt( "-onlynet=", _("Only connect to nodes in network (ipv4, ipv6 or onion)")); strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %d)"), DEFAULT_PERMIT_BAREMULTISIG)); strUsage += HelpMessageOpt( "-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom " "filters (default: %d)"), DEFAULT_PEERBLOOMFILTERS)); strUsage += HelpMessageOpt( "-port=", strprintf( _("Listen for connections on (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort())); strUsage += HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy")); strUsage += HelpMessageOpt( "-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This " "enables Tor stream isolation (default: %d)"), DEFAULT_PROXYRANDOMIZE)); strUsage += HelpMessageOpt( "-seednode=", _("Connect to a node to retrieve peer addresses, and disconnect")); strUsage += HelpMessageOpt( "-timeout=", strprintf(_("Specify connection timeout in " "milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion " "listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); #ifdef USE_UPNP #if USE_UPNP strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port " "(default: 1 when listening and no -proxy)")); #else strUsage += HelpMessageOpt( "-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0)); #endif #endif strUsage += HelpMessageOpt("-whitebind=", _("Bind to given address and whitelist peers connecting " "to it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt( "-whitelist=", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) " "or CIDR notated network (e.g. 1.2.3.0/24). Can be specified " "multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions " "are always relayed, even if they are already in the mempool, " "useful e.g. for a gateway")); strUsage += HelpMessageOpt( "-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted " "peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); strUsage += HelpMessageOpt( "-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even " "if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); strUsage += HelpMessageOpt( "-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in " "MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); strUsage += g_wallet_init_interface->GetHelpString(showDebug); #if ENABLE_ZMQ strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); strUsage += HelpMessageOpt("-zmqpubhashblock=
", _("Enable publish hash block in
")); strUsage += HelpMessageOpt("-zmqpubhashtx=
", _("Enable publish hash transaction in
")); strUsage += HelpMessageOpt("-zmqpubrawblock=
", _("Enable publish raw block in
")); strUsage += HelpMessageOpt("-zmqpubrawtx=
", _("Enable publish raw transaction in
")); #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); strUsage += HelpMessageOpt("-uacomment=", _("Append comment to the user agent string")); if (showDebug) { strUsage += HelpMessageOpt( "-checkblocks=", strprintf( _("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of " "-checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); strUsage += HelpMessageOpt( "-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, " "setBlockIndexCandidates, chainActive and " "mapBlocksUnlinked occasionally. Also sets -checkmempool " "(default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt( "-checkmempool=", strprintf("Run checks every transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt( "-checkpoints", strprintf("Only accept block chain matching " "built-in checkpoints (default: %d)", DEFAULT_CHECKPOINTS_ENABLED)); strUsage += HelpMessageOpt( "-disablesafemode", strprintf("Disable safemode, override a real " "safe mode event (default: %d)", DEFAULT_DISABLE_SAFEMODE)); strUsage += HelpMessageOpt("-deprecatedrpc=", "Allows deprecated RPC method(s) to be used"); strUsage += HelpMessageOpt( "-testsafemode", strprintf("Force safe mode (default: %d)", DEFAULT_TESTSAFEMODE)); strUsage += HelpMessageOpt("-dropmessagestest=", "Randomly drop 1 of every network messages"); strUsage += HelpMessageOpt( "-stopafterblockimport", strprintf( "Stop running after importing blocks from disk (default: %d)", DEFAULT_STOPAFTERBLOCKIMPORT)); strUsage += HelpMessageOpt( "-stopatheight", strprintf("Stop running after reaching the given " "height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT)); strUsage += HelpMessageOpt( "-limitancestorcount=", strprintf("Do not accept transactions if number of in-mempool " "ancestors is or more (default: %u)", DEFAULT_ANCESTOR_LIMIT)); strUsage += HelpMessageOpt("-limitancestorsize=", strprintf("Do not accept transactions whose size " "with all in-mempool ancestors exceeds " " kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT)); strUsage += HelpMessageOpt( "-limitdescendantcount=", strprintf("Do not accept transactions if any ancestor would have " " or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); strUsage += HelpMessageOpt( "-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have " "more than kilobytes of in-mempool descendants " "(default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost"); } strUsage += HelpMessageOpt( "-debug=", strprintf(_("Output debugging information (default: %u, supplying " " is optional)"), 0) + ". " + _("If is not supplied or if = 1, output all " "debugging information.") + _(" can be:") + " " + ListLogCategories() + "."); strUsage += HelpMessageOpt( "-debugexclude=", strprintf(_("Exclude debugging information for a category. Can be used " "in conjunction with -debug=1 to output debug logs for all " "categories except one or more specified categories."))); if (showDebug) { strUsage += HelpMessageOpt( "-nodebug", "Turn off debugging messages, same as -debug=0"); } strUsage += HelpMessageOpt( "-help-debug", _("Show all debugging options (usage: --help -help-debug)")); strUsage += HelpMessageOpt( "-logips", strprintf(_("Include IP addresses in debug output (default: %d)"), DEFAULT_LOGIPS)); strUsage += HelpMessageOpt( "-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %d)"), DEFAULT_LOGTIMESTAMPS)); if (showDebug) { strUsage += HelpMessageOpt( "-logtimemicros", strprintf( "Add microsecond precision to debug timestamps (default: %d)", DEFAULT_LOGTIMEMICROS)); strUsage += HelpMessageOpt( "-mocktime=", "Replace actual time with seconds since epoch (default: 0)"); strUsage += HelpMessageOpt( "-limitfreerelay=", strprintf("Continuously rate-limit free transactions to *1000 " "bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY)); strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free " "or low-fee transactions (default: %d)", DEFAULT_RELAYPRIORITY)); strUsage += HelpMessageOpt( "-maxsigcachesize=", strprintf("Limit size of signature cache to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); strUsage += HelpMessageOpt( "-maxscriptcachesize=", strprintf("Limit size of script cache to MiB (default: %u)", DEFAULT_MAX_SCRIPT_CACHE_SIZE)); strUsage += HelpMessageOpt( "-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial " "block download (default: %u)", DEFAULT_MAX_TIP_AGE)); } strUsage += HelpMessageOpt( "-excessutxocharge=", strprintf(_("Fees (in %s/kB) to charge per utxo created for" "relaying, and mining (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_UTXO_FEE))); strUsage += HelpMessageOpt( "-minrelaytxfee=", strprintf( _("Fees (in %s/kB) smaller than this are considered zero fee for " "relaying, mining and transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB))); strUsage += HelpMessageOpt( "-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet " "transaction or raw transaction; setting this too low may " "abort large transactions (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); strUsage += HelpMessageOpt( "-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); if (showDebug) { strUsage += HelpMessageOpt( "-printpriority", strprintf("Log transaction priority and fee per " "kB when mining blocks (default: %d)", DEFAULT_PRINTPRIORITY)); } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup " "(default: 1 when no -debug)")); AppendParamsHelpMessages(strUsage, showDebug); strUsage += HelpMessageGroup(_("Node relay options:")); if (showDebug) { strUsage += HelpMessageOpt( "-acceptnonstdtxn", strprintf( "Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard())); strUsage += HelpMessageOpt("-excessiveblocksize=", strprintf(_("Do not accept blocks larger than this " "limit, in bytes (default: %d)"), DEFAULT_MAX_BLOCK_SIZE)); strUsage += HelpMessageOpt( "-dustrelayfee=", strprintf("Fee rate (in %s/kB) used to defined dust, the value of " "an output such that it will cost about 1/3 of its value " "in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); } strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions " "for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); strUsage += HelpMessageOpt( "-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %d)"), DEFAULT_ACCEPT_DATACARRIER)); strUsage += HelpMessageOpt( "-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we " "relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt( "-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_MAX_GENERATED_BLOCK_SIZE)); strUsage += HelpMessageOpt( "-blockprioritypercentage=", strprintf(_("Set maximum percentage of a block reserved to " "high-priority/low-fee transactions (default: %d)"), DEFAULT_BLOCK_PRIORITY_PERCENTAGE)); strUsage += HelpMessageOpt( "-blockmintxfee=", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be " "included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB))); if (showDebug) { strUsage += HelpMessageOpt("-blockversion=", "Override block version to test forking scenarios"); } strUsage += HelpMessageGroup(_("RPC server options:")); strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt( "-rest", strprintf(_("Accept public REST requests (default: %d)"), DEFAULT_REST_ENABLE)); strUsage += HelpMessageOpt( "-rpcbind=", _("Bind to given address to listen for JSON-RPC connections. Use " "[host]:port notation for IPv6. This option can be specified " "multiple times (default: bind to all interfaces)")); strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie (default: data dir)")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt( "-rpcauth=", _("Username and hashed password for JSON-RPC connections. The field " " comes in the format: :$. A canonical " "python script is included in share/rpcuser. The client then " "connects normally using the " "rpcuser=/rpcpassword= pair of arguments. This " "option can be specified multiple times")); strUsage += HelpMessageOpt( "-rpcport=", strprintf(_("Listen for JSON-RPC connections on (default: %u or " "testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); strUsage += HelpMessageOpt( "-rpcallowip=", _("Allow JSON-RPC connections from specified source. Valid for " "are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. " "1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This " "option can be specified multiple times")); strUsage += HelpMessageOpt( "-rpcthreads=", strprintf( _("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); strUsage += HelpMessageOpt( "-rpccorsdomain=value", "Domain from which to accept cross origin requests (browser enforced)"); if (showDebug) { strUsage += HelpMessageOpt( "-rpcworkqueue=", strprintf("Set the depth of the work queue to " "service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); strUsage += HelpMessageOpt( "-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); } return strUsage; } std::string LicenseInfo() { const std::string URL_SOURCE_CODE = ""; const std::string URL_WEBSITE = ""; return CopyrightHolders( strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" + "\n" + strprintf(_("Please contribute if you find %s useful. " "Visit %s for further information about the software."), PACKAGE_NAME, URL_WEBSITE) + "\n" + strprintf(_("The source code is available from %s."), URL_SOURCE_CODE) + "\n" + "\n" + _("This is experimental software.") + "\n" + strprintf(_("Distributed under the MIT software license, see the " "accompanying file %s or %s"), "COPYING", "") + "\n" + "\n" + strprintf(_("This product includes software developed by the " "OpenSSL Project for use in the OpenSSL Toolkit %s and " "cryptographic software written by Eric Young and UPnP " "software written by Thomas Bernard."), "") + "\n"; } static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex) { if (initialSync || !pBlockIndex) { return; } std::string strCmd = gArgs.GetArg("-blocknotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); std::thread t(runCommand, strCmd); // thread runs free t.detach(); } } static bool fHaveGenesis = false; static CWaitableCriticalSection cs_GenesisWait; static CConditionVariable condvar_GenesisWait; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != nullptr) { { LOCK(cs_GenesisWait); fHaveGenesis = true; } condvar_GenesisWait.notify_all(); } } struct CImportingNow { CImportingNow() { assert(fImporting == false); fImporting = true; } ~CImportingNow() { assert(fImporting == true); fImporting = false; } }; // If we're using -prune with -reindex, then delete block files that will be // ignored by the reindex. Since reindexing works by starting at block file 0 // and looping until a blockfile is missing, do the same here to delete any // later block files after a gap. Also delete all rev files since they'll be // rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync // with what's actually on disk by the time we start downloading, so that // pruning works correctly. void CleanupBlockRevFiles() { std::map mapBlockFiles; // Glob all blk?????.dat and rev?????.dat files from the blocks directory. // Remove the rev files immediately and insert the blk file paths into an // ordered map keyed by block file index. LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for " "-reindex with -prune\n"); fs::path blocksdir = GetDataDir() / "blocks"; for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { if (is_regular_file(*it) && it->path().filename().string().length() == 12 && it->path().filename().string().substr(8, 4) == ".dat") { if (it->path().filename().string().substr(0, 3) == "blk") { mapBlockFiles[it->path().filename().string().substr(3, 5)] = it->path(); } else if (it->path().filename().string().substr(0, 3) == "rev") { remove(it->path()); } } } // Remove all block files that aren't part of a contiguous set starting at // zero by walking the ordered map (keys are block file indices) by keeping // a separate counter. Once we hit a gap (or if 0 doesn't exist) start // removing block files. int nContigCounter = 0; for (const std::pair &item : mapBlockFiles) { if (atoi(item.first) == nContigCounter) { nContigCounter++; continue; } remove(item.second); } } void ThreadImport(const Config &config, std::vector vImportFiles) { RenameThread("bitcoin-loadblk"); { CImportingNow imp; // -reindex if (fReindex) { int nFile = 0; while (true) { CDiskBlockPos pos(nFile, 0); if (!fs::exists(GetBlockPosFilename(pos, "blk"))) { // No block files left to reindex break; } FILE *file = OpenBlockFile(pos, true); if (!file) { // This error is logged in OpenBlockFile break; } LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); LoadExternalBlockFile(config, file, &pos); nFile++; } pblocktree->WriteReindexing(false); fReindex = false; LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try // initializing (no-op if reindexing worked): LoadGenesisBlock(config.GetChainParams()); } // hardcoded $DATADIR/bootstrap.dat fs::path pathBootstrap = GetDataDir() / "bootstrap.dat"; if (fs::exists(pathBootstrap)) { FILE *file = fsbridge::fopen(pathBootstrap, "rb"); if (file) { fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LogPrintf("Importing bootstrap.dat...\n"); LoadExternalBlockFile(config, file); RenameOver(pathBootstrap, pathBootstrapOld); } else { LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string()); } } // -loadblock= for (const fs::path &path : vImportFiles) { FILE *file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); LoadExternalBlockFile(config, file); } else { LogPrintf("Warning: Could not open blocks file %s\n", path.string()); } } // scan for better chains in the block chain database, that are not yet // connected in the active best chain CValidationState state; if (!ActivateBestChain(config, state)) { LogPrintf("Failed to connect best block"); StartShutdown(); } if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); } } // End scope of CImportingNow if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { LoadMempool(config); fDumpMempoolLater = !fRequestShutdown; } } /** Sanity checks * Ensure that Bitcoin is running in a usable environment with all * necessary library support. */ bool InitSanityCheck(void) { if (!ECC_InitSanityCheck()) { InitError( "Elliptic curve cryptography sanity check failure. Aborting."); return false; } if (!glibc_sanity_test() || !glibcxx_sanity_test()) { return false; } if (!Random_SanityCheck()) { InitError("OS cryptographic RNG sanity check failure. Aborting."); return false; } return true; } static bool AppInitServers(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { RPCServerSignals::OnStarted(&OnRPCStarted); RPCServerSignals::OnStopped(&OnRPCStopped); if (!InitHTTPServer(config)) { return false; } if (!StartRPC()) { return false; } if (!StartHTTPRPC(config, httpRPCRequestProcessor)) { return false; } if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) { return false; } if (!StartHTTPServer()) { return false; } return true; } // Parameter interaction based on rules void InitParameterInteraction() { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified. if (gArgs.IsArgSet("-bind")) { if (gArgs.SoftSetBoolArg("-listen", true)) { LogPrintf( "%s: parameter interaction: -bind set -> setting -listen=1\n", __func__); } } if (gArgs.IsArgSet("-whitebind")) { if (gArgs.SoftSetBoolArg("-listen", true)) { LogPrintf("%s: parameter interaction: -whitebind set -> setting " "-listen=1\n", __func__); } } if (gArgs.IsArgSet("-connect")) { // when only connecting to trusted nodes, do not seed via DNS, or listen // by default. if (gArgs.SoftSetBoolArg("-dnsseed", false)) { LogPrintf("%s: parameter interaction: -connect set -> setting " "-dnsseed=0\n", __func__); } if (gArgs.SoftSetBoolArg("-listen", false)) { LogPrintf("%s: parameter interaction: -connect set -> setting " "-listen=0\n", __func__); } } if (gArgs.IsArgSet("-proxy")) { // to protect privacy, do not listen by default if a default proxy // server is specified. if (gArgs.SoftSetBoolArg("-listen", false)) { LogPrintf( "%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); } // to protect privacy, do not use UPNP when a proxy is set. The user may // still specify -listen=1 to listen locally, so don't rely on this // happening through -listen below. if (gArgs.SoftSetBoolArg("-upnp", false)) { LogPrintf( "%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); } // to protect privacy, do not discover addresses by default if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf("%s: parameter interaction: -proxy set -> setting " "-discover=0\n", __func__); } } if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { // do not map ports or try to retrieve public IP when not listening // (pointless) if (gArgs.SoftSetBoolArg("-upnp", false)) { LogPrintf( "%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); } if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf( "%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); } if (gArgs.SoftSetBoolArg("-listenonion", false)) { LogPrintf("%s: parameter interaction: -listen=0 -> setting " "-listenonion=0\n", __func__); } } if (gArgs.IsArgSet("-externalip")) { // if an explicit public IP is specified, do not try to find others if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf("%s: parameter interaction: -externalip set -> setting " "-discover=0\n", __func__); } } // disable whitelistrelay in blocksonly mode if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting " "-whitelistrelay=0\n", __func__); } } // Forcing relay from whitelisted hosts implies we will accept relays from // them in the first place. if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) { LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> " "setting -whitelistrelay=1\n", __func__); } } // Warn if network-specific options (-addnode, -connect, etc) are // specified in default section of config file, but not overridden // on the command line or in this network's section of the config file. gArgs.WarnForSectionOnlyArgs(); } static std::string ResolveErrMsg(const char *const optname, const std::string &strBind) { return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } void InitLogging() { BCLog::Logger &logger = GetLogger(); logger.m_print_to_console = gArgs.GetBoolArg("-printtoconsole", false); logger.m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); logger.m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion()); } namespace { // Variables internal to initialization process only int nMaxConnections; int nUserMaxConnections; int nFD; ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED); } // namespace [[noreturn]] static void new_handler_terminate() { // Rather than throwing std::bad-alloc if allocation fails, terminate // immediately to (try to) avoid chain corruption. Since LogPrintf may // itself allocate memory, set the handler directly to terminate first. std::set_new_handler(std::terminate); LogPrintf("Error: Out of memory. Terminating.\n"); // The log was successful, terminate now. std::terminate(); }; bool AppInitBasicSetup() { // Step 1: setup #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); #endif #if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= // 0x0601 (Windows 7), which is not correct. Can be removed, when GCCs winbase.h // is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL(WINAPI * PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress( GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); if (setProcDEPPol != nullptr) { setProcDEPPol(PROCESS_DEP_ENABLE); } #endif if (!SetupNetworking()) { return InitError("Initializing networking failed"); } #ifndef WIN32 if (!gArgs.GetBoolArg("-sysperms", false)) { umask(077); } // Clean shutdown on SIGTERM struct sigaction sa; sa.sa_handler = HandleSIGTERM; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, nullptr); sigaction(SIGINT, &sa, nullptr); // Reopen debug.log on SIGHUP struct sigaction sa_hup; sa_hup.sa_handler = HandleSIGHUP; sigemptyset(&sa_hup.sa_mask); sa_hup.sa_flags = 0; sigaction(SIGHUP, &sa_hup, nullptr); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client // closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif std::set_new_handler(new_handler_terminate); return true; } bool AppInitParameterInteraction(Config &config, RPCServer &rpcServer) { const CChainParams &chainparams = config.GetChainParams(); // Step 2: parameter interactions // also see: InitParameterInteraction() // if using block pruning, then disallow txindex if (gArgs.GetArg("-prune", 0)) { if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { return InitError(_("Prune mode is incompatible with -txindex.")); } } // if space reserved for high priority transactions is misconfigured // stop program execution and warn the user with a proper error message const int64_t blkprio = gArgs.GetArg("-blockprioritypercentage", DEFAULT_BLOCK_PRIORITY_PERCENTAGE); if (!config.SetBlockPriorityPercentage(blkprio)) { return InitError(_("Block priority percentage has to belong to the " "[0..100] interval.")); } // -bind and -whitebind can't be set when not listening size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size(); if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { return InitError( "Cannot set -bind or -whitebind together with -listen=0"); } // Make sure enough file descriptors are available int nBind = std::max(nUserBind, size_t(1)); nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0); nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); if (nFD < MIN_CORE_FILEDESCRIPTORS) { return InitError(_("Not enough file descriptors available.")); } nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections); if (nMaxConnections < nUserMaxConnections) { InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, " "because of system limitations."), nUserMaxConnections, nMaxConnections)); } // Step 3: parameter-to-internal-flags if (gArgs.IsArgSet("-debug")) { // Special-case: if -debug=0/-nodebug is set, turn off debugging // messages const std::vector &categories = gArgs.GetArgs("-debug"); if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) { for (const auto &cat : categories) { BCLog::LogFlags flag; if (!GetLogCategory(flag, cat)) { InitWarning( strprintf(_("Unsupported logging category %s=%s."), "-debug", cat)); } GetLogger().EnableCategory(flag); } } } // Now remove the logging categories which were explicitly excluded for (const std::string &cat : gArgs.GetArgs("-debugexclude")) { BCLog::LogFlags flag; if (!GetLogCategory(flag, cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); } GetLogger().DisableCategory(flag); } // Check for -debugnet if (gArgs.GetBoolArg("-debugnet", false)) { InitWarning( _("Unsupported argument -debugnet ignored, use -debug=net.")); } // Check for -socks - as this is a privacy risk to continue, exit here if (gArgs.IsArgSet("-socks")) { return InitError( _("Unsupported argument -socks found. Setting SOCKS version isn't " "possible anymore, only SOCKS5 proxies are supported.")); } // Check for -tor - as this is a privacy risk to continue, exit here if (gArgs.GetBoolArg("-tor", false)) { return InitError(_("Unsupported argument -tor found, use -onion.")); } if (gArgs.GetBoolArg("-benchmark", false)) { InitWarning( _("Unsupported argument -benchmark ignored, use -debug=bench.")); } if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) { InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use " "-whitelistrelay and/or -whitelistforcerelay.")); } if (gArgs.IsArgSet("-blockminsize")) { InitWarning("Unsupported argument -blockminsize ignored."); } // Checkmempool and checkblockindex default to true in regtest mode int ratio = std::min( std::max( gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); if (ratio != 0) { g_mempool.setSanityCheck(1.0 / ratio); } fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); if (fCheckpointsEnabled) { LogPrintf("Checkpoints will be verified.\n"); } else { LogPrintf("Skipping checkpoint verification.\n"); } hashAssumeValid = uint256S( gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); if (!hashAssumeValid.IsNull()) { LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); } else { LogPrintf("Validating signatures for all blocks.\n"); } if (gArgs.IsArgSet("-minimumchainwork")) { const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", ""); if (!IsHexNumber(minChainWorkStr)) { return InitError(strprintf( "Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr)); } nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); } else { nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork); } LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex()); } // mempool limits int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) { return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); } // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) { nScriptCheckThreads += GetNumCores(); } if (nScriptCheckThreads <= 1) { nScriptCheckThreads = 0; } else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) { nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; } // Configure excessive block size. const uint64_t nProposedExcessiveBlockSize = gArgs.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE); if (!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) { return InitError( _("Excessive block size must be > 1,000,000 bytes (1MB)")); } // Check blockmaxsize does not exceed maximum accepted block size. const uint64_t nProposedMaxGeneratedBlockSize = gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE); if (nProposedMaxGeneratedBlockSize > config.GetMaxBlockSize()) { auto msg = _("Max generated block size (blockmaxsize) cannot exceed " "the excessive block size (excessiveblocksize)"); return InitError(msg); } // block pruning; get the amount of disk space (in MiB) to allot for block & // undo files int64_t nPruneArg = gArgs.GetArg("-prune", 0); if (nPruneArg < 0) { return InitError( _("Prune cannot be configured with a negative value.")); } nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024; if (nPruneArg == 1) { // manual pruning: -prune=1 LogPrintf("Block pruning enabled. Use RPC call " "pruneblockchain(height) to manually prune block and undo " "files.\n"); nPruneTarget = std::numeric_limits::max(); fPruneMode = true; } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { return InitError( strprintf(_("Prune configured below the minimum of %d MiB. " "Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } LogPrintf("Prune configured to target %uMiB on disk for block and undo " "files.\n", nPruneTarget / 1024 / 1024); fPruneMode = true; } RegisterAllRPCCommands(config, rpcServer, tableRPC); g_wallet_init_interface->RegisterRPC(tableRPC); #ifdef ENABLE_WALLET RegisterDumpRPCCommands(tableRPC); #endif nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) { nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; } // Obtain the amount to charge excess UTXO if (gArgs.IsArgSet("-excessutxocharge")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-excessutxocharge", ""), n); if (!parsed || Amount::zero() > n) { return InitError(AmountErrMsg( "excessutxocharge", gArgs.GetArg("-excessutxocharge", ""))); } config.SetExcessUTXOCharge(n); } else { config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE); } // Fee-per-kilobyte amount considered the same as "free". If you are mining, // be careful setting this: if you set it to zero then a transaction spammer // can cheaply fill blocks using 1-satoshi-fee transactions. It should be // set above the real cost to you of processing a transaction. if (gArgs.IsArgSet("-minrelaytxfee")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n); if (!parsed || Amount::zero() == n) { return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } // High fee check is done afterward in WalletParameterInteraction() config.SetMinFeePerKB(CFeeRate(n)); } else { config.SetMinFeePerKB(CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)); } // Sanity check argument for min fee for including tx in block // TODO: Harmonize which arguments need sanity checking and where that // happens. if (gArgs.IsArgSet("-blockmintxfee")) { Amount n = Amount::zero(); if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) { return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); } } // Feerate used to define dust. Shouldn't be changed lightly as old // implementations may inadvertently create non-standard transactions. if (gArgs.IsArgSet("-dustrelayfee")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n); if (!parsed || Amount::zero() == n) { return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); } dustRelayFee = CFeeRate(n); } fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (chainparams.RequireStandard() && !fRequireStandard) { return InitError( strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); } nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); if (!g_wallet_init_interface->ParameterInteraction()) { return false; } fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); // Option to startup with mocktime set (used for regression testing): SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) { nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); } // Signal Bitcoin Cash support. // TODO: remove some time after the hardfork when no longer needed // to differentiate the network nodes. nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH); nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); return true; } static bool LockDataDirectory(bool probeOnly) { std::string strDataDir = GetDataDir().string(); // Make sure only a single Bitcoin process is using the data directory. fs::path pathLockFile = GetDataDir() / ".lock"; // empty lock file; created if it doesn't exist. FILE *file = fsbridge::fopen(pathLockFile, "a"); if (file) { fclose(file); } try { static boost::interprocess::file_lock lock( pathLockFile.string().c_str()); if (!lock.try_lock()) { return InitError( strprintf(_("Cannot obtain a lock on data directory %s. %s is " "probably already running."), strDataDir, _(PACKAGE_NAME))); } if (probeOnly) { lock.unlock(); } } catch (const boost::interprocess::interprocess_exception &e) { return InitError(strprintf(_("Cannot obtain a lock on data directory " "%s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what())); } return true; } bool AppInitSanityChecks() { // Step 4: sanity checks // Initialize elliptic curve code std::string sha256_algo = SHA256AutoDetect(); LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo); RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); // Sanity check if (!InitSanityCheck()) { return InitError(strprintf( _("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); } // Probe the data directory lock to give an early error message, if possible // We cannot hold the data directory lock here, as the forking for daemon() // hasn't yet happened, and a fork will cause weird behavior to it. return LockDataDirectory(true); } bool AppInitLockDataDirectory() { // After daemonization get the data directory lock again and hold on to it // until exit. This creates a slight window for a race condition to happen, // however this condition is harmless: it will at most make us exit without // printing a message to console. if (!LockDataDirectory(false)) { // Detailed error printed inside LockDataDirectory return false; } return true; } bool AppInitMain(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { // Step 4a: application initialization const CChainParams &chainparams = config.GetChainParams(); #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif BCLog::Logger &logger = GetLogger(); bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile(); if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) { // Do this first since it both loads a bunch of debug.log into memory, // and because this needs to happen before any other debug.log printing. logger.ShrinkDebugFile(); } if (logger.m_print_to_file) { if (!logger.OpenDebugLog()) { return InitError(strprintf("Could not open debug log file %s", logger.GetDebugLogPath().string())); } } if (!logger.m_log_timestamps) { LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); } LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf( "Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using at most %i automatic connections (%i file descriptors " "available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) { LogPrintf("Warning: relative datadir option '%s' specified, which will " "be interpreted relative to the current working directory " "'%s'. This is fragile, because if bitcoin is started in the " "future from a different location, it will be unable to " "locate the current data files. There could also be data " "loss if bitcoin is started while in a temporary " "directory.\n", gArgs.GetArg("-datadir", ""), fs::current_path().string()); } InitSignatureCache(); InitScriptExecutionCache(); LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { for (int i = 0; i < nScriptCheckThreads - 1; i++) { threadGroup.create_thread(&ThreadScriptCheck); } } // Start the lightweight task scheduler thread CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(boost::bind(&TraceThread, "scheduler", serviceLoop)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); GetMainSignals().RegisterWithMempoolSignals(g_mempool); /** * Start the RPC server. It will be started in "warmup" mode and not * process calls yet (but it will verify that the server is there and will * be ready later). Warmup mode will be completed when initialisation is * finished. */ if (gArgs.GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers(config, httpRPCRequestProcessor)) { return InitError( _("Unable to start HTTP server. See debug log for details.")); } } // Step 5: verify wallet database integrity if (!g_wallet_init_interface->Verify(chainparams)) { return false; } // Step 6: network initialization // Note that we absolutely cannot open any actual connections // until the very end ("start node") as the UTXO/block state // is not yet setup and may end up being set up twice if we // need to reindex later. assert(!g_connman); g_connman = std::unique_ptr( new CConnman(config, GetRand(std::numeric_limits::max()), GetRand(std::numeric_limits::max()))); CConnman &connman = *g_connman; peerLogic.reset(new PeerLogicValidation(&connman, scheduler)); RegisterValidationInterface(peerLogic.get()); // sanitize comments per BIP-0014, format user agent and check total size std::vector uacomments; for (const std::string &cmt : gArgs.GetArgs("-uacomment")) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) return InitError(strprintf( _("User Agent comment (%s) contains unsafe characters."), cmt)); uacomments.push_back(cmt); } const std::string strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { return InitError(strprintf( _("Total length of network version string (%i) exceeds maximum " "length (%i). Reduce the number or size of uacomments."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); } if (gArgs.IsArgSet("-onlynet")) { std::set nets; for (const std::string &snet : gArgs.GetArgs("-onlynet")) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) { return InitError(strprintf( _("Unknown network specified in -onlynet: '%s'"), snet)); } nets.insert(net); } for (int n = 0; n < NET_MAX; n++) { enum Network net = (enum Network)n; if (!nets.count(net)) SetLimited(net); } } // Check for host lookup allowed before parsing any network related // parameters fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); bool proxyRandomize = gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE); // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set // a proxy, this is the default std::string proxyArg = gArgs.GetArg("-proxy", ""); SetLimited(NET_TOR); if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) { return InitError(strprintf( _("Invalid -proxy address or hostname: '%s'"), proxyArg)); } proxyType addrProxy = proxyType(proxyAddr, proxyRandomize); if (!addrProxy.IsValid()) { return InitError(strprintf( _("Invalid -proxy address or hostname: '%s'"), proxyArg)); } SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); SetProxy(NET_TOR, addrProxy); SetNameProxy(addrProxy); // by default, -proxy sets onion as reachable, unless -noonion later SetLimited(NET_TOR, false); } // -onion can be used to set only a proxy for .onion, or override normal // proxy for .onion addresses. // -noonion (or -onion=0) disables connecting to .onion entirely. An empty // string is used to not override the onion proxy (in which case it defaults // to -proxy set above, or none) std::string onionArg = gArgs.GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 SetLimited(NET_TOR); // set onions as unreachable } else { CService onionProxy; if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) { return InitError(strprintf( _("Invalid -onion address or hostname: '%s'"), onionArg)); } proxyType addrOnion = proxyType(onionProxy, proxyRandomize); if (!addrOnion.IsValid()) { return InitError(strprintf( _("Invalid -onion address or hostname: '%s'"), onionArg)); } SetProxy(NET_TOR, addrOnion); SetLimited(NET_TOR, false); } } // see Step 2: parameter interactions for more information about these fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = gArgs.GetBoolArg("-discover", true); fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); for (const std::string &strAddr : gArgs.GetArgs("-externalip")) { CService addrLocal; if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) { AddLocal(addrLocal, LOCAL_MANUAL); } else { return InitError(ResolveErrMsg("externalip", strAddr)); } } #if ENABLE_ZMQ pzmqNotificationInterface = CZMQNotificationInterface::Create(); if (pzmqNotificationInterface) { RegisterValidationInterface(pzmqNotificationInterface); } #endif // unlimited unless -maxuploadtarget is set uint64_t nMaxOutboundLimit = 0; uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME; if (gArgs.IsArgSet("-maxuploadtarget")) { nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024; } // Step 7: load block chain fReindex = gArgs.GetBoolArg("-reindex", false); bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false); // cache size calculations int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be greater than nMaxDbcache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); int64_t nBlockTreeDBCache = nTotalCache / 8; nBlockTreeDBCache = std::min(nBlockTreeDBCache, (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); nTotalCache -= nBlockTreeDBCache; // use 25%-50% of the remainder for disk cache int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // cap total coins db cache nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); nTotalCache -= nCoinDBCache; // the rest goes to in-memory cache nCoinCacheUsage = nTotalCache; int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of " "unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); int64_t nStart = 0; bool fLoaded = false; while (!fLoaded && !fRequestShutdown) { bool fReset = fReindex; std::string strLoadError; uiInterface.InitMessage(_("Loading block index...")); LOCK(cs_main); nStart = GetTimeMillis(); do { try { UnloadBlockIndex(); pcoinsTip.reset(); pcoinsdbview.reset(); pcoinscatcher.reset(); pblocktree.reset( new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); if (fReindex) { pblocktree->WriteReindexing(true); // If we're reindexing in prune mode, wipe away unusable // block files and all undo data files if (fPruneMode) { CleanupBlockRevFiles(); } } if (fRequestShutdown) { break; } // LoadBlockIndex will load fTxIndex from the db, or set it if // we're reindexing. It will also load fHavePruned if we've // ever removed a block file from disk. if (!LoadBlockIndex(config)) { strLoadError = _("Error loading block database"); break; } // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way // around). if (!mapBlockIndex.empty() && !LookupBlockIndex( chainparams.GetConsensus().hashGenesisBlock)) { return InitError(_("Incorrect or no genesis block found. " "Wrong datadir for network?")); } // Check for changed -txindex state if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { strLoadError = _("You need to rebuild the database using " "-reindex-chainstate to change -txindex"); break; } // Check for changed -prune state. What we are concerned about // is a user who has pruned blocks in the past, but is now // trying to run unpruned. if (fHavePruned && !fPruneMode) { strLoadError = _("You need to rebuild the database using -reindex to " "go back to unpruned mode. This will redownload the " "entire blockchain"); break; } // At this point blocktree args are consistent with what's on // disk. If we're not mid-reindex (based on disk + args), add a // genesis block on disk. This is called again in ThreadImport // if the reindex completes. if (!fReindex && !LoadGenesisBlock(chainparams)) { strLoadError = _("Error initializing block database"); break; } // At this point we're either in reindex or we've loaded a // useful block tree into mapBlockIndex! pcoinsdbview.reset(new CCoinsViewDB( nCoinDBCache, false, fReset || fReindexChainState)); pcoinscatcher.reset( new CCoinsViewErrorCatcher(pcoinsdbview.get())); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex // or -reindex-chainstate if (!pcoinsdbview->Upgrade()) { strLoadError = _("Error upgrading chainstate database"); break; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with // -reindex or -reindex-chainstate if (!ReplayBlocks(config, pcoinsdbview.get())) { strLoadError = _("Unable to replay blocks. You will need to rebuild " "the database using -reindex-chainstate."); break; } // The on-disk coinsdb is now in a good state, create the cache pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get())); if (!fReindex && !fReindexChainState) { // LoadChainTip sets chainActive based on pcoinsTip's best // block if (!LoadChainTip(config)) { strLoadError = _("Error initializing block database"); break; } assert(chainActive.Tip() != nullptr); } if (!fReindex) { // Note that RewindBlockIndex MUST run even if we're about // to -reindex-chainstate. It both disconnects blocks based // on chainActive, and drops block data in mapBlockIndex // based on lack of available witness data. uiInterface.InitMessage(_("Rewinding blocks...")); if (!RewindBlockIndex(config)) { strLoadError = _("Unable to rewind the database to a " "pre-fork state. You will need to " "redownload the blockchain"); break; } } if (!fReindex && !fReindexChainState) { uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more " "than %d blocks; only checking available " "blocks", MIN_BLOCKS_TO_KEEP); } CBlockIndex *tip = chainActive.Tip(); RPCNotifyBlockChange(true, tip); if (tip && tip->nTime > GetAdjustedTime() + MAX_FUTURE_BLOCK_TIME) { strLoadError = _( "The block database contains a block which appears " "to be from the future. This may be due to your " "computer's date and time being set incorrectly. " "Only rebuild the block database if you are sure " "that your computer's date and time are correct"); break; } if (!CVerifyDB().VerifyDB( config, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { strLoadError = _("Corrupted block database detected"); break; } } } catch (const std::exception &e) { LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); break; } fLoaded = true; } while (false); if (!fLoaded && !fRequestShutdown) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), strLoadError + ".\nPlease restart with -reindex or " "-reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; fRequestShutdown = false; } else { LogPrintf("Aborted block database rebuild. Exiting.\n"); return false; } } else { return InitError(strLoadError); } } } // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly // overkill. if (fRequestShutdown) { LogPrintf("Shutdown requested. Exiting.\n"); return false; } LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); // Encoded addresses using cashaddr instead of base58 // Activates by default on Jan, 14 config.SetCashAddrEncoding( gArgs.GetBoolArg("-usecashaddr", GetAdjustedTime() > 1515900000)); // Step 8: load wallet if (!g_wallet_init_interface->Open(chainparams)) { return false; } // Step 9: data directory maintenance // if pruning, unset the service bit and perform the initial blockstore // prune after any wallet rescanning has taken place. if (fPruneMode) { LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); if (!fReindex) { uiInterface.InitMessage(_("Pruning blockstore...")); PruneAndFlush(); } } // Step 10: import blocks if (!CheckDiskSpace()) { return false; } // Either install a handler to notify us when genesis activates, or set // fHaveGenesis directly. // No locking, as this happens before any background thread is started. if (chainActive.Tip() == nullptr) { uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); } else { fHaveGenesis = true; } if (gArgs.IsArgSet("-blocknotify")) { uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); } std::vector vImportFiles; for (const std::string &strFile : gArgs.GetArgs("-loadblock")) { vImportFiles.push_back(strFile); } threadGroup.create_thread( boost::bind(&ThreadImport, std::ref(config), vImportFiles)); // Wait for genesis block to be processed { WAIT_LOCK(cs_GenesisWait, lock); // We previously could hang here if StartShutdown() is called prior to // ThreadImport getting started, so instead we just wait on a timer to // check ShutdownRequested() regularly. while (!fHaveGenesis && !ShutdownRequested()) { condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } // Step 11: start node int chain_active_height; //// debug print { LOCK(cs_main); LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); chain_active_height = chainActive.Height(); } LogPrintf("nBestHeight = %d\n", chain_active_height); if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) { StartTorControl(); } Discover(); // Map ports with UPnP if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) { StartMapPort(); } CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; connOptions.nMaxConnections = nMaxConnections; connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = 1; connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000 * gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000 * gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); connOptions.m_added_nodes = gArgs.GetArgs("-addnode"); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; for (const std::string &strBind : gArgs.GetArgs("-bind")) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) { return InitError(ResolveErrMsg("bind", strBind)); } connOptions.vBinds.push_back(addrBind); } for (const std::string &strBind : gArgs.GetArgs("-whitebind")) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) { return InitError(ResolveErrMsg("whitebind", strBind)); } if (addrBind.GetPort() == 0) { return InitError(strprintf( _("Need to specify a port with -whitebind: '%s'"), strBind)); } connOptions.vWhiteBinds.push_back(addrBind); } for (const auto &net : gArgs.GetArgs("-whitelist")) { CSubNet subnet; LookupSubNet(net.c_str(), subnet); if (!subnet.IsValid()) { return InitError(strprintf( _("Invalid netmask specified in -whitelist: '%s'"), net)); } connOptions.vWhitelistedRange.push_back(subnet); } connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); // Initiate outbound connections unless connect=0 connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); if (!connOptions.m_use_addrman_outgoing) { const auto connect = gArgs.GetArgs("-connect"); if (connect.size() != 1 || connect[0] != "0") { connOptions.m_specified_outgoing = connect; } } if (!connman.Start(scheduler, connOptions)) { return false; } // Step 12: finished SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); g_wallet_init_interface->Start(scheduler); return !fRequestShutdown; } diff --git a/src/net.cpp b/src/net.cpp index b90e5894b..59d6a66f4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,3096 +1,3096 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "net.h" #include "addrman.h" #include "chainparams.h" #include "clientversion.h" #include "config.h" #include "consensus/consensus.h" #include "crypto/common.h" #include "crypto/sha256.h" #include "hash.h" #include "netbase.h" #include "primitives/transaction.h" #include "scheduler.h" #include "ui_interface.h" #include "utilstrencodings.h" #ifdef WIN32 -#include +#include #else #include #endif #ifdef USE_UPNP #include #include #include #include #endif #include // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 // We add a random period time (0 to 1 seconds) to feeler connections to prevent // synchronization. #define FEELER_SLEEP_WINDOW 1 // MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define // it as 0 #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif // MSG_DONTWAIT is not available on some platforms, if it doesn't exist define // it as 0 #if !defined(MSG_DONTWAIT) #define MSG_DONTWAIT 0 #endif // Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h. // Todo: Can be removed when our pull-tester is upgraded to a modern MinGW // version. #ifdef WIN32 #ifndef PROTECTION_LEVEL_UNRESTRICTED #define PROTECTION_LEVEL_UNRESTRICTED 10 #endif #ifndef IPV6_PROTECTION_LEVEL #define IPV6_PROTECTION_LEVEL 23 #endif #endif /** Used to pass flags to the Bind() function */ enum BindFlags { BF_NONE = 0, BF_EXPLICIT = (1U << 0), BF_REPORT_ERROR = (1U << 1), BF_WHITELIST = (1U << 2), }; const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; // SHA256("netgroup")[0:8] static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("localhostnonce")[0:8] static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // // Global state variables // bool fDiscover = true; bool fListen = true; bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; std::map mapLocalHost; static bool vfLimited[NET_MAX] = {}; limitedmap mapAlreadyAskedFor(MAX_INV_SZ); void CConnman::AddOneShot(const std::string &strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); } unsigned short GetListenPort() { return (unsigned short)(gArgs.GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer bool GetLocal(CService &addr, const CNetAddr *paddrPeer) { if (!fListen) { return false; } int nBestScore = -1; int nBestReachability = -1; { LOCK(cs_mapLocalHost); for (const auto &entry : mapLocalHost) { int nScore = entry.second.nScore; int nReachability = entry.first.GetReachabilityFrom(paddrPeer); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService(entry.first, entry.second.nPort); nBestReachability = nReachability; nBestScore = nScore; } } } return nBestScore >= 0; } //! Convert the pnSeeds6 array into usable address objects. static std::vector convertSeed6(const std::vector &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, // it'll get a pile of addresses with newer timestamps. Seed nodes are given // a random 'last seen time' of between one and two weeks ago. const int64_t nOneWeek = 7 * 24 * 60 * 60; std::vector vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); for (const auto &seed_in : vSeedsIn) { struct in6_addr ip; memcpy(&ip, seed_in.addr, sizeof(ip)); CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE)); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } return vSeedsOut; } // Get best local address for a particular peer as a CAddress. Otherwise, return // the unroutable 0.0.0.0 but filled in with the normal parameters, since the IP // may be changed to a useful one by discovery. CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) { CAddress ret(CService(CNetAddr(), GetListenPort()), nLocalServices); CService addr; if (GetLocal(addr, paddrPeer)) { ret = CAddress(addr, nLocalServices); } ret.nTime = GetAdjustedTime(); return ret; } static int GetnScore(const CService &addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == LOCAL_NONE) { return 0; } return mapLocalHost[addr].nScore; } // Is our peer's addrLocal potentially useful as an external IP source? bool IsPeerAddrLocalGood(CNode *pnode) { CService addrLocal = pnode->GetAddrLocal(); return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() && !IsLimited(addrLocal.GetNetwork()); } // Pushes our own address to a peer. void AdvertiseLocal(CNode *pnode) { if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); if (gArgs.GetBoolArg("-addrmantest", false)) { // use IPv4 loopback during addrmantest addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices()); } // If discovery is enabled, sometimes give our peer the address it tells // us that it sees us as in case it has a better idea of our address // than we do. if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8 : 2) == 0)) { addrLocal.SetIP(pnode->GetAddrLocal()); } if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false)) { LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); FastRandomContext insecure_rand; pnode->PushAddress(addrLocal, insecure_rand); } } } // Learn a new local address. bool AddLocal(const CService &addr, int nScore) { if (!addr.IsRoutable()) { return false; } if (!fDiscover && nScore < LOCAL_MANUAL) { return false; } if (IsLimited(addr)) { return false; } LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore); { LOCK(cs_mapLocalHost); bool fAlready = mapLocalHost.count(addr) > 0; LocalServiceInfo &info = mapLocalHost[addr]; if (!fAlready || nScore >= info.nScore) { info.nScore = nScore + (fAlready ? 1 : 0); info.nPort = addr.GetPort(); } } return true; } bool AddLocal(const CNetAddr &addr, int nScore) { return AddLocal(CService(addr, GetListenPort()), nScore); } void RemoveLocal(const CService &addr) { LOCK(cs_mapLocalHost); LogPrintf("RemoveLocal(%s)\n", addr.ToString()); mapLocalHost.erase(addr); } /** * Make a particular network entirely off-limits (no automatic connects to it). */ void SetLimited(enum Network net, bool fLimited) { if (net == NET_UNROUTABLE || net == NET_INTERNAL) { return; } LOCK(cs_mapLocalHost); vfLimited[net] = fLimited; } bool IsLimited(enum Network net) { LOCK(cs_mapLocalHost); return vfLimited[net]; } bool IsLimited(const CNetAddr &addr) { return IsLimited(addr.GetNetwork()); } /** vote for a local address */ bool SeenLocal(const CService &addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) { return false; } mapLocalHost[addr].nScore++; return true; } /** check whether a given address is potentially local */ bool IsLocal(const CService &addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } /** check whether a given network is one we can probably connect to */ bool IsReachable(enum Network net) { LOCK(cs_mapLocalHost); return !vfLimited[net]; } /** check whether a given address is in a network we can probably connect to */ bool IsReachable(const CNetAddr &addr) { enum Network net = addr.GetNetwork(); return IsReachable(net); } CNode *CConnman::FindNode(const CNetAddr &ip) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (static_cast(pnode->addr) == ip) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const CSubNet &subNet) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (subNet.Match(static_cast(pnode->addr))) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const std::string &addrName) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (pnode->GetAddrName() == addrName) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const CService &addr) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (static_cast(pnode->addr) == addr) { return pnode; } } return nullptr; } bool CConnman::CheckIncomingNonce(uint64_t nonce) { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce) return false; } return true; } /** Get the bind address for a socket as CAddress */ static CAddress GetBindAddress(SOCKET sock) { CAddress addr_bind; struct sockaddr_storage sockaddr_bind; socklen_t sockaddr_bind_len = sizeof(sockaddr_bind); if (sock != INVALID_SOCKET) { if (!getsockname(sock, (struct sockaddr *)&sockaddr_bind, &sockaddr_bind_len)) { addr_bind.SetSockAddr((const struct sockaddr *)&sockaddr_bind); } else { LogPrint(BCLog::NET, "Warning: getsockname failed\n"); } } return addr_bind; } CNode *CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { if (pszDest == nullptr) { if (IsLocal(addrConnect)) { return nullptr; } // Look for an existing connection CNode *pnode = FindNode(static_cast(addrConnect)); if (pnode) { LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } /// debug print LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0); // Resolve const int default_port = Params().GetDefaultPort(); if (pszDest) { std::vector resolved; if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); if (!addrConnect.IsValid()) { LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest); return nullptr; } // It is possible that we already have a connection to the IP/port // pszDest resolved to. In that case, drop the connection that was // just created, and return the existing CNode instead. Also store // the name we used to connect in that CNode, so that future // FindNode() calls to that name catch this early. LOCK(cs_vNodes); CNode *pnode = FindNode(static_cast(addrConnect)); if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } } // Connect bool connected = false; SOCKET hSocket = INVALID_SOCKET; proxyType proxy; if (addrConnect.IsValid()) { bool proxyConnectionFailed = false; if (GetProxy(addrConnect.GetNetwork(), proxy)) { hSocket = CreateSocket(proxy.proxy); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectThroughProxy( proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed); } else { // no proxy needed (none set for target network) hSocket = CreateSocket(addrConnect); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); } if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) // is not caused by a problem connecting to the proxy, mark this as // an attempt. addrman.Attempt(addrConnect, fCountFailure); } } else if (pszDest && GetNameProxy(proxy)) { hSocket = CreateSocket(proxy.proxy); if (hSocket == INVALID_SOCKET) { return nullptr; } std::string host; int port = default_port; SplitHostPort(std::string(pszDest), port, host); connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr); } if (!connected) { CloseSocket(hSocket); return nullptr; } // Add node NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false); pnode->AddRef(); return pnode; } void CConnman::DumpBanlist() { // Clean unused entries (if bantime has expired) SweepBanned(); if (!BannedSetIsDirty()) { return; } int64_t nStart = GetTimeMillis(); CBanDB bandb(config->GetChainParams()); banmap_t banmap; GetBanned(banmap); if (bandb.Write(banmap)) { SetBannedSetDirty(false); } LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } void CNode::CloseSocketDisconnect() { fDisconnect = true; LOCK(cs_hSocket); if (hSocket != INVALID_SOCKET) { LogPrint(BCLog::NET, "disconnecting peer=%d\n", id); CloseSocket(hSocket); } } void CConnman::ClearBanned() { { LOCK(cs_setBanned); setBanned.clear(); setBannedIsDirty = true; } // Store banlist to disk. DumpBanlist(); if (clientInterface) { clientInterface->BannedListChanged(); } } bool CConnman::IsBanned(CNetAddr ip) { LOCK(cs_setBanned); for (const auto &it : setBanned) { CSubNet subNet = it.first; CBanEntry banEntry = it.second; if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) { return true; } } return false; } bool CConnman::IsBanned(CSubNet subnet) { LOCK(cs_setBanned); banmap_t::iterator i = setBanned.find(subnet); if (i != setBanned.end()) { CBanEntry banEntry = (*i).second; if (GetTime() < banEntry.nBanUntil) { return true; } } return false; } void CConnman::Ban(const CNetAddr &addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CSubNet subNet(addr); Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } void CConnman::Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CBanEntry banEntry(GetTime()); banEntry.banReason = banReason; if (bantimeoffset <= 0) { bantimeoffset = gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); sinceUnixEpoch = false; } banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime()) + bantimeoffset; { LOCK(cs_setBanned); if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { setBanned[subNet] = banEntry; setBannedIsDirty = true; } else { return; } } if (clientInterface) { clientInterface->BannedListChanged(); } { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (subNet.Match(static_cast(pnode->addr))) { pnode->fDisconnect = true; } } } if (banReason == BanReasonManuallyAdded) { // Store banlist to disk immediately if user requested ban. DumpBanlist(); } } bool CConnman::Unban(const CNetAddr &addr) { CSubNet subNet(addr); return Unban(subNet); } bool CConnman::Unban(const CSubNet &subNet) { { LOCK(cs_setBanned); if (!setBanned.erase(subNet)) { return false; } setBannedIsDirty = true; } if (clientInterface) { clientInterface->BannedListChanged(); } // Store banlist to disk immediately. DumpBanlist(); return true; } void CConnman::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); // Sweep the banlist so expired bans are not returned SweepBanned(); // Create a thread safe copy. banMap = setBanned; } void CConnman::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; setBannedIsDirty = true; } void CConnman::SweepBanned() { int64_t now = GetTime(); bool notifyUI = false; { LOCK(cs_setBanned); banmap_t::iterator it = setBanned.begin(); while (it != setBanned.end()) { CSubNet subNet = (*it).first; CBanEntry banEntry = (*it).second; if (now > banEntry.nBanUntil) { setBanned.erase(it++); setBannedIsDirty = true; notifyUI = true; LogPrint( BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); } else { ++it; } } } // update UI if (notifyUI && clientInterface) { clientInterface->BannedListChanged(); } } bool CConnman::BannedSetIsDirty() { LOCK(cs_setBanned); return setBannedIsDirty; } void CConnman::SetBannedSetDirty(bool dirty) { // Reuse setBanned lock for the isDirty flag. LOCK(cs_setBanned); setBannedIsDirty = dirty; } bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { for (const CSubNet &subnet : vWhitelistedRange) { if (subnet.Match(addr)) { return true; } } return false; } std::string CNode::GetAddrName() const { LOCK(cs_addrName); return addrName; } void CNode::MaybeSetAddrName(const std::string &addrNameIn) { LOCK(cs_addrName); if (addrName.empty()) { addrName = addrNameIn; } } CService CNode::GetAddrLocal() const { LOCK(cs_addrLocal); return addrLocal; } void CNode::SetAddrLocal(const CService &addrLocalIn) { LOCK(cs_addrLocal); if (addrLocal.IsValid()) { error("Addr local already set for node: %i. Refusing to change from %s " "to %s", id, addrLocal.ToString(), addrLocalIn.ToString()); } else { addrLocal = addrLocalIn; } } void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); stats.nServices = nServices; stats.addr = addr; stats.addrBind = addrBind; { LOCK(cs_filter); stats.fRelayTxes = fRelayTxes; } stats.nLastSend = nLastSend; stats.nLastRecv = nLastRecv; stats.nTimeConnected = nTimeConnected; stats.nTimeOffset = nTimeOffset; stats.addrName = GetAddrName(); stats.nVersion = nVersion; { LOCK(cs_SubVer); stats.cleanSubVer = cleanSubVer; } stats.fInbound = fInbound; stats.m_manual_connection = m_manual_connection; stats.nStartingHeight = nStartingHeight; { LOCK(cs_vSend); stats.mapSendBytesPerMsgCmd = mapSendBytesPerMsgCmd; stats.nSendBytes = nSendBytes; } { LOCK(cs_vRecv); stats.mapRecvBytesPerMsgCmd = mapRecvBytesPerMsgCmd; stats.nRecvBytes = nRecvBytes; } stats.fWhitelisted = fWhitelisted; // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. Merely reporting // pingtime might fool the caller into thinking the node was still // responsive, since pingtime does not update until the ping is complete, // which might take a while. So, if a ping is taking an unusually long time // in flight, the caller can immediately detect that this is happening. int64_t nPingUsecWait = 0; if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) { nPingUsecWait = GetTimeMicros() - nPingUsecStart; } // Raw ping time is in microseconds, but show it to user as whole seconds // (Bitcoin users should be well used to small numbers with many decimal // places by now :) stats.dPingTime = ((double(nPingUsecTime)) / 1e6); stats.dMinPing = ((double(nMinPingUsecTime)) / 1e6); stats.dPingWait = ((double(nPingUsecWait)) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; } static bool IsOversizedMessage(const Config &config, const CNetMessage &msg) { if (!msg.in_data) { // Header only, cannot be oversized. return false; } return msg.hdr.IsOversized(config); } bool CNode::ReceiveMsgBytes(const Config &config, const char *pch, uint32_t nBytes, bool &complete) { complete = false; int64_t nTimeMicros = GetTimeMicros(); LOCK(cs_vRecv); nLastRecv = nTimeMicros / 1000000; nRecvBytes += nBytes; while (nBytes > 0) { // Get current incomplete message, or create a new one. if (vRecvMsg.empty() || vRecvMsg.back().complete()) { vRecvMsg.push_back(CNetMessage(config.GetChainParams().NetMagic(), SER_NETWORK, INIT_PROTO_VERSION)); } CNetMessage &msg = vRecvMsg.back(); // Absorb network data. int handled; if (!msg.in_data) { handled = msg.readHeader(config, pch, nBytes); } else { handled = msg.readData(pch, nBytes); } if (handled < 0) { return false; } if (IsOversizedMessage(config, msg)) { LogPrint(BCLog::NET, "Oversized message from peer=%i, disconnecting\n", GetId()); return false; } pch += handled; nBytes -= handled; if (msg.complete()) { // Store received bytes per message command to prevent a memory DOS, // only allow valid commands. mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.hdr.pchCommand.data()); if (i == mapRecvBytesPerMsgCmd.end()) { i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER); } assert(i != mapRecvBytesPerMsgCmd.end()); i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; msg.nTime = nTimeMicros; complete = true; } } return true; } void CNode::SetSendVersion(int nVersionIn) { // Send version may only be changed in the version message, and only one // version message is allowed per session. We can therefore treat this value // as const and even atomic as long as it's only used once a version message // has been successfully processed. Any attempt to set this twice is an // error. if (nSendVersion != 0) { error("Send version already set for node: %i. Refusing to change from " "%i to %i", id, nSendVersion, nVersionIn); } else { nSendVersion = nVersionIn; } } int CNode::GetSendVersion() const { // The send version should always be explicitly set to INIT_PROTO_VERSION // rather than using this value until SetSendVersion has been called. if (nSendVersion == 0) { error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION); return INIT_PROTO_VERSION; } return nSendVersion; } int CNetMessage::readHeader(const Config &config, const char *pch, uint32_t nBytes) { // copy data to temporary parsing buffer uint32_t nRemaining = 24 - nHdrPos; uint32_t nCopy = std::min(nRemaining, nBytes); memcpy(&hdrbuf[nHdrPos], pch, nCopy); nHdrPos += nCopy; // if header incomplete, exit if (nHdrPos < 24) { return nCopy; } // deserialize to CMessageHeader try { hdrbuf >> hdr; } catch (const std::exception &) { return -1; } // Reject oversized messages if (hdr.IsOversized(config)) { LogPrint(BCLog::NET, "Oversized header detected\n"); return -1; } // switch state to reading message data in_data = true; return nCopy; } int CNetMessage::readData(const char *pch, uint32_t nBytes) { unsigned int nRemaining = hdr.nMessageSize - nDataPos; unsigned int nCopy = std::min(nRemaining, nBytes); if (vRecv.size() < nDataPos + nCopy) { // Allocate up to 256 KiB ahead, but never more than the total message // size. vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); } hasher.Write((const uint8_t *)pch, nCopy); memcpy(&vRecv[nDataPos], pch, nCopy); nDataPos += nCopy; return nCopy; } const uint256 &CNetMessage::GetMessageHash() const { assert(complete()); if (data_hash.IsNull()) { hasher.Finalize(data_hash.begin()); } return data_hash; } // requires LOCK(cs_vSend) size_t CConnman::SocketSendData(CNode *pnode) const { AssertLockHeld(pnode->cs_vSend); size_t nSentSize = 0; size_t nMsgCount = 0; for (const auto &data : pnode->vSendMsg) { assert(data.size() > pnode->nSendOffset); int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { break; } nBytes = send(pnode->hSocket, reinterpret_cast(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes == 0) { // couldn't send anything at all break; } if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { LogPrintf("socket send error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } break; } assert(nBytes > 0); pnode->nLastSend = GetSystemTimeInSeconds(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; nSentSize += nBytes; if (pnode->nSendOffset != data.size()) { // could not send full message; stop sending more break; } pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; nMsgCount++; } pnode->vSendMsg.erase(pnode->vSendMsg.begin(), pnode->vSendMsg.begin() + nMsgCount); if (pnode->vSendMsg.empty()) { assert(pnode->nSendOffset == 0); assert(pnode->nSendSize == 0); } return nSentSize; } struct NodeEvictionCandidate { NodeId id; int64_t nTimeConnected; int64_t nMinPingUsecTime; int64_t nLastBlockTime; int64_t nLastTXTime; bool fRelevantServices; bool fRelayTxes; bool fBloomFilter; CAddress addr; uint64_t nKeyedNetGroup; }; static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nMinPingUsecTime > b.nMinPingUsecTime; } static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nTimeConnected > b.nTimeConnected; } static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nKeyedNetGroup < b.nKeyedNetGroup; } static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have many // peers which have not yet relayed a block. if (a.nLastBlockTime != b.nLastBlockTime) { return a.nLastBlockTime < b.nLastBlockTime; } if (a.fRelevantServices != b.fRelevantServices) { return b.fRelevantServices; } return a.nTimeConnected > b.nTimeConnected; } static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have more // than a few peers that have not yet relayed txn. if (a.nLastTXTime != b.nLastTXTime) { return a.nLastTXTime < b.nLastTXTime; } if (a.fRelayTxes != b.fRelayTxes) { return b.fRelayTxes; } if (a.fBloomFilter != b.fBloomFilter) { return a.fBloomFilter; } return a.nTimeConnected > b.nTimeConnected; } //! Sort an array by the specified comparator, then erase the last K elements. template static void EraseLastKElements(std::vector &elements, Comparator comparator, size_t k) { std::sort(elements.begin(), elements.end(), comparator); size_t eraseSize = std::min(k, elements.size()); elements.erase(elements.end() - eraseSize, elements.end()); } /** * Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker triggered * network partitioning. * The strategy used here is to protect a small number of peers for each of * several distinct characteristics which are difficult to forge. In order to * partition a node the attacker must be simultaneously better at all of them * than honest peers. */ bool CConnman::AttemptToEvictConnection() { std::vector vEvictionCandidates; { LOCK(cs_vNodes); for (CNode *node : vNodes) { if (node->fWhitelisted || !node->fInbound || node->fDisconnect) { continue; } NodeEvictionCandidate candidate = { node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, HasAllDesirableServiceFlags(node->nServices), node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } } // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes // closer to the target. EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); // Protect 4 nodes that most recently sent us transactions. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); // Protect 4 nodes that most recently sent us blocks. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); // Protect the half of the remaining nodes which have been connected the // longest. This replicates the non-eviction implicit behavior, and // precludes attacks that start later. EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2); if (vEvictionCandidates.empty()) { return false; } // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; std::map> mapNetGroupNodes; for (const NodeEvictionCandidate &node : vEvictionCandidates) { std::vector &group = mapNetGroupNodes[node.nKeyedNetGroup]; group.push_back(node); int64_t grouptime = group[0].nTimeConnected; size_t group_size = group.size(); if (group_size > nMostConnections || (group_size == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = group_size; nMostConnectionsTime = grouptime; naMostConnections = node.nKeyedNetGroup; } } // Reduce to the network group with the most connections vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (pnode->GetId() == evicted) { pnode->fDisconnect = true; return true; } } return false; } void CConnman::AcceptConnection(const ListenSocket &hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr *)&sockaddr, &len); CAddress addr; int nInbound = 0; int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler); if (hSocket != INVALID_SOCKET) { if (!addr.SetSockAddr((const struct sockaddr *)&sockaddr)) { LogPrintf("Warning: Unknown socket family\n"); } } bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->fInbound) { nInbound++; } } } if (hSocket == INVALID_SOCKET) { int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK) { LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); } return; } if (!fNetworkActive) { LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString()); CloseSocket(hSocket); return; } if (!IsSelectableSocket(hSocket)) { LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); CloseSocket(hSocket); return; } // According to the internet TCP_NODELAY is not carried into accepted // sockets on all platforms. Set it again here just to be sure. SetSocketNoDelay(hSocket); if (IsBanned(addr) && !whitelisted) { LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); return; } if (nInbound >= nMaxInbound) { if (!AttemptToEvictConnection()) { // No connection to evict, disconnect the new connection LogPrint(BCLog::NET, "failed to find an eviction candidate - " "connection dropped (full)\n"); CloseSocket(hSocket); return; } } NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; m_msgproc->InitializeNode(*config, pnode); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); { LOCK(cs_vNodes); vNodes.push_back(pnode); } } void CConnman::ThreadSocketHandler() { unsigned int nPrevNodeCount = 0; while (!interruptNet) { // // Disconnect nodes // { LOCK(cs_vNodes); if (!fNetworkActive) { // Disconnect any connected nodes for (CNode *pnode : vNodes) { if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId()); pnode->fDisconnect = true; } } } // Disconnect unused nodes std::vector vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { if (pnode->fDisconnect) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); // release outbound grant (if any) pnode->grantOutbound.Release(); // close socket and cleanup pnode->CloseSocketDisconnect(); // hold in disconnected pool until all refs are released pnode->Release(); vNodesDisconnected.push_back(pnode); } } } { // Delete disconnected nodes std::list vNodesDisconnectedCopy = vNodesDisconnected; for (CNode *pnode : vNodesDisconnectedCopy) { // wait until threads are done using it if (pnode->GetRefCount() <= 0) { bool fDelete = false; { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { fDelete = true; } } } if (fDelete) { vNodesDisconnected.remove(pnode); DeleteNode(pnode); } } } } size_t vNodesSize; { LOCK(cs_vNodes); vNodesSize = vNodes.size(); } if (vNodesSize != nPrevNodeCount) { nPrevNodeCount = vNodesSize; if (clientInterface) { clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount); } } // // Find which sockets have data to receive // struct timeval timeout; timeout.tv_sec = 0; // Frequency to poll pnode->vSend timeout.tv_usec = 50000; fd_set fdsetRecv; fd_set fdsetSend; fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; bool have_fds = false; for (const ListenSocket &hListenSocket : vhListenSocket) { FD_SET(hListenSocket.socket, &fdsetRecv); hSocketMax = std::max(hSocketMax, hListenSocket.socket); have_fds = true; } { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { // Implement the following logic: // * If there is data to send, select() for sending data. As // this only happens when optimistic write failed, we choose to // first drain the write buffer in this case before receiving // more. This avoids needlessly queueing received data, if the // remote peer is not themselves receiving data. This means // properly utilizing TCP flow control signalling. // * Otherwise, if there is space left in the receive buffer, // select() for receiving data. // * Hand off all complete messages to the processor, to be // handled without blocking here. bool select_recv = !pnode->fPauseRecv; bool select_send; { LOCK(pnode->cs_vSend); select_send = !pnode->vSendMsg.empty(); } LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } FD_SET(pnode->hSocket, &fdsetError); hSocketMax = std::max(hSocketMax, pnode->hSocket); have_fds = true; if (select_send) { FD_SET(pnode->hSocket, &fdsetSend); continue; } if (select_recv) { FD_SET(pnode->hSocket, &fdsetRecv); } } } int nSelect = select(have_fds ? hSocketMax + 1 : 0, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); if (interruptNet) { return; } if (nSelect == SOCKET_ERROR) { if (have_fds) { int nErr = WSAGetLastError(); LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); for (unsigned int i = 0; i <= hSocketMax; i++) { FD_SET(i, &fdsetRecv); } } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); if (!interruptNet.sleep_for( std::chrono::milliseconds(timeout.tv_usec / 1000))) { return; } } // // Accept new connections // for (const ListenSocket &hListenSocket : vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { AcceptConnection(hListenSocket); } } // // Service each socket // std::vector vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { pnode->AddRef(); } } for (CNode *pnode : vNodesCopy) { if (interruptNet) { return; } // // Receive // bool recvSet = false; bool sendSet = false; bool errorSet = false; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); errorSet = FD_ISSET(pnode->hSocket, &fdsetError); } if (recvSet || errorSet) { // typical socket buffer is 8K-64K char pchBuf[0x10000]; int32_t nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } if (nBytes > 0) { bool notify = false; if (!pnode->ReceiveMsgBytes(*config, pchBuf, nBytes, notify)) { pnode->CloseSocketDisconnect(); } RecordBytesRecv(nBytes); if (notify) { size_t nSizeAdded = 0; auto it(pnode->vRecvMsg.begin()); for (; it != pnode->vRecvMsg.end(); ++it) { if (!it->complete()) { break; } nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; } { LOCK(pnode->cs_vProcessMsg); pnode->vProcessMsg.splice( pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); pnode->nProcessQueueSize += nSizeAdded; pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; } WakeMessageHandler(); } } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "socket closed\n"); } pnode->CloseSocketDisconnect(); } else if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) { LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); } pnode->CloseSocketDisconnect(); } } } // // Send // if (sendSet) { LOCK(pnode->cs_vSend); size_t nBytes = SocketSendData(pnode); if (nBytes) { RecordBytesSent(nBytes); } } // // Inactivity checking // int64_t nTime = GetSystemTimeInSeconds(); if (nTime - pnode->nTimeConnected > 60) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d " "from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId()); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); pnode->fDisconnect = true; } else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90 * 60)) { LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); pnode->fDisconnect = true; } else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) { LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart)); pnode->fDisconnect = true; } else if (!pnode->fSuccessfullyConnected) { LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); pnode->fDisconnect = true; } } } { LOCK(cs_vNodes); for (CNode *pnode : vNodesCopy) { pnode->Release(); } } } } void CConnman::WakeMessageHandler() { { std::lock_guard lock(mutexMsgProc); fMsgProcWake = true; } condMsgProc.notify_one(); } #ifdef USE_UPNP static CThreadInterrupt g_upnp_interrupt; static std::thread g_upnp_thread; static void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); const char *multicastif = nullptr; const char *minissdpdpath = nullptr; struct UPNPDev *devlist = nullptr; char lanaddr[64]; #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); #elif MINIUPNPC_API_VERSION < 14 /* miniupnpc 1.6 */ int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); #else /* miniupnpc 1.9.20150730 */ int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); #endif struct UPNPUrls urls; struct IGDdatas data; int r; r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); if (r == 1) { if (fDiscover) { char externalIPAddress[40]; r = UPNP_GetExternalIPAddress( urls.controlURL, data.first.servicetype, externalIPAddress); if (r != UPNPCOMMAND_SUCCESS) { LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); } else { if (externalIPAddress[0]) { CNetAddr resolved; if (LookupHost(externalIPAddress, resolved, false)) { LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str()); AddLocal(resolved, LOCAL_UPNP); } } else { LogPrintf("UPnP: GetExternalIPAddress failed.\n"); } } } std::string strDesc = "Bitcoin " + FormatFullVersion(); do { #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); #else /* miniupnpc 1.6 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); #endif if (r != UPNPCOMMAND_SUCCESS) { LogPrintf( "AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); } else { LogPrintf("UPnP Port Mapping successful.\n"); } } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); freeUPNPDevlist(devlist); devlist = nullptr; FreeUPNPUrls(&urls); } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = nullptr; if (r != 0) { FreeUPNPUrls(&urls); } } } void StartMapPort() { if (!g_upnp_thread.joinable()) { assert(!g_upnp_interrupt); g_upnp_thread = std::thread( (std::bind(&TraceThread, "upnp", &ThreadMapPort))); } } void InterruptMapPort() { if (g_upnp_thread.joinable()) { g_upnp_interrupt(); } } void StopMapPort() { if (g_upnp_thread.joinable()) { g_upnp_thread.join(); g_upnp_interrupt.reset(); } } #else void StartMapPort() { // Intentionally left blank. } void InterruptMapPort() { // Intentionally left blank. } void StopMapPort() { // Intentionally left blank. } #endif void CConnman::ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute. // Avoiding DNS seeds when we don't need them improves user privacy by // creating fewer identifying DNS requests, reduces trust by giving seeds // less influence on the network topology, and reduces traffic to the seeds. if ((addrman.size() > 0) && (!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { if (!interruptNet.sleep_for(std::chrono::seconds(11))) { return; } LOCK(cs_vNodes); int nRelevant = 0; for (const CNode *pnode : vNodes) { nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; } if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); return; } } const std::vector &vSeeds = config->GetChainParams().DNSSeeds(); int found = 0; LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); for (const std::string &seed : vSeeds) { if (interruptNet) { return; } if (HaveNameProxy()) { AddOneShot(seed); } else { std::vector vIPs; std::vector vAdd; ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE); std::string host = strprintf("x%x.%s", requiredServiceBits, seed); CNetAddr resolveSource; if (!resolveSource.SetInternal(host)) { continue; } // Limits number of IPs learned from a DNS seed unsigned int nMaxIPs = 256; if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) { for (const CNetAddr &ip : vIPs) { int nOneDay = 24 * 3600; CAddress addr = CAddress( CService(ip, config->GetChainParams().GetDefaultPort()), requiredServiceBits); // Use a random age between 3 and 7 days old. addr.nTime = GetTime() - 3 * nOneDay - GetRand(4 * nOneDay); vAdd.push_back(addr); found++; } addrman.Add(vAdd, resolveSource); } else { // We now avoid directly using results from DNS Seeds which do // not support service bit filtering, instead using them as a // oneshot to get nodes with our desired service bits. AddOneShot(seed); } } } LogPrintf("%d addresses found from DNS seeds\n", found); } void CConnman::DumpAddresses() { int64_t nStart = GetTimeMillis(); CAddrDB adb(config->GetChainParams()); adb.Write(addrman); LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } void CConnman::DumpData() { DumpAddresses(); DumpBanlist(); } void CConnman::ProcessOneShot() { std::string strDest; { LOCK(cs_vOneShots); if (vOneShots.empty()) { return; } strDest = vOneShots.front(); vOneShots.pop_front(); } CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true); } } bool CConnman::GetTryNewOutboundPeer() { return m_try_another_outbound_peer; } void CConnman::SetTryNewOutboundPeer(bool flag) { m_try_another_outbound_peer = flag; LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false"); } // Return the number of peers we have over our outbound connection limit. // Exclude peers that are marked for disconnect, or are going to be disconnected // soon (eg one-shots and feelers). // Also exclude peers that haven't finished initial connection handshake yet (so // that we don't decide we're over our desired connection limit, and then evict // some peer that has finished the handshake). int CConnman::GetExtraOutboundCount() { int nOutbound = 0; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) { ++nOutbound; } } } return std::max(nOutbound - nMaxOutbound, 0); } void CConnman::ThreadOpenConnections(const std::vector connect) { // Connect to specific addresses if (!connect.empty()) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); for (const std::string &strAddr : connect) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for( std::chrono::milliseconds(500))) { return; } } } if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } } } // Initiate network connections int64_t nStart = GetTime(); // Minimum time before next feeler connection (in microseconds). int64_t nNextFeeler = PoissonNextSend(nStart * 1000 * 1000, FEELER_INTERVAL); while (!interruptNet) { ProcessOneShot(); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } CSemaphoreGrant grant(*semOutbound); if (interruptNet) { return; } // Add seed nodes if DNS seeds are all down (an infrastructure attack?). if (addrman.size() == 0 && (GetTime() - nStart > 60)) { static bool done = false; if (!done) { LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be " "available.\n"); CNetAddr local; local.SetInternal("fixedseeds"); addrman.Add(convertSeed6(config->GetChainParams().FixedSeeds()), local); done = true; } } // // Choose an address to connect to based on most recently seen // CAddress addrConnect; // Only connect out to one peer per network group (/16 for IPv4). Do // this here so we don't have to critsect vNodes inside mapAddresses // critsect. int nOutbound = 0; std::set> setConnected; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (!pnode->fInbound && !pnode->m_manual_connection) { // Netgroups for inbound and addnode peers are not excluded // because our goal here is to not use multiple of our // limited outbound slots on a single netgroup but inbound // and addnode peers do not use our outbound slots. Inbound // peers also have the added issue that they're attacker // controlled and could be used to prevent us from // connecting to particular hosts if we used them here. setConnected.insert(pnode->addr.GetGroup()); nOutbound++; } } } // Feeler Connections // // Design goals: // * Increase the number of connectable addresses in the tried table. // // Method: // * Choose a random address from new and attempt to connect to it if // we can connect successfully it is added to tried. // * Start attempting feeler connections only after node finishes // making outbound connections. // * Only make a feeler connection once every few minutes. // bool fFeeler = false; if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) { // The current time right now (in microseconds). int64_t nTime = GetTimeMicros(); if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); fFeeler = true; } else { continue; } } addrman.ResolveCollisions(); int64_t nANow = GetAdjustedTime(); int nTries = 0; while (!interruptNet) { CAddrInfo addr = addrman.SelectTriedCollision(); // SelectTriedCollision returns an invalid address if it is empty. if (!fFeeler || !addr.IsValid()) { addr = addrman.Select(fFeeler); } // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) { break; } // If we didn't find an appropriate destination after trying 100 // addresses fetched from addrman, stop this loop, and let the outer // loop run again (which sleeps, adds seed nodes, recalculates // already-connected network ranges, ...) before trying new addrman // addresses. nTries++; if (nTries > 100) { break; } if (IsLimited(addr)) { continue; } // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) { continue; } // for non-feelers, require all the services we'll want, // for feelers, only require they be a full node (only because most // SPV clients don't have a good address DB available) if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) { continue; } if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) { continue; } // do not allow non-default ports, unless after 50 invalid addresses // selected already. if (addr.GetPort() != config->GetChainParams().GetDefaultPort() && nTries < 50) { continue; } addrConnect = addr; break; } if (addrConnect.IsValid()) { if (fFeeler) { // Add small amount of random noise before connection to avoid // synchronization. int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); if (!interruptNet.sleep_for( std::chrono::milliseconds(randsleep))) { return; } LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString()); } OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler); } } } std::vector CConnman::GetAddedNodeInfo() { std::vector ret; std::list lAddresses(0); { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses)); } // Build a map of all already connected addresses (by IP:port and by name) // to inbound/outbound and resolved CService std::map mapConnected; std::map> mapConnectedByName; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->addr.IsValid()) { mapConnected[pnode->addr] = pnode->fInbound; } std::string addrName = pnode->GetAddrName(); if (!addrName.empty()) { mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->fInbound, static_cast(pnode->addr)); } } } for (const std::string &strAddNode : lAddresses) { CService service( LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); AddedNodeInfo addedNode{strAddNode, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { addedNode.resolvedAddress = service; addedNode.fConnected = true; addedNode.fInbound = it->second; } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { addedNode.resolvedAddress = it->second.second; addedNode.fConnected = true; addedNode.fInbound = it->second.first; } } ret.emplace_back(std::move(addedNode)); } return ret; } void CConnman::ThreadOpenAddedConnections() { while (true) { CSemaphoreGrant grant(*semAddnode); std::vector vInfo = GetAddedNodeInfo(); bool tried = false; for (const AddedNodeInfo &info : vInfo) { if (!info.fConnected) { if (!grant.TryAcquire()) { // If we've used up our semaphore and need a new one, lets // not wait here since while we are waiting the // addednodeinfo state might change. break; } tried = true; CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } } } // Retry every 60 seconds if a connection was attempted, otherwise two // seconds. if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2))) { return; } } } // If successful, this moves the passed grant to the constructed node. void CConnman::OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection) { // // Initiate outbound network connection // if (interruptNet) { return; } if (!fNetworkActive) { return; } if (!pszDest) { if (IsLocal(addrConnect) || FindNode(static_cast(addrConnect)) || IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) { return; } } else if (FindNode(std::string(pszDest))) { return; } CNode *pnode = ConnectNode(addrConnect, pszDest, fCountFailure); if (!pnode) { return; } if (grantOutbound) { grantOutbound->MoveTo(pnode->grantOutbound); } if (fOneShot) { pnode->fOneShot = true; } if (fFeeler) { pnode->fFeeler = true; } if (manual_connection) { pnode->m_manual_connection = true; } m_msgproc->InitializeNode(*config, pnode); { LOCK(cs_vNodes); vNodes.push_back(pnode); } } void CConnman::ThreadMessageHandler() { while (!flagInterruptMsgProc) { std::vector vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { pnode->AddRef(); } } bool fMoreWork = false; for (CNode *pnode : vNodesCopy) { if (pnode->fDisconnect) { continue; } // Receive messages bool fMoreNodeWork = m_msgproc->ProcessMessages( *config, pnode, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) { return; } // Send messages { LOCK(pnode->cs_sendProcessing); m_msgproc->SendMessages(*config, pnode, flagInterruptMsgProc); } if (flagInterruptMsgProc) { return; } } { LOCK(cs_vNodes); for (CNode *pnode : vNodesCopy) { pnode->Release(); } } WAIT_LOCK(mutexMsgProc, lock); if (!fMoreWork) { condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } fMsgProcWake = false; } } bool CConnman::BindListenPort(const CService &addrBind, std::string &strError, bool fWhitelisted) { strError = ""; int nOne = 1; // Create socket for listening for incoming connections struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr *)&sockaddr, &len)) { strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); LogPrintf("%s\n", strError); return false; } SOCKET hListenSocket = CreateSocket(addrBind); if (hListenSocket == INVALID_SOCKET) { strError = strprintf("Error: Couldn't open socket for incoming " "connections (socket returned error %s)", NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)); // Some systems don't have IPV6_V6ONLY but are always v6only; others do have // the option and enable it by default or not. Try to enable it, if // possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)); #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (sockopt_arg_type)&nProtLevel, sizeof(int)); #endif } if (::bind(hListenSocket, (struct sockaddr *)&sockaddr, len) == SOCKET_ERROR) { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) { strError = strprintf(_("Unable to bind to %s on this computer. %s " "is probably already running."), addrBind.ToString(), _(PACKAGE_NAME)); } else { strError = strprintf(_("Unable to bind to %s on this computer " "(bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); } LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { strError = strprintf(_("Error: Listening for incoming connections " "failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; } vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted)); if (addrBind.IsRoutable() && fDiscover && !fWhitelisted) { AddLocal(addrBind, LOCAL_BIND); } return true; } void Discover() { if (!fDiscover) { return; } #ifdef WIN32 // Get local host IP char pszHostName[256] = ""; if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { std::vector vaddr; if (LookupHost(pszHostName, vaddr, 0, true)) { for (const CNetAddr &addr : vaddr) { if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString()); } } } } #else // Get local host ip struct ifaddrs *myaddrs; if (getifaddrs(&myaddrs) == 0) { for (struct ifaddrs *ifa = myaddrs; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr == nullptr || (ifa->ifa_flags & IFF_UP) == 0 || strcmp(ifa->ifa_name, "lo") == 0 || strcmp(ifa->ifa_name, "lo0") == 0) { continue; } if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *s4 = reinterpret_cast(ifa->ifa_addr); CNetAddr addr(s4->sin_addr); if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *s6 = reinterpret_cast(ifa->ifa_addr); CNetAddr addr(s6->sin6_addr); if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } } } freeifaddrs(myaddrs); } #endif } void CConnman::SetNetworkActive(bool active) { LogPrint(BCLog::NET, "SetNetworkActive: %s\n", active); if (fNetworkActive == active) { return; } fNetworkActive = active; uiInterface.NotifyNetworkActiveChanged(fNetworkActive); } CConnman::CConnman(const Config &configIn, uint64_t nSeed0In, uint64_t nSeed1In) : config(&configIn), nSeed0(nSeed0In), nSeed1(nSeed1In) { fNetworkActive = true; setBannedIsDirty = false; fAddressesInitialized = false; nLastNodeId = 0; nSendBufferMaxSize = 0; nReceiveFloodSize = 0; flagInterruptMsgProc = false; SetTryNewOutboundPeer(false); Options connOptions; Init(connOptions); } NodeId CConnman::GetNewNodeId() { return nLastNodeId.fetch_add(1, std::memory_order_relaxed); } bool CConnman::Bind(const CService &addr, unsigned int flags) { if (!(flags & BF_EXPLICIT) && IsLimited(addr)) { return false; } std::string strError; if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { if ((flags & BF_REPORT_ERROR) && clientInterface) { clientInterface->ThreadSafeMessageBox( strError, "", CClientUIInterface::MSG_ERROR); } return false; } return true; } bool CConnman::InitBinds(const std::vector &binds, const std::vector &whiteBinds) { bool fBound = false; for (const auto &addrBind : binds) { fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } for (const auto &addrBind : whiteBinds) { fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); } if (binds.empty() && whiteBinds.empty()) { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT; fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE); fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } return fBound; } bool CConnman::Start(CScheduler &scheduler, const Options &connOptions) { Init(connOptions); nTotalBytesRecv = 0; nTotalBytesSent = 0; nMaxOutboundTotalBytesSentInCycle = 0; nMaxOutboundCycleStartTime = 0; if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( _("Failed to listen on any port. Use -listen=0 if you want " "this."), "", CClientUIInterface::MSG_ERROR); } return false; } for (const auto &strDest : connOptions.vSeedNodes) { AddOneShot(strDest); } if (clientInterface) { clientInterface->InitMessage(_("Loading P2P addresses...")); } // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { CAddrDB adb(config->GetChainParams()); if (adb.Read(addrman)) { LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } else { // Addrman can be in an inconsistent state after failure, reset it addrman.Clear(); LogPrintf("Invalid or missing peers.dat; recreating\n"); DumpAddresses(); } } if (clientInterface) { clientInterface->InitMessage(_("Loading banlist...")); } // Load addresses from banlist.dat nStart = GetTimeMillis(); CBanDB bandb(config->GetChainParams()); banmap_t banmap; if (bandb.Read(banmap)) { // thread save setter SetBanned(banmap); // no need to write down, just read data SetBannedSetDirty(false); // sweep out unused entries SweepBanned(); LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } else { LogPrintf("Invalid or missing banlist.dat; recreating\n"); // force write SetBannedSetDirty(true); DumpBanlist(); } uiInterface.InitMessage(_("Starting network threads...")); fAddressesInitialized = true; if (semOutbound == nullptr) { // initialize semaphore semOutbound = std::make_unique( std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } if (semAddnode == nullptr) { // initialize semaphore semAddnode = std::make_unique(nMaxAddnode); } // // Start threads // assert(m_msgproc); InterruptSocks5(false); interruptNet.reset(); flagInterruptMsgProc = false; { LOCK(mutexMsgProc); fMsgProcWake = false; } // Send and receive from sockets, accept connections threadSocketHandler = std::thread( &TraceThread>, "net", std::function(std::bind(&CConnman::ThreadSocketHandler, this))); if (!gArgs.GetBoolArg("-dnsseed", true)) { LogPrintf("DNS seeding disabled\n"); } else { threadDNSAddressSeed = std::thread(&TraceThread>, "dnsseed", std::function( std::bind(&CConnman::ThreadDNSAddressSeed, this))); } // Initiate outbound connections from -addnode threadOpenAddedConnections = std::thread(&TraceThread>, "addcon", std::function(std::bind( &CConnman::ThreadOpenAddedConnections, this))); if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( _("Cannot provide specific connections and have addrman find " "outgoing connections at the same."), "", CClientUIInterface::MSG_ERROR); } return false; } if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) { threadOpenConnections = std::thread(&TraceThread>, "opencon", std::function( std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing))); } // Process messages threadMessageHandler = std::thread(&TraceThread>, "msghand", std::function( std::bind(&CConnman::ThreadMessageHandler, this))); // Dump network addresses scheduler.scheduleEvery( [this]() { this->DumpData(); return true; }, DUMP_ADDRESSES_INTERVAL * 1000); return true; } class CNetCleanup { public: CNetCleanup() {} ~CNetCleanup() { #ifdef WIN32 // Shutdown Windows Sockets WSACleanup(); #endif } } instance_of_cnetcleanup; void CConnman::Interrupt() { { std::lock_guard lock(mutexMsgProc); flagInterruptMsgProc = true; } condMsgProc.notify_all(); interruptNet(); InterruptSocks5(true); if (semOutbound) { for (int i = 0; i < (nMaxOutbound + nMaxFeeler); i++) { semOutbound->post(); } } if (semAddnode) { for (int i = 0; i < nMaxAddnode; i++) { semAddnode->post(); } } } void CConnman::Stop() { if (threadMessageHandler.joinable()) { threadMessageHandler.join(); } if (threadOpenConnections.joinable()) { threadOpenConnections.join(); } if (threadOpenAddedConnections.joinable()) { threadOpenAddedConnections.join(); } if (threadDNSAddressSeed.joinable()) { threadDNSAddressSeed.join(); } if (threadSocketHandler.joinable()) { threadSocketHandler.join(); } if (fAddressesInitialized) { DumpData(); fAddressesInitialized = false; } // Close sockets for (CNode *pnode : vNodes) { pnode->CloseSocketDisconnect(); } for (ListenSocket &hListenSocket : vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET) { if (!CloseSocket(hListenSocket.socket)) { LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); } } } // clean up some globals (to help leak detection) for (CNode *pnode : vNodes) { DeleteNode(pnode); } for (CNode *pnode : vNodesDisconnected) { DeleteNode(pnode); } vNodes.clear(); vNodesDisconnected.clear(); vhListenSocket.clear(); semOutbound.reset(); semAddnode.reset(); } void CConnman::DeleteNode(CNode *pnode) { assert(pnode); bool fUpdateConnectionTime = false; m_msgproc->FinalizeNode(*config, pnode->GetId(), fUpdateConnectionTime); if (fUpdateConnectionTime) { addrman.Connected(pnode->addr); } delete pnode; } CConnman::~CConnman() { Interrupt(); Stop(); } size_t CConnman::GetAddressCount() const { return addrman.size(); } void CConnman::SetServices(const CService &addr, ServiceFlags nServices) { addrman.SetServices(addr, nServices); } void CConnman::MarkAddressGood(const CAddress &addr) { addrman.Good(addr); } void CConnman::AddNewAddresses(const std::vector &vAddr, const CAddress &addrFrom, int64_t nTimePenalty) { addrman.Add(vAddr, addrFrom, nTimePenalty); } std::vector CConnman::GetAddresses() { return addrman.GetAddr(); } bool CConnman::AddNode(const std::string &strNode) { LOCK(cs_vAddedNodes); for (const std::string &it : vAddedNodes) { if (strNode == it) { return false; } } vAddedNodes.push_back(strNode); return true; } bool CConnman::RemoveAddedNode(const std::string &strNode) { LOCK(cs_vAddedNodes); for (std::vector::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { if (strNode == *it) { vAddedNodes.erase(it); return true; } } return false; } size_t CConnman::GetNodeCount(NumConnections flags) { LOCK(cs_vNodes); // Shortcut if we want total if (flags == CConnman::CONNECTIONS_ALL) { return vNodes.size(); } int nNum = 0; for (const auto &pnode : vNodes) { if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) { nNum++; } } return nNum; } void CConnman::GetNodeStats(std::vector &vstats) { vstats.clear(); LOCK(cs_vNodes); vstats.reserve(vNodes.size()); for (CNode *pnode : vNodes) { vstats.emplace_back(); pnode->copyStats(vstats.back()); } } bool CConnman::DisconnectNode(const std::string &strNode) { LOCK(cs_vNodes); if (CNode *pnode = FindNode(strNode)) { pnode->fDisconnect = true; return true; } return false; } bool CConnman::DisconnectNode(NodeId id) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (id == pnode->GetId()) { pnode->fDisconnect = true; return true; } } return false; } void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); nTotalBytesRecv += bytes; } void CConnman::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; uint64_t now = GetTime(); if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) { // timeframe expired, reset cycle nMaxOutboundCycleStartTime = now; nMaxOutboundTotalBytesSentInCycle = 0; } // TODO, exclude whitebind peers nMaxOutboundTotalBytesSentInCycle += bytes; } void CConnman::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); nMaxOutboundLimit = limit; } uint64_t CConnman::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } uint64_t CConnman::GetMaxOutboundTimeframe() { LOCK(cs_totalBytesSent); return nMaxOutboundTimeframe; } uint64_t CConnman::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return 0; } if (nMaxOutboundCycleStartTime == 0) { return nMaxOutboundTimeframe; } uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; uint64_t now = GetTime(); return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); } void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe) { LOCK(cs_totalBytesSent); if (nMaxOutboundTimeframe != timeframe) { // reset measure-cycle in case of changing the timeframe. nMaxOutboundCycleStartTime = GetTime(); } nMaxOutboundTimeframe = timeframe; } bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return false; } if (historicalBlockServingLimit) { // keep a large enough buffer to at least relay each block once. uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); uint64_t buffer = timeLeftInCycle / 600 * ONE_MEGABYTE; if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) { return true; } } else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) { return true; } return false; } uint64_t CConnman::GetOutboundTargetBytesLeft() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return 0; } return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } uint64_t CConnman::GetTotalBytesRecv() { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } uint64_t CConnman::GetTotalBytesSent() { LOCK(cs_totalBytesSent); return nTotalBytesSent; } ServiceFlags CConnman::GetLocalServices() const { return nLocalServices; } void CConnman::SetBestHeight(int height) { nBestHeight.store(height, std::memory_order_release); } int CConnman::GetBestHeight() const { return nBestHeight.load(std::memory_order_acquire); } unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, bool fInboundIn) : nTimeConnected(GetSystemTimeInSeconds()), addr(addrIn), addrBind(addrBindIn), fInbound(fInboundIn), nKeyedNetGroup(nKeyedNetGroupIn), addrKnown(5000, 0.001), filterInventoryKnown(50000, 0.000001), id(idIn), nLocalHostNonce(nLocalHostNonceIn), nLocalServices(nLocalServicesIn), nMyStartingHeight(nMyStartingHeightIn), nSendVersion(0) { nServices = NODE_NONE; hSocket = hSocketIn; nRecvVersion = INIT_PROTO_VERSION; nLastSend = 0; nLastRecv = 0; nSendBytes = 0; nRecvBytes = 0; nTimeOffset = 0; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; strSubVer = ""; fWhitelisted = false; fOneShot = false; m_manual_connection = false; // set by version message fClient = false; // set by version message m_limited_node = false; fFeeler = false; fSuccessfullyConnected = false; fDisconnect = false; nRefCount = 0; nSendSize = 0; nSendOffset = 0; hashContinue = uint256(); nStartingHeight = -1; filterInventoryKnown.reset(); fSendMempool = false; fGetAddr = false; nNextLocalAddrSend = 0; nNextAddrSend = 0; nNextInvSend = 0; fRelayTxes = false; fSentAddr = false; pfilter = std::make_unique(); timeLastMempoolReq = 0; nLastBlockTime = 0; nLastTXTime = 0; nPingNonceSent = 0; nPingUsecStart = 0; nPingUsecTime = 0; fPingQueued = false; nMinPingUsecTime = std::numeric_limits::max(); minFeeFilter = Amount::zero(); lastSentFeeFilter = Amount::zero(); nextSendTimeFeeFilter = 0; fPauseRecv = false; fPauseSend = false; nProcessQueueSize = 0; for (const std::string &msg : getAllNetMessageTypes()) { mapRecvBytesPerMsgCmd[msg] = 0; } mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; if (fLogIPs) { LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", addrName, id); } else { LogPrint(BCLog::NET, "Added connection peer=%d\n", id); } } CNode::~CNode() { CloseSocket(hSocket); } void CNode::AskFor(const CInv &inv) { if (mapAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) { return; } // a peer may not have multiple non-responded queue positions for a single // inv item. if (!setAskFor.insert(inv.hash).second) { return; } // We're using mapAskFor as a priority queue, the key is the earliest time // the request can be sent. int64_t nRequestTime; limitedmap::const_iterator it = mapAlreadyAskedFor.find(inv.hash); if (it != mapAlreadyAskedFor.end()) { nRequestTime = it->second; } else { nRequestTime = 0; } LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, FormatISO8601DateTime(nRequestTime / 1000000), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; static int64_t nLastTime; ++nLastTime; nNow = std::max(nNow, nLastTime); nLastTime = nNow; // Each retry is 2 minutes after the last nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); if (it != mapAlreadyAskedFor.end()) { mapAlreadyAskedFor.update(it, nRequestTime); } else { mapAlreadyAskedFor.insert(std::make_pair(inv.hash, nRequestTime)); } mapAskFor.insert(std::make_pair(nRequestTime, inv)); } bool CConnman::NodeFullyConnected(const CNode *pnode) { return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect; } void CConnman::PushMessage(CNode *pnode, CSerializedNetMsg &&msg) { size_t nMessageSize = msg.data.size(); size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE; LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command.c_str()), nMessageSize, pnode->GetId()); std::vector serializedHeader; serializedHeader.reserve(CMessageHeader::HEADER_SIZE); uint256 hash = Hash(msg.data.data(), msg.data.data() + nMessageSize); CMessageHeader hdr(config->GetChainParams().NetMagic(), msg.command.c_str(), nMessageSize); memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE); CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, serializedHeader, 0, hdr}; size_t nBytesSent = 0; { LOCK(pnode->cs_vSend); bool optimisticSend(pnode->vSendMsg.empty()); // log total amount of bytes per command pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize; pnode->nSendSize += nTotalSize; if (pnode->nSendSize > nSendBufferMaxSize) { pnode->fPauseSend = true; } pnode->vSendMsg.push_back(std::move(serializedHeader)); if (nMessageSize) { pnode->vSendMsg.push_back(std::move(msg.data)); } // If write queue empty, attempt "optimistic write" if (optimisticSend == true) { nBytesSent = SocketSendData(pnode); } } if (nBytesSent) { RecordBytesSent(nBytesSent); } } bool CConnman::ForNode(NodeId id, std::function func) { CNode *found = nullptr; LOCK(cs_vNodes); for (auto &&pnode : vNodes) { if (pnode->GetId() == id) { found = pnode; break; } } return found != nullptr && NodeFullyConnected(found) && func(found); } int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return nNow + int64_t(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); } CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const { return CSipHasher(nSeed0, nSeed1).Write(id); } uint64_t CConnman::CalculateKeyedNetGroup(const CAddress &ad) const { std::vector vchNetGroup(ad.GetGroup()); return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP) .Write(vchNetGroup.data(), vchNetGroup.size()) .Finalize(); } /** * This function convert MaxBlockSize from byte to * MB with a decimal precision one digit rounded down * E.g. * 1660000 -> 1.6 * 2010000 -> 2.0 * 1000000 -> 1.0 * 230000 -> 0.2 * 50000 -> 0.0 * * NB behavior for EB<1MB not standardized yet still * the function applies the same algo used for * EB greater or equal to 1MB */ std::string getSubVersionEB(uint64_t MaxBlockSize) { // Prepare EB string we are going to add to SubVer: // 1) translate from byte to MB and convert to string // 2) limit the EB string to the first decimal digit (floored) std::stringstream ebMBs; ebMBs << (MaxBlockSize / (ONE_MEGABYTE / 10)); std::string eb = ebMBs.str(); eb.insert(eb.size() - 1, ".", 1); if (eb.substr(0, 1) == ".") { eb = "0" + eb; } return eb; } std::string userAgent(const Config &config) { // format excessive blocksize value std::string eb = getSubVersionEB(config.GetMaxBlockSize()); std::vector uacomments; uacomments.push_back("EB" + eb); // Comments are checked for char compliance at startup, it is safe to add // them to the user agent string for (const std::string &cmt : gArgs.GetArgs("-uacomment")) { uacomments.push_back(cmt); } // Size compliance is checked at startup, it is safe to not check it again std::string subversion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); return subversion; } diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index 31f30f2c1..0ebd7b7b6 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,573 +1,573 @@ #include "bitcoin.h" #include "clientversion.h" #include "db.h" #include "dns.h" #include "logging.h" #include "protocol.h" #include "streams.h" #include #include #include +#include #include #include #include #include -#include class CDnsSeedOpts { public: int nThreads; int nPort; int nDnsThreads; int fUseTestNet; int fWipeBan; int fWipeIgnore; const char *mbox; const char *ns; const char *host; const char *tor; const char *ipv4_proxy; const char *ipv6_proxy; std::set filter_whitelist; CDnsSeedOpts() : nThreads(96), nPort(53), nDnsThreads(4), fUseTestNet(false), fWipeBan(false), fWipeIgnore(false), mbox(nullptr), ns(nullptr), host(nullptr), tor(nullptr), ipv4_proxy(nullptr), ipv6_proxy(nullptr) {} void ParseCommandLine(int argc, char **argv) { static const char *help = "Bitcoin-cash-seeder\n" "Usage: %s -h -n [-m ] [-t ] [-p " "]\n" "\n" "Options:\n" "-h Hostname of the DNS seed\n" "-n Hostname of the nameserver\n" "-m E-Mail address reported in SOA records\n" "-t Number of crawlers to run in parallel (default " "96)\n" "-d Number of DNS server threads (default 4)\n" "-p UDP port to listen on (default 53)\n" "-o Tor proxy IP/Port\n" "-i IPV4 SOCKS5 proxy IP/Port\n" "-k IPV6 SOCKS5 proxy IP/Port\n" "-w f1,f2,... Allow these flag combinations as filters\n" "--testnet Use testnet\n" "--wipeban Wipe list of banned nodes\n" "--wipeignore Wipe list of ignored nodes\n" "-?, --help Show this text\n" "\n"; bool showHelp = false; while (1) { static struct option long_options[] = { {"host", required_argument, 0, 'h'}, {"ns", required_argument, 0, 'n'}, {"mbox", required_argument, 0, 'm'}, {"threads", required_argument, 0, 't'}, {"dnsthreads", required_argument, 0, 'd'}, {"port", required_argument, 0, 'p'}, {"onion", required_argument, 0, 'o'}, {"proxyipv4", required_argument, 0, 'i'}, {"proxyipv6", required_argument, 0, 'k'}, {"filter", required_argument, 0, 'w'}, {"testnet", no_argument, &fUseTestNet, 1}, {"wipeban", no_argument, &fWipeBan, 1}, {"wipeignore", no_argument, &fWipeBan, 1}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "h:n:m:t:p:d:o:i:k:w:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': { host = optarg; break; } case 'm': { mbox = optarg; break; } case 'n': { ns = optarg; break; } case 't': { int n = strtol(optarg, nullptr, 10); if (n > 0 && n < 1000) nThreads = n; break; } case 'd': { int n = strtol(optarg, nullptr, 10); if (n > 0 && n < 1000) nDnsThreads = n; break; } case 'p': { int p = strtol(optarg, nullptr, 10); if (p > 0 && p < 65536) nPort = p; break; } case 'o': { tor = optarg; break; } case 'i': { ipv4_proxy = optarg; break; } case 'k': { ipv6_proxy = optarg; break; } case 'w': { char *ptr = optarg; while (*ptr != 0) { unsigned long l = strtoul(ptr, &ptr, 0); if (*ptr == ',') { ptr++; } else if (*ptr != 0) { break; } filter_whitelist.insert(l); } break; } case '?': { showHelp = true; break; } } } if (filter_whitelist.empty()) { filter_whitelist.insert(NODE_NETWORK); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); filter_whitelist.insert(NODE_NETWORK | NODE_XTHIN); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); } if (host != nullptr && ns == nullptr) showHelp = true; if (showHelp) fprintf(stderr, help, argv[0]); } }; extern "C" { #include "dns.h" } CAddrDb db; extern "C" void *ThreadCrawler(void *data) { int *nThreads = (int *)data; do { std::vector ips; int wait = 5; db.GetMany(ips, 16, wait); int64_t now = time(nullptr); if (ips.empty()) { wait *= 1000; wait += rand() % (500 * *nThreads); Sleep(wait); continue; } std::vector addr; for (size_t i = 0; i < ips.size(); i++) { CServiceResult &res = ips[i]; res.nBanTime = 0; res.nClientV = 0; res.nHeight = 0; res.strClientV = ""; bool getaddr = res.ourLastSuccess + 86400 < now; res.fGood = TestNode(res.service, res.nBanTime, res.nClientV, res.strClientV, res.nHeight, getaddr ? &addr : nullptr); } db.ResultMany(ips); db.Add(addr); } while (1); return nullptr; } extern "C" uint32_t GetIPList(void *thread, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6); class CDnsThread { public: struct FlagSpecificData { int nIPv4, nIPv6; std::vector cache; time_t cacheTime; unsigned int cacheHits; FlagSpecificData() : nIPv4(0), nIPv6(0), cacheTime(0), cacheHits(0) {} }; dns_opt_t dns_opt; // must be first const int id; std::map perflag; std::atomic dbQueries; std::set filterWhitelist; void cacheHit(uint64_t requestedFlags, bool force = false) { static bool nets[NET_MAX] = {}; if (!nets[NET_IPV4]) { nets[NET_IPV4] = true; nets[NET_IPV6] = true; } time_t now = time(nullptr); FlagSpecificData &thisflag = perflag[requestedFlags]; thisflag.cacheHits++; if (force || thisflag.cacheHits * 400 > (thisflag.cache.size() * thisflag.cache.size()) || (thisflag.cacheHits * thisflag.cacheHits * 20 > thisflag.cache.size() && (now - thisflag.cacheTime > 5))) { std::set ips; db.GetIPs(ips, requestedFlags, 1000, nets); dbQueries++; thisflag.cache.clear(); thisflag.nIPv4 = 0; thisflag.nIPv6 = 0; thisflag.cache.reserve(ips.size()); for (auto &ip : ips) { struct in_addr addr; struct in6_addr addr6; if (ip.GetInAddr(&addr)) { addr_t a; a.v = 4; memcpy(&a.data.v4, &addr, 4); thisflag.cache.push_back(a); thisflag.nIPv4++; } else if (ip.GetIn6Addr(&addr6)) { addr_t a; a.v = 6; memcpy(&a.data.v6, &addr6, 16); thisflag.cache.push_back(a); thisflag.nIPv6++; } } thisflag.cacheHits = 0; thisflag.cacheTime = now; } } CDnsThread(CDnsSeedOpts *opts, int idIn) : id(idIn) { dns_opt.host = opts->host; dns_opt.ns = opts->ns; dns_opt.mbox = opts->mbox; dns_opt.datattl = 3600; dns_opt.nsttl = 40000; dns_opt.cb = GetIPList; dns_opt.port = opts->nPort; dns_opt.nRequests = 0; dbQueries = 0; perflag.clear(); filterWhitelist = opts->filter_whitelist; } void run() { dnsserver(&dns_opt); } }; extern "C" uint32_t GetIPList(void *data, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6) { CDnsThread *thread = (CDnsThread *)data; uint64_t requestedFlags = 0; int hostlen = strlen(requestedHostname); if (hostlen > 1 && requestedHostname[0] == 'x' && requestedHostname[1] != '0') { char *pEnd; uint64_t flags = (uint64_t)strtoull(requestedHostname + 1, &pEnd, 16); if (*pEnd == '.' && pEnd <= requestedHostname + 17 && std::find(thread->filterWhitelist.begin(), thread->filterWhitelist.end(), flags) != thread->filterWhitelist.end()) { requestedFlags = flags; } else { return 0; } } else if (strcasecmp(requestedHostname, thread->dns_opt.host)) { return 0; } thread->cacheHit(requestedFlags); auto &thisflag = thread->perflag[requestedFlags]; uint32_t size = thisflag.cache.size(); uint32_t maxmax = (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0); if (max > size) { max = size; } if (max > maxmax) { max = maxmax; } uint32_t i = 0; while (i < max) { uint32_t j = i + (rand() % (size - i)); do { bool ok = (ipv4 && thisflag.cache[j].v == 4) || (ipv6 && thisflag.cache[j].v == 6); if (ok) { break; } j++; if (j == size) { j = i; } } while (1); addr[i] = thisflag.cache[j]; thisflag.cache[j] = thisflag.cache[i]; thisflag.cache[i] = addr[i]; i++; } return max; } std::vector dnsThread; extern "C" void *ThreadDNS(void *arg) { CDnsThread *thread = (CDnsThread *)arg; thread->run(); return nullptr; } int StatCompare(const CAddrReport &a, const CAddrReport &b) { if (a.uptime[4] == b.uptime[4]) { if (a.uptime[3] == b.uptime[3]) { return a.clientVersion > b.clientVersion; } else { return a.uptime[3] > b.uptime[3]; } } else { return a.uptime[4] > b.uptime[4]; } } extern "C" void *ThreadDumper(void *) { int count = 0; do { // First 100s, than 200s, 400s, 800s, 1600s, and then 3200s forever Sleep(100000 << count); if (count < 5) { count++; } { std::vector v = db.GetAll(); sort(v.begin(), v.end(), StatCompare); FILE *f = fopen("dnsseed.dat.new", "w+"); if (f) { { CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf << db; } rename("dnsseed.dat.new", "dnsseed.dat"); } FILE *d = fopen("dnsseed.dump", "w"); fprintf(d, "# address good " "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " "%%(30d) blocks svcs version\n"); double stat[5] = {0, 0, 0, 0, 0}; for (CAddrReport rep : v) { fprintf( d, "%-47s %4d %11" PRId64 " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 " %5i \"%s\"\n", rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], 100.0 * rep.uptime[4], rep.blocks, rep.services, rep.clientVersion, rep.clientSubVersion.c_str()); stat[0] += rep.uptime[0]; stat[1] += rep.uptime[1]; stat[2] += rep.uptime[2]; stat[3] += rep.uptime[3]; stat[4] += rep.uptime[4]; } fclose(d); FILE *ff = fopen("dnsstats.log", "a"); fprintf(ff, "%llu %g %g %g %g %g\n", (unsigned long long)(time(nullptr)), stat[0], stat[1], stat[2], stat[3], stat[4]); fclose(ff); } } while (1); return nullptr; } extern "C" void *ThreadStats(void *) { bool first = true; do { char c[256]; time_t tim = time(nullptr); struct tm *tmp = localtime(&tim); strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); CAddrDbStats stats; db.GetStats(stats); if (first) { first = false; printf("\n\n\n\x1b[3A"); } else printf("\x1b[2K\x1b[u"); printf("\x1b[s"); uint64_t requests = 0; uint64_t queries = 0; for (unsigned int i = 0; i < dnsThread.size(); i++) { requests += dnsThread[i]->dns_opt.nRequests; queries += dnsThread[i]->dbQueries; } printf("%s %i/%i available (%i tried in %is, %i new, %i active), %i " "banned; %llu DNS requests, %llu db queries\n", c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, stats.nBanned, (unsigned long long)requests, (unsigned long long)queries); Sleep(1000); } while (1); return nullptr; } static const std::string mainnet_seeds[] = { "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", "seed.deadalnix.me", "seeder.criptolayer.net", ""}; static const std::string testnet_seeds[] = { "testnet-seed.bitcoinabc.org", "testnet-seed-abc.bitcoinforks.org", "testnet-seed.bitprim.org", "testnet-seed.deadalnix.me", "testnet-seeder.criptolayer.net", ""}; static const std::string *seeds = mainnet_seeds; const static unsigned int MAX_HOSTS_PER_SEED = 128; extern "C" void *ThreadSeeder(void *) { do { for (int i = 0; seeds[i] != ""; i++) { std::vector ips; LookupHost(seeds[i].c_str(), ips, MAX_HOSTS_PER_SEED, true); for (auto &ip : ips) { db.Add(CAddress(CService(ip, GetDefaultPort()), ServiceFlags()), true); } } Sleep(1800000); } while (1); return nullptr; } int main(int argc, char **argv) { // The logger dump everything on the console by default. GetLogger().m_print_to_console = true; signal(SIGPIPE, SIG_IGN); setbuf(stdout, nullptr); CDnsSeedOpts opts; opts.ParseCommandLine(argc, argv); printf("Supporting whitelisted filters: "); for (std::set::const_iterator it = opts.filter_whitelist.begin(); it != opts.filter_whitelist.end(); it++) { if (it != opts.filter_whitelist.begin()) { printf(","); } printf("0x%lx", (unsigned long)*it); } printf("\n"); if (opts.tor) { CService service(LookupNumeric(opts.tor, 9050)); if (service.IsValid()) { printf("Using Tor proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_TOR, proxyType(service)); } } if (opts.ipv4_proxy) { CService service(LookupNumeric(opts.ipv4_proxy, 9050)); if (service.IsValid()) { printf("Using IPv4 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV4, proxyType(service)); } } if (opts.ipv6_proxy) { CService service(LookupNumeric(opts.ipv6_proxy, 9050)); if (service.IsValid()) { printf("Using IPv6 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV6, proxyType(service)); } } bool fDNS = true; if (opts.fUseTestNet) { printf("Using testnet.\n"); netMagic[0] = 0xf4; netMagic[1] = 0xe5; netMagic[2] = 0xf3; netMagic[3] = 0xf4; seeds = testnet_seeds; fTestNet = true; } if (!opts.ns) { printf("No nameserver set. Not starting DNS server.\n"); fDNS = false; } if (fDNS && !opts.host) { fprintf(stderr, "No hostname set. Please use -h.\n"); exit(1); } if (fDNS && !opts.mbox) { fprintf(stderr, "No e-mail address set. Please use -m.\n"); exit(1); } FILE *f = fopen("dnsseed.dat", "r"); if (f) { printf("Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf >> db; if (opts.fWipeBan) db.banned.clear(); if (opts.fWipeIgnore) db.ResetIgnores(); printf("done\n"); } pthread_t threadDns, threadSeed, threadDump, threadStats; if (fDNS) { printf("Starting %i DNS threads for %s on %s (port %i)...", opts.nDnsThreads, opts.host, opts.ns, opts.nPort); dnsThread.clear(); for (int i = 0; i < opts.nDnsThreads; i++) { dnsThread.push_back(new CDnsThread(&opts, i)); pthread_create(&threadDns, nullptr, ThreadDNS, dnsThread[i]); printf("."); Sleep(20); } printf("done\n"); } printf("Starting seeder..."); pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); printf("done\n"); printf("Starting %i crawler threads...", opts.nThreads); pthread_attr_t attr_crawler; pthread_attr_init(&attr_crawler); pthread_attr_setstacksize(&attr_crawler, 0x20000); for (int i = 0; i < opts.nThreads; i++) { pthread_t thread; pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); } pthread_attr_destroy(&attr_crawler); printf("done\n"); pthread_create(&threadStats, nullptr, ThreadStats, nullptr); pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); void *res; pthread_join(threadDump, &res); return 0; } diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp index 16d2df402..1d2a0c801 100644 --- a/src/test/univalue_tests.cpp +++ b/src/test/univalue_tests.cpp @@ -1,327 +1,327 @@ // Copyright (c) 2014 BitPay Inc. // Copyright (c) 2014-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "test/test_bitcoin.h" +#include #include -#include #include #include #include #include BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(univalue_constructor) { UniValue v1; BOOST_CHECK(v1.isNull()); UniValue v2(UniValue::VSTR); BOOST_CHECK(v2.isStr()); UniValue v3(UniValue::VSTR, "foo"); BOOST_CHECK(v3.isStr()); BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); UniValue numTest; BOOST_CHECK(numTest.setNumStr("82")); BOOST_CHECK(numTest.isNum()); BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); uint64_t vu64 = 82; UniValue v4(vu64); BOOST_CHECK(v4.isNum()); BOOST_CHECK_EQUAL(v4.getValStr(), "82"); int64_t vi64 = -82; UniValue v5(vi64); BOOST_CHECK(v5.isNum()); BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); int vi = -688; UniValue v6(vi); BOOST_CHECK(v6.isNum()); BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); double vd = -7.21; UniValue v7(vd); BOOST_CHECK(v7.isNum()); BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); std::string vs("yawn"); UniValue v8(vs); BOOST_CHECK(v8.isStr()); BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); const char *vcs = "zappa"; UniValue v9(vcs); BOOST_CHECK(v9.isStr()); BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); } BOOST_AUTO_TEST_CASE(univalue_typecheck) { UniValue v1; BOOST_CHECK(v1.setNumStr("1")); BOOST_CHECK(v1.isNum()); BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error); UniValue v2; BOOST_CHECK(v2.setBool(true)); BOOST_CHECK_EQUAL(v2.get_bool(), true); BOOST_CHECK_THROW(v2.get_int(), std::runtime_error); UniValue v3; BOOST_CHECK(v3.setNumStr("32482348723847471234")); BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error); BOOST_CHECK(v3.setNumStr("1000")); BOOST_CHECK_EQUAL(v3.get_int64(), 1000); UniValue v4; BOOST_CHECK(v4.setNumStr("2147483648")); BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); BOOST_CHECK_THROW(v4.get_int(), std::runtime_error); BOOST_CHECK(v4.setNumStr("1000")); BOOST_CHECK_EQUAL(v4.get_int(), 1000); BOOST_CHECK_THROW(v4.get_str(), std::runtime_error); BOOST_CHECK_EQUAL(v4.get_real(), 1000); BOOST_CHECK_THROW(v4.get_array(), std::runtime_error); BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error); BOOST_CHECK_THROW(v4.getValues(), std::runtime_error); BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error); UniValue v5; BOOST_CHECK(v5.read("[true, 10]")); BOOST_CHECK_NO_THROW(v5.get_array()); std::vector vals = v5.getValues(); BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error); BOOST_CHECK_EQUAL(vals[0].get_bool(), true); BOOST_CHECK_EQUAL(vals[1].get_int(), 10); BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error); } BOOST_AUTO_TEST_CASE(univalue_set) { UniValue v(UniValue::VSTR, "foo"); v.clear(); BOOST_CHECK(v.isNull()); BOOST_CHECK_EQUAL(v.getValStr(), ""); BOOST_CHECK(v.setObject()); BOOST_CHECK(v.isObject()); BOOST_CHECK_EQUAL(v.size(), 0); BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); BOOST_CHECK(v.empty()); BOOST_CHECK(v.setArray()); BOOST_CHECK(v.isArray()); BOOST_CHECK_EQUAL(v.size(), 0); BOOST_CHECK(v.setStr("zum")); BOOST_CHECK(v.isStr()); BOOST_CHECK_EQUAL(v.getValStr(), "zum"); BOOST_CHECK(v.setFloat(-1.01)); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); BOOST_CHECK(v.setInt((int)1023)); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "1023"); BOOST_CHECK(v.setInt((int64_t)-1023LL)); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); BOOST_CHECK(v.setInt((uint64_t)1023ULL)); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "1023"); BOOST_CHECK(v.setNumStr("-688")); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "-688"); BOOST_CHECK(v.setBool(false)); BOOST_CHECK_EQUAL(v.isBool(), true); BOOST_CHECK_EQUAL(v.isTrue(), false); BOOST_CHECK_EQUAL(v.isFalse(), true); BOOST_CHECK_EQUAL(v.getBool(), false); BOOST_CHECK(v.setBool(true)); BOOST_CHECK_EQUAL(v.isBool(), true); BOOST_CHECK_EQUAL(v.isTrue(), true); BOOST_CHECK_EQUAL(v.isFalse(), false); BOOST_CHECK_EQUAL(v.getBool(), true); BOOST_CHECK(!v.setNumStr("zombocom")); BOOST_CHECK(v.setNull()); BOOST_CHECK(v.isNull()); } BOOST_AUTO_TEST_CASE(univalue_array) { UniValue arr(UniValue::VARR); UniValue v((int64_t)1023LL); BOOST_CHECK(arr.push_back(v)); std::string vStr("zippy"); BOOST_CHECK(arr.push_back(vStr)); const char *s = "pippy"; BOOST_CHECK(arr.push_back(s)); std::vector vec; v.setStr("boing"); vec.push_back(v); v.setStr("going"); vec.push_back(v); BOOST_CHECK(arr.push_backV(vec)); BOOST_CHECK_EQUAL(arr.empty(), false); BOOST_CHECK_EQUAL(arr.size(), 5); BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); arr.clear(); BOOST_CHECK(arr.empty()); BOOST_CHECK_EQUAL(arr.size(), 0); } BOOST_AUTO_TEST_CASE(univalue_object) { UniValue obj(UniValue::VOBJ); std::string strKey, strVal; UniValue v; strKey = "age"; v.setInt(100); BOOST_CHECK(obj.pushKV(strKey, v)); strKey = "first"; strVal = "John"; BOOST_CHECK(obj.pushKV(strKey, strVal)); strKey = "last"; const char *cVal = "Smith"; BOOST_CHECK(obj.pushKV(strKey, cVal)); strKey = "distance"; BOOST_CHECK(obj.pushKV(strKey, (int64_t)25)); strKey = "time"; BOOST_CHECK(obj.pushKV(strKey, (uint64_t)3600)); strKey = "calories"; BOOST_CHECK(obj.pushKV(strKey, (int)12)); strKey = "temperature"; BOOST_CHECK(obj.pushKV(strKey, (double)90.012)); UniValue obj2(UniValue::VOBJ); BOOST_CHECK(obj2.pushKV("cat1", 9000)); BOOST_CHECK(obj2.pushKV("cat2", 12345)); BOOST_CHECK(obj.pushKVs(obj2)); BOOST_CHECK_EQUAL(obj.empty(), false); BOOST_CHECK_EQUAL(obj.size(), 9); BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); BOOST_CHECK(obj.exists("age")); BOOST_CHECK(obj.exists("first")); BOOST_CHECK(obj.exists("last")); BOOST_CHECK(obj.exists("distance")); BOOST_CHECK(obj.exists("time")); BOOST_CHECK(obj.exists("calories")); BOOST_CHECK(obj.exists("temperature")); BOOST_CHECK(obj.exists("cat1")); BOOST_CHECK(obj.exists("cat2")); BOOST_CHECK(!obj.exists("nyuknyuknyuk")); std::map objTypes; objTypes["age"] = UniValue::VNUM; objTypes["first"] = UniValue::VSTR; objTypes["last"] = UniValue::VSTR; objTypes["distance"] = UniValue::VNUM; objTypes["time"] = UniValue::VNUM; objTypes["calories"] = UniValue::VNUM; objTypes["temperature"] = UniValue::VNUM; objTypes["cat1"] = UniValue::VNUM; objTypes["cat2"] = UniValue::VNUM; BOOST_CHECK(obj.checkObject(objTypes)); objTypes["cat2"] = UniValue::VSTR; BOOST_CHECK(!obj.checkObject(objTypes)); obj.clear(); BOOST_CHECK(obj.empty()); BOOST_CHECK_EQUAL(obj.size(), 0); } static const char *json1 = "[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800," "\"key3\":{\"name\":\"martian http://test.com\"}}]"; BOOST_AUTO_TEST_CASE(univalue_readwrite) { UniValue v; BOOST_CHECK(v.read(json1)); std::string strJson1(json1); BOOST_CHECK(v.read(strJson1)); BOOST_CHECK(v.isArray()); BOOST_CHECK_EQUAL(v.size(), 2); BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); UniValue obj = v[1]; BOOST_CHECK(obj.isObject()); BOOST_CHECK_EQUAL(obj.size(), 3); BOOST_CHECK(obj["key1"].isStr()); std::string correctValue("str"); correctValue.push_back('\0'); BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); BOOST_CHECK(obj["key2"].isNum()); BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); BOOST_CHECK(obj["key3"].isObject()); BOOST_CHECK_EQUAL(strJson1, v.write()); /* Check for (correctly reporting) a parsing error if the initial JSON construct is followed by more stuff. Note that whitespace is, of course, exempt. */ BOOST_CHECK(v.read(" {}\n ")); BOOST_CHECK(v.isObject()); BOOST_CHECK(v.read(" []\n ")); BOOST_CHECK(v.isArray()); BOOST_CHECK(!v.read("@{}")); BOOST_CHECK(!v.read("{} garbage")); BOOST_CHECK(!v.read("[]{}")); BOOST_CHECK(!v.read("{}[]")); BOOST_CHECK(!v.read("{} 42")); } BOOST_AUTO_TEST_SUITE_END()