Protocol

1: 
module Protocol

The module begins the usual set of imports. I use the RX Framework to handle events and data flow. There are a few other libraries:

  • FSharpx: A set of extensions to F#. It adds a few useful monads
  • NodaTime: Mostly because I used JodaTime in Java and find it very well designed
  • Log4Net
  • BouncyCastle: Originally, I used it to calculate hashes and verify signatures. But the .NET framework already includes a crypto digest library and ECDSA in BC is extremely slow compared to SECP256K1. So now the usage of BC is limited to HEX to String conversions
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
open System
open System.Net
open System.Collections
open System.Collections.Generic
open System.IO
open System.Text
open System.Reactive.Linq
open System.Reactive.Subjects
open System.Reactive.Disposables
open System.Threading.Tasks
open System.Security.Cryptography
open FSharpx
open FSharpx.Collections
open FSharpx.Choice
open FSharpx.Validation
open FSharpx.Option
open NodaTime
open log4net
open Org.BouncyCastle.Crypto.Digests
open Org.BouncyCastle.Utilities.Encoders

These are constants defined in the configuration file. They are imported through a type provider and end up being checked at compile time. It's a small but nice feature.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
type settings = AppSettings<"app.config">
let baseDir = settings.BaseDir
let version = 70001
let connectTimeout = TimeSpan.FromSeconds(float settings.ConnectTimeout)
let handshakeTimeout = TimeSpan.FromSeconds(float settings.HandshakeTimeout)
let commandTimeout = TimeSpan.FromSeconds(float settings.CommandTimeout)
let minGetdataBatchSize = settings.MinGetdataBatchSize
let maxGetdataBatchSize = settings.MaxGetdataBatchSize
let coinbaseMaturity = 100
let maxBlockSize = 1000000
let maxMoney = 2100000000000000L

I define some helper functions and some constants here.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
let (|?|) = defaultArg // infix version
let txInvType = 1
let blockInvType = 2
let filteredBlockInvType = 3
let zeroHash: byte[] = Array.zeroCreate 32
let random = new Random() // used to produce nonces in ping/pong
exception ValidationException of string
let either = new EitherBuilder()
let maybe = new MaybeBuilder()

let rec iterate f value = seq { // Same as iterate in clojure
    yield value
    yield! iterate f (f value) }

Find the first byte that matches a given condition starting from the end of the array

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let revFind (arr: byte[]) (f: byte -> bool) =
    let rec rf (i: int) =
        if i < 0 || f(arr.[i]) then 
            i
        else
            rf (i-1)
    rf (arr.Length-1)

Extends the log4net logger with F# printf style. The later checks the format string against the type of the effective parameters

1: 
2: 
3: 
4: 
5: 
6: 
let logger = LogManager.GetLogger("bitcoinfs")
type ILog with
    member x.DebugF format = Printf.ksprintf (x.Debug) format
    member x.InfoF format = Printf.ksprintf (x.Info) format
    member x.ErrorF format = Printf.ksprintf (x.Error) format
    member x.FatalF format = Printf.ksprintf (x.Fatal) format

F# has structural comparison of arrays and you can directly compare them with =. However, this is not the case for .NET containers and there are a few places where they are more convenient than their F# counterparts. In these few cases, I need a comparer that does deep compare, fortunately the BCL offers StructuralComparisons.

1: 
2: 
3: 
4: 
type HashCompare() = 
    interface IEqualityComparer<byte[]> with
        member x.Equals(left: byte[], right: byte[]) = StructuralComparisons.StructuralEqualityComparer.Equals(left, right)
        member x.GetHashCode(hash: byte[]) = StructuralComparisons.StructuralEqualityComparer.GetHashCode hash

Extensions methods

When a task finishes, delivers a single element and then closes the observable

