Cannot create commits on local `inmemory` ceramic daemon

Hey,

I’m unable to test my app on a local ceramic node. When creating a new account, the commit call fails with the following error:

ERROR: Error: Commit rejected by conflict resolution. Rejected commit CID: bagcqcera...

The communication with ceramic seems fine up until this point, and the request data appears well-formed.

What does this error message mean? Where would you suggest I try debugging this?

Can you provide the code you’re using to created the commit? It’s hard to know what’s wrong without seeing it.

I’m using the self.id/web library. I authenticate like so:


const CERAMIC_URL = process.env.CERAMIC_URL || 'http://localhost:7007'

export async function connectIDX(signer: Signer) : Promise<SelfID<ConfigType>> {
  log.trace("IDX: Initiating connection...");

  const authProvider = await createAuthProvider(signer);
  const aliases = await getConfig();
  const self = await SelfID.authenticate<ConfigType>({
    authProvider,
    ceramic: CERAMIC_URL,
    connectNetwork: process.env.THREEID_NETWORK as ConnectNetwork|undefined,
    aliases,
  });

  log.trace(`IDX: connected ${!!self?.id}...`);

  // Attempt link, do not await this.
  createLink(self, authProvider);
  return self;
}

My .env file has the following:

# TODO: ThreeID should run entirely self-hosted
THREEID_NETWORK=local
CERAMIC_URL=http://localhost:7007

Pretty bog-standard, works on testnet and I’m pretty sure it used to work many iterations ago.

The only other thing I can think that might be relevant is our bootstrap process?

import { promises } from "fs";
import { CeramicClient } from '@ceramicnetwork/http-client'
import { ModelManager } from '@glazed/devtools'
import { Ed25519Provider } from 'key-did-provider-ed25519';
import { fromString } from 'uint8arrays';
import { getResolver } from 'key-did-resolver'
import { DID } from 'dids';

const idxFolder = new URL('../src/', import.meta.url);
const schemaPath = new URL(`config.${process.env.CONFIG_NAME}.json`, idxFolder);
const { writeFile, readFile } = promises;

//
// Publish our schema to the node (must happen once per deploy)
async function publish(manager: ModelManager) {
  // Publish the two schemas
  const schema = await readFile(new URL('schemaJWE.json', idxFolder), 'utf8');
  const SchemaJWE = JSON.parse(schema);
  const schemaId =  await manager.createSchema("jwe", SchemaJWE);
  const schemaUrl = manager.getSchemaURL(schemaId);
  if (!schemaUrl) throw new Error("Cannot publish schema: missing URL");

  const privateDetails = await manager.createDefinition("AccountDetails", {
    name: 'AccountDetails',
    description: 'Verified account details',
    schema: schemaUrl,
  })
  return privateDetails;
}

//
// Connect to the Ceramic node
async function connect() {
  // Create and authenticate the DID
  const seed = fromString(process.env.CERAMIC_SEED!, 'base16')
  const did = new DID({
    provider: new Ed25519Provider(seed),
    resolver: getResolver(),
  })
  // Authenticate the Ceramic instance with the provider
  await did.authenticate()

  // The seed must be provided as an environment variable
  const ceramic = new CeramicClient(process.env.CERAMIC_URL)
  await ceramic.setDID(did)
  return ceramic;
}

async function run() {

  // Connect to local node
  const ceramic = await connect();
  // Create a manager for the model
  const manager = new ModelManager({ceramic})

  // prepare schema for publishing
  await publish(manager);

  // Publish and store details
  const model = await manager.deploy();

  // Write details to path
  const config = JSON.stringify(model, null, 2)
  await writeFile(schemaPath, config);

  console.log(`Config written to ${schemaPath}`, config)
  process.exit(0)
}

run().catch(console.error)

This completes, I think correctly, and again - it used to work (I’m running on clay & mainnet).

My daemon starts with the following config:

