massa icon indicating copy to clipboard operation
massa copied to clipboard

Encapsulate the bootstrap process in the type-system - server side

Open Ben-PH opened this issue 2 years ago • 2 comments
trafficstars

The bootstrap process on the server side can be broken down into the following steps:

  1. Accept a connection. This provides a TcpStream, and is functionally the same as std::net::TcpListener::accept
  2. Create a connection binding: This creates an object containing the stream, book-keeing data, and configugation variables
  3. Perform the handshake: This is done with a single stream read to obtain the first message-hash
  4. Confirm no-error: The server tries to get a message from client. If it takes too long, good: the client hasn't sent an error (Investigate not having to rely on a timeout)
  5. Send the client the servers time
  6. loop over messages received from client:
    • timeout: break out of the bootstrap process with an Ok(())
    • AskBootstrapPart => stream_bootstrap_informaton(/* what the client currently has */)
    • AskBootstrapMipStore => send it to the client
    • BootstrapSuccess => break out of the process with an Ok(())
    • BootstrapError => break out of the process, returning the error
  7. Handle the result of said loop and end the session

stream_bootstrap_information

Is a loop that basically simplifies down to this:

loop {
    /* checks what client currently has against what the client needs */
    /* constructs a data packet to get client closer to what it needs */
    if /* data-packet is empty */ {
       return server.send_msg(BootstrapServerMessage::BootstrapFinished);
    }
    
    server.send_msg(/* data-packet */)?;
}

sending a message

Sending a message is not a simple tcp-stream write.

  1. Compute the message signature
    1. take the signature of the previous message (in byte-vec form) (handshake process creates first signature)
    2. append the bytes of the message to be sent
    3. use the servers keypair to sign these bytes
  2. send the message signature
  3. send the length of the message
  4. send the message itself

(could probably squash 2-4 into a single step)

possible states:

  • expecting send
  • send-in-progress(bytes sent/remaining)
  • send-complete
  • send-failed(bytes sent/remaining)

reading a message

  1. Reads 32 bytes from the client (in order to verify a signature)
  2. reads the bytes needed to decode the length of the message from the client (and decodes it)
  3. reads the number of bytes that was encoded in the previous read
  4. checks that the bytes received in step 1 matches the most recently sent message signature
  5. concatenates the bytes received in step 1 and 3
    • computes the hash of this concatenation
    • updates the servers most recent message-hash with this new hash

possible states:

  • expecting read
  • reading signature and message length (bytes remaining)
  • reading message (bytes remaining)
  • failed read: message sequencing broken because received signature doesn't match previously sent signature
  • failed read: bad message deserialisation
  • good read (with resulting mesage)

Ben-PH avatar Apr 11 '23 09:04 Ben-PH

state-spike can be found on the bootstrap/state-spike branch. It attempts to change the pattern from fn some_func(&mut self, ...) -> ...; to fn some_funct(mut self, ...) -> Self + ...; where Self is the BootstrapServerBinder

Run into troubles in the loop streaming bootstrap information. Would require the methods to take ownership of the binding for each iteration.

Ben-PH avatar Apr 11 '23 12:04 Ben-PH

#3811 compiles and passes. It uses a non-handshaked binding, then turns into an actual binding with the handshake method.

Ben-PH avatar Apr 11 '23 14:04 Ben-PH