1: 
2: 
3: 
4: 
5: 
6: 
type Async =
    static member AsObservable(op: Async<'a>) =
        Observable.Create(fun (obs: IObserver<'a>) ->
            Async.StartWithContinuations(op, (fun r -> obs.OnNext r; obs.OnCompleted()), obs.OnError, obs.OnError)
            Disposable.Create(fun () -> ignore())
        )

Most of the error handling in done in functional style with the Option monad. Checks are expressed like an assert that logs a message and evaluates to None. errorIfFalse lets me write statements like condition |> errorIfFalse "error" in a do-block.

1: 
2: 
3: 
4: 
5: 
type Option =
    static member iterSecond(f: unit -> unit) (opt: Option<'a>) = 
        if opt.IsNone then f()
        opt
let errorIfFalse (errorMsg: string) (b: bool) = b |> Option.ofBool |> Option.iterSecond(fun() -> logger.Debug errorMsg)

Utility functions

Hashing functions. It's important to pass the digest by name i.e. in a factory function otherwise the digest is bound at the let and there will be a single digest per hashing function. As a result, hashing is no threat safe and hilarity ensues

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let hashBlock (digest: unit -> HashAlgorithm) (input: byte[]): byte[] = digest().ComputeHash(input)

let ripe160 = hashBlock (fun () -> new RIPEMD160Managed() :> HashAlgorithm)
let sha1 = hashBlock (fun () -> new SHA1Managed() :> HashAlgorithm)
let sha256 = hashBlock (fun () -> new SHA256Managed() :> HashAlgorithm)
let hash160 = sha256 >> ripe160
let dsha = sha256 >> sha256

Conversion from hash to hex string for debugging and display purposes. Bitcoin reverses hashes when they are displayed or when they are compared to big integers but not when they are calculated.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let hashFromHex(s: String) = 
    let h = Hex.Decode(s)
    Array.Reverse h
    h
let hashToHex(h: byte[]) = 
    let rh = h |> Array.copy |> Array.rev
    Hex.ToHexString rh

Helper functions that instanciate a binary reader/writer and feed that to the parser/serializer.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let ToBinaryArray(f: BinaryWriter -> unit) = 
    use ms = new MemoryStream()
    use os = new BinaryWriter(ms)
    f(os)
    ms.ToArray()

let ParseByteArray(b: byte[])(f: BinaryReader -> 'a) = 
    use ms = new MemoryStream(b)
    use reader = new BinaryReader(ms, Encoding.ASCII)
    f(reader)

Extend the BinaryReader with support for variable ints, strings and byte strings varints should actually be longs because the protocol supports up to 8 byte ints. However, there isn't a place where varints could possibly exceed 4-bytes. I need to support longs but I can safely cast down to ints.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
type BinaryReader with 
    member x.ReadVarInt(): int = 
        let b = x.ReadByte()
        match b with 
        | 0xFDuy -> int(x.ReadUInt16())
        | 0xFEuy -> int(x.ReadUInt32())
        | 0xFFuy -> int(x.ReadUInt64())
        | _ -> int(b)
    member x.ReadVarString(): string = 
        Encoding.ASCII.GetString(x.ReadScript())
    member x.ReadScript(): byte[] =
        let length = x.ReadVarInt()
        x.ReadBytes(length)

Extend the BinaryWriter with similar functions. Likewise for varints, I never have to actually go to longs.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
type BinaryWriter with
    member x.WriteVarInt(i: int) = 
        if i <= 0xFC then
            x.Write(byte(i))
        elif i <= 0xFFFF then
            x.Write(byte(0xFD))
            x.Write(int16 i)
        else // Protocol allows for int64 but there isn't anything varint that goes so high
            x.Write(byte(0xFE))
            x.Write(int32 i)
    member x.WriteVarString(s: string) =
        x.WriteVarInt(s.Length)
        x.Write(Encoding.ASCII.GetBytes(s))
    member x.WriteScript(b: byte[]) =
        x.WriteVarInt(b.Length)
        x.Write(b)

Primitive types & Messages

This section has the parser/serializer code for the basic types of the Protocol. Essentially they are the non standard scalar types that can be included in messages.

It also has the same code for the messages themselves. The functions are written by hand. There are some cases where the exact same code is reused. For instance when two messages have the same payload. I didn't try to factorize but instead the code is duplicated. It's mainly to keep the code similar between messages and because the gains wouldn't be big anyway.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
type NetworkAddr(ip: IPEndPoint) = 
    // the address put in the version message. The other side may want to connect back using this IP
    static member MyAddress = NetworkAddr(new IPEndPoint(IPAddress.Parse(settings.MyExtIp), settings.ServerPort)) // TODO: Lookup external address
    member x.ToByteArray() =
        use ms = new MemoryStream()
        use os = new BinaryWriter(ms)
        os.Write(0L)
        os.Write(0L)
        os.Write(0xFFFF0000)
        os.Write(ip.Address.GetAddressBytes())
        os.Write(int16(ip.Port))
        ms.ToArray()

    static member Parse(reader: BinaryReader) = 
        reader.ReadBytes(20) |> ignore
        let ip = reader.ReadBytes(4)
        let ipAddress = new IPAddress(ip)
        let port = int(uint16(IPAddress.NetworkToHostOrder(reader.ReadInt16())))
        new NetworkAddr(new IPEndPoint(ipAddress, port))

    member val EndPoint = ip with get

The Version message. The other messages that follow the same pattern are not presented here to keep the page short. Refer to the code source

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
type Version(version: int32, services: int64, timestamp: int64, recv: byte[], from: byte[], nonce: int64, userAgent: string, height: int32, relay: byte) = 
    member x.ToByteArray() = ToBinaryArray (fun os ->
        os.Write(version)
        os.Write(services)
        os.Write(timestamp)
        os.Write(recv)
        os.Write(from)
        os.Write(nonce)
        os.WriteVarString(userAgent)
        os.Write(height)
        os.Write(relay)
        )

    static member Create(timestamp: Instant, recv: IPEndPoint, from: IPEndPoint, nonce: int64, userAgent: string, height: int32, relay: byte) = 
        new Version(version, 1L, timestamp.Ticks / NodaConstants.TicksPerSecond, (new NetworkAddr(recv)).ToByteArray(),
            (new NetworkAddr(from)).ToByteArray(), nonce, userAgent, height, relay)

    static member Parse(reader: BinaryReader) =
        let version = reader.ReadInt32()
        let services = reader.ReadInt64()
        let timestamp = reader.ReadInt64()
        let recv = reader.ReadBytes(26)
        let from = reader.ReadBytes(26)
        let nonce = reader.ReadInt64()
        let userAgent = reader.ReadVarString()
        let height = reader.ReadInt32()
        let relay = if version >= 70001 && reader.BaseStream.Position < reader.BaseStream.Length then reader.ReadByte() else 1uy
        new Version(version, services, timestamp, recv, from, nonce, userAgent, height, relay)

    override x.ToString() = sprintf "%s" userAgent
    member val Recv = recv with get
    member val From = from with get
    member val UserAgent = userAgent with get
    member val Relay = relay with get
    member val Height = height with get

A BitcoinMessage is still a low-level object. The command has been identified and the payload extracted but the later hasn't been parsed.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
let magic = if settings.TestNet then 0xDAB5BFFA else 0xD9B4BEF9 
type BitcoinMessage(command: string, payload: byte[]) =
    let parsePayload(): obj = 
        use ms = new MemoryStream(payload)
        match command with
        | "getheaders" -> ParseByteArray payload GetHeaders.Parse :> obj
        | "getblocks" -> ParseByteArray payload GetBlocks.Parse :> obj
        | "getdata" -> ParseByteArray payload GetData.Parse :> obj
        | "version" -> ParseByteArray payload Version.Parse :> obj
        | "headers" -> ParseByteArray payload Headers.Parse :> obj
        | "addr" -> ParseByteArray payload Addr.Parse :> obj
        | "block" -> ParseByteArray payload Block.Parse :> obj
        | "inv" -> ParseByteArray payload InvVector.Parse :> obj
        | "tx" -> ParseByteArray payload Tx.Parse :> obj
        | "ping" -> ParseByteArray payload Ping.Parse :> obj
        | "mempool" -> ParseByteArray payload Mempool.Parse :> obj
        | "filterload" -> ParseByteArray payload FilterLoad.Parse :> obj
        | "filteradd" -> ParseByteArray payload FilterAdd.Parse :> obj
        | "filterclear" -> ParseByteArray payload FilterClear.Parse :> obj
        | _ -> null

    member x.ToByteArray() = 
        let hash256 = dsha payload
        let checksum = hash256.[0..3]
        use ms = new MemoryStream()
        use os = new BinaryWriter(ms)
        os.Write(magic)
        let mutable c = Encoding.ASCII.GetBytes(command)
        Array.Resize(&c, 12) // Pads with null bytes
        os.Write(c)
        os.Write(payload.Length)
        os.Write(checksum)
        os.Write(payload)
        ms.ToArray()

    static member Parse(reader: BinaryReader) = 
        let magic = reader.ReadInt32()
        let command = (new string(reader.ReadChars(12))).TrimEnd([| '\000' |])
        let length = reader.ReadInt32()
        let checksum = reader.ReadInt32()
        let payload = reader.ReadBytes(length)

        new BitcoinMessage(command, payload)

    override x.ToString() = command
    member val Command = command with get
    member val Payload = payload with get
    member x.ParsePayload() = parsePayload()

The message parser hooks up to an Observable of network buffers and produces an Observable of BitcoinMessage. Network buffers can split messages at any point since they are not aware of the semantics of the data transported. Therefore, there can be several messages in one buffer and/or a message can be split accross multiple buffers.

The network buffers are treated as a sequence of byte arrays over which I fold a parser function. It maintains a parsing state that has the type of the current message and the position (inside the header part or the payload part) and it returns a sequence of messages.

Running the fold gives a sequence of sequence of messages so they need to be flatten to a simple sequence.

If the data is ill-formed, because of a bad magic value or an invalid checksum for example, the stream will raise an ParseException that will be propagated as an Error element through the Observable. This method ensures that exceptions are treated as data and not as control flow.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
exception ParseException 

type ParserState = {
    InHeader: bool
    Command: string
    Checksum: int
    PayloadLength: int
    Data: byte[]
}

type BitcoinMessageParser(networkData: IObservable<byte[]>) =
    let headerLen = 24

    let rec parse (state: ParserState) (reader: BinaryReader) (messages: BitcoinMessage list) = 
        let remaining = int(reader.BaseStream.Length - reader.BaseStream.Position)
        if (state.InHeader && remaining < headerLen) || (not state.InHeader && remaining < state.PayloadLength) then
            (messages, state)
        else
            if state.InHeader then
                let messageMagic = reader.ReadInt32()
                if messageMagic <> magic then
                    raise ParseException
                else
                    let command = (new string(reader.ReadChars(12))).TrimEnd([| '\000' |])
                    let payloadLength = reader.ReadInt32()
                    let checksum = reader.ReadInt32()
                    parse 
                        ({ state with InHeader = false; Command = command; Checksum = checksum; PayloadLength = payloadLength })
                        reader
                        messages
            else 
                let payload = reader.ReadBytes(state.PayloadLength)
                let hash256 = dsha payload
                let checksum = BitConverter.ToInt32(hash256, 0)
                if checksum <> state.Checksum then
                    raise ParseException
                parse 
                    ({ state with InHeader = true })
                    reader
                    (new BitcoinMessage(state.Command, payload) :: messages)

    let acc (state: ParserState) (chunk: byte[]): BitcoinMessage list * ParserState =
        let data = Array.concat [state.Data; chunk]
        use ms = new MemoryStream(data)
        use reader = new BinaryReader(ms)
        let (messages, newState) = parse state reader List.empty
        let remainingBytes = data.[int ms.Position..]
        (messages, { newState with Data = remainingBytes })

    let bitcoinMessages = 
        networkData
            .Scan(
                (List.empty<BitcoinMessage>, { InHeader = true; Data = Array.empty; Command = ""; Checksum = 0; PayloadLength = 0}),
                fun (messages, state) (chunk) ->
                    acc state chunk 
            )
            .Select(fst)
            .SelectMany(fun m -> (m |> List.toSeq).ToObservable())
            // .Select(fun m -> logger.DebugF "Incoming %A" m; m)
    member x.BitcoinMessages with get() = bitcoinMessages

let hashCompare = new HashCompare() :> IEqualityComparer<byte[]>
module Protocol
namespace System
namespace System.Net
namespace System.Collections
namespace System.Collections.Generic
namespace System.IO
namespace System.Text
Multiple items
namespace System.Linq

--------------------
namespace Microsoft.FSharp.Linq
namespace System.Threading
namespace System.Threading.Tasks
namespace System.Security
namespace System.Security.Cryptography
Multiple items
namespace System.Collections

--------------------
namespace Microsoft.FSharp.Collections
Multiple items
type Choice<'T1,'T2> =
  | Choice1Of2 of 'T1
  | Choice2Of2 of 'T2

Full name: Microsoft.FSharp.Core.Choice<_,_>

--------------------
type Choice<'T1,'T2,'T3> =
  | Choice1Of3 of 'T1
  | Choice2Of3 of 'T2
  | Choice3Of3 of 'T3

Full name: Microsoft.FSharp.Core.Choice<_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4> =
  | Choice1Of4 of 'T1
  | Choice2Of4 of 'T2
  | Choice3Of4 of 'T3
  | Choice4Of4 of 'T4

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5> =
  | Choice1Of5 of 'T1
  | Choice2Of5 of 'T2
  | Choice3Of5 of 'T3
  | Choice4Of5 of 'T4
  | Choice5Of5 of 'T5

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =
  | Choice1Of6 of 'T1
  | Choice2Of6 of 'T2
  | Choice3Of6 of 'T3
  | Choice4Of6 of 'T4
  | Choice5Of6 of 'T5
  | Choice6Of6 of 'T6

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
  | Choice1Of7 of 'T1
  | Choice2Of7 of 'T2
  | Choice3Of7 of 'T3
  | Choice4Of7 of 'T4
  | Choice5Of7 of 'T5
  | Choice6Of7 of 'T6
  | Choice7Of7 of 'T7

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_,_>
module Option

from Microsoft.FSharp.Core
type settings = obj

Full name: Protocol.settings
val baseDir : obj

Full name: Protocol.baseDir
val version : int

Full name: Protocol.version
val connectTimeout : TimeSpan

Full name: Protocol.connectTimeout
Multiple items
type TimeSpan =
  struct
    new : ticks:int64 -> TimeSpan + 3 overloads
    member Add : ts:TimeSpan -> TimeSpan
    member CompareTo : value:obj -> int + 1 overload
    member Days : int
    member Duration : unit -> TimeSpan
    member Equals : value:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member Hours : int
    member Milliseconds : int
    member Minutes : int
    ...
  end

Full name: System.TimeSpan

--------------------
TimeSpan()
TimeSpan(ticks: int64) : unit
TimeSpan(hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : unit
TimeSpan.FromSeconds(value: float) : TimeSpan
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val handshakeTimeout : TimeSpan

Full name: Protocol.handshakeTimeout
val commandTimeout : TimeSpan

Full name: Protocol.commandTimeout
val minGetdataBatchSize : obj

Full name: Protocol.minGetdataBatchSize
val maxGetdataBatchSize : obj

Full name: Protocol.maxGetdataBatchSize
val coinbaseMaturity : int

Full name: Protocol.coinbaseMaturity
val maxBlockSize : int

Full name: Protocol.maxBlockSize
val maxMoney : int64

Full name: Protocol.maxMoney
val defaultArg : arg:'T option -> defaultValue:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.defaultArg
val txInvType : int

Full name: Protocol.txInvType
val blockInvType : int

Full name: Protocol.blockInvType
val filteredBlockInvType : int

Full name: Protocol.filteredBlockInvType
val zeroHash : byte []

Full name: Protocol.zeroHash
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.byte

--------------------
type byte = Byte

Full name: Microsoft.FSharp.Core.byte
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : params indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...

Full name: System.Array
val zeroCreate : count:int -> 'T []

Full name: Microsoft.FSharp.Collections.Array.zeroCreate
val random : Random

Full name: Protocol.random
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
Random() : unit
Random(Seed: int) : unit
exception ValidationException of string

Full name: Protocol.ValidationException
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string
val either : obj

Full name: Protocol.either
val maybe : obj

Full name: Protocol.maybe
val iterate : f:('a -> 'a) -> value:'a -> seq<'a>

Full name: Protocol.iterate
val f : ('a -> 'a)
val value : 'a
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val revFind : arr:byte [] -> f:(byte -> bool) -> int

Full name: Protocol.revFind
val arr : byte []
val f : (byte -> bool)
type bool = Boolean

Full name: Microsoft.FSharp.Core.bool
val rf : (int -> int)
val i : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
property Array.Length: int
val logger : obj

Full name: Protocol.logger
module Printf

from Microsoft.FSharp.Core
val ksprintf : continutation:(string -> 'Result) -> format:Printf.StringFormat<'T,'Result> -> 'T

Full name: Microsoft.FSharp.Core.Printf.ksprintf
Multiple items
type HashCompare =
  interface IEqualityComparer<byte []>
  new : unit -> HashCompare

Full name: Protocol.HashCompare

--------------------
new : unit -> HashCompare
Multiple items
type IEqualityComparer =
  member Equals : x:obj * y:obj -> bool
  member GetHashCode : obj:obj -> int

Full name: System.Collections.IEqualityComparer

--------------------
type IEqualityComparer<'T> =
  member Equals : x:'T * y:'T -> bool
  member GetHashCode : obj:'T -> int

Full name: System.Collections.Generic.IEqualityComparer<_>
val x : HashCompare
override HashCompare.Equals : left:byte [] * right:byte [] -> bool

Full name: Protocol.HashCompare.Equals
val left : byte []
val right : byte []
type StructuralComparisons =
  static member StructuralComparer : IComparer
  static member StructuralEqualityComparer : IEqualityComparer

Full name: System.Collections.StructuralComparisons
property StructuralComparisons.StructuralEqualityComparer: IEqualityComparer
Object.Equals(obj: obj) : bool
IEqualityComparer.Equals(x: obj, y: obj) : bool
override HashCompare.GetHashCode : hash:byte [] -> int

Full name: Protocol.HashCompare.GetHashCode
val hash : byte []
Object.GetHashCode() : int
IEqualityComparer.GetHashCode(obj: obj) : int
Multiple items
type Async =
  static member AsObservable : op:Async<'a> -> 'a0

Full name: Protocol.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.AsObservable : op:Async<'a> -> 'a0

Full name: Protocol.Async.AsObservable
val op : Async<'a>
module Observable

from Microsoft.FSharp.Control
type IObserver<'T> =
  member OnCompleted : unit -> unit
  member OnError : error:Exception -> unit
  member OnNext : value:'T -> unit

Full name: System.IObserver<_>
static member Async.StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:Threading.CancellationToken -> unit
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
Multiple items
module Option

from Microsoft.FSharp.Core

--------------------
type Option =
  static member iterSecond : f:(unit -> unit) -> opt:Option<'a> -> Option<'a>

Full name: Protocol.Option
static member Option.iterSecond : f:(unit -> unit) -> opt:Option<'a> -> Option<'a>

Full name: Protocol.Option.iterSecond
val f : (unit -> unit)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val opt : Option<'a>
property Option.IsNone: bool
val errorIfFalse : errorMsg:string -> b:bool -> Option<'a>

Full name: Protocol.errorIfFalse
val errorMsg : string
val b : bool
static member Option.iterSecond : f:(unit -> unit) -> opt:Option<'a> -> Option<'a>
val hashBlock : digest:(unit -> HashAlgorithm) -> input:byte [] -> byte []

Full name: Protocol.hashBlock
val digest : (unit -> HashAlgorithm)
type HashAlgorithm =
  member CanReuseTransform : bool
  member CanTransformMultipleBlocks : bool
  member Clear : unit -> unit
  member ComputeHash : inputStream:Stream -> byte[] + 2 overloads
  member Dispose : unit -> unit
  member Hash : byte[]
  member HashSize : int
  member Initialize : unit -> unit
  member InputBlockSize : int
  member OutputBlockSize : int
  ...

Full name: System.Security.Cryptography.HashAlgorithm
val input : byte []
val ripe160 : (byte [] -> byte [])

Full name: Protocol.ripe160
Multiple items
type RIPEMD160Managed =
  inherit RIPEMD160
  new : unit -> RIPEMD160Managed
  member Initialize : unit -> unit

Full name: System.Security.Cryptography.RIPEMD160Managed

--------------------
RIPEMD160Managed() : unit
val sha1 : (byte [] -> byte [])

Full name: Protocol.sha1
Multiple items
type SHA1Managed =
  inherit SHA1
  new : unit -> SHA1Managed
  member Initialize : unit -> unit

Full name: System.Security.Cryptography.SHA1Managed

--------------------
SHA1Managed() : unit
val sha256 : (byte [] -> byte [])

Full name: Protocol.sha256
Multiple items
type SHA256Managed =
  inherit SHA256
  new : unit -> SHA256Managed
  member Initialize : unit -> unit

Full name: System.Security.Cryptography.SHA256Managed

--------------------
SHA256Managed() : unit
val hash160 : (byte [] -> byte [])

Full name: Protocol.hash160
val dsha : (byte [] -> byte [])

Full name: Protocol.dsha
val hashFromHex : s:String -> #Array

Full name: Protocol.hashFromHex
val s : String
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Encoding) : unit
val h : #Array
Array.Reverse(array: Array) : unit
Array.Reverse(array: Array, index: int, length: int) : unit
val hashToHex : h:byte [] -> 'a

Full name: Protocol.hashToHex
val h : byte []
val rh : byte []
val copy : array:'T [] -> 'T []

Full name: Microsoft.FSharp.Collections.Array.copy
val rev : array:'T [] -> 'T []

Full name: Microsoft.FSharp.Collections.Array.rev
val ToBinaryArray : f:(BinaryWriter -> unit) -> byte []

Full name: Protocol.ToBinaryArray
val f : (BinaryWriter -> unit)
Multiple items
type BinaryWriter =
  new : output:Stream -> BinaryWriter + 1 overload
  member BaseStream : Stream
  member Close : unit -> unit
  member Dispose : unit -> unit
  member Flush : unit -> unit
  member Seek : offset:int * origin:SeekOrigin -> int64
  member Write : value:bool -> unit + 17 overloads
  static val Null : BinaryWriter

Full name: System.IO.BinaryWriter

--------------------
BinaryWriter(output: Stream) : unit
BinaryWriter(output: Stream, encoding: Encoding) : unit
val ms : MemoryStream
Multiple items
type MemoryStream =
  inherit Stream
  new : unit -> MemoryStream + 6 overloads
  member CanRead : bool
  member CanSeek : bool
  member CanWrite : bool
  member Capacity : int with get, set
  member Flush : unit -> unit
  member GetBuffer : unit -> byte[]
  member Length : int64
  member Position : int64 with get, set
  member Read : buffer:byte[] * offset:int * count:int -> int
  ...

Full name: System.IO.MemoryStream

--------------------
MemoryStream() : unit
MemoryStream(capacity: int) : unit
MemoryStream(buffer: byte []) : unit
MemoryStream(buffer: byte [], writable: bool) : unit
MemoryStream(buffer: byte [], index: int, count: int) : unit
MemoryStream(buffer: byte [], index: int, count: int, writable: bool) : unit
MemoryStream(buffer: byte [], index: int, count: int, writable: bool, publiclyVisible: bool) : unit
val os : BinaryWriter
MemoryStream.ToArray() : byte []
val ParseByteArray : b:byte [] -> f:(BinaryReader -> 'a) -> 'a

Full name: Protocol.ParseByteArray
val b : byte []
val f : (BinaryReader -> 'a)
Multiple items
type BinaryReader =
  new : input:Stream -> BinaryReader + 1 overload
  member BaseStream : Stream
  member Close : unit -> unit
  member Dispose : unit -> unit
  member PeekChar : unit -> int
  member Read : unit -> int + 2 overloads
  member ReadBoolean : unit -> bool
  member ReadByte : unit -> byte
  member ReadBytes : count:int -> byte[]
  member ReadChar : unit -> char
  ...

Full name: System.IO.BinaryReader

--------------------
BinaryReader(input: Stream) : unit
BinaryReader(input: Stream, encoding: Encoding) : unit
val reader : BinaryReader
type Encoding =
  member BodyName : string
  member Clone : unit -> obj
  member CodePage : int
  member DecoderFallback : DecoderFallback with get, set
  member EncoderFallback : EncoderFallback with get, set
  member EncodingName : string
  member Equals : value:obj -> bool
  member GetByteCount : chars:char[] -> int + 3 overloads
  member GetBytes : chars:char[] -> byte[] + 5 overloads
  member GetCharCount : bytes:byte[] -> int + 2 overloads
  ...

Full name: System.Text.Encoding
property Encoding.ASCII: Encoding
val x : BinaryReader
member BinaryReader.ReadVarInt : unit -> int

Full name: Protocol.ReadVarInt
val b : byte
BinaryReader.ReadByte() : byte
BinaryReader.ReadUInt16() : uint16
BinaryReader.ReadUInt32() : uint32
BinaryReader.ReadUInt64() : uint64
member BinaryReader.ReadVarString : unit -> string

Full name: Protocol.ReadVarString
Encoding.GetString(bytes: byte []) : string
Encoding.GetString(bytes: byte [], index: int, count: int) : string
member BinaryReader.ReadScript : unit -> byte []
member BinaryReader.ReadScript : unit -> byte []

Full name: Protocol.ReadScript
val length : int
member BinaryReader.ReadVarInt : unit -> int
BinaryReader.ReadBytes(count: int) : byte []
val x : BinaryWriter
member BinaryWriter.WriteVarInt : i:int -> unit

Full name: Protocol.WriteVarInt
BinaryWriter.Write(value: string) : unit
   (+0 other overloads)
BinaryWriter.Write(value: float32) : unit
   (+0 other overloads)
BinaryWriter.Write(value: uint64) : unit
   (+0 other overloads)
BinaryWriter.Write(value: int64) : unit
   (+0 other overloads)
BinaryWriter.Write(value: uint32) : unit
   (+0 other overloads)
BinaryWriter.Write(value: int) : unit
   (+0 other overloads)
BinaryWriter.Write(value: uint16) : unit
   (+0 other overloads)
BinaryWriter.Write(value: int16) : unit
   (+0 other overloads)
BinaryWriter.Write(value: decimal) : unit
   (+0 other overloads)
BinaryWriter.Write(value: float) : unit
   (+0 other overloads)
Multiple items
val int16 : value:'T -> int16 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int16

--------------------
type int16 = Int16

Full name: Microsoft.FSharp.Core.int16

--------------------
type int16<'Measure> = int16

Full name: Microsoft.FSharp.Core.int16<_>
Multiple items
val int32 : value:'T -> int32 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int32

--------------------
type int32 = Int32

Full name: Microsoft.FSharp.Core.int32
member BinaryWriter.WriteVarString : s:string -> unit

Full name: Protocol.WriteVarString
val s : string
member BinaryWriter.WriteVarInt : i:int -> unit
property String.Length: int
Encoding.GetBytes(s: string) : byte []
Encoding.GetBytes(chars: char []) : byte []
Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
member BinaryWriter.WriteScript : b:byte [] -> unit

Full name: Protocol.WriteScript
Multiple items
type NetworkAddr =
  new : ip:IPEndPoint -> NetworkAddr
  member ToByteArray : unit -> byte []
  member EndPoint : IPEndPoint
  static member Parse : reader:BinaryReader -> NetworkAddr
  static member MyAddress : NetworkAddr

Full name: Protocol.NetworkAddr

--------------------
new : ip:IPEndPoint -> NetworkAddr
val ip : IPEndPoint
Multiple items
type IPEndPoint =
  inherit EndPoint
  new : address:int64 * port:int -> IPEndPoint + 1 overload
  member Address : IPAddress with get, set
  member AddressFamily : AddressFamily
  member Create : socketAddress:SocketAddress -> EndPoint
  member Equals : comparand:obj -> bool
  member GetHashCode : unit -> int
  member Port : int with get, set
  member Serialize : unit -> SocketAddress
  member ToString : unit -> string
  static val MinPort : int
  ...

Full name: System.Net.IPEndPoint

--------------------
IPEndPoint(address: int64, port: int) : unit
IPEndPoint(address: IPAddress, port: int) : unit
static member NetworkAddr.MyAddress : NetworkAddr

Full name: Protocol.NetworkAddr.MyAddress
Multiple items
type IPAddress =
  new : newAddress:int64 -> IPAddress + 2 overloads
  member Address : int64 with get, set
  member AddressFamily : AddressFamily
  member Equals : comparand:obj -> bool
  member GetAddressBytes : unit -> byte[]
  member GetHashCode : unit -> int
  member IsIPv6LinkLocal : bool
  member IsIPv6Multicast : bool
  member IsIPv6SiteLocal : bool
  member IsIPv6Teredo : bool
  ...

Full name: System.Net.IPAddress

--------------------
IPAddress(newAddress: int64) : unit
IPAddress(address: byte []) : unit
IPAddress(address: byte [], scopeid: int64) : unit
IPAddress.Parse(ipString: string) : IPAddress
val x : NetworkAddr
member NetworkAddr.ToByteArray : unit -> byte []

Full name: Protocol.NetworkAddr.ToByteArray
property IPEndPoint.Address: IPAddress
IPAddress.GetAddressBytes() : byte []
property IPEndPoint.Port: int
static member NetworkAddr.Parse : reader:BinaryReader -> NetworkAddr

Full name: Protocol.NetworkAddr.Parse
val ip : byte []
val ipAddress : IPAddress
val port : int
Multiple items
val uint16 : value:'T -> uint16 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.uint16

--------------------
type uint16 = UInt16

Full name: Microsoft.FSharp.Core.uint16
IPAddress.NetworkToHostOrder(network: int16) : int16
IPAddress.NetworkToHostOrder(network: int) : int
IPAddress.NetworkToHostOrder(network: int64) : int64
BinaryReader.ReadInt16() : int16
type EndPoint =
  member AddressFamily : AddressFamily
  member Create : socketAddress:SocketAddress -> EndPoint
  member Serialize : unit -> SocketAddress

Full name: System.Net.EndPoint
Multiple items
type Version =
  new : version:int32 * services:int64 * timestamp:int64 * recv:byte [] * from:byte [] * nonce:int64 * userAgent:string * height:int32 * relay:byte -> Version
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member From : byte []
  member Height : int32
  member Recv : byte []
  member Relay : byte
  member UserAgent : string
  static member Create : timestamp:'a * recv:IPEndPoint * from:IPEndPoint * nonce:int64 * userAgent:string * height:int32 * relay:byte -> Version
  static member Parse : reader:BinaryReader -> Version

Full name: Protocol.Version

--------------------
new : version:int32 * services:int64 * timestamp:int64 * recv:byte [] * from:byte [] * nonce:int64 * userAgent:string * height:int32 * relay:byte -> Version
val version : int32
val services : int64
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int64

--------------------
type int64 = Int64

Full name: Microsoft.FSharp.Core.int64

--------------------
type int64<'Measure> = int64

Full name: Microsoft.FSharp.Core.int64<_>
val timestamp : int64
val recv : byte []
val from : byte []
val nonce : int64
val userAgent : string
val height : int32
val relay : byte
val x : Version
member Version.ToByteArray : unit -> byte []

Full name: Protocol.Version.ToByteArray
member BinaryWriter.WriteVarString : s:string -> unit
static member Version.Create : timestamp:'a * recv:IPEndPoint * from:IPEndPoint * nonce:int64 * userAgent:string * height:int32 * relay:byte -> Version

Full name: Protocol.Version.Create
val timestamp : 'a
val recv : IPEndPoint
val from : IPEndPoint
static member Version.Parse : reader:BinaryReader -> Version

Full name: Protocol.Version.Parse
val version : int
BinaryReader.ReadInt32() : int
BinaryReader.ReadInt64() : int64
member BinaryReader.ReadVarString : unit -> string
val height : int
property BinaryReader.BaseStream: Stream
property Stream.Position: int64
property Stream.Length: int64
override Version.ToString : unit -> string

Full name: Protocol.Version.ToString
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
type AddrEntry =
  {Timestamp: int32;
   Address: NetworkAddr;}

Full name: Protocol.AddrEntry
AddrEntry.Timestamp: int32
AddrEntry.Address: NetworkAddr
Multiple items
type Addr =
  new : addrs:AddrEntry [] -> Addr
  member ToByteArray : unit -> byte []
  member Addrs : AddrEntry []
  static member Parse : reader:BinaryReader -> Addr

Full name: Protocol.Addr

--------------------
new : addrs:AddrEntry [] -> Addr
val addrs : AddrEntry []
val x : Addr
member Addr.ToByteArray : unit -> byte []

Full name: Protocol.Addr.ToByteArray
val addr : AddrEntry
member NetworkAddr.ToByteArray : unit -> byte []
static member Addr.Parse : reader:BinaryReader -> Addr

Full name: Protocol.Addr.Parse
val count : int
val timestamp : int
val nodeAddr : NetworkAddr
static member NetworkAddr.Parse : reader:BinaryReader -> NetworkAddr
module Seq

from Microsoft.FSharp.Collections
val toArray : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Seq.toArray
Multiple items
type GetHeaders =
  new : hashes:byte [] list * hashStop:byte [] -> GetHeaders
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member HashStop : byte []
  member Hashes : byte [] list
  static member Parse : reader:BinaryReader -> GetHeaders

Full name: Protocol.GetHeaders

--------------------
new : hashes:byte [] list * hashStop:byte [] -> GetHeaders
val hashes : byte [] list
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val hashStop : byte []
val x : GetHeaders
member GetHeaders.ToByteArray : unit -> byte []

Full name: Protocol.GetHeaders.ToByteArray
val length : source:seq<'T> -> int

Full name: Microsoft.FSharp.Collections.Seq.length
static member GetHeaders.Parse : reader:BinaryReader -> GetHeaders

Full name: Protocol.GetHeaders.Parse
val hashCount : int
val toList : source:seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
override GetHeaders.ToString : unit -> string

Full name: Protocol.GetHeaders.ToString
val head : source:seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.head
Multiple items
type BlockHeaderCompare =
  interface IEqualityComparer<BlockHeader>
  new : unit -> BlockHeaderCompare

Full name: Protocol.BlockHeaderCompare

--------------------
new : unit -> BlockHeaderCompare
val hashCompare : IEqualityComparer<byte []>
Multiple items
type BlockHeader =
  new : hash:byte [] -> BlockHeader
  new : hash:byte [] * version:int * prevBlockHash:byte [] * merkleRoot:byte [] * timestamp:uint32 * bits:int * nonce:int * txCount:int -> BlockHeader
  member ToByteArray : nocount:bool -> full:bool -> byte []
  override ToString : unit -> string
  member Bits : int
  member Hash : byte []
  member Height : int
  member IsMain : bool
  member MerkleRoot : byte []
  member NextHash : byte []
  ...

Full name: Protocol.BlockHeader

--------------------
new : hash:byte [] -> BlockHeader
new : hash:byte [] * version:int * prevBlockHash:byte [] * merkleRoot:byte [] * timestamp:uint32 * bits:int * nonce:int * txCount:int -> BlockHeader
val x : BlockHeaderCompare
override BlockHeaderCompare.Equals : left:BlockHeader * right:BlockHeader -> bool

Full name: Protocol.BlockHeaderCompare.Equals
val left : BlockHeader
val right : BlockHeader
type obj = Object

Full name: Microsoft.FSharp.Core.obj
Object.ReferenceEquals(objA: obj, objB: obj) : bool
Object.Equals(obj: obj) : bool
IEqualityComparer.Equals(x: byte [], y: byte []) : bool
property BlockHeader.Hash: byte []
override BlockHeaderCompare.GetHashCode : header:BlockHeader -> int

Full name: Protocol.BlockHeaderCompare.GetHashCode
val header : BlockHeader
Object.GetHashCode() : int
IEqualityComparer.GetHashCode(obj: byte []) : int
val prevBlockHash : byte []
val merkleRoot : byte []
val timestamp : uint32
Multiple items
val uint32 : value:'T -> uint32 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.uint32

--------------------
type uint32 = UInt32

Full name: Microsoft.FSharp.Core.uint32
val bits : int
val nonce : int
val txCount : int
val mutable height : int
val mutable nextBlockHash : byte []
val empty<'T> : 'T []

Full name: Microsoft.FSharp.Collections.Array.empty
val mutable isMain : bool
val x : BlockHeader
member BlockHeader.ToByteArray : nocount:bool -> full:bool -> byte []

Full name: Protocol.BlockHeader.ToByteArray
val nocount : bool
val full : bool
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
static member BlockHeader.Parse : reader:BinaryReader -> BlockHeader

Full name: Protocol.BlockHeader.Parse
val headerHashPart : byte []
val r : BinaryReader
static member BlockHeader.Zero : BlockHeader

Full name: Protocol.BlockHeader.Zero
override BlockHeader.ToString : unit -> string

Full name: Protocol.BlockHeader.ToString
member BlockHeader.Height : int with set

Full name: Protocol.BlockHeader.Height
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val value : int
member BlockHeader.NextHash : byte [] with set

Full name: Protocol.BlockHeader.NextHash
val value : byte []
member BlockHeader.IsMain : bool with set

Full name: Protocol.BlockHeader.IsMain
val value : bool
Multiple items
type Headers =
  new : headers:BlockHeader list -> Headers
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member Headers : BlockHeader list
  static member Parse : reader:BinaryReader -> Headers

Full name: Protocol.Headers

--------------------
new : headers:BlockHeader list -> Headers
val headers : BlockHeader list
val x : Headers
member Headers.ToByteArray : unit -> byte []

Full name: Protocol.Headers.ToByteArray
property List.Length: int
member BlockHeader.ToByteArray : nocount:bool -> full:bool -> byte []
static member Headers.Parse : reader:BinaryReader -> Headers

Full name: Protocol.Headers.Parse
static member BlockHeader.Parse : reader:BinaryReader -> BlockHeader
override Headers.ToString : unit -> string

Full name: Protocol.Headers.ToString
Multiple items
type InvEntry =
  new : tpe:int * hash:byte [] -> InvEntry
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member Hash : byte []
  member Type : int
  static member Parse : reader:BinaryReader -> InvEntry

Full name: Protocol.InvEntry

--------------------
new : tpe:int * hash:byte [] -> InvEntry
val tpe : int
val x : InvEntry
member InvEntry.ToByteArray : unit -> byte []

Full name: Protocol.InvEntry.ToByteArray
type Type =
  inherit MemberInfo
  member Assembly : Assembly
  member AssemblyQualifiedName : string
  member Attributes : TypeAttributes
  member BaseType : Type
  member ContainsGenericParameters : bool
  member DeclaringMethod : MethodBase
  member DeclaringType : Type
  member Equals : o:obj -> bool + 1 overload
  member FindInterfaces : filter:TypeFilter * filterCriteria:obj -> Type[]
  member FindMembers : memberType:MemberTypes * bindingAttr:BindingFlags * filter:MemberFilter * filterCriteria:obj -> MemberInfo[]
  ...

Full name: System.Type
override InvEntry.ToString : unit -> string

Full name: Protocol.InvEntry.ToString
static member InvEntry.Parse : reader:BinaryReader -> InvEntry

Full name: Protocol.InvEntry.Parse
Multiple items
type InvVector =
  new : invs:InvEntry list -> InvVector
  member ToByteArray : unit -> byte []
  member Invs : InvEntry list
  static member Parse : reader:BinaryReader -> InvVector

Full name: Protocol.InvVector

--------------------
new : invs:InvEntry list -> InvVector
val invs : InvEntry list
val x : InvVector
member InvVector.ToByteArray : unit -> byte []

Full name: Protocol.InvVector.ToByteArray
val inv : InvEntry
member InvEntry.ToByteArray : unit -> byte []
static member InvVector.Parse : reader:BinaryReader -> InvVector

Full name: Protocol.InvVector.Parse
static member InvEntry.Parse : reader:BinaryReader -> InvEntry
Multiple items
type NotFound =
  new : invs:InvEntry list -> NotFound
  member ToByteArray : unit -> byte []
  member Invs : InvEntry list
  static member Parse : reader:BinaryReader -> NotFound

Full name: Protocol.NotFound

--------------------
new : invs:InvEntry list -> NotFound
val x : NotFound
member NotFound.ToByteArray : unit -> byte []

Full name: Protocol.NotFound.ToByteArray
static member NotFound.Parse : reader:BinaryReader -> NotFound

Full name: Protocol.NotFound.Parse
Multiple items
type GetData =
  new : invs:InvEntry list -> GetData
  member ToByteArray : unit -> byte []
  member Invs : InvEntry list
  static member Parse : reader:BinaryReader -> GetData

Full name: Protocol.GetData

--------------------
new : invs:InvEntry list -> GetData
val x : GetData
member GetData.ToByteArray : unit -> byte []

Full name: Protocol.GetData.ToByteArray
static member GetData.Parse : reader:BinaryReader -> GetData

Full name: Protocol.GetData.Parse
Multiple items
type OutPoint =
  new : hash:byte [] * index:int -> OutPoint
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member Hash : byte []
  member Index : int
  static member Parse : reader:BinaryReader -> OutPoint

Full name: Protocol.OutPoint

--------------------
new : hash:byte [] * index:int -> OutPoint
val index : int
val x : OutPoint
member OutPoint.ToByteArray : unit -> byte []

Full name: Protocol.OutPoint.ToByteArray
static member OutPoint.Parse : reader:BinaryReader -> OutPoint

Full name: Protocol.OutPoint.Parse
override OutPoint.ToString : unit -> string

Full name: Protocol.OutPoint.ToString
Multiple items
type TxIn =
  new : prevOutpoint:OutPoint * script:byte [] * sequence:int -> TxIn
  member ToByteArray : unit -> byte []
  member PrevOutPoint : OutPoint
  member Script : byte []
  member Sequence : int
  static member Parse : reader:BinaryReader -> TxIn

Full name: Protocol.TxIn

--------------------
new : prevOutpoint:OutPoint * script:byte [] * sequence:int -> TxIn
val prevOutpoint : OutPoint
val script : byte []
val sequence : int
val x : TxIn
member TxIn.ToByteArray : unit -> byte []

Full name: Protocol.TxIn.ToByteArray
member OutPoint.ToByteArray : unit -> byte []
member BinaryWriter.WriteScript : b:byte [] -> unit
static member TxIn.Parse : reader:BinaryReader -> TxIn

Full name: Protocol.TxIn.Parse
static member OutPoint.Parse : reader:BinaryReader -> OutPoint
Multiple items
type TxOut =
  new : value:int64 * script:byte [] -> TxOut
  member ToByteArray : unit -> byte []
  member Script : byte []
  member Value : int64
  static member Parse : reader:BinaryReader -> TxOut

Full name: Protocol.TxOut

--------------------
new : value:int64 * script:byte [] -> TxOut
val value : int64
val x : TxOut
member TxOut.ToByteArray : unit -> byte []

Full name: Protocol.TxOut.ToByteArray
static member TxOut.Parse : reader:BinaryReader -> TxOut

Full name: Protocol.TxOut.Parse
Multiple items
type AllowNullLiteralAttribute =
  inherit Attribute
  new : unit -> AllowNullLiteralAttribute

Full name: Microsoft.FSharp.Core.AllowNullLiteralAttribute

--------------------
new : unit -> AllowNullLiteralAttribute
Multiple items
type UTXO =
  new : txOut:TxOut * height:int -> UTXO
  member ToByteArray : unit -> byte []
  member Height : int
  member TxOut : TxOut
  static member Parse : reader:BinaryReader -> UTXO

Full name: Protocol.UTXO

--------------------
new : txOut:TxOut * height:int -> UTXO
val txOut : TxOut
val x : UTXO
member UTXO.ToByteArray : unit -> byte []

Full name: Protocol.UTXO.ToByteArray
member TxOut.ToByteArray : unit -> byte []
static member UTXO.Parse : reader:BinaryReader -> UTXO

Full name: Protocol.UTXO.Parse
static member TxOut.Parse : reader:BinaryReader -> TxOut
Multiple items
type Tx =
  new : hash:byte [] * version:int * txIns:TxIn [] * txOuts:TxOut [] * lockTime:uint32 -> Tx
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member Hash : byte []
  member LockTime : uint32
  member TxIns : TxIn []
  member TxOuts : TxOut []
  member Version : int
  static member Parse : reader:BinaryReader -> Tx

Full name: Protocol.Tx

--------------------
new : hash:byte [] * version:int * txIns:TxIn [] * txOuts:TxOut [] * lockTime:uint32 -> Tx
val txIns : TxIn []
val txOuts : TxOut []
val lockTime : uint32
val x : Tx
member Tx.ToByteArray : unit -> byte []

Full name: Protocol.Tx.ToByteArray
val txIn : TxIn
member TxIn.ToByteArray : unit -> byte []
static member Tx.Parse : reader:BinaryReader -> Tx

Full name: Protocol.Tx.Parse
val beginPosition : int64
val txInCount : int
static member TxIn.Parse : reader:BinaryReader -> TxIn
val txOutCount : int
val endPosition : int64
val txSize : int64
Stream.Seek(offset: int64, origin: SeekOrigin) : int64
type SeekOrigin =
  | Begin = 0
  | Current = 1
  | End = 2

Full name: System.IO.SeekOrigin
field SeekOrigin.Begin = 0
val txBytes : byte []
val txHash : byte []
override Tx.ToString : unit -> string

Full name: Protocol.Tx.ToString
Multiple items
type Block =
  new : header:BlockHeader * txs:Tx [] -> Block
  member ToByteArray : unit -> byte []
  member Header : BlockHeader
  member Txs : Tx []
  static member Parse : reader:BinaryReader -> Block

Full name: Protocol.Block

--------------------
new : header:BlockHeader * txs:Tx [] -> Block
val txs : Tx []
val x : Block
member Block.ToByteArray : unit -> byte []

Full name: Protocol.Block.ToByteArray
val tx : Tx
member Tx.ToByteArray : unit -> byte []
static member Block.Parse : reader:BinaryReader -> Block

Full name: Protocol.Block.Parse
property BlockHeader.TxCount: int
static member Tx.Parse : reader:BinaryReader -> Tx
Multiple items
type MerkleBlock =
  new : header:BlockHeader * txHashes:byte [] list * flags:byte [] -> MerkleBlock
  member ToByteArray : unit -> byte []
  member Flags : byte []
  member Header : BlockHeader
  member TxHashes : byte [] list

Full name: Protocol.MerkleBlock

--------------------
new : header:BlockHeader * txHashes:byte [] list * flags:byte [] -> MerkleBlock
val txHashes : byte [] list
val flags : byte []
val x : MerkleBlock
member MerkleBlock.ToByteArray : unit -> byte []

Full name: Protocol.MerkleBlock.ToByteArray
Multiple items
type FlagsAttribute =
  inherit Attribute
  new : unit -> FlagsAttribute

Full name: System.FlagsAttribute

--------------------
FlagsAttribute() : unit
Multiple items
type Mempool =
  new : unit -> Mempool
  member ToByteArray : unit -> byte []
  static member Parse : reader:BinaryReader -> Mempool

Full name: Protocol.Mempool

--------------------
new : unit -> Mempool
val x : Mempool
member Mempool.ToByteArray : unit -> byte []

Full name: Protocol.Mempool.ToByteArray
static member Mempool.Parse : reader:BinaryReader -> Mempool

Full name: Protocol.Mempool.Parse
Multiple items
type Ping =
  new : nonce:uint64 -> Ping
  member ToByteArray : unit -> byte []
  member Nonce : uint64
  static member Parse : reader:BinaryReader -> Ping

Full name: Protocol.Ping

--------------------
new : nonce:uint64 -> Ping
val nonce : uint64
Multiple items
val uint64 : value:'T -> uint64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.uint64

--------------------
type uint64 = UInt64

Full name: Microsoft.FSharp.Core.uint64
val x : Ping
member Ping.ToByteArray : unit -> byte []

Full name: Protocol.Ping.ToByteArray
static member Ping.Parse : reader:BinaryReader -> Ping

Full name: Protocol.Ping.Parse
Multiple items
type Pong =
  new : nonce:uint64 -> Pong
  member ToByteArray : unit -> byte []
  member Nonce : uint64
  static member Parse : reader:BinaryReader -> Pong

Full name: Protocol.Pong

--------------------
new : nonce:uint64 -> Pong
val x : Pong
member Pong.ToByteArray : unit -> byte []

Full name: Protocol.Pong.ToByteArray
static member Pong.Parse : reader:BinaryReader -> Pong

Full name: Protocol.Pong.Parse
Multiple items
type GetBlocks =
  new : hashes:byte [] list * hashStop:byte [] -> GetBlocks
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member HashStop : byte []
  member Hashes : byte [] list
  static member Parse : reader:BinaryReader -> GetBlocks

Full name: Protocol.GetBlocks

--------------------
new : hashes:byte [] list * hashStop:byte [] -> GetBlocks
val x : GetBlocks
member GetBlocks.ToByteArray : unit -> byte []

Full name: Protocol.GetBlocks.ToByteArray
static member GetBlocks.Parse : reader:BinaryReader -> GetBlocks

Full name: Protocol.GetBlocks.Parse
override GetBlocks.ToString : unit -> string

Full name: Protocol.GetBlocks.ToString
Multiple items
type FilterLoad =
  new : filter:byte [] * nHash:int * nTweak:int * nFlags:byte -> FilterLoad
  member ToByteArray : unit -> byte []
  member Filter : byte []
  member NFlags : byte
  member NHash : int
  member NTweak : int
  static member Parse : reader:BinaryReader -> FilterLoad

Full name: Protocol.FilterLoad

--------------------
new : filter:byte [] * nHash:int * nTweak:int * nFlags:byte -> FilterLoad
val filter : byte []
val nHash : int
val nTweak : int
val nFlags : byte
val x : FilterLoad
member FilterLoad.ToByteArray : unit -> byte []

Full name: Protocol.FilterLoad.ToByteArray
static member FilterLoad.Parse : reader:BinaryReader -> FilterLoad

Full name: Protocol.FilterLoad.Parse
val filterLen : int
Multiple items
type FilterAdd =
  new : data:byte [] -> FilterAdd
  member ToByteArray : unit -> byte []
  member Data : byte []
  static member Parse : reader:BinaryReader -> FilterAdd

Full name: Protocol.FilterAdd

--------------------
new : data:byte [] -> FilterAdd
val data : byte []
val x : FilterAdd
member FilterAdd.ToByteArray : unit -> byte []

Full name: Protocol.FilterAdd.ToByteArray
static member FilterAdd.Parse : reader:BinaryReader -> FilterAdd

Full name: Protocol.FilterAdd.Parse
val dataLen : int
Multiple items
namespace System.Data

--------------------
namespace Microsoft.FSharp.Data
Multiple items
type FilterClear =
  new : unit -> FilterClear
  member ToByteArray : unit -> 'a []
  static member Parse : reader:BinaryReader -> FilterClear

Full name: Protocol.FilterClear

--------------------
new : unit -> FilterClear
val x : FilterClear
member FilterClear.ToByteArray : unit -> 'a []

Full name: Protocol.FilterClear.ToByteArray
static member FilterClear.Parse : reader:BinaryReader -> FilterClear

Full name: Protocol.FilterClear.Parse
val magic : int

Full name: Protocol.magic
Multiple items
type BitcoinMessage =
  new : command:string * payload:byte [] -> BitcoinMessage
  member ParsePayload : unit -> obj
  member ToByteArray : unit -> byte []
  override ToString : unit -> string
  member Command : string
  member Payload : byte []
  static member Parse : reader:BinaryReader -> BitcoinMessage

Full name: Protocol.BitcoinMessage

--------------------
new : command:string * payload:byte [] -> BitcoinMessage
val command : string
val payload : byte []
val parsePayload : (unit -> obj)
static member GetHeaders.Parse : reader:BinaryReader -> GetHeaders
static member GetBlocks.Parse : reader:BinaryReader -> GetBlocks
static member GetData.Parse : reader:BinaryReader -> GetData
Multiple items
static member Version.Parse : reader:BinaryReader -> Version

--------------------
Version.Parse(input: string) : Version
static member Headers.Parse : reader:BinaryReader -> Headers
static member Addr.Parse : reader:BinaryReader -> Addr
static member Block.Parse : reader:BinaryReader -> Block
static member InvVector.Parse : reader:BinaryReader -> InvVector
static member Ping.Parse : reader:BinaryReader -> Ping
static member Mempool.Parse : reader:BinaryReader -> Mempool
static member FilterLoad.Parse : reader:BinaryReader -> FilterLoad
static member FilterAdd.Parse : reader:BinaryReader -> FilterAdd
static member FilterClear.Parse : reader:BinaryReader -> FilterClear
val x : BitcoinMessage
member BitcoinMessage.ToByteArray : unit -> byte []

Full name: Protocol.BitcoinMessage.ToByteArray
val hash256 : byte []
val checksum : byte []
val mutable c : byte []
Array.Resize<'T>(array: byref<'T []>, newSize: int) : unit
static member BitcoinMessage.Parse : reader:BinaryReader -> BitcoinMessage

Full name: Protocol.BitcoinMessage.Parse
val magic : int
BinaryReader.ReadChars(count: int) : char []
val checksum : int
override BitcoinMessage.ToString : unit -> string

Full name: Protocol.BitcoinMessage.ToString
member BitcoinMessage.ParsePayload : unit -> obj

Full name: Protocol.BitcoinMessage.ParsePayload
exception ParseException

Full name: Protocol.ParseException
type ParserState =
  {InHeader: bool;
   Command: string;
   Checksum: int;
   PayloadLength: int;
   Data: byte [];}

Full name: Protocol.ParserState
ParserState.InHeader: bool
ParserState.Command: string
ParserState.Checksum: int
ParserState.PayloadLength: int
Multiple items
ParserState.Data: byte []

--------------------
namespace System.Data

--------------------
namespace Microsoft.FSharp.Data
Multiple items
type BitcoinMessageParser =
  new : networkData:IObservable<byte []> -> BitcoinMessageParser
  member BitcoinMessages : obj

Full name: Protocol.BitcoinMessageParser

--------------------
new : networkData:IObservable<byte []> -> BitcoinMessageParser
val networkData : IObservable<byte []>
type IObservable<'T> =
  member Subscribe : observer:IObserver<'T> -> IDisposable

Full name: System.IObservable<_>
val headerLen : int
val parse : (ParserState -> BinaryReader -> BitcoinMessage list -> BitcoinMessage list * ParserState)
val state : ParserState
val messages : BitcoinMessage list
val remaining : int
val messageMagic : int
val raise : exn:Exception -> 'T

Full name: Microsoft.FSharp.Core.Operators.raise
val payloadLength : int
type BitConverter =
  static val IsLittleEndian : bool
  static member DoubleToInt64Bits : value:float -> int64
  static member GetBytes : value:bool -> byte[] + 9 overloads
  static member Int64BitsToDouble : value:int64 -> float
  static member ToBoolean : value:byte[] * startIndex:int -> bool
  static member ToChar : value:byte[] * startIndex:int -> char
  static member ToDouble : value:byte[] * startIndex:int -> float
  static member ToInt16 : value:byte[] * startIndex:int -> int16
  static member ToInt32 : value:byte[] * startIndex:int -> int
  static member ToInt64 : value:byte[] * startIndex:int -> int64
  ...

Full name: System.BitConverter
BitConverter.ToInt32(value: byte [], startIndex: int) : int
val acc : (ParserState -> byte [] -> BitcoinMessage list * ParserState)
val chunk : byte []
val concat : arrays:seq<'T []> -> 'T []

Full name: Microsoft.FSharp.Collections.Array.concat
ParserState.Data: byte []
val newState : ParserState
Multiple items
type List<'T> =
  new : unit -> List<'T> + 2 overloads
  member Add : item:'T -> unit
  member AddRange : collection:IEnumerable<'T> -> unit
  member AsReadOnly : unit -> ReadOnlyCollection<'T>
  member BinarySearch : item:'T -> int + 2 overloads
  member Capacity : int with get, set
  member Clear : unit -> unit
  member Contains : item:'T -> bool
  member ConvertAll<'TOutput> : converter:Converter<'T, 'TOutput> -> List<'TOutput>
  member CopyTo : array:'T[] -> unit + 2 overloads
  ...
  nested type Enumerator

Full name: System.Collections.Generic.List<_>

--------------------
List() : unit
List(capacity: int) : unit
List(collection: IEnumerable<'T>) : unit
val empty<'T> : 'T list

Full name: Microsoft.FSharp.Collections.List.empty
val remainingBytes : byte []
property MemoryStream.Position: int64
val bitcoinMessages : obj
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val toSeq : list:'T list -> seq<'T>

Full name: Microsoft.FSharp.Collections.List.toSeq
val x : BitcoinMessageParser
member BitcoinMessageParser.BitcoinMessages : obj

Full name: Protocol.BitcoinMessageParser.BitcoinMessages
val hashCompare : IEqualityComparer<byte []>

Full name: Protocol.hashCompare