Sidetree for Ruby Build Status Gem Version MIT License

This is an experimental library that implements the Sidetree protocol in Ruby. If you use this library, please test to see if it works properly with reference implementation.

Installation

Add this line to your application's Gemfile:

gem 'sidetree'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install sidetree

Usage

Environment

Currently, this library support mainnet and testnet. If you want to run with testnet, configure the network with the following code.

Sidetree::Params.network = Sidetree::Params::Network::TESTNET

Create new DID

# Generate recovery key
recovery_key = Sidetree::Key.generate

# Generate update key
update_key = Sidetree::Key.generate

# Generate signing key
signing_key = Sidetree::Key.generate(id: "signing-key")

# Create DID Document from signing key
document = Sidetree::Model::Document.new(public_keys: [signing_key])

# Create DID
did =
  Sidetree::DID.create(
    document,
    update_key,
    recovery_key
  )
=> "did:sidetree:test:EiAw8oTzWzmPyTANALvzeHkCc6-uQ7XxnohqphrwC_jhNg:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nLWtleSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJJQ3BuZXVJZ2ZqZW5UZzhhMmVCeEd1RkVjV1lwZkFTcDRXdHVmbmdGd3owIiwieSI6IlNPbUxzSzhIWlU0bmY5YWZCQmF0SlJUSjVWeGZSMlVRZ1hFVV8xTXVyTzgifSwicHVycG9zZXMiOltdLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFWZXJpZmljYXRpb25LZXkyMDE5In1dLCJzZXJ2aWNlcyI6W119fV0sInVwZGF0ZUNvbW1pdG1lbnQiOiJFaUExQ1l4NWE5NTlPRGRicThrMG1WUDJwZmVEY0RXaHd3TXhWamNDS2tZOXF3In0sInN1ZmZpeERhdGEiOnsiZGVsdGFIYXNoIjoiRWlDWklRdjlyeVVDR2dLbjMtSjNfTV9ZN0k1VTIyZlpldFRaSTFxSjZqdnVEQSIsInJlY292ZXJ5Q29tbWl0bWVudCI6IkVpQ2dPTGRPb0F5a3A4SzBrMlNwMmlzRUtNOFpmRnBVSWhVcnRLbUEydEcxcWcifX0"

DID operation

Create Operation

# Generate Create Operation
create_op = did.create_op

# Generate Chunk File from create operation
chunk_file =  Sidetree::Model::ChunkFile.create_from_ops(create_ops: [create_op])

# Write chunk file to IPFS
# Initialize IPFS client(If the parameter is omitted, the default is to access http://localhost:5001/api/v0.)
ipfs = Sidetree::CAS::IPFS.new

chunk_file_uri = ipfs.write(chunk_file.to_compress)

# Generate Provisional Index File and write to IPFS
provisional_index_file = Sidetree::Model::ProvisionalIndexFile.new(chunks: [Sidetree::Model::Chunk.new(chunk_file_uri)])
provisional_index_file_uri = ipfs.write(provisional_index_file.to_compress)

# Generate Core Index File and write to IPFS
core_index_file = Sidetree::Model::CoreIndexFile.new(
  create_ops: [create_op],
  provisional_index_file_uri: provisional_index_file_uri
)
core_index_file_uri = ipfs.write(core_index_file.to_compress)

# Generate Anchor String to anchoring to Bitcoin
anchor_str = Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

Update Operation

# DID suffix to be updated
did_suffix = "EiBRrmEha_Q30GieEwLB-XM8CZd_b49dQ7znhaBxfAHTsQ"

# Current update key
update_key = Sidetree::Key.new(private_key: <private key>)

# Generate new signing key
new_signing_key = Sidetree::Key.generate(id: "signing-key")

# Create replace DID Document
document = Sidetree::Model::Document.new(public_keys: [new_signing_key])

# Generate next update key
next_update_key = Sidetree::Key.generate

# Generate update delta object
delta =
  Sidetree::Model::Delta.new(
    [document.to_replace_patch],
    next_update_key.to_commitment
  )

# Generate jws and sign with update key
claim = { updateKey: update_key.to_jwk.normalize, deltaHash: delta.to_hash }
jws = Sidetree::Util::JWS.sign(claim, update_key)

# Generate Provisional Proof File and write to IPFS
provisional_proof = Sidetree::Model::ProvisionalProofFile.new([jws])
provisional_proof_uri = ipfs.write(provisional_proof.to_compress)