[2022-08-02T14:23:49.233Z] IMPORTANT: Starting Ceramic Daemon at version 2.4.0 with config:
{
  "anchor": {},
  "http-api": {
    "cors-allowed-origins": [
      ".*"
    ]
  },
  "ipfs": {
    "mode": "bundled"
  },
  "logger": {
    "log-level": 2,
    "log-to-files": false
  },
  "metrics": {
    "metrics-exporter-enabled": false,
    "metrics-port": 9090
  },
  "network": {
    "name": "inmemory"
  },
  "node": {},
  "state-store": {
    "mode": "fs"
  },
  "indexing": {
    "db": "sqlite:///Users/kiwi_/.ceramic/indexing.sqlite",
    "models": []
  }
}
[2022-08-02T14:23:49.273Z] IMPORTANT: Connecting to ceramic network 'inmemory' using pubsub topic '/ceramic/inmemory-3160334000'
[2022-08-02T14:23:49.282Z] IMPORTANT: Connected to anchor service '<inmemory>' with supported anchor chains ['inmemory:12345']
[2022-08-02T14:23:49.342Z] IMPORTANT: Ceramic API running on 0.0.0.0:7007'

The one difference this has to regular work is that I’m built the management app from ‘develop’ branch. (So I can have my own iframe @ localhost:30001), so it might be a bit different to whatever has been published on clay/mainnet.

Related question: if I didn’t have to host this iframe, I wouldn’t. Is there any way to have the clay iframe connect to localhost chain?

Most requests succeed until the call to commit:

<image removed because “you can’t embed media items in a post”>

VM3416 LinkNewDID.tsx:50 ID creation error Error: HTTP request to 'http://localhost:7007/api/v0/commits' failed with status 'Internal Server Error': {"error":"Commit rejected by conflict resolution. Rejected commit CID: bagcqcerafg6t4zu63xxjush6q43sl2itzw2x5tffnghrffnji7cgccm7vv5a "}
    at fetchJson (http-utils.js?ae17:19:1)
    at async Document.applyCommit (document.js?79a0:53:25)
    at async CeramicClient.applyCommit (ceramic-http-client.js?13a1:94:1)
    at async TileDocument.update (tile-document.js?defa:120:1)
    at async DIDDataStore._getOwnIDXDoc (index.js?c115:273:1)
    at async TileProxy.eval (index.js?c115:284:34)
    at async TileProxy.get (proxy.js?b8da:80:1)
    at async DIDDataStore.getIndex (index.js?c115:220:1)
    at async DIDDataStore.getRecordID (index.js?c115:312:1)
    at async DIDDataStore.getRecordDocument (index.js?c115:318:1)
    at async DIDDataStore.getRecord (index.js?c115:324:1)
    at async DIDDataStore.get (index.js?c115:103:1)
    at async ThreeIDX.getAuthMap (three-idx.js?d210:209:1)
    at async Keychain.list (keychain.js?6494:141:1)
    at async ThreeIdProvider.create (threeid-provider.js?c691:110:1)
    at async Manager._initIdentity (manager.js?04a6:102:1)
    at async Manager.createAccount (manager.js?04a6:66:1)

The offending payload:

{
    "streamId": "k2t6wyfsu4pg304irxez0bv057wc0iob05owmcg82pohg3kj1rrv4qoxstm3ld",
    "commit": {
        "jws": {
            "payload": "AXESINJLdnd95ck-dKO2MozLCtbjUL7M8yEU5v7eYlkPkKXo",
            "signatures": [
                {
                    "protected": "eyJraWQiOiJkaWQ6MzpranpsNmN3ZTFqdzE0NjltbHpqNWtrdGgxbjd1MWc3OHVvaHJsY2k3N2Q1Y3V1MXJobjhqN2E4cXFyaHVzM3k_dmVyc2lvbi1pZD0wI1NRb0JNZFZjSGVXaDUzcyIsImFsZyI6IkVTMjU2SyJ9",
                    "signature": "hgBQB_Q4eXdo2DbIUyIW1vXq0wlEilgL5OLG_DxC-pghIEDOMk6bC9XgL_fclz9ilGvW-w_zKW1GEX2Bw1ngSg"
                }
            ],
            "link": "bafyreigsjn3ho7pfze7hji5wgkgmwcww4nil5thteekon7w6mjmq7eff5a"
        },
        "linkedBlock": "pGJpZNgqWCUAAXESIP1roV2+Aj2rEBffm6M6zQ0RPD//wH2NlFA5zW+YMfixZGRhdGGAZHByZXbYKlglAAFxEiD9a6FdvgI9qxAX35ujOs0NETw//8B9jZRQOc1vmDH4sWZoZWFkZXKhZnNjaGVtYXhLY2VyYW1pYzovL2szeTUybDdxYnYxZnJ5am42MnNnZ2poMWxwbjExYzU2cWZvZnptdHkxOTBkNjJod2sxY2FsMWM3cWM1aGU1NG93"
    },
    "opts": {
        "anchor": true,
        "publish": true,
        "sync": 0,
        "throwOnInvalidCommit": true
    }
}

My daemon logs the following:

[2022-08-02T14:37:17.339Z] ERROR: Error: Commit rejected by conflict resolution. Rejected commit CID: bagcqcerafg6t4zu63xxjush6q43sl2itzw2x5tffnghrffnji7cgccm7vv5a

I’ve tried deleting the %USER%/.ceramic folder to delete cache etc…

Any assistance would be very much appreciated

  1. The iframe just handles signing, if you don’t host it yourself & just use the one we provide it’ll still generate accounts on your node as long as you pass in the right details. (I’m spacing on the config but if memory serves there’s a param to pass in the network type.
  2. Is this error occuring on creation or are you trying to update the commit? The conflict resolution error isn’t making a lot of sense atm with what I see in your code. Additionally are you using a schema or are you just creating?

What might be happening is internally we’ve begun focusing more on did-session & on CACAO for our DIDs, it’s possible that an update was missed for the Self.ID SDKs, can you attempt to fuse a key-did instead of one generated through Self.ID (sample code here)

  1. It happens on the first commit even with fresh Ceramic/IPFS combo.

It’s not technically the first commit because we bootstrap our custom definitions first prior to launching our app, but the commit getting rejected here isn’t ours - it happens before we even get to our code (in the SelfID.authenticate call.

  1. It feels like that should be the case, but I couldn’t figure out how last time I took a crack at it. I’ll try again
  1. Mixing networks almost works,
# TODO: ThreeID should run entirely self-hosted
THREEID_NETWORK=testnet-clay
CERAMIC_URL=http://localhost:7007

but on account creation I get

Error: Failed to resolve did:3:kjzl6cwe1jw146vyxh3bf1l325qcb3b4rxnns181whfw1mezrpmgyia2mm2otzm?version-id=0#yFiNsYeCGwn4b16: invalidDid, Error: Failed to properly resolve 3ID, stream kjzl6cwe1jw146vyxh3bf1l325qcb3b4rxnns181whfw1mezrpmgyia2mm2otzm not found in response

Looking through the network connections there is an odd mix of requests to fer example https://ceramic-private-clay.3boxlabs.com/api/v0/multiqueries and http://localhost:7007/api/v0/multiqueries - it seems like it’s loading some of the data from clay (where an account has been previously created) and localhost (which I just flushed clean).

I’ll keep digging, but if you have any suggestions they’d be much appreciated

RE: Using key-did - I’m sorry, I can’t figure out what your asking me to test here? Is it creating a CeramicInstance with authenticated DID to replace the self.id instance? Then query the ceramic daemon to see if we can create some (any) kind of commit? (Similar to the bootstrap code above)

I’m just a little confused because the sample code linked doesn’t appear to show that - I’m actually not sure what it shows. When I try to replicate the sample in my library I can’t pass the Ceramic instance to the KeyResolver, and I’m not sure which version is out of date. If I skip that step tho (similar to bootstrap) I can’t imagine how keyResolver manages to resolve anything.