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[]> |
namespace System.Linq
--------------------
namespace Microsoft.FSharp.Linq
namespace System.Collections
--------------------
namespace Microsoft.FSharp.Collections
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<_,_,_,_,_,_,_>
from Microsoft.FSharp.Core
Full name: Protocol.settings
Full name: Protocol.baseDir
Full name: Protocol.version
Full name: Protocol.connectTimeout
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
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<_>
Full name: Protocol.handshakeTimeout
Full name: Protocol.commandTimeout
Full name: Protocol.minGetdataBatchSize
Full name: Protocol.maxGetdataBatchSize
Full name: Protocol.coinbaseMaturity
Full name: Protocol.maxBlockSize
Full name: Protocol.maxMoney
Full name: Microsoft.FSharp.Core.Operators.defaultArg
Full name: Protocol.txInvType
Full name: Protocol.blockInvType
Full name: Protocol.filteredBlockInvType
Full name: Protocol.zeroHash
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
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
Full name: Microsoft.FSharp.Collections.Array.zeroCreate
Full name: Protocol.random
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
Full name: Protocol.ValidationException
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
Full name: Protocol.either
Full name: Protocol.maybe
Full name: Protocol.iterate
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<_>
Full name: Protocol.revFind
Full name: Microsoft.FSharp.Core.bool
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<_>
Full name: Protocol.logger
from Microsoft.FSharp.Core
Full name: Microsoft.FSharp.Core.Printf.ksprintf
type HashCompare =
interface IEqualityComparer<byte []>
new : unit -> HashCompare
Full name: Protocol.HashCompare
--------------------
new : unit -> HashCompare
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<_>
Full name: Protocol.HashCompare.Equals
static member StructuralComparer : IComparer
static member StructuralEqualityComparer : IEqualityComparer
Full name: System.Collections.StructuralComparisons
IEqualityComparer.Equals(x: obj, y: obj) : bool
Full name: Protocol.HashCompare.GetHashCode
IEqualityComparer.GetHashCode(obj: obj) : int
type Async =
static member AsObservable : op:Async<'a> -> 'a0
Full name: Protocol.Async
--------------------
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
Full name: Protocol.Async.AsObservable
from Microsoft.FSharp.Control
member OnCompleted : unit -> unit
member OnError : error:Exception -> unit
member OnNext : value:'T -> unit
Full name: System.IObserver<_>
Full name: Microsoft.FSharp.Core.Operators.ignore
module Option
from Microsoft.FSharp.Core
--------------------
type Option =
static member iterSecond : f:(unit -> unit) -> opt:Option<'a> -> Option<'a>
Full name: Protocol.Option
Full name: Protocol.Option.iterSecond
Full name: Microsoft.FSharp.Core.unit
Full name: Protocol.errorIfFalse
Full name: Protocol.hashBlock
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
Full name: Protocol.ripe160
type RIPEMD160Managed =
inherit RIPEMD160
new : unit -> RIPEMD160Managed
member Initialize : unit -> unit
Full name: System.Security.Cryptography.RIPEMD160Managed
--------------------
RIPEMD160Managed() : unit
Full name: Protocol.sha1
type SHA1Managed =
inherit SHA1
new : unit -> SHA1Managed
member Initialize : unit -> unit
Full name: System.Security.Cryptography.SHA1Managed
--------------------
SHA1Managed() : unit
Full name: Protocol.sha256
type SHA256Managed =
inherit SHA256
new : unit -> SHA256Managed
member Initialize : unit -> unit
Full name: System.Security.Cryptography.SHA256Managed
--------------------
SHA256Managed() : unit
Full name: Protocol.hash160
Full name: Protocol.dsha
Full name: Protocol.hashFromHex
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
Array.Reverse(array: Array, index: int, length: int) : unit
Full name: Protocol.hashToHex
Full name: Microsoft.FSharp.Collections.Array.copy
Full name: Microsoft.FSharp.Collections.Array.rev
Full name: Protocol.ToBinaryArray
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
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
Full name: Protocol.ParseByteArray
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
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
Full name: Protocol.ReadVarInt
Full name: Protocol.ReadVarString
Encoding.GetString(bytes: byte [], index: int, count: int) : string
Full name: Protocol.ReadScript
Full name: Protocol.WriteVarInt
(+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)
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<_>
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
Full name: Protocol.WriteVarString
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
Full name: Protocol.WriteScript
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
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
Full name: Protocol.NetworkAddr.MyAddress
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
Full name: Protocol.NetworkAddr.ToByteArray
Full name: Protocol.NetworkAddr.Parse
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: int) : int
IPAddress.NetworkToHostOrder(network: int64) : int64
member AddressFamily : AddressFamily
member Create : socketAddress:SocketAddress -> EndPoint
member Serialize : unit -> SocketAddress
Full name: System.Net.EndPoint
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 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<_>
Full name: Protocol.Version.ToByteArray
Full name: Protocol.Version.Create
Full name: Protocol.Version.Parse
Full name: Protocol.Version.ToString
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
{Timestamp: int32;
Address: NetworkAddr;}
Full name: Protocol.AddrEntry
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
Full name: Protocol.Addr.ToByteArray
Full name: Protocol.Addr.Parse
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.toArray
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
Full name: Microsoft.FSharp.Collections.list<_>
Full name: Protocol.GetHeaders.ToByteArray
Full name: Microsoft.FSharp.Collections.Seq.length
Full name: Protocol.GetHeaders.Parse
Full name: Microsoft.FSharp.Collections.Seq.toList
Full name: Protocol.GetHeaders.ToString
Full name: Microsoft.FSharp.Collections.Seq.head
type BlockHeaderCompare =
interface IEqualityComparer<BlockHeader>
new : unit -> BlockHeaderCompare
Full name: Protocol.BlockHeaderCompare
--------------------
new : unit -> BlockHeaderCompare
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
Full name: Protocol.BlockHeaderCompare.Equals
Full name: Microsoft.FSharp.Core.obj
IEqualityComparer.Equals(x: byte [], y: byte []) : bool
Full name: Protocol.BlockHeaderCompare.GetHashCode
IEqualityComparer.GetHashCode(obj: byte []) : int
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
Full name: Microsoft.FSharp.Collections.Array.empty
Full name: Protocol.BlockHeader.ToByteArray
Full name: Microsoft.FSharp.Core.Operators.not
Full name: Protocol.BlockHeader.Parse
Full name: Protocol.BlockHeader.Zero
Full name: Protocol.BlockHeader.ToString
Full name: Protocol.BlockHeader.Height
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
Full name: Protocol.BlockHeader.NextHash
Full name: Protocol.BlockHeader.IsMain
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
Full name: Protocol.Headers.ToByteArray
Full name: Protocol.Headers.Parse
Full name: Protocol.Headers.ToString
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
Full name: Protocol.InvEntry.ToByteArray
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
Full name: Protocol.InvEntry.ToString
Full name: Protocol.InvEntry.Parse
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
Full name: Protocol.InvVector.ToByteArray
Full name: Protocol.InvVector.Parse
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
Full name: Protocol.NotFound.ToByteArray
Full name: Protocol.NotFound.Parse
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
Full name: Protocol.GetData.ToByteArray
Full name: Protocol.GetData.Parse
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
Full name: Protocol.OutPoint.ToByteArray
Full name: Protocol.OutPoint.Parse
Full name: Protocol.OutPoint.ToString
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
Full name: Protocol.TxIn.ToByteArray
Full name: Protocol.TxIn.Parse
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
Full name: Protocol.TxOut.ToByteArray
Full name: Protocol.TxOut.Parse
type AllowNullLiteralAttribute =
inherit Attribute
new : unit -> AllowNullLiteralAttribute
Full name: Microsoft.FSharp.Core.AllowNullLiteralAttribute
--------------------
new : unit -> AllowNullLiteralAttribute
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
Full name: Protocol.UTXO.ToByteArray
Full name: Protocol.UTXO.Parse
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
Full name: Protocol.Tx.ToByteArray
Full name: Protocol.Tx.Parse
| Begin = 0
| Current = 1
| End = 2
Full name: System.IO.SeekOrigin
Full name: Protocol.Tx.ToString
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
Full name: Protocol.Block.ToByteArray
Full name: Protocol.Block.Parse
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
Full name: Protocol.MerkleBlock.ToByteArray
type FlagsAttribute =
inherit Attribute
new : unit -> FlagsAttribute
Full name: System.FlagsAttribute
--------------------
FlagsAttribute() : unit
type Mempool =
new : unit -> Mempool
member ToByteArray : unit -> byte []
static member Parse : reader:BinaryReader -> Mempool
Full name: Protocol.Mempool
--------------------
new : unit -> Mempool
Full name: Protocol.Mempool.ToByteArray
Full name: Protocol.Mempool.Parse
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 uint64 : value:'T -> uint64 (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.uint64
--------------------
type uint64 = UInt64
Full name: Microsoft.FSharp.Core.uint64
Full name: Protocol.Ping.ToByteArray
Full name: Protocol.Ping.Parse
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
Full name: Protocol.Pong.ToByteArray
Full name: Protocol.Pong.Parse
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
Full name: Protocol.GetBlocks.ToByteArray
Full name: Protocol.GetBlocks.Parse
Full name: Protocol.GetBlocks.ToString
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
Full name: Protocol.FilterLoad.ToByteArray
Full name: Protocol.FilterLoad.Parse
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
Full name: Protocol.FilterAdd.ToByteArray
Full name: Protocol.FilterAdd.Parse
namespace System.Data
--------------------
namespace Microsoft.FSharp.Data
type FilterClear =
new : unit -> FilterClear
member ToByteArray : unit -> 'a []
static member Parse : reader:BinaryReader -> FilterClear
Full name: Protocol.FilterClear
--------------------
new : unit -> FilterClear
Full name: Protocol.FilterClear.ToByteArray
Full name: Protocol.FilterClear.Parse
Full name: Protocol.magic
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
static member Version.Parse : reader:BinaryReader -> Version
--------------------
Version.Parse(input: string) : Version
Full name: Protocol.BitcoinMessage.ToByteArray
Full name: Protocol.BitcoinMessage.Parse
Full name: Protocol.BitcoinMessage.ToString
Full name: Protocol.BitcoinMessage.ParsePayload
Full name: Protocol.ParseException
{InHeader: bool;
Command: string;
Checksum: int;
PayloadLength: int;
Data: byte [];}
Full name: Protocol.ParserState
ParserState.Data: byte []
--------------------
namespace System.Data
--------------------
namespace Microsoft.FSharp.Data
type BitcoinMessageParser =
new : networkData:IObservable<byte []> -> BitcoinMessageParser
member BitcoinMessages : obj
Full name: Protocol.BitcoinMessageParser
--------------------
new : networkData:IObservable<byte []> -> BitcoinMessageParser
member Subscribe : observer:IObserver<'T> -> IDisposable
Full name: System.IObservable<_>
Full name: Microsoft.FSharp.Core.Operators.raise
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
Full name: Microsoft.FSharp.Collections.Array.concat
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
Full name: Microsoft.FSharp.Collections.List.empty
Full name: Microsoft.FSharp.Core.Operators.fst
Full name: Microsoft.FSharp.Collections.List.toSeq
Full name: Protocol.BitcoinMessageParser.BitcoinMessages
Full name: Protocol.hashCompare