# Create update operation
update_op =
  Sidetree::OP::Update.new(did_suffix, delta, jws, update_key.to_reveal_value)

# Generate Chunk File and write to IPFS
chunk_file =
  Sidetree::Model::ChunkFile.create_from_ops(update_ops: [update_op])
chunk_file_uri = ipfs.write(chunk_file.to_compress)

# Generate Provisional Index File and write to IPFS
provisional_index_file =
  Sidetree::Model::ProvisionalIndexFile.new(
    proof_file_uri: provisional_proof_uri,
    chunks: [Sidetree::Model::Chunk.new(chunk_file_uri)],
    operations: [update_op]
  )
provisional_index_file_uri = ipfs.write(provisional_index_file.to_compress)

# Generate Core Index File and write to IPFS
core_index_file =
  Sidetree::Model::CoreIndexFile.new(
    provisional_index_file_uri: provisional_index_file_uri
  )
core_index_file_uri = ipfs.write(core_index_file.to_compress)

anchor_str =
  Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

Recover Operation

did_suffix = "EiBRrmEha_Q30GieEwLB-XM8CZd_b49dQ7znhaBxfAHTsQ"

# Current recovery key
recovery_key = Sidetree::Key.new(private_key: <private key>)

# Generate new recovery key
new_recovery_key = Sidetree::Key.generate

# Generate new update key
new_update_key = Sidetree::Key.generate

# Generate new signing key
new_signing_key = Sidetree::Key.generate(id: "signing-key")

# Generate replace DID Document
document = Sidetree::Model::Document.new(public_keys: [new_signing_key])
delta =
  Sidetree::Model::Delta.new(
    [document.to_replace_patch],
    new_update_key.to_commitment
  )

# Generate jws and sign with recovery key
claim = {
  recoveryKey: recovery_key.to_jwk.normalize,
  recoveryCommitment: new_recovery_key.to_commitment,
  deltaHash: delta.to_hash
}
jws = Sidetree::Util::JWS.sign(claim, recovery_key)

# Generate Core Proof File from jws
core_proof_file = Sidetree::Model::CoreProofFile.new(recover_proofs: [jws])
core_proof_file_uri = ipfs.write(core_proof_file.to_compress)

# Create recover operation
recover_op =
  Sidetree::OP::Recover.new(
    did_suffix,
    delta,
    jws,
    recovery_key.to_reveal_value
  )

# Generate Chunk File and write to IPFS
chunk_file =
  Sidetree::Model::ChunkFile.create_from_ops(recover_ops: [recover_op])
chunk_file_uri = ipfs.write(chunk_file.to_compress)

# Generate Provisional Index File and write to IPFS
provisional_index_file =
  Sidetree::Model::ProvisionalIndexFile.new(
    chunks: [Sidetree::Model::Chunk.new(chunk_file_uri)]
  )
provisional_index_file_uri = ipfs.write(provisional_index_file.to_compress)

# Generate Core Index File and write to IPFS
core_index_file =
  Sidetree::Model::CoreIndexFile.new(
    provisional_index_file_uri: provisional_index_file_uri,
    core_proof_file_uri: core_proof_file_uri,
    recover_ops: [recover_op]
  )
core_index_file_uri = ipfs.write(core_index_file.to_compress)

anchor_str =
  Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

Deactivate operation

# Current recovery key
recovery_key = Sidetree::Key.new(private_key: <private key>)

# Generate jws and sign with recovery key
claim = {
  didSuffix: did_suffix,
  recoveryKey: recovery_key.to_jwk.normalize
}
jws = Sidetree::Util::JWS.sign(claim, recovery_key)

# Generate Core Proof File and write to IPFS
core_proof_file = Sidetree::Model::CoreProofFile.new(deactivate_proofs: [jws])
core_proof_file_uri = ipfs.write(core_proof_file.to_compress)

# Create Deactivate Operation
deactivate_op =
  Sidetree::OP::Deactivate.new(
    did_suffix,
    jws,
    recovery_key.to_reveal_value
  )

# Generate Core Index File and write to IPFS
core_index_file =
  Sidetree::Model::CoreIndexFile.new(
    core_proof_file_uri: core_proof_file_uri,
    deactivate_ops: [deactivate_op]
  )
core_index_file_uri = ipfs.write(core_index_file.to_compress)

anchor_str =
  Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

The pseudocode above all anchor a single operation, but you can be combined into a batch(i.e. to single anchor string).

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sidetree. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Sidetree project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.