code: async def download_cot_artifacts(chain):
      
      upstream_artifacts = chain.task['payload'].get('upstreamArtifacts', [])
      all_artifacts_per_task_id = get_all_artifacts_per_task_id(chain, upstream_artifacts)
  
      mandatory_artifact_tasks = []
      optional_artifact_tasks = []
      for task_id, paths in all_artifacts_per_task_id.items():
          for path in paths:
              coroutine = asyncio.ensure_future(download_cot_artifact(chain, task_id, path))
  
              if is_artifact_optional(chain, task_id, path):
                  optional_artifact_tasks.append(coroutine)
              else:
                  mandatory_artifact_tasks.append(coroutine)
  
      mandatory_artifacts_paths = await raise_future_exceptions(mandatory_artifact_tasks)
      succeeded_optional_artifacts_paths, failed_optional_artifacts = \
          await get_results_and_future_exceptions(optional_artifact_tasks)
  
      if failed_optional_artifacts:
          log.warning('Could not download {} artifacts: {}'.format(len(failed_optional_artifacts), failed_optional_artifacts))
  
      return mandatory_artifacts_paths + succeeded_optional_artifacts_paths
docs: Call ``download_cot_artifact`` in parallel for each "upstreamArtifacts".
  
      Optional artifacts are allowed to not be downloaded.
  
      Args:
          chain (ChainOfTrust): the chain of trust object
  
      Returns:
          list: list of full paths to downloaded artifacts. Failed optional artifacts
          aren't returned
  
      Raises:
          CoTError: on chain of trust sha validation error, on a mandatory artifact
          BaseDownloadError: on download error on a mandatory artifact