public void initFromBuffer(ByteBuffer buf) throws IOException {
    FastDeserializer in = new FastDeserializer(buf);
    byte version = in.readByte(); // version number also embeds the type
    type = ProcedureInvocationType.typeFromByte(version);

    /*
     * If it's a replicated invocation, there should be two txn IDs
     * following the version byte. The first txn ID is the new txn ID, the
     * second one is the original txn ID.
     */
    if (ProcedureInvocationType.isDeprecatedInternalDRType(type)) {
      originalTxnId = in.readLong();
      originalUniqueId = in.readLong();
    }

    if (version >= BatchTimeoutOverrideType.BATCH_TIMEOUT_VERSION) {
      BatchTimeoutOverrideType batchTimeoutType =
          BatchTimeoutOverrideType.typeFromByte(in.readByte());
      if (batchTimeoutType == BatchTimeoutOverrideType.NO_OVERRIDE_FOR_BATCH_TIMEOUT) {
        batchTimeout = BatchTimeoutOverrideType.NO_TIMEOUT;
      } else {
        batchTimeout = in.readInt();
        // Client side have already checked the batchTimeout value, but,
        // on server side, we should check non-negative batchTimeout value again
        // in case of someone is using a non-standard client.
        if (batchTimeout < 0) {
          throw new IllegalArgumentException("Timeout value can't be negative.");
        }
      }
    }

    procName = in.readString().intern();
    clientHandle = in.readLong();
    // do not deserialize parameters in ClientInterface context
    serializedParams = in.remainder();
    final ByteBuffer duplicate = serializedParams.duplicate();
    params =
        new FutureTask<ParameterSet>(
            new Callable<ParameterSet>() {
              @Override
              public ParameterSet call() throws Exception {
                return ParameterSet.fromByteBuffer(duplicate);
              }
            });
  }