For the /block endpoint, if the partialblockidentifier only specifies an index and not a hash, is it acceptable to pick any block at that height (in a blockchain that doesn’t have instant finality)? Knowing that a block is canonical on the best path through the Coda Protocol blockchain (when archiving old blocks is turned on) would be an O(n) operation starting from the best tip and walking backwards – it would be best to avoid this expensive step.
Many clients (including the rosetta-cli) assume that a query by index (where only PartialBlockIdentifier.Index is populated) returns the canonical block at a particular height.
At tip (where there may exist many candidate blocks), it is ok to return any valid block. However, as soon as there is a new tip (where one of these candidate blocks is referenced as a parent block) it should be returned.
If your implementation returns any block at historical height, clients may believe there is a chain re-organization occurring and end up in a syncing re-org cycle (where syncing progress is never made as every block is being orphaned). Here is code from the syncer package used in rosetta-cli that determines if a block should be removed (if a non-canonical block is returned, this path could be used in a cycle) :
Requiring all block fetches to have an index and hash would certainly solve the problem here for your implementation but would require blocks to be fetched from tip backwards (as you have indicated) which could greatly impact the speed at which clients can sync the chain (as blocks would need to be fetched serially instead of in parallel).
Other Rosetta implementations that do not have instant finality either store the canonical block at each index in their core node or in some additional index used in their Rosetta Data API implementation. For example, a Bitcoin Data API implementation built on Bitcoin Core would call getblockhash(N) -> hash at the provided index and then return getblock(hash) when only index was provided in a query.