anchor.diff:                 {<<"key2">>, antidote_crdt_orset, <<"bucket2">>}],
       TxId = term_to_binary({12}),
            %% Dummy value, structure of TxId is opaque to client
  -    EncRecord = antidote_pb_codec:encode(read_objects, {Objects, TxId}),
  +    EncRecord = antidote_pb_codec:encode_read_objects(Objects, TxId),
       ?assertMatch(true, is_record(EncRecord, apbreadobjects)),
       [MsgCode, MsgData] = riak_pb_codec:encode(EncRecord),
       Msg = riak_pb_codec:decode(MsgCode, list_to_binary(MsgData)),
       ?assertMatch(true, is_record(Msg, apbreadobjects)),
       DecObjects = lists:map(fun(O) ->
  -                                antidote_pb_codec:decode(bound_object, O) end,
  +                                antidote_pb_codec:decode_bound_object(O) end,
                              Msg#apbreadobjects.boundobjects),
       ?assertMatch(Objects, DecObjects),
       %% Test encoding error
  
positive.code:     {static_read_objects_resp, Values, TimeStamp};
  decode_response(Other) ->
      erlang:error("Unexpected message: ~p",[Other]).
  
  
  
  
negative.code: -endif.
  
  -export([encode/2,
    decode/2,
    decode_response/1, encode_read_objects/2, decode_bound_object/1, encode_update_objects/2, decode_update_op/1]).
  
  -define(TYPE_COUNTER, counter).
  -define(TYPE_SET, set).
  
  % general encode function
  encode(start_transaction, {Clock, Properties}) ->
    encode_start_transaction(Clock, Properties);
  encode(txn_properties, Props) ->
    encode_txn_properties(Props);
  encode(abort_transaction, TxId) ->
    encode_abort_transaction(TxId);
  encode(commit_transaction, TxId) ->
    encode_commit_transaction(TxId);
  encode(update_objects, {Updates, TxId}) ->
    encode_update_objects(Updates, TxId);
  encode(update_op, {Object, Op, Param}) ->
    encode_update_op(Object, Op, Param);
  encode(static_update_objects, {Clock, Properties, Updates}) ->
    encode_static_update_objects(Clock, Properties, Updates);
  encode(bound_object, {Key, Type, Bucket}) ->
    encode_bound_object(Key, Type, Bucket);
  encode(type, Type) ->
    encode_type(Type);
  encode(reg_update, Update) ->
    encode_reg_update(Update);
  encode(counter_update, Update) ->
    encode_counter_update(Update);
  encode(set_update, Update) ->
    encode_set_update(Update);
  encode(read_objects, {Objects, TxId}) ->
    encode_read_objects(Objects, TxId);
  encode(static_read_objects, {Clock, Properties, Objects}) ->
    encode_static_read_objects(Clock, Properties, Objects);
  encode(start_transaction_response, Resp) ->
    encode_start_transaction_response(Resp);
  encode(operation_response, Resp) ->
    encode_operation_response(Resp);
  encode(commit_response, Resp) ->
    encode_commit_response(Resp);
  encode(read_objects_response, Resp) ->
    encode_read_objects_response(Resp);
  encode(read_object_resp, Resp) ->
    encode_read_object_resp(Resp);
  encode(static_read_objects_response, {ok, Results, CommitTime}) ->
    encode_static_read_objects_response(Results, CommitTime);
  encode(error_code, Code) ->
    encode_error_code(Code);
  encode(_Other, _) ->
      erlang:error("Incorrect operation/Not yet implemented").
  
  % general decode function
  decode(txn_properties, Properties) ->
    decode_txn_properties(Properties);
  decode(bound_object, Obj) ->
    decode_bound_object(Obj);
  decode(type, Type) ->
    decode_type(Type);
  decode(error_code, Code) ->
    decode_error_code(Code);
  decode(update_object, Obj) ->
    decode_update_op(Obj);
  decode(reg_update, Update) ->
    decode_reg_update(Update);
  decode(counter_update, Update) ->
    decode_counter_update(Update);
  decode(set_update, Update) ->
    decode_set_update(Update);
  decode(_Other, _) ->
      erlang:error("Unknown message").
  
  
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%
  % error codes
  
  encode_error_code(unknown) -> 0;
  encode_error_code(timeout) -> 1;
  encode_error_code(_Other) -> 0.
  
  decode_error_code(0) -> unknown;
  decode_error_code(1) -> timeout.
  
  
  
  %%%%%%%%%%%%%%%%%%%%%%%
  % Transactions
  
  encode_start_transaction(Clock, Properties) ->
    case Clock of
      ignore ->
        #apbstarttransaction{
          properties = encode_txn_properties(Properties)};
      _ ->
        #apbstarttransaction{timestamp = Clock,
          properties = encode_txn_properties(Properties)}
    end.
  
  
  encode_commit_transaction(TxId) ->
    #apbcommittransaction{transaction_descriptor = TxId}.
  
  encode_abort_transaction(TxId) ->
    #apbaborttransaction{transaction_descriptor = TxId}.
  
  
  encode_txn_properties(_Props) ->
    %%TODO: Add more property parameters
    #apbtxnproperties{}.
  
  decode_txn_properties(_Properties) ->
    {}.
  
  
  
  %%%%%%%%%%%%%%%%%%%%%
  %% Updates
  
  % bound objects
  
  encode_bound_object({Key, Type, Bucket}) ->
    encode_bound_object(Key, Type, Bucket).
  encode_bound_object(Key, Type, Bucket) ->
    #apbboundobject{key = Key, type = encode_type(Type), bucket = Bucket}.
  
  decode_bound_object(Obj) ->
    #apbboundobject{key = Key, type = Type, bucket = Bucket} = Obj,
    {Key, decode_type(Type), Bucket}.
  
  
  % static_update_objects
  
  encode_static_update_objects(Clock, Properties, Updates) ->
    EncTransaction = encode_start_transaction(Clock, Properties),
    EncUpdates = lists:map(fun(Update) ->
      encode_update_op(Update) end,
      Updates),
    #apbstaticupdateobjects{transaction = EncTransaction,
      updates = EncUpdates}.
  
  
  decode_update_op(Obj) ->
    #apbupdateop{boundobject = Object, optype = OpType, counterop = CounterOp, setop = SetOp,
      regop = RegOp} = Obj,
    {Op, OpParam} = case OpType of
                      'COUNTER' ->
                        decode_counter_update(CounterOp);
                      'SET' ->
                        decode_set_update(SetOp);
                      'REG' ->
                        decode_reg_update(RegOp)
                    end,
    {decode_bound_object(Object), Op, OpParam}.
  
  
  
  encode_update_objects(Updates, TxId) ->
    EncUpdates = lists:map(fun(Update) ->
      encode_update_op(Update) end,
      Updates),
    #apbupdateobjects{updates = EncUpdates, transaction_descriptor = TxId}.
  
  
  
  
  %%%%%%%%%%%%%%%%%%%%%%%%
  %% Responses
  
  encode_static_read_objects_response(Results, CommitTime) ->
    #apbstaticreadobjectsresp{
      objects = encode_read_objects_response({ok, Results}),
      committime = encode_commit_response({ok, CommitTime})}.
  
  
  encode_read_objects_response({error, Reason}) ->
      #apbreadobjectsresp{success=false, errorcode = encode_error_code(Reason)};
  encode_read_objects_response({ok, Results}) ->
      EncResults = lists:map(fun(R) ->
                                     encode_read_object_resp(R) end,
                             Results),
      #apbreadobjectsresp{success=true, objects = EncResults}.
  
  
  encode_start_transaction_response({error, Reason}) ->
    #apbstarttransactionresp{success = false, errorcode = encode_error_code(Reason)};
  encode_start_transaction_response({ok, TxId}) ->
    #apbstarttransactionresp{success = true, transaction_descriptor = term_to_binary(TxId)}.
  
  encode_operation_response({error, Reason}) ->
      #apboperationresp{success=false, errorcode = encode_error_code(Reason)};
  encode_operation_response(ok) ->
      #apboperationresp{success=true}.
  
  encode_commit_response({error, Reason}) ->
      #apbcommitresp{success=false, errorcode = encode_error_code(Reason)};
  
  encode_commit_response({ok, CommitTime}) ->
      #apbcommitresp{success=true, commit_time= term_to_binary(CommitTime)}.
  
  decode_response(#apboperationresp{success = true}) ->
      {opresponse, ok};
  decode_response(#apboperationresp{success = false, errorcode = Reason})->
      {error, decode_error_code(Reason)};
  decode_response(#apbstarttransactionresp{success=true,
                                           transaction_descriptor = TxId}) ->
      {start_transaction, TxId};
  decode_response(#apbstarttransactionresp{success=false, errorcode = Reason}) ->
      {error, decode_error_code(Reason)};
  decode_response(#apbcommitresp{success=true, commit_time = TimeStamp}) ->
      {commit_transaction, TimeStamp};
  decode_response(#apbcommitresp{success=false, errorcode = Reason}) ->
      {error, decode_error_code(Reason)};
  decode_response(#apbreadobjectsresp{success=false, errorcode=Reason}) ->
      {error, decode_error_code(Reason)};
  decode_response(#apbreadobjectsresp{success=true, objects = Objects}) ->
      Resps = lists:map(fun(O) ->
                                decode_response(O) end,
                        Objects),
      {read_objects, Resps};
  decode_response(#apbreadobjectresp{}=ReadObjectResp) ->
    decode_read_object_resp(ReadObjectResp);
  decode_response(#apbstaticreadobjectsresp{objects = Objects,
                                            committime = CommitTime}) ->
      {read_objects, Values} = decode_response(Objects),